Example 1: Cepstral Analysis of solid amorphous Silica

This example shows the basic usage of sportran to compute the thermal conductivity of a classical MD simulation of a-SiO\(_2\).

[1]:
import numpy as np
import scipy as sp
import matplotlib.pyplot as plt
try:
    import sportran as st
except ImportError:
    from sys import path
    path.append('..')
    import sportran as st

c = plt.rcParams['axes.prop_cycle'].by_key()['color']
[2]:
%matplotlib notebook

1. Load trajectory

Read the heat current from a simple column-formatted file. The desired columns are selected based on their header (e.g. with LAMMPS format).

For other input formats see Example #3.

[3]:
jfile = st.i_o.TableFile('./data/Silica/Silica.dat', group_vectors=True)
# Solid Silica - BKS potential, melted and quenched
# 216 atoms, T~1000K, dens~2.295g/cm^3
# NVE, dt = 1.0 fs, 100 ps, print_step = 1.0 fs
# Temperature = 983.172635 K, Volume = 3130.431110818 A^3
# LAMMPS metal units
Temp c_flux1[1] c_flux1[2] c_flux1[3]
 #####################################
  all_ckeys =  [('Temp', [0]), ('flux1', array([1, 2, 3]))]
 #####################################
Data length =  100001
[4]:
jfile.read_datalines(start_step=0, NSTEPS=0, select_ckeys=['flux1', 'Temp'], max_vector_dim=3)
  ckey =  [('Temp', [0]), ('flux1', array([1, 2, 3]))]
    step =    100000 - 100.00% completed
  ( 100000 ) steps read.
DONE.  Elapsed time:  0.9626550674438477 seconds
[4]:
{'flux1': array([[ -265.30586 ,  1520.6107  ,    67.461829],
        [ -168.68352 ,  1377.4459  ,   101.82146 ],
        [  -93.688306,  1180.375   ,   117.20939 ],
        ...,
        [ 1226.9778  ,   212.0939  , -1126.4643  ],
        [ 1223.8753  ,   186.93836 ,  -881.39541 ],
        [ 1232.7723  ,   141.30647 ,  -620.41895 ]]),
 'Temp': array([[ 998.48171],
        [1003.699  ],
        [1003.8906 ],
        ...,
        [ 967.21723],
        [ 978.47566],
        [ 985.41455]])}

2. Heat Current

Define a HeatCurrent from the trajectory, with the correct parameters.

[5]:
DT_FS = 1.0                                # time step [fs]
TEMPERATURE = np.mean(jfile.data['Temp'])  # temperature [K] (983.173 K)
VOLUME = 3130.431110818                    # volume [A^3]
print(DT_FS, TEMPERATURE, VOLUME)

j = st.HeatCurrent(jfile.data['flux1'], UNITS='metal', DT_FS=DT_FS, TEMPERATURE=TEMPERATURE, VOLUME=VOLUME)
1.0 983.1726353043 3130.431110818
Using single component code.
[6]:
# trajectory
f = plt.figure()
ax = plt.plot(j.timeseries()/1000., j.traj);
plt.xlim([0, 1.0])
plt.xlabel(r'$t$ [ps]')
plt.ylabel(r'$J$ [eV A/ps]');

Compute the Power Spectral Density (\(\rm PSD\)) and filter it for visualization.

[7]:
# Periodogram with given filtering window width
ax = j.plot_periodogram(PSD_FILTER_W=0.5, kappa_units=True)
print(j.Nyquist_f_THz)
plt.xlim([0, 50])
ax[0].set_ylim([0, 150]);
ax[1].set_ylim([12, 18]);
500.0

3. Resampling

If the Nyquist frequency is very high (i.e. the sampling time is very small), such that the log-spectrum goes to low values, you may want resample your time series, in order to reduce the maximum frequency to \(f^*\). Before performing that operation, the time series is automatically filtered to reduce the amount of aliasing introduced. Ideally you do not want to go too low in \(f^*\), because the statistical error may increase. In an intermediate region the results should not change.

To perform resampling you can choose the resampling frequency \(f^*\) (fstar_THz) or the resampling step (TSKIP). If you choose \(f^*\), the code will try to find the closest value allowed. The resulting \(\rm PSD\) can be visualized to ensure that the low-frequency region was not affected by this operations

[8]:
FSTAR_THZ = 28.0
jf, ax = j.resample(fstar_THz=FSTAR_THZ, plot=True, freq_units='thz')
plt.xlim([0, 80])
ax[1].set_ylim([12,18]);
Using single component code.
-----------------------------------------------------
  RESAMPLE TIME SERIES
