def _repr_html_(self): table = """ <table style="border:none; width: 100%"> <tr {nb}> <th {nb} >Central Moment / Cumulant</th> <th {nb} >Eq. Value </th> <th {nb} >Relaxation Rate</th> </tr> {content} </table> """ content = "" for cumulant, (eq_value, rr) in self._cumulant_to_relaxation_info_dict.items(): vals = { 'rr': f"${sp.latex(rr)}$", 'cumulant': f"${sp.latex(cumulant)}$", 'eq_value': f"${sp.latex(eq_value)}$", 'nb': 'style="border:none"', } order = get_order(cumulant) if order <= 1: vals['cumulant'] += ' (central moment)' if order == 1 and self.force_model_rr_override: vals['rr'] += ' (overridden by force model)' content += """<tr {nb}> <td {nb}>{cumulant}</td> <td {nb}>{eq_value}</td> <td {nb}>{rr}</td> </tr>\n""".format(**vals) return table.format(content=content, nb='style="border:none"')
def backward_transform(self, cumulant_base=POST_COLLISION_CUMULANT, central_moment_base=POST_COLLISION_MONOMIAL_CENTRAL_MOMENT, simplification=True, omit_conserved_moments=False, subexpression_base='sub_C_to_k'): simplification = self._get_simp_strategy(simplification) main_assignments = [] for exp in self.central_moment_exponents: if omit_conserved_moments and get_order(exp) <= 1: continue eq = self.central_moment_from_cumulants(exp, cumulant_base) k_symbol = statistical_quantity_symbol(central_moment_base, exp) main_assignments.append(Assignment(k_symbol, eq)) symbol_gen = SymbolGen(subexpression_base) ac = AssignmentCollection(main_assignments, subexpression_symbol_generator=symbol_gen) if simplification: ac = simplification.apply(ac) return ac
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_centered_cumulant_model( stencil, cumulant_to_rr_dict, force_model=None, equilibrium_order=None, c_s_sq=sp.Rational(1, 3), galilean_correction=False, central_moment_transform_class=PdfsToCentralMomentsByShiftMatrix, cumulant_transform_class=CentralMomentsToCumulantsByGeneratingFunc): r"""Creates a cumulant lattice Boltzmann model. Args: stencil: instance of :class:`lbmpy.stencils.LBStencil` cumulant_to_rr_dict: dict that has as many entries as the stencil. Each cumulant, which can be represented by an exponent tuple or in polynomial form is mapped to a relaxation rate. See :func:`lbmpy.methods.default_moment_sets.cascaded_moment_sets_literature` force_model: force model used for the collision. For cumulant LB method a good choice is `lbmpy.methods.centeredcumulant.CenteredCumulantForceModel` equilibrium_order: approximation order of macroscopic velocity :math:`\mathbf{u}` in the equilibrium c_s_sq: Speed of sound squared galilean_correction: special correction for D3Q27 cumulant collisions. See Appendix H in :cite:`geier2015`. Implemented in :mod:`lbmpy.methods.centeredcumulant.galilean_correction` central_moment_transform_class: Class which defines the transformation to the central moment space (see :mod:`lbmpy.moment_transforms`) cumulant_transform_class: Class which defines the transformation from the central moment space to the cumulant space (see :mod:`lbmpy.methods.centeredcumulant.cumulant_transform`) Returns: :class:`lbmpy.methods.centeredcumulant.CenteredCumulantBasedLbMethod` instance """ one = sp.Integer(1) assert len(cumulant_to_rr_dict) == stencil.Q, \ "The number of moments has to be equal to the number of stencil entries" # CQC cqc = DensityVelocityComputation(stencil, True, force_model=force_model) density_symbol = cqc.zeroth_order_moment_symbol velocity_symbols = cqc.first_order_moment_symbols # Equilibrium Values higher_order_polynomials = list( filter(lambda x: get_order(x) > 1, cumulant_to_rr_dict.keys())) # Lower Order Equilibria cumulants_to_relaxation_info_dict = { one: RelaxationInfo(density_symbol, cumulant_to_rr_dict[one]) } for d in MOMENT_SYMBOLS[:stencil.D]: cumulants_to_relaxation_info_dict[d] = RelaxationInfo( 0, cumulant_to_rr_dict[d]) # Polynomial Cumulant Equilibria polynomial_equilibria = get_equilibrium_values_of_maxwell_boltzmann_function( higher_order_polynomials, stencil.D, rho=density_symbol, u=velocity_symbols, c_s_sq=c_s_sq, order=equilibrium_order, space="cumulant") polynomial_equilibria = [density_symbol * v for v in polynomial_equilibria] for i, c in enumerate(higher_order_polynomials): cumulants_to_relaxation_info_dict[c] = RelaxationInfo( polynomial_equilibria[i], cumulant_to_rr_dict[c]) return CenteredCumulantBasedLbMethod( stencil, cumulants_to_relaxation_info_dict, cqc, force_model, galilean_correction=galilean_correction, central_moment_transform_class=central_moment_transform_class, cumulant_transform_class=cumulant_transform_class)
def get_update_rules_velocity(src_field, u_in, lb_method, force_model, density, sub_iterations=2): r""" Get assignments to update the velocity with a force shift Args: src_field: the source field of the hydrodynamic distribution function u_in: velocity field lb_method: mrt lattice boltzmann method used for hydrodynamics force_model: one of the phase_field force models which are applied in the collision space density: the interpolated density of the simulation sub_iterations: number of updates of the velocity field """ stencil = lb_method.stencil rho = lb_method.conserved_quantity_computation.zeroth_order_moment_symbol u_symp = lb_method.conserved_quantity_computation.first_order_moment_symbols force = force_model._force force_symp = force_model.force_symp moment_matrix = lb_method.moment_matrix moments = lb_method.moments indices = list() for i in range(len(moments)): if get_order(moments[i]) == 1: indices.append(i) m0 = moment_matrix * sp.Matrix(src_field.center_vector) update_u = list() update_u.append(Assignment(rho, m0[0])) index = 0 aleph = sp.symbols(f"aleph_:{stencil.D * sub_iterations}") for i in range(stencil.D): update_u.append(Assignment(aleph[i], u_in.center_vector[i])) index += 1 for k in range(sub_iterations - 1): subs_dict = dict(zip(u_symp, aleph[k * stencil.D:index])) for i in range(stencil.D): update_u.append( Assignment( aleph[index], m0[indices[i]] + force[i].subs(subs_dict) / density / 2)) index += 1 subs_dict = dict(zip(u_symp, aleph[index - stencil.D:index])) for i in range(stencil.D): update_u.append(Assignment(force_symp[i], force[i].subs(subs_dict))) for i in range(stencil.D): update_u.append( Assignment(u_symp[i], m0[indices[i]] + force_symp[i] / density / 2)) return update_u
def _centered_cumulant_collision_rule(self, cumulant_to_relaxation_info_dict, conserved_quantity_equations=None, pre_simplification=False, include_force_terms=False, include_galilean_correction=True, symbolic_relaxation_rates=False): # Filter out JobLib warnings. They are not usefull for use: # https://github.com/joblib/joblib/issues/683 filterwarnings("ignore", message="Persisting input arguments took") stencil = self.stencil f = self.pre_collision_pdf_symbols density = self.zeroth_order_equilibrium_moment_symbol velocity = self.first_order_equilibrium_moment_symbols cqe = conserved_quantity_equations relaxation_info_dict = dict() subexpressions_relaxation_rates = [] if symbolic_relaxation_rates: subexpressions_relaxation_rates, sd = self._generate_symbolic_relaxation_matrix( ) for i, cumulant in enumerate(cumulant_to_relaxation_info_dict): relaxation_info_dict[cumulant] = RelaxationInfo( cumulant_to_relaxation_info_dict[cumulant][0], sd[i, i]) else: relaxation_info_dict = cumulant_to_relaxation_info_dict if cqe is None: cqe = self._conserved_quantity_computation.equilibrium_input_equations_from_pdfs( f, False) forcing_subexpressions = AssignmentCollection([]) if self._force_model is not None: forcing_subexpressions = AssignmentCollection( self._force_model.subs_dict_force) # 1) Extract Monomial Cumulants for the higher-order polynomials polynomial_cumulants = relaxation_info_dict.keys() polynomial_cumulants = sorted(list(polynomial_cumulants), key=moment_sort_key) higher_order_polynomials = [ p for p in polynomial_cumulants if get_order(p) > 1 ] monomial_cumulants = sorted(list( extract_monomials(higher_order_polynomials, dim=self.dim)), key=exponent_tuple_sort_key) # 2) Get Forward and Backward Transformations between central moment and cumulant space, # and find required central moments k_to_c_transform = self._cumulant_transform_class( stencil, monomial_cumulants, density, velocity) k_to_c_eqs = cached_forward_transform( k_to_c_transform, simplification=pre_simplification) c_post_to_k_post_eqs = cached_backward_transform( k_to_c_transform, simplification=pre_simplification, omit_conserved_moments=True) central_moments = k_to_c_transform.required_central_moments assert len( central_moments ) == stencil.Q, 'Number of required central moments must match stencil size.' # 3) Get Forward Transformation from PDFs to central moments pdfs_to_k_transform = self._central_moment_transform_class( stencil, None, density, velocity, moment_exponents=central_moments, conserved_quantity_equations=cqe) pdfs_to_k_eqs = cached_forward_transform( pdfs_to_k_transform, f, simplification=pre_simplification, return_monomials=True) # 4) Add relaxation rules for lower order moments lower_order_moments = moments_up_to_order(1, dim=self.dim) lower_order_moment_symbols = [ statistical_quantity_symbol(PRE_COLLISION_MONOMIAL_CENTRAL_MOMENT, exp) for exp in lower_order_moments ] lower_order_relaxation_infos = [ relaxation_info_dict[exponent_to_polynomial_representation(e)] for e in lower_order_moments ] lower_order_relaxation_rates = [ info.relaxation_rate for info in lower_order_relaxation_infos ] lower_order_equilibrium = [ info.equilibrium_value for info in lower_order_relaxation_infos ] lower_order_moment_collision_eqs = relax_lower_order_central_moments( lower_order_moments, tuple(lower_order_moment_symbols), tuple(lower_order_relaxation_rates), tuple(lower_order_equilibrium)) # 5) Add relaxation rules for higher-order, polynomial cumulants poly_relaxation_infos = [ relaxation_info_dict[c] for c in higher_order_polynomials ] poly_relaxation_rates = [ info.relaxation_rate for info in poly_relaxation_infos ] poly_equilibrium = [ info.equilibrium_value for info in poly_relaxation_infos ] if self._galilean_correction and include_galilean_correction: galilean_correction_terms = get_galilean_correction_terms( relaxation_info_dict, density, velocity) else: galilean_correction_terms = None cumulant_collision_eqs = relax_polynomial_cumulants( tuple(monomial_cumulants), tuple(higher_order_polynomials), tuple(poly_relaxation_rates), tuple(poly_equilibrium), pre_simplification, galilean_correction_terms=galilean_correction_terms) # 6) Get backward transformation from central moments to PDFs d = self.post_collision_pdf_symbols k_post_to_pdfs_eqs = cached_backward_transform( pdfs_to_k_transform, d, simplification=pre_simplification, start_from_monomials=True) # 7) That's all. Now, put it all together. all_acs = [] if pdfs_to_k_transform.absorbs_conserved_quantity_equations else [ cqe ] subexpressions_relaxation_rates = AssignmentCollection( subexpressions_relaxation_rates) all_acs += [ subexpressions_relaxation_rates, forcing_subexpressions, pdfs_to_k_eqs, k_to_c_eqs, lower_order_moment_collision_eqs, cumulant_collision_eqs, c_post_to_k_post_eqs ] subexpressions = [ac.all_assignments for ac in all_acs] subexpressions += k_post_to_pdfs_eqs.subexpressions main_assignments = k_post_to_pdfs_eqs.main_assignments # 8) Maybe add forcing terms if CenteredCumulantForceModel was not used if self._force_model is not None and \ not isinstance(self._force_model, CenteredCumulantForceModel) and include_force_terms: force_model_terms = self._force_model(self) force_term_symbols = sp.symbols( f"forceTerm_:{len(force_model_terms)}") force_subexpressions = [ Assignment(sym, force_model_term) for sym, force_model_term in zip( force_term_symbols, force_model_terms) ] subexpressions += force_subexpressions main_assignments = [ Assignment(eq.lhs, eq.rhs + force_term_symbol) for eq, force_term_symbol in zip(main_assignments, force_term_symbols) ] # Aaaaaand we're done. return LbmCollisionRule(self, main_assignments, subexpressions)