Example #1
0
    def backward_transform(self,
                           pdf_symbols,
                           simplification=True,
                           subexpression_base='sub_k_to_f',
                           start_from_monomials=False):
        r"""Returns an assignment collection containing equations for post-collision populations, 
        expressed in terms of the post-collision polynomial moments by matrix-multiplication.

        The moment transformation matrix :math:`M` provided by :func:`lbmpy.moments.moment_matrix` is
        inverted and used to compute the pre-collision moments as 
        :math:`\mathbf{f}^{\ast} = M^{-1} \cdot \mathbf{M}_{\mathrm{post}}`, which is returned element-wise.

        **Simplifications**

        If simplification is enabled, the equations for populations :math:`f_i` and :math:`f_{\bar{i}}` 
        of opposite stencil directions :math:`\mathbf{c}_i` and :math:`\mathbf{c}_{\bar{i}} = - \mathbf{c}_i`
        are split into their symmetric and antisymmetric parts :math:`f_i^{\mathrm{sym}}, f_i^{\mathrm{anti}}`, such
        that

        .. math::

            f_i = f_i^{\mathrm{sym}} + f_i^{\mathrm{anti}}

            f_{\bar{i}} = f_i^{\mathrm{sym}} - f_i^{\mathrm{anti}}


        Args:
            pdf_symbols: List of symbols that represent the post-collision populations
            simplification: Simplification specification. See :class:`AbstractMomentTransform`
            subexpression_base: The base name used for any subexpressions of the transformation.
            start_from_monomials: Return equations for monomial moments. Use only when specifying 
                                  ``moment_exponents`` in constructor!
        """
        simplification = self._get_simp_strategy(simplification, 'backward')

        if start_from_monomials:
            assert len(self.moment_exponents) == self.q, "Could not derive invertible monomial transform." \
                f"Expected {self.q} monomials, but got {len(self.moment_exponents)}."
            mm_inv = moment_matrix(self.moment_exponents, self.stencil).inv()
            post_collision_moments = self.post_collision_monomial_symbols
        else:
            mm_inv = self.inv_moment_matrix
            post_collision_moments = self.post_collision_symbols

        m_to_f_vec = mm_inv * sp.Matrix(post_collision_moments)
        main_assignments = [
            Assignment(f, eq) for f, eq in zip(pdf_symbols, m_to_f_vec)
        ]

        symbol_gen = SymbolGen(subexpression_base)
        ac = AssignmentCollection(main_assignments,
                                  subexpression_symbol_generator=symbol_gen)

        ac.add_simplification_hint('stencil', self.stencil)
        ac.add_simplification_hint('post_collision_pdf_symbols', pdf_symbols)
        if simplification:
            ac = simplification.apply(ac)
        return ac
Example #2
0
def fluctuation_correction(method, rng_generator, amplitudes=SymbolGen("phi")):
    """Returns additive correction terms to be added to the the collided pdfs"""
    conserved_moments = {sp.sympify(1), *MOMENT_SYMBOLS}

    # A diagonal matrix containing the random fluctuations
    random_matrix = sp.Matrix([0 if m in conserved_moments else (next(rng_generator) - 0.5) * sp.sqrt(12)
                               for m in method.moments])
    amplitude_matrix = sp.diag(*[v for v, _ in zip(iter(amplitudes), method.moments)])

    # corrections are applied in real space hence we need to convert to real space here
    return method.moment_matrix.inv() * amplitude_matrix * random_matrix
