Example #1
0
    def _create_pdf_hierarchy(self, taylored_equation):
        """
        Expresses the expanded pdfs f^1, f^2, ..  as functions of the equilibrium f^0.
        Returns a list where element [1] is the equation for f^1 etc.
        """
        chapman_enskog_hierarchy = chapman_enskog_ansatz(
            taylored_equation,
            spatial_derivative_orders=None,
            pdfs=(['f', 0, self.order + 1], ),
            commutative=False)
        chapman_enskog_hierarchy = [
            chapman_enskog_hierarchy[i] for i in range(self.order + 1)
        ]

        inserted_hierarchy = []
        raw_hierarchy = []
        substitution_dict = {}
        for ce_eq, f_i in zip(chapman_enskog_hierarchy, self.f_syms):
            new_eq = -1 / self.collision_op_sym * (ce_eq -
                                                   self.collision_op_sym * f_i)
            raw_hierarchy.append(new_eq)
            new_eq = expand_diff_linear(new_eq.subs(substitution_dict),
                                        functions=self.f_syms +
                                        [self.force_sym])
            if new_eq:
                substitution_dict[f_i] = new_eq
            inserted_hierarchy.append(new_eq)

        return inserted_hierarchy, raw_hierarchy
Example #2
0
def free_energy_functional_3_phases(order_parameters=None, interface_width=interface_width_symbol, transformed=True,
                                    include_bulk=True, include_interface=True, expand_derivatives=True,
                                    kappa=sp.symbols("kappa_:3")):
    """Free Energy of ternary multi-component model :cite:`Semprebon2016`. """
    kappa_prime = tuple(interface_width ** 2 * k for k in kappa)
    c = sp.symbols("C_:3")

    bulk_free_energy = sum(k * C_i ** 2 * (1 - C_i) ** 2 / 2 for k, C_i in zip(kappa, c))
    surface_free_energy = sum(k * Diff(C_i) ** 2 / 2 for k, C_i in zip(kappa_prime, c))

    f = 0
    if include_bulk:
        f += bulk_free_energy
    if include_interface:
        f += surface_free_energy

    if not transformed:
        return f

    if order_parameters:
        rho, phi, psi = order_parameters
    else:
        rho, phi, psi = sp.symbols("rho phi psi")

    transformation_matrix = sp.Matrix([[1, 1, 1],
                                       [1, -1, 0],
                                       [0, 0, 1]])
    rho_def, phi_def, psi_def = transformation_matrix * sp.Matrix(c)
    order_param_to_concentration_relation = sp.solve([rho_def - rho, phi_def - phi, psi_def - psi], c)

    f = f.subs(order_param_to_concentration_relation)
    if expand_derivatives:
        f = expand_diff_linear(f, functions=order_parameters)

    return f, transformation_matrix
Example #3
0
def chemical_potentials_from_free_energy(free_energy, order_parameters=None):
    """Computes chemical potentials as functional derivative of free energy."""
    symbols = free_energy.atoms(sp.Symbol)
    if order_parameters is None:
        order_parameters = [s for s in symbols if s.name.startswith(order_parameter_symbol_name)]
        order_parameters.sort(key=lambda e: e.name)
        order_parameters = order_parameters[:-1]
    constants = [s for s in symbols if s not in order_parameters]
    return sp.Matrix([expand_diff_linear(functional_derivative(free_energy, op), constants=constants)
                      for op in order_parameters])
