Ideal Solution

Prelude

from landau.phases import LinePhase, IdealSolution
from landau.calculate import calc_phase_diagram
from landau.plot import plot_phase_diagram, plot_1d_T_phase_diagram, plot_1d_mu_phase_diagram, plot_mu_phase_diagram
import numpy as np
from scipy.constants import Boltzmann, eV
kB = Boltzmann/eV
import seaborn as sns
import matplotlib.pyplot as plt

Phases Setup

Set up some simple toy phase models that yields this phase diagram.

image.png

solid_a = LinePhase('A', fixed_concentration=0, line_energy=-2.0, line_entropy=1.0*kB)
solid_b = LinePhase('B', fixed_concentration=1, line_energy=-3.0, line_entropy=1.5*kB)
solid = IdealSolution('solid', solid_a, solid_b)
liquid_a = LinePhase('A(l)', fixed_concentration=0, line_energy=-1.9, line_entropy=2.5*kB)
liquid_b = LinePhase('B(l)', fixed_concentration=1, line_energy=-2.9, line_entropy=2.2*kB)
liquid = IdealSolution('liquid', liquid_a, liquid_b)

Examples for congruent melting

For the terminals and at fixed concentration, the chemical potential difference does not matter, so we can set it to zero. In this case the semigrand potential is equal to the free energy.

plt.figure(figsize=(12,6))
plt.subplot(121)
df = calc_phase_diagram([solid_a, liquid_a], Ts=np.linspace(100, 2000, 100), mu=0.0, keep_unstable=True)
plot_1d_T_phase_diagram(df, ax=plt.gca(), show=False)

plt.subplot(122)
df = calc_phase_diagram([solid_b, liquid_b], Ts=np.linspace(100, 2000, 100), mu=0.0, keep_unstable=True)
plot_1d_T_phase_diagram(df, ax=plt.gca(), show=False)
<Axes: xlabel='Temperature [K]', ylabel='Semi-grandcanonical potential [eV/atom]'>
../_images/7edc93bbcfcfab30f81674f2039da14905d5a18e4e8ce14f37b8efbcf6cc8a44.png

Non-Congruent Melting

However, the general formulation in the semigrand ensemble allows us to also investigate non-congruent melting transitions using the exact same code and formalism.

image.png

To do this, let’s first find out, what’s the chemical potential difference where liquid and solid are in equilibrium at, say, 1000K.

image.png

T_twophase = 1000
df = calc_phase_diagram([solid, liquid], Ts=T_twophase, mu=1000, keep_unstable=True)
plot_1d_mu_phase_diagram(df)
../_images/97a5c4f7423199ad44531c4a13c093b3a996201fd0d68f6431bb0f9089abf8a9.png

From the results, we can already get the concentrations on the liquidus and solidus, either by plot isotherms or numerically from the dataframe.

df.query('border')
T phase phi mu c stable border refined f f_excess
2000 1000 liquid -2.149932 -1.035211 0.329906 True True mu -2.491454 -0.041544
2001 1000 solid -2.149932 -1.035211 0.522831 True True mu -2.691173 -0.045672
mu_equilibrium_1000k = df.query('border').mu.iloc[0] # ~ -1.035 eV
mu_equilibrium_1000k
np.float64(-1.0352114196607491)
sns.lineplot(
    data=df,
    x='mu', y='c',
    hue='phase',
    style='stable', style_order=[True, False],
)
plt.axvline(mu_equilibrium_1000k, ls='dotted', c='k')
<matplotlib.lines.Line2D at 0x7f5e2bb7ea10>
../_images/c6c602361ff22fb7dd0b17e5c70a27cef184327cf0251e4aced72e0023acc08f.png

Using the f and f_excess columns, we can also plot the more familiar common tangent construction.

sns.lineplot(
    data=df,
    x='c', y='f',
    hue='phase',
    style='stable', style_order=[True, False]
)
sns.lineplot(
    data=df.query('border'),
    x='c', y='f',
    ls='dotted', c='k'
)
sns.scatterplot(
    data=df.query('border'),
    x='c', y='f',
    c='k',
    zorder=2
)
<Axes: xlabel='c', ylabel='f'>
../_images/f4215813a259faae6b4c3294815917482ba1d749cd5407a3de9fd71de44dc2c5.png

Difficult to see in \(f\) due to linear shift, let’s use \(f_\mathrm{excess}\) to see it more clearly.

sns.lineplot(
    data=df,
    x='c', y='f_excess',
    hue='phase',
    style='stable', style_order=[True, False]
)
sns.lineplot(
    data=df.query('border'),
    x='c', y='f_excess',
    ls='dotted', c='k'
)
sns.scatterplot(
    data=df.query('border'),
    x='c', y='f_excess',
    c='k',
    zorder=2
)
<Axes: xlabel='c', ylabel='f_excess'>
../_images/5d23aac15a53ab71171c24241f81fd1c77197f5469d05e576cc159353d9ad5ef.png
df = calc_phase_diagram([solid, liquid], Ts=np.linspace(0, 2000, 1000), mu=mu_equilibrium_1000k, keep_unstable=True, refine=True)
plot_1d_T_phase_diagram(df)
../_images/3898f7021c672b1d423ed3d93a9efe0c4b43d847d9a6098eb0905d5cd753052d.png
<Axes: xlabel='Temperature [K]', ylabel='Semi-grandcanonical potential [eV/atom]'>
sns.lineplot(
    data=df,
    x='T', y='c',
    hue='phase',
    style='stable', style_order=[True, False],
)
plt.axvline(T_twophase, ls='dotted', c='k')
<matplotlib.lines.Line2D at 0x7f5e2a019a90>
../_images/ec9ccaefb3d95b6f2a9b4a597e2ea1ae47483cbf4a7ed3bf80a884053c1a7d64.png

Appendix: Inline Plots

Code that generates the pasted pictures above.

bdf = calc_phase_diagram([solid, liquid], Ts=np.linspace(0, 2000, 100), mu=200, keep_unstable=True)

Fig1

plt.figure(figsize=(12,6))
plt.subplot(121)
plot_phase_diagram(bdf, element='B', poly_method='fasttsp')
plt.subplot(122)
plot_mu_phase_diagram(bdf, element='B', poly_method='fasttsp')
../_images/e5413028f226b87770978f056961f3419f4802a11d416e9b19ad38bb0f4b4104.png

Fig3

T_twophase = 1000
df1000 = calc_phase_diagram([solid, liquid], Ts=1000, mu=1000, keep_unstable=True)
mu_equilibrium_1000k = df.query('border').mu.iloc[0]
plot_phase_diagram(bdf, element='B', poly_method='fasttsp')
plt.axhline(1000, ls='-', c='k')
sns.scatterplot(
    data=df1000.query('border'),
    x='c', y='T',
    marker='o', c='k'
)
<Axes: xlabel='$c_\\mathrm{B}$', ylabel='$T$ [K]'>
../_images/6a9e05d3da213590d4b412e50e984efdca262d0a1eddae93a5095c2ba7f940cd.png

Fig2

dfisomu = calc_phase_diagram([solid, liquid], Ts=np.linspace(0, 2000, 1000), mu=mu_equilibrium_1000k, keep_unstable=True, refine=True)
plot_phase_diagram(bdf, element='B', poly_method='fasttsp')
sns.lineplot(
    data=dfisomu.query('stable'),
    x='c', y='T',
    orient='y', 
    # sort=False,
    estimator=None,
    c='k'
)
<Axes: xlabel='$c_\\mathrm{B}$', ylabel='$T$ [K]'>
../_images/d9896eccf8ef34713d898bcd6f32427be4e2d145e62680ace230cf7debda5a36.png