Example #3
0
    def backward_transform(self, pdf_symbols, simplification=True, subexpression_base='sub_k_to_f',
                           start_from_monomials=False):
        r"""Returns an assignment collection containing equations for post-collision populations, 
        expressed in terms of the post-collision polynomial central moments using the backward
        fast central moment transform.

        First, monomial central moments are obtained from the polynomial moments by multiplication
        with :math:`P^{-1}`. Then, the elementwise equations of the matrix 
        multiplication :math:`K^{-1} \cdot \mathbf{K}` with the monomial central moment matrix 
        (see `PdfsToCentralMomentsByMatrix`) are recursively simplified by extracting certain linear 
        combinations of velocities, to obtain equations similar to the ones given in :cite:`geier2015`.

        The backward transform is designed for D3Q27, inherently generalizes to D2Q9, and is tested
        for D3Q19. It also returns correct equations for D3Q15, whose efficiency is however questionable.

        **De-Aliasing**: 

        See `FastCentralMomentTransform.forward_transform`.

        Args:
            pdf_symbols: List of symbols that represent the post-collision populations
            simplification: Simplification specification. See :class:`AbstractMomentTransform`
            subexpression_base: The base name used for any subexpressions of the transformation.
            start_from_monomials: Return equations for monomial moments. Use only when specifying 
                                  ``moment_exponents`` in constructor!
        """
        simplification = self._get_simp_strategy(simplification, 'backward')

        post_collision_moments = self.post_collision_symbols
        post_collision_monomial_moments = self.post_collision_monomial_symbols

        subexpressions = []
        if not start_from_monomials:
            monomial_eqs = self.poly_to_mono_matrix * sp.Matrix(post_collision_moments)
            subexpressions += [Assignment(m, v) for m, v in zip(post_collision_monomial_moments, monomial_eqs)]

        raw_equations = self.inv_monomial_matrix * sp.Matrix(post_collision_monomial_moments)
        raw_equations = [Assignment(f, eq) for f, eq in zip(pdf_symbols, raw_equations)]

        symbol_gen = SymbolGen(subexpression_base)

        ac = self._split_backward_equations(raw_equations, symbol_gen)
        ac.subexpressions = subexpressions + ac.subexpressions
        if simplification:
            ac = simplification.apply(ac)
        return ac
Example #4
0
    def forward_transform(self,
                          pdf_symbols,
                          simplification=True,
                          subexpression_base='sub_f_to_M',
                          return_monomials=False):
        r"""Returns an assignment collection containing equations for pre-collision polynomial
        moments, expressed in terms of the pre-collision populations by matrix-multiplication.

        The moment transformation matrix :math:`M` provided by :func:`lbmpy.moments.moment_matrix` is
        used to compute the pre-collision moments as :math:`\mathbf{M} = M \cdot \mathbf{f}`,
        which is returned element-wise.

        Args:
            pdf_symbols: List of symbols that represent the pre-collision populations
            simplification: Simplification specification. See :class:`AbstractMomentTransform`
            subexpression_base: The base name used for any subexpressions of the transformation.
            return_monomials: Return equations for monomial moments. Use only when specifying 
                              ``moment_exponents`` in constructor!
        """
        simplification = self._get_simp_strategy(simplification, 'forward')

        if return_monomials:
            assert len(self.moment_exponents) == self.q, "Could not derive invertible monomial transform." \
                f"Expected {self.q} monomials, but got {len(self.moment_exponents)}."
            mm = moment_matrix(self.moment_exponents, self.stencil)
            pre_collision_moments = self.pre_collision_monomial_symbols
        else:
            mm = self.moment_matrix
            pre_collision_moments = self.pre_collision_symbols

        f_to_m_vec = mm * sp.Matrix(pdf_symbols)
        main_assignments = [
            Assignment(m, eq)
            for m, eq in zip(pre_collision_moments, f_to_m_vec)
        ]

        symbol_gen = SymbolGen(symbol=subexpression_base)
        ac = AssignmentCollection(main_assignments,
                                  subexpression_symbol_generator=symbol_gen)

        if simplification:
            ac = simplification.apply(ac)
        return ac
Example #5
0
    def forward_transform(self,
                          cumulant_base=PRE_COLLISION_CUMULANT,
                          central_moment_base=PRE_COLLISION_MONOMIAL_CENTRAL_MOMENT,
                          simplification=True,
                          subexpression_base='sub_k_to_C'):
        simplification = self._get_simp_strategy(simplification)

        main_assignments = []
        for exp in self.cumulant_exponents:
            eq = self.cumulant_from_central_moments(exp, central_moment_base)
            c_symbol = statistical_quantity_symbol(cumulant_base, exp)
            main_assignments.append(Assignment(c_symbol, eq))
        symbol_gen = SymbolGen(subexpression_base)
        ac = AssignmentCollection(
            main_assignments, subexpression_symbol_generator=symbol_gen)
        
        if simplification:
            ac = simplification.apply(ac)
        return ac
Example #6
0
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
Example #7
0
    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
