From 29a9ecfff29fb342f4094198e42c1a25f907d111 Mon Sep 17 00:00:00 2001
From: Markus Holzer <markus.holzer@fau.de>
Date: Sun, 3 Oct 2021 10:05:27 +0200
Subject: [PATCH] Implemented zero center logic

---
 lbmpy/creationfunctions.py                    |  5 +
 lbmpy/macroscopic_value_kernels.py            | 22 +++--
 .../centeredcumulantmethod.py                 | 12 ++-
 lbmpy/methods/conservedquantitycomputation.py | 16 ++--
 lbmpy/methods/creationfunctions.py            | 92 +++++++++++--------
 .../momentbased/centralmomentbasedmethod.py   |  8 ++
 lbmpy/methods/momentbased/entropic_eq_srt.py  |  8 ++
 .../methods/momentbased/momentbasedmethod.py  | 12 ++-
 8 files changed, 115 insertions(+), 60 deletions(-)

diff --git a/lbmpy/creationfunctions.py b/lbmpy/creationfunctions.py
index 4a21560f..949b5332 100644
--- a/lbmpy/creationfunctions.py
+++ b/lbmpy/creationfunctions.py
@@ -412,6 +412,7 @@ def create_lb_method(lbm_config=None, **params):
 
     common_params = {
         'compressible': lbm_config.compressible,
+        'zero_centered_pdfs': lbm_config.zero_centered_pdfs,
         'equilibrium_order': lbm_config.equilibrium_order,
         'force_model': lbm_config.force_model,
         'maxwellian_moments': lbm_config.maxwellian_moments,
@@ -421,6 +422,7 @@ def create_lb_method(lbm_config=None, **params):
     }
 
     cumulant_params = {
+        'zero_centered_pdfs': lbm_config.zero_centered_pdfs,
         'equilibrium_order': lbm_config.equilibrium_order,
         'force_model': lbm_config.force_model,
         'c_s_sq': lbm_config.c_s_sq,
@@ -579,6 +581,8 @@ class LBMConfig:
         relaxation_rates: List of relexation rates for the method. Can contain symbols, ints and floats
         relaxation_rate: For TRT and KBC methods the remaining relaxation rate is determined via magic parameter
         compressible: If Helmholtz decomposition is used to better approximate the incompressible Navier Stokes eq
+        zero_centered_pdfs: If True the density is subtracted from the PDFs to center them around zero.
+                            This is important for simulations in single precision
         equilibrium_order: order of the highest term in the equilibrium formulation
         c_s_sq: speed of sound. Usually 1 / 3
         weighted: For MRT methods. If True the moments are weigted orthogonal. Usually desired.
@@ -618,6 +622,7 @@ class LBMConfig:
     relaxation_rates: Any = None
     relaxation_rate: Union[int, float, Type[sp.Symbol]] = None
     compressible: bool = False
+    zero_centered_pdfs: bool = True
     equilibrium_order: int = 2
     c_s_sq: sympy.core.numbers.Rational = sp.Rational(1, 3)
     weighted: bool = True
diff --git a/lbmpy/macroscopic_value_kernels.py b/lbmpy/macroscopic_value_kernels.py
index 3f7a2603..2e5b233a 100644
--- a/lbmpy/macroscopic_value_kernels.py
+++ b/lbmpy/macroscopic_value_kernels.py
@@ -67,7 +67,9 @@ def compile_macroscopic_values_getter(lb_method, output_quantities, pdf_arr=None
         iteration_slice: if not None, iteration is done only over this slice of the field
         field_layout: layout for output field, also used for pdf field if pdf_arr is not given
         target: `Target.CPU` or `Target.GPU`
-        previous_step_accessor: The accessor used by the streaming pattern of the previous timestep
+        streaming_pattern: define the streaming pattern for the getter kernel. Default is pull
+        previous_timestep: timestep which is used. Timestep.BOTH for two population methods.
+                           Timestep.EVEN to use the even order timestep, Timestep.ODD to use the odd order timestep
 
     Returns:
         a function to compute macroscopic values:
@@ -80,8 +82,8 @@ def compile_macroscopic_values_getter(lb_method, output_quantities, pdf_arr=None
     cqc = lb_method.conserved_quantity_computation
     unknown_quantities = [oq for oq in output_quantities if oq not in cqc.conserved_quantities]
     if unknown_quantities:
-        raise ValueError("No such conserved quantity: %s, conserved quantities are %s" %
-                         (str(unknown_quantities), str(cqc.conserved_quantities.keys())))
+        raise ValueError(f"No such conserved quantity: {str(unknown_quantities)}, "
+                         f"conserved quantities are {str(cqc.conserved_quantities.keys())}")
 
     if pdf_arr is None:
         pdf_field = Field.create_generic('pdfs', lb_method.dim, index_dimensions=1, layout=field_layout)
@@ -126,15 +128,15 @@ def compile_macroscopic_values_getter(lb_method, output_quantities, pdf_arr=None
         kernel = gpu.make_python_function(gpu.create_cuda_kernel(
             eqs, ghost_layers=ghost_layers, iteration_slice=iteration_slice))
     else:
-        raise ValueError("Unknown target '%s'. Possible targets are `Target.CPU` and `Target.GPU`" % (target,))
+        raise ValueError(f"Unknown target '{target}'. Possible targets are `Target.CPU` and `Target.GPU`")
 
     def getter(pdfs, **kwargs):
         if pdf_arr is not None:
             assert pdfs.shape == pdf_arr.shape and pdfs.strides == pdf_arr.strides, \
                 "Pdf array not matching blueprint which was used to compile" + str(pdfs.shape) + str(pdf_arr.shape)
         if not set(output_quantities).issubset(kwargs.keys()):
-            raise ValueError("You have to specify the output field for each of the following quantities: %s"
-                             % (str(output_quantities),))
+            raise ValueError(f"You have to specify the output field for "
+                             f"each of the following quantities: {str(output_quantities)}")
         kernel(pdfs=pdfs, **kwargs)
 
     return getter
@@ -158,7 +160,9 @@ def compile_macroscopic_values_setter(lb_method, quantities_to_set, pdf_arr=None
         iteration_slice: if not None, iteration is done only over this slice of the field
         field_layout: layout of the pdf field if pdf_arr was not given
         target: `Target.CPU` or `Target.GPU`
-        previous_step_accessor: The accessor used by the streaming pattern of the previous timestep
+        streaming_pattern: define the streaming pattern for the getter kernel. Default is pull
+        previous_timestep: timestep which is used. Timestep.BOTH for two population methods.
+                           Timestep.EVEN to use the even order timestep, Timestep.ODD to use the odd order timestep
 
     Returns:
         function taking pdf array as single argument and which sets the field to the given values
@@ -211,12 +215,12 @@ def compile_macroscopic_values_setter(lb_method, quantities_to_set, pdf_arr=None
         kernel = gpu.make_python_function(gpu.create_cuda_kernel(eq))
         kernel = functools.partial(kernel, **fixed_kernel_parameters)
     else:
-        raise ValueError("Unknown target '%s'. Possible targets are `Target.CPU` and `Target.GPU`" % (target,))
+        raise ValueError(f"Unknown target '{target}'. Possible targets are `Target.CPU` and `Target.GPU`")
 
     def setter(pdfs, **kwargs):
         if pdf_arr is not None:
             assert pdfs.shape == pdf_arr.shape and pdfs.strides == pdf_arr.strides, \
-                "Pdf array not matching blueprint which was used to compile" + str(pdfs.shape) + str(pdf_arr.shape)
+                f"Pdf array not matching blueprint which was used to compile {str(pdfs.shape)} {str(pdfs.strides)}"
         kernel(pdfs=pdfs, **kwargs)
 
     return setter
diff --git a/lbmpy/methods/centeredcumulant/centeredcumulantmethod.py b/lbmpy/methods/centeredcumulant/centeredcumulantmethod.py
index 99e67bec..e4519be4 100644
--- a/lbmpy/methods/centeredcumulant/centeredcumulantmethod.py
+++ b/lbmpy/methods/centeredcumulant/centeredcumulantmethod.py
@@ -177,7 +177,7 @@ class CenteredCumulantBasedLbMethod(AbstractLbMethod):
 
     @property
     def central_moment_transform_class(self):
-        self._central_moment_transform_class
+        return self._central_moment_transform_class
 
     @property
     def cumulants(self):
@@ -189,7 +189,7 @@ class CenteredCumulantBasedLbMethod(AbstractLbMethod):
 
     @property
     def cumulant_transform_class(self):
-        self._cumulant_transform_class
+        return self._cumulant_transform_class
 
     @property
     def first_order_equilibrium_moment_symbols(self, ):
@@ -211,6 +211,14 @@ class CenteredCumulantBasedLbMethod(AbstractLbMethod):
     def relaxation_rates(self):
         return tuple([e.relaxation_rate for e in self._cumulant_to_relaxation_info_dict.values()])
 
+    @property
+    def compressible(self):
+        return self._conserved_quantity_computation.compressible
+
+    @property
+    def zero_centered_pdfs(self):
+        return self._conserved_quantity_computation.zero_centered_pdfs
+
     @property
     def zeroth_order_equilibrium_moment_symbol(self, ):
         return self._conserved_quantity_computation.zeroth_order_moment_symbol
diff --git a/lbmpy/methods/conservedquantitycomputation.py b/lbmpy/methods/conservedquantitycomputation.py
index a07de0c0..f0956206 100644
--- a/lbmpy/methods/conservedquantitycomputation.py
+++ b/lbmpy/methods/conservedquantitycomputation.py
@@ -82,13 +82,14 @@ class AbstractConservedQuantityComputation(abc.ABC):
 
 
 class DensityVelocityComputation(AbstractConservedQuantityComputation):
-    def __init__(self, stencil, compressible, force_model=None,
+    def __init__(self, stencil, compressible, zero_centered_pdfs,  force_model=None,
                  zeroth_order_moment_symbol=sp.Symbol("rho"),
                  first_order_moment_symbols=sp.symbols("u_:3"),
                  second_order_moment_symbols=sp.symbols("p_:9")):
         dim = stencil.D
         self._stencil = stencil
         self._compressible = compressible
+        self._zero_centered_pdfs = zero_centered_pdfs
         self._forceModel = force_model
         self._symbolOrder0 = zeroth_order_moment_symbol
         self._symbolsOrder1 = first_order_moment_symbols[:dim]
@@ -103,6 +104,10 @@ class DensityVelocityComputation(AbstractConservedQuantityComputation):
     def compressible(self):
         return self._compressible
 
+    @property
+    def zero_centered_pdfs(self):
+        return self._zero_centered_pdfs
+
     def defined_symbols(self, order='all'):
         if order == 'all':
             return {'density': self._symbolOrder0,
@@ -114,10 +119,6 @@ class DensityVelocityComputation(AbstractConservedQuantityComputation):
         else:
             return None
 
-    @property
-    def zero_centered_pdfs(self):
-        return not self._compressible
-
     @property
     def zeroth_order_moment_symbol(self):
         return self._symbolOrder0
@@ -149,7 +150,7 @@ class DensityVelocityComputation(AbstractConservedQuantityComputation):
 
     def equilibrium_input_equations_from_init_values(self, density=1, velocity=(0, 0, 0), force_substitution=True):
         dim = self._stencil.D
-        zeroth_order_moment = density if self._compressible else density - sp.Rational(1, 1)
+        zeroth_order_moment = density - sp.Rational(1, 1) if self._zero_centered_pdfs else density
         first_order_moments = velocity[:dim]
         vel_offset = [0] * dim
 
@@ -180,7 +181,8 @@ class DensityVelocityComputation(AbstractConservedQuantityComputation):
 
         if self._compressible:
             ac = divide_first_order_moments_by_rho(ac, dim)
-        else:
+
+        if self._zero_centered_pdfs:
             ac = add_density_offset(ac)
 
         if self._forceModel is not None:
diff --git a/lbmpy/methods/creationfunctions.py b/lbmpy/methods/creationfunctions.py
index ba63e990..202f6ac2 100644
--- a/lbmpy/methods/creationfunctions.py
+++ b/lbmpy/methods/creationfunctions.py
@@ -27,12 +27,13 @@ from lbmpy.moments import (
     is_bulk_moment, is_shear_moment, get_order, set_up_shift_matrix)
 
 from lbmpy.relaxationrates import relaxation_rate_from_magic_number
-from lbmpy.stencils import get_stencil
+from lbmpy.stencils import LBStencil
 from pystencils.stencil import have_same_entries
 from pystencils.sympyextensions import common_denominator
 
 
-def create_with_discrete_maxwellian_eq_moments(stencil, moment_to_relaxation_rate_dict, compressible=False,
+def create_with_discrete_maxwellian_eq_moments(stencil, moment_to_relaxation_rate_dict,
+                                               compressible=False, zero_centered_pdfs=True,
                                                force_model=None, equilibrium_order=2, c_s_sq=sp.Rational(1, 3),
                                                central_moment_space=False,
                                                moment_transform_class=None,
@@ -42,7 +43,7 @@ def create_with_discrete_maxwellian_eq_moments(stencil, moment_to_relaxation_rat
     These moments are relaxed against the moments of the discrete Maxwellian distribution.
 
     Args:
-        stencil: nested tuple defining the discrete velocity space. See `get_stencil`
+        stencil: nested tuple defining the discrete velocity space. See `LBStencil`
         moment_to_relaxation_rate_dict: dict that has as many entries as the stencil. Each moment, which can be
                                         represented by an exponent tuple or in polynomial form
                                         (see `lbmpy.moments`), is mapped to a relaxation rate.
@@ -50,6 +51,8 @@ def create_with_discrete_maxwellian_eq_moments(stencil, moment_to_relaxation_rat
                       where :math:`\rho_0` is chosen as one, and the first moment of the pdfs is :math:`\Delta \rho` .
                       This approximates the incompressible Navier-Stokes equations better than the standard
                       compressible model.
+        zero_centered_pdfs: If True the density is subtracted from the PDFs to center them around zero.
+                            This is important for simulations in single precision
         force_model: force model instance, or None if no external forces
         equilibrium_order: approximation order of macroscopic velocity :math:`\mathbf{u}` in the equilibrium
         c_s_sq: Speed of sound squared
@@ -64,12 +67,13 @@ def create_with_discrete_maxwellian_eq_moments(stencil, moment_to_relaxation_rat
         :class:`lbmpy.methods.momentbased.CentralMomentBasedLbMethod` 
     """
     if isinstance(stencil, str):
-        stencil = get_stencil(stencil)
+        stencil = LBStencil(stencil)
     mom_to_rr_dict = OrderedDict(moment_to_relaxation_rate_dict)
     assert len(mom_to_rr_dict) == len(stencil), \
         "The number of moments has to be the same as the number of stencil entries"
 
-    density_velocity_computation = DensityVelocityComputation(stencil, compressible, force_model)
+    density_velocity_computation = DensityVelocityComputation(stencil, compressible,
+                                                              zero_centered_pdfs, force_model)
 
     moments = tuple(mom_to_rr_dict.keys())
     eq_values = get_moments_of_discrete_maxwellian_equilibrium(stencil, moments,
@@ -89,7 +93,8 @@ def create_with_discrete_maxwellian_eq_moments(stencil, moment_to_relaxation_rat
         return MomentBasedLbMethod(stencil, rr_dict, density_velocity_computation, force_model, moment_transform_class)
 
 
-def create_with_continuous_maxwellian_eq_moments(stencil, moment_to_relaxation_rate_dict, compressible=False,
+def create_with_continuous_maxwellian_eq_moments(stencil, moment_to_relaxation_rate_dict,
+                                                 compressible=False, zero_centered_pdfs=True,
                                                  force_model=None, equilibrium_order=2, c_s_sq=sp.Rational(1, 3),
                                                  central_moment_space=False,
                                                  moment_transform_class=None,
@@ -101,7 +106,7 @@ def create_with_continuous_maxwellian_eq_moments(stencil, moment_to_relaxation_r
     By using the continuous Maxwellian we automatically get a compressible model.
 
     Args:
-        stencil: nested tuple defining the discrete velocity space. See `get_stencil`
+        stencil: nested tuple defining the discrete velocity space. See `LBStencil`
         moment_to_relaxation_rate_dict: dict that has as many entries as the stencil. Each moment, which can be
                                         represented by an exponent tuple or in polynomial form
                                         (see `lbmpy.moments`), is mapped to a relaxation rate.
@@ -109,6 +114,8 @@ def create_with_continuous_maxwellian_eq_moments(stencil, moment_to_relaxation_r
                       where :math:`\rho_0` is chosen as one, and the first moment of the pdfs is :math:`\Delta \rho` .
                       This approximates the incompressible Navier-Stokes equations better than the standard
                       compressible model.
+        zero_centered_pdfs: If True the density is subtracted from the PDFs to center them around zero.
+                            This is important for simulations in single precision
         force_model: force model instance, or None if no external forces
         equilibrium_order: approximation order of macroscopic velocity :math:`\mathbf{u}` in the equilibrium
         c_s_sq: Speed of sound squared
@@ -123,11 +130,12 @@ def create_with_continuous_maxwellian_eq_moments(stencil, moment_to_relaxation_r
         :class:`lbmpy.methods.momentbased.CentralMomentBasedLbMethod` 
     """
     if isinstance(stencil, str):
-        stencil = get_stencil(stencil)
+        stencil = LBStencil(stencil)
     mom_to_rr_dict = OrderedDict(moment_to_relaxation_rate_dict)
     assert len(mom_to_rr_dict) == stencil.Q, "The number of moments has to be equal to the number of stencil entries"
     dim = stencil.D
-    density_velocity_computation = DensityVelocityComputation(stencil, compressible, force_model)
+    density_velocity_computation = DensityVelocityComputation(stencil, compressible,
+                                                              zero_centered_pdfs, force_model)
     moments = tuple(mom_to_rr_dict.keys())
 
     if compressible and central_moment_space:
@@ -191,7 +199,7 @@ def create_from_equilibrium(stencil, equilibrium, moment_to_relaxation_rate_dict
         force_model: see create_with_discrete_maxwellian_eq_moments
     """
     if isinstance(stencil, str):
-        stencil = get_stencil(stencil)
+        stencil = LBStencil(stencil)
     if any(isinstance(moment_to_relaxation_rate_dict, t) for t in (sp.Symbol, float, int)):
         moment_to_relaxation_rate_dict = {m: moment_to_relaxation_rate_dict
                                           for m in get_default_moment_set_for_stencil(stencil)}
@@ -212,7 +220,7 @@ def create_srt(stencil, relaxation_rate, maxwellian_moments=False, **kwargs):
     r"""Creates a single relaxation time (SRT) lattice Boltzmann model also known as BGK model.
 
     Args:
-        stencil: nested tuple defining the discrete velocity space. See :func:`lbmpy.stencils.get_stencil`
+        stencil: nested tuple defining the discrete velocity space. See :func:`lbmpy.stencils.LBStencil`
         relaxation_rate: relaxation rate (inverse of the relaxation time)
                         usually called :math:`\omega` in LBM literature
         maxwellian_moments: determines if the discrete or continuous maxwellian equilibrium is
@@ -222,7 +230,7 @@ def create_srt(stencil, relaxation_rate, maxwellian_moments=False, **kwargs):
         :class:`lbmpy.methods.momentbased.MomentBasedLbMethod` instance
     """
     if isinstance(stencil, str):
-        stencil = get_stencil(stencil)
+        stencil = LBStencil(stencil)
     moments = get_default_moment_set_for_stencil(stencil)
     rr_dict = OrderedDict([(m, relaxation_rate) for m in moments])
     if maxwellian_moments:
@@ -243,7 +251,7 @@ def create_trt(stencil, relaxation_rate_even_moments, relaxation_rate_odd_moment
     If unsure how to choose the odd relaxation rate, use the function :func:`lbmpy.methods.create_trt_with_magic_number`
     """
     if isinstance(stencil, str):
-        stencil = get_stencil(stencil)
+        stencil = LBStencil(stencil)
     moments = get_default_moment_set_for_stencil(stencil)
     rr_dict = OrderedDict([(m, relaxation_rate_even_moments if is_even(m) else relaxation_rate_odd_moments)
                            for m in moments])
@@ -260,7 +268,7 @@ def create_trt_with_magic_number(stencil, relaxation_rate, magic_number=sp.Ratio
     For possible parameters see :func:`lbmpy.methods.create_trt`
 
     Args:
-        stencil: nested tuple defining the discrete velocity space. See :func:`lbmpy.stencils.get_stencil`
+        stencil: nested tuple defining the discrete velocity space. See :func:`lbmpy.stencils.LBStencil`
         relaxation_rate: relaxation rate (inverse of the relaxation time)
                         usually called :math:`\omega` in LBM literature
         magic_number: magic number which is used to calculate the relxation rate for the odd moments.
@@ -278,7 +286,7 @@ def create_mrt_raw(stencil, relaxation_rates, maxwellian_moments=False, **kwargs
     Creates a MRT method using non-orthogonalized moments.
 
     Args:
-        stencil: nested tuple defining the discrete velocity space. See :func:`lbmpy.stencils.get_stencil`
+        stencil: nested tuple defining the discrete velocity space. See :func:`lbmpy.stencils.LBStencil`
         relaxation_rates: relaxation rates (inverse of the relaxation times) for each moment
         maxwellian_moments: determines if the discrete or continuous maxwellian equilibrium is
                         used to compute the equilibrium moments.
@@ -286,7 +294,7 @@ def create_mrt_raw(stencil, relaxation_rates, maxwellian_moments=False, **kwargs
         :class:`lbmpy.methods.momentbased.MomentBasedLbMethod` instance
     """
     if isinstance(stencil, str):
-        stencil = get_stencil(stencil)
+        stencil = LBStencil(stencil)
     moments = get_default_moment_set_for_stencil(stencil)
     rr_dict = OrderedDict(zip(moments, relaxation_rates))
     if maxwellian_moments:
@@ -301,7 +309,7 @@ def create_central_moment(stencil, relaxation_rates, nested_moments=None,
     Creates moment based LB method where the collision takes place in the central moment space.
 
     Args:
-        stencil: nested tuple defining the discrete velocity space. See :func:`lbmpy.stencils.get_stencil`
+        stencil: nested tuple defining the discrete velocity space. See :func:`lbmpy.stencils.LBStencil`
         relaxation_rates: relaxation rates (inverse of the relaxation times) for each moment
         maxwellian_moments: determines if the discrete or continuous maxwellian equilibrium is
                         used to compute the equilibrium moments.
@@ -309,7 +317,7 @@ def create_central_moment(stencil, relaxation_rates, nested_moments=None,
         :class:`lbmpy.methods.momentbased.CentralMomentBasedLbMethod` instance
     """
     if isinstance(stencil, str):
-        stencil = get_stencil(stencil)
+        stencil = LBStencil(stencil)
 
     dim = len(stencil)
 
@@ -415,7 +423,7 @@ def create_trt_kbc(dim, shear_relaxation_rate, higher_order_relaxation_rate, met
                        [omega_s] * len(shear_part) + \
                        [omega_h] * len(rest_part)
 
-    stencil = get_stencil("D2Q9") if dim == 2 else get_stencil("D3Q27")
+    stencil = LBStencil("D2Q9") if dim == 2 else LBStencil("D3Q27")
     all_moments = rho + velocity + shear_part + rest_part
     moment_to_rr = OrderedDict((m, rr) for m, rr in zip(all_moments, relaxation_rates))
 
@@ -434,7 +442,7 @@ def create_mrt_orthogonal(stencil, relaxation_rate_getter, maxwellian_moments=Fa
     To create a generic MRT method use `create_with_discrete_maxwellian_eq_moments`
 
     Args:
-        stencil: nested tuple defining the discrete velocity space. See `get_stencil`
+        stencil: nested tuple defining the discrete velocity space. See `LBStencil`
         relaxation_rate_getter: function getting a list of moments as argument, returning the associated relaxation
         maxwellian_moments: determines if the discrete or continuous maxwellian equilibrium is
                                                used to compute the equilibrium moments
@@ -445,7 +453,7 @@ def create_mrt_orthogonal(stencil, relaxation_rate_getter, maxwellian_moments=Fa
     """
     dim = len(stencil[0])
     if isinstance(stencil, str):
-        stencil = get_stencil(stencil)
+        stencil = LBStencil(stencil)
 
     if weighted is None and not nested_moments:
         raise ValueError("Please specify whether you want weighted or unweighted orthogonality using 'weighted='")
@@ -493,7 +501,7 @@ def mrt_orthogonal_modes_literature(stencil, is_weighted):
     This is for commonly used MRT models found in literature.
 
     Args:
-        stencil: nested tuple defining the discrete velocity space. See `get_stencil`
+        stencil: nested tuple defining the discrete velocity space. See `LBStencil`
         is_weighted: whether to use weighted or unweighted orthogonality
 
     MRT schemes as described in the following references are used
@@ -501,7 +509,7 @@ def mrt_orthogonal_modes_literature(stencil, is_weighted):
     x, y, z = MOMENT_SYMBOLS
     one = sp.Rational(1, 1)
 
-    if have_same_entries(stencil, get_stencil("D2Q9")) and is_weighted:
+    if have_same_entries(stencil, LBStencil("D2Q9")) and is_weighted:
         # Reference:
         # Duenweg, B., Schiller, U. D., & Ladd, A. J. (2007). Statistical mechanics of the fluctuating
         # lattice Boltzmann equation. Physical Review E, 76(3)
@@ -510,7 +518,7 @@ def mrt_orthogonal_modes_literature(stencil, is_weighted):
                        (3 * sq - 4) * x, (3 * sq - 4) * y, 9 * sq ** 2 - 15 * sq + 2]
         nested_moments = list(sort_moments_into_groups_of_same_order(all_moments).values())
         return nested_moments
-    elif have_same_entries(stencil, get_stencil("D3Q15")) and is_weighted:
+    elif have_same_entries(stencil, LBStencil("D3Q15")) and is_weighted:
         sq = x ** 2 + y ** 2 + z ** 2
         nested_moments = [
             [one, x, y, z],  # [0, 3, 5, 7]
@@ -520,7 +528,7 @@ def mrt_orthogonal_modes_literature(stencil, is_weighted):
             [3 * x ** 2 - sq, y ** 2 - z ** 2, x * y, y * z, x * z],  # [9, 10, 11, 12, 13]
             [x * y * z]
         ]
-    elif have_same_entries(stencil, get_stencil("D3Q19")) and is_weighted:
+    elif have_same_entries(stencil, LBStencil("D3Q19")) and is_weighted:
         # This MRT variant mentioned in the dissertation of Ulf Schiller
         # "Thermal fluctuations and boundary conditions in the lattice Boltzmann method" (2008), p. 24ff
         # There are some typos in the moment matrix on p.27
@@ -542,7 +550,7 @@ def mrt_orthogonal_modes_literature(stencil, is_weighted):
             [(2 * sq - 3) * (3 * x ** 2 - sq), (2 * sq - 3) * (y ** 2 - z ** 2)],  # [10, 12]
             [(y ** 2 - z ** 2) * x, (z ** 2 - x ** 2) * y, (x ** 2 - y ** 2) * z]  # [16, 17, 18]
         ]
-    elif have_same_entries(stencil, get_stencil("D3Q27")) and not is_weighted:
+    elif have_same_entries(stencil, LBStencil("D3Q27")) and not is_weighted:
         xsq, ysq, zsq = x ** 2, y ** 2, z ** 2
         all_moments = [
             sp.Rational(1, 1),  # 0
@@ -592,7 +600,7 @@ def cascaded_moment_sets_literature(stencil):
         stencil: can be D2Q9, D2Q19 or D3Q27
     """
     x, y, z = MOMENT_SYMBOLS
-    if have_same_entries(stencil, get_stencil("D2Q9")):
+    if have_same_entries(stencil, LBStencil("D2Q9")):
         #   Cumulants of the D2Q9 stencil up to third order are equal to
         #   the central moments; only the fourth-order cumulant x**2 * y**2
         #   has a more complicated form. They can be arranged into groups
@@ -613,7 +621,7 @@ def cascaded_moment_sets_literature(stencil):
             [x**2 * y**2]
         ]
 
-    elif have_same_entries(stencil, get_stencil("D3Q15")):
+    elif have_same_entries(stencil, LBStencil("D3Q15")):
         #   D3Q15 central moments by Premnath et al. https://arxiv.org/pdf/1202.6081.pdf.
         return [
             [sp.sympify(1)],                # density is conserved
@@ -636,7 +644,7 @@ def cascaded_moment_sets_literature(stencil):
             [x ** 2 * y ** 2 + x ** 2 * z ** 2 + y ** 2 * z ** 2]
         ]
 
-    elif have_same_entries(stencil, get_stencil("D3Q19")):
+    elif have_same_entries(stencil, LBStencil("D3Q19")):
         #   D3Q19 cumulants are obtained by pruning the D3Q27 cumulant set as
         #   described by Coreixas, 2019.
         return [
@@ -665,7 +673,7 @@ def cascaded_moment_sets_literature(stencil):
             [x ** 2 * y ** 2 + x ** 2 * z ** 2 + y ** 2 * z ** 2]
         ]
 
-    elif have_same_entries(stencil, get_stencil("D3Q27")):
+    elif have_same_entries(stencil, LBStencil("D3Q27")):
         #   Cumulants grouped to preserve rotational invariance as described by Geier et al, 2015
         return [
             [sp.sympify(1)],                # density is conserved
@@ -712,7 +720,8 @@ def cascaded_moment_sets_literature(stencil):
 # ----------------------------------------- Cumulant method creators ---------------------------------------------------
 
 
-def create_centered_cumulant_model(stencil, cumulant_to_rr_dict, force_model=None,
+def create_centered_cumulant_model(stencil, cumulant_to_rr_dict,
+                                   zero_centered_pdfs=True, force_model=None,
                                    equilibrium_order=None, c_s_sq=sp.Rational(1, 3),
                                    galilean_correction=False,
                                    central_moment_transform_class=PdfsToCentralMomentsByShiftMatrix,
@@ -720,10 +729,12 @@ def create_centered_cumulant_model(stencil, cumulant_to_rr_dict, force_model=Non
     r"""Creates a cumulant lattice Boltzmann model.
 
     Args:
-        stencil: nested tuple defining the discrete velocity space. See :func:`lbmpy.stencils.get_stencil`
+        stencil: nested tuple defining the discrete velocity space. See :func:`lbmpy.stencils.LBStencil`
         cumulant_to_rr_dict: dict that has as many entries as the stencil. Each cumulant, which can be
                              represented by an exponent tuple or in polynomial form is mapped to a relaxation rate.
                              See `cascaded_moment_sets_literature`
+        zero_centered_pdfs: If True the density is subtracted from the PDFs to center them around zero.
+                            This is important for simulations in single precision
         force_model: force model used for the collision. For cumulant LB method a good choice is
                      `lbmpy.methods.centeredcumulant.CenteredCumulantForceModel`
         equilibrium_order: approximation order of macroscopic velocity :math:`\mathbf{u}` in the equilibrium
@@ -741,14 +752,15 @@ def create_centered_cumulant_model(stencil, cumulant_to_rr_dict, force_model=Non
 
     one = sp.Integer(1)
     if isinstance(stencil, str):
-        stencil = get_stencil(stencil)
+        stencil = LBStencil(stencil)
 
     assert len(cumulant_to_rr_dict) == len(
         stencil), "The number of moments has to be equal to the number of stencil entries"
     dim = len(stencil[0])
 
-    # CQC
-    cqc = DensityVelocityComputation(stencil, True, force_model=force_model)
+    # CQC: compressible is fixed to True because cumulants are only implemented in compressible version at the moment
+    compressible = True
+    cqc = DensityVelocityComputation(stencil, compressible, zero_centered_pdfs, force_model=force_model)
     density_symbol = cqc.zeroth_order_moment_symbol
     velocity_symbols = cqc.first_order_moment_symbols
 
@@ -779,7 +791,7 @@ def create_with_polynomial_cumulants(stencil, relaxation_rates, cumulant_groups,
     r"""Creates a cumulant lattice Boltzmann model based on a default polynomial set.
 
     Args:
-        stencil: nested tuple defining the discrete velocity space. See :func:`lbmpy.stencils.get_stencil`
+        stencil: nested tuple defining the discrete velocity space. See :func:`lbmpy.stencils.LBStencil`
         relaxation_rates: relaxation rates for each cumulant group. If None are provided a list of symbolic relaxation
                           rates is created and used. If only a list with one entry is provided this relaxation rate is
                           used for determine the viscosity of the simulation. All other cumulants are relaxed with one.
@@ -793,7 +805,7 @@ def create_with_polynomial_cumulants(stencil, relaxation_rates, cumulant_groups,
         :class:`lbmpy.methods.centeredcumulant.CenteredCumulantBasedLbMethod` instance
     """
     if isinstance(stencil, str):
-        stencil = get_stencil(stencil)
+        stencil = LBStencil(stencil)
 
     cumulant_to_rr_dict = OrderedDict()
     rr_iter = iter(relaxation_rates)
@@ -816,7 +828,7 @@ def create_with_monomial_cumulants(stencil, relaxation_rates, **kwargs):
     r"""Creates a cumulant lattice Boltzmann model based on a default polinomial set.
 
     Args:
-        stencil: nested tuple defining the discrete velocity space. See :func:`lbmpy.stencils.get_stencil`
+        stencil: nested tuple defining the discrete velocity space. See :func:`lbmpy.stencils.LBStencil`
         relaxation_rates: relaxation rates for each cumulant group. If None are provided a list of symbolic relaxation
                           rates is created and used. If only a list with one entry is provided this relaxation rate is
                           used for determine the viscosity of the simulation. All other cumulants are relaxed with one.
@@ -829,7 +841,7 @@ def create_with_monomial_cumulants(stencil, relaxation_rates, **kwargs):
     """
 
     if isinstance(stencil, str):
-        stencil = get_stencil(stencil)
+        stencil = LBStencil(stencil)
 
     dim = len(stencil[0])
 
@@ -867,7 +879,7 @@ def create_with_default_polynomial_cumulants(stencil, relaxation_rates, **kwargs
     """
 
     if isinstance(stencil, str):
-        stencil = get_stencil(stencil)
+        stencil = LBStencil(stencil)
 
     # Get polynomial groups
     cumulant_groups = cascaded_moment_sets_literature(stencil)
diff --git a/lbmpy/methods/momentbased/centralmomentbasedmethod.py b/lbmpy/methods/momentbased/centralmomentbasedmethod.py
index a84b8b5a..c2bf9dc8 100644
--- a/lbmpy/methods/momentbased/centralmomentbasedmethod.py
+++ b/lbmpy/methods/momentbased/centralmomentbasedmethod.py
@@ -86,6 +86,14 @@ class CentralMomentBasedLbMethod(AbstractLbMethod):
     def relaxation_rates(self):
         return tuple([e.relaxation_rate for e in self._moment_to_relaxation_info_dict.values()])
 
+    @property
+    def compressible(self):
+        return self._conserved_quantity_computation.compressible
+
+    @property
+    def zero_centered_pdfs(self):
+        return self._conserved_quantity_computation.zero_centered_pdfs
+
     @property
     def zeroth_order_equilibrium_moment_symbol(self, ):
         return self._conserved_quantity_computation.zeroth_order_moment_symbol
diff --git a/lbmpy/methods/momentbased/entropic_eq_srt.py b/lbmpy/methods/momentbased/entropic_eq_srt.py
index 903005e0..c1a26dc5 100644
--- a/lbmpy/methods/momentbased/entropic_eq_srt.py
+++ b/lbmpy/methods/momentbased/entropic_eq_srt.py
@@ -24,6 +24,14 @@ class EntropicEquilibriumSRT(AbstractLbMethod):
     def conserved_quantity_computation(self):
         return self._cqc
 
+    @property
+    def compressible(self):
+        return self._cqc.compressible
+
+    @property
+    def zero_centered_pdfs(self):
+        return self._cqc.zero_centered_pdfs
+
     @property
     def weights(self):
         return self._weights
diff --git a/lbmpy/methods/momentbased/momentbasedmethod.py b/lbmpy/methods/momentbased/momentbasedmethod.py
index 8926669a..5d4bda19 100644
--- a/lbmpy/methods/momentbased/momentbasedmethod.py
+++ b/lbmpy/methods/momentbased/momentbasedmethod.py
@@ -58,6 +58,14 @@ class MomentBasedLbMethod(AbstractLbMethod):
     def conserved_quantity_computation(self):
         return self._conservedQuantityComputation
 
+    @property
+    def compressible(self):
+        return self._conservedQuantityComputation.compressible
+
+    @property
+    def zero_centered_pdfs(self):
+        return self._conservedQuantityComputation.zero_centered_pdfs
+
     @property
     def moments(self):
         return tuple(self._momentToRelaxationInfoDict.keys())
@@ -71,11 +79,11 @@ class MomentBasedLbMethod(AbstractLbMethod):
         return tuple([e.relaxation_rate for e in self._momentToRelaxationInfoDict.values()])
 
     @property
-    def zeroth_order_equilibrium_moment_symbol(self, ):
+    def zeroth_order_equilibrium_moment_symbol(self):
         return self._conservedQuantityComputation.zeroth_order_moment_symbol
 
     @property
-    def first_order_equilibrium_moment_symbols(self, ):
+    def first_order_equilibrium_moment_symbols(self):
         return self._conservedQuantityComputation.first_order_moment_symbols
 
     @property
-- 
GitLab