diff --git a/boundaries/boundaryhandling.py b/boundaries/boundaryhandling.py
index 407c68637a820e51d338cbce2c65c724935f1dcc..cc2dbb9b227749e014b1517583bc5802f287eea6 100644
--- a/boundaries/boundaryhandling.py
+++ b/boundaries/boundaryhandling.py
@@ -5,8 +5,6 @@ from pystencils.backends.cbackend import CustomCppCode
 from pystencils.ast import Block, SympyAssignment, LoopOverCoordinate, KernelFunction
 from pystencils.transformations import moveConstantsBeforeLoop, resolveFieldAccesses, typingFromSympyInspection, \
     typeAllEquations
-from pystencils.cpu import makePythonFunction as makePythonCpuFunction
-from pystencils.gpucuda import makePythonFunction as makePythonGpuFunction
 from lbmpy.boundaries.createindexlist import createBoundaryIndexList
 
 INV_DIR_SYMBOL = TypedSymbol("invDir", "int")
@@ -75,8 +73,10 @@ class BoundaryHandling:
             ast = generateBoundaryHandling(self._symbolicPdfField, idxField, self._latticeModel, boundaryFunc)
 
             if self._target == 'cpu':
+                from pystencils.cpu import makePythonFunction as makePythonCpuFunction
                 self._boundarySweeps.append(makePythonCpuFunction(ast, {'indexField': idxField}))
             elif self._target == 'gpu':
+                from pystencils.gpucuda import makePythonFunction as makePythonGpuFunction
                 self._boundarySweeps.append(makePythonGpuFunction(ast, {'indexField': idxField}))
             else:
                 assert False
diff --git a/creationfunctions.py b/creationfunctions.py
index c04b1ea1523524b56efb37edadf6759cf086eca5..2ed6ec431560da7eb7b75c35731ce23a3f054340 100644
--- a/creationfunctions.py
+++ b/creationfunctions.py
@@ -8,10 +8,6 @@ from lbmpy.methods.momentbased import createSRT, createTRT, createOrthogonalMRT
 import lbmpy.forcemodels as forceModels
 from lbmpy.simplificationfactory import createSimplificationStrategy
 from lbmpy.updatekernels import createStreamPullKernel, createPdfArray
-from pystencils.cpu.kernelcreation import createKernel as createCpuKernel, addOpenMP
-from pystencils.gpucuda.kernelcreation import createCUDAKernel as createGpuKernel
-from pystencils.cpu import makePythonFunction as makePythonCpuFunction
-from pystencils.gpucuda import makePythonFunction as makePythonGpuFunction
 
 
 def _getParams(params, optParams):
@@ -56,6 +52,7 @@ def createLatticeBoltzmannFunction(optimizationParams={}, **kwargs):
 
     ast = createLatticeBoltzmannKernel(**params, optimizationParams=optParams)
     if params['target'] == 'cpu':
+        from pystencils.cpu import makePythonFunction as makePythonCpuFunction, addOpenMP
         if 'openMP' in optParams:
             if isinstance(optParams['openMP'], bool) and optParams['openMP']:
                 addOpenMP(ast)
@@ -63,6 +60,7 @@ def createLatticeBoltzmannFunction(optimizationParams={}, **kwargs):
                 addOpenMP(ast, numThreads=optParams['openMP'])
         return makePythonCpuFunction(ast)
     elif params['target'] == 'gpu':
+        from pystencils.gpucuda import makePythonFunction as makePythonGpuFunction
         return makePythonGpuFunction(ast)
     else:
         return ValueError("'target' has to be either 'cpu' or 'gpu'")
@@ -74,13 +72,15 @@ def createLatticeBoltzmannKernel(optimizationParams={}, **kwargs):
     updateRule = createLatticeBoltzmannUpdateRule(**params, optimizationParams=optimizationParams)
 
     if params['target'] == 'cpu':
+        from pystencils.cpu import createKernel
         if 'splitGroups' in updateRule.simplificationHints:
             splitGroups = updateRule.simplificationHints['splitGroups']
         else:
             splitGroups = ()
