diff --git a/lbmpy/creationfunctions.py b/lbmpy/creationfunctions.py index 430586edc0d96fb85cdcdf98397daa122293a0b7..2334ba00a0bba6dea39124b932d2122b3d2dacae 100644 --- a/lbmpy/creationfunctions.py +++ b/lbmpy/creationfunctions.py @@ -436,22 +436,7 @@ def create_lb_method(lbm_config=None, **params): assert len(relaxation_rates) >= 2, "Not enough relaxation rates" method = create_trt(lbm_config.stencil, relaxation_rates[0], relaxation_rates[1], **common_params) elif lbm_config.method == Method.MRT: - next_relaxation_rate = [0] - - def relaxation_rate_getter(moments): - try: - if all(get_order(m) < 2 for m in moments): - if lbm_config.entropic: - return relaxation_rates[0] - else: - return 0 - res = relaxation_rates[next_relaxation_rate[0]] - next_relaxation_rate[0] += 1 - except IndexError: - raise ValueError("Too few relaxation rates specified") - return res - - method = create_mrt_orthogonal(lbm_config.stencil, relaxation_rate_getter, weighted=lbm_config.weighted, + method = create_mrt_orthogonal(lbm_config.stencil, relaxation_rates, weighted=lbm_config.weighted, nested_moments=lbm_config.nested_moments, **common_params) elif lbm_config.method == Method.CENTRAL_MOMENT: method = create_central_moment(lbm_config.stencil, relaxation_rates, diff --git a/lbmpy/methods/__init__.py b/lbmpy/methods/__init__.py index d73e0aca929fb4a0c72f9ce705636bd5a0b1ad7d..9fa48fdd027109c3667308021fbe296cbac3c797 100644 --- a/lbmpy/methods/__init__.py +++ b/lbmpy/methods/__init__.py @@ -1,10 +1,12 @@ from lbmpy.methods.creationfunctions import ( create_mrt_orthogonal, create_mrt_raw, create_central_moment, create_srt, create_trt, create_trt_kbc, create_trt_with_magic_number, create_with_continuous_maxwellian_eq_moments, - create_with_discrete_maxwellian_eq_moments, mrt_orthogonal_modes_literature, + create_with_discrete_maxwellian_eq_moments, create_centered_cumulant_model, create_with_default_polynomial_cumulants, create_with_polynomial_cumulants, create_with_monomial_cumulants) +from lbmpy.methods.default_moment_sets import mrt_orthogonal_modes_literature + from lbmpy.methods.abstractlbmethod import AbstractLbMethod, RelaxationInfo from lbmpy.methods.conservedquantitycomputation import AbstractConservedQuantityComputation diff --git a/lbmpy/methods/creationfunctions.py b/lbmpy/methods/creationfunctions.py index 0a60b6c93d8bc655131fc2987987c625456c3440..494d1100993f393754b78d0f3d393094d40fae10 100644 --- a/lbmpy/methods/creationfunctions.py +++ b/lbmpy/methods/creationfunctions.py @@ -10,6 +10,7 @@ from lbmpy.maxwellian_equilibrium import ( get_moments_of_discrete_maxwellian_equilibrium, get_weights) from lbmpy.methods.abstractlbmethod import RelaxationInfo +from lbmpy.methods.default_moment_sets import cascaded_moment_sets_literature from lbmpy.methods.centeredcumulant import CenteredCumulantBasedLbMethod from lbmpy.methods.centeredcumulant.cumulant_transform import CentralMomentsToCumulantsByGeneratingFunc @@ -27,9 +28,8 @@ 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 import Stencil +from lbmpy.enums import Stencil from lbmpy.stencils import LBStencil -from pystencils.stencil import have_same_entries from pystencils.sympyextensions import common_denominator @@ -278,7 +278,8 @@ def create_mrt_raw(stencil, relaxation_rates, maxwellian_moments=False, **kwargs :class:`lbmpy.methods.momentbased.MomentBasedLbMethod` instance """ moments = get_default_moment_set_for_stencil(stencil) - rr_dict = OrderedDict(zip(moments, relaxation_rates)) + nested_moments = [(c,) for c in moments] + rr_dict = get_relaxation_info_dict(relaxation_rates, nested_moments, stencil.D) if maxwellian_moments: return create_with_continuous_maxwellian_eq_moments(stencil, rr_dict, **kwargs) else: @@ -293,6 +294,9 @@ def create_central_moment(stencil, relaxation_rates, nested_moments=None, Args: stencil: instance of :class:`lbmpy.stencils.LBStenil` relaxation_rates: relaxation rates (inverse of the relaxation times) for each moment + nested_moments: a list of lists of modes, grouped by common relaxation times. This is usually used in + conjunction with `mrt_orthogonal_modes_literature`. If this argument is not provided, + Gram-Schmidt orthogonalization of the default modes is performed. maxwellian_moments: determines if the discrete or continuous maxwellian equilibrium is used to compute the equilibrium moments. Returns: @@ -310,28 +314,7 @@ def create_central_moment(stencil, relaxation_rates, nested_moments=None, if not nested_moments: nested_moments = cascaded_moment_sets_literature(stencil) - if len(relaxation_rates) == 1: - r_rates = [relaxation_rates[0]] # For correct viscosity - r_rates += [1] * (len(nested_moments) - 3) - else: - assert len(relaxation_rates) >= len(nested_moments) - 2, \ - f"Number of relaxation rates must at least match the number of non-conserved moment groups. " \ - f"For this stencil we have {len(nested_moments) - 2} such moment groups" - r_rates = relaxation_rates - - rr_dict = OrderedDict() - rr_iter = iter(r_rates) - for group in nested_moments: - if all(get_order(c) <= 1 for c in group): - for moment in group: - rr_dict[moment] = 0 - else: - try: - rr = next(rr_iter) - for moment in group: - rr_dict[moment] = rr - except StopIteration: - raise ValueError('Not enough relaxation rates specified.') + rr_dict = get_relaxation_info_dict(relaxation_rates, nested_moments, stencil.D) if maxwellian_moments: return create_with_continuous_maxwellian_eq_moments(stencil, rr_dict, central_moment_space=True, **kwargs) @@ -355,6 +338,7 @@ def create_trt_kbc(dim, shear_relaxation_rate, higher_order_relaxation_rate, met maxwellian_moments: determines if the discrete or continuous maxwellian equilibrium is used to compute the equilibrium moments. """ + def product(iterable): return reduce(operator.mul, iterable, 1) @@ -375,7 +359,7 @@ def create_trt_kbc(dim, shear_relaxation_rate, higher_order_relaxation_rate, met explicitly_defined = set(rho + velocity + shear_tensor_off_diagonal + shear_tensor_diagonal + energy_transport_tensor) rest = list(set(poly_repr(moments_up_to_component_order(2, dim))) - explicitly_defined) - assert len(rest) + len(explicitly_defined) == 3**dim + assert len(rest) + len(explicitly_defined) == 3 ** dim # naming according to paper Karlin 2015: Entropic multi relaxation lattice Boltzmann models for turbulent flows d = shear_tensor_off_diagonal + shear_tensor_trace_free_diagonal[:-1] @@ -410,7 +394,7 @@ def create_trt_kbc(dim, shear_relaxation_rate, higher_order_relaxation_rate, met return create_with_discrete_maxwellian_eq_moments(stencil, moment_to_rr, **kwargs) -def create_mrt_orthogonal(stencil, relaxation_rate_getter, maxwellian_moments=False, weighted=None, +def create_mrt_orthogonal(stencil, relaxation_rates, maxwellian_moments=False, weighted=None, nested_moments=None, **kwargs): r""" Returns an orthogonal multi-relaxation time model for the stencils D2Q9, D3Q15, D3Q19 and D3Q27. @@ -420,7 +404,7 @@ def create_mrt_orthogonal(stencil, relaxation_rate_getter, maxwellian_moments=Fa Args: stencil: instance of :class: `LBStencil` - relaxation_rate_getter: function getting a list of moments as argument, returning the associated relaxation + relaxation_rates: relaxation rates for the moments maxwellian_moments: determines if the discrete or continuous maxwellian equilibrium is used to compute the equilibrium moments weighted: whether to use weighted or unweighted orthogonality @@ -433,30 +417,27 @@ def create_mrt_orthogonal(stencil, relaxation_rate_getter, maxwellian_moments=Fa else: weights = None - moment_to_relaxation_rate_dict = OrderedDict() if not nested_moments: moments = get_default_moment_set_for_stencil(stencil) x, y, z = MOMENT_SYMBOLS - if stencil.Q == 2: + if stencil.D == 2: diagonal_viscous_moments = [x ** 2 + y ** 2, x ** 2] else: diagonal_viscous_moments = [x ** 2 + y ** 2 + z ** 2, x ** 2, y ** 2 - z ** 2] - for i, d in enumerate(MOMENT_SYMBOLS[:stencil.Q]): - moments[moments.index(d**2)] = diagonal_viscous_moments[i] + for i, d in enumerate(MOMENT_SYMBOLS[:stencil.D]): + moments[moments.index(d ** 2)] = diagonal_viscous_moments[i] orthogonal_moments = gram_schmidt(moments, stencil, weights) orthogonal_moments_scaled = [e * common_denominator(e) for e in orthogonal_moments] nested_moments = list(sort_moments_into_groups_of_same_order(orthogonal_moments_scaled).values()) # second order moments: separate bulk from shear second_order_moments = nested_moments[2] - bulk_moment = [m for m in second_order_moments if is_bulk_moment(m, stencil.Q)] - shear_moments = [m for m in second_order_moments if is_shear_moment(m, stencil.Q)] + bulk_moment = [m for m in second_order_moments if is_bulk_moment(m, stencil.D)] + shear_moments = [m for m in second_order_moments if is_shear_moment(m, stencil.D)] assert len(shear_moments) + len(bulk_moment) == len(second_order_moments) nested_moments[2] = shear_moments nested_moments.insert(3, bulk_moment) - for moment_list in nested_moments: - rr = relaxation_rate_getter(moment_list) - for m in moment_list: - moment_to_relaxation_rate_dict[m] = rr + + moment_to_relaxation_rate_dict = get_relaxation_info_dict(relaxation_rates, nested_moments, stencil.D) if maxwellian_moments: return create_with_continuous_maxwellian_eq_moments(stencil, @@ -466,228 +447,6 @@ def create_mrt_orthogonal(stencil, relaxation_rate_getter, maxwellian_moments=Fa moment_to_relaxation_rate_dict, **kwargs) -def mrt_orthogonal_modes_literature(stencil, is_weighted): - """ - Returns a list of lists of modes, grouped by common relaxation times. - This is for commonly used MRT models found in literature. - - Args: - stencil: instance of :class:`lbmpy.stencils.LBStenil` - is_weighted: whether to use weighted or unweighted orthogonality - - MRT schemes as described in the following references are used - """ - x, y, z = MOMENT_SYMBOLS - one = sp.Rational(1, 1) - - if have_same_entries(stencil, LBStencil(Stencil.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) - sq = x ** 2 + y ** 2 - all_moments = [one, x, y, 3 * sq - 2, 2 * x ** 2 - sq, x * y, - (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, LBStencil(Stencil.D3Q15)) and is_weighted: - sq = x ** 2 + y ** 2 + z ** 2 - nested_moments = [ - [one, x, y, z], # [0, 3, 5, 7] - [sq - 1], # [1] - [3 * sq ** 2 - 9 * sq + 4], # [2] - [(3 * sq - 5) * x, (3 * sq - 5) * y, (3 * sq - 5) * z], # [4, 6, 8] - [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, LBStencil(Stencil.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 - # The here implemented ordering of the moments is however different from that reference (Eq. 2.61-2.63) - # The moments are weighted-orthogonal (Eq. 2.58) - - # Further references: - # Duenweg, B., Schiller, U. D., & Ladd, A. J. (2007). Statistical mechanics of the fluctuating - # lattice Boltzmann equation. Physical Review E, 76(3) - # Chun, B., & Ladd, A. J. (2007). Interpolated boundary condition for lattice Boltzmann simulations of - # flows in narrow gaps. Physical review E, 75(6) - sq = x ** 2 + y ** 2 + z ** 2 - nested_moments = [ - [one, x, y, z], # [0, 3, 5, 7] - [sq - 1], # [1] - [3 * sq ** 2 - 6 * sq + 1], # [2] - [(3 * sq - 5) * x, (3 * sq - 5) * y, (3 * sq - 5) * z], # [4, 6, 8] - [3 * x ** 2 - sq, y ** 2 - z ** 2, x * y, y * z, x * z], # [9, 11, 13, 14, 15] - [(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, LBStencil(Stencil.D3Q27)) and not is_weighted: - xsq, ysq, zsq = x ** 2, y ** 2, z ** 2 - all_moments = [ - sp.Rational(1, 1), # 0 - x, y, z, # 1, 2, 3 - x * y, x * z, y * z, # 4, 5, 6 - xsq - ysq, # 7 - (xsq + ysq + zsq) - 3 * zsq, # 8 - (xsq + ysq + zsq) - 2, # 9 - 3 * (x * ysq + x * zsq) - 4 * x, # 10 - 3 * (xsq * y + y * zsq) - 4 * y, # 11 - 3 * (xsq * z + ysq * z) - 4 * z, # 12 - x * ysq - x * zsq, # 13 - xsq * y - y * zsq, # 14 - xsq * z - ysq * z, # 15 - x * y * z, # 16 - 3 * (xsq * ysq + xsq * zsq + ysq * zsq) - 4 * (xsq + ysq + zsq) + 4, # 17 - 3 * (xsq * ysq + xsq * zsq - 2 * ysq * zsq) - 2 * (2 * xsq - ysq - zsq), # 18 - 3 * (xsq * ysq - xsq * zsq) - 2 * (ysq - zsq), # 19 - 3 * (xsq * y * z) - 2 * (y * z), # 20 - 3 * (x * ysq * z) - 2 * (x * z), # 21 - 3 * (x * y * zsq) - 2 * (x * y), # 22 - 9 * (x * ysq * zsq) - 6 * (x * ysq + x * zsq) + 4 * x, # 23 - 9 * (xsq * y * zsq) - 6 * (xsq * y + y * zsq) + 4 * y, # 24 - 9 * (xsq * ysq * z) - 6 * (xsq * z + ysq * z) + 4 * z, # 25 - 27 * (xsq * ysq * zsq) - 18 * (xsq * ysq + xsq * zsq + ysq * zsq) + 12 * (xsq + ysq + zsq) - 8, # 26 - ] - nested_moments = list(sort_moments_into_groups_of_same_order(all_moments).values()) - else: - raise NotImplementedError("No MRT model is available (yet) for this stencil. " - "Create a custom MRT using 'create_with_discrete_maxwellian_eq_moments'") - - return nested_moments - - -def cascaded_moment_sets_literature(stencil): - """ - Returns default groups of cumulants to be relaxed with common relaxation rates as stated in literature. - Groups are ordered like this: - - - First group is density - - Second group are the momentum modes - - Third group are the shear modes - - Fourth group is the bulk mode - - Remaining groups do not govern hydrodynamic properties - - Args: - stencil: instance of :class:`lbmpy.stencils.LBStenil`. Can be D2Q9, D3Q15, D3Q19 or D3Q27 - """ - x, y, z = MOMENT_SYMBOLS - if have_same_entries(stencil, LBStencil(Stencil.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 - # for the preservation of rotational invariance as described by - # Martin Geier in his dissertation. - # - # Reference: Martin Geier. Ab inito derivation of the cascaded Lattice Boltzmann - # Automaton. Dissertation. University of Freiburg. 2006. - return [ - [sp.sympify(1)], # density is conserved - [x, y], # momentum is relaxed for cumulant forcing - - [x * y, x**2 - y**2], # shear - - [x**2 + y**2], # bulk - - [x**2 * y, x * y**2], - [x**2 * y**2] - ] - - elif have_same_entries(stencil, LBStencil(Stencil.D3Q15)): - # D3Q15 central moments by Premnath et al. https://arxiv.org/pdf/1202.6081.pdf. - return [ - [sp.sympify(1)], # density is conserved - [x, y, z], # momentum might be affected by forcing - - [x * y, - x * z, - y * z, - x ** 2 - y ** 2, - x ** 2 - z ** 2], # shear - - [x ** 2 + y ** 2 + z ** 2], # bulk - - [x * (x**2 + y**2 + z**2), - y * (x**2 + y**2 + z**2), - z * (x**2 + y**2 + z**2)], - - [x * y * z], - - [x ** 2 * y ** 2 + x ** 2 * z ** 2 + y ** 2 * z ** 2] - ] - - elif have_same_entries(stencil, LBStencil(Stencil.D3Q19)): - # D3Q19 cumulants are obtained by pruning the D3Q27 cumulant set as - # described by Coreixas, 2019. - return [ - [sp.sympify(1)], # density is conserved - [x, y, z], # momentum might be affected by forcing - - [x * y, - x * z, - y * z, - x ** 2 - y ** 2, - x ** 2 - z ** 2], # shear - - [x ** 2 + y ** 2 + z ** 2], # bulk - - [x * y ** 2 + x * z ** 2, - x ** 2 * y + y * z ** 2, - x ** 2 * z + y ** 2 * z], - - [x * y ** 2 - x * z ** 2, - x ** 2 * y - y * z ** 2, - x ** 2 * z - y ** 2 * z], - - [x ** 2 * y ** 2 - 2 * x ** 2 * z ** 2 + y ** 2 * z ** 2, - x ** 2 * y ** 2 + x ** 2 * z ** 2 - 2 * y ** 2 * z ** 2], - - [x ** 2 * y ** 2 + x ** 2 * z ** 2 + y ** 2 * z ** 2] - ] - - elif have_same_entries(stencil, LBStencil(Stencil.D3Q27)): - # Cumulants grouped to preserve rotational invariance as described by Geier et al, 2015 - return [ - [sp.sympify(1)], # density is conserved - [x, y, z], # momentum might be affected by forcing - - [x * y, - x * z, - y * z, - x ** 2 - y ** 2, - x ** 2 - z ** 2], # shear - - [x ** 2 + y ** 2 + z ** 2], # bulk - - [x * y ** 2 + x * z ** 2, - x ** 2 * y + y * z ** 2, - x ** 2 * z + y ** 2 * z], - - [x * y ** 2 - x * z ** 2, - x ** 2 * y - y * z ** 2, - x ** 2 * z - y ** 2 * z], - - [x * y * z], - - [x ** 2 * y ** 2 - 2 * x ** 2 * z ** 2 + y ** 2 * z ** 2, - x ** 2 * y ** 2 + x ** 2 * z ** 2 - 2 * y ** 2 * z ** 2], - - [x ** 2 * y ** 2 + x ** 2 * z ** 2 + y ** 2 * z ** 2], - - [x ** 2 * y * z, - x * y ** 2 * z, - x * y * z ** 2], - - [x ** 2 * y ** 2 * z, - x ** 2 * y * z ** 2, - x * y ** 2 * z ** 2], - - [x ** 2 * y ** 2 * z ** 2] - ] - else: - raise ValueError("No default set of cascaded moments is available for this stencil. " - "Please specify your own set of cascaded moments.") - - # ----------------------------------------- Cumulant method creators --------------------------------------------------- @@ -720,7 +479,7 @@ def create_centered_cumulant_model(stencil, cumulant_to_rr_dict, force_model=Non one = sp.Integer(1) - assert len(cumulant_to_rr_dict) == stencil.Q,\ + assert len(cumulant_to_rr_dict) == stencil.Q, \ "The number of moments has to be equal to the number of stencil entries" # CQC @@ -768,20 +527,7 @@ def create_with_polynomial_cumulants(stencil, relaxation_rates, cumulant_groups, Returns: :class:`lbmpy.methods.centeredcumulant.CenteredCumulantBasedLbMethod` instance """ - cumulant_to_rr_dict = OrderedDict() - rr_iter = iter(relaxation_rates) - for group in cumulant_groups: - if all(get_order(c) <= 1 for c in group): - for cumulant in group: - cumulant_to_rr_dict[cumulant] = 0 - else: - try: - rr = next(rr_iter) - for cumulant in group: - cumulant_to_rr_dict[cumulant] = rr - except StopIteration: - raise ValueError('Not enough relaxation rates specified.') - + cumulant_to_rr_dict = get_relaxation_info_dict(relaxation_rates, cumulant_groups, stencil.D) return create_centered_cumulant_model(stencil, cumulant_to_rr_dict, **kwargs) @@ -802,26 +548,9 @@ def create_with_monomial_cumulants(stencil, relaxation_rates, **kwargs): """ # Get monomial moments cumulants = get_default_moment_set_for_stencil(stencil) - - if len(relaxation_rates) == 1: - r_rates = [] - for c in cumulants: - order = get_order(c) - if order <= 1: - # Conserved moments - continue - elif is_shear_moment(c, stencil.D): - # Viscosity-governing moments - r_rates.append(relaxation_rates[0]) - else: - # The rest - r_rates.append(1) - else: - r_rates = relaxation_rates - cumulant_groups = [(c,) for c in cumulants] - return create_with_polynomial_cumulants(stencil, r_rates, cumulant_groups, **kwargs) + return create_with_polynomial_cumulants(stencil, relaxation_rates, cumulant_groups, **kwargs) def create_with_default_polynomial_cumulants(stencil, relaxation_rates, **kwargs): @@ -834,19 +563,75 @@ def create_with_default_polynomial_cumulants(stencil, relaxation_rates, **kwargs """ # Get polynomial groups cumulant_groups = cascaded_moment_sets_literature(stencil) + return create_with_polynomial_cumulants(stencil, relaxation_rates, cumulant_groups, **kwargs) - if len(relaxation_rates) == 1: - r_rates = [relaxation_rates[0]] # For correct viscosity - r_rates += [1] * (len(cumulant_groups) - 3) - else: - assert len(relaxation_rates) >= len(cumulant_groups) - 2, \ - f"Number of relaxation rates must at least match the number of non-conserved cumulant groups. " \ - f"For this stencil we have {len(cumulant_groups) - 2} such cumulant groups" - r_rates = relaxation_rates - return create_with_polynomial_cumulants(stencil, r_rates, cumulant_groups, **kwargs) +def get_relaxation_info_dict(relaxation_rates, nested_moments, dim): + result = OrderedDict() + number_of_moments = 0 + shear_moments = 0 + bulk_moments = 0 + for group in nested_moments: + for moment in group: + number_of_moments += 1 + if is_shear_moment(moment, dim): + shear_moments += 1 + if is_bulk_moment(moment, dim): + bulk_moments += 1 + + # if only one relaxation rate is specified it is used as the shear relaxation rate + if len(relaxation_rates) == 1: + for group in nested_moments: + for moment in group: + if get_order(moment) <= 1: + result[moment] = 0 + elif is_shear_moment(moment, dim): + result[moment] = relaxation_rates[0] + else: + result[moment] = 1 + + # is relaxation rate for each moment is specified they are all used + if len(relaxation_rates) == number_of_moments: + rr_iter = iter(relaxation_rates) + for group in nested_moments: + for moment in group: + rr = next(rr_iter) + result[moment] = rr + + # Fallback case, relaxes each group with the same relaxation rate and separates shear and bulk moments + next_rr = True + if len(relaxation_rates) != 1 and len(relaxation_rates) != number_of_moments: + try: + rr_iter = iter(relaxation_rates) + if shear_moments > 0: + shear_rr = next(rr_iter) + if bulk_moments > 0: + bulk_rr = next(rr_iter) + for group in nested_moments: + if next_rr: + rr = next(rr_iter) + next_rr = False + for moment in group: + if get_order(moment) <= 1: + result[moment] = 0 + elif is_shear_moment(moment, dim): + result[moment] = shear_rr + elif is_bulk_moment(moment, dim): + result[moment] = bulk_rr + else: + next_rr = True + result[moment] = rr + except StopIteration: + raise ValueError("Not enough relaxation rates are specified. You can either specify one relaxation rate, " + "which is used as the shear relaxation rate. In this case, conserved moments are " + "relaxed with 0, and higher-order moments are relaxed with 1. Another " + "possibility is to specify a relaxation rate for shear, bulk and one for each order " + "moment. In this case, conserved moments are also " + "relaxed with 0. The last possibility is to specify a relaxation rate for each moment, " + "including conserved moments") + return result # ----------------------------------------- Comparison view for notebooks ---------------------------------------------- diff --git a/lbmpy/methods/default_moment_sets.py b/lbmpy/methods/default_moment_sets.py new file mode 100644 index 0000000000000000000000000000000000000000..ae7ada6a53be4c869423c90be0925cbb64952961 --- /dev/null +++ b/lbmpy/methods/default_moment_sets.py @@ -0,0 +1,228 @@ +import sympy as sp + +from lbmpy.enums import Stencil +from lbmpy.moments import MOMENT_SYMBOLS, sort_moments_into_groups_of_same_order +from lbmpy.stencils import LBStencil +from pystencils.stencil import have_same_entries + + +def cascaded_moment_sets_literature(stencil): + """ + Returns default groups of cumulants to be relaxed with common relaxation rates as stated in literature. + Groups are ordered like this: + + - First group is density + - Second group are the momentum modes + - Third group are the shear modes + - Fourth group is the bulk mode + - Remaining groups do not govern hydrodynamic properties + + Args: + stencil: instance of :class:`lbmpy.stencils.LBStenil`. Can be D2Q9, D3Q15, D3Q19 or D3Q27 + """ + x, y, z = MOMENT_SYMBOLS + if have_same_entries(stencil, LBStencil(Stencil.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 + # for the preservation of rotational invariance as described by + # Martin Geier in his dissertation. + # + # Reference: Martin Geier. Ab inito derivation of the cascaded Lattice Boltzmann + # Automaton. Dissertation. University of Freiburg. 2006. + return [ + [sp.sympify(1)], # density is conserved + [x, y], # momentum is relaxed for cumulant forcing + + [x * y, x ** 2 - y ** 2], # shear + + [x ** 2 + y ** 2], # bulk + + [x ** 2 * y, x * y ** 2], + [x ** 2 * y ** 2] + ] + + elif have_same_entries(stencil, LBStencil(Stencil.D3Q15)): + # D3Q15 central moments by Premnath et al. https://arxiv.org/pdf/1202.6081.pdf. + return [ + [sp.sympify(1)], # density is conserved + [x, y, z], # momentum might be affected by forcing + + [x * y, + x * z, + y * z, + x ** 2 - y ** 2, + x ** 2 - z ** 2], # shear + + [x ** 2 + y ** 2 + z ** 2], # bulk + + [x * (x ** 2 + y ** 2 + z ** 2), + y * (x ** 2 + y ** 2 + z ** 2), + z * (x ** 2 + y ** 2 + z ** 2)], + + [x * y * z], + + [x ** 2 * y ** 2 + x ** 2 * z ** 2 + y ** 2 * z ** 2] + ] + + elif have_same_entries(stencil, LBStencil(Stencil.D3Q19)): + # D3Q19 cumulants are obtained by pruning the D3Q27 cumulant set as + # described by Coreixas, 2019. + return [ + [sp.sympify(1)], # density is conserved + [x, y, z], # momentum might be affected by forcing + + [x * y, + x * z, + y * z, + x ** 2 - y ** 2, + x ** 2 - z ** 2], # shear + + [x ** 2 + y ** 2 + z ** 2], # bulk + + [x * y ** 2 + x * z ** 2, + x ** 2 * y + y * z ** 2, + x ** 2 * z + y ** 2 * z], + + [x * y ** 2 - x * z ** 2, + x ** 2 * y - y * z ** 2, + x ** 2 * z - y ** 2 * z], + + [x ** 2 * y ** 2 - 2 * x ** 2 * z ** 2 + y ** 2 * z ** 2, + x ** 2 * y ** 2 + x ** 2 * z ** 2 - 2 * y ** 2 * z ** 2], + + [x ** 2 * y ** 2 + x ** 2 * z ** 2 + y ** 2 * z ** 2] + ] + + elif have_same_entries(stencil, LBStencil(Stencil.D3Q27)): + # Cumulants grouped to preserve rotational invariance as described by Geier et al, 2015 + return [ + [sp.sympify(1)], # density is conserved + [x, y, z], # momentum might be affected by forcing + + [x * y, + x * z, + y * z, + x ** 2 - y ** 2, + x ** 2 - z ** 2], # shear + + [x ** 2 + y ** 2 + z ** 2], # bulk + + [x * y ** 2 + x * z ** 2, + x ** 2 * y + y * z ** 2, + x ** 2 * z + y ** 2 * z], + + [x * y ** 2 - x * z ** 2, + x ** 2 * y - y * z ** 2, + x ** 2 * z - y ** 2 * z], + + [x * y * z], + + [x ** 2 * y ** 2 - 2 * x ** 2 * z ** 2 + y ** 2 * z ** 2, + x ** 2 * y ** 2 + x ** 2 * z ** 2 - 2 * y ** 2 * z ** 2], + + [x ** 2 * y ** 2 + x ** 2 * z ** 2 + y ** 2 * z ** 2], + + [x ** 2 * y * z, + x * y ** 2 * z, + x * y * z ** 2], + + [x ** 2 * y ** 2 * z, + x ** 2 * y * z ** 2, + x * y ** 2 * z ** 2], + + [x ** 2 * y ** 2 * z ** 2] + ] + else: + raise ValueError("No default set of cascaded moments is available for this stencil. " + "Please specify your own set of cascaded moments.") + + +def mrt_orthogonal_modes_literature(stencil, is_weighted): + """ + Returns a list of lists of modes, grouped by common relaxation times. + This is for commonly used MRT models found in literature. + + Args: + stencil: instance of :class:`lbmpy.stencils.LBStenil` + is_weighted: whether to use weighted or unweighted orthogonality + + MRT schemes as described in the following references are used + """ + x, y, z = MOMENT_SYMBOLS + one = sp.Rational(1, 1) + + if have_same_entries(stencil, LBStencil(Stencil.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) + sq = x ** 2 + y ** 2 + all_moments = [one, x, y, 3 * sq - 2, 2 * x ** 2 - sq, x * y, + (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, LBStencil(Stencil.D3Q15)) and is_weighted: + sq = x ** 2 + y ** 2 + z ** 2 + nested_moments = [ + [one, x, y, z], # [0, 3, 5, 7] + [sq - 1], # [1] + [3 * sq ** 2 - 9 * sq + 4], # [2] + [(3 * sq - 5) * x, (3 * sq - 5) * y, (3 * sq - 5) * z], # [4, 6, 8] + [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, LBStencil(Stencil.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 + # The here implemented ordering of the moments is however different from that reference (Eq. 2.61-2.63) + # The moments are weighted-orthogonal (Eq. 2.58) + + # Further references: + # Duenweg, B., Schiller, U. D., & Ladd, A. J. (2007). Statistical mechanics of the fluctuating + # lattice Boltzmann equation. Physical Review E, 76(3) + # Chun, B., & Ladd, A. J. (2007). Interpolated boundary condition for lattice Boltzmann simulations of + # flows in narrow gaps. Physical review E, 75(6) + sq = x ** 2 + y ** 2 + z ** 2 + nested_moments = [ + [one, x, y, z], # [0, 3, 5, 7] + [sq - 1], # [1] + [3 * sq ** 2 - 6 * sq + 1], # [2] + [(3 * sq - 5) * x, (3 * sq - 5) * y, (3 * sq - 5) * z], # [4, 6, 8] + [3 * x ** 2 - sq, y ** 2 - z ** 2, x * y, y * z, x * z], # [9, 11, 13, 14, 15] + [(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, LBStencil(Stencil.D3Q27)) and not is_weighted: + xsq, ysq, zsq = x ** 2, y ** 2, z ** 2 + all_moments = [ + sp.Rational(1, 1), # 0 + x, y, z, # 1, 2, 3 + x * y, x * z, y * z, # 4, 5, 6 + xsq - ysq, # 7 + (xsq + ysq + zsq) - 3 * zsq, # 8 + (xsq + ysq + zsq) - 2, # 9 + 3 * (x * ysq + x * zsq) - 4 * x, # 10 + 3 * (xsq * y + y * zsq) - 4 * y, # 11 + 3 * (xsq * z + ysq * z) - 4 * z, # 12 + x * ysq - x * zsq, # 13 + xsq * y - y * zsq, # 14 + xsq * z - ysq * z, # 15 + x * y * z, # 16 + 3 * (xsq * ysq + xsq * zsq + ysq * zsq) - 4 * (xsq + ysq + zsq) + 4, # 17 + 3 * (xsq * ysq + xsq * zsq - 2 * ysq * zsq) - 2 * (2 * xsq - ysq - zsq), # 18 + 3 * (xsq * ysq - xsq * zsq) - 2 * (ysq - zsq), # 19 + 3 * (xsq * y * z) - 2 * (y * z), # 20 + 3 * (x * ysq * z) - 2 * (x * z), # 21 + 3 * (x * y * zsq) - 2 * (x * y), # 22 + 9 * (x * ysq * zsq) - 6 * (x * ysq + x * zsq) + 4 * x, # 23 + 9 * (xsq * y * zsq) - 6 * (xsq * y + y * zsq) + 4 * y, # 24 + 9 * (xsq * ysq * z) - 6 * (xsq * z + ysq * z) + 4 * z, # 25 + 27 * (xsq * ysq * zsq) - 18 * (xsq * ysq + xsq * zsq + ysq * zsq) + 12 * (xsq + ysq + zsq) - 8, # 26 + ] + nested_moments = list(sort_moments_into_groups_of_same_order(all_moments).values()) + else: + raise NotImplementedError("No MRT model is available (yet) for this stencil. " + "Create a custom MRT using 'create_with_discrete_maxwellian_eq_moments'") + + return nested_moments