def get_bulk_relaxation_rate(method): """ The bulk moment is x^2 + y^2 + z^2, plus a constant for orthogonalization. If this moment does not exist, the bulk relaxation is part of the shear relaxation. The bulk relaxation rate determines the bulk viscosity in hydrodynamic LBM schemes. """ for moment, relax_info in method.relaxation_info_dict.items(): if is_bulk_moment(moment, method.dim): return relax_info.relaxation_rate return get_shear_relaxation_rate(method)
def create_central_moment(stencil, relaxation_rates, nested_moments=None, maxwellian_moments=False, **kwargs): r""" Creates moment based LB method where the collision takes place in the central moment space. Args: stencil: instance of :class:`lbmpy.stencils.LBStencil` 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. maxwellian_moments: determines if the discrete or continuous maxwellian equilibrium is used to compute the equilibrium moments. Returns: :class:`lbmpy.methods.momentbased.CentralMomentBasedLbMethod` instance """ if nested_moments and not isinstance(nested_moments[0], list): nested_moments = list( sort_moments_into_groups_of_same_order(nested_moments).values()) second_order_moments = nested_moments[2] 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) if not nested_moments: nested_moments = cascaded_moment_sets_literature(stencil) 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) else: return create_with_discrete_maxwellian_eq_moments( stencil, rr_dict, central_moment_space=True, **kwargs)
def _check_modes(stencil, force_model, compressible): omega_s = sp.Symbol("omega_s") omega_b = sp.Symbol("omega_b") omega_o = sp.Symbol("omega_o") omega_e = sp.Symbol("omega_e") F = list(sp.symbols(f"F_:{stencil.D}")) lbm_config = LBMConfig(method=Method.MRT, stencil=stencil, relaxation_rates=[ omega_s, omega_b, omega_o, omega_e, omega_o, omega_e ], compressible=compressible, force_model=force_model, force=tuple(F)) method = create_lb_method(lbm_config=lbm_config) subs_dict = method.subs_dict_relxation_rate force_moments = method.force_model.moment_space_forcing(method) force_moments = force_moments.subs(subs_dict) # The mass mode should be zero assert force_moments[0] == 0 # The momentum moments should contain the force assert list(force_moments[1:stencil.D + 1]) == F if force_model == ForceModel.GUO: num_stresses = (stencil.D * stencil.D - stencil.D) // 2 + stencil.D lambda_s, lambda_b = -omega_s, -omega_b # The stress moments should match eq. 47 from https://doi.org/10.1023/A:1010414013942 u = method.first_order_equilibrium_moment_symbols def traceless(m): tr = sp.simplify(sum([m[i, i] for i in range(stencil.D)])) return m - tr / m.shape[0] * sp.eye(m.shape[0]) C = sp.Rational(1, 2) * (2 + lambda_s) * (traceless(sp.Matrix(u) * sp.Matrix(F).transpose()) + traceless(sp.Matrix(F) * sp.Matrix(u).transpose())) + \ sp.Rational(1, method.dim) * (2 + lambda_b) * sp.Matrix(u).dot(F) * sp.eye(method.dim) subs = { sp.Symbol(chr(ord("x") + i)) * sp.Symbol(chr(ord("x") + j)): C[i, j] for i in range(stencil.D) for j in range(stencil.D) } for force_moment, moment in zip( force_moments[stencil.D + 1:stencil.D + 1 + num_stresses], method.moments[stencil.D + 1:stencil.D + 1 + num_stresses]): ref = moment.subs(subs) diff = sp.simplify(ref - force_moment) if is_bulk_moment(moment, stencil.D): assert diff == 0 or isinstance( diff, sp.Rational) # difference should be zero or a constant else: assert diff == 0 # difference should be zero ff = method.moment_matrix.inv() * sp.Matrix( method.force_model.moment_space_forcing(method).subs(subs_dict)) # Check eq. 4.53a from schiller2008thermal assert sp.simplify(sum(ff)) == 0 # Check eq. 4.53b from schiller2008thermal assert [ sp.simplify(sum(ff[i] * stencil[i][j] for i in range(len(stencil)))) for j in range(stencil.D) ] == F # Check eq. 4.61a from schiller2008thermal ref = (2 + lambda_s) / 2 * ( traceless(sp.Matrix(u) * sp.Matrix(F).transpose()) + traceless(sp.Matrix(F) * sp.Matrix(u).transpose())) s = sp.zeros(stencil.D) for i in range(0, len(stencil)): s += ff[i] * traceless( sp.Matrix(stencil[i]) * sp.Matrix(stencil[i]).transpose()) assert sp.simplify(s - ref) == sp.zeros(stencil.D) # Check eq. 4.61b from schiller2008thermal assert sp.simplify( sum(ff[i] * stencil[i][a]**2 for i in range(len(stencil)) for a in range(stencil.D)) - (2 + lambda_b) * sp.Matrix(u).dot(F)) == 0 # All other moments should be zero assert list(force_moments[stencil.D + 1 + num_stresses:]) == [0] * ( len(stencil) - (stencil.D + 1 + num_stresses)) elif force_model == ForceModel.SIMPLE: # All other moments should be zero assert list(force_moments[stencil.D + 1:]) == [0] * (len(stencil) - (stencil.D + 1))
def _get_relaxation_info_dict(relaxation_rates, nested_moments, dim): r"""Creates a dictionary where each moment is mapped to a relaxation rate. Args: relaxation_rates: list of relaxation rates which should be used. This can also be a function which takes a moment group in the list of nested moments and returns a list of relaxation rates. This list has to have the length of the moment group and is then used for the moments in the moment group. nested_moments: list of lists containing the moments. dim: dimension """ result = OrderedDict() if callable(relaxation_rates): for group in nested_moments: rr = iter(relaxation_rates(group)) for moment in group: result[moment] = next(rr) return result 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.0 elif is_shear_moment(moment, dim): result[moment] = relaxation_rates[0] else: result[moment] = 1.0 # if 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.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
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. These MRT methods are just one specific version - there are many MRT methods possible for all these stencils which differ by the linear combination of moments and the grouping into equal relaxation times. To create a generic MRT method use `create_with_discrete_maxwellian_eq_moments` Args: stencil: instance of :class:`lbmpy.stencils.LBStencil` 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 nested_moments: a list of lists of modes, grouped by common relaxation times. If this argument is not provided, Gram-Schmidt orthogonalization of the default modes is performed. The default modes equal the raw moments except for the separation of the shear and bulk viscosity. """ if weighted: weights = get_weights(stencil, sp.Rational(1, 3)) else: weights = None if not nested_moments: moments = get_default_moment_set_for_stencil(stencil) x, y, z = MOMENT_SYMBOLS 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.D]): if d**2 in moments: 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.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) 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, moment_to_relaxation_rate_dict, **kwargs) else: return create_with_discrete_maxwellian_eq_moments( stencil, moment_to_relaxation_rate_dict, **kwargs)
def test_mrt_orthogonal(): m_ref = {} moments = mrt_orthogonal_modes_literature(LBStencil(Stencil.D2Q9), True) lbm_config = LBMConfig(method=Method.MRT, stencil=LBStencil(Stencil.D2Q9), maxwellian_moments=True, nested_moments=moments) m = create_lb_method(lbm_config=lbm_config) assert m.is_weighted_orthogonal m_ref[(Stencil.D2Q9, True)] = m moments = mrt_orthogonal_modes_literature(LBStencil(Stencil.D3Q15), True) lbm_config = LBMConfig(method=Method.MRT, stencil=LBStencil(Stencil.D3Q15), maxwellian_moments=True, nested_moments=moments) m = create_lb_method(lbm_config=lbm_config) assert m.is_weighted_orthogonal m_ref[(Stencil.D3Q15, True)] = m moments = mrt_orthogonal_modes_literature(LBStencil(Stencil.D3Q19), True) lbm_config = LBMConfig(method=Method.MRT, stencil=LBStencil(Stencil.D3Q19), maxwellian_moments=True, nested_moments=moments) m = create_lb_method(lbm_config=lbm_config) assert m.is_weighted_orthogonal m_ref[(Stencil.D3Q19, True)] = m moments = mrt_orthogonal_modes_literature(LBStencil(Stencil.D3Q27), False) lbm_config = LBMConfig(method=Method.MRT, stencil=LBStencil(Stencil.D3Q27), maxwellian_moments=True, nested_moments=moments) m = create_lb_method(lbm_config=lbm_config) assert m.is_orthogonal m_ref[(Stencil.D3Q27, False)] = m for weighted in [True, False]: for stencil in [ Stencil.D2Q9, Stencil.D3Q15, Stencil.D3Q19, Stencil.D3Q27 ]: lbm_config = LBMConfig(method=Method.MRT, stencil=LBStencil(stencil), maxwellian_moments=True, weighted=weighted) m = create_lb_method(lbm_config=lbm_config) if weighted: assert m.is_weighted_orthogonal else: assert m.is_orthogonal bulk_moments = set( [mom for mom in m.moments if is_bulk_moment(mom, m.dim)]) shear_moments = set( [mom for mom in m.moments if is_shear_moment(mom, m.dim)]) assert len(bulk_moments) == 1 assert len(shear_moments) == 1 + (m.dim - 2) + m.dim * (m.dim - 1) / 2 if (stencil, weighted) in m_ref: ref = m_ref[(stencil, weighted)] bulk_moments_lit = set([ mom for mom in ref.moments if is_bulk_moment(mom, ref.dim) ]) shear_moments_lit = set([ mom for mom in ref.moments if is_shear_moment(mom, ref.dim) ]) if stencil != stencil.D3Q27: # this one uses a different linear combination in literature assert shear_moments == shear_moments_lit assert bulk_moments == bulk_moments_lit