Example #4
0
def get_taylor_expanded_lb_equation(pdf_symbol_name="f",
                                    pdfs_after_collision_operator="\\Omega f",
                                    velocity_name="c",
                                    dim=3,
                                    taylor_order=2,
                                    shift=True):
    dim_labels = [sp.Rational(i, 1) for i in range(dim)]

    c = sp.Matrix([
        expanded_symbol(velocity_name, subscript=label) for label in dim_labels
    ])
    dt, t = sp.symbols("Delta_t t")
    pdf = sp.Symbol(pdf_symbol_name)
    collided_pdf = sp.Symbol(pdfs_after_collision_operator)

    dt_operator = DiffOperator(target=t)
    dx_operator = sp.Matrix([DiffOperator(target=l) for l in dim_labels])

    taylor_operator = sum(dt**k * (dt_operator + c.dot(dx_operator))**k /
                          sp.functions.factorial(k)
                          for k in range(1, taylor_order + 1))

    functions = [pdf, collided_pdf]
    eq_4_5 = taylor_operator - dt * collided_pdf
    applied_eq_4_5 = expand_diff_linear(
        DiffOperator.apply(eq_4_5, pdf, apply_to_constants=False), functions)

    if shift:
        operator = ((dt / 2) * (dt_operator + c.dot(dx_operator))).expand()
        op_times_eq_4_5 = expand_diff_linear(
            DiffOperator.apply(operator,
                               applied_eq_4_5,
                               apply_to_constants=False), functions).expand()
        op_times_eq_4_5 = normalize_diff_order(op_times_eq_4_5, functions)
        eq_4_7 = (applied_eq_4_5 - op_times_eq_4_5).subs(
            dt**(taylor_order + 1), 0)
    else:
        eq_4_7 = applied_eq_4_5.subs(dt**(taylor_order + 1), 0)

    eq_4_7 = eq_4_7.subs(dt, 1)
    return eq_4_7.expand()
Example #5
0
    def determine_viscosities(self, coordinate):
        """Matches the first order term of the momentum equation to Navier stokes.

        Automatically neglects higher order velocity terms and rho derivatives

        The bulk viscosity is predicted differently than by the normal Navier Stokes analysis...why??

        Args:
            coordinate: which momentum equation to use i.e. x,y or z, to approximate Navier Stokes
                        all have to return the same result
        """
        dim = self.method.dim

        def d(arg, *args):
            """Shortcut to create nested derivatives"""
            assert arg is not None
            args = sorted(args,
                          reverse=True,
                          key=lambda e: e.name
                          if isinstance(e, sp.Symbol) else e)
            res = arg
            for i in args:
                res = Diff(res, i)
            return res

        s = functools.partial(multidimensional_sum, dim=dim)
        kd = kronecker_delta

        eta, eta_b = sp.symbols("nu nu_B")
        u = sp.symbols("u_:3")[:dim]
        a = coordinate
        navier_stokes_ref = eta * sum(
            d(u[a], b, b) + d(u[b], b, a)
            for b, in s(1)) + (eta_b - 2 * eta / 3) * sum(
                d(u[g], b, g) * kd(a, b) for b, g in s(2))
        navier_stokes_ref = -navier_stokes_ref.expand()

        first_order_terms = self.get_momentum_equation(coordinate, order=1)
        first_order_terms = remove_higher_order_u(first_order_terms)
        first_order_terms = expand_diff_linear(first_order_terms,
                                               constants=[sp.Symbol("rho")])

        match_coeff_equations = []
        for item in navier_stokes_ref.atoms(Diff):
            match_coeff_equations.append(
                navier_stokes_ref.coeff(item) - first_order_terms.coeff(item))
        return sp.solve(match_coeff_equations, [eta, eta_b])
Example #6
0
    def _compute_moments(self, recombined_eq, symbols_to_values):
        eq = recombined_eq.expand()
        assert eq.func is sp.Add

        new_products = []
        for product in eq.args:
            assert product.func is sp.Mul

            derivative = None

            new_prod = 1
            for arg in reversed(normalize_product(product)):
                if isinstance(arg, Diff):
                    assert derivative is None, "More than one derivative term in the product"
                    derivative = arg
                    arg = arg.get_arg_recursive(
                    )  # new argument is inner part of derivative

                if arg in symbols_to_values:
                    arg = symbols_to_values[arg]

                have_shape = hasattr(arg, 'shape') and hasattr(
                    new_prod, 'shape')
                if have_shape and arg.shape == new_prod.shape and arg.shape[
                        1] == 1:
                    # since sympy 1.9 sp.matrix_multiply_elementwise does not work anymore in this case
                    new_prod = sp.Matrix(np.multiply(new_prod, arg))
                else:
                    new_prod = arg * new_prod
                if new_prod == 0:
                    break

            if new_prod == 0:
                continue

            new_prod = sp.expand(sum(new_prod))

            if derivative is not None:
                new_prod = derivative.change_arg_recursive(new_prod)

            new_products.append(new_prod)

        return normalize_diff_order(
            expand_diff_linear(sp.Add(*new_products),
                               functions=self.physical_variables))