Example #8
0
    def backward_transform(self, pdf_symbols, simplification=True, subexpression_base='sub_k_to_f',
                           start_from_monomials=False):
        r"""Returns an assignment collection containing equations for post-collision populations, 
        expressed in terms of the post-collision polynomial central moments by matrix-multiplication.

        The moment transformation matrix :math:`K` provided by :func:`lbmpy.moments.moment_matrix` is
        inverted and used to compute the pre-collision moments as 
        :math:`\mathbf{f}^{\ast} = K^{-1} \cdot \mathbf{K}_{\mathrm{post}}`, which is returned element-wise.

        Args:
            pdf_symbols: List of symbols that represent the post-collision populations
            simplification: Simplification specification. See :class:`AbstractMomentTransform`
            subexpression_base: The base name used for any subexpressions of the transformation.
            start_from_monomials: Return equations for monomial moments. Use only when specifying 
                                  ``moment_exponents`` in constructor!
        """
        simplification = self._get_simp_strategy(simplification)

        if start_from_monomials:
            assert len(self.moment_exponents) == self.q, "Could not derive invertible monomial transform." \
                f"Expected {self.q} monomials, but got {len(self.moment_exponents)}."
            mm_inv = moment_matrix(self.moment_exponents, self.stencil).inv()
            shift_inv = set_up_shift_matrix(self.moment_exponents, self.stencil, self.equilibrium_velocity).inv()
            km_inv = mm_inv * shift_inv
            post_collision_moments = self.post_collision_monomial_symbols
        else:
            km_inv = self.backward_matrix
            post_collision_moments = self.post_collision_symbols

        m_to_f_vec = km_inv * sp.Matrix(post_collision_moments)
        main_assignments = [Assignment(f, eq) for f, eq in zip(pdf_symbols, m_to_f_vec)]

        symbol_gen = SymbolGen(subexpression_base)
        ac = AssignmentCollection(main_assignments, subexpression_symbol_generator=symbol_gen)

        if simplification:
            ac = simplification.apply(ac)
        return ac
Example #9
0
def test_assignment_collection():
    x, y, z, t = sp.symbols("x y z t")
    symbol_gen = SymbolGen("a")

    ac = AssignmentCollection([Assignment(z, x + y)], [],
                              subexpression_symbol_generator=symbol_gen)

    lhs = ac.add_subexpression(t)
    assert lhs == sp.Symbol("a_0")
    ac.subexpressions.append(Assignment(t, 3))
    ac.topological_sort(sort_main_assignments=False, sort_subexpressions=True)
    assert ac.subexpressions[0].lhs == t

    assert ac.new_with_inserted_subexpression(sp.Symbol("not_defined")) == ac
    ac_inserted = ac.new_with_inserted_subexpression(t)
    ac_inserted2 = ac.new_without_subexpressions({lhs})
    assert all(a == b for a, b in zip(ac_inserted.all_assignments,
                                      ac_inserted2.all_assignments))

    print(ac_inserted)
    assert ac_inserted.subexpressions[0] == Assignment(lhs, 3)

    assert 'a_0' in str(ac_inserted)
    assert '<table' in ac_inserted._repr_html_()
Example #10
0
    def forward_transform(self, pdf_symbols, simplification=True, subexpression_base='sub_f_to_k',
                          return_monomials=False):
        r"""Returns equations for polynomial central moments, computed from pre-collision populations
        through a cascade of three steps.

        First, the monomial raw moment vector :math:`\mathbf{m}` is computed using the raw-moment
        chimera transform (see `lbmpy.moment_transforms.PdfsToMomentsByChimeraTransform`). Then, the
        monomial shift matrix :math:`N` provided by `lbmpy.moments.set_up_shift_matrix` is used to compute
        the monomial central moment vector as :math:`\mathbf{\kappa} = N \mathbf{m}`. Lastly, the polynomial
        central moments are computed using the polynomialization matrix as :math:`\mathbf{K} = P \mathbf{\kappa}`.

        **Conserved Quantity Equations**

        If given, this transform absorbs the conserved quantity equations and simplifies them
        using the raw moment equations, if simplification is enabled.


        **Simplification**

        If simplification is enabled, the absorbed conserved quantity equations are - if possible - 
        rewritten using the monomial symbols. If the conserved quantities originate somewhere else
        than in the lower-order moments (like from an external field), they are not affected by this
        simplification.

        The relations between conserved quantities and raw moments are used to simplify the equations
        obtained from the shift matrix. Further, these equations are simplified by recursively inserting
        lower-order moments into equations for higher-order moments.

         **De-Aliasing**

        If more than :math:`q` monomial moments are extracted from the polynomial set, they
        are de-aliased and reduced to a set of only :math:`q` moments using the same rules
        as for raw moments. For polynomialization, a special reduced matrix :math:`\tilde{P}`
        is used, which is computed using `lbmpy.moments.central_moment_reduced_monomial_to_polynomial_matrix`.


        Args:
            pdf_symbols: List of symbols that represent the pre-collision populations
            simplification: Simplification specification. See :class:`AbstractMomentTransform`
            subexpression_base: The base name used for any subexpressions of the transformation.
            return_monomials: Return equations for monomial moments. Use only when specifying 
                              ``moment_exponents`` in constructor!

        """
        simplification = self._get_simp_strategy(simplification, 'forward')

        raw_moment_base = self.raw_moment_transform.mono_base_pre
        central_moment_base = self.mono_base_pre

        mono_rm_symbols = self.raw_moment_transform.pre_collision_monomial_symbols
        mono_cm_symbols = self.pre_collision_monomial_symbols

        rm_ac = self.raw_moment_transform.forward_transform(pdf_symbols, simplification=False, return_monomials=True)
        cq_symbols_to_moments = self.raw_moment_transform.get_cq_to_moment_symbols_dict(raw_moment_base)
        rm_to_cm_vec = self.shift_matrix * sp.Matrix(mono_rm_symbols)

        cq_subs = dict()
        if simplification:
            from lbmpy.methods.momentbased.momentbasedsimplifications import (
                substitute_moments_in_conserved_quantity_equations)
            rm_ac = substitute_moments_in_conserved_quantity_equations(rm_ac)

            #   Compute replacements for conserved moments in terms of the CQE
            rm_asm_dict = rm_ac.main_assignments_dict
            for cq_sym, moment_sym in cq_symbols_to_moments.items():
                cq_eq = rm_asm_dict[cq_sym]
                solutions = sp.solve(cq_eq - cq_sym, moment_sym)
                if len(solutions) > 0:
                    cq_subs[moment_sym] = solutions[0]

            rm_to_cm_vec = fast_subs(rm_to_cm_vec, cq_subs)

        rm_to_cm_dict = {cm: rm for cm, rm in zip(mono_cm_symbols, rm_to_cm_vec)}

        if simplification:
            rm_to_cm_dict = self._simplify_raw_to_central_moments(
                rm_to_cm_dict, self.moment_exponents, raw_moment_base, central_moment_base)
            rm_to_cm_dict = self._undo_remaining_cq_subexpressions(rm_to_cm_dict, cq_subs)

        subexpressions = rm_ac.all_assignments

        if return_monomials:
            main_assignments = [Assignment(lhs, rhs) for lhs, rhs in rm_to_cm_dict.items()]
        else:
            subexpressions += [Assignment(lhs, rhs) for lhs, rhs in rm_to_cm_dict.items()]
            poly_eqs = self.mono_to_poly_matrix * sp.Matrix(mono_cm_symbols)
            main_assignments = [Assignment(m, v) for m, v in zip(self.pre_collision_symbols, poly_eqs)]

        symbol_gen = SymbolGen(subexpression_base)
        ac = AssignmentCollection(main_assignments=main_assignments, subexpressions=subexpressions,
                                  subexpression_symbol_generator=symbol_gen)

        if simplification:
            ac = simplification.apply(ac)
        return ac
Example #11
0
    def forward_transform(self, pdf_symbols, simplification=True, subexpression_base='sub_f_to_k',
                          return_monomials=False):
        r"""Returns an assignment collection containing equations for pre-collision polynomial
        central moments, expressed in terms of the pre-collision populations.

        The monomial central moments are computed from populations through the central-moment
        chimera transform:

        .. math::

            f_{xyz} &:= f_i \text{ such that } c_i = (x,y,z)^T \\
            \kappa_{xy|\gamma} &:= \sum_{z \in \{-1, 0, 1\} } f_{xyz} \cdot (z - u_z)^{\gamma} \\
            \kappa_{x|\beta \gamma} &:= \sum_{y \in \{-1, 0, 1\}} \kappa_{xy|\gamma} \cdot (y - u_y)^{\beta} \\
            \kappa_{\alpha \beta \gamma} &:= \sum_{x \in \{-1, 0, 1\}} \kappa_{x|\beta \gamma} \cdot (x - u_x)^{\alpha}

        The polynomial moments are afterward computed from the monomials by matrix-multiplication 
        using the polynomialization matrix :math:`P`.

        **De-Aliasing**

        If more than :math:`q` monomial moments are extracted from the polynomial set, they
        are de-aliased and reduced to a set of only :math:`q` moments using the same rules
        as for raw moments. For polynomialization, a special reduced matrix :math:`\tilde{P}`
        is used, which is computed using `lbmpy.moments.central_moment_reduced_monomial_to_polynomial_matrix`.


        Args:
            pdf_symbols: List of symbols that represent the pre-collision populations
            simplification: Simplification specification. See :class:`AbstractMomentTransform`
            subexpression_base: The base name used for any subexpressions of the transformation.
            return_monomials: Return equations for monomial moments. Use only when specifying 
                              ``moment_exponents`` in constructor!
        """
        simplification = self._get_simp_strategy(simplification, 'forward')
        monomial_symbol_base = self.mono_base_pre

        def _partial_kappa_symbol(fixed_directions, remaining_exponents):
            fixed_str = '_'.join(str(direction) for direction in fixed_directions).replace('-', 'm')
            exp_str = '_'.join(str(exp) for exp in remaining_exponents).replace('-', 'm')
            return sp.Symbol(f"partial_{monomial_symbol_base}_{fixed_str}_e_{exp_str}")

        partial_sums_dict = dict()
        monomial_moment_eqs = []

        def collect_partial_sums(exponents, dimension=0, fixed_directions=tuple()):
            if dimension == self.dim:
                #   Base Case
                if fixed_directions in self.stencil:
                    return pdf_symbols[self.stencil.index(fixed_directions)]
                else:
                    return 0
            else:
                #   Recursive Case
                summation = sp.sympify(0)
                for d in [-1, 0, 1]:
                    next_partial = collect_partial_sums(
                        exponents, dimension=dimension + 1, fixed_directions=fixed_directions + (d,))
                    summation += next_partial * (d - self.equilibrium_velocity[dimension])**exponents[dimension]

                if dimension == 0:
                    lhs_symbol = sq_sym(monomial_symbol_base, exponents)
                    monomial_moment_eqs.append(Assignment(lhs_symbol, summation))
                else:
                    lhs_symbol = _partial_kappa_symbol(fixed_directions, exponents[dimension:])
                    partial_sums_dict[lhs_symbol] = summation
                return lhs_symbol

        for e in self.moment_exponents:
            collect_partial_sums(e)

        subexpressions = [Assignment(lhs, rhs) for lhs, rhs in partial_sums_dict.items()]

        if return_monomials:
            main_assignments = monomial_moment_eqs
        else:
            subexpressions += monomial_moment_eqs
            moment_eqs = self.mono_to_poly_matrix * sp.Matrix(self.pre_collision_monomial_symbols)
            main_assignments = [Assignment(m, v) for m, v in zip(self.pre_collision_symbols, moment_eqs)]

        symbol_gen = SymbolGen(subexpression_base)
        ac = AssignmentCollection(main_assignments, subexpressions=subexpressions,
                                  subexpression_symbol_generator=symbol_gen)
        if simplification:
            ac = self._simplify_lower_order_moments(ac, monomial_symbol_base, return_monomials)
            ac = simplification.apply(ac)
        return ac
