Ejemplo n.º 1
0
 def match_continuity_eq(continuity_eq):
     continuity_eq = diff_simplify(continuity_eq)
     is_compressible = u[0] * Diff(rho, 0) in continuity_eq.args
     factor = rho if is_compressible else 1
     ref_continuity_eq = diff_simplify(
         Diff(rho, t) + sum(Diff(factor * u[i], i) for i in range(dim)))
     return ref_continuity_eq - continuity_eq, is_compressible
Ejemplo n.º 2
0
def free_energy_interfacial(c, surface_tensions, a, epsilon):
    n = len(c)
    # this is different than in the paper: the sum is under the condition i < j, not i != j
    # otherwise the model does not correctly reduce to equation 1.5
    return -epsilon * a / 2 * sum(
        surface_tensions[i, j] * Diff(c[i]) * Diff(c[j]) for i in range(n)
        for j in range(i))
Ejemplo n.º 3
0
    def force_component(b):
        r = -sum(Diff(pressure_tensor[a, b], a) for a in range(dim))
        r = expand_diff_full(r, functions=functions)

        if pbs is not None:
            r += 2 * Diff(pbs, b) * pbs

        return r
Ejemplo n.º 4
0
 def get_shear_viscosity_candidates(self):
     result = set()
     dim = self._method.dim
     for i, j in symmetric_product(range(dim),
                                   range(dim),
                                   with_diagonal=False):
         result.add(-sp.cancel(self._sigmaWithoutErrorTerms[i, j] /
                               (Diff(self.u[i], j) + Diff(self.u[j], i))))
     return result
Ejemplo n.º 5
0
def substitute_laplacian_by_sum(eq, dim):
    """Substitutes abstract Laplacian represented by ∂∂ by a sum over all dimensions
    i.e. in case of 3D: ∂∂ is replaced by ∂0∂0 + ∂1∂1 + ∂2∂2

    Args:
        eq: the term where the substitutions should be made
        dim: spatial dimension, in example above, 3
    """
    functions = [d.args[0] for d in eq.atoms(Diff)]
    substitutions = {Diff(Diff(op)): sum(Diff(Diff(op, i), i) for i in range(dim))
                     for op in functions}
    return expand_diff_full(eq.subs(substitutions))
Ejemplo n.º 6
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
Ejemplo n.º 7
0
def remove_error_terms(expr):
    rho_diffs_to_zero = {Diff(sp.Symbol("rho"), i): 0 for i in range(3)}
    expr = expr.subs(rho_diffs_to_zero)
    if isinstance(expr, sp.Matrix):
        expr = expr.applyfunc(remove_higher_order_u)
    else:
        expr = remove_higher_order_u(expr.expand())
    return sp.cancel(expr.expand())
Ejemplo n.º 8
0
def free_energy_high_density_ratio(eos, density, density_gas, density_liquid, c_liquid_1, c_liquid_2, lambdas, kappas):
    """ Free energy for a ternary system with high density ratio :cite:`Wohrwag2018`

    Args:
        eos: equation of state, has to depend on exactly on symbol, the density
        density: symbol for density
        density_gas: numeric value for gas density (can be obtained by `maxwell_construction`)
        density_liquid: numeric value for liquid density (can be obtained by `maxwell_construction`)
        c_liquid_1: symbol for concentration of first liquid phase
        c_liquid_2: symbol for concentration of second liquid phase
        lambdas: pre-factors of bulk terms, lambdas[0] multiplies the density term, lambdas[1] the first liquid and
                 lambdas[2] the second liquid phase
        kappas: pre-factors of interfacial terms, order same as for lambdas

    Returns:
        free energy expression
    """
    assert eos.atoms(sp.Symbol) == {density}
    # ---- Part 1: contribution of equation of state, ψ_eos
    symbolic_integration_constant = sp.Dummy(real=True)
    psi_eos = free_energy_from_eos(eos, density, symbolic_integration_constant)
    # accuracy problems in free_energy_from_eos can lead to complex solutions for integration constant
    psi_eos = remove_small_floats(psi_eos, 1e-14)

    # integration constant is determined from the condition ψ(ρ_gas) == ψ(ρ_liquid)
    equal_psi_condition = psi_eos.subs(density, density_gas) - psi_eos.subs(density, density_liquid)
    solve_res = sp.solve(equal_psi_condition, symbolic_integration_constant)
    assert len(solve_res) == 1
    integration_constant = solve_res[0]
    psi_eos = psi_eos.subs(symbolic_integration_constant, integration_constant)

    # energy is shifted by ψ_0 = ψ(ρ_gas) which is also ψ(ρ_liquid) by construction
    psi_0 = psi_eos.subs(density, density_gas)

    # ---- Part 2: standard double well potential as bulk term, and gradient squares as interface term
    def f(c):
        return c ** 2 * (1 - c) ** 2

    f_bulk = (lambdas[0] / 2 * (psi_eos - psi_0)
              + lambdas[1] / 2 * f(c_liquid_1)
              + lambdas[2] / 2 * f(c_liquid_2))
    f_interface = (kappas[0] / 2 * Diff(density) ** 2
                   + kappas[1] / 2 * Diff(c_liquid_1) ** 2
                   + kappas[2] / 2 * Diff(c_liquid_2) ** 2)
    return f_bulk + f_interface
Ejemplo n.º 9
0
def chapman_enskog_derivative_recombination(expr,
                                            label,
                                            eps=sp.Symbol("epsilon"),
                                            start_order=1,
                                            stop_order=4):
    """Inverse operation of 'chapman_enskog_derivative_expansion'"""
    expr = expr.expand()
    diffs = [
        d for d in expr.atoms(Diff)
        if d.target == label and d.superscript == stop_order - 1
    ]
    for d in diffs:
        substitution = Diff(d.arg, label)
        substitution -= sum([
            eps**i * Diff(d.arg, label, i)
            for i in range(start_order, stop_order - 1)
        ])
        expr = expr.subs(d, substitution / eps**(stop_order - 1))
    return expr
Ejemplo n.º 10
0
 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
Ejemplo n.º 11
0
    def does_approximate_navier_stokes(self):
        """Returns a set of equations that are required in order for the method to approximate Navier Stokes equations
        up to second order"""
        conditions = {0}
        dim = self._method.dim
        assert dim > 1
        # Check that shear viscosity does not depend on any u derivatives - create conditions (equations) that
        # have to be fulfilled for this to be the case
        viscosity_reference = self._sigmaWithoutErrorTerms[0,
                                                           1].expand().coeff(
                                                               Diff(
                                                                   self.u[0],
                                                                   1))
        for i, j in symmetric_product(range(dim),
                                      range(dim),
                                      with_diagonal=False):
            term = self._sigmaWithoutErrorTerms[i, j]
            equal_cross_term_condition = sp.expand(
                term.coeff(Diff(self.u[i], j)) - viscosity_reference)
            term = term.subs({Diff(self.u[i], j): 0, Diff(self.u[j], i): 0})

            conditions.add(equal_cross_term_condition)
            for k in range(dim):
                symmetric_term_condition = term.coeff(Diff(self.u[k], k))
                conditions.add(symmetric_term_condition)
            term = term.subs({Diff(self.u[k], k): 0 for k in range(dim)})
            conditions.add(term)

        bulk_candidates = list(
            self.get_bulk_viscosity_candidates(-viscosity_reference))
        if len(bulk_candidates) > 0:
            for i in range(1, len(bulk_candidates)):
                conditions.add(bulk_candidates[0] - bulk_candidates[i])

        return conditions
Ejemplo n.º 12
0
    def match_moment_eqs(moment_eqs, is_compressible):
        shear_and_pressure_eqs = []
        for i, mom_eq in enumerate(moment_eqs):
            factor = rho if is_compressible else 1
            ref = diff_simplify(
                Diff(factor * u[i], t) +
                sum(Diff(factor * u[i] * u[j], j) for j in range(dim)))
            shear_and_pressure_eqs.append(diff_simplify(moment_eqs[i]) - ref)

        # new_filtered pressure term
        coefficient_arg_sets = []
        for i, eq in enumerate(shear_and_pressure_eqs):
            coefficient_arg_sets.append(set())
            eq = eq.expand()
            assert eq.func == sp.Add
            for term in eq.args:
                if term.atoms(CeMoment):
                    continue
                candidate_list = [e for e in term.atoms(Diff) if e.target == i]
                if len(candidate_list) != 1:
                    continue
                coefficient_arg_sets[i].add(
                    (term / candidate_list[0], candidate_list[0].arg))
        pressure_terms = set.intersection(*coefficient_arg_sets)

        sigma_ = sp.zeros(dim)
        error_terms_ = []
        for i, shear_and_pressure_eq in enumerate(shear_and_pressure_eqs):
            eq_without_pressure = shear_and_pressure_eq - sum(
                coeff * Diff(arg, i) for coeff, arg in pressure_terms)
            for d in eq_without_pressure.atoms(Diff):
                eq_without_pressure = eq_without_pressure.collect(d)
                sigma_[i, d.target] += eq_without_pressure.coeff(d) * d.arg
                eq_without_pressure = eq_without_pressure.subs(d, 0)

            error_terms_.append(eq_without_pressure)
        pressure_ = [coeff * arg for coeff, arg in pressure_terms]

        return pressure_, sigma_, error_terms_
def force_computation_equivalence(dim=3, num_phases=4):

    def Λ(i, j):
        if i > j:
            i, j = j, i
        return sp.Symbol("Lambda_{}{}".format(i, j))

    φ = sp.symbols("φ_:{}".format(num_phases))
    f_if = sum(Λ(α, β) / 2 * Diff(φ[α]) * Diff(φ[β])
               for α in range(num_phases) for β in range(num_phases))
    μ = chemical_potentials_from_free_energy(f_if, order_parameters=φ)
    μ = substitute_laplacian_by_sum(μ, dim=dim)

    p = sp.Matrix(dim, dim,
                  lambda i, j: pressure_tensor_interface_component_new(f_if, φ, dim, i, j))
    force_from_p = force_from_pressure_tensor(p, functions=φ)

    for d in range(dim):
        t1 = normalize_diff_order(force_from_p[d])
        t2 = expand_diff_full(force_from_phi_and_mu(φ, dim=dim, mu=μ)[d], functions=φ).expand()
        assert t1 - t2 == 0
    print("Success")
Ejemplo n.º 14
0
def forth_order_isotropic_discretize(field):
    second_neighbor_stencil = [(i, j) for i in (-2, -1, 0, 1, 2)
                               for j in (-2, -1, 0, 1, 2)]
    x_diff = FiniteDifferenceStencilDerivation((0, ), second_neighbor_stencil)
    x_diff.set_weight((2, 0), sp.Rational(1, 10))
    x_diff.assume_symmetric(0, anti_symmetric=True)
    x_diff.assume_symmetric(1)
    x_diff_stencil = x_diff.get_stencil(isotropic=True)

    y_diff = FiniteDifferenceStencilDerivation((1, ), second_neighbor_stencil)
    y_diff.set_weight((0, 2), sp.Rational(1, 10))
    y_diff.assume_symmetric(1, anti_symmetric=True)
    y_diff.assume_symmetric(0)
    y_diff_stencil = y_diff.get_stencil(isotropic=True)

    substitutions = {}
    for i in range(field.index_shape[0]):
        substitutions.update({
            Diff(field(i), 0): x_diff_stencil.apply(field(i)),
            Diff(field(i), 1): y_diff_stencil.apply(field(i))
        })
    return substitutions
Ejemplo n.º 15
0
def chapman_enskog_derivative_expansion(expr,
                                        label,
                                        eps=sp.Symbol("epsilon"),
                                        start_order=1,
                                        stop_order=4):
    """Substitutes differentials with given target and no superscript by the sum:
    eps**(start_order) * Diff(superscript=start_order)   + ... + eps**(stop_order) * Diff(superscript=stop_order)"""
    diffs = [d for d in expr.atoms(Diff) if d.target == label]
    subs_dict = {
        d: sum([
            eps**i * Diff(d.arg, d.target, i)
            for i in range(start_order, stop_order)
        ])
        for d in diffs
    }
    return expr.subs(subs_dict)
Ejemplo n.º 16
0
def free_energy_functional_n_phases_penalty_term(order_parameters, interface_width=interface_width_symbol, kappa=None,
                                                 penalty_term_factor=0.01):
    num_phases = len(order_parameters)
    if kappa is None:
        kappa = sp.symbols(f"kappa_:{num_phases}")
    if not hasattr(kappa, "__len__"):
        kappa = [kappa] * num_phases

    def f(x):
        return x ** 2 * (1 - x) ** 2

    bulk = sum(f(c) * k / 2 for c, k in zip(order_parameters, kappa))
    interface = sum(Diff(c) ** 2 / 2 * interface_width ** 2 * k
                    for c, k in zip(order_parameters, kappa))

    bulk_penalty_term = (1 - sum(c for c in order_parameters)) ** 2
    return bulk + interface + penalty_term_factor * bulk_penalty_term
Ejemplo n.º 17
0
 def get_bulk_viscosity_candidates(self, viscosity=None):
     sigma = self._sigmaWithoutErrorTerms
     assert self._sigmaWithHigherOrderMoments.is_square
     result = set()
     if viscosity is None:
         viscosity = self.get_dynamic_viscosity()
     for i in range(sigma.shape[0]):
         bulk_term = sigma[i, i] + 2 * viscosity * Diff(self.u[i], i)
         bulk_term = bulk_term.expand()
         for d in bulk_term.atoms(Diff):
             bulk_term = bulk_term.collect(d)
             result.add(bulk_term.coeff(d))
             bulk_term = bulk_term.subs(d, 0)
         if bulk_term != 0:
             return set()
     if len(result) == 0:
         result.add(0)
     return result
Ejemplo n.º 18
0
def test_fs():
    f = sp.Symbol("f", commutative=False)

    a = Diff(Diff(Diff(f, 1), 0), 0)
    assert a.is_commutative is False
    print(str(a))

    assert diff(f) == f

    x, y = sp.symbols("x, y")
    collected_terms = collect_diffs(diff(x, 0, 0))
    assert collected_terms == Diff(Diff(x, 0, -1), 0, -1)

    src = fields("src : double[2D]")
    expr = sp.Add(Diff(Diff(src[0, 0])), 10)
    expected = Diff(Diff(src[0, 0], 0, -1), 0, -1) + Diff(
        Diff(src[0, 0], 1, -1), 1, -1) + 10
    result = replace_generic_laplacian(expr, 3)
    assert result == expected
Ejemplo n.º 19
0
def free_energy_functional_n_phases(num_phases=None, surface_tensions=symmetric_symbolic_surface_tension,
                                    interface_width=interface_width_symbol, order_parameters=None,
                                    include_bulk=True, include_interface=True, symbolic_lambda=False,
                                    symbolic_dependent_variable=False,
                                    f1=lambda c: c ** 2 * (1 - c) ** 2,
                                    f2=lambda c: c ** 2 * (1 - c) ** 2,
                                    triple_point_energy=0):
    r"""
    Returns a symbolic expression for the free energy of a system with N phases and
    specified surface tensions. The total free energy is the sum of a bulk and an interface component.

    .. math ::

        F_{bulk} = \int \frac{3}{\sqrt{2} \eta}
            \sum_{\substack{\alpha,\beta=0 \\ \alpha \neq \beta}}^{N-1}
            \frac{\tau(\alpha,\beta)}{2} \left[ f(\phi_\alpha) + f(\phi_\beta)
            - f(\phi_\alpha + \phi_\beta)  \right] \; d\Omega

        F_{interface} = \int \sum_{\alpha,\beta=0}^{N-2} \frac{\Lambda_{\alpha\beta}}{2}
                        \left( \nabla \phi_\alpha \cdot \nabla \phi_\beta \right)\; d\Omega

        \Lambda_{\alpha \beta} = \frac{3 \eta}{\sqrt{2}}  \left[ \tau(\alpha,N-1) + \tau(\beta,N-1) -
                                 \tau(\alpha,\beta)  \right]

        f(c) = c^2( 1-c)^2

    Args:
        num_phases: number of phases, called N above
        surface_tensions: surface tension function, called with two phase indices (two integers)
        interface_width: called :math:`\eta` above, controls the interface width
        order_parameters: explicitly
        f1: bulk energy is computed as f1(c_i) + f1(c_j) - f2(c_i + c_j)
        f2: see f2
        triple_point_energy: term multiplying c[i]*c[j]*c[k] for i < j < k
      Parameters useful for viewing / debugging the function
        include_bulk: if false no bulk term is added
        include_interface:if false no interface contribution is added
        symbolic_lambda: surface energy coefficient is represented by symbol, not in expanded form
        symbolic_dependent_variable: last phase variable is defined as 1-other_phase_vars, if this is set to True
                                     it is represented by phi_A for better readability
    """
    assert not (num_phases is None and order_parameters is None)
    if order_parameters is None:
        phi = symbolic_order_parameters(num_phases - 1)
    else:
        phi = order_parameters
        num_phases = len(phi) + 1

    if not symbolic_dependent_variable:
        phi = tuple(phi) + (1 - sum(phi),)
    else:
        phi = tuple(phi) + (sp.Symbol("phi_D"), )

    if callable(surface_tensions):
        surface_tensions = surface_tensions
    else:
        t = surface_tensions

        def surface_tensions(i, j):
            return t if i != j else 0

    # Compared to handwritten notes we scale the interface width parameter here to obtain the correct
    # equations for the interface profile and the surface tensions i.e. to pass tests
    # test_analyticInterfaceSolution and test_surfaceTensionDerivation
    interface_width *= sp.sqrt(2)

    def lambda_coeff(k, l):
        if symbolic_lambda:
            symbol_names = (k, l) if k < l else (l, k)
            return sp.Symbol(f"Lambda_{symbol_names[0]}{symbol_names[1]}")
        n = num_phases - 1
        if k == l:
            assert surface_tensions(l, l) == 0
        return 3 / sp.sqrt(2) * interface_width * (surface_tensions(k, n)
                                                   + surface_tensions(l, n) - surface_tensions(k, l))

    def bulk_term(i, j):
        return surface_tensions(i, j) / 2 * (f1(phi[i]) + f1(phi[j]) - f2(phi[i] + phi[j]))

    f_bulk = 3 / sp.sqrt(2) / interface_width * sum(bulk_term(i, j) for i, j in multi_sum(2, num_phases) if i != j)
    f_interface = sum(lambda_coeff(i, j) / 2 * Diff(phi[i]) * Diff(phi[j]) for i, j in multi_sum(2, num_phases - 1))

    for i in range(len(phi)):
        for j in range(i):
            for k in range(j):
                f_bulk += triple_point_energy * phi[i] * phi[j] * phi[k]

    result = 0
    if include_bulk:
        result += f_bulk
    if include_interface:
        result += f_interface
    return result
Ejemplo n.º 20
0
 def lapl(e):
     return sum(Diff(Diff(e, i), i) for i in range(dh.dim))
Ejemplo n.º 21
0
def force_from_phi_and_mu(order_parameters, dim, mu=None):
    if mu is None:
        mu = sp.symbols(f"mu_:{len(order_parameters)}")

    return sp.Matrix([sum(- c_i * Diff(mu_i, a) for c_i, mu_i in zip(order_parameters, mu))
                      for a in range(dim)])