diff --git a/lbmpy/creationfunctions.py b/lbmpy/creationfunctions.py index acf9ee67b9fa924cc268c42dbed9adb95aea1f00..6c18b643d4e57b4106cb8bf0158262d4ef12b476 100644 --- a/lbmpy/creationfunctions.py +++ b/lbmpy/creationfunctions.py @@ -73,6 +73,12 @@ LES methods: - ``smagorinsky=False``: set to Smagorinsky constant to activate turbulence model, ``omega_output_field`` can be set to write out adapted relaxation rates +Fluctuating LB: + +- ``fluctuating=(variance1, variance2, )``: enables fluctuating lattice Boltzmann by randomizing collision process. + Pass sequence of variances for each moment, or `True` to use symbolic variances + + Optimization Parameters ^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,11 +175,11 @@ import sympy as sp import lbmpy.forcemodels as forcemodels from lbmpy.fieldaccess import ( - AAEvenTimeStepAccessor, AAOddTimeStepAccessor, CollideOnlyInplaceAccessor, - EsoTwistEvenTimeStepAccessor, EsoTwistOddTimeStepAccessor, PdfFieldAccessor, - PeriodicTwoFieldsAccessor, StreamPullTwoFieldsAccessor, StreamPushTwoFieldsAccessor) -from lbmpy.methods import ( - create_mrt3, create_mrt_orthogonal, create_mrt_raw, create_srt, create_trt, create_trt_kbc) + AAEvenTimeStepAccessor, AAOddTimeStepAccessor, CollideOnlyInplaceAccessor, EsoTwistEvenTimeStepAccessor, + EsoTwistOddTimeStepAccessor, PdfFieldAccessor, PeriodicTwoFieldsAccessor, StreamPullTwoFieldsAccessor, + StreamPushTwoFieldsAccessor) +from lbmpy.fluctuatinglb import fluctuation_correction, method_with_rescaled_equilibrium_values +from lbmpy.methods import create_mrt3, create_mrt_orthogonal, create_mrt_raw, create_srt, create_trt, create_trt_kbc from lbmpy.methods.creationfunctions import create_generic_mrt from lbmpy.methods.cumulantbased import CumulantBasedLbMethod from lbmpy.methods.entropic import add_entropy_condition, add_iterative_entropy_condition @@ -187,7 +193,8 @@ from pystencils import Assignment, AssignmentCollection, create_kernel from pystencils.cache import disk_cache_no_fallback from pystencils.data_types import collate_types from pystencils.field import Field, get_layout_of_array -from pystencils.simp import add_subexpressions_for_field_reads +from pystencils.rng import random_symbol +from pystencils.simp.assignment_collection import SymbolGen from pystencils.stencil import have_same_entries @@ -302,6 +309,9 @@ def create_lb_collision_rule(lb_method=None, optimization={}, **kwargs): if rho_in is not None and isinstance(rho_in, Field): rho_in = rho_in.center + if params['fluctuating']: + lb_method = method_with_rescaled_equilibrium_values(lb_method) + if u_in is not None: density_rhs = sum(lb_method.pre_collision_pdf_symbols) if rho_in is None else rho_in eqs = [Assignment(cqc.zeroth_order_moment_symbol, density_rhs)] @@ -315,6 +325,14 @@ def create_lb_collision_rule(lb_method=None, optimization={}, **kwargs): collision_rule = simplification(collision_rule) + if params['fluctuating']: + variances = SymbolGen("variance") if params['fluctuating'] is True else params['fluctuating'] + correction = fluctuation_correction(lb_method, random_symbol(collision_rule.subexpressions, dim=lb_method.dim), + variances) + for i, corr in enumerate(correction): + collision_rule.main_assignments[i] = Assignment(collision_rule.main_assignments[i].lhs, + collision_rule.main_assignments[i].rhs + corr) + if params['entropic']: if params['smagorinsky']: raise ValueError("Choose either entropic or smagorinsky") @@ -509,6 +527,7 @@ def update_with_default_parameters(params, opt_params=None, fail_on_unknown_para 'entropic_newton_iterations': None, 'omega_output_field': None, 'smagorinsky': False, + 'fluctuating': False, 'output': {}, 'velocity_input': None, diff --git a/lbmpy/fluctuatinglb.py b/lbmpy/fluctuatinglb.py new file mode 100644 index 0000000000000000000000000000000000000000..c0d4ab3b23fe7c75fa55226ec9f777f23ee9f634 --- /dev/null +++ b/lbmpy/fluctuatinglb.py @@ -0,0 +1,50 @@ +"""Functions for implementation of fluctuating (randomized) lattice Boltzmann + +to generate a fluctuating LBM the equilibrium moment values have to be scaled and an additive (random) +correction term is added to the collision rule + + +Usage example: + + >>> from lbmpy.session import * + >>> from pystencils.rng import random_symbol + >>> method = create_lb_method(stencil='D2Q9', method='MRT') + >>> rescaled_method = method_with_rescaled_equilibrium_values(method) + >>> cr = create_lb_collision_rule(lb_method=rescaled_method) + >>> correction = fluctuation_correction(rescaled_method, + ... rng_generator=random_symbol(cr.subexpressions, dim=method.dim)) + >>> for i, corr in enumerate(correction): + ... cr.main_assignments[i] = ps.Assignment(cr.main_assignments[i].lhs, + ... cr.main_assignments[i].rhs + corr) + >>> + +""" +import sympy as sp + +from lbmpy.moments import MOMENT_SYMBOLS +from pystencils.simp.assignment_collection import SymbolGen + + +def method_with_rescaled_equilibrium_values(base_method): + """Re-scales the equilibrium moments by 1 / sqrt(M*w) with moment matrix M and weights w""" + from lbmpy.creationfunctions import create_lb_method_from_existing + + sig_k = abs(base_method.moment_matrix) * sp.Matrix(base_method.weights) + + def modification_rule(moment, eq, rr): + i = base_method.moments.index(moment) + return moment, eq / sp.sqrt(sig_k[i]), rr + + return create_lb_method_from_existing(base_method, modification_rule) + + +def fluctuation_correction(method, rng_generator, variances=SymbolGen("variance")): + """Returns additive correction terms to be added to the the collided pdfs""" + conserved_moments = {sp.sympify(1), *MOMENT_SYMBOLS} + + # A diagonal matrix containing the random fluctuations + random_matrix = sp.Matrix([0 if m in conserved_moments else next(rng_generator) for m in method.moments]) + random_variance = sp.diag(*[v for v, _ in zip(iter(variances), method.moments)]) + + # corrections are applied in real space hence we need to convert to real space here + return method.moment_matrix.inv() * random_variance * random_matrix diff --git a/lbmpy/forcemodels.py b/lbmpy/forcemodels.py index 7b116e462507e5372e02675b414747f2642cedbb..017927c37c8cb27f962497ab24770f35a11329de 100644 --- a/lbmpy/forcemodels.py +++ b/lbmpy/forcemodels.py @@ -91,7 +91,7 @@ import sympy as sp from lbmpy.relaxationrates import get_shear_relaxation_rate -class Simple(object): +class Simple: r""" A simple force model which introduces the following additional force term in the collision process :math:`\frac{w_q}{c_s^2} c_{qi} \; a_i` (often: force = rho * acceleration) @@ -114,7 +114,7 @@ class Simple(object): return default_velocity_shift(density, self._force) -class Luo(object): +class Luo: r"""Force model by Luo :cite:`luo1993lattice`. Shifts the macroscopic velocity by F/2, but does not change the equilibrium velocity. @@ -136,7 +136,7 @@ class Luo(object): return default_velocity_shift(density, self._force) -class Guo(object): +class Guo: r""" Force model by Guo :cite:`guo2002discrete` Adapts the calculation of the macroscopic velocity as well as the equilibrium velocity (both shifted by F/2)! @@ -158,7 +158,7 @@ class Guo(object): return default_velocity_shift(density, self._force) -class Buick(object): +class Buick: r""" This force model :cite:`buick2000gravity` has a force term with zero second moment. It is suited for incompressible lattice models. @@ -181,7 +181,7 @@ class Buick(object): return default_velocity_shift(density, self._force) -class EDM(object): +class EDM: r"""Exact differencing force model""" def __init__(self, force): diff --git a/lbmpy_tests/test_fluctuation.py b/lbmpy_tests/test_fluctuation.py new file mode 100644 index 0000000000000000000000000000000000000000..f3e45f5b20cb8a3aeda5646edbde5dda1bd9764c --- /dev/null +++ b/lbmpy_tests/test_fluctuation.py @@ -0,0 +1,8 @@ +from lbmpy.scenarios import create_channel + +def test_fluctuating_generation_pipeline(): + ch = create_channel((40, 10), method='mrt3', relaxation_rates=[1.5, 1, 1], force=1e-5, + fluctuating=[1e-3] * 9, + kernel_params={'time_step': 1}) + + ch.run(10)