-        return createCpuKernel(updateRule.allEquations, splitGroups=splitGroups)
+        return createKernel(updateRule.allEquations, splitGroups=splitGroups)
     elif params['target'] == 'gpu':
-        return createGpuKernel(updateRule.allEquations)
+        from pystencils.gpucuda import createCUDAKernel
+        return createCUDAKernel(updateRule.allEquations)
     else:
         return ValueError("'target' has to be either 'cpu' or 'gpu'")
 
diff --git a/macroscopicValueKernels.py b/macroscopicValueKernels.py
index 679f36843f62868070e291a5581349d21427d09c..2e911a4e3351c20d538531f046cfc0e9e1d167a7 100644
--- a/macroscopicValueKernels.py
+++ b/macroscopicValueKernels.py
@@ -1,23 +1,25 @@
 import sympy as sp
 
+from lbmpy.simplificationfactory import createSimplificationStrategy
 from pystencils.field import Field
-import pystencils.cpu as cpu
-import pystencils.gpucuda as gpu
 
 
 def compileMacroscopicValuesGetter(lbMethod, outputQuantities, pdfArr=None, fieldLayout='numpy', target='cpu'):
     """
+    Create kernel to compute macroscopic value(s) from a pdf field (e.g. density or velocity)
 
-    :param lbMethod:
-    :param outputQuantities:
-    :param pdfArr:
+    :param lbMethod: instance of :class:`lbmpy.methods.AbstractLbMethod`
+    :param outputQuantities: sequence of quantities to compute e.g. ['density', 'velocity']
+    :param pdfArr: optional numpy array for pdf field - used to get optimal loop structure for kernel
     :param fieldLayout: layout for output field, also used for pdf field if pdfArr is not given
-    :param target:
+    :param target: 'cpu' or 'gpu'
     :return: a function to compute macroscopic values:
-        - pdfArray, can be omitted if pdf array was already passed while compiling
-        - densityOut, if not None, density is written to that array
-        - velocityOut, if not None, velocity is written to that array
+        - pdfArray
+        - keyword arguments from name of conserved quantity (as in outputQuantities) to numpy field
     """
+    if not (isinstance(outputQuantities, list) or isinstance(outputQuantities, tuple)):
+        outputQuantities = [outputQuantities]
+
     cqc = lbMethod.conservedQuantityComputation
     unknownQuantities = [oq for oq in outputQuantities if oq not in cqc.conservedQuantities]
     if unknownQuantities:
@@ -31,7 +33,7 @@ def compileMacroscopicValuesGetter(lbMethod, outputQuantities, pdfArr=None, fiel
 
     outputMapping = {}
     for outputQuantity in outputQuantities:
-        numberOfElements = cqc.conservedQuantityComputation[outputQuantity]
+        numberOfElements = cqc.conservedQuantities[outputQuantity]
         assert numberOfElements >= 1
         outputField = Field.createGeneric(outputQuantity, lbMethod.dim, layout=fieldLayout,
                                           indexDimensions=0 if numberOfElements <= 1 else 1)
@@ -42,90 +44,83 @@ def compileMacroscopicValuesGetter(lbMethod, outputQuantities, pdfArr=None, fiel
 
     stencil = lbMethod.stencil
     pdfSymbols = [pdfField(i) for i in range(len(stencil))]
-    eqs = cqc.outputEquationsFromPdfs(pdfSymbols, outputMapping)
+    eqs = cqc.outputEquationsFromPdfs(pdfSymbols, outputMapping).allEquations
 
     if target == 'cpu':
+        import pystencils.cpu as cpu
         kernel = cpu.makePythonFunction(cpu.createKernel(eqs))
     elif target == 'gpu':
+        import pystencils.gpucuda as gpu
         kernel = gpu.makePythonFunction(gpu.createCUDAKernel(eqs))
     else:
         raise ValueError("Unknown target '%s'. Possible targets are 'cpu' and 'gpu'" % (target,))
 
-    def getter(pdfs=None, **kwargs):
+    def getter(pdfs, **kwargs):
         if pdfArr is not None:
             assert pdfs.shape == pdfArr.shape and pdfs.strides == pdfArr.strides, \
                 "Pdf array not matching blueprint which was used to compile" + str(pdfs.shape) + str(pdfArr.shape)
-            if not set(kwargs.keys()).issubset(set(outputQuantities)):
-                raise ValueError("You have to specify the output field for each of the following quantities: %s"
-                                 % (str(outputQuantities),))
-            kernel(pdfs=pdfs, **kwargs)
+        if not set(outputQuantities) == set(kwargs.keys()):
+            raise ValueError("You have to specify the output field for each of the following quantities: %s"
+                             % (str(outputQuantities),))
+        kernel(pdfs=pdfs, **kwargs)
 
     return getter
 
 
-def compileMacroscopicValuesGetter(lbMethod, pdfArr=None, macroscopicFieldLayout='numpy'):
+def compileMacroscopicValuesSetter(lbMethod, quantitiesToSet, pdfArr=None, fieldLayout='numpy', target='cpu'):
     """
-    Creates a function that computes density and/or velocity and stores it into given arrays
-    :param lbMethod: lattice model (to get information about stencil, force velocity shift and compressibility)
-    :param pdfArr: array to set can already be specified here, or later when the returned function is called
-    :param macroscopicFieldLayout: layout specification for Field.createGeneric
-    :return: a function, which has three parameters:
-        - pdfArray, can be omitted if pdf array was already passed while compiling
-        - densityOut, if not None, density is written to that array
-        - velocityOut, if not None, velocity is written to that array
+    Creates a function that sets a pdf field to specified macroscopic quantities
+    The returned function can be called with the pdf field to set as single argument
+
+    :param lbMethod: instance of :class:`lbmpy.methods.AbstractLbMethod`
+    :param quantitiesToSet: map from conserved quantity name to fixed value or numpy array
+    :param pdfArr: optional numpy array for pdf field - used to get optimal loop structure for kernel
     """
-    if pdfArr is None:
-        pdfField = Field.createGeneric('pdfs', lbMethod.dim, indexDimensions=1)
-    else:
+    if pdfArr is not None:
         pdfField = Field.createFromNumpyArray('pdfs', pdfArr, indexDimensions=1)
+    else:
+        pdfField = Field.createGeneric('pdfs', lbMethod.dim, indexDimensions=1, layout=fieldLayout)
 
-    rhoField = Field.createGeneric('rho', lbMethod.dim, indexDimensions=0, layout=macroscopicFieldLayout)
-    velField = Field.createGeneric('vel', lbMethod.dim, indexDimensions=1, layout=macroscopicFieldLayout)
+    fixedKernelParameters = {}
 
-    lm = lbMethod
-    Q = len(lm.stencil)
-    symPdfs = [pdfField(i) for i in range(Q)]
-    subexpressions, rho, velSubexpressions, u = getDensityVelocityExpressions(lm.stencil, symPdfs, lm.compressible)
+    valueMap = {}
+    atLeastOneFieldInput = False
+    for quantityName, value in quantitiesToSet.items():
+        if hasattr(value, 'shape'):
+            fixedKernelParameters[quantityName] = value
+            value = Field.createFromNumpyArray(quantityName, value)
+            atLeastOneFieldInput = True
+        valueMap[quantityName] = value
 
-    uRhs = [u_i.rhs for u_i in u]
-    uLhs = [u_i.lhs for u_i in u]
-    if hasattr(lm.forceModel, "macroscopicVelocity"):
-        correctedVel = lm.forceModel.macroscopicVelocity(lm, uRhs, rho.lhs)
-        u = [sp.Eq(a, b) for a, b in zip(uLhs, correctedVel)]
+    cqc = lbMethod.conservedQuantityComputation
+    cqEquations = cqc.equilibriumInputEquationsFromInitValues(**valueMap)
 
-    eqsRhoKernel = subexpressions + [sp.Eq(rhoField(0), rho.rhs)]
-    eqsVelKernel = subexpressions + [rho] + velSubexpressions + [sp.Eq(velField(i), u[i].rhs) for i in range(lm.dim)]
-    eqsRhoAndVelKernel = eqsVelKernel + [sp.Eq(rhoField(0), rho.lhs)]
+    eq = lbMethod.getEquilibrium(conservedQuantityEquations=cqEquations)
+    if atLeastOneFieldInput:
+        simplification = createSimplificationStrategy(eq)
+        eq = simplification(eq)
+    else:
+        eq = eq.insertSubexpressions()
 
-    stencil = lbMethod.stencil
-    cqc = lbMethod.conservedQuantityComputation
-    pdfSymbols = [pdfField(i) for i in range(len(stencil))]
-    eqsDensity  = cqc.outputEquationsFromPdfs(pdfSymbols, {'density': rhoField(0)})
-    eqsVelocity = cqc.outputEquationsFromPdfs(pdfSymbols, {'velocity': [velField(i) for i in range(lbMethod.dim)]})
+    substitutions = {sym: pdfField(i) for i, sym in enumerate(lbMethod.postCollisionPdfSymbols)}
+    eq = eq.copyWithSubstitutionsApplied(substitutions).allEquations
 
-    kernelRho = makePythonFunction(createKernel(eqsRhoKernel))
-    kernelVel = makePythonFunction(createKernel(eqsVelKernel))
-    kernelRhoAndVel = makePythonFunction(createKernel(eqsRhoAndVelKernel))
+    if target == 'cpu':
+        import pystencils.cpu as cpu
+        kernel = cpu.makePythonFunction(cpu.createKernel(eq), argumentDict=fixedKernelParameters)
+    elif target == 'gpu':
+        import pystencils.gpucuda as gpu
+        kernel = gpu.makePythonFunction(gpu.createCUDAKernel(eq), argumentDict=fixedKernelParameters)
+    else:
+        raise ValueError("Unknown target '%s'. Possible targets are 'cpu' and 'gpu'" % (target,))
 
-    def getter(pdfs=None, densityOut=None, velocityOut=None):
-        if pdfs is not None and pdfArr is not None:
+    def setter(pdfs):
+        if pdfArr is not None:
             assert pdfs.shape == pdfArr.shape and pdfs.strides == pdfArr.strides, \
                 "Pdf array not matching blueprint which was used to compile" + str(pdfs.shape) + str(pdfArr.shape)
-        if pdfs is None:
-            assert pdfArr is not None, "Pdf array has to be passed either when compiling or when calling."
-            pdfs = pdfArr
-
-        assert not (densityOut is None and velocityOut is None), \
-            "Specify either densityOut or velocityOut parameter or both"
+        kernel(pdfs=pdfs)
 
-        if densityOut is not None and velocityOut is None:
-            kernelRho(pdfs=pdfs, rho=densityOut)
-        elif densityOut is None and velocityOut is not None:
-            kernelVel(pdfs=pdfs, vel=velocityOut)
-        else:
-            kernelRhoAndVel(pdfs=pdfs, rho=densityOut, vel=velocityOut)
-
-    return getter
+    return setter
 
 
 def compileAdvancedVelocitySetter(collisionRule, velocityArray, pdfArr=None):
@@ -157,74 +152,3 @@ def compileAdvancedVelocitySetter(collisionRule, velocityArray, pdfArr=None):
     kernelAst = createKernel(newCollisionRule.equations)
     return makePythonFunction(kernelAst, {'vel': velocityArray})
 
-
-def compileMacroscopicValuesSetter(latticeModel, density=1, velocity=0, equilibriumOrder=2, pdfArr=None):
-    """
-    Creates a function that sets a pdf field to specified macroscopic quantities
-    The returned function can be called with the pdf field to set as single argument
-
-    :param latticeModel: lattice model (to get information about stencil, force velocity shift and compressibility)
-    :param density: density used for equilibrium. Can either be scalar (all cells are set to same density) or array
-    :param velocity: velocity for equilibrium. Can either be scalar (e.g. 0, sets all cells to (0,0,0) velocity)
-                    or a tuple with D (2 or 3) entries to set same velocity in  the complete domain, or an array
-                    specifying the velocity for each cell
-    :param equilibriumOrder: approximation order of equilibrium
-    :param pdfArr: array to set can already be specified here, or later when the returned function is called
-    """
-    if pdfArr is not None:
-        pdfField = Field.createFromNumpyArray('pdfs', pdfArr, indexDimensions=1)
-    else:
-        pdfField = Field.createGeneric('pdfs', latticeModel.dim, indexDimensions=1)
-
-    noOffset = tuple([0] * latticeModel.dim)
-    kernelArguments = {}
-
-    if hasattr(density, 'shape'):
-        densityValue = Field.createFromNumpyArray('rho', density, indexDimensions=0)[noOffset]
-        kernelArguments['rho'] = density
-    else:
-        densityValue = density
-
-    if hasattr(velocity, 'shape'):
-        assert velocity.shape[-1] == latticeModel.dim, "Wrong shape of velocity array"
-        velocityValue = [Field.createFromNumpyArray('vel', velocity, indexDimensions=1)[noOffset](i)
-                         for i in range(latticeModel.dim)]
-        kernelArguments['vel'] = velocity
-    else:
-        if not hasattr(velocity, "__len__"):
-            velocity = [velocity] * latticeModel.dim
-        velocityValue = tuple(velocity)
-
-    # force shift
-    if latticeModel.forceModel and hasattr(latticeModel.forceModel, "macroscopicVelocity"):
-        # force model knows only about one direction - use sympy to solve the shift equations to get backward
-        fm = latticeModel.forceModel
-        unshiftedVel = [sp.Symbol("v_unshifted_%d" % (i,)) for i in range(latticeModel.dim)]
-        shiftedVel = fm.macroscopicVelocity(latticeModel, unshiftedVel, densityValue)
-        velShiftEqs = [sp.Eq(a, b) for a, b in zip(velocityValue, shiftedVel)]
-        solveRes = sp.solve(velShiftEqs, unshiftedVel)
-        assert len(solveRes) == latticeModel.dim
-        velocityValue = [solveRes[unshiftedVel_i] for unshiftedVel_i in unshiftedVel]
-
-    eq = standardDiscreteEquilibrium(latticeModel.stencil, densityValue, velocityValue, equilibriumOrder,
-                                     c_s_sq=sp.Rational(1, 3), compressible=latticeModel.compressible)
-    updateEquations = [sp.Eq(pdfField(i), eq[i]) for i in range(len(latticeModel.stencil))]
-    f = makePythonFunction(createKernel(updateEquations), kernelArguments)
-
-    def setter(pdfs=None, **kwargs):
-        if pdfs is not None and pdfArr is not None:
-            assert pdfs.shape == pdfArr.shape and pdfs.strides == pdfArr.strides, \
-                "Pdf array not matching blueprint which was used to compile"
-
-        if pdfs is None:
-            assert pdfArr is not None, "Pdf array has to be passed either when compiling or when calling."
-            pdfs = pdfArr
-        assert pdfs.shape[-1] == len(latticeModel.stencil), "Wrong shape of pdf array"
-        assert len(pdfs.shape) == latticeModel.dim + 1, "Wrong shape of pdf array"
-        if hasattr(density, 'shape'):
-            assert pdfs.shape[:-1] == density.shape, "Density array shape does not match pdf array shape"
-        if hasattr(velocity, 'shape'):
-            assert pdfs.shape[:-1] == velocity.shape[:-1], "Velocity array shape does not match pdf array shape"
-        f(pdfs=pdfs, **kwargs)  # kwargs passed here, since force model might need additional fields
-
-    return setter
diff --git a/methods/__init__.py b/methods/__init__.py
index e187d0ab43733ea92530fd0504937c1fe639a0ee..f5231bd7ed67bb704e7b5164b649c5f9243743f0 100644
--- a/methods/__init__.py
+++ b/methods/__init__.py
@@ -1,4 +1,4 @@
-from lbmpy.methods.abstractlbmmethod import AbstractLbMethod
+from lbmpy.methods.abstractlbmethod import AbstractLbMethod
 from lbmpy.methods.momentbased import MomentBasedLbMethod, RelaxationInfo
 from lbmpy.methods.momentbased import createSRT, createTRT, createTRTWithMagicNumber, createOrthogonalMRT, \
     createWithContinuousMaxwellianEqMoments, createWithDiscreteMaxwellianEqMoments
diff --git a/methods/momentbased.py b/methods/momentbased.py
index 198ed4f3c31d1afbe3e6334eca763f8e74a3de61..05075f761896ad2ecc5311e2b0b05eecf38768b3 100644
--- a/methods/momentbased.py
+++ b/methods/momentbased.py
@@ -5,7 +5,7 @@ from collections import namedtuple, OrderedDict, defaultdict
 from lbmpy.stencils import stencilsHaveSameEntries, getStencil
 from lbmpy.maxwellian_equilibrium import getMomentsOfDiscreteMaxwellianEquilibrium, \
     getMomentsOfContinuousMaxwellianEquilibrium
-from lbmpy.methods.abstractlbmmethod import AbstractLbMethod, LbmCollisionRule
+from lbmpy.methods.abstractlbmethod import AbstractLbMethod, LbmCollisionRule
 from lbmpy.methods.conservedquantitycomputation import AbstractConservedQuantityComputation, DensityVelocityComputation
 from lbmpy.moments import MOMENT_SYMBOLS, momentMatrix, isShearMoment, \
     isEven, gramSchmidt, getOrder, getDefaultMomentSetForStencil
@@ -143,9 +143,9 @@ class MomentBasedLbMethod(AbstractLbMethod):
                 raise NotImplementedError("Shear moments seem to be not relaxed separately - "
                                           "Can not determine their relaxation rate automatically")
 
-    def getEquilibrium(self):
+    def getEquilibrium(self, conservedQuantityEquations=None):
         D = sp.eye(len(self._relaxationRates))
-        return self._getCollisionRuleWithRelaxationMatrix(D)
+        return self._getCollisionRuleWithRelaxationMatrix(D, conservedQuantityEquations=conservedQuantityEquations)
 
     def getCollisionRule(self):
         D = sp.diag(*self._relaxationRates)
@@ -161,7 +161,7 @@ class MomentBasedLbMethod(AbstractLbMethod):
     def conservedQuantityComputation(self):
         return self._conservedQuantityComputation
 
-    def _getCollisionRuleWithRelaxationMatrix(self, D, additionalSubexpressions=[]):
+    def _getCollisionRuleWithRelaxationMatrix(self, D, additionalSubexpressions=[], conservedQuantityEquations=None):
         f = sp.Matrix(self.preCollisionPdfSymbols)
         M = self._momentMatrix
         m_eq = self._equilibriumMoments
@@ -169,12 +169,14 @@ class MomentBasedLbMethod(AbstractLbMethod):
         collisionRule = f + M.inv() * D * (m_eq - M * f)
         collisionEqs = [sp.Eq(lhs, rhs) for lhs, rhs in zip(self.postCollisionPdfSymbols, collisionRule)]
 
-        eqValueEqs = self._conservedQuantityComputation.equilibriumInputEquationsFromPdfs(f)
-        simplificationHints = eqValueEqs.simplificationHints
+        if conservedQuantityEquations is None:
+            conservedQuantityEquations = self._conservedQuantityComputation.equilibriumInputEquationsFromPdfs(f)
+
+        simplificationHints = conservedQuantityEquations.simplificationHints
         simplificationHints.update(self._conservedQuantityComputation.definedSymbols())
         simplificationHints['relaxationRates'] = D.atoms(sp.Symbol)
 
-        allSubexpressions = additionalSubexpressions + eqValueEqs.subexpressions + eqValueEqs.mainEquations
+        allSubexpressions = additionalSubexpressions + conservedQuantityEquations.allEquations
         return LbmCollisionRule(self, collisionEqs, allSubexpressions,
                                 simplificationHints)