Embedding jupyter notebooks as jekyll blog posts
Jupyter notebook is now a paradigm approach to present and communicate scientific results, as it promotes the readibility and enriches the code by markdown text and colored figures. It is then appealing to create blog posts directly from Jupyter notebooks to present the calcultation results without worrying about how to convert notebooks into the formats for publication. People usually do it by two steps:
- Export the notebook as a
html
file. - Copy the content between
<html></html>
tags and paste them into a markdown post file.md
Then you are done! It’s as simple as that. However, you may find the notebook appearance is messed up with the global style setting. To resolve this, we need to create an additional post-jupyter.html
file in the _layouts
folder with the following pattern (see my github for example):
<div class="post">
<div class="post-jupyter">
{ post content }
</div>
</div>
and add the corresponding styles in .scss
:
.post {
.post-jupyter {
// reset the paragraph back to global style
font-family: global-font-family;
pre {
.nc {
text-decoration: none;
}
}
// fix conflicts between global and local styles
.jp-Cell-inputWrapper pre {
border-radius: 0;
background-color: var(--jp-cell-editor-background);
.nn {
text-decoration: none;
}
}
.jp-Cell-outputWrapper pre {
background-color: var(--global-bg-color);
}
}
}
We also need to add light theme and dark theme css files in _sass
folder and include them in /assets/css/main.scss
@import
"jupyter-light",
"jupyter-dark"
;
. Now jupyter notebooks have correct color and style rednering even when the site changes from light theme to dark theme! 👍🏻
(Optional) We can further move the universal <head>
section in the beginning of all jupyter notebook to post-jupyter.html
and only embed content between <body>
tags in the markdown file to solve local and global <head>
conflicts.
The following is the example of jupyter notebook embedding.
Import packages¶
import glob
import re
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from pymatgen.io.vasp import Vasprun
Import multiple vasprun.xml
files into Pandas DataFrame¶
pot_all, ele_all = [], []
for i, job in enumerate(glob.glob('*-*/run_*')):
pot, ele = job.split('/') # potential, electronic structure
pot = re.split(r'\d+-', pot, 1)[1]
ele = re.split(r'run_', ele, 1)[1]
pot_all.append(pot)
ele_all.append(ele)
iterables = [list(set(pot_all)), list(set(ele_all))]
index = pd.MultiIndex.from_product(iterables)
lattices = pd.DataFrame(-1, index=index, columns=['a', 'b', 'c', 'alpha', 'beta', 'gamma'])
vruns = pd.DataFrame(0, index=list(set(pot_all)), columns=list(set(ele_all)))
for i, job in enumerate(glob.glob('*-*/run_*')):
pot, ele = job.split('/') # potential, electronic structure
pot = re.split(r'\d+-', pot, 1)[1]
ele = re.split(r'run_', ele, 1)[1]
try:
vrun = Vasprun(job+'/vasprun.xml')
# print(vrun.converged, vrun.converged_electronic, vrun.converged_ionic)
except:
print('\033[91m'+'\033[1m'+'error:', i, pot, ele, '\033[0m')
else:
print(i, pot, ele, vrun.converged, vrun.converged_electronic, vrun.converged_ionic)
lattices.loc[(pot, ele)] = vrun.final_structure.lattice.abc + vrun.final_structure.lattice.angles
vruns.loc[(pot, ele)] = vrun
0 PBE Na_Cl True True True 1 PBE Na_Cl_h True True True 2 PBE Na_pv_Cl True True True 3 PBE Na_pv_Cl_h True True True 4 PBE Na_sv_Cl True True True 5 PBE Na_sv_Cl_h True True True 6 D2 Na_Cl True True True 7 D2 Na_Cl_h True True True 8 D2 Na_pv_Cl True True True 9 D2 Na_pv_Cl_h True True True 10 D2 Na_sv_Cl True True True 11 D2 Na_sv_Cl_h True True True 12 D3 Na_Cl True True True 13 D3 Na_Cl_h True True True 14 D3 Na_pv_Cl True True True 15 D3 Na_pv_Cl_h True True True 16 D3 Na_sv_Cl True True True 17 D3 Na_sv_Cl_h True True True 18 dDsC Na_Cl True True True 19 dDsC Na_Cl_h True True True 20 dDsC Na_pv_Cl True True True 21 dDsC Na_pv_Cl_h True True True 22 dDsC Na_sv_Cl True True True 23 dDsC Na_sv_Cl_h True True True 24 vdW-DF2 Na_Cl True True True 25 vdW-DF2 Na_Cl_h True True True 26 vdW-DF2 Na_pv_Cl True True True 27 vdW-DF2 Na_pv_Cl_h True True True 28 vdW-DF2 Na_sv_Cl True True True 29 vdW-DF2 Na_sv_Cl_h True True True
for ele, value in vruns.items():
plt.rcParams.update({
"pgf.texsystem": "pdflatex",
'font.family': 'sans-serif',
'text.usetex': True,
'pgf.rcfonts': False,
'legend.fontsize': 'x-small'
})
plt.figure(
figsize=(3,3),
constrained_layout=True
)
for pot, vrun in vruns[ele].items():
if type(vrun) is not int:
energy = []
for i, ionic_step in enumerate(vrun.ionic_steps):
for j, electronic_step in enumerate(ionic_step['electronic_steps']):
energy.append(electronic_step['e_0_energy'])
plt.plot(
np.arange(len(energy)), energy,
'--.', lw=0.5, alpha=0.5,
label=f'{pot}'
)
plt.ylabel('energy [eV]')
plt.xlabel('step')
plt.title(f'{ele}')
plt.ylim(-30, 0)
plt.legend()
plt.savefig(f'figures/{ele}-energy.pdf', transparent=True, dpi=300)
plt.savefig(f'figures/{ele}-energy.png', transparent=True, dpi=300)
plt.show()
lattices
a | b | c | alpha | beta | gamma | ||
---|---|---|---|---|---|---|---|
D3 | Na_sv_Cl_h | 5.639012 | 5.639012 | 5.639012 | 90.000001 | 90.000000 | 89.999999 |
Na_pv_Cl_h | 5.652891 | 5.652892 | 5.652891 | 90.000001 | 90.000000 | 90.000000 | |
Na_Cl | 5.608770 | 5.608770 | 5.608770 | 90.000000 | 90.000000 | 90.000000 | |
Na_pv_Cl | 5.655135 | 5.655135 | 5.655135 | 90.000000 | 89.999999 | 90.000000 | |
Na_Cl_h | 5.605908 | 5.605908 | 5.605908 | 90.000000 | 90.000000 | 90.000000 | |
Na_sv_Cl | 5.641151 | 5.641151 | 5.641151 | 90.000000 | 90.000001 | 89.999999 | |
dDsC | Na_sv_Cl_h | 5.600473 | 5.600473 | 5.600473 | 89.988775 | 89.984916 | 89.980648 |
Na_pv_Cl_h | 5.616143 | 5.616143 | 5.616143 | 89.985741 | 89.983647 | 89.982795 | |
Na_Cl | 5.549591 | 5.549592 | 5.549591 | 89.979097 | 89.972278 | 89.974083 | |
Na_pv_Cl | 5.604479 | 5.604479 | 5.604479 | 89.984037 | 89.975211 | 89.978639 | |
Na_Cl_h | 5.564135 | 5.564135 | 5.564135 | 89.982348 | 89.977125 | 89.978212 | |
Na_sv_Cl | 5.587784 | 5.587788 | 5.587765 | 89.993386 | 89.984859 | 89.997202 | |
D2 | Na_sv_Cl_h | 5.645721 | 5.645720 | 5.645720 | 90.000001 | 90.000000 | 89.999999 |
Na_pv_Cl_h | 5.657343 | 5.657343 | 5.657343 | 90.000001 | 90.000000 | 90.000000 | |
Na_Cl | 5.624607 | 5.624607 | 5.624607 | 90.000000 | 90.000000 | 90.000000 | |
Na_pv_Cl | 5.658516 | 5.658516 | 5.658516 | 90.000000 | 90.000000 | 90.000000 | |
Na_Cl_h | 5.622398 | 5.622398 | 5.622398 | 90.000000 | 90.000000 | 90.000000 | |
Na_sv_Cl | 5.647336 | 5.647336 | 5.647336 | 90.000000 | 90.000001 | 89.999999 | |
vdW-DF2 | Na_sv_Cl_h | 5.672899 | 5.672899 | 5.672899 | 90.000000 | 89.999999 | 90.000000 |
Na_pv_Cl_h | 5.677273 | 5.677273 | 5.677273 | 89.999999 | 90.000003 | 90.000001 | |
Na_Cl | 5.645379 | 5.645379 | 5.645379 | 90.000000 | 90.000000 | 90.000000 | |
Na_pv_Cl | 5.675419 | 5.675419 | 5.675419 | 89.999999 | 90.000000 | 90.000000 | |
Na_Cl_h | 5.645312 | 5.645312 | 5.645312 | 90.000000 | 90.000000 | 90.000000 | |
Na_sv_Cl | 5.670797 | 5.670797 | 5.670796 | 90.000000 | 90.000001 | 89.999999 | |
PBE | Na_sv_Cl_h | 5.680436 | 5.680435 | 5.680435 | 90.000001 | 90.000000 | 89.999999 |
Na_pv_Cl_h | 5.695502 | 5.695502 | 5.695502 | 90.000001 | 90.000000 | 90.000000 | |
Na_Cl | 5.655048 | 5.655048 | 5.655048 | 90.000000 | 90.000000 | 90.000000 | |
Na_pv_Cl | 5.697120 | 5.697120 | 5.697120 | 90.000000 | 89.999999 | 90.000000 | |
Na_Cl_h | 5.652917 | 5.652917 | 5.652917 | 90.000000 | 90.000000 | 90.000000 | |
Na_sv_Cl | 5.681792 | 5.681792 | 5.681792 | 90.000000 | 90.000001 | 89.999998 |
Electronic structure¶
from pymatgen.electronic_structure.core import OrbitalType
from pymatgen.electronic_structure.plotter import DosPlotter
pot_all, ele_all = [], []
for i, job in enumerate(glob.glob('*-*/run_*/1-dos')):
pot, ele, _ = job.split('/') # potential, electronic structure
pot = re.split(r'\d+-', pot, 1)[1]
ele = re.split(r'run_', ele, 1)[1]
pot_all.append(pot)
ele_all.append(ele)
iterables = [list(set(pot_all)), list(set(ele_all))]
index = pd.MultiIndex.from_product(iterables)
lattices = pd.DataFrame(-1, index=index, columns=['a', 'b', 'c', 'alpha', 'beta', 'gamma'])
vruns = pd.DataFrame(0, index=list(set(pot_all)), columns=list(set(ele_all)))
for i, job in enumerate(glob.glob('*-*/run_*/1-dos')):
pot, ele, _ = job.split('/') # potential, electronic structure
pot = re.split(r'\d+-', pot, 1)[1]
ele = re.split(r'run_', ele, 1)[1]
try:
vrun = Vasprun(job+'/vasprun.xml')
# print(vrun.converged, vrun.converged_electronic, vrun.converged_ionic)
except:
print('\033[91m'+'\033[1m'+'error:', i, pot, ele, '\033[0m')
else:
print(i, pot, ele, vrun.converged, vrun.converged_electronic, vrun.converged_ionic)
lattices.loc[(pot, ele)] = vrun.final_structure.lattice.abc + vrun.final_structure.lattice.angles
vruns.loc[(pot, ele)] = vrun
0 PBE Na_Cl True True True 1 PBE Na_Cl_h True True True 2 PBE Na_pv_Cl True True True 3 PBE Na_pv_Cl_h True True True 4 PBE Na_sv_Cl True True True 5 PBE Na_sv_Cl_h True True True 6 D2 Na_Cl True True True 7 D2 Na_Cl_h True True True 8 D2 Na_pv_Cl True True True 9 D2 Na_pv_Cl_h True True True 10 D2 Na_sv_Cl True True True 11 D2 Na_sv_Cl_h True True True 12 D3 Na_Cl True True True 13 D3 Na_Cl_h True True True 14 D3 Na_pv_Cl True True True 15 D3 Na_pv_Cl_h True True True 16 D3 Na_sv_Cl True True True 17 D3 Na_sv_Cl_h True True True 18 dDsC Na_Cl True True True 19 dDsC Na_Cl_h True True True 20 dDsC Na_pv_Cl True True True 21 dDsC Na_pv_Cl_h True True True 22 dDsC Na_sv_Cl True True True 23 dDsC Na_sv_Cl_h True True True 24 vdW-DF2 Na_Cl True True True 25 vdW-DF2 Na_Cl_h True True True 26 vdW-DF2 Na_pv_Cl True True True 27 vdW-DF2 Na_pv_Cl_h True True True 28 vdW-DF2 Na_sv_Cl True True True 29 vdW-DF2 Na_sv_Cl_h True True True
bandgaps = pd.DataFrame(-1, index=index, columns=['band gap'])
for ele, value in vruns.items():
for pot, vrun in vruns[ele].items():
bandgaps.loc[(pot, ele)] = vrun.complete_dos.get_gap()
bandgaps.unstack()
band gap | ||||||
---|---|---|---|---|---|---|
Na_Cl | Na_Cl_h | Na_pv_Cl | Na_pv_Cl_h | Na_sv_Cl | Na_sv_Cl_h | |
D2 | 5.152 | 5.160 | 5.104 | 5.112 | 5.120 | 5.128 |
D3 | 5.192 | 5.192 | 5.112 | 5.120 | 5.136 | 5.144 |
PBE | 5.088 | 5.096 | 5.024 | 5.032 | 5.056 | 5.064 |
dDsC | 5.320 | 5.288 | 5.216 | 5.192 | 5.248 | 5.224 |
vdW-DF2 | 4.984 | 4.976 | 4.928 | 4.904 | 4.944 | 4.920 |
print(bandgaps.to_latex())
for ele, value in vruns.items():
for pot, vrun in vruns[ele].items():
complete_dos = vrun.complete_dos
with plt.style.context('default'):
(figwidth, figheight) = (3, 2)
SMALL_SIZE = 6
MEDIUM_SIZE = 8
LARGE_SIZE = 10
LINE_WIDTH = 0.5
plt.rcParams.update({
'pgf.texsystem': 'pdflatex',
'font.family': 'sans-serif',
'text.usetex': True,
'pgf.rcfonts': True,
# 'figure.figsize': [3, 2],
# 'axes.labelsize': 8,
# 'axes.titlesize': 8,
# 'legend.frameon': False,
# 'legend.fontsize': 6,
# 'legend.loc': 'upper right',
# 'lines.linewidth': 0.5,
# 'xtick.labelsize': 6,
# 'ytick.labelsize': 6,
})
plotter = DosPlotter(stack=False)
plotter.add_dos('Total DOS', complete_dos)
for key, value in complete_dos.get_element_dos().items():
plotter.add_dos(key, value)
plotter.get_plot(
xlim=(-3, 8),
# ylim=(-20,20)
)
fig = plt.gcf()
plt.setp(fig, figwidth=figwidth, figheight=figheight, dpi=300, constrained_layout=True)
plt.setp(fig.axes[0].title, text=f'{ele}: {pot}', fontsize=MEDIUM_SIZE)
for line in fig.axes[0].lines:
plt.setp(line, linewidth=LINE_WIDTH)
plt.setp(fig.axes[0].xaxis.label, text='$E - E_F$ (eV)', fontsize=MEDIUM_SIZE)
plt.setp(fig.axes[0].xaxis.get_ticklabels(), fontsize=MEDIUM_SIZE)
plt.setp(fig.axes[0].yaxis.label, text='Density of States', fontsize=MEDIUM_SIZE)
plt.setp(fig.axes[0].yaxis.get_ticklabels(), fontsize=MEDIUM_SIZE)
# plt.setp(fig.axes[0].legend_, frame_on=False, bbox_to_anchor=[0.9,0.9,0.15,0.1])
plt.legend(frameon=False, labelspacing=0.1, loc='upper right')
for text, line in zip(fig.axes[0].legend_.get_texts(), fig.axes[0].legend_.get_lines()):
plt.setp(text, fontsize=SMALL_SIZE)
plt.setp(line, linewidth=LINE_WIDTH)
plt.savefig(f'figures/{ele}-{pot}-dos.png', transparent=True, dpi=300)
# plt.savefig(f'figures/{ele}-{pot}-dos.pdf', transparent=True, dpi=300)
# plt.savefig(f'figures/{ele}-{pot}-dos.pgf', transparent=True, dpi=300)
plt.show()
for ele, value in vruns.items():
for pot, vrun in vruns[ele].items():
complete_dos = vrun.complete_dos
with plt.style.context('default'):
(figwidth, figheight) = (3, 2)
SMALL_SIZE = 6
MEDIUM_SIZE = 8
LARGE_SIZE = 10
LINE_WIDTH = 0.5
plt.rcParams.update({
'pgf.texsystem': 'pdflatex',
'font.family': 'sans-serif',
'text.usetex': True,
'pgf.rcfonts': True,
# 'figure.figsize': [3, 2],
# 'axes.labelsize': 8,
# 'axes.titlesize': 8,
# 'legend.frameon': False,
# 'legend.fontsize': SMALL_SIZE,
# 'legend.loc': 'upper right',
# 'lines.linewidth': 0.5,
# 'xtick.labelsize': 6,
# 'ytick.labelsize': 6,
})
plotter = DosPlotter(stack=True)
for key, value in reversed(complete_dos.get_element_spd_dos('Cl').items()):
plotter.add_dos(f'Cl (${key}$)', value)
for key, value in reversed(complete_dos.get_element_spd_dos('Na').items()):
plotter.add_dos(f'Na (${key}$)', value)
plotter.get_plot(
xlim=(-3, 8),
# ylim=(-10,10)
)
fig = plt.gcf()
plt.setp(fig, figwidth=figwidth, figheight=figheight, dpi=300, constrained_layout=True)
plt.setp(fig.axes[0].title, text=f'{ele}: {pot}', fontsize=MEDIUM_SIZE)
for line in fig.axes[0].lines:
plt.setp(line, linewidth=LINE_WIDTH)
for patch in fig.axes[0].patches:
patch.set(edgecolor=None)
plt.setp(fig.axes[0].xaxis.label, text='$E - E_F$ (eV)', fontsize=MEDIUM_SIZE)
plt.setp(fig.axes[0].xaxis.get_ticklabels(), fontsize=MEDIUM_SIZE)
plt.setp(fig.axes[0].yaxis.label, text='Density of States', fontsize=MEDIUM_SIZE)
plt.setp(fig.axes[0].yaxis.get_ticklabels(), fontsize=MEDIUM_SIZE)
# plt.setp(fig.axes[0].legend_, frame_on=False, bbox_to_anchor=[0.9,0.9,0.15,0.1])
for text, line, patch in zip(fig.axes[0].legend_.get_texts(), fig.axes[0].legend_.get_lines(), fig.axes[0].legend_.get_patches()):
plt.setp(text, fontsize=SMALL_SIZE)
plt.setp(line, linewidth=LINE_WIDTH)
patch.set(height=SMALL_SIZE)
plt.legend(
frameon=False,
fontsize=SMALL_SIZE,
loc='upper right',
labelspacing=0.1,
columnspacing=0.2,
ncol=1
)
plt.savefig(f'figures/{ele}-{pot}-spd-dos.png', transparent=True, dpi=300)
# plt.savefig(f'figures/{ele}-{pot}-spd-dos.pdf', transparent=True, dpi=300)
# plt.savefig(f'figures/{ele}-{pot}-spd-dos.pgf', transparent=True, dpi=300)
plt.show()
Local environment¶
from pymatgen.core.structure import Structure
list(vruns.iterrows())
with plt.style.context('default'):
figwidth, figheight = 3, 2
SMALL_SIZE = 6
MEDIUM_SIZE = 8
LARGE_SIZE = 10
LINE_WIDTH = 0.5
plt.rcParams.update({
'pgf.texsystem': 'pdflatex',
'font.family': 'sans-serif',
'text.usetex': True,
'pgf.rcfonts': True,
'figure.figsize': [figwidth, figheight],
'figure.titlesize': LARGE_SIZE,
'font.size': SMALL_SIZE,
'axes.labelsize': MEDIUM_SIZE,
'axes.titlesize': MEDIUM_SIZE,
'xtick.labelsize': SMALL_SIZE,
'ytick.labelsize': SMALL_SIZE,
'legend.fontsize': SMALL_SIZE,
'legend.frameon': False,
'legend.loc': 'upper right',
'lines.linewidth': LINE_WIDTH,
})
fig, axes = plt.subplots(
figsize=[6, 4],
ncols=len(list(vruns.items())), nrows=len(list(vruns.iterrows())),
sharex=True, sharey=True,
dpi=300,
constrained_layout=True
)
bins = np.linspace(2.70, 2.90, int(50))
for icol, (ele, value) in enumerate(vruns.items()):
for irow, (pot, vrun) in enumerate(vruns[ele].items()):
center_indices, points_indices, offset_vectors, distances = vrun.final_structure.get_neighbor_list(r=3)
# print(irow, icol, pot, ele, distances)
axes[irow, icol].hist(distances, bins=bins)
axes[irow, icol].axvline(
x=2.81,
c='r',
alpha=0.5,
zorder=0, clip_on=False)
mean, std = np.mean(distances), np.std(distances)
axes[irow, icol].set(
xlim=(bins[0], bins[-1]),
ylim=(0, 80)
)
axes[irow, icol].text(
0.95, 0.95,
f'$\mu = {mean:.3f}$\n$\sigma = \\textrm}$',
horizontalalignment='right',
verticalalignment='top',
transform=axes[irow, icol].transAxes,
fontsize=SMALL_SIZE
)
axes[irow, icol].tick_params(axis='both', direction='in')
axes[irow, icol].spines[['right', 'top']].set_visible(False)
if icol == 0:
axes[irow, icol].set(ylabel=f'{pot}')
if irow == 0:
axes[irow, icol].set(title=f'{ele}')
if irow == len(list(vruns.iterrows()))-1:
axes[irow, icol].set(xlabel=r'$d_\textrm{Na-Cl}\,(\textrm{\AA})$')
plt.savefig(f'figures/d_Na-Cl.png', transparent=True, dpi=300)
# plt.savefig(f'figures/d_Na-Cl.pdf', transparent=True, dpi=300)
# plt.savefig(f'figures/d_Na-Cl.pgf', transparent=True, dpi=300)
plt.show()
vrun.final_structure.get_neighbor_list(r=3)