-----------------------------------------------------
 Original Nyquist freq  f_Ny =     500.00000 THz
 Resampling freq          f* =      27.77778 THz
 Sampling time         TSKIP =            18 steps
                             =        18.000 fs
 Original  n. of frequencies =         50001
 Resampled n. of frequencies =          2778
 PSD      @cutoff  (pre-filter&sample) ~ 2666288.02378
                  (post-filter&sample) ~ 2206043.36451
 log(PSD) @cutoff  (pre-filter&sample) ~     14.61451
                  (post-filter&sample) ~     14.40954
 min(PSD)          (pre-filter&sample) =      4.03008
 min(PSD)         (post-filter&sample) =  60168.84968
 % of original PSD Power f<f* (pre-filter&sample)  = 77.164 %
-----------------------------------------------------

[9]:
ax = jf.plot_periodogram(PSD_FILTER_W=0.1)
ax[1].set_ylim([12, 18]);

4. Cepstral Analysis

Perform Cepstral Analysis. The code will perform the following operations: 1. compute the parameters describing the theoretical distribution of the \(\rm PSD\) 2. compute the Cepstral coefficients by Fourier transforming the \(\log(\rm{PSD})\) 3. apply the Akaike Information Criterion, to choose the number of cepstral coefficients \(P^*\) 4. return the resulting transport coefficient (thermal conductivity) \(\kappa\)

[10]:
jf.cepstral_analysis()
-----------------------------------------------------
  CEPSTRAL ANALYSIS
-----------------------------------------------------
  cutoffK = (P*-1) = 33  (auto, AIC_Kmin = 33, corr_factor =  1.0)
  L_0*   =          13.114928 +/-   0.097614
  S_0*   =      717769.108506 +/- 70064.257396
-----------------------------------------------------
  kappa* =           2.205099 +/-   0.215248  W/m/K
-----------------------------------------------------

[11]:
# Cepstral Coefficients
print('c_k = ', jf.cepf.logpsdK)

ax = jf.plot_ck()
ax.set_xlim([0, 50])
ax.set_ylim([-0.5, 0.5])
ax.grid();
c_k =  [ 1.51720808e+01 -7.09450459e-01 -1.75761630e-01 ...  6.61581529e-04
 -1.41143322e-02  5.27000116e-03]
[12]:
# AIC function
f = plt.figure()
plt.plot(jf.cepf.aic, '.-', c=c[0])
plt.xlim([0, 150])
plt.ylim([min(jf.cepf.aic[:150]) - 10, max(jf.cepf.aic[50:150]) + 10]);

print('K of AIC_min = {:d}'.format(jf.cepf.aic_Kmin))
print('AIC_min = {:f}'.format(jf.cepf.aic_min))
K of AIC_min = 33
AIC_min = 2948.593166

Plot the thermal conductivity \(\kappa\) as a function of the cutoff \(P^*\)

[13]:
# L_0 as a function of cutoff K
ax = jf.plot_L0_Pstar()
ax.set_xlim([0, 150])
ax.set_ylim([12.5, 14.5]);

print('K of AIC_min = {:d}'.format(jf.cepf.aic_Kmin))
print('AIC_min = {:f}'.format(jf.cepf.aic_min))
K of AIC_min = 33
AIC_min = 2948.593166
[14]:
# kappa as a function of cutoff K
ax = jf.plot_kappa_Pstar()
ax.set_xlim([0, 150])
ax.set_ylim([0, 5.0]);

print('K of AIC_min = {:d}'.format(jf.cepf.aic_Kmin))
print('AIC_min = {:f}'.format(jf.cepf.aic_min))
K of AIC_min = 33
AIC_min = 2948.593166

Print the results :)

[15]:
results = jf.cepstral_log
print(results)
-----------------------------------------------------
  CEPSTRAL ANALYSIS
-----------------------------------------------------
  cutoffK = (P*-1) = 33  (auto, AIC_Kmin = 33, corr_factor =  1.0)
  L_0*   =          13.114928 +/-   0.097614
  S_0*   =      717769.108506 +/- 70064.257396
-----------------------------------------------------
  kappa* =           2.205099 +/-   0.215248  W/m/K
-----------------------------------------------------

You can now visualize the filtered PSD…

[16]:
# filtered log-PSD
ax = j.plot_periodogram(0.5, kappa_units=True)
ax = jf.plot_periodogram(0.5, axes=ax, kappa_units=True)
ax = jf.plot_cepstral_spectrum(axes=ax, kappa_units=True)
ax[0].axvline(x = jf.Nyquist_f_THz, ls='--', c='r')
ax[1].axvline(x = jf.Nyquist_f_THz, ls='--', c='r')
plt.xlim([0., 50.])
ax[1].set_ylim([12,18])
ax[0].legend(['original', 'resampled', 'cepstrum-filtered'])
ax[1].legend(['original', 'resampled', 'cepstrum-filtered']);
[ ]: