def get_shear_relaxation_rate(method): """ Assumes that all shear moments are relaxed with same rate - returns this rate Shear moments in 3D are: x*y, x*z and y*z - in 2D its only x*y The shear relaxation rate determines the viscosity in hydrodynamic LBM schemes """ if hasattr(method, 'shear_relaxation_rate'): return method.shear_relaxation_rate relaxation_rates = set() for moment, relax_info in method.relaxation_info_dict.items(): if is_shear_moment(moment, method.dim): relaxation_rates.add(relax_info.relaxation_rate) if len(relaxation_rates) == 1: return relaxation_rates.pop() else: if len(relaxation_rates) > 1: raise ValueError( "Shear moments are relaxed with different relaxation times: %s" % (relaxation_rates, )) else: all_relaxation_rates = set( v.relaxation_rate for v in method.relaxation_info_dict.values()) if len(all_relaxation_rates) == 1: return list(all_relaxation_rates)[0] raise NotImplementedError( "Shear moments seem to be not relaxed separately - " "Can not determine their relaxation rate automatically")
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 _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