Esempio n. 1
0
def test_surface_tension_derivation():
    """Computes the excess free energy per unit area of an interface transition between two phases
    which should give exactly the surface tension parameter"""
    num_phases = 4
    eta = sp.Symbol("eta")

    free_energy = free_energy_functional_n_phases(num_phases,
                                                  interface_width=eta)
    phi = symbolic_order_parameters(num_phases)

    x = sp.Symbol("x")
    sol = analytic_interface_profile(x, interface_width=eta)

    for a, b in [(1, 3), (0, 1)]:
        substitutions = {phi[a]: sol}
        if b < len(phi) - 1:
            substitutions[phi[b]] = 1 - sol
        for i, phi_i in enumerate(phi[:-1]):
            if i not in (a, b):
                substitutions[phi_i] = 0

        free_energy_2_phase = sp.simplify(
            evaluate_diffs(free_energy.subs(substitutions), x))
        result = cosh_integral(free_energy_2_phase, x)
        assert result == symmetric_symbolic_surface_tension(a, b)
Esempio n. 2
0
def test_analytic_interface_solution():
    """Ensures that the tanh is an analytical solution for the prescribed free energy / chemical potential
    """
    num_phases = 4
    phi = symbolic_order_parameters(num_phases - 1)
    free_energy = free_energy_functional_n_phases(num_phases,
                                                  order_parameters=phi).subs(
                                                      {p: 0
                                                       for p in phi[1:]})
    mu_diff_eq = chemical_potentials_from_free_energy(free_energy, [phi[0]])[0]

    x = sp.Symbol("x")
    sol = analytic_interface_profile(x)

    inserted = mu_diff_eq.subs(phi[0], sol)
    assert sp.expand(evaluate_diffs(inserted, x)) == 0
Esempio n. 3
0
def tanh_test(pf_step, phase0, phase1, expected_interface_width=1, time_steps=10000):
    """
    Initializes a sharp interface and checks if tanh-shaped profile is developing

    Args:
        pf_step: phase field scenario / step
        phase0: index of first phase to initialize
        phase1: index of second phase to initialize inversely
        expected_interface_width: interface width parameter alpha that is used in analytical form
        time_steps: number of time steps run before evaluation

    Returns:
        deviation of simulated profile from analytical solution as average(abs(simulation-analytic))
    """
    import lbmpy.plot as plt
    from lbmpy.phasefield.analytical import analytic_interface_profile

    domain_size = pf_step.data_handling.shape
    pf_step.reset()
    pf_step.data_handling.fill(pf_step.phi_field_name, 0)
    init_sharp_interface(pf_step, phase_idx=phase0, inverse=False)
    init_sharp_interface(pf_step, phase_idx=phase1, inverse=True)
    pf_step.set_pdf_fields_from_macroscopic_values()
    pf_step.run(time_steps)

    vis_width = 20
    x = np.arange(vis_width) - (vis_width // 2)
    analytic = np.array([analytic_interface_profile(x_i - 0.5, expected_interface_width) for x_i in x],
                        dtype=np.float64)

    step_location = domain_size[0] // 4
    simulated = pf_step.phi[step_location - vis_width // 2:step_location + vis_width // 2, 0, phase0]
    plt.plot(analytic, label='analytic', marker='o')
    plt.plot(simulated, label='simulated', marker='x')
    plt.legend()

    return np.average(np.abs(simulated - analytic))
Esempio n. 4
0
def analytic_profile(width, alpha):
    x = np.arange(width) - (width // 2)
    return np.array([analytic_interface_profile(x_i - 0.5, alpha) for x_i in x], dtype=np.float64)
Esempio n. 5
0
def galilean_invariance_test(pf_step, velocity=0.05, rounds=3, phase0=0, phase1=1,
                             expected_interface_width=1, init_time_steps=5000):
    """
    Moves interface at constant speed through periodic domain - check if momentum is conserved

    Args:
        pf_step: phase field scenario / step
        velocity: constant velocity to move interface
        rounds: how many times the interface should travel through the domain
        phase0: index of first phase to initialize
        phase1: index of second phase to initialize inversely
        expected_interface_width: interface width parameter alpha that is used in analytical form
        init_time_steps: before velocity is set, this many time steps are run to let interface settle to tanh shape

    Returns:
        change in velocity
    """
    import lbmpy.plot as plt
    from lbmpy.phasefield.analytical import analytic_interface_profile

    domain_size = pf_step.data_handling.shape
    round_time_steps = int((domain_size[0] + 0.25) / velocity)

    print("Velocity:", velocity, " Time steps for round:", round_time_steps)

    pf_step.reset()
    pf_step.data_handling.fill(pf_step.phi_field_name, 0)
    init_sharp_interface(pf_step, phase_idx=phase0, inverse=False)
    init_sharp_interface(pf_step, phase_idx=phase1, inverse=True)
    pf_step.set_pdf_fields_from_macroscopic_values()

    print("Running", init_time_steps, "initial time steps")
    pf_step.run(init_time_steps)
    pf_step.data_handling.fill(pf_step.vel_field_name, velocity, value_idx=0)
    pf_step.set_pdf_fields_from_macroscopic_values()

    step_location = domain_size[0] // 4
    vis_width = 20

    simulated_profiles = []

    def capture_profile():
        simulated = pf_step.phi[step_location - vis_width // 2:step_location + vis_width // 2, 0, phase0].copy()
        simulated_profiles.append(simulated)

    capture_profile()
    for rt in range(rounds):
        print("Running round %d/%d" % (rt + 1, rounds))
        pf_step.run(round_time_steps)
        capture_profile()

    x = np.arange(vis_width) - (vis_width // 2)
    ref = np.array([analytic_interface_profile(x_i - 0.5, expected_interface_width) for x_i in x], dtype=np.float64)

    plt.plot(x, ref, label='analytic', marker='o')
    for i, profile in enumerate(simulated_profiles):
        plt.plot(x, profile, label="After %d rounds" % (i,))

    plt.legend()

    return np.average(pf_step.velocity[:, 0, 0]) - velocity