Exemplo n.º 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
Exemplo n.º 2
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
Exemplo n.º 3
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