批量读取能量
- 问题背景
- 解决办法
- 后记
问题背景
在量化计算时,能量是比较重要的概念,而在高斯的输出文件中能量数值往往藏在巨大的文本文件.log
中。根据计算任务的大小,.log
文件大小也有变化,不过日常计算中获得的文件基本大于十万行,按行查找显然不现实。
Linux
的一大好处就是强大的文本文件处理能力,根据计算软件的设定,能量数值出现的行内有特别的标注SCF Done
,使用vi
打开.log
文件,底行模式下输入/SCF Done
回车即能瞬间找到目标值。
vi
打开查找的办法还不够简便,因为要重复进行文件打开和关闭的操作,文本查找工具grep
便体现出他的价值,无需打开文件就能查找符合条件的文本。
[engzsinger@master TBEP]$ grep "SCF Done" TBEP.log SCF Done: E(RB3LYP) = -894.244966606 A.U. after 12 cyclesSCF Done: E(RB3LYP) = -894.263225424 A.U. after 11 cyclesSCF Done: E(RB3LYP) = -894.267963679 A.U. after 10 cyclesSCF Done: E(RB3LYP) = -894.270585213 A.U. after 10 cyclesSCF Done: E(RB3LYP) = -894.273164308 A.U. after 12 cyclesSCF Done: E(RB3LYP) = -894.274338978 A.U. after 10 cyclesSCF Done: E(RB3LYP) = -894.274519626 A.U. after 9 cyclesSCF Done: E(RB3LYP) = -894.274545020 A.U. after 9 cyclesSCF Done: E(RB3LYP) = -894.274550239 A.U. after 9 cyclesSCF Done: E(RB3LYP) = -894.274551722 A.U. after 9 cyclesSCF Done: E(RB3LYP) = -894.274554413 A.U. after 9 cyclesSCF Done: E(RB3LYP) = -894.274556118 A.U. after 9 cyclesSCF Done: E(RB3LYP) = -894.274557613 A.U. after 6 cyclesSCF Done: E(RB3LYP) = -894.274558611 A.U. after 6 cyclesSCF Done: E(RB3LYP) = -894.274558795 A.U. after 6 cyclesSCF Done: E(RB3LYP) = -894.274558911 A.U. after 5 cyclesSCF Done: E(RB3LYP) = -894.274558955 A.U. after 5 cyclesSCF Done: E(RB3LYP) = -894.274558987 A.U. after 4 cyclesSCF Done: E(RB3LYP) = -894.274558987 A.U. after 1 cycles
然而,实际情况中,并不是查看一个.log
文件那么简单
[engzsinger@master b3lyp-d3bj-def2tzvp]$ tree
.
├── AMMION
│ ├── BMIM_NH3
│ │ ├── BMIM_NH3_1
│ │ │ ├── BMIM_NH3_1.chk
│ │ │ ├── BMIM_NH3_1.gjf
│ │ │ ├── BMIM_NH3_1.log
│ │ │ ├── BMIM_NH3_1.o79924
│ │ │ └── g09.pbs
│ │ ├── BMIM_NH3_2
│ │ │ ├── BMIM_NH3_2.chk
│ │ │ ├── BMIM_NH3_2.gjf
│ │ │ ├── BMIM_NH3_2.log
│ │ │ ├── BMIM_NH3_2.o79925
│ │ │ └── g09.pbs
#######################
├── NTF2
│ ├── CHECKED
│ ├── Ig09.pbs
│ ├── NTF2_1
│ │ ├── g09.pbs
│ │ ├── NTF2_1.chk
│ │ ├── NTF2_1.gjf
│ │ └── NTF2_1.log
│ ├── NTF2_2
│ │ ├── g09.pbs
│ │ ├── NTF2_2.chk
│ │ ├── NTF2_2.gjf
│ │ └── NTF2_2.log
├── OAC
│ ├── CHECKED
│ ├── g09.pbs
│ ├── OAC.chk
│ ├── OAC.gjf
│ ├── OAC.log
│ └── OAC_n02.o78139
├── OHBMIM
│ ├── CHECKED
│ ├── g09.pbs
│ ├── OHBMIM.chk
│ ├── OHBMIM.gjf
│ ├── OHBMIM_gpu01.o78552
│ └── OHBMIM.log
└── TRIIM├── CHECKED├── g09.pbs├── TRIIM.chk├── TRIIM.gjf├── TRIIM.log└── TRIIM_n07.o785481205 directories, 5637 files
由于计算过程中的需要,可能会产生多级文件夹,而.log
在各文件夹中非均匀分布。
综上,读取多级文件夹中的.log
文件,我们需要考虑一下三个问题:
- 列出当前路径下所有
.log
文件,包含所有子路径 - 由于是优化过程,每个文件中含有多行
SCF Done
内容,对应优化过程中的每个状态,需要取最后一个值 - 期望只输出数值,过滤掉行内无关信息
解决办法
根据以上需求,将需要用到的命令在下面表格中列出。
命令 | 用法说明 | 说明 |
---|---|---|
find | find -name *.log | 递归找出当前文件夹下所有后缀名为log 的文件 |
basename | basename /opt/bin/bash.exe | 从字符串中剥除路径 |
grep | grep “SCF Done” filename | 查找文件中包含对应字符串的行 |
tail | tail filename -n 1 | 输出文件最后一行 |
awk | awk ‘{print $5}’ filename | 输出文件每行的第五项,默认空格为分隔符 |
xargs | cat file | xargs -n2 | 将文件内容输出为两列 |
将所有命令进行组合,得到一条复合命令,直接执行,就能得到我们需要的结果。在下列输出中,每一行的信息是对应文件夹的名称和其中对应的最低能量(即最后一个能量值),得到的输出内容能直接拷贝至excel
中进行进一步处理。
[engzsinger@master b3lyp-gd3_6-311+G**]$ for a in `find -name *log`; do b=`basename $a`;echo ${b%.log}; cat $a | grep "SCF Done" | tail -n 1| awk '{print $5}' ; done | xargs -n2
BMIM_TFA_DCM_28 -1909.34555265
BMIM_TFA_DCM_15 -1909.34923203
BMIM_TFA_DCM_13 -1909.34555245
BMIM_TFA_DCM_25 -1909.34610488
BMIM_TFA_DCM_3 -1909.34555218
BMIM_TFA_DCM_11 -1909.34610491
BMIM_TFA_DCM_20 -1909.34424301
BMIM_TFA_DCM_1 -1909.34555343
BMIM_TFA_DCM_9 -1909.34555221
BMIM_TFA_DCM_29 -1909.34555353
BMIM_TFA_DCM_2 -1909.33946751
BMIM_TFA_DCM_27 -1909.34555236
BMIM_TFA_DCM_4 -1909.33579623
BMIM_TFA_DCM_22 -1909.34610491
BMIM_TFA_DCM_24 -1909.34129356
BMIM_TFA_DCM_19 -1909.35001942
BMIM_TFA_DCM_5 -1909.34555248
BMIM_TFA_DCM_30 -1909.34555262
BMIM_TFA_DCM_14 -1909.34378242
BMIM_TFA_DCM_10 -1909.34378328
BMIM_TFA_DCM_7 -1909.34626601
BMIM_TFA_DCM_12 -1909.34555654
BMIM_TFA_DCM_23 -1909.34555211
BMIM_TFA_DCM_26 -1909.34809965
BMIM_TFA_DCM_21 -1909.34610488
BMIM_TFA_DCM_0 -1909.34610488
BMIM_TFA_DCM_16 -1909.34923356
BMIM_TFA_DCM_17 -1909.34610488
BMIM_TFA_DCM_6 -1909.33463415
BMIM_TFA_DCM_18 -1909.33460725
BMIM_TFA_DCM_8 -1909.34610488
考虑到每次输入命令比较麻烦,所以将对应的命令写入脚本文件中,也能更加直观的看到命令的逻辑结构。
#! /bin/bash
#
for a in `find -name *log`
dob=`basename $a`echo ${b%.log}cat $a | grep "SCF Done" | tail -n 1 | awk '{print $5}'
done | xargs -n2
其实这个命令的逻辑很简单,主要分为以下三步:
- 列出文件夹下所有的
.log
文件,并作为for循环的数组变量,单独运行输出效果如下
[engzsinger@master b3lyp-gd3_6-311+G**]$ find -name *log
./DCM_BMIM_TFA/BMIM_TFA_DCM_30/BMIM_TFA_DCM_30.log
./DCM_BMIM_TFA/BMIM_TFA_DCM_14/BMIM_TFA_DCM_14.log
./DCM_BMIM_TFA/BMIM_TFA_DCM_10/BMIM_TFA_DCM_10.log
./DCM_BMIM_TFA/BMIM_TFA_DCM_7/BMIM_TFA_DCM_7.log
./DCM_BMIM_TFA/BMIM_TFA_DCM_12/BMIM_TFA_DCM_12.log
./DCM_BMIM_TFA/BMIM_TFA_DCM_23/BMIM_TFA_DCM_23.log
./DCM_BMIM_TFA/BMIM_TFA_DCM_26/BMIM_TFA_DCM_26.log
./DCM_BMIM_TFA/BMIM_TFA_DCM_21/BMIM_TFA_DCM_21.log
./DCM_BMIM_TFA/BMIM_TFA_DCM_0/BMIM_TFA_DCM_0.log
./DCM_BMIM_TFA/BMIM_TFA_DCM_16/BMIM_TFA_DCM_16.log
./DCM_BMIM_TFA/BMIM_TFA_DCM_17/BMIM_TFA_DCM_17.log
./DCM_BMIM_TFA/BMIM_TFA_DCM_6/BMIM_TFA_DCM_6.log
./DCM_BMIM_TFA/BMIM_TFA_DCM_18/BMIM_TFA_DCM_18.log
./DCM_BMIM_TFA/BMIM_TFA_DCM_8/BMIM_TFA_DCM_8.log
- 把
find
得到的结果中路径信息去除掉
[engzsinger@master b3lyp-gd3_6-311+G**]$ basename ./DCM_BMIM_TFA/BMIM_TFA_DCM_8/BMIM_TFA_DCM_8.log
BMIM_TFA_DCM_8.log
- 查找单个文件中包含
SCF Done
的行,获得最后一行,并提取第五个元素,管道命令挨个运行结果如下
[engzsinger@master b3lyp-gd3_6-311+G**]$ cat ./DCM_BMIM_TFA/BMIM_TFA_DCM_8/BMIM_TFA_DCM_8.log | grep "SCF Done" SCF Done: E(RB3LYP) = -1909.34609321 A.U. after 7 cyclesSCF Done: E(RB3LYP) = -1909.34609734 A.U. after 8 cyclesSCF Done: E(RB3LYP) = -1909.34610011 A.U. after 7 cyclesSCF Done: E(RB3LYP) = -1909.34610224 A.U. after 8 cyclesSCF Done: E(RB3LYP) = -1909.34610328 A.U. after 8 cyclesSCF Done: E(RB3LYP) = -1909.34610371 A.U. after 7 cyclesSCF Done: E(RB3LYP) = -1909.34610412 A.U. after 7 cyclesSCF Done: E(RB3LYP) = -1909.34610462 A.U. after 7 cyclesSCF Done: E(RB3LYP) = -1909.34610488 A.U. after 7 cyclesSCF Done: E(RB3LYP) = -1909.34610488 A.U. after 1 cycles
[engzsinger@master b3lyp-gd3_6-311+G**]$ cat ./DCM_BMIM_TFA/BMIM_TFA_DCM_8/BMIM_TFA_DCM_8.log | grep "SCF Done" | tail -n1SCF Done: E(RB3LYP) = -1909.34610488 A.U. after 1 cycles
[engzsinger@master b3lyp-gd3_6-311+G**]$ cat ./DCM_BMIM_TFA/BMIM_TFA_DCM_8/BMIM_TFA_DCM_8.log | grep "SCF Done" | tail -n1 | awk '{print $5}'
-1909.34610488
- 把循环总的输出结果分两列输出
后记
linux
强大之处在于文本处理功能,将各种文本分析工具进行组合,可以获得意想不到的效果,由于专业背景所限,这里只举出了在量化计算中的应用。各位看官也可以根据自己的实际需要自由探索。