Example #12
0
    def backward_transform(self,
                           pdf_symbols,
                           simplification=True,
                           subexpression_base='sub_k_to_f',
                           start_from_monomials=False):
        r"""Returns an assignment collection containing equations for post-collision populations, 
        expressed in terms of the post-collision polynomial moments by matrix-multiplication.

        The post-collision monomial moments :math:`\mathbf{m}_{\mathrm{post}}` are first obtained from the polynomials.
        Then, the monomial transformation matrix :math:`M_r` provided by :func:`lbmpy.moments.moment_matrix` 
        is inverted and used to compute the post-collision populations as 
        :math:`\mathbf{f}_{\mathrm{post}} = M_r^{-1} \cdot \mathbf{m}_{\mathrm{post}}`.

        **De-Aliasing**

        See `PdfsToMomentsByChimeraTransform.forward_transform`.

        **Simplifications**

        If simplification is enabled, the equations for populations :math:`f_i` and :math:`f_{\bar{i}}` 
        of opposite stencil directions :math:`\mathbf{c}_i` and :math:`\mathbf{c}_{\bar{i}} = - \mathbf{c}_i`
        are split into their symmetric and antisymmetric parts :math:`f_i^{\mathrm{sym}}, f_i^{\mathrm{anti}}`, such
        that

        .. math::

            f_i = f_i^{\mathrm{sym}} + f_i^{\mathrm{anti}}

            f_{\bar{i}} = f_i^{\mathrm{sym}} - f_i^{\mathrm{anti}}


        Args:
            pdf_symbols: List of symbols that represent the post-collision populations
            simplification: Simplification specification. See :class:`AbstractMomentTransform`
            subexpression_base: The base name used for any subexpressions of the transformation.
            start_from_monomials: Return equations for monomial moments. Use only when specifying 
                                  ``moment_exponents`` in constructor!
        """

        simplification = self._get_simp_strategy(simplification, 'backward')

        post_collision_moments = self.post_collision_symbols
        post_collision_monomial_moments = self.post_collision_monomial_symbols

        subexpressions = []
        if not start_from_monomials:
            raw_moment_eqs = self.poly_to_mono_matrix * sp.Matrix(
                post_collision_moments)
            subexpressions += [
                Assignment(rm, v) for rm, v in zip(
                    post_collision_monomial_moments, raw_moment_eqs)
            ]

        rm_to_f_vec = self.inv_moment_matrix * sp.Matrix(
            post_collision_monomial_moments)
        main_assignments = [
            Assignment(f, eq) for f, eq in zip(pdf_symbols, rm_to_f_vec)
        ]
        symbol_gen = SymbolGen(subexpression_base)

        ac = AssignmentCollection(main_assignments,
                                  subexpressions=subexpressions,
                                  subexpression_symbol_generator=symbol_gen)
        ac.add_simplification_hint('stencil', self.stencil)
        ac.add_simplification_hint('post_collision_pdf_symbols', pdf_symbols)
        if simplification:
            ac = simplification.apply(ac)
        return ac
