Skip to content
Snippets Groups Projects
Commit b8ea0f9e authored by Markus Holzer's avatar Markus Holzer
Browse files

Added comment about rr setter

parent aab7bc75
No related branches found
No related tags found
1 merge request!100Adaption to new API
%% Cell type:code id: tags:
``` python
from lbmpy.session import *
```
%% Cell type:markdown id: tags:
# Tutorial 03: Defining LB methods in *lbmpy*
## A) General Form
%% Cell type:markdown id: tags:
The lattice Boltzmann equation in its most general form is:
$$f_q(\mathbf{x} + \mathbf{c}_q \delta t, t+\delta t) = K\left( f_q(\mathbf{x}, t) \right)$$
with a discrete velocity set $\mathbf{c}_q$ (stencil) and a generic collision operator $K$.
So a lattice Boltzmann method can be fully defined by picking a stencil and a collision operator.
The collision operator $K$ has the following structure:
- Transformation of particle distribution function $f$ into collision space. This transformation has to be invertible and may be nonlinear.
- The collision operation is an convex combination of the pdf representation in collision space $c$ and some equilibrium vector $c^{(eq)}$. This equilibrium can also be defined in physical space, then $c^{(eq)} = C( f^{(eq)} ) $. The convex combination is done elementwise using a diagonal matrix $S$ where the diagonal entries are the relaxation rates.
- After collision, the collided state $c'$ is transformed back into physical space
![](../img/collision.svg)
The full collision operator is:
$$K(f) = C^{-1}\left( (I-S)C(f) + SC(f^{(eq}) \right)$$
or
$$K(f) = C^{-1}\left( C(f) - S (C(f) - C(f^{(eq})) \right)$$
%% Cell type:markdown id: tags:
## B) Moment-based relaxation
The most commonly used LBM collision operator is the multi relaxation time (MRT) collision.
In MRT methods the collision space is spanned by moments of the distribution function. This is a very natural approach, since the pdf moments are the quantities that go into the Chapman Enskog analysis that is used to show that LB methods can solve the Navier Stokes equations. Also the lower order moments correspond to the macroscopic quantities of interest (density/pressure, velocity, shear rates, heat flux). Furthermore the transformation to collision space is linear in this case, simplifying the collision equations:
$$K(f) = C^{-1}\left( C(f) - S (C(f) - C(f^{(eq})) \right)$$
$$K(f) = f - \underbrace{ C^{-1}SC}_{A}(f - f^{(eq)})$$
in *lbmpy* the following formulation is used, since it is more natural to define the equilibrium in moment-space instead of physical space:
$$K(f) = f - C^{-1}S(Cf - c^{(eq)})$$
%% Cell type:markdown id: tags:
### Use a pre-defined method
Lets create a moment-based method in *lbmpy* and see how the moment transformation $C$ and the relaxation rates that comprise the diagonal matrix $S$ can be defined. We start with a function that creates a basic MRT model.
Don't use this for real simulations, there orthogonalized MRT methods should be used, as discussed in the next section.
%% Cell type:code id: tags:
``` python
from lbmpy.creationfunctions import create_lb_method
lbm_config = LBMConfig(stencil=Stencil.D2Q9, method=Method.MRT_RAW)
method = create_lb_method(lbm_config=lbm_config)
# check also method='srt', 'trt', 'mrt'
method
```
%% Output
<lbmpy.methods.momentbased.momentbasedmethod.MomentBasedLbMethod at 0x10300dfd0>
<lbmpy.methods.momentbased.momentbasedmethod.MomentBasedLbMethod at 0x107ac7a30>
%% Cell type:markdown id: tags:
The first column labeled "Moment" defines the collision space and thus the transformation matrix $C$.
The remaining columns specify the equilibrium vector in moment space $c^{(eq)}$ and the corresponding relaxation rate.
Each row of the "Moment" column defines one row of $C$. In the next cells this matrix and the discrete velocity set (stencil) of our method are shown. Check for example the second last row of the table $x^2 y$: In the corresponding second last row of the moment matrix $C$ where each column stands for a lattice velocity (for ordering visualized stencil below) and each entry is the expression $x^2 y$ where $x$ and $y$ are the components of the lattice velocity.
In general the transformation matrix $C_{iq}$ is defined as;
$$c_i = C_{iq} f_q = \sum_q m_i(c_q)$$
where $m_i(c_q)$ is the $i$'th moment polynomial where $x$ and $y$ are substituted with the components of the $q$'th lattice velocity
%% Cell type:code id: tags:
``` python
# Transformation matrix C
method.moment_matrix
```
%% Output
$\displaystyle \left[\begin{matrix}1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1\\0 & 0 & 0 & -1 & 1 & -1 & 1 & -1 & 1\\0 & 1 & -1 & 0 & 0 & 1 & 1 & -1 & -1\\0 & 0 & 0 & 1 & 1 & 1 & 1 & 1 & 1\\0 & 1 & 1 & 0 & 0 & 1 & 1 & 1 & 1\\0 & 0 & 0 & 0 & 0 & -1 & 1 & 1 & -1\\0 & 0 & 0 & 0 & 0 & 1 & 1 & -1 & -1\\0 & 0 & 0 & 0 & 0 & -1 & 1 & -1 & 1\\0 & 0 & 0 & 0 & 0 & 1 & 1 & 1 & 1\end{matrix}\right]$
⎡1 1 1 1 1 1 1 1 1 ⎤
⎢ ⎥
⎢0 0 0 -1 1 -1 1 -1 1 ⎥
⎢ ⎥
⎢0 1 -1 0 0 1 1 -1 -1⎥
⎢ ⎥
⎢0 0 0 1 1 1 1 1 1 ⎥
⎢ ⎥
⎢0 1 1 0 0 1 1 1 1 ⎥
⎢ ⎥
⎢0 0 0 0 0 -1 1 1 -1⎥
⎢ ⎥
⎢0 0 0 0 0 1 1 -1 -1⎥
⎢ ⎥
⎢0 0 0 0 0 -1 1 -1 1 ⎥
⎢ ⎥
⎣0 0 0 0 0 1 1 1 1 ⎦
%% Cell type:code id: tags:
``` python
method.stencil.plot()
```
%% Output
%% Cell type:code id: tags:
``` python
method.stencil
```
%% Output
<lbmpy.stencils.LBStencil at 0x12b3b06d0>
<lbmpy.stencils.LBStencil at 0x13ae6b670>
%% Cell type:markdown id: tags:
### Orthogonal MRTs
For a real MRT method, the moments should be orthogonalized.
One can either orthogonalize using the standard scalar product or a scalar product that is weighted with the lattice weights. If unsure, use the weighted version.
The next cell shows how to get both orthogonalized MRT versions in lbmpy.
%% Cell type:code id: tags:
``` python
lbm_config = LBMConfig(stencil=Stencil.D2Q9, method=Method.MRT, weighted=True)
rr = [sp.Symbol('omega_shear'), sp.Symbol('omega_bulk'), sp.Symbol('omega_3'), sp.Symbol('omega_4')]
lbm_config = LBMConfig(stencil=Stencil.D2Q9, method=Method.MRT, weighted=True,
relaxation_rates=rr)
weighted_ortho_mrt = create_lb_method(lbm_config=lbm_config)
weighted_ortho_mrt
```
%% Output
<lbmpy.methods.momentbased.momentbasedmethod.MomentBasedLbMethod at 0x12b4ce550>
<lbmpy.methods.momentbased.momentbasedmethod.MomentBasedLbMethod at 0x13ae91df0>
%% Cell type:code id: tags:
``` python
lbm_config = LBMConfig(stencil=Stencil.D2Q9, method=Method.MRT, weighted=False)
lbm_config = LBMConfig(stencil=Stencil.D2Q9, method=Method.MRT, weighted=False,
relaxation_rates=rr)
ortho_mrt = create_lb_method(lbm_config=lbm_config)
ortho_mrt
```
%% Output
<lbmpy.methods.momentbased.momentbasedmethod.MomentBasedLbMethod at 0x12c55ae50>
<lbmpy.methods.momentbased.momentbasedmethod.MomentBasedLbMethod at 0x13accd610>
%% Cell type:markdown id: tags:
One can check if a method is orthogonalized:
%% Cell type:code id: tags:
``` python
ortho_mrt.is_orthogonal, weighted_ortho_mrt.is_weighted_orthogonal
```
%% Output
(True, True)
%% Cell type:markdown id: tags:
Note here how the relaxation rate for each moment is set. By default, a single relaxation rate per moment group is assigned. With a moment group, we mean here moment polynomials of the same order. However, the second-order moments are split between the shear and bulk moments (defining the shear and bulk viscosity). Thus the first two relaxation rates in the `rr`-list are used for these.
If only a single relaxation rate would be defined, it is applied to the shear moments, and all other higher-order moments are set to one. This means the PDFs are directly fixed to the equilibrium in the collision process. Furthermore, the relaxation rate for conserved moments can be chosen freely as they are conserved in the collision. Thus by default, we set it to zero.
It is also possible to define an individual relaxation rate per moment. In this case, `Q` relaxation rates need to be defined, where `Q` is the number of moments.
%% Cell type:markdown id: tags:
### Central moment lattice Boltzmann methods
%% Cell type:markdown id: tags:
Another popular method is the cascaded lattice Boltzmann method. The cascaded LBM increases the numerical stability by shifting the collision step to the central moment space. Thus it is applied in the non-moving frame and achieves a better Galilean invariance. Typically the central moment collision operator is derived for compressible LB methods, and a higher-order equilibrium is used. Although incompressible LB methods with a second-order equilibrium can be derived with lbmpy, it violates the Galilean invariance and thus reduces the advantages of the method.
%% Cell type:code id: tags:
``` python
lbm_config = LBMConfig(stencil=Stencil.D2Q9, method=Method.CENTRAL_MOMENT, equilibrium_order=4,
compressible=True)
compressible=True, relaxation_rates=rr)
central_moment_method = create_lb_method(lbm_config)
central_moment_method
```
%% Output
<lbmpy.methods.momentbased.centralmomentbasedmethod.CentralMomentBasedLbMethod at 0x12b422880>
<lbmpy.methods.momentbased.centralmomentbasedmethod.CentralMomentBasedLbMethod at 0x13ad7e460>
%% Cell type:markdown id: tags:
The shift to the central moment space is done by applying a so-called shift matrix. Usually, this introduces a high numerical overhead. This problem is solved with lbmpy because each transformation stage can be specifically optimised individually. Therefore, it is possible to derive a CLBM with only a little numerical overhead.
%% Cell type:code id: tags:
``` python
central_moment_method.shift_matrix
```
%% Output
$\displaystyle \left[\begin{matrix}1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\- u_{0} & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\- u_{1} & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\u_{0} u_{1} & - u_{1} & - u_{0} & 1 & 0 & 0 & 0 & 0 & 0\\u_{0}^{2} - u_{1}^{2} & - 2 u_{0} & 2 u_{1} & 0 & 1 & 0 & 0 & 0 & 0\\u_{0}^{2} + u_{1}^{2} & - 2 u_{0} & - 2 u_{1} & 0 & 0 & 1 & 0 & 0 & 0\\- u_{0}^{2} u_{1} & 2 u_{0} u_{1} & u_{0}^{2} & - 2 u_{0} & - \frac{u_{1}}{2} & - \frac{u_{1}}{2} & 1 & 0 & 0\\- u_{0} u_{1}^{2} & u_{1}^{2} & 2 u_{0} u_{1} & - 2 u_{1} & \frac{u_{0}}{2} & - \frac{u_{0}}{2} & 0 & 1 & 0\\u_{0}^{2} u_{1}^{2} & - 2 u_{0} u_{1}^{2} & - 2 u_{0}^{2} u_{1} & 4 u_{0} u_{1} & - \frac{u_{0}^{2}}{2} + \frac{u_{1}^{2}}{2} & \frac{u_{0}^{2}}{2} + \frac{u_{1}^{2}}{2} & - 2 u_{1} & - 2 u_{0} & 1\end{matrix}\right]$
⎡ 1 0 0 0 0 0 0 0 0⎤
⎢ ⎥
⎢ -u₀ 1 0 0 0 0 0 0 0⎥
⎢ ⎥
⎢ -u₁ 0 1 0 0 0 0 0 0⎥
⎢ ⎥
⎢ u₀⋅u₁ -u₁ -u₀ 1 0 0 0 0 0⎥
⎢ ⎥
⎢ 2 2 ⎥
⎢u₀ - u₁ -2⋅u₀ 2⋅u₁ 0 1 0 0 0 0⎥
⎢ ⎥
⎢ 2 2 ⎥
⎢u₀ + u₁ -2⋅u₀ -2⋅u₁ 0 0 1 0 0 0⎥
⎢ ⎥
⎢ 2 2 -u₁ -u₁ ⎥
⎢ -u₀ ⋅u₁ 2⋅u₀⋅u₁ u₀ -2⋅u₀ ──── ──── 1 0 0⎥
⎢ 2 2 ⎥
⎢ ⎥
⎢ 2 2 u₀ -u₀ ⎥
⎢ -u₀⋅u₁ u₁ 2⋅u₀⋅u₁ -2⋅u₁ ── ──── 0 1 0⎥
⎢ 2 2 ⎥
⎢ ⎥
⎢ 2 2 2 2 ⎥
⎢ 2 2 2 2 u₀ u₁ u₀ u₁ ⎥
⎢ u₀ ⋅u₁ -2⋅u₀⋅u₁ -2⋅u₀ ⋅u₁ 4⋅u₀⋅u₁ - ─── + ─── ─── + ─── -2⋅u₁ -2⋅u₀ 1⎥
⎣ 2 2 2 2 ⎦
%% Cell type:markdown id: tags:
### Define custom MRT method
To choose custom values for the left moment column one can pass a nested list of moments.
Moments that should be relaxed with the same paramter are grouped together.
*lbmpy* also comes with a few templates for this list taken from literature:
%% Cell type:code id: tags:
``` python
from lbmpy.methods import mrt_orthogonal_modes_literature
from lbmpy.stencils import get_stencil
from lbmpy.moments import MOMENT_SYMBOLS
x, y, z = MOMENT_SYMBOLS
moments = mrt_orthogonal_modes_literature(LBStencil(Stencil.D2Q9), is_weighted=True)
moments
```
%% Output
$\displaystyle \left[ \left[ 1\right], \ \left[ x, \ y\right], \ \left[ 3 x^{2} + 3 y^{2} - 2, \ x^{2} - y^{2}, \ x y\right], \ \left[ x \left(3 x^{2} + 3 y^{2} - 4\right), \ y \left(3 x^{2} + 3 y^{2} - 4\right)\right], \ \left[ - 15 x^{2} - 15 y^{2} + 9 \left(x^{2} + y^{2}\right)^{2} + 2\right]\right]$
⎡ ⎡ 2
⎢ ⎡ 2 2 2 2 ⎤ ⎡ ⎛ 2 2 ⎞ ⎛ 2 2 ⎞⎤ ⎢ 2 2 ⎛ 2 2⎞
⎣[1], [x, y], ⎣3⋅x + 3⋅y - 2, x - y , x⋅y⎦, ⎣x⋅⎝3⋅x + 3⋅y - 4⎠, y⋅⎝3⋅x + 3⋅y - 4⎠⎦, ⎣- 15⋅x - 15⋅y + 9⋅⎝x + y ⎠
⎤⎤
⎥⎥
+ 2⎦⎦
%% Cell type:markdown id: tags:
This nested moment list can be passed to `create_lb_method`:
%% Cell type:code id: tags:
``` python
create_lb_method(LBMConfig(stencil=Stencil.D2Q9, method=Method.MRT, nested_moments=moments))
create_lb_method(LBMConfig(stencil=Stencil.D2Q9, method=Method.MRT, nested_moments=moments,
relaxation_rates=rr))
```
%% Output
<lbmpy.methods.momentbased.momentbasedmethod.MomentBasedLbMethod at 0x12c757220>
<lbmpy.methods.momentbased.momentbasedmethod.MomentBasedLbMethod at 0x13acdf850>
%% Cell type:markdown id: tags:
If one needs to also specify custom equilibrium moments the following approach can be used
%% Cell type:code id: tags:
``` python
rho = sp.symbols("rho")
u = sp.symbols("u_:3")
omega = sp.symbols("omega_:4")
method_table = [
# Conserved moments
(1, rho, 0 ),
(x, u[0], 0 ),
(y, u[1], 0 ),
# Shear moments
(x*y, u[0]*u[1], omega[0]),
(x**2-y**2, u[0]**2 - u[1]**2, omega[0]),
(x**2+y**2, 2*rho/3 + u[0]**2 + u[1]**2, omega[1]),
# Higher order
(x * y**2, u[0]/3, omega[2]),
(x**2 * y, u[1]/3, omega[2]),
(x**2 * y**2, rho/9 + u[0]**2/3 + u[1]**2/3, omega[3]),
]
method = create_generic_mrt(LBStencil(Stencil.D2Q9), method_table)
method
```
%% Output
<lbmpy.methods.momentbased.momentbasedmethod.MomentBasedLbMethod at 0x12c79daf0>
<lbmpy.methods.momentbased.momentbasedmethod.MomentBasedLbMethod at 0x13b22f5b0>
%% Cell type:markdown id: tags:
Instead of manually defining all entries in the method table, *lbmpy* has functions to fill the table according to a specific pattern. For example:
- for a full stencil (D2Q9, D3Q27) there exist exactly 9 or 27 linearly independent moments. These can either be taken as they are, or orthogonalized using Gram-Schmidt, weighted Gram-Schmidt or a Hermite approach
- equilibrium values can be computed from the standard discrete equilibrium of order 1,2 or 3. Alternatively they can also be computed as continuous moments of a Maxwellian distribution
One option is to start with one of *lbmpy*'s built-in methods and modify it with `create_lb_method_from_existing`.
In the next cell we fix the fourth order relaxation rate to a constant, by writing a function that defines how to alter each row of the collision table. This is for demonstration only, of course we could have done it right away when passing in the collision table.
%% Cell type:code id: tags:
``` python
def modification_func(moment, eq, rate):
if rate == omega[3]:
return moment, eq, 1.0
return moment, eq, rate
method = create_lb_method_from_existing(method, modification_func)
method
```
%% Output
<lbmpy.methods.momentbased.momentbasedmethod.MomentBasedLbMethod at 0x12c5bd670>
<lbmpy.methods.momentbased.momentbasedmethod.MomentBasedLbMethod at 0x13b212460>
%% Cell type:markdown id: tags:
Our customized method can be directly passed into one of the scenarios. We can for example set up a channel flow with it. Since we used symbols as relaxation rates, we have to pass them in as `kernel_params`.
%% Cell type:code id: tags:
``` python
ch = create_channel(domain_size=(100, 30), lb_method=method, u_max=0.05,
kernel_params={'omega_0': 1.8, 'omega_1': 1.4, 'omega_2': 1.5})
ch.run(500)
plt.figure(dpi=200)
plt.vector_field(ch.velocity[:, :]);
```
%% Output
%% Cell type:markdown id: tags:
#### Bonus: Automatic analysis
Above we have created a non-orthogonal MRT, where the shear viscosity and bulk viscosity can be independently controlled. For moment-based methods, *lbmpy* also offers an automatic Chapman Enskog analysis that can find the relation between viscosity and relaxation rate(s):
%% Cell type:code id: tags:
``` python
from lbmpy.chapman_enskog import ChapmanEnskogAnalysis
analysis = ChapmanEnskogAnalysis(method)
analysis.get_dynamic_viscosity()
```
%% Output
$\displaystyle - \frac{\omega_{0} - 2}{6 \omega_{0}}$
-(ω₀ - 2)
──────────
6⋅ω₀
%% Cell type:code id: tags:
``` python
analysis.get_bulk_viscosity()
```
%% Output
$\displaystyle - \frac{1}{9} - \frac{1}{3 \omega_{1}} + \frac{5}{9 \omega_{0}}$
1 1 5
- ─ - ──── + ────
9 3⋅ω₁ 9⋅ω₀
......
%% Cell type:code id: tags:
 
``` python
from lbmpy.session import *
```
 
%% Cell type:markdown id: tags:
 
# Tutorial 04: The Cumulant Lattice Boltzmann Method in lbmpy
 
## A) Principles of the centered cumulant collision operator
 
Recently, an advanced Lattice Boltzmann collision operator based on relaxation in cumulant space has gained popularity. Similar to moments, cumulants are statistical quantities inherent to a probability distribution. A significant advantage of the cumulants is that they are statistically independent by construction. Moments can be defined by using the so-called moment generating function, which for the discrete particle distribution of the LB equation is stated as
 
$$
\begin{align}
M( \vec{X} ) =
\sum_i f_i
\exp \left(
\vec{c}_i \cdot \vec{X}
\right)
\end{align}
$$
 
The raw moments $m_{\alpha \beta \gamma}$ can be expressed as its derivatives, evaluated at zero:
 
$$
\begin{align}
m_{\alpha \beta \gamma}
=
\left.
\frac{\partial^{\alpha}}{\partial X^{\alpha}}
\frac{\partial^{\beta}}{\partial Y^{\beta}}
\frac{\partial^{\gamma}}{\partial Z^{\gamma}}
M(X, Y, Z)
\right\vert_{\vec{X}=0}
\end{align}
$$
 
The cumulant-generating function is defined as the natural logarithm of this moment-generating function, and the cumulants $c_{\alpha \beta \gamma}$ are defined as its derivatives evaluated at zero:
 
$$
\begin{align}
C(\vec{X}) :=& \, \log ( M(\vec{X}) ) \\
c_{\alpha \beta \gamma}
=& \,
\left.
\frac{\partial^{\alpha}}{\partial X^{\alpha}}
\frac{\partial^{\beta}}{\partial Y^{\beta}}
\frac{\partial^{\gamma}}{\partial Z^{\gamma}}
C(X, Y, Z)
\right\vert_{\vec{X}=0}
\end{align}
$$
 
Other than with moments, there is no straightforward way to express cumulants in terms of the populations. However, their generating functions can be related to allowing the computation of cumulants from both raw and central moments, computed from populations. In lbmpy, the transformations from populations to cumulants and back are implemented using central moments as intermediaries. This is done for two primary reasons:
1. All cumulants of orders 2 and 3 are equal to their corresponding central moments, up to the density $\rho$ as a proportionality factor.
2. The conserved modes of the first order, which correspond to momentum, are relaxed in central moment space to allow for a more efficient implicit forcing scheme.
 
The central moment-generating function $K$ can be related to the moment-generating function through $K(\vec{X}) = \exp( - \vec{X} \cdot \vec{u} ) M(\vec{X})$. It is possible to recombine the equation with the definition of the cumulant-generating function
 
$$
\begin{align}
C( \vec{X} ) = \vec{X} \cdot \vec{u} + \log( K( \vec{X} ) ).
\end{align}
$$
 
Derivatives of $C$ can thus be expressed in terms of derivatives of $K$, directly yielding equations of the cumulants in terms of central moments.
 
In the cumulant collision operator, forces can be applied through a simple forcing scheme which in lbmpy is called *implicit forcing*. Ony the force contributions to the first-order (momentum) modes are considered. When the first-order central moments are taken in the frame of reference shifted by $F/2$, they trail behind zero by just that much. Forcing can be applied by first adding half the force *before* collision, and then adding the remaining half *after* collision. Due to this, the first-order central moments entering the cumulant equations are always zero. By applying the force already in central-moment space, the cumulant equations are simplified significantly as no forces need to be taken into account. The pre- and post-collision momentum central moments take the form:
 
$$
\begin{align}
\kappa_{100} &= - \frac{F_x}{2}, \\
\kappa^{\ast}_{100} &= \kappa_{100} + F_x = \frac{F_x}{2}
\end{align}
$$
 
In total, through forcing, the first central moments change sign. This is equivalent to relaxation with a relaxation rate $\omega = 2$. For this reason, lbmpy's implementation of the cumulant LBM calculates the collision of the momentum modes in central moment space, and the default force model overrides their relaxation rate by setting it to 2.
 
%% Cell type:markdown id: tags:
 
## B) Method Creation
 
Using the `create_lb_method` interface, creation of a cumulant-based lattice Boltzmann method in lbmpy is straightforward. Cumulants can either be relaxed in their raw (monomial forms) or in polynomial combinations. Both variants are available as predefined setups.
 
### Relaxation of Monomial Cumulants
 
Monomial cumulant relaxation is available through the `method="monomial_cumulant"` parameter setting. This variant requires fewer transformation steps and is a little faster than polynomial relaxation, but it does not allow separation of bulk and shear viscosity. Default monomial cumulant sets are available for the D2Q9, D3Q19 and D3Q27 stencils. It is also possible to define a custom set of monomial cumulants.
 
When creating a monomial cumulant method, one option is to specify only a single relaxation rate which will then be assigned to all cumulants related to the shear viscosity. In this case, all other non-conserved cumulants will be relaxed to equilibrium. Alternatively, individual relaxation rates for all non-conserved cumulants can be specified. The conserved cumulants are set to zero by default to save computational cost. They can be adjusted with `set_zeroth_moment_relaxation_rate`, `set_first_moment_relaxation_rate` and `set_conserved_moments_relaxation_rate`.
 
%% Cell type:code id: tags:
 
``` python
lbm_config = LBMConfig(method=Method.MONOMIAL_CUMULANT, relaxation_rate=sp.Symbol('omega_v'))
method_monomial = create_lb_method(lbm_config=lbm_config)
method_monomial
```
 
%% Output
 
<lbmpy.methods.centeredcumulant.centeredcumulantmethod.CenteredCumulantBasedLbMethod at 0x103b14880>
<lbmpy.methods.centeredcumulant.centeredcumulantmethod.CenteredCumulantBasedLbMethod at 0x127652c40>
 
%% Cell type:markdown id: tags:
 
### Relaxation of Polynomial Cumulants
 
By setting `method="cumulant"`, a set of default polynomial cumulants is chosen to be relaxed. Those cumulants are taken from literature and assembled into groups selected to enforce rotational invariance (see: `lbmpy.methods.centeredcumulant.centered_cumulants`). Default polynomial groups are available for the D2Q9, D3Q19 and D3Q27 stencils. As before it is possible to specify only a single relaxation rate assigned to the moments governing the shear viscosity. All other relaxation rates are then automatically set to one.
 
%% Cell type:code id: tags:
 
``` python
lbm_config = LBMConfig(method=Method.CUMULANT, relaxation_rate=sp.Symbol('omega_v'))
method_polynomial = create_lb_method(lbm_config=lbm_config)
method_polynomial
```
 
%% Output
 
<lbmpy.methods.centeredcumulant.centeredcumulantmethod.CenteredCumulantBasedLbMethod at 0x103b14a30>
<lbmpy.methods.centeredcumulant.centeredcumulantmethod.CenteredCumulantBasedLbMethod at 0x12745b5e0>
 
%% Cell type:markdown id: tags:
 
### Central Moments and Forcing
 
The conserved modes are marked with the note *(central moment)* in the table above. It highlights the fact that these modes are relaxed in central moment space, other than cumulant space. As described in section A, this is done to enable the implicit forcing scheme. When a force is specified, the momentum modes' relaxation rates are overridden by the force model. In the following cell, a symbolic force is specified. Further, a full list of relaxation rates is passed to allow the specification of bulk viscosity.
 
%% Cell type:code id: tags:
 
``` python
lbm_config = LBMConfig(method=Method.CUMULANT, force=sp.symbols('F_:3'),
relaxation_rates= [sp.Symbol('omega_shear'),
sp.Symbol('omega_bulk'),
sp.Symbol('omega_4'),
sp.Symbol('omega_5')])
sp.Symbol('omega_3'),
sp.Symbol('omega_4')])
method_with_force = create_lb_method(lbm_config=lbm_config)
method_with_force
```
 
%% Output
 
<lbmpy.methods.centeredcumulant.centeredcumulantmethod.CenteredCumulantBasedLbMethod at 0x11f7fd070>
<lbmpy.methods.centeredcumulant.centeredcumulantmethod.CenteredCumulantBasedLbMethod at 0x127652520>
 
%% Cell type:markdown id: tags:
 
## C) Exemplary simulation: flow around sphere
 
To end this tutorial, we show an example simulation of a channel flow with a spherical obstacle. This example is shown in 2D with the D2Q9 stencil but can be adapted easily for a 3D simulation.
 
%% Cell type:code id: tags:
 
``` python
from lbmpy.relaxationrates import relaxation_rate_from_lattice_viscosity
from lbmpy.macroscopic_value_kernels import pdf_initialization_assignments
```
 
%% Cell type:markdown id: tags:
 
To define the channel flow with dimensionless parameters, we first define the reference length in lattice cells. The reference length will be the diameter of the spherical obstacle. Furthermore, we define a maximal velocity which will be set for the inflow later. The Reynolds number is set relatively high to 100000, which will cause a turbulent flow in the channel.
 
From these definitions, we can calculate the relaxation rate `omega` for the lattice Boltzmann method.
 
%% Cell type:code id: tags:
 
``` python
reference_length = 30
maximal_velocity = 0.05
reynolds_number = 100000
kinematic_vicosity = (reference_length * maximal_velocity) / reynolds_number
initial_velocity=(maximal_velocity, 0)
 
omega = relaxation_rate_from_lattice_viscosity(kinematic_vicosity)
```
 
%% Cell type:markdown id: tags:
 
As a next step, we define the domain size of our set up.
 
%% Cell type:code id: tags:
 
``` python
stencil = LBStencil(Stencil.D2Q9)
domain_size = (reference_length * 12, reference_length * 4)
dim = len(domain_size)
```
 
%% Cell type:markdown id: tags:
 
Now the data for the simulation is allocated. We allocate a field `src` for the PDFs and field `dst` used as a temporary field to implement the two grid pull pattern. Additionally, we allocate a velocity field `velField`
 
%% Cell type:code id: tags:
 
``` python
dh = ps.create_data_handling(domain_size=domain_size, periodicity=(False, False))
 
src = dh.add_array('src', values_per_cell=len(stencil), alignment=True)
dh.fill('src', 0.0, ghost_layers=True)
dst = dh.add_array('dst', values_per_cell=len(stencil), alignment=True)
dh.fill('dst', 0.0, ghost_layers=True)
 
velField = dh.add_array('velField', values_per_cell=dh.dim, alignment=True)
dh.fill('velField', 0.0, ghost_layers=True)
```
 
%% Cell type:markdown id: tags:
 
We choose a cumulant lattice Boltzmann method, as described above. Here the second-order cumulants are relaxed with the relaxation rate calculated above. All higher-order cumulants are relaxed with one, which means we set them to the equilibrium.
 
%% Cell type:code id: tags:
 
``` python
lbm_config = LBMConfig(stencil=Stencil.D2Q9, method=Method.CUMULANT, relaxation_rate=omega,
output={'velocity': velField}, kernel_type='stream_pull_collide')
 
method = create_lb_method(lbm_config=lbm_config)
method
```
 
%% Output
 
<lbmpy.methods.centeredcumulant.centeredcumulantmethod.CenteredCumulantBasedLbMethod at 0x103b562b0>
<lbmpy.methods.centeredcumulant.centeredcumulantmethod.CenteredCumulantBasedLbMethod at 0x103b6d610>
 
%% Cell type:markdown id: tags:
 
### Initialisation with equilibrium
 
%% Cell type:code id: tags:
 
``` python
init = pdf_initialization_assignments(method, 1.0, initial_velocity, src.center_vector)
 
ast_init = ps.create_kernel(init, target=dh.default_target)
kernel_init = ast_init.compile()
 
dh.run_kernel(kernel_init)
```
 
%% Cell type:markdown id: tags:
 
### Definition of the update rule
 
%% Cell type:code id: tags:
 
``` python
lbm_optimisation = LBMOptimisation(symbolic_field=src, symbolic_temporary_field=dst)
update = create_lb_update_rule(lb_method=method,
lbm_config=lbm_config,
lbm_optimisation=lbm_optimisation)
 
ast_kernel = ps.create_kernel(update, target=dh.default_target, cpu_openmp=True)
kernel = ast_kernel.compile()
```
 
%% Cell type:markdown id: tags:
 
### Definition of the boundary set up
 
%% Cell type:code id: tags:
 
``` python
def set_sphere(x, y, *_):
mid = (domain_size[0] // 3, domain_size[1] // 2)
radius = reference_length // 2
return (x-mid[0])**2 + (y-mid[1])**2 < radius**2
```
 
%% Cell type:code id: tags:
 
``` python
bh = LatticeBoltzmannBoundaryHandling(method, dh, 'src', name="bh")
 
inflow = UBB(initial_velocity)
outflow = ExtrapolationOutflow(stencil[4], method)
wall = NoSlip("wall")
 
bh.set_boundary(inflow, slice_from_direction('W', dim))
bh.set_boundary(outflow, slice_from_direction('E', dim))
for direction in ('N', 'S'):
bh.set_boundary(wall, slice_from_direction(direction, dim))
 
bh.set_boundary(NoSlip("obstacle"), mask_callback=set_sphere)
 
plt.figure(dpi=200)
plt.boundary_handling(bh)
```
 
%% Output
 
 
%% Cell type:code id: tags:
 
``` python
def timeloop(timeSteps):
for i in range(timeSteps):
bh()
dh.run_kernel(kernel)
dh.swap("src", "dst")
```
 
%% Cell type:markdown id: tags:
 
### Run the simulation
 
%% Cell type:code id: tags:
 
``` python
mask = np.fromfunction(set_sphere, (domain_size[0], domain_size[1], len(domain_size)))
if 'is_test_run' not in globals():
timeloop(50000) # initial steps
 
def run():
timeloop(100)
return np.ma.array(dh.gather_array('velField'), mask=mask)
 
animation = plt.vector_field_magnitude_animation(run, frames=600, rescale=True)
set_display_mode('video')
res = display_animation(animation)
else:
timeloop(10)
res = None
res
```
 
%% Output
 
<IPython.core.display.HTML object>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment