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)
def create_n_phase_model_penalty_term(alpha=1, num_phases=4, kappa=0.015, penalty_term_factor=0.01, **kwargs): order_parameters = symbolic_order_parameters(num_phases) free_energy = free_energy_functional_n_phases_penalty_term( order_parameters, alpha, kappa, penalty_term_factor) return PhaseFieldStep(free_energy, order_parameters, **kwargs)
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
def create_n_phase_model(alpha=1, num_phases=4, surface_tensions=lambda i, j: 0.005 if i != j else 0, f1=lambda c: c**2 * (1 - c)**2, f2=lambda c: c**2 * (1 - c)**2, triple_point_energy=0, **kwargs): order_parameters = symbolic_order_parameters(num_phases - 1) free_energy = free_energy_functional_n_phases( num_phases, surface_tensions, alpha, order_parameters, f1=f1, f2=f2, triple_point_energy=triple_point_energy) def concentration_to_order_parameters(c): c = np.array(c) c_sum = np.sum(c, axis=-1) if isinstance(c_sum, np.ndarray): np.subtract(c_sum, 1, out=c_sum) np.abs(c_sum, out=c_sum) deviation = np.max(c_sum) else: deviation = np.abs(c_sum - 1) if deviation > 1e-5: warnings.warn( "Initialization problem: concentrations have to add up to 1") return c[..., :-1] def order_parameters_to_concentrations(op): op_shape = list(op.shape) op_shape[-1] += 1 result = np.empty(op_shape) np.copyto(result[..., :-1], op) result[..., -1] = 1 - np.sum(op, axis=-1) return result return PhaseFieldStep( free_energy, order_parameters, concentration_to_order_parameters=concentration_to_order_parameters, order_parameters_to_concentrations=order_parameters_to_concentrations, **kwargs)
def test_pressure_tensor(): """ Checks that the following ways are equivalent: 1) phi -> mu -> force 2) phi -> pressure tensor -> force """ dim = 3 c = symbolic_order_parameters(3) f = free_energy_functional_n_phases(order_parameters=c) mu = chemical_potentials_from_free_energy(f, c) mu = substitute_laplacian_by_sum(mu, dim) force_chem_pot = expand_diff_full(force_from_phi_and_mu(c, dim, mu), functions=c) p = pressure_tensor_from_free_energy(f, c, dim) force_pressure_tensor = force_from_pressure_tensor(p, functions=c) for f1_i, f2_i in zip(force_chem_pot, force_pressure_tensor): assert sp.expand(f1_i - f2_i) == 0