Wurtzite Aluminum Nitride Ace Pyace

Anharmonic Lattice Dynamics (ALD) for \(\ce{w-AlN}\) with ACE

Import Necessary Packages

[1]:
from ase.io import read
from ase.visualize.plot import plot_atoms
import pandas as pd
from pylab import *
import warnings
warnings.filterwarnings("ignore")

Custom Define Functions

[2]:
def cumulative_cond_cal(observables, kappa_tensor, prefactor=1/3):

    """Compute cumulative conductivity based on either frequency or mean-free path

       input:
       observables: (ndarray) either phonon frequency or mean-free path
       cond_tensor: (ndarray) conductivity tensor
       prefactor: (float) prefactor to average kappa tensor, 1/3 for bulk material

       ouput:
       observeables: (ndarray) sorted phonon frequency or mean-free path
       kappa_cond (ndarray) cumulative conductivity

    """

    # Sum over kappa by directions
    kappa = np.einsum('maa->m', prefactor * kappa_tensor)

    # Sort observables
    observables_argsort_indices = np.argsort(observables)
    cumulative_kappa = np.cumsum(kappa[observables_argsort_indices])
    return observables[observables_argsort_indices], cumulative_kappa



def set_fig_properties(ax_list, panel_color_str='black', line_width=2):
    tl = 4
    tw = 2
    tlm = 2

    for ax in ax_list:
        ax.tick_params(which='major', length=tl, width=tw)
        ax.tick_params(which='minor', length=tlm, width=tw)
        ax.tick_params(which='both', axis='both', direction='in',
                       right=True, top=True)
        ax.spines['bottom'].set_color(panel_color_str)
        ax.spines['top'].set_color(panel_color_str)
        ax.spines['left'].set_color(panel_color_str)
        ax.spines['right'].set_color(panel_color_str)

        ax.spines['bottom'].set_linewidth(line_width)
        ax.spines['top'].set_linewidth(line_width)
        ax.spines['left'].set_linewidth(line_width)
        ax.spines['right'].set_linewidth(line_width)

        for t in ax.xaxis.get_ticklines(): t.set_color(panel_color_str)
        for t in ax.yaxis.get_ticklines(): t.set_color(panel_color_str)
        for t in ax.xaxis.get_ticklines(): t.set_linewidth(line_width)
        for t in ax.yaxis.get_ticklines(): t.set_linewidth(line_width)

Denote Latex Font for Plots

[3]:
# Denote plot default format
aw = 2
fs = 12
font = {'size': fs}
matplotlib.rc('font', **font)
matplotlib.rc('axes', linewidth=aw)

# Configure Matplotlib to use a LaTeX-like style without LaTeX
plt.rcParams['text.usetex'] = False
plt.rcParams['font.family'] = 'serif'
plt.rcParams['mathtext.fontset'] = 'cm'

Illustrate Crystal Structure and Elastic Constants

[4]:
# Denote data path
data_folder = "kaldo_runs/"

# Extract supercell structure for later display
atoms = read(data_folder  + 'fd_ACE/replicated_atoms.xyz')

# Retrieve elastic constants and compute bulk modulus
Cijs = np.load(data_folder + 'Cij.npy')
C11 = Cijs[0, 0, 0, 0]
C12 = Cijs[0, 0, 1, 1]
C13 = Cijs[0, 0, 2, 2]
C33 = Cijs[2, 2, 2, 2]
Bulk_modulus = (2*(C11 + C12) + 4*C13 + C33) / 9

# Display supercell and bulk modulus side by side
figure(figsize=(4, 3))
set_fig_properties([gca()])
plot_atoms(atoms)
gca().axis('off')
gca().set_title('w-AlN supercell')
show()

print('\n')

# Prepare data for pandas
df = pd.DataFrame({
    "Properties": ["C11", "C12", "C13", "C33", "Bulk Modulus"],
    "Predictions (GPa)": [C11, C12, C13, C33, Bulk_modulus],
    "DFT references (GPa)": [379, 128, 96, 355, 195]
})

# Format the floats to one decimal place and print
pd.set_option('display.float_format', '{:.1f}'.format)
print(df.to_string(index=False))
../../_images/docsource_machine_learning_potentials_wurtzite_aluminum_nitride_ACE_PyACE_9_0.png


  Properties  Predictions (GPa)  DFT references (GPa)
         C11              367.4                   379
         C12              123.1                   128
         C13              121.6                    96
         C33              345.1                   355
Bulk Modulus              201.4                   195

Plot Phonon Spectra (Dispersion) and Density of State (DOS)

[5]:
# Derive symbols for high symmetry directions in Brillouin zone
dispersion = np.loadtxt(data_folder  + 'plots/15_15_9/dispersion')
point_names = np.loadtxt(data_folder  + 'plots/15_15_9/point_names', dtype=str)

point_names_list = []
for point_name in point_names:
    if point_name == 'G':
        point_name = r'$\Gamma$'
    elif point_name == 'U':
        point_name = 'U=K'
    point_names_list.append(point_name)

# Load in the "tick-mark" values for these symbols
q = np.loadtxt(data_folder  + 'plots/15_15_9/q')
Q = np.loadtxt(data_folder  + 'plots/15_15_9/Q_val')

# Load in grid and dos individually
dos_AlN = np.load(data_folder + 'plots/15_15_9/dos.npy')
pdos_Al = np.load(data_folder + 'plots/15_15_9/pdos_Al.npy')
pdos_N = np.load(data_folder + 'plots/15_15_9/pdos_N.npy')

# Plot dispersion
fig = figure(figsize=(4,3))
set_fig_properties([gca()])
plot(q[0], dispersion[0, 0], 'r-', ms=1)
plot(q, dispersion, 'r-', ms=1)
for i in range(1, 4):
    axvline(x=Q[i], ymin=0, ymax=2, ls='--',  lw=2, c="k")
ylabel('Frequency (THz)', fontsize=14)
xlabel(r'Wave vector ($\frac{2\pi}{a}$)', fontsize=14)
gca().set_yticks(np.arange(0, 36, 6))
xticks(Q, point_names_list)
ylim([-0.1, 30])
xlim([Q[0], Q[4]])
dosax = fig.add_axes([0.91, .11, .17, .77])
set_fig_properties([gca()])

# Plot per projection
for p_Al in np.expand_dims(pdos_Al[1],0):
        dosax.plot(p_Al, pdos_N[0], c='m', label='Al')
for p_N in np.expand_dims(pdos_Al[1],0) :
        dosax.plot(p_N, pdos_N[0], c='orange', label='N')
for d in np.expand_dims(dos_AlN[1],0):
    dosax.plot(d, dos_AlN[0],c='r', label='AlN')
dosax.set_yticks([])
dosax.set_xticks([])
dosax.set_xlabel("DOS")
dosax.legend(fontsize=6)
ylim([-0.1, 30])
show()
../../_images/docsource_machine_learning_potentials_wurtzite_aluminum_nitride_ACE_PyACE_11_0.png

Plot Heat Capacity, Group Velocities and Phase Space

[6]:
# Load in group velocity, heat_capacity (cv) and frequency data
frequency =  np.load(
    data_folder + 'ALD_AlN_ACE/15_15_9/frequency.npy',
    allow_pickle=True)

cv =  np.load(
    data_folder + 'ALD_AlN_ACE/15_15_9/300/quantum/heat_capacity.npy',
    allow_pickle=True)


group_velocity = np.load(
    data_folder + 'ALD_AlN_ACE/15_15_9/velocity.npy')

phase_space = np.load(data_folder +
 'ALD_AlN_ACE/15_15_9/300/quantum/_ps_and_gamma.npy',
                      allow_pickle=True)[:,0]

# Compute norm of group velocity
# Convert the unit from angstrom / picosecond to kilometer/ second
group_velcotiy_norm = np.linalg.norm(
    group_velocity.reshape(-1, 3), axis=1) / 10.0

# Plot observables in subplot
figure(figsize=(12, 3))
subplot(1,3, 1)
set_fig_properties([gca()])
scatter(frequency.flatten(order='C')[3:], 1e23*cv.flatten(order='C')[3:],
        facecolor='w', edgecolor='r', s=10, marker='8')
ylabel (r"$C_{v}$ ($10^{23}$ J/K)")
xlabel('Frequency (THz)', fontsize=14)
ylim(0.9*1e23*cv.flatten(order='C')[3:].min(), 1.05*1e23*cv.flatten(order='C')[3:].max())
gca().set_xticks(np.arange(0, 30, 5))

subplot(1 ,3, 2)
set_fig_properties([gca()])
scatter(frequency.flatten(order='C'),
        group_velcotiy_norm, facecolor='w', edgecolor='r', s=10, marker='^')
xlabel('Frequency (THz)', fontsize=14)
ylabel(r'$|v| \ (\frac{km}{s})$', fontsize=14)
gca().set_xticks(np.arange(0, 30, 5))
gca().set_yticks(np.arange(0, 21, 3))

subplot(1 ,3, 3)
set_fig_properties([gca()])
scatter(frequency.flatten(order='C'),
        phase_space, facecolor='w', edgecolor='r', s=10, marker='o')
ylim([-0.1, 0.8])
gca().set_xticks(np.arange(0, 30, 5))
xlabel('Frequency (THz)', fontsize=14)
ylabel('Phase space', fontsize=14)
subplots_adjust(wspace=0.33)
show()
../../_images/docsource_machine_learning_potentials_wurtzite_aluminum_nitride_ACE_PyACE_13_0.png

Plot Phonon Lifetime (\(\tau\)), Scattering Rate (\(\Gamma\)) \(\&\) MFP (\(\lambda\))

  • Note that \(\Gamma\) and \(\tau\) are computed only with the relaxation time approximation (RTA).

[7]:
# Load in scattering rate
scattering_rate = np.load(
    data_folder +
    'ALD_AlN_ACE/15_15_9/300/quantum/bandwidth.npy', allow_pickle=True)

# Derive lifetime, which is inverse of scattering rate
life_time = scattering_rate ** (-1)

# Denote lists to intake mean free path in each direction
mean_free_path = []
for i in range(3):
    mean_free_path.append(np.loadtxt(
    data_folder +
    'ALD_AlN_ACE/15_15_9/inverse/300/quantum/mean_free_path_' +
    str(i) + '.dat'))

# Convert list to numpy array and compute norm for mean free path
# Convert the unit from angstrom  to nanometer
mean_free_path = np.array(mean_free_path).T
mean_free_path_norm = np.linalg.norm(
    mean_free_path.reshape(-1, 3), axis=1) / 10.0

# Plot observables in subplot
figure(figsize=(12, 3))
subplot(1,3, 1)
set_fig_properties([gca()])
scatter(frequency.flatten(order='C'),
        life_time, facecolor='w', edgecolor='r', s=10, marker='s')
yscale('log')
ylabel(r'$\tau$ (ps)', fontsize=14)
xlabel('Frequency (THz)', fontsize=14)
gca().set_xticks(np.arange(0, 30, 5))

subplot(1,3, 2)
set_fig_properties([gca()])
scatter(frequency.flatten(order='C'),
        scattering_rate, facecolor='w', edgecolor='r', s=10, marker='d')
ylabel(r'$\Gamma$ (THz)', fontsize=14)
xlabel('Frequency (THz)', fontsize=14)
gca().set_xticks(np.arange(0, 30, 5))

subplot(1,3, 3)
set_fig_properties([gca()])
scatter(frequency.flatten(order='C'),
        mean_free_path_norm, facecolor='w', edgecolor='r', s=10, marker='8')
ylabel(r'$\lambda$ (nm)', fontsize=14)
xlabel('Frequency (THz)', fontsize=14)
yscale('log')
gca().set_xticks(np.arange(0, 30, 5))
ylim([1e-2, 1e5])
subplots_adjust(wspace=0.33)
show()
../../_images/docsource_machine_learning_potentials_wurtzite_aluminum_nitride_ACE_PyACE_15_0.png

Plot per mode and cumulative \(\kappa\)

[8]:
# Denote zeros to intake kappa tensor
kappa_tensor = np.zeros([mean_free_path.shape[0], 3, 3])
kappa_tensor_iso = np.zeros([mean_free_path.shape[0], 3, 3])

for i in range(3):
    for j in range(3):
        kappa_tensor[:, i, j] = np.loadtxt(data_folder +
    'ALD_AlN_ACE/15_15_9/inverse/300/quantum/conductivity_' +
    str(i) + '_' + str(j) + '.dat')
        kappa_tensor_iso[:, i, j] = np.loadtxt(data_folder +
    'ALD_AlN_ACE/15_15_9/inverse/300/quantum/isotopes/conductivity_' +
    str(i) + '_' + str(j) + '.dat')

# Sum over the 0th dimension to recover 3-by-3 kappa matrix
kappa_matrix = kappa_tensor.sum(axis=0)
print("Bulk thermal conductivity: %.1f W m^-1 K^-1\n"
      %np.mean(np.diag(kappa_matrix)))
print("kappa matrix: ")
print(kappa_matrix)
print('\n')

kappa_matrix_iso = kappa_tensor_iso.sum(axis=0)
print("Bulk thermal conductivity with isotopic scattering: %.1f W m^-1 K^-1\n"
      %np.mean(np.diag(kappa_matrix_iso)))
print("kappa matrix with isotopic scattering: ")
print(kappa_matrix_iso)
print('\n')

# Compute kappa in per mode and cumulative representations
kappa_per_mode = kappa_tensor.sum(axis=-1).sum(axis=1)
freq_sorted, kappa_cum_wrt_freq = cumulative_cond_cal(
    frequency.flatten(order='C'), kappa_tensor)
lambda_sorted, kappa_cum_wrt_lambda = cumulative_cond_cal(
    mean_free_path_norm, kappa_tensor)

kappa_per_mode_iso = kappa_tensor_iso.sum(axis=-1).sum(axis=1)
freq_sorted, kappa_cum_wrt_freq_iso = cumulative_cond_cal(
    frequency.flatten(order='C'), kappa_tensor_iso)
lambda_sorted, kappa_cum_wrt_lambda_iso = cumulative_cond_cal(
    mean_free_path_norm, kappa_tensor_iso)

# Plot observables in subplot
figure(figsize=(12, 3))
subplot(1,3, 1)
set_fig_properties([gca()])
scatter(frequency.flatten(order='C'),
        kappa_per_mode, facecolor='w', edgecolor='r', s=10, marker='>', label='$\kappa_{per \ mode, pure }$')
scatter(frequency.flatten(order='C'),
        kappa_per_mode_iso, facecolor='w', edgecolor='m', s=10, marker='o', label='$\kappa_{per \ mode, iso }$')
gca().axhline(y = 0, color='k', ls='--', lw=1)
ylabel(r'$\kappa_{per \ mode}\;\left(\frac{\rm{W}}{\rm{m}\cdot\rm{K}}\right)$',fontsize=14)
xlabel('Frequency (THz)', fontsize=14)
legend(loc=1, fontsize=10)
gca().set_xticks(np.arange(0, 30, 5))
ylim([-0.2, 8])

subplot(1,3, 2)
set_fig_properties([gca()])
plot(freq_sorted, kappa_cum_wrt_freq, 'r',
     label=r'$\kappa_{pure, ACE} \approx 260.3\;\frac{\rm{W}}{\rm{m}\cdot\rm{K}}$')
plot(freq_sorted, kappa_cum_wrt_freq_iso, c='m',
     ls='-.', label=r"$\kappa_{iso, ACE} \approx 259.9\;\frac{\rm{W}}{\rm{m}\cdot\rm{K}}$")
gca().set_yticks(np.arange(0, 300, 50))
ylabel(r'$\kappa_{cumulative, \omega}\;\left(\frac{\rm{W}}{\rm{m}\cdot\rm{K}}\right)$',fontsize=14)
xlabel('Frequency (THz)', fontsize=14)
gca().set_xticks(np.arange(0, 30, 5))
legend(loc=4, fontsize=6)

subplot(1,3, 3)
set_fig_properties([gca()])
plot(lambda_sorted, kappa_cum_wrt_lambda, 'r')
plot(lambda_sorted, kappa_cum_wrt_lambda_iso, c='m')
gca().set_yticks(np.arange(0, 300, 50))
xlabel(r'$\lambda$ (nm)', fontsize=14)
ylabel(r'$\kappa_{cumulative, \lambda}\;\left(\frac{\rm{W}}{\rm{m}\cdot\rm{K}}\right)$',fontsize=14)
xscale('log')
xlim([1e-1, 1e4])
subplots_adjust(wspace=0.33)
show()
Bulk thermal conductivity: 260.3 W m^-1 K^-1

kappa matrix:
[[ 2.64608547e+02  5.78997217e-01  2.43088665e-01]
 [ 4.18946787e-01  2.64671284e+02 -2.31451194e-01]
 [ 3.69859965e-01  3.98814383e-01  2.51516013e+02]]


Bulk thermal conductivity with isotopic scattering: 259.9 W m^-1 K^-1

kappa matrix with isotopic scattering:
[[ 2.64186375e+02  5.76801222e-01  2.42219797e-01]
 [ 4.17874653e-01  2.64246959e+02 -2.31684048e-01]
 [ 3.66651347e-01  3.93057933e-01  2.51164394e+02]]


../../_images/docsource_machine_learning_potentials_wurtzite_aluminum_nitride_ACE_PyACE_17_1.png