Example #7
0
 def diff_simplify(eq):
     variables = eq.atoms(CeMoment)
     variables.update(funcs)
     return expand_diff_products(expand_diff_linear(eq, variables)).expand()
Example #8
0
def chapman_enskog_ansatz(equation,
                          time_derivative_orders=(1, 3),
                          spatial_derivative_orders=(1, 2),
                          pdfs=(['f', 0, 3], ['\\Omega f', 1, 3]),
                          commutative=True):
    r"""Uses a Chapman Enskog Ansatz to expand given equation.

    Args:
        equation: equation to expand
        time_derivative_orders: tuple describing range for time derivative to expand
        spatial_derivative_orders: tuple describing range for spatial derivatives to expand
        pdfs: symbols to expand: sequence of triples (symbol_name, start_order, end_order)
        commutative: can be set to False to have non-commutative pdf symbols
    Returns:
        tuple mapping epsilon order to equation
    """
    t, eps = sp.symbols("t epsilon")

    # expand time derivatives
    if time_derivative_orders:
        equation = chapman_enskog_derivative_expansion(equation, t, eps,
                                                       *time_derivative_orders)

    # expand spatial derivatives
    if spatial_derivative_orders:
        spatial_derivatives = [
            a for a in equation.atoms(Diff) if str(a.target) != 't'
        ]
        labels = set(a.target for a in spatial_derivatives)
        for label in labels:
            equation = chapman_enskog_derivative_expansion(
                equation, label, eps, *spatial_derivative_orders)

    # expand pdfs
    subs_dict = {}
    expanded_pdf_symbols = []

    max_expansion_order = spatial_derivative_orders[
        1] if spatial_derivative_orders else 10
    for pdf_name, start_order, stop_order in pdfs:
        if isinstance(pdf_name, sp.Symbol):
            pdf_name = pdf_name.name
        expanded_pdf_symbols += [
            expanded_symbol(pdf_name, superscript=i, commutative=commutative)
            for i in range(start_order, stop_order)
        ]
        pdf_symbol = sp.Symbol(pdf_name, commutative=commutative)
        subs_dict[pdf_symbol] = sum(
            eps**i *
            expanded_symbol(pdf_name, superscript=i, commutative=commutative)
            for i in range(start_order, stop_order))
        max_expansion_order = max(max_expansion_order, stop_order)
    equation = equation.subs(subs_dict)
    equation = expand_diff_linear(
        equation, functions=expanded_pdf_symbols).expand().collect(eps)
    result = {
        eps_order: equation.coeff(eps**eps_order)
        for eps_order in range(1, 2 * max_expansion_order)
    }
    result[0] = equation.subs(eps, 0)
    return result