Example #13
0
    def forward_transform(self,
                          pdf_symbols,
                          simplification=True,
                          subexpression_base='sub_f_to_m',
                          return_monomials=False):
        r"""Returns an assignment collection containing equations for pre-collision polynomial
        moments, expressed in terms of the pre-collision populations, using the raw moment chimera transform.

        The chimera transform for raw moments is given by :cite:`geier2015` :

        .. math::

            f_{xyz} &:= f_i \text{ such that } c_i = (x,y,z)^T \\
            m_{xy|\gamma} &:= \sum_{z \in \{-1, 0, 1\} } f_{xyz} \cdot z^{\gamma} \\
            m_{x|\beta \gamma} &:= \sum_{y \in \{-1, 0, 1\}} m_{xy|\gamma} \cdot y^{\beta} \\
            m_{\alpha \beta \gamma} &:= \sum_{x \in \{-1, 0, 1\}} m_{x|\beta \gamma} \cdot x^{\alpha}


        The obtained raw moments are afterward combined to the desired polynomial moments.

        **Conserved Quantity Equations**

        If given, this transform absorbs the conserved quantity equations and simplifies them
        using the monomial raw moment equations, if simplification is enabled.

        **De-Aliasing**

        If more than :math:`q` monomial moments are extracted from the polynomial set, the polynomials
        are de-aliased by eliminating aliases according to the stencil 
        using `lbmpy.moments.non_aliased_polynomial_raw_moments`.

        **Simplification**

        If simplification is enabled, the absorbed conserved quantity equations are - if possible - 
        rewritten using the monomial symbols. If the conserved quantities originate somewhere else
        than in the lower-order moments (like from an external field), they are not affected by this
        simplification.

        Args:
            pdf_symbols: List of symbols that represent the pre-collision populations
            simplification: Simplification specification. See :class:`AbstractMomentTransform`
            subexpression_base: The base name used for any subexpressions of the transformation.
            return_monomials: Return equations for monomial moments. Use only when specifying 
                              ``moment_exponents`` in constructor!
        """

        simplification = self._get_simp_strategy(simplification, 'forward')
        monomial_symbol_base = self.mono_base_pre

        def _partial_kappa_symbol(fixed_directions, remaining_exponents):
            fixed_str = '_'.join(
                str(direction)
                for direction in fixed_directions).replace('-', 'm')
            exp_str = '_'.join(
                str(exp) for exp in remaining_exponents).replace('-', 'm')
            return sp.Symbol(
                f"partial_{monomial_symbol_base}_{fixed_str}_e_{exp_str}")

        partial_sums_dict = dict()
        monomial_eqs = []

        def collect_partial_sums(exponents,
                                 dimension=0,
                                 fixed_directions=tuple()):
            if dimension == self.dim:
                #   Base Case
                if fixed_directions in self.stencil:
                    return pdf_symbols[self.stencil.index(fixed_directions)]
                else:
                    return 0
            else:
                #   Recursive Case
                summation = sp.sympify(0)
                for d in [-1, 0, 1]:
                    next_partial = collect_partial_sums(
                        exponents,
                        dimension=dimension + 1,
                        fixed_directions=fixed_directions + (d, ))
                    summation += next_partial * d**exponents[dimension]

                if dimension == 0:
                    lhs_symbol = sq_sym(monomial_symbol_base, exponents)
                    monomial_eqs.append(Assignment(lhs_symbol, summation))
                else:
                    lhs_symbol = _partial_kappa_symbol(fixed_directions,
                                                       exponents[dimension:])
                    partial_sums_dict[lhs_symbol] = summation
                return lhs_symbol

        for e in self.moment_exponents:
            collect_partial_sums(e)

        main_assignments = self.cqe.main_assignments.copy(
        ) if self.cqe is not None else []
        subexpressions = self.cqe.subexpressions.copy(
        ) if self.cqe is not None else []
        subexpressions += [
            Assignment(lhs, rhs) for lhs, rhs in partial_sums_dict.items()
        ]

        if return_monomials:
            main_assignments += monomial_eqs
        else:
            subexpressions += monomial_eqs
            moment_eqs = self.mono_to_poly_matrix * sp.Matrix(
                self.pre_collision_monomial_symbols)
            main_assignments += [
                Assignment(m, v)
                for m, v in zip(self.pre_collision_symbols, moment_eqs)
            ]

        symbol_gen = SymbolGen(subexpression_base)
        ac = AssignmentCollection(main_assignments,
                                  subexpressions=subexpressions,
                                  subexpression_symbol_generator=symbol_gen)
        ac.add_simplification_hint(
            'cq_symbols_to_moments',
            self.get_cq_to_moment_symbols_dict(monomial_symbol_base))

        if simplification:
            ac = simplification.apply(ac)
        return ac
Example #14
0
import pytest
import sympy as sp
import pystencils as ps

from pystencils import Assignment, AssignmentCollection
from pystencils.astnodes import Conditional
from pystencils.simp.assignment_collection import SymbolGen

a, b, c = sp.symbols("a b c")
x, y, z, t = sp.symbols("x y z t")
symbol_gen = SymbolGen("a")
f = ps.fields("f(2) : [2D]")
d = ps.fields("d(2) : [2D]")


def test_assignment_collection():
    ac = AssignmentCollection([Assignment(z, x + y)], [],
                              subexpression_symbol_generator=symbol_gen)

    lhs = ac.add_subexpression(t)
    assert lhs == sp.Symbol("a_0")
    ac.subexpressions.append(Assignment(t, 3))
    ac.topological_sort(sort_main_assignments=False, sort_subexpressions=True)
    assert ac.subexpressions[0].lhs == t

    assert ac.new_with_inserted_subexpression(sp.Symbol("not_defined")) == ac
    ac_inserted = ac.new_with_inserted_subexpression(t)
    ac_inserted2 = ac.new_without_subexpressions({lhs})
    assert all(a == b for a, b in zip(ac_inserted.all_assignments,
                                      ac_inserted2.all_assignments))