diff --git a/chapman_enskog/__init__.py b/chapman_enskog/__init__.py
index ea6f81742bb7f360c880da4d3e25a7d9bb85ebe8..abe7e69bdfe7f1f3fb11ef5e331d292b2548764b 100644
--- a/chapman_enskog/__init__.py
+++ b/chapman_enskog/__init__.py
@@ -1,4 +1,4 @@
 from lbmpy.chapman_enskog.derivative import DiffOperator, Diff, expandUsingLinearity, expandUsingProductRule, \
     normalizeDiffOrder, chapmanEnskogDerivativeExpansion, chapmanEnskogDerivativeRecombination
-from lbmpy.chapman_enskog.chapman_enskog import getExpandedName, LbMethodEqMoments, insertMoments, takeMoments, \
+from lbmpy.chapman_enskog.chapman_enskog import LbMethodEqMoments, insertMoments, takeMoments, \
     CeMoment, chainSolveAndSubstitute, timeDiffSelector, momentSelector, ChapmanEnskogAnalysis
diff --git a/chapman_enskog/chapman_enskog.py b/chapman_enskog/chapman_enskog.py
index 6883c4efa22a4ce7227c20621ff6a8d0eb8de92f..286cbfff166fc675c943c341c2727cc4a18b64e1 100644
--- a/chapman_enskog/chapman_enskog.py
+++ b/chapman_enskog/chapman_enskog.py
@@ -8,7 +8,8 @@ from lbmpy.chapman_enskog import Diff, expandUsingLinearity, expandUsingProductR
 from lbmpy.chapman_enskog import DiffOperator, normalizeDiffOrder, chapmanEnskogDerivativeExpansion, \
     chapmanEnskogDerivativeRecombination
 from lbmpy.chapman_enskog.derivative import collectDerivatives, createNestedDiff
-from lbmpy.moments import discreteMoment, momentMatrix, polynomialToExponentRepresentation, getMomentIndices
+from lbmpy.moments import discreteMoment, momentMatrix, polynomialToExponentRepresentation, getMomentIndices, \
+    nonAliasedMoment
 from pystencils.cache import diskcache
 from pystencils.sympyextensions import normalizeProduct, multidimensionalSummation, kroneckerDelta
 from pystencils.sympyextensions import productSymmetric
@@ -17,14 +18,6 @@ from pystencils.sympyextensions import productSymmetric
 # --------------------------------------------- Helper Functions -------------------------------------------------------
 
 
-def getExpandedName(originalObject, number):
-    import warnings
-    warnings.warn("Deprecated!")
-    name = originalObject.name
-    newName = name + "^{(%i)}" % (number,)
-    return originalObject.func(newName)
-
-
 def expandedSymbol(name, subscript=None, superscript=None, **kwargs):
     if subscript is not None:
         name += "_{%s}" % (subscript,)
@@ -32,49 +25,10 @@ def expandedSymbol(name, subscript=None, superscript=None, **kwargs):
         name += "^{(%s)}" % (superscript,)
     return sp.Symbol(name, **kwargs)
 
-# --------------------------------   Summation Convention  -------------------------------------------------------------
-
-
-def getOccurrenceCountOfIndex(term, index):
-    if isinstance(term, Diff):
-        return getOccurrenceCountOfIndex(term.arg, index) + (1 if term.target == index else 0)
-    elif isinstance(term, sp.Symbol):
-        return 1 if term.name.endswith("_" + str(index)) else 0
-    else:
-        return 0
-
-
-def replaceIndex(term, oldIndex, newIndex):
-    if isinstance(term, Diff):
-        newArg = replaceIndex(term.arg, oldIndex, newIndex)
-        newLabel = newIndex if term.target == oldIndex else term.target
-        return Diff(newArg, newLabel, term.superscript)
-    elif isinstance(term, sp.Symbol):
-        if term.name.endswith("_" + str(oldIndex)):
-            baseName = term.name[:-(len(str(oldIndex))+1)]
-            return sp.Symbol(baseName + "_" + str(newIndex))
-        else:
-            return term
-    else:
-        newArgs = [replaceIndex(a, oldIndex, newIndex) for a in term.args]
-        return term.func(*newArgs) if newArgs else term
-
-# Problem: when there are more than two repeated indices... which one to replace?
 
 # ----------------------------------------------------------------------------------------------------------------------
 
 