Example #9
0
def take_moments(eqn,
                 pdf_to_moment_name=(('f', '\\Pi'), ('\\Omega f',
                                                     '\\Upsilon')),
                 velocity_name='c',
                 max_expansion=5,
                 use_one_neighborhood_aliasing=False):

    pdf_symbols = [
        tuple(
            expanded_symbol(name, superscript=i) for i in range(max_expansion))
        for name, _ in pdf_to_moment_name
    ]

    velocity_terms = tuple(
        expanded_symbol(velocity_name, subscript=i) for i in range(3))

    def determine_f_index(factor):
        FIndex = namedtuple("FIndex", ['moment_name', 'superscript'])
        for symbol_list_id, pdf_symbols_element in enumerate(pdf_symbols):
            try:
                return FIndex(pdf_to_moment_name[symbol_list_id][1],
                              pdf_symbols_element.index(factor))
            except ValueError:
                pass
        return None

    def handle_product(product_term):
        f_index = None
        derivative_term = None
        c_indices = []
        rest = 1
        for factor in normalize_product(product_term):
            if isinstance(factor, Diff):
                assert f_index is None
                f_index = determine_f_index(factor.get_arg_recursive())
                derivative_term = factor
            elif factor in velocity_terms:
                c_indices += [velocity_terms.index(factor)]
            else:
                new_f_index = determine_f_index(factor)
                if new_f_index is None:
                    rest *= factor
                else:
                    assert not (new_f_index and f_index)
                    f_index = new_f_index

        moment_tuple = [0] * len(velocity_terms)
        for c_idx in c_indices:
            moment_tuple[c_idx] += 1
        moment_tuple = tuple(moment_tuple)

        if use_one_neighborhood_aliasing:
            moment_tuple = non_aliased_moment(moment_tuple)
        result = CeMoment(f_index.moment_name, moment_tuple,
                          f_index.superscript)
        if derivative_term is not None:
            result = derivative_term.change_arg_recursive(result)
        result *= rest
        return result

    functions = sum(pdf_symbols, ())
    eqn = expand_diff_linear(eqn, functions).expand()

    if eqn.func == sp.Mul:
        return handle_product(eqn)
    else:
        assert eqn.func == sp.Add
        return sum(handle_product(t) for t in eqn.args)
Example #10
0
    def __init__(self, method, constants=None):
        cqc = method.conserved_quantity_computation
        self._method = method
        self._moment_cache = LbMethodEqMoments(method)
        self.rho = cqc.defined_symbols(order=0)[1]
        self.u = cqc.defined_symbols(order=1)[1]
        self.t = sp.Symbol("t")
        self.epsilon = sp.Symbol("epsilon")

        taylored_lb_eq = get_taylor_expanded_lb_equation(dim=self._method.dim)
        self.equations_by_order = chapman_enskog_ansatz(taylored_lb_eq)

        # Taking moments
        c = sp.Matrix([
            expanded_symbol("c", subscript=i) for i in range(self._method.dim)
        ])
        moments_until_order1 = [1] + list(c)
        moments_order2 = [c_i * c_j for c_i, c_j in symmetric_product(c, c)]

        symbolic_relaxation_rates = [
            rr for rr in method.relaxation_rates if isinstance(rr, sp.Symbol)
        ]
        if constants is None:
            constants = set(symbolic_relaxation_rates)
        else:
            constants.update(symbolic_relaxation_rates)

        self.constants = constants

        o_eps_moments1 = [
            expand_diff_linear(self._take_and_insert_moments(
                self.equations_by_order[1] * moment),
                               constants=constants)
            for moment in moments_until_order1
        ]
        o_eps_moments2 = [
            expand_diff_linear(self._take_and_insert_moments(
                self.equations_by_order[1] * moment),
                               constants=constants)
            for moment in moments_order2
        ]
        o_eps_sq_moments1 = [
            expand_diff_linear(self._take_and_insert_moments(
                self.equations_by_order[2] * moment),
                               constants=constants)
            for moment in moments_until_order1
        ]

        self._equationsWithHigherOrderMoments = [
            self._ce_recombine(ord1 * self.epsilon + ord2 * self.epsilon**2)
            for ord1, ord2 in zip(o_eps_moments1, o_eps_sq_moments1)
        ]

        self.higher_order_moments = compute_higher_order_moment_subs_dict(
            tuple(o_eps_moments1 + o_eps_moments2))

        # Match to Navier stokes
        compressible, pressure, sigma = match_to_navier_stokes(
            self._equationsWithHigherOrderMoments)
        self.compressible = compressible
        self.pressure_equation = pressure
        self._sigmaWithHigherOrderMoments = sigma
        self._sigma = sigma.subs(self.higher_order_moments).expand().applyfunc(
            self._ce_recombine)
        self._sigmaWithoutErrorTerms = remove_error_terms(self._sigma)
