from pystencils.simp.simplifications import sympy_cse import sympy as sp from warnings import warn from pystencils import Assignment, AssignmentCollection from pystencils.simp.assignment_collection import SymbolGen from pystencils.stencil import have_same_entries from pystencils.cache import disk_cache from lbmpy.stencils import get_stencil from lbmpy.methods.abstractlbmethod import AbstractLbMethod, LbmCollisionRule, RelaxationInfo from lbmpy.methods.conservedquantitycomputation import AbstractConservedQuantityComputation from lbmpy.moments import (moments_up_to_order, get_order, monomial_to_polynomial_transformation_matrix, moment_sort_key, exponent_tuple_sort_key, exponent_to_polynomial_representation, extract_monomials, MOMENT_SYMBOLS, statistical_quantity_symbol) from lbmpy.forcemodels import Luo, Simple # Local Imports from .cumulant_transform import ( PRE_COLLISION_CUMULANT, POST_COLLISION_CUMULANT, CentralMomentsToCumulantsByGeneratingFunc) from lbmpy.moment_transforms import ( PRE_COLLISION_MONOMIAL_CENTRAL_MOMENT, POST_COLLISION_MONOMIAL_CENTRAL_MOMENT, PdfsToCentralMomentsByShiftMatrix) from lbmpy.methods.centeredcumulant.force_model import CenteredCumulantForceModel from lbmpy.methods.centeredcumulant.galilean_correction import ( contains_corrected_polynomials, add_galilean_correction, get_galilean_correction_terms) # ============================ Cached Transformations ================================================================ @disk_cache def cached_forward_transform(transform_obj, *args, **kwargs): return transform_obj.forward_transform(*args, **kwargs) @disk_cache def cached_backward_transform(transform_obj, *args, **kwargs): return transform_obj.backward_transform(*args, **kwargs) # ============================ Lower Order Central Moment Collision ================================================== @disk_cache def relax_lower_order_central_moments(moment_indices, pre_collision_values, relaxation_rates, equilibrium_values, post_collision_base=POST_COLLISION_MONOMIAL_CENTRAL_MOMENT): post_collision_symbols = [statistical_quantity_symbol(post_collision_base, i) for i in moment_indices] equilibrium_vec = sp.Matrix(equilibrium_values) moment_vec = sp.Matrix(pre_collision_values) relaxation_matrix = sp.diag(*relaxation_rates) moment_vec = moment_vec + relaxation_matrix * (equilibrium_vec - moment_vec) main_assignments = [Assignment(s, eq) for s, eq in zip(post_collision_symbols, moment_vec)] return AssignmentCollection(main_assignments) # ============================ Polynomial Cumulant Collision ========================================================= @disk_cache def relax_polynomial_cumulants(monomial_exponents, polynomials, relaxation_rates, equilibrium_values, pre_simplification, galilean_correction_terms=None, pre_collision_base=PRE_COLLISION_CUMULANT, post_collision_base=POST_COLLISION_CUMULANT, subexpression_base='sub_col'): mon_to_poly_matrix = monomial_to_polynomial_transformation_matrix(monomial_exponents, polynomials) mon_vec = sp.Matrix([statistical_quantity_symbol(pre_collision_base, exp) for exp in monomial_exponents]) equilibrium_vec = sp.Matrix(equilibrium_values) relaxation_matrix = sp.diag(*relaxation_rates) subexpressions = [] poly_vec = mon_to_poly_matrix * mon_vec relaxed_polys = poly_vec + relaxation_matrix * (equilibrium_vec - poly_vec) if galilean_correction_terms is not None: relaxed_polys = add_galilean_correction(relaxed_polys, polynomials, galilean_correction_terms) subexpressions = galilean_correction_terms.all_assignments relaxed_monos = mon_to_poly_matrix.inv() * relaxed_polys main_assignments = [Assignment(statistical_quantity_symbol(post_collision_base, exp), v) for exp, v in zip(monomial_exponents, relaxed_monos)] symbol_gen = SymbolGen(subexpression_base) ac = AssignmentCollection( main_assignments, subexpressions=subexpressions, subexpression_symbol_generator=symbol_gen) if pre_simplification == 'default_with_cse': ac = sympy_cse(ac) return ac # =============================== LB Method Implementation =========================================================== class CenteredCumulantBasedLbMethod(AbstractLbMethod): """ This class implements cumulant-based lattice boltzmann methods which relax all the non-conserved quantities as either monomial or polynomial cumulants. It is mostly inspired by the work presented in :cite:`geier2015`. Conserved quantities are relaxed in central moment space. This method supports an implicit forcing scheme through :class:`lbmpy.methods.centeredcumulant.CenteredCumulantForceModel` where forces are applied by shifting the central-moment frame of reference by :math:`F/2` and then relaxing the first-order central moments with a relaxation rate of two. This corresponds to the change-of-sign described in the paper. Classical forcing schemes can still be applied. The galilean correction described in :cite:`geier2015` is also available for the D3Q27 lattice. This method is implemented modularily as the transformation from populations to central moments to cumulants is governed by subclasses of :class:`lbmpy.moment_transforms.AbstractMomentTransform` which can be specified by constructor argument. This allows the selection of the most efficient transformation for a given setup. Args: stencil: see :func:`lbmpy.stencils.get_stencil` cumulant_to_relaxation_info_dict: a dictionary mapping cumulants in either tuple or polynomial formulation to a RelaxationInfo, which consists of the corresponding equilibrium cumulant and a relaxation rate conserved_quantity_computation: instance of :class:`lbmpy.methods.AbstractConservedQuantityComputation`. This determines how conserved quantities are computed, and defines the symbols used in the equilibrium moments like e.g. density and velocity force_model: force model instance, or None if no forcing terms are required galilean_correction: if set to True the galilean_correction is applied to a D3Q27 cumulant method central_moment_transform_class: transform class to get from PDF space to the central moment space cumulant_transform_class: transform class to get from the central moment space to the cumulant space """ def __init__(self, stencil, cumulant_to_relaxation_info_dict, conserved_quantity_computation, force_model=None, galilean_correction=False, central_moment_transform_class=PdfsToCentralMomentsByShiftMatrix, cumulant_transform_class=CentralMomentsToCumulantsByGeneratingFunc): assert isinstance(conserved_quantity_computation, AbstractConservedQuantityComputation) super(CenteredCumulantBasedLbMethod, self).__init__(stencil) if force_model is not None: assert (isinstance(force_model, CenteredCumulantForceModel) or isinstance(force_model, Simple) or isinstance(force_model, Luo)), "Given force model currently not supported." for m in moments_up_to_order(1, dim=self.dim): if exponent_to_polynomial_representation(m) not in cumulant_to_relaxation_info_dict.keys(): raise ValueError(f'No relaxation info given for conserved cumulant {m}!') self._cumulant_to_relaxation_info_dict = cumulant_to_relaxation_info_dict self._conserved_quantity_computation = conserved_quantity_computation self._force_model = force_model self._weights = None self._galilean_correction = galilean_correction if galilean_correction: if not have_same_entries(stencil, get_stencil("D3Q27")): raise ValueError("Galilean Correction only available for D3Q27 stencil") if not contains_corrected_polynomials(cumulant_to_relaxation_info_dict): raise ValueError("For the galilean correction, all three polynomial cumulants" "(x^2 - y^2), (x^2 - z^2) and (x^2 + y^2 + z^2) must be present!") self._cumulant_transform_class = cumulant_transform_class self._central_moment_transform_class = central_moment_transform_class self.force_model_rr_override = False if isinstance(self._force_model, CenteredCumulantForceModel) and \ self._force_model.override_momentum_relaxation_rate is not None: self.set_first_moment_relaxation_rate(self._force_model.override_momentum_relaxation_rate) self.force_model_rr_override = True @property def central_moment_transform_class(self): self._central_moment_transform_class @property def cumulants(self): return tuple(self._cumulant_to_relaxation_info_dict.keys()) @property def cumulant_equilibrium_values(self): return tuple([e.equilibrium_value for e in self._cumulant_to_relaxation_info_dict.values()]) @property def cumulant_transform_class(self): self._cumulant_transform_class @property def first_order_equilibrium_moment_symbols(self, ): return self._conserved_quantity_computation.first_order_moment_symbols @property def force_model(self): return self._force_model @property def galilean_correction(self): return self._galilean_correction @property def relaxation_info_dict(self): return self._cumulant_to_relaxation_info_dict @property def relaxation_rates(self): return tuple([e.relaxation_rate for e in self._cumulant_to_relaxation_info_dict.values()]) @property def zeroth_order_equilibrium_moment_symbol(self, ): return self._conserved_quantity_computation.zeroth_order_moment_symbol def set_zeroth_moment_relaxation_rate(self, relaxation_rate): e = sp.Rational(1, 1) prev_entry = self._cumulant_to_relaxation_info_dict[e] new_entry = RelaxationInfo(prev_entry[0], relaxation_rate) self._cumulant_to_relaxation_info_dict[e] = new_entry def set_first_moment_relaxation_rate(self, relaxation_rate): if self.force_model_rr_override: warn("Overwriting first-order relaxation rates governed by CenteredCumulantForceModel " "might break your forcing scheme.") for e in MOMENT_SYMBOLS[:self.dim]: assert e in self._cumulant_to_relaxation_info_dict, \ "First cumulants are not relaxed separately by this method" for e in MOMENT_SYMBOLS[:self.dim]: prev_entry = self._cumulant_to_relaxation_info_dict[e] new_entry = RelaxationInfo(prev_entry[0], relaxation_rate) self._cumulant_to_relaxation_info_dict[e] = new_entry def set_conserved_moments_relaxation_rate(self, relaxation_rate): self.set_zeroth_moment_relaxation_rate(relaxation_rate) self.set_first_moment_relaxation_rate(relaxation_rate) def set_force_model(self, force_model): assert (isinstance(force_model, CenteredCumulantForceModel) or isinstance(force_model, Simple) or isinstance(force_model, Luo)), "Given force model currently not supported." self._force_model = force_model def _repr_html_(self): table = """
Central Moment / Cumulant | Eq. Value | Relaxation Rate |
---|