-def momentAliasing(momentTuple):
-    moment = list(momentTuple)
-    result = []
-    for element in moment:
-        if element > 2:
-            result.append(2 - (element % 2))
-        else:
-            result.append(element)
-    return tuple(result)
-
-
 class CeMoment(sp.Symbol):
     def __new__(cls, name, *args, **kwds):
         obj = CeMoment.__xnew_cached_(cls, name, *args, **kwds)
@@ -256,7 +210,7 @@ def takeMoments(eqn, pdfToMomentName=(('f', '\Pi'), ('\Omega f', '\\Upsilon')),
         momentTuple = tuple(momentTuple)
 
         if useOneNeighborhoodAliasing:
-            momentTuple = momentAliasing(momentTuple)
+            momentTuple = nonAliasedMoment(momentTuple)
         result = CeMoment(fIndex.momentName, momentTuple, fIndex.superscript)
         if derivativeTerm is not None:
             result = derivativeTerm.changeArgRecursive(result)
diff --git a/moments.py b/moments.py
index ce8d32f2386b5addb9b476e91bfcafce2f9816ae..a49954089f4819c07dcb9dac4138e42a960586d3 100644
--- a/moments.py
+++ b/moments.py
@@ -1,5 +1,5 @@
+# -*- coding: utf-8 -*-
 """
-
 Module Overview
 ~~~~~~~~~~~~~~~
 
@@ -25,9 +25,9 @@ All moment polynomials have to use ``MOMENT_SYMBOLS`` (which is a module variabl
 
 Example ::
 
-    from lbmpy.moments import MOMENT_SYMBOLS
-    x, y, z = MOMENT_SYMBOLS
-    secondOrderMoment = x*y + y*z
+    >>> from lbmpy.moments import MOMENT_SYMBOLS
+    >>> x, y, z = MOMENT_SYMBOLS
+    >>> secondOrderMoment = x*y + y*z
 
 
 Functions
@@ -36,65 +36,17 @@ Functions
 """
 import itertools
 import math
-from collections import Counter, defaultdict
 from copy import copy
-
+from collections import Counter, defaultdict
+from typing import Iterable, List, Sequence, Tuple, TypeVar, Optional
 import sympy as sp
 
 from pystencils.cache import memorycache
 from pystencils.sympyextensions import removeHigherOrderTerms
 
-MOMENT_SYMBOLS = sp.symbols("x y z")
 
-
-def __uniqueList(seq):
-    seen = {}
-    result = []
-    for item in seq:
-        if item in seen:
-            continue
-        seen[item] = 1
-        result.append(item)
-    return result
-
-
-def __uniquePermutations(elements):
-    if len(elements) == 1:
-        yield (elements[0],)
-    else:
-        unique_elements = set(elements)
-        for first_element in unique_elements:
-            remaining_elements = list(elements)
-            remaining_elements.remove(first_element)
-            for sub_permutation in __uniquePermutations(remaining_elements):
-                yield (first_element,) + sub_permutation
-
-
-def __generateFixedSumTuples(tupleLength, tupleSum, allowedValues=None, ordered=False):
-    if not allowedValues:
-        allowedValues = list(range(0, tupleSum + 1))
-
-    assert (0 in allowedValues)
-
-    def recursive_helper(currentList, position, totalSum):
-        newPosition = position + 1
-        if newPosition < len(currentList):
-            for i in allowedValues:
-                currentList[position] = i
-                newSum = totalSum - i
-                if newSum < 0:
-                    continue
-                for item in recursive_helper(currentList, newPosition, newSum):
-                    yield item
-        else:
-            if totalSum in allowedValues:
-                currentList[-1] = totalSum
-                if not ordered:
-                    yield tuple(currentList)
-                if ordered and currentList == sorted(currentList, reverse=True):
-                    yield tuple(currentList)
-
-    return recursive_helper([0] * tupleLength, 0, tupleSum)
+MOMENT_SYMBOLS = sp.symbols('x y z')
+T = TypeVar('T')
 
 
 # ------------------------------ Discrete (Exponent Tuples) ------------------------------------------------------------
@@ -128,12 +80,12 @@ def pickRepresentativeMoments(moments):
 
 def momentPermutations(exponentTuple):
     """Returns all (unique) permutations of the given tuple"""
-    return __uniquePermutations(exponentTuple)
+    return __unique_permutations(exponentTuple)
 
 
 def momentsOfOrder(order, dim=3, includePermutations=True):
     """All tuples of length 'dim' which sum equals 'order'"""
-    for item in __generateFixedSumTuples(dim, order, ordered=not includePermutations):
+    for item in __fixed_sum_tuples(dim, order, ordered=not includePermutations):
         assert(len(item) == dim)
         assert(sum(item) == order)
         yield item
@@ -155,7 +107,7 @@ def extendMomentsWithPermutations(exponentTuples):
     allMoments = []
     for i in exponentTuples:
         allMoments += list(momentPermutations(i))
-    return __uniqueList(allMoments)
+    return __unique(allMoments)
 
 
 # ------------------------------ Representation Conversions ------------------------------------------------------------
@@ -278,8 +230,7 @@ def getExponentTupleFromIndices(momentIndices, dim):
 
 
 def getOrder(moment):
-    """
-    Computes polynomial order of given moment
+    """Computes polynomial order of given moment.
 
     Examples:
         >>> x , y, z = MOMENT_SYMBOLS
@@ -294,9 +245,32 @@ def getOrder(moment):
         return sum(moment)
     if len(moment.atoms(sp.Symbol)) == 0:
         return 0
-    leadingCoefficient = sp.polys.polytools.LM(moment)
-    symbolsInLeadingCoefficient = leadingCoefficient.atoms(sp.Symbol)
-    return sum([sp.degree(leadingCoefficient, gen=m) for m in symbolsInLeadingCoefficient])
+    leading_coefficient = sp.polys.polytools.LM(moment)
+    symbols_in_leading_coefficient = leading_coefficient.atoms(sp.Symbol)
+    return sum([sp.degree(leading_coefficient, gen=m) for m in symbols_in_leading_coefficient])
+
+
+def nonAliasedMoment(moment_tuple: Sequence[int]) -> Tuple[int, ...]:
+    """Takes a moment exponent tuple and returns the non-aliased version of it.
+
+    For first neighborhood stencils, all moments with exponents 3, 5, 7... are equal to exponent 1,
+    and exponents 4, 6, 8... are equal to exponent 2. This is because first neighborhood stencils only have values
+    d ∈ {+1, 0, -1}. So for example d**5 is always the same as d**3 and d, and d**6 == d**4 == d**2
+
+    Example:
+         >>> nonAliasedMoment((5, 4, 2))
+         (1, 2, 2)
+         >>> nonAliasedMoment((9, 1, 2))
+         (1, 1, 2)
+    """
+    moment = list(moment_tuple)
+    result = []
+    for element in moment:
+        if element > 2:
+            result.append(2 - (element % 2))
+        else:
+            result.append(element)
+    return tuple(result)
 
 
 def isShearMoment(moment):
@@ -605,3 +579,93 @@ def monomialToPolynomialTransformationMatrix(monomials, polynomials):
             exponentTuple = exponentTuple[:dim]
             result[polynomialIdx, monomials.index(exponentTuple)] = factor
     return result
+
+
+# --------------------------------------- Internal Functions -----------------------------------------------------------
+
+def __unique(seq: Sequence[T]) -> List[T]:
+    """Removes duplicates from a sequence in an order preserving way.
+
+    Example:
+        >>> __unique((1, 2, 3, 1, 4, 6, 3))
+        [1, 2, 3, 4, 6]
+    """
+    seen = {}
+    result = []
+    for item in seq:
+        if item in seen:
+            continue
+        seen[item] = 1
+        result.append(item)
+    return result
+
+
+def __unique_permutations(elements: Sequence[T]) -> Iterable[T]:
+    """Generates all unique permutations of the passed sequence.
+
+    Example:
+        >>> list(__unique_permutations([1, 1, 2]))
+        [(1, 1, 2), (1, 2, 1), (2, 1, 1)]
+
+    """
+    if len(elements) == 1:
+        yield (elements[0],)
+    else:
+        unique_elements = set(elements)
+        for first_element in unique_elements:
+            remaining_elements = list(elements)
+            remaining_elements.remove(first_element)
+            for sub_permutation in __unique_permutations(remaining_elements):
+                yield (first_element,) + sub_permutation
+
+
+def __fixed_sum_tuples(tuple_length: int, tuple_sum: int,
+                       allowed_values: Optional[Sequence[int]] = None,
+                       ordered: bool = False) -> Iterable[Tuple[int, ...]]:
+    """Generates all possible tuples of positive integers with a fixed sum of all entries.
+
+    Args:
+        tuple_length: length of the returned tuples
+        tuple_sum: summing over the entries of a yielded tuple should give this number
+        allowed_values: optional sequence of positive integers that are considered as tuple entries
+                        zero has to be in the set of allowed values
+                        if None, all possible positive integers are allowed
+        ordered: if True, only tuples are returned where the entries are descending, i.e. where the entries are ordered
+
+    Examples:
+        Generate all 2-tuples where the sum of entries is 3
+        >>> list(__fixed_sum_tuples(tuple_length=2, tuple_sum=3))
+        [(0, 3), (1, 2), (2, 1), (3, 0)]
+
+        Same with ordered tuples only
+        >>> list(__fixed_sum_tuples(tuple_length=2, tuple_sum=3, ordered=True))
+        [(2, 1), (3, 0)]
+
+        Restricting the allowed values, note that zero has to be in the allowed values!
+        >>> list(__fixed_sum_tuples(tuple_length=3, tuple_sum=4, allowed_values=(0, 1, 3)))
+        [(0, 1, 3), (0, 3, 1), (1, 0, 3), (1, 3, 0), (3, 0, 1), (3, 1, 0)]
+    """
+    if not allowed_values:
+        allowed_values = set(range(0, tuple_sum + 1))
+
+    assert 0 in allowed_values and all(i >= 0 for i in allowed_values)
+
+    def recursive_helper(current_list, position, total_sum):
+        new_position = position + 1
+        if new_position < len(current_list):
+            for i in allowed_values:
+                current_list[position] = i
+                new_sum = total_sum - i
+                if new_sum < 0:
+                    continue
+                for item in recursive_helper(current_list, new_position, new_sum):
+                    yield item
+        else:
+            if total_sum in allowed_values:
+                current_list[-1] = total_sum
+                if not ordered:
+                    yield tuple(current_list)
+                if ordered and current_list == sorted(current_list, reverse=True):
+                    yield tuple(current_list)
+
+    return recursive_helper([0] * tuple_length, 0, tuple_sum)
\ No newline at end of file
diff --git a/session.py b/session.py
index 2bb6a6f0bbfa4d6b3984decbbb558d634c1c1012..259855dbb7121eac277fb998d7561c679a435de6 100644
--- a/session.py
+++ b/session.py
@@ -1,6 +1,5 @@
 import sympy as sp
 import numpy as np
-from pystencils.jupytersetup import *
 from lbmpy.scenarios import *
 from lbmpy.creationfunctions import *
 from pystencils import makeSlice, showCode