Example #11
0
    def __init__(self, method, order=4):
        self.method = method
        dim = method.dim
        moment_computation = LbMethodEqMoments(method)

        eps, collision_operator, f, dt = sp.symbols("epsilon B f Delta_t")
        self.dt = dt
        expanded_pdf_symbols = [
            expanded_symbol("f", superscript=i) for i in range(0, order + 1)
        ]
        feq = expanded_pdf_symbols[0]
        c = sp.Matrix([expanded_symbol("c", subscript=i) for i in range(dim)])
        dx = sp.Matrix([DiffOperator(target=l) for l in range(dim)])
        differential_operator = sum((dt * eps * c.dot(dx))**n / sp.factorial(n)
                                    for n in range(1, order + 1))
        taylor_expansion = DiffOperator.apply(differential_operator.expand(),
                                              f,
                                              apply_to_constants=False)
        eps_dict = chapman_enskog_ansatz(
            taylor_expansion,
            spatial_derivative_orders=
            None,  # do not expand the differential operator
            pdfs=(['f', 0, order + 1], ))  # expand only the 'f' terms

        self.scale_hierarchy = [
            -collision_operator * eps_dict[i] for i in range(0, order + 1)
        ]
        self.scale_hierarchy_raw = self.scale_hierarchy.copy()

        expanded_pdfs = [feq, self.scale_hierarchy[1]]
        subs_dict = {expanded_pdf_symbols[1]: self.scale_hierarchy[1]}

        for i in range(2, len(self.scale_hierarchy)):
            eq = self.scale_hierarchy[i].subs(subs_dict)
            eq = expand_diff_linear(eq, functions=expanded_pdf_symbols)
            eq = normalize_diff_order(eq, functions=expanded_pdf_symbols)
            subs_dict[expanded_pdf_symbols[i]] = eq
            expanded_pdfs.append(eq)
        self.scale_hierarchy = expanded_pdfs

        constants = sp.Matrix(method.relaxation_rates).atoms(sp.Symbol)
        recombined = -sum(self.scale_hierarchy[n]
                          for n in range(1, order + 1))  # Eq 18a
        recombined = sp.cancel(
            recombined /
            (dt * collision_operator)).expand()  # cancel common factors

        def handle_postcollision_values(expr):
            expr = expr.expand()
            assert isinstance(expr, sp.Add)
            result = 0
            for summand in expr.args:

                moment = summand.atoms(CeMoment)
                moment = moment.pop()
                collision_operator_exponent = normalize_product(summand).count(
                    collision_operator)
                if collision_operator_exponent == 0:
                    result += summand
                else:
                    substitutions = {
                        collision_operator:
                        1,
                        moment:
                        -moment_computation.get_post_collision_moment(
                            moment, -collision_operator_exponent),
                    }
                    result += summand.subs(substitutions)

            return result

        # Continuity equation (mass transport)
        cont_eq = take_moments(recombined, max_expansion=(order + 1) * 2)
        cont_eq = handle_postcollision_values(cont_eq)
        cont_eq = expand_diff_linear(cont_eq,
                                     constants=constants).expand().collect(dt)
        self.continuity_equation_with_moments = cont_eq
        cont_eq = insert_moments(cont_eq,
                                 moment_computation,
                                 use_solvability_conditions=False)
        cont_eq = expand_diff_linear(cont_eq,
                                     constants=constants).expand().collect(dt)
        self.continuity_equation = cont_eq

        # Momentum equation (momentum transport)
        self.momentum_equations_with_moments = []
        self.momentum_equations = []
        for h in range(dim):
            mom_eq = take_moments(recombined * c[h],
                                  max_expansion=(order + 1) * 2)
            mom_eq = handle_postcollision_values(mom_eq)
            mom_eq = expand_diff_linear(
                mom_eq, constants=constants).expand().collect(dt)
            self.momentum_equations_with_moments.append(mom_eq)
            mom_eq = insert_moments(mom_eq,
                                    moment_computation,
                                    use_solvability_conditions=False)
            mom_eq = expand_diff_linear(
                mom_eq, constants=constants).expand().collect(dt)
            self.momentum_equations.append(mom_eq)