(no title)

周日闲着心血来潮,看看远处的Gentoo,这篇博文也当成长期更新的记录贴吧,只针对于AMD 64架构,just keep learning!

安装

引导进入安装镜像

与其他发行版一样,Gentoo安装也需要一个启动镜像,有纯命令行的最小化安装CD和Gentoo LiveGUI两种,我选择前者。这个镜像可以方便地使用Ventoy进行引导,但仅仅是一个安装媒介而已

引导时我选择默认地gentoo内核,除此外还有一个gentoo-nofb,nofb即no framebuffer,它让内核和用户态程序通过一个统一的设备文件(通常是 /dev/fb0)直接访问显卡的显存,从而在不依赖 X server 或 Wayland 之类图形系统的情况下,也能在控制台显示图形、字体、图片,甚至跑一些简单的图形程序。

安装时可以用另一台机器远程ssh链接,一边查wiki一边安装,复制粘贴什么的都很方便。然鹅我没有多余的机子,手敲长串的命令行,屏幕还没驱动默认最大亮度,就很坐牢。

配置网络

gentoo的安装是需要网络的,如果计算机使用网线连接到IPv6路由或DHCP路由器,那么安装镜像一般是可以自动连接网络的。如果没有路由器只有个光猫,需要pppoe拨号上网,具体参考wiki。用安卓手机通过数据线连接至电脑,启用USB网络共享模式大概也是可行的。

使用WiFi连接网络也是可行的,可以使用net-setup工具,参考wiki

磁盘分区

这一步可以在先前已有的系统上进行,实在不行在pe系统里进行也可以,更直观更不容易出错,并且Linux自带的cfdisk工具和diskgenius之类的软件的操作逻辑有些不同。数据无价,谨慎更改!

2025年了,legacy BIOS逐渐消亡,GUID(GPT)表早已成为主流。我大概分了三个区:根分区、与Windows共用的EFI分区、Swap分区

使用fdisk -l命令查看磁盘设备,如果不是用ssh连接的,消息过多,可能会无法看到部分信息,此时也可以使用lsblk命令查看。

分好区之后接着将分区格式化为Linux适用的文件系统。俗话说:人生苦短,远离Gentoo😇。Gentoo是纯手动档,大量的软件包需要自己编译,这可能会对磁盘进行较多的高速I/O读写,笔者了解的文件系统类型也不多:

  • xfs
  • btrfs
  • ext4(以及过去的ext3,ext2)
  • zfs
  • f2fs

我选择了xfs,它在高速IO读写方面略胜一筹,接下来是格式化:

1
mkfs.xfs /dev/nvmexnxpx

EFI分区由于是共用的,不用格式化,接下来开启swap分区:

1
mkswap /dev/nvmexnxpx

接下来挂载分区:

1
2
mkdir --parents /mnt/gentoo
mount /dev/nvmexnxpx /mnt/gentoo

wiki推荐挂载EFI分区到/efi,我已经习惯了挂载到/boot/efi

1
2
mkdir --parents /mnt/gentoo/boot/efi
mount /dev/nvmexnxpx /mnt/gentoo/boot/efi

安装基本的系统环境

安装镜像提供了一个临时的基本系统环境,带有内核、常用工具(bash、fdisk、wget…),让你能进入一个命令行环境进行磁盘分区、下载文件等操作。它不是最终系统,只是“施工工具”。接下来需要下载stage3文件,这是一个 预编译好的基础系统,是一个能开机、能用 chroot 进去的最小 Linux 用户空间环境,里面已经有:

  • C 库(glibc 或 musl)
  • 编译器(gcc 或 clang)
  • 核心工具链(binutils、bash、coreutils、portage …)

有了 stage3,就能在 chroot 里运行 emerge 去拉源码并编译其余的软件。

stage3文件类型多种多样,我选择了最省事最习惯的使用systemd和multilib的stage3文件。

首先进入挂载位置/mnt/gentoo,然后设置时间,这个很关键,因为Stage 存档通常使用 HTTPS 获取,这需要相对准确的系统时间。

1
chronyd -q

使用命令行浏览器下载stage3文件:

1
links https://www.gentoo.org/downloads/mirrors/

进入后选择Asia源中的CN,即中国镜像源,选择合适的镜像站点后进入releases/amd64/autobuilds/目录,选中合适的stage3文件,按d键开始下载,一般下载没什么问题。

接着解压包:

1
tar xpvf stage3-*.tar.xz --xattrs-include='*.*' --numeric-owner -C /mnt/gentoo

Gentoo系统是需要编译的,因此接下来配置的编译选项也是非常重要的。

编译选项配置文件是/mnt/gentoo/etc/portage/make.conf,下面是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# These settings were set by the catalyst build script that automatically
# built this stage.
# Please consult /usr/share/portage/config/make.conf.example for a more
# detailed example.
COMMON_FLAGS="-march=znver4 -O3 -pipe -fomit-frame-pointer -flto=thin"
CFLAGS="${COMMON_FLAGS}"
CXXFLAGS="${COMMON_FLAGS}"
FCFLAGS="${COMMON_FLAGS}"
FFLAGS="${COMMON_FLAGS}"
RUSTFLAGS="-C target-cpu=native -C opt-level=3 -C codegen-units=1 -C lto"
MAKEOPTS="-j20"


# NOTE: This stage was built with the bindist USE flag enabled

# This sets the language of build output to English.
# Please keep this setting intact when reporting bugs.
LC_MESSAGES=C.utf8

CFLAGS 和 CXXFLAGS 变量分别定义了GCC C和C ++编译器的优化标志,详细信息在GNU在线手册中。

编译安装基础系统

接下来就是chroot到stage3所带的基础编译环境,开始编译新的系统,在这之前需要:

1
cp --dereference /etc/resolv.conf /mnt/gentoo/etc/

以此确保chroot后网络仍然可用,接下来:

1
2
3
arch-chroot /mnt/gentoo
source /etc/profile
export PS1="(chroot) ${PS1}"

至此,我们已经进入了stage3所带的基础编译环境,也是根分区。接下来挂载EFI分区:

1
mount /dev/nvmexnxpx /boot/efi

然后选择合适的镜像源:

1
2
emerge --ask --verbose --oneshot app-portage/mirrorselect
mirrorselect -i -o >> /etc/portage/make.conf

然后执行:

1
emerge-webrsyn

Gentoo的软件包源rsyngit两种方式,可以方便时换成后者。

接下来选择合适的配置文件,我选择了plasma桌面且带有systemd,使用eselect profile list查看所有的配置文件,使用eselect profile set 2选择相应的配置文件。

也有部分二进制软件包来自官方,参考wiki,然而我拒绝🤪。

接下来是USE变量的设置,这非常重要,它控制了编译时是否加入/删除对某些功能的支持,比如说-gtk就是删除所有编译后的包对gtk的支持,他也是在/mnt/gentoo/etc/portage/make.conf中进行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
USE="
# 桌面环境 & UI
X wayland kde plasma egl gles2 gtk gtk2 gtk3 gtk4 qt4 qt5 qt6
xinerama xrandr xcomposite xcursor
opengl vulkan vaapi vdpau drm

# 音频
alsa pulseaudio pipewire sound server dbus
jack -oss

# 视频/图像
jpeg png gif tiff webp heif
ffmpeg theora x264 x265
av1 vpx opencl openmp

# 输入法/语言
icu nls l10n_zh l10n_en
freetype fontconfig
xim ibus

# 网络
ssl tls http2 curl
samba smbclient
ssh bluetooth wifi networkmanager
dns ipv6 zeroconf

# 游戏相关
steam vulkan opengl sdl sdl2
gamepad joystick

# 文件压缩/格式
zlib bzip2 lzma lzo zstd
archive rar zip 7zip

# 开发/脚本语言支持
python ruby perl lua
jit jit-lua

# 其他通用优化
threads multilib
udev udisks upower
policykit systemd
"

接下来设置CPU_FLAGS_*,这是给 ebuild 提供 CPU 指令集信息的,不同包会根据这些 flag 决定是否启用 SIMD 优化。

1
2
3
emerge --ask --oneshot app-portage/cpuid2cpuflags
cpuid2cpuflags
echo "*/* $(cpuid2cpuflags)" > /etc/portage/package.use/00cpu-flags

/etc/portage/make.conf设置显示卡:

1
VIDEO_CARDS="amdgpu radeonsi vdpau vaapi"

娱乐用户直接大开:

1
ACCEPT_LICENSE="*"

更新系统:

1
emerge --ask --verbose --update --deep --newuse @world

设置时区:

1
2
3
ls /usr/share/zoneinfo
ls -l /usr/share/zoneinfo/Europe/
ln -sf ../usr/share/zoneinfo/Europe/Brussels /etc/localtime

设置本地化:

1
2
3
echo 'zh_CN.UTF-8 UTF-8' >> /etc/locale.gen
echo 'en_US.UTF-8 UTF-8' >> /etc/locale.gen
locale-gen

安装固件、微码:

1
emerge --ask sys-kernel/linux-firmware

安装内核:

1
sys-kernel/installkernel

安装引导程序,创建文件/etc/portage/package.use/installkernel

1
sys-kernel/installkernel grub

接着:

1
emerge --ask sys-kernel/installkernel

配置系统

创建fstab!!!

设置主机名:

1
2
echo storm > /etc/hostname
hostnamectl hostname storm

安装dhcpcd并启用:

1
2
emerge --ask net-misc/dhcpcd
systemctl enable dhcpcd

创建hosts文件

设置root密码

收尾

GRUB_PLATFORMS="efi-64"加如/etc/portage/make.conf

1
2
3
echo 'GRUB_PLATFORMS="efi-64"' >> /etc/portage/make.conf
emerge --ask sys-boot/grub
grub-install --target=x86_64-efi --efi-directory=/efi

创建一个普通用户:

1
2
useradd -m -G users,wheel,audio -s /bin/bash storm
passwd storm

添加sudo权限

内核选择

  • gentoo

    默认内核,支持K8 CPU(包括NUMA支持)和EM64T CPU。

  • gentoo-nofb

    与“gentoo”相同,但没有framebuffer支持。

Comment and share

(no title)

后记

重新安装了Rocky Linux10,并安装了Slurm作业系统,尝试编译安装vasp,由于用的Rocky10,gcc等各种套件都比较新,折腾麻了,各种编译报错,干脆换到了较新的vasp.6.5.1版本,一遍过!

编译HDF5

编译HDF5部分内容参考了教程:https://zhuanlan.zhihu.com/p/617261007

编译安装HDF5之前需要先编译安装它的依赖zlib😂,首先去官网https://www.zlib.net/,目前最新的的版本是zlib 1.3.1,下载源代码source code,哪种格式的都行,我的工作站有网就直接用wget了:

1
wget https://www.zlib.net/zlib-1.3.1.tar.gz

没网的集群自己下载到本地计算机再上传到集群上。

解压、配置、编译、测试、安装:

1
tar -zxvf zlib-1.3.1.tar.gz
1
./configure --prefix=install_directory
1
make -jN
1
make test
1
make install

接下来安装szip,官网(https://docs.hdfgroup.org/archive/support/doc_resource/SZIP/index.html)同样只提供了源代码,需要编译安装,安装过程大差不差,类似的

接下来编译安装HDF5,官网打开比较慢、、、目前最新版本是1.14.6,使用1.14.5版本编译成功

1
wget https://support.hdfgroup.org/releases/hdf5/v1_14/v1_14_6/downloads/hdf5-1.14.6.tar.gz
1
tar -zxvf hdf5-1.14.6.tar.gz

接下来是配置,需要使用intel的编译器,所有首先需要安装intel的oneapi套件,安装下面这些:

intel的C++编译器,部分组件会捆绑其他组件
intel的fortran编译器

需要格外注意,我是用的是最新版的oneapi套件,已经删除了对iccifort的支持,所以需要注意CCFCCXX的写法

1
2
3
4
5
6
7
8
9
./configure --prefix=/opt/apps/vasp.6.5.1/hdf5/hdf5 \
--enable-fortran \
--with-zlib=/opt/apps/vasp.6.5.1/hdf5/dependences/zlib \
--with-szlib=/opt/apps/vasp.6.5.1/hdf5/dependencies/szip \
--enable-shared \
--enable-parallel \
CC=mpiicx \
FC=mpiifx \
CXX=mpiicpx

接着编译、测试、安装

1
make -jN
1
make check
1
make install

Wannier安装

接下来编译安装Wannier90,官网是https://wannier.org/download/,下载:

1
wget https://github.com/wannier-developers/wannier90/archive/v3.1.0.tar.gz

然后是解压,接着拷贝config文件夹中的模板:

1
cp config/make.inc.ifort make.inc

Wannier90的模板并没有调整,依然使用的ifort,然鹅Intel已经废弃了ifort,所以得手动修改一下模板了,把ifort改成ifx,依此类推,改完后的模板如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
root@cernet2:~/Wannier90/wannier90-3.1.0# cat make.inc 

#=====================================================
# For Linux with intel version 11/12 on 64bit machines
#=====================================================
F90 = ifx
COMMS=mpi
MPIF90=mpiifx
FCOPTS=-O2
LDOPTS=-O2

#========================================================
# Intel mkl libraries. Set LIBPATH if not in default path
#========================================================

LIBDIR = /opt/intel/mkl/lib/intel64
LIBS = -L$(LIBDIR) -lmkl_core -lmkl_intel_lp64 -lmkl_sequential -lpthread

#=======================
# ATLAS Blas and LAPACK
#=======================
#LIBDIR = /usr/local/lib
#LIBS = -L$(LIBDIR) -llapack -lf77blas -lcblas -latlas

接着编译吧:

1
make -jN

再编译库:

1
make lib -jN

最终,得到了两个重要的文件:wannier90.xlibwannier.a,记住这两个文件所在的目录,

VTST的安装

VTST,是一个”插件“,随着vasp的编译而安装,首先下载VTST

1
wget https://theory.cm.utexas.edu/code/vtstcode-209.tgz

参考官网的安装过程,需要修改源代码,谨慎且小麻烦,https://theory.cm.utexas.edu/vtsttools/installation.html

按照教程修改vasp的main.F源代码,源代码都在src目录内

修改main.F,记得备份源码
修改main.F,记得备份源码

接着将VTST文件夹下的所有源码文件复制到vasp的源码文件夹内,此时会覆盖掉chain.F

然后修改.object

修改.object,记得备份

接着修改源码目录内的makefile文件,将其中的某一行由:

1
LIB=lib parser

修改为:

1
LIB= lib parser pyamff_fortran

我编译vasp时为了加快编译速度,需要并行编译,因此也需要将:

1
dependencies: sources

修改为:

1
dependencies: sources libs

至此,VTST需要做的已经完成了,开始编译vasp吧~~

编译vasp

使用makefile.include.oneapi这个模板,这个模板已经适配了新版的intel套件,只需要修改hdf5wannier90这两个地方就行,本次编译使用了比较激进的方案,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# Default precompiler options
CPP_OPTIONS = -DHOST=\"LinuxIFC\" \
-DMPI -DMPI_BLOCK=8000 -Duse_collective \
-DscaLAPACK \
-DCACHE_SIZE=4000 \
-Davoidalloc \
-Dvasp6 \
-Dtbdyn \
-Dfock_dblbuf

CPP = fpp -f_com=no -free -w0 $*$(FUFFIX) $*$(SUFFIX) $(CPP_OPTIONS)

FC = mpiifort -fc=ifx
FCL = mpiifort -fc=ifx

FREE = -free -names lowercase

FFLAGS = -assume byterecl -w

OFLAG = -O3 -xHost -unroll-aggressive #激进的方案
OFLAG_IN = $(OFLAG)
DEBUG = -O0

# For what used to be vasp.5.lib
CPP_LIB = $(CPP)
FC_LIB = $(FC)
CC_LIB = icx
CFLAGS_LIB = -O
FFLAGS_LIB = -O1
FREE_LIB = $(FREE)

OBJECTS_LIB = linpack_double.o

# For the parser library
CXX_PARS = icpx
LLIBS = -lstdc++

##
## Customize as of this point! Of course you may change the preceding
## part of this file as well if you like, but it should rarely be
## necessary ...
##

# When compiling on the target machine itself, change this to the
# relevant target when cross-compiling for another architecture
VASP_TARGET_CPU ?= -xHOST
FFLAGS += $(VASP_TARGET_CPU)

# Intel MKL (FFTW, BLAS, LAPACK, and scaLAPACK)
# (Note: for Intel Parallel Studio's MKL use -mkl instead of -qmkl)
FCL += -qmkl=parallel
MKLROOT ?= /path/to/your/mkl/installation
LLIBS += -L$(MKLROOT)/lib/intel64 -lmkl_scalapack_lp64 -lmkl_blacs_intelmpi_lp64
INCS =-I$(MKLROOT)/include/fftw

MKL_THREADING_LAYER=INTEL
OMP_NUM_THREADS=1 # MPI+MKL 并行时必须绑 1,否则容易 oversubscribe

# HDF5-support (optional but strongly recommended, and mandatory for some features)
CPP_OPTIONS+= -DVASP_HDF5
HDF5_ROOT ?= /opt/apps/vasp.6.5.1/hdf5/hdf5
LLIBS += -L$(HDF5_ROOT)/lib -lhdf5_fortran
INCS += -I$(HDF5_ROOT)/include

# For the VASP-2-Wannier90 interface (optional)
CPP_OPTIONS += -DVASP2WANNIER90
WANNIER90_ROOT ?= /opt/apps/vasp.6.5.1/wannier90-3.1.0
LLIBS += -L$(WANNIER90_ROOT)/lib -lwannier

# For machine learning library vaspml (experimental)
#CPP_OPTIONS += -Dlibvaspml
#CPP_OPTIONS += -DVASPML_USE_CBLAS
#CPP_OPTIONS += -DVASPML_USE_MKL
#CPP_OPTIONS += -DVASPML_DEBUG_LEVEL=3
#CXX_ML = mpiicpc -cxx=icpx
#CXXFLAGS_ML = -O3 -std=c++17 -Wall
#INCLUDE_ML =
1
make DEPS=1 -jN

笔者用的双路2676 v3的落后的工作站,因为调用了24个核心并行编译所以比较快,编译时间大概是四五分钟

接着是测试,make test默认调用4个核心,可以编辑runtest文件手动修改核心数目加快测试速度,关于测试的说明在官网:https://www.vasp.at/wiki/Validation_tests

我自己测试时需要手动加上hdf5库的环境变量,否则一个测试也跑不了:

1
export LD_LIBRARY_PATH=/opt/apps/vasp.6.5.1/hdf5/hdf5/lib:$LD_LIBRARY_PATH

对了,为了避免任何关于内存栈的错误,也请执行下面四行命令(从集群slurm脚本抄的)

1
2
3
4
ulimit -d unlimited
ulimit -s unlimited
ulimit -t unlimited
ulimit -v unlimited

./install_cp2k_toolchain.sh –with-sirius=no –with-intelmpi=system –with-mkl=system –with-openmpi=no –with-plumed=install –with-cmake=system –with-ninja=install –with-dftd4=install

cp2k的dftd4库和ninja库参考公社上的文件,http://bbs.keinsci.com/forum.php?mod=viewthread&tid=55624&highlight=dftd4

Comment and share

使用VASP计算离子电导率,需要进行从头算分子动力学(AIMD)模拟,对象为$\rm Li_6PS_5Cl$ Li-Argyrodite型硫化物固态电解质。

收敛性测试

在进行昂贵的AIMD模拟之前需要进行收敛性测试,测试最优的ENCUT、k点等参数。如果设置太小则有失精度,设置太大又增加成本,收敛性测试的脚本网上应该能找到,我之前用Claude写了一个,发到了公社论坛上面:http://bbs.keinsci.com/thread-55151-1-1.html

变胞优化

一个能量较低、结构合理的起始三维构型是进行MD模拟的第一步,本次模拟使用变胞优化后的惯用晶胞进行。

AIMD

分子动力学模拟的基本步骤如下图:

生产流程图

这次的AIMD跑的很短,总共30ps(30000fs),15000步,即每步2fs(POTIM = 2)。在30ps长的AIMD的模拟中,最初的一段时间体系并未达到平衡,因而需要舍弃,真正的AIMD有效数据此时间段向后截取。

以下是本次AIMD的INCAR:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Global Parameters
ISTART = 1 (Read existing wavefunction, if there)
ISPIN = 1 (Non-Spin polarised DFT)
# ICHARG = 11 (Non-self-consistent: GGA/LDA band structures)
LREAL = .FALSE. (Projection operators: automatic)
ENCUT = 510 (Cut-off energy for plane wave basis set, in eV)
# PREC = Accurate (Precision level: Normal or Accurate, set Accurate when perform structure lattice relaxation calculation)
#LWAVE = .TRUE. (Write WAVECAR or not)
#LCHARG = .TRUE. (Write CHGCAR or not)
ADDGRID= .TRUE. (Increase grid, helps GGA convergence)
LASPH = .TRUE. (Give more accurate total energies and band structure calculations)
PREC = Normal (Accurate strictly avoids any aliasing or wrap around errors)
# LVTOT = .TRUE. (Write total electrostatic potential into LOCPOT or not)
# LVHAR = .TRUE. (Write ionic + Hartree electrostatic potential into LOCPOT or not)
# NELECT = (No. of electrons: charged cells, be careful)
# LPLANE = .TRUE. (Real space distribution, supercells)
# NWRITE = 2 (Medium-level output)
# KPAR = 2 (Divides k-grid into separate groups)
# NGXF = 300 (FFT grid mesh density for nice charge/potential plots)
# NGYF = 300 (FFT grid mesh density for nice charge/potential plots)
# NGZF = 300 (FFT grid mesh density for nice charge/potential plots)

Electronic Relaxation
ISMEAR = 0
SIGMA = 0.05
EDIFF = 1E-06
ALGO = VeryFast
Molecular Dynamics
IBRION = 0 (Activate MD)
NSW = 15000 (Max ionic steps)
EDIFFG = -1E-02 (Ionic convergence, eV/A)
POTIM = 2 (Timestep in fs)
SMASS = 0.12 (MD Algorithm: -3-microcanonical ensemble, 0-canonical ensemble)
TEBEG = 300 (Start temperature K)
TEEND = 300 (Final temperature K)
MDALGO = 2 (Thermostat)
ISYM = 0 (Switch symmetry off)
NWRITE = 0 (For long MD-runs use NWRITE=0 or NWRITE=1)
KPAR = 2
NCORE = 20

BTW,这里的SMASS参数可以设置0。我先进行了2000fs左右的预模拟,如下:

1
2
3
[ctan@baifq-hpc141 SMASS]$ grep SMASS OUTCAR
SMASS = 0 (MD Algorithm: -3-microcanonical ensemble, 0-canonical ensemble)
SMASS = 0.12 Nose mass-parameter (am)

OUTCAR中给出的SMASS值是0.12,设置为0也可以。

简单介绍一下其他参数:

  • MDALGO设置为2,可以控制使用Nose-Hoover热浴
  • TEBEG初始温度
  • TEEND终止温度
  • IBRION设置为-1启用分子动力学模拟

接着就开始漫长的模拟吧,大概模拟了八天左右:

1
2
3
4
5
6
7
8
9
10
11
[ctan@baifq-hpc141 pre-pro]$ tail OUTCAR
User time (sec): 854845.149
System time (sec): 17398.447
Elapsed time (sec): 882414.331

Maximum memory used (kb): 464952.
Average memory used (kb): N/A

Minor page faults: 1437354
Major page faults: 688
Voluntary context switches: 9709

AIMD后处理

本次模拟的目的主要是为了得到离子电导率,模拟结束之后可以使用vaspkit方便地计算扩散系数,否则可能需要使用pymatgen自己编写代码。

首先使用下面的代码查看体系在合适趋于平衡,来自github上的一个老师,仓库地址是https://github.com/tamaswells/XDATCAR_toolkit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
#!/bin/python
# -*- coding:utf-8 -*-
#Convert XDATCAR to PDB and extract energy & temperature profile for AIMD simulations
# -h for help;-b for frame started;-e for frame ended;;-p for PDB conversion
#by nxu tamas@zju.edu.cn
#version 1.2
#date 2019.4.9

import os
import shutil
import numpy as np
import matplotlib as mpl
import math
mpl.use('Agg') #silent mode
from matplotlib import pyplot as plt
from optparse import OptionParser
import sys
from copy import deepcopy

class debug(object):
def __init__(self,info='debug'):
self.info=info
def __call__(self,func):
def wrapper(*args,**kwargs):
print('[{info}] Now entering function {function}.....' \
.format(info=self.info,function=getattr(func,"__name__")))
return func(*args,**kwargs)
return wrapper

class Energy_Temp(object):
def __init__(self):
'Read vasp MD energies and temperature.'
self.temp=[]
self.energy=[]
self.energy_extract()

def energy_extract(self):
print('Now reading vasp MD energies and temperature.')
if os.path.exists("OSZICAR"):
oszicar=open("OSZICAR",'r')
for index,line in enumerate(oszicar):
if "E0=" in line:
self.temp.append(float(line.split()[2]))
self.energy.append(float(line.split()[4]))
oszicar.close()
#return (self.temp,self.energy)
else:
raise IOError('OSZICAR does not exist!')

class XDATCAR(Energy_Temp):
def __init__(self):
'Read lattice information and atomic coordinate.'
super(XDATCAR,self).__init__()
print('Now reading vasp XDATCAR.')
self.lattice=np.zeros((3,3))
self.NPT=False
self.frames=0
self._timestep=1 # timestep 1fs
self.alpha=0.0
self.beta=0.0
self.gamma=0.0
self.current_frame=0

self._lattice1=0.0
self._lattice2=0.0
self._lattice3=0.0
self._lattice=np.zeros(3)
self.format_trans=False

@property
def timestep(self):
return self._timestep
@timestep.setter
def timestep(self,new_time_step):
self._timestep=new_time_step

if os.path.exists("XDATCAR"):
self.XDATCAR=open("XDATCAR",'r')
else:
raise IOError('XDATCAR does not exist!')
title=self.XDATCAR.readline().strip()
for index,line in enumerate(self.XDATCAR):
if "Direct" in line:
self.frames+=1
# elif title in line:
# self.NPT=True
self.XDATCAR.seek(0)
self.lattice_read()
self.XDATCAR.readline()
for i in range(self.total_atom):
self.XDATCAR.readline()

if "Direct configuration" not in self.XDATCAR.readline():
self.NPT=True
#self.frames=len(['Direct' for line in self.XDATCAR if "Direct" in line])
print('Total frames {0}, NpT is {1}'.format(self.frames,self.NPT))
# assert len(self.energy)==self.frames, \
# 'Number of XDATCAR frames does not equal to that of Energy terms in OSZICAR'
self.XDATCAR.seek(0)
self.lowrange=0;self.uprange=self.frames-1
if self.NPT == False: self.lattice_read()

def step_select(self,selected_step): # 't>100ps and t < 1000ps'
'eg. t > 100 and t < 1000'
assert isinstance(selected_step,str),'Selected timestep must be in a "string"'
if 'and' in selected_step:
conditions=selected_step.split("and")
else:
conditions=[selected_step]
for condition in conditions:
try:
if '>=' in condition:
ranges=int(condition.split('>=')[1].strip())
self.lowrange=ranges-1
elif '<=' in condition:
ranges=int(condition.split('<=')[1].strip())
self.uprange=ranges-1
elif '>' in condition:
ranges=int(condition.split('>')[1].strip())
self.lowrange=ranges
elif '<' in condition:
ranges=int(condition.split('<')[1].strip())
self.uprange=ranges-2
else:
print('Selected timestep is invaid!');continue
except ValueError:
print('Selected timestep is invaid!')
sys.exit(0)
if (self.lowrange >= self.frames-1) or (self.uprange < 0):
raise ValueError('Selected timestep is wrong!')
if self.lowrange < 0: self.lowrange= 0
if self.uprange > self.frames-1: self.uprange= self.frames-1

def lattice_read(self):
self.title=self.XDATCAR.readline().rstrip('\r\n').rstrip('\n')
self.scaling_factor=float(self.XDATCAR.readline())
for i in range(3):
self.lattice[i]=np.array([float(j) for j in self.XDATCAR.readline().split()])
self.lattice*=self.scaling_factor
self._lattice1=np.sqrt(np.sum(np.square(self.lattice[0])))
self._lattice2=np.sqrt(np.sum(np.square(self.lattice[1])))
self._lattice3=np.sqrt(np.sum(np.square(self.lattice[2])))
self._lattice[0]=self._lattice1
self._lattice[1]=self._lattice2
self._lattice[2]=self._lattice3
self.alpha=math.acos(np.dot(self.lattice[1],self.lattice[2]) \
/float((self._lattice2*self._lattice3)))/np.pi*180.0
self.beta=math.acos(np.dot(self.lattice[0],self.lattice[2]) \
/float((self._lattice1*self._lattice3)))/np.pi*180.0
self.gamma=math.acos(np.dot(self.lattice[0],self.lattice[1]) \
/float((self._lattice1*self._lattice2)))/np.pi*180.0
self.element_list=[j for j in self.XDATCAR.readline().split()]
try:
self.element_amount=[int(j) for j in self.XDATCAR.readline().split()]
except ValueError:
raise ValueError('VASP 5.x XDATCAR is needed!')
self.total_elements=[]
for i in range(len(self.element_amount)):
self.total_elements.extend([self.element_list[i]]*self.element_amount[i])
#print(self.total_elements)
self.total_atom=sum(self.element_amount)
self.atomic_position=np.zeros((self.total_atom,3))

def __iter__(self):
return self

def __next__(self):
self.next()

def skiplines_(self):
if self.NPT == True: self.lattice_read()
self.XDATCAR.readline()
for i in range(self.total_atom):
self.XDATCAR.readline()

def next(self):
if self.NPT == True: self.lattice_read()
self.XDATCAR.readline()
for i in range(self.total_atom):
line_tmp=self.XDATCAR.readline()
self.atomic_position[i]=np.array([float(j) for j in line_tmp.split()[0:3]])
self.cartesian_position=np.dot(self.atomic_position,self.lattice)
self.current_frame+=1
#
#self.cartesian_position*=self.scaling_factor
return self.cartesian_position

def writepdb(self,pdb_frame):
tobewriten=[]
tobewriten.append("MODEL %r" %(pdb_frame))
tobewriten.append("REMARK Converted from XDATCAR file")
tobewriten.append("REMARK Converted using VASPKIT")
tobewriten.append('CRYST1{0:9.3f}{1:9.3f}{2:9.3f}{3:7.2f}{4:7.2f}{5:7.2f}' .format(self._lattice1,\
self._lattice2,self._lattice3,self.alpha,self.beta,self.gamma))
#print(len(self.total_elements))
for i in range(len(self.total_elements)):
tobewriten.append('%4s%7d%4s%5s%6d%4s%8.3f%8.3f%8.3f%6.2f%6.2f%12s' \
%("ATOM",i+1,self.total_elements[i],"MOL",1,' ',self.cartesian_position[i][0],\
self.cartesian_position[i][1],self.cartesian_position[i][2],1.0,0.0,self.total_elements[i]))
tobewriten.append('TER')
tobewriten.append('ENDMDL')
with open('XDATCAR.pdb','a+') as pdbwriter:
tobewriten=[i+'\n' for i in tobewriten]
pdbwriter.writelines(tobewriten)

def unswrapPBC(self,prev_atomic_cartesian):
diff= self.cartesian_position-prev_atomic_cartesian
prev_atomic_cartesian=deepcopy(self.cartesian_position)
xx=np.where(diff[:,0]>(self._lattice1/2),diff[:,0]-self._lattice1\
,np.where(diff[:,0]<-(self._lattice1/2),diff[:,0]+self._lattice1\
,diff[:,0]))
yy=np.where(diff[:,1]>(self._lattice2/2),diff[:,1]-self._lattice2\
,np.where(diff[:,1]<-(self._lattice2/2),diff[:,1]+self._lattice2\
,diff[:,1]))
zz=np.where(diff[:,2]>(self._lattice3/2),diff[:,2]-self._lattice3\
,np.where(diff[:,2]<-(self._lattice3/2),diff[:,2]+self._lattice3\
,diff[:,2]))
xx=xx.reshape(-1,1);yy=yy.reshape(-1,1);zz=zz.reshape(-1,1)
return (prev_atomic_cartesian,np.concatenate([xx,yy,zz],axis=1))

def reset_cartesian(self,real_atomic_cartesian,center_atom):
if center_atom > len(real_atomic_cartesian)-1:
raise SystemError("Selected atom does not exist!")
for i in range(0,len(real_atomic_cartesian)):
for j in range(3):
if (real_atomic_cartesian[i][j]-real_atomic_cartesian[center_atom][j])>self._lattice[j]/2:
real_atomic_cartesian[i][j]-=self._lattice[j]
if (real_atomic_cartesian[i][j]-real_atomic_cartesian[center_atom][j])<-self._lattice[j]/2:
real_atomic_cartesian[i][j]+=self._lattice[j]
return real_atomic_cartesian

def __call__(self,selected_step):
self.step_select(selected_step)
def __str__(self):
return ('Read lattice information and atomic coordinate.')
__repr__=__str__

class plot(object):
'Plot MD temperature and energy profile'
def __init__(self,lwd,font,dpi,figsize,XDATCAR_inst=None):
self.XDATCAR_inst=XDATCAR_inst;self.timestep=self.XDATCAR_inst.timestep
self.time_range=(self.XDATCAR_inst.lowrange,self.XDATCAR_inst.uprange+1)
self.lwd=lwd;self.font=font;self.dpi=dpi;self.figsize=figsize

#@debug(info='debug')
def plotfigure(self,title='MD temperature and energy profile'):
from matplotlib import pyplot as plt
self.newtemp=self.XDATCAR_inst.temp;self.newenergy=self.XDATCAR_inst.energy
xdata=np.arange(self.time_range[0],self.time_range[1])*self.timestep
axe = plt.subplot(121)
self.newtemp=self.newtemp[self.time_range[0]:self.time_range[1]]
axe.plot(xdata,self.newtemp, \
color='black', lw=self.lwd, linestyle='-', alpha=1)
with open("Temperature.dat",'w') as writer:
writer.write("Time(fs) Temperature(K)\n")
for line in range(len(xdata)):
writer.write("{0:f} {1:f}\n" .format(xdata[line],self.newtemp[line]))
axe.set_xlabel(r'$Time$ (fs)',fontdict=self.font)
axe.set_ylabel(r'$Temperature$ (K)',fontdict=self.font)
axe.set_xlim((self.time_range[0]*self.timestep, self.time_range[1]*self.timestep))
axe.set_title('MD temperature profile')

axe1 = plt.subplot(122)
self.newenergy=self.newenergy[self.time_range[0]:self.time_range[1]]
axe1.plot(xdata,self.newenergy, \
color='black', lw=self.lwd, linestyle='-', alpha=1)
with open("Energy.dat",'w') as writer:
writer.write("Time(fs) Energy(ev)\n")
for line in range(len(xdata)):
writer.write("{0:f} {1:f}\n" .format(xdata[line],self.newenergy[line]))
axe1.set_xlabel(r'$Time$ (fs)',fontdict=self.font)
axe1.set_ylabel(r'$Energy$ (ev)',fontdict=self.font)
axe1.set_xlim((self.time_range[0]*self.timestep, self.time_range[1]*self.timestep))
axe1.set_title('MD energy profile')
#plt.suptitle(title)
fig = plt.gcf()
fig.set_size_inches(self.figsize)
plt.tight_layout()
plt.savefig('ENERGY.png',bbox_inches='tight',pad_inches=0.1,dpi=self.dpi)


if __name__ == "__main__":
parser = OptionParser()
parser.add_option("-b", "--begin",
dest="begin", default=1,
help="frames begin")

parser.add_option("-e", "--end",
dest="end", default='false',
help="frames end")

parser.add_option("-t", "--timestep",
dest="timestep", default=1.0,
help="timestep per frame")

parser.add_option("-p", "--pdb",
action="store_true", dest="format_trans", default=False,
help="choose whether to convert XDATCAR to PDB!")

parser.add_option("--pbc",
action="store_true", dest="periodic", default=False,
help="choose whether to swrap PBC images!")

parser.add_option("-i", "--index",
dest="index", default=-1,
help="choose which atom to center whole molecule!")

parser.add_option("--interval",
dest="interval", default=1,
help="extract frames interval!")

(options,args) = parser.parse_args()
try:
float(options.timestep)
int(options.index)
_interval=int(options.interval)
int(options.begin)
if options.end != 'false': int(options.end)
except:
raise ValueError('wrong arguments')
XDATCAR_inst=XDATCAR()
XDATCAR_iter=iter(XDATCAR_inst)
if options.format_trans:
XDATCAR_inst.format_trans=True
if os.path.exists("XDATCAR.pdb"):
shutil.copyfile("XDATCAR.pdb","XDATCAR-bak.pdb")
os.remove("XDATCAR.pdb")
else:
XDATCAR_inst.format_trans=False
XDATCAR_inst.timestep=float(options.timestep) #timestep 1fs
if options.end == 'false':
XDATCAR_inst('t>= %r' %(int(options.begin)))
else:
XDATCAR_inst('t>= %r and t <= %r' %(int(options.begin),int(options.end))) # frame 10~300 corresponding to 20~600fs
if _interval >(XDATCAR_inst.uprange-XDATCAR_inst.lowrange):
raise SystemError('Trajectory interval exceed selected time range!')
elif _interval<=1:
_interval=1
count=0
current_pdb=1
for i in range(XDATCAR_inst.uprange+1):
if (i>=XDATCAR_inst.lowrange):
cartesian_position=XDATCAR_iter.next()
if count % _interval == 0:
if options.format_trans == True:
if options.periodic == True:
if i == XDATCAR_inst.lowrange:
real_atomic_cartesian=deepcopy(cartesian_position)
if int(options.index) != -1:
real_atomic_cartesian=XDATCAR_inst.reset_cartesian(real_atomic_cartesian,int(options.index)-1)
XDATCAR_inst.cartesian_position=real_atomic_cartesian
prev_atomic_cartesian=deepcopy(cartesian_position)
else:
prev_atomic_cartesian,diffs=XDATCAR_inst.unswrapPBC(prev_atomic_cartesian)
real_atomic_cartesian+=diffs
XDATCAR_inst.cartesian_position=real_atomic_cartesian
XDATCAR_inst.writepdb(current_pdb)
current_pdb+=1
count+=1
else:
XDATCAR_iter.skiplines_()

newtemp=XDATCAR_inst.temp
newenergy=XDATCAR_inst.energy
print('Finish reading XDATCAR.')

timestep=XDATCAR_inst.timestep
print('Selected time-range:{0}~{1}fs'.format((XDATCAR_inst.lowrange)*timestep,\
(XDATCAR_inst.uprange)*timestep))
XDATCAR_inst.XDATCAR.close()
print('Timestep for new PDB trajectory is :{0}fs'.format(timestep*_interval))
lwd = 0.2 # Control width of line
dpi=300 # figure_resolution
figsize=(5,4) #figure_inches
font = {'family' : 'arial',
'color' : 'black',
'weight' : 'normal',
'size' : 13.0,
} #FONT_setup
plots=plot(lwd,font,dpi,figsize,XDATCAR_inst)
plots.plotfigure()

运行python程序:

1
2
3
4
5
6
7
8
9
10
11
12
(base) [storm@X16 vaspkit]$ python 1.py
Now reading vasp MD energies and temperature.
Now reading vasp XDATCAR.
Total frames 15000, NpT is False
Finish reading XDATCAR.
Selected time-range:0.0~14999.0fs
Timestep for new PDB trajectory is :1.0fs
findfont: Font family 'arial' not found.
findfont: Font family ['arial'] not found. Falling back to DejaVu Sans.
findfont: Font family 'arial' not found.
findfont: Font family 'arial' not found.
findfont: Font family 'arial' not found.

查看温度和能量曲线,如下图

温度和能量曲线

大概在5800fs时候体系达到平衡,于是可以使用5800fs的数据进行后处理,使用vaspkit的722功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 ------------>>
722
===================== Selected Options ==========================
1) Select Atoms by Atomic Indices/Labels
2) Select Atoms by Layers
3) Select Atoms by Heights
4) Select Atoms by SELECTED_ATOMS_LIST File

0) Quit
9) Back
------------>>
1
+---------------------------------------------------------------+
The selected atoms will be listed in SELECTED_ATOMS_LIST file.
Input element-symbol and/or atom-indexes to choose [1 <= & <= 52]
(Free-format input, e.g., 1 3 1-4 H O Au all inverse)

------------>>
Li
-->> (01) Written SELECTED_ATOMS_LIST File.
-->> (02) Reading Input Parameters From INCAR File.
-->> (03) Reading Atomic Relaxations From XDATCAR File.
How many initial frames do you want to skip? [Typical value: < 1000]

------------>>
2900
Which frames will be used to calculate? [Typical value: <=5]
(e.g., 10 means the 1st, 11st, 21st, ... frame will be used)

------------>>
1
-->> (04) Written MSD.dat and DIFFUSION_COEFFICIENT.dat Files.
o---------------------------------------------------------------o
| * ACKNOWLEDGMENTS * |
| Other Contributors (in no particular order): Peng-Fei LIU, |
| Xue-Fei LIU, Dao-Xiong WU, Zhao-Fu ZHANG, Tian WANG, Qiang LI,|
| Ya-Chao LIU, Jiang-Shan ZHAO, Qi-Jing ZHENG, Yue QIU and You! |
| Advisors: Wen-Tong GENG, Yoshiyuki KAWAZOE |
:) Any Suggestions for Improvement are Welcome and Appreciated (:
|---------------------------------------------------------------|
| * CITATIONS * |
| When using VASPKIT in your research PLEASE cite the paper: |
| [1] V. WANG, N. XU, J.-C. LIU, G. TANG, W.-T. GENG, VASPKIT: A|
| User-Friendly Interface Facilitating High-Throughput Computing|
| and Analysis Using VASP Code, Computer Physics Communications |
| 267, 108033, (2021), DOI: 10.1016/j.cpc.2021.108033 |
o---------------------------------------------------------------o

722功能可以得到MSD,均方位移,接着可以使用723功能得到扩散系数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
723

+---------------------------- Tip ------------------------------+
Please First Run vaspkit with Task 722/721 to Get MSD.dat File.
+---------------------------------------------------------------+
-->> (01) Reading Input Parameters From INCAR File.
-->> (02) Reading Mean Squared Displacement From MSD.dat File.
Please input the time steps to do linear fitting. [2< & <12100]

------------>>
2,12100
Please input the ionic valency (e.g., 1 2 3)

------------>>
1
-->> (03) Written MSD_FIT.dat File.
+-------------------------- Summary ----------------------------+
Warning! The following values are roughly estimated.
Plot MSD.dat and MSD_FIT.dat files to Evaluate Fitting Precision!
Temperature (K): 300.00
Diffusion Coefficient in x direction (cm^2/s): 0.3511E-05
Diffusion Coefficient in y direction (cm^2/s): 0.3611E-05
Diffusion Coefficient in z direction (cm^2/s): 0.3542E-05
Average Diffusion Coefficient (cm^2/s): 0.3555E-05
Ionic Mobility in x direction (cm^2/s/V): 0.1358E-03
Ionic Mobility in y direction (cm^2/s/V): 0.1397E-03
Ionic Mobility in z direction (cm^2/s/V): 0.1370E-03
Average Ionic Mobility (cm^2/s/V): 0.1375E-03

MSD数据是5800fs-15000fs的,所以723功能计算扩散系数的时候默认读取的这个区间,因此Please input the time steps to do linear fitting选择全部范围就行,最后可以看到锂离子的扩散系数是$\rm 3.555\times 10^{-6} cm^2/s$

根据Nernst-Einstein方程计算离子电导率: $$ \begin{align} \sigma&=\dfrac{ne^2Z^2}{k_BT}D_s\\ \end{align} $$ 其中的n为数密度,单位体积中的锂离子数量

image-20250904121159800

kB为玻尔兹曼常数,Z为离子所带电荷,Ds为扩散系数,离子电导率可计算为 $$ \begin{align} \sigma&=\dfrac{\dfrac{24}{1076\times(10^{-8})^3}\times (1.602\times10^{-19})^2\times 1^2}{1.380\times10^{-23} \times 300}\times 3.555\times10^{-6}\\ &=0.49 \rm mS/cm \end{align} $$

Comment and share

(no title)

计算机网络

第一章:概论

什么是Internet?

  • 网络

  • 计算机网络

  • 互联网

节点

  • 主机节点
    • 画方框
  • 数据交换节点:路由器(工作在网络层)、交换机(工作在链路层)、中继器、负载交换设备
    • 既不是源,也不是目标,用作中转节点
    • 画圆

链路:边,把各个节点连在一起

  • 接入网链路(access):主机连接到互联网的链路,出现“方框”

  • 主干链路(bakcbone):路由器(交换机)之间的,圆和圆连在一起

协议

  • 物理层
  • 网络层

从具体构成角度讲,Internet包括:

  • 计算设备如主机
  • 通信链路
    • 光纤、同轴电缆无线电
    • 传输速率:带宽(bps)
  • 分组交换设备:用于转发分组
    • 路由器
    • 交换机

Comment and share

SEI膜的结构

锂离子电池工作电位范围为2~4.3V。其中,石墨类负极工作电位范围在0~1.0V(vs.Li+/Li),正极工作电位范围一般在2.5~ 4.3V(vs.Li+/Li)。而目前商用电解液不发生氧化还原反应的电化学窗口一般为1.2~3.7V(vs.Li+/Li)。因此,在负极侧,当负极点位 <1.2 V时会生成SEI固态电解质中间相,类似的在正极侧会生成CEI结构。

从机理来看,当负极的Fermi能级高于电解质的LUMO时,电子容易从Fermi能级转移到LUMO轨道,即发生氧化还原反应形成SEI界面

SEI膜形成原理示意图

如果电极材料的充放电电位范围较窄,例如负极的嵌锂电位高于1.2V( vs.Li+/Li ) , 正极的脱锂电位低于 3.5V(vs.Li+/Li),则正负极表面可以不发生电解质的氧化还原反应,不会形成SEI膜。

SEI界相的厚度可能需要>2 nm以防止电子隧穿效应,<50 nm来确保离子能够顺利传输。它的结构如下图所示:

image-20250908105843399

SEI膜的成分和结构通常认为是靠近电极材料的为无机物层,对于锂离子电池,主要包含$\rm Li_2CO_3、LiF、Li_2O$等;中间层为有机物层,包括$\rm ROCO_2Li、ROLi、RCOO_2Li$(R为有机基团)等;最外面为聚合物层,例如PEO-Li等

SEI的成分与微观结构基本不存在普适性的规律,针对特定的电极与电解质体系需要具体问题具体分析,且不 易表征。Novák等对SEI膜表征方法做了总结,包括:俄歇电子能谱(AES),飞行时间二次离子质谱(ToF-SIMS),扫描探针显微 镜(SPM),扫描电子显微镜(SEM),透射电子显微镜(TEM),红外吸收光谱(IRAS),拉曼光谱(RS),X射线衍射(XRD),电子能量损失谱(ELLS),X射线近边吸收谱(XANES),电化学阻抗谱(EIS),差分扫描量热仪(DSC),程序升温脱附仪(TPD),核磁共振仪(NMR),原子吸收光谱(AAS),电化学石英晶体微天平(EQCM),离子色谱(IC),二次电子聚焦离子束与元素线性扫描分析仪(FIB-ELSA),傅立叶变换红外光谱(FTIR),X射线光电子能谱(XPS)等

TEM适合探测纳米材料的微观结构,常用来研究电极材料的SEI膜形貌,但是SEI膜较敏感,在被电子束照射后会收缩甚至消失,在常规透射电镜下难以保持原有的化学状态,无法实现纳米尺度的原位观测。崔屹等人首次拍摄出了具有原子级分辨率的SEI膜透射电镜照片(冷冻电镜)。研究发现,具有马赛克结构SEI的金属锂脱锂不均匀,从而形成大量失 去与电极接触的金属锂,俗称“死锂”导致电池循环效率降低。与之相比,具有层状结构SEI的金属锂脱锂均匀,残留的“死锂”较少,所以循环效率也较高。

SEI膜的化学成分可以使用表面增强拉曼光谱(SERS)研究

Comment and share

项目 3C设备 动力电池 储能
质量能量密度 /(W·h·kg⁻¹) 260~295 240~300 140~200
体积能量密度 /(W·h·L⁻¹) 650~730 500~600 320~450
循环寿命 /周 1000 1500~3000 5000~15000
倍率 3C 1~3C 0.2~0.5C
工作温度 /℃ ‑20~55 ‑30~55 ‑30~55
成本 /(元·(W·h电芯)⁻¹) 1.2~2.0 0.5~1.2 0.5~0.8
电池容量 /A·h 3~20 3.2~200 50~400
工程能力指数 (Cpk) 1.33~1.66
安全性(欧洲汽车危险等级) 4

上表是三种常见的不同应用场合锂离子电池的性能参数

电化学池的环境可近似为恒温、恒压且非体积功只有电功,那么反应中吉布斯自由能的改变即为电功: ΔrGs = −nFEs markdown不方便打出化学标准状态,就用standard上标代替了。

n为每摩尔电极材料在氧化还原反应中转移电子的量,F为法拉第常数(F=96485C/mol),E为电化学池的电动势。

电池的能量密度可以用两种方式表示:质量能量密度(W·h/kg)和体积能量密度(W·h/L),根据不同的体系选择合适的标度,分别定义为: $$ \varepsilon_M=\dfrac{\Delta_rG^s}{\sum M} $$

$$ \varepsilon_V=\dfrac{\Delta_rG^s}{\sum V_M} $$

使用国际单位制,电量定义为: $$ Q=I\times t=\rm 1A\times 3600s=3600C $$ 所以: $$ \rm 1mAh=3.6C $$ 所以比容量定义为: $$ \text{Capacity}=\dfrac{Q}{m}=\dfrac{nF}{m}=\dfrac{F}{M}(\text{C/g})=\dfrac{F}{3.6M}(\text{g/mol}) $$ 常见的非理想情况:

  • 反应物气体来自于外界,若在计算理论能量密度时不考虑气体的质量,计算出的理论能量密度会显著高于考虑气体质量的计算方法
  • 计算采用的吉布斯生成能一般为不含缺陷的体材料(perfect bulk material)的测量数据,实际材料由于存 在缺陷和尺寸效应,导致生成能会偏离理想材料的生成能,因此需要考虑各类缺陷能的贡献:

ΔfGs(real material) = ΔfGs(perfect material) − ∑ΔfGis(defect)

可充放锂电池的可能发展体系

在实际电池电芯中,存在多种非活性物质,如集流体、导电添加剂、黏结剂、隔膜、电解质溶液、引线、封装材料等。

典型电池的计算能量密度与实际能量密度的比值

Comment and share

终于也是把文献整理得差不多了,博客也翻新了一下,继续分享一些之前的计算。

我的计算体系是Argyrodite硫化物固态电解质体系,计算以$\rm Li_6PS_5Cl$为例吧。能带计算最好是使用primitive cell,即原胞。这是惯用晶胞或者超胞计算得到的能带是折叠的,固体物理的相关书籍中有提到,(当然计算成本也是不同的),参考公社论坛上的两篇讨论帖子:

  • http://bbs.keinsci.com/thread-11373-1-1.html
  • http://bbs.keinsci.com/thread-19361-1-1.html

原胞的变胞优化

计算使用我稍微熟一点的VASP来,首先从Materials Project网站下载晶体结构文件,下载primitive cell,如果已经有了conventional cell的cif文件,也可以使用vaspkit的602功能生成PRIMCELL.vasp原胞文件。

然后进行原胞的变胞优化,除了POSCAR的剩下三个输入文件可以使用vaspkit方便地生成,使用101功能,选择lattice relaxation进行变胞优化,102功能生成自洽计算的K点,选择Gamma撒点方案,0.03间隙,原胞计算量比较小,间隙填小一点也无妨。

笔者在Intel 5218R的集群上进行计算,分配了40个物理核心,计算时在INCAR中设置KPAR=2NCORE=20来最大化效率,一般而言NPAR参数不需要设置,KPARNCORE的参数设置众说纷纭,我参考的公社某一篇帖子里的经验,前者设置为2,后者设置为物理核心数的一般,当然这是对于CPU版本的VASP而言的。

原胞的变胞优化用时很短:

1
2
3
4
5
6
7
8
9
10
11
12
 reached required accuracy - stopping structural energy minimisation
[ctan@baifq-hpc141 primitive_cell-opt]$ tail OUTCAR
User time (sec): 88.516
System time (sec): 3.010
Elapsed time (sec): 94.030

Maximum memory used (kb): 238296.
Average memory used (kb): N/A

Minor page faults: 36126
Major page faults: 669
Voluntary context switches: 1271

在确认收敛并确认结构正常后进行能带的计算

PBE泛函计算能带

能带计算的KPOINTS与普通计算的KPOINTS不一样,通常需要第一布里渊区内的一条或几条高对称点路径来计算能带性质

VASP的计算中PBE泛函是被使用最广泛的,需要修改K点和输入文件。参考vaspkit官网的教程:http://vaspkit.cn/index.php/27.html

PBE能带的计算可以分两步进行。

首先进行自洽计算,此时也即(最常用的)静态计算,将变胞优化的CONTCAR更名为POSCAR,使用vaspkit的101功能,选择Static-Calculation,k点选择可以不变。

接着进行能带计算,需要使用vaspkit的303功能生成高对称K点,将生成的KPATH.in文件更名为KPOINTS

1
2
(base) [storm@X16 temp]$ ls
HIGH_SYMMETRY_POINTS INCAR KPATH.in KPOINTS POSCAR POTCAR PRIMCELL.vasp SYMMETRY vmd-1.9.3

可以看到,303功能也生成了原胞结构,当然前面我们已经提到过。

在进行能带计算的目录中,将上一步自洽计算的CHGCAR文件拷贝过来并在INCAR中设置电荷密度不更新,即ICHARG=11,这是必须的,为了加速计算,建议读取上一步的WAVECAR文件,虽然ISTART设置为1或者2程序都可以读取WAVECAR,但是为了保持整个计算任务中(第一步和第二步)的平面波基组一致,建议使用ISTART=2

此种方法计算能带分两步的原因是使用高密度 k 点网格进行计算时耗时较长,因此用不耗时的普通静态计算多次迭代得到的CHGCARWAVECAR来加速高密度k点网格的计算。

其实对于我的小体系,直接使用高密度k点网格进行静态计算也是完全可接受的,并且SCF 和能带完全自洽,会更精确一些,用时并不长:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[ctan@baifq-hpc141 primitive_cell-band_PBE]$ ls
4736.log CONTCAR HIGH_SYMMETRY_POINTS KPOINTS PCDAT PRIMCELL.vasp slurm-4736.out vasp.out WAVECAR
CHG DOSCAR INCAR OSZICAR POSCAR PROCAR SYMMETRY vaspout.h5 XDATCAR
CHGCAR EIGENVAL KPATH.in OUTCAR POTCAR REPORT vasp.err vasprun.xml
[ctan@baifq-hpc141 primitive_cell-band_PBE]$ tail OUTCAR
User time (sec): 216.131
System time (sec): 4.733
Elapsed time (sec): 223.789

Maximum memory used (kb): 427044.
Average memory used (kb): N/A

Minor page faults: 28812
Major page faults: 666
Voluntary context switches: 1232

HSE06杂化泛函计算能带

PBE最常被诟病的方面即能带计算,它常常低估带隙。By the way,Materials Project上的能带数据刚好能和我用PBE算出来的能带数据对上,明显小于HSE06泛函计算的数据,估计是使用PBE算的。

HSE (Heyd–Scuseria–Ernzerhof)杂化泛函在2003年首次被提出(HSE03),在2006年HSE06被提出,它能更加精确地计算能带。使用HSE06杂化泛函做计算是十分昂贵的,用时是PBE泛函的数十倍不止

受vasp算法所限制,使用HSE06杂化泛函计算能带时只能使用自洽计算(此时为静态计算),不能进行非自洽计算。中文互联网上有一些教程是错误的,PBE可以自洽+非自洽两步,也可以自洽一步,但是HSE06杂化泛函只能一步自洽。不过可以在能带计算之前也先做一下静态计算(自洽计算),然后将产生的WAVECAR文件用于能带计算以加速

使用vaspkit的251功能生成所需要的KPOINTS文件,这是一个“混合”的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 ------------>>
251
-->> (01) Reading Input Parameters From INCAR File.
======================== K-Mesh Scheme ==========================
1) Monkhorst-Pack Scheme
2) Gamma Scheme

0) Quit
9) Back
------------->>
2
+---------------------------- Tip ------------------------------+
Input the K-spacing value for SCF Calculation:
(Typical Value: 0.03-0.04 is Generally Precise Enough)
------------>>
0.03
Input the K-spacing value for Band Calculation:
(Typical Value: 0.03-0.04 for DFT and 0.04-0.06 for hybrid DFT)
------------>>
0.03
+---------------------------------------------------------------+
-->> (02) Reading K-Path From KPATH.in File.
+-------------------------- Summary ----------------------------+
K-Mesh for SCF Calculation: 3 3 3
The Number of K-Points along K-Path No.1: 14
The Number of K-Points along K-Path No.2: 6
The Number of K-Points along K-Path No.3: 18
The Number of K-Points along K-Path No.4: 17
The Number of K-Points along K-Path No.5: 7
The Number of K-Points along K-Path No.6: 7
+---------------------------------------------------------------+
-->> (03) Written KPOINTS File.

首先需要生成自洽(静态)计算所需要的k点网格,然后生成能带计算的高密度k点网格。INCAR文件可以沿用普通静态计算的然后改一下泛函,也可以使用101功能生成HSE06 Calculation的INCAR

HSE06杂化泛函极为昂贵,大概在第五个电子步时,用时超过8h仍未计算完,遂放弃。之前注册某家超算平台时送了一点机时,开通了GPU版本的vasp计算,发现快了不少:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
General timing and accounting informations for this job:
========================================================

Total CPU time used (sec): 22600.775
User time (sec): 10925.947
System time (sec): 11674.828
Elapsed time (sec): 7781.069

Maximum memory used (kb): 6086716.
Average memory used (kb): N/A

Minor page faults: 6831986
Major page faults: 282
Voluntary context switches: 53436870

PROFILE, used timers: 405
=============================

数据后处理与Origin绘图

能带计算结束后,将目录拷贝到可以使用vaspkit的机器上。

对于PBE计算的能带,使用211功能处理数据,得到带隙、CBM、VBM、Fermi Energy等信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 ------------>>
21
============================ Band Options =======================
211) Band-Structure
212) Projected Band-Structure of Only-One-Selected Atom
213) Projected Band-Structure of Each Element
214) Projected Band-Structure of Selected Atoms
215) Projected Band-Structure by Element-Weights
216) The Sum of Projected Band for Selected Atoms and Orbitals

0) Quit
9) Back
------------>>
211
-->> (01) Reading Input Parameters From INCAR File.
+---------------------------------------------------------------+
| >>> The Fermi Energy will be set to zero eV <<< |
+---------------------------------------------------------------+
-->> (02) Reading Fermi-Energy from DOSCAR File.
-->> (03) Reading Energy-Levels From EIGENVAL File.
-->> (04) Reading K-Path From KPOINTS File.
-->> (05) Written KLABELS File.
+---------------------------- Tip ------------------------------+
|If You Want to Get Fine Band Structrue by Interpolating Method.|
| You CAN set GET_INTERPOLATED_DATA = .TRUE. in ~/.vaspkit file.|
+---------------------------------------------------------------+
-->> (06) Written BAND.dat File.
-->> (07) Written REFORMATTED_BAND.dat File.
-->> (08) Written KLINES.dat File.
-->> (09) Written BAND_GAP File.

对于HSE06计算的能带,使用252功能处理数据,得到带隙、CBM、VBM、Fermi Energy等信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 ------------>>
252

-->> (01) Reading Input Parameters From INCAR File.
+---------------------------------------------------------------+
| >>> The Fermi Energy will be set to zero eV <<< |
+---------------------------------------------------------------+
-->> (02) Reading Fermi-Level From FERMI_ENERGY.in File.
-->> (03) Reading Energy-Levels From EIGENVAL File.
-->> (04) Reading KPT-Params in the First Line of KPOINTS File.
-->> (05) Reading K-Path From KPATH.in File.
-->> (06) Written KLABELS File.
+---------------------------- Tip ------------------------------+
|If You Want to Get Fine Band Structrue by Interpolating Method.|
| You CAN set GET_INTERPOLATED_DATA = .TRUE. in ~/.vaspkit file.|
+---------------------------------------------------------------+
-->> (07) Written BAND.dat File.
-->> (08) Written REFORMATTED_BAND.dat File.
-->> (09) Written KLINES.dat File.
-->> (10) Written BAND_GAP File.

绘图时我习惯把价带顶归零,即纵坐标为$\rm E-E_{VBM}$,操作如下。

FERMI_ENERGY文件就拷贝一份,改名为FERMI_ENERGY.in。将文件FERMI_ENERGY.in中的费米能级数据改为价带顶数据,然后再重新运行252功能,此时得到的REFORMATTED_BAND.dat文件中的数据是归零的。

接下来就是origin绘图了

REFORMATTED_BAND.dat文件拖入工作表内,确认只有一个X轴,剩下全是Y轴。新建一个sheet。打开KLABELS文本文件,手动复制数据,如下图

sheet2
sheet1

接着选中sheet1表格中的所有数据,绘制折线图:

很丑、、、

设置一下横坐标,改成高对称点的样子,看图:

sheet2第二列
sheet2第一列

这样横坐标就被设置好了:

还是很丑

接下来就是美化了,放一张最后画好的图吧:

Li6PS5Cl能带图

Comment and share

费米孔

Coulomb孔

由于Coulomb孔的存在,电子之间存在动态相关,如何描述原子分子体系中电子之间的动态相关是量子化学的主要任务

玻色子的波函数是对称的,没有反对称性的要求因此可以靠得很近

Hartree-Fock方程与自洽场计算

HF方程

HF方程是量子化学中计算多电子体系近似波函数和能量的基本方法,通过“平均场”近似把复杂的多电子问题拆解成单电子问题。

多电子波函数用单电子轨道(分子轨道)的Slater行列式表示 (xi)φk(x1) = εkφk(x1) Fock算符 $$ \hat{f}(x_1)=\hat{h}(x_1)+\sum_{j=1}^{N/2}(2\hat{J}-\hat{K_j}) $$

  • 每个电子在其他电子构成的“平均场”中运动,忽略瞬时电子相关性(这是HF的局限性)。

类比理解

想象教室里的学生(电子)在考试:

  • HF方法:每个学生只关心自己的试卷(轨道),并假设其他学生的行为是固定的“平均干扰”(库仑和交换势)。
  • 现实:学生之间其实会互相偷看(电子相关作用),但HF忽略了这点。

自洽场计算(SCF)

一句话总结

SCF是求解HF方程的迭代计算过程,通过不断更新“平均场”直到结果自洽。

关键步骤

  1. 猜初始轨道(如用原子轨道线性组合)。

  2. 构建Fock算符 (依赖当前轨道)。

  3. 解HF方程 ,得到新轨道。

  4. 检查收敛

    新轨道与旧轨道是否几乎相同?

    • 是 → 计算结束,输出能量和波函数。
    • 否 → 用新轨道回到步骤2,继续迭代。

为什么叫“自洽”?

最终得到的轨道生成的“平均场”,与计算这些轨道时用的场是一致的(自圆其说)。

1
2
3
4
5
6
7
[多电子薛定谔方程]  
↓ 太难解 → 近似
[HF方程](单电子近似 + 平均场)
↓ 需要迭代求解
[SCF计算]
↓ 收敛后
[分子轨道和能量] → 用于计算化学性质

组态相互作用(Configuration Interaction, CI)计算

CI是一种后Hartree-Fock方法,通过将多个电子组态(Slater行列式)线性组合,考虑电子相关能,弥补HF方法的不足。

核心思想

  • HF的缺陷: HF单行列式近似忽略了电子瞬时排斥(动态相关)和激发态贡献(静态相关),导致能量偏高。

  • CI的改进: 将波函数表示为多个激发组态的叠加: ΨCI = c0ΦHF + ∑iciΦisingle + ∑jcjΦjdouble + ⋯

    • (Φisingle):单激发组态(一个电子从占据轨道跃迁到虚轨道)。
    • ( Φjdouble ):双激发组态(两个电子同时跃迁),贡献最大。

CI等级

类型 包含的激发组态 计算成本 典型应用
CIS 仅单激发 激发态初步探索
CISD 单+双激发 中等 小分子基态相关能修正
CISDT 单+双+三激发 高精度小体系
FCI 全组态(所有可能激发) 天文数字 极限精度(如H₂O极小基)

关键问题

  • 计算量爆炸:FCI的组态数随电子数和轨道数阶乘增长,仅适用于极小体系。
  • 大小一致性:CISD不满足大小一致性(如两个远离的H原子能量≠2×单个H能量),需更高激发或耦合簇(CC)修正。
1
2
3
4
5
6
7
[Gauss基函数]
↓ 基组展开
[Hartree-Fock方程] → 单行列式近似
↓ 电子相关修正
[组态相互作用(CI)] → 用二次量子化算符组合激发组态
↓ 更高精度
[耦合簇(CC)、多参考方法]

理论计算中的各种方法

简单总结了一下Sob老师的博文,个人的总结仅供参考,详细信息请阅读原文:http://sobereva.com/680

任务类型

  • 优化极小点
  • 优化过渡态
  • 产生反应路径
  • 振动分析
  • 分子动力学
  • 构象/构型搜索
  • 波函数分析

计算方法

计算方法 VASP
分子力场 计算耗时极低 无法描述化学反应
机器学习势 通过机器学习的思想构造依赖于原子坐标的分子描述符与能量之间的抽象关系
密度泛函理论(QM) 性价比非常高 耗时高于分子力场N个数量级
Hartree-Fock(QM) 完全过时
后Hatree-Fock类(QM) 额外把动态相关在一定程度上考虑了进来 耗时高了很多
MCSCF如CASSCF(QM) 弥补HF缺乏对静态相关的考虑 几乎没有或很少考虑动态相关
多参考方法如CASPT2、NEVPT2、MRCI(QM) 在MCSCF基础上进一步把动态相关考虑进来,精度整体很好,普适性很强 昂贵
半经验类方法如AM1、PM3、PM6(QM) 对HF的简化以巨幅降低耗时,耗时只是HF的微小零头 精度低,只能用于有限的元素
GFN-xTB(QM) 相当于纳入了一定DFT思想的半经验级别的方法,整体精度和可靠性>=主流的半经验方法

凡是基于量子理论思想提出的,在实际数值求解的过程中一般都要涉及分子轨道,绝大多数计算程序中都是把分子轨道展开成基函数的线性组合来描述的

最常见的基函数一类是原子中心基函数(如Gaussian等大多数量子化学程序以及CP2K等部分第一性原理程序用的高斯型基函数),其中心一般位于原子核,还有一类常见的基函数是平面波基函数,是大多数计算周期性体系为主的第一性原理程序用的,它的分布覆盖整个被计算的晶胞

基组(basis set)是对于原子中心基函数而言的,例如6-31G*、def2-TZVP、cc-pVDZ等都是很常见的基组,它定义了实际计算时对各种元素原子具体用多少、什么参数的基函数。做HF、DFT、后HF、MCSCF、多参考等方法计算时都需要告诉计算程序用什么基组,基组质量越好,也就是越接近于所谓的完备基组极限,这些方法自身的精度发挥得就越充分,但代价就是耗时越高。一个好方法配一个烂基组,以及一个烂方法配一个好基组,结果都不理想,必须好方法配好基组才能得到较准确结果。

1

Comment and share

仅供学习、交流。

好久没来博客了,分享一下近期折腾的东西以及踩的坑吧。

之前尝试过使用红米AC2100路由器实现校园网认证,但是没有记录,并且其处理MT 7621性能孱弱,干不了太多事,这次换成了稍微热门一点的——捷稀 JCG Q30 Pro,处理器是MT 7981B,架构为Arm Cortex-A53 (1.3 GHz, dual-core),可以干的事更多了,不过个人还是主要用于dogcom认证JLU的校园网以及搭建openclash实现透明代理。

安装ImmortalWRT

首先是替换路由器官方的固件,捷稀JCG Q30 Pro似乎是中国移动的固件,可以为其刷入OpenWRT,也可刷入衍生版比如Immortalwrt、QWRT等等,说来话长,就不提供教程了,具体教程在恩山论坛。

SDK编译

路由器固件用的是Immortalwrt,爱来自恩山大佬😀,原帖链接:https://www.right.com.cn/forum/forum.php?mod=viewthread&tid=8398454&highlight=%E6%8D%B7%E7%A8%80%2BJCG%2BQ30%2BPro

泥吉校园网使用的是哆点客户端,即Dr.COM认证(包括DHCP、PPPoE、802.1x三种认证方式,即D版、P版、X版),桌面端(windows、Linux、MacOS)下载客户端认证,然而Dr.COM备受吐槽、嫌弃,Arm路由器也无法使用Dr.COM。各位民间大佬分析了DrCOM的通信协议,开发了drcom-generic。当时的开发者是通过python2脚本来代替Dr.COM实现校园网认证的,下图是项目仓库。

drcom-generic官方仓库

然而,现在python3才是主流,OpenWRT及其衍生版本的官方仓库中早已抛弃了python2,在离线环境安装python2还是比较痛苦的。因此,后续大佬使用C语言重新复现drcom-generic的功能,将新项目命名为dogcom,以及后续有大佬开发了泥吉专属的C语言版客户端。

dogcom项目仓库
吉大版dogcom

本次使用前者,开发者大佬开源了源代码,需要将源代码编译为MT 7981可用的二进制文件,由于MT 7981性能远不如AMD64处理器,因此编译在Linux(WSL)上完成。笔者使用的机子为2*2676V3,固件为Immortalwrt,首先搭建交叉编译环境。

交叉编译环境需要固件的SDK,然而Immortalwrt并没有现成的关于MT 7981的SDK,还得先手动编译SDK😅。进入适用于处理器的Immortalwrt项目仓库,如下图。

immortalwrt-mt798x仓库

一定一定一定听劝,使用推荐的Ubuntu 20.04 LTS。高版本的gcc编译套件可能会报错,Arch系已踩过坑,降级gcc版本够折腾的😂。以及Rocky Linux 10可能会遇到ninja编译报错。

首先更新源,然后更新系统,接着安装所需依赖。

1
2
3
4
5
6
7
8
9
sudo apt update -y
sudo apt full-upgrade -y
sudo apt install -y ack antlr3 asciidoc autoconf automake autopoint binutils bison build-essential \
bzip2 ccache clang clangd cmake cpio curl device-tree-compiler ecj fastjar flex gawk gettext gcc-multilib \
g++-multilib git gperf haveged help2man intltool lib32gcc-s1 libc6-dev-i386 libelf-dev libglib2.0-dev \
libgmp3-dev libltdl-dev libmpc-dev libmpfr-dev libncurses5-dev libncursesw5 libncursesw5-dev libreadline-dev \
libssl-dev libtool lld lldb lrzsz mkisofs msmtp nano ninja-build p7zip p7zip-full patch pkgconf python2.7 \
python3 python3-pip python3-ply python3-docutils qemu-utils re2c rsync scons squashfs-tools subversion swig \
texinfo uglifyjs upx-ucl unzip vim wget xmlto xxd zlib1g-dev

然后就可以克隆代码,开始搭建交叉环境了。

1
2
3
4
git clone --depth=1 https://github.com/hanwckf/immortalwrt-mt798x.git 
cd immortalwrt-mt798x
scripts/feeds update -a
scripts/feeds install -a

如果是Win11的话,可以在WSL设置中将网络模式设置为镜像mirror,然后在Windows上开启代理,那么WSL中就能连上GitHub。

接着cp -f defconfig/mt7981-ax3000.config .config拷贝配置文件,然后make menuconfig进一步编辑,如下图。

menuconfig

确保上面三项正确。

我们只需要编译一下SDK(不需要生成tar.xz文件)搭建交叉环境就可以了,不需要编译整个Immortalwrt for MT 798x因此执行make toolchain/install -j$(nproc)而不是make -j$(nproc)

dogcom编译

交叉编译环境搭建好后,开始编译dogcom.

Immortalwrt根目录执行:

1
git clone https://github.com/mchome/openwrt-dogcom.git package/openwrt-dogcom
1
git clone https://github.com/mchome/luci-app-dogcom.git package/luci-app-dogcom

前者为dogcom本体,后者为图形化界面。然后再次:

1
make menuconfig

分别添加dogcomluci-app-dogcom

添加dogcom
添加luci-app-dogcom

然后save,重新生成.config配置文件,然后就可以开始编译这两个软件包了(dogcom及其luci图形界面)。

执行:

1
2
make package/openwrt-dogcom/compile
make package/luci-app-dogcom/compile

这两个软件包都不大,很快就编译好了,生成的ipk文件(适用于OpenWRT及其衍生发行版的安装包文件)在/bin/packages目录下:

ipk文件

安装dogcom及图形化界面

拿到ipk文件后就可以安装了。使用ssh登录Immortalwrt。使用winscp上传两个文件,OpenWRT及其衍生版的包管理器为opkg

安装ipk文件

配置校园网

具体的设置可以参考:https://www.bilibili.com/opus/1063006213972688900#reply273467151024

比较麻烦的是配置静态IPV4地址,很容易配置错误导致连不上认证服务器。

参考资料

https://www.bilibili.com/opus/1063006213972688900#reply273467151024

https://shenshichao.notion.site/OpenWrt-JLU-fb5132d707114e22a4486b0e657421f0

https://www.right.com.cn/forum/thread-215978-1-1.html

https://github.com/mchome/dogcom?tab=readme-ov-file

https://deconf.xyz/2019/04/13/openwrt-dogcom/#0x03-%E5%AE%89%E8%A3%85dogcom%E5%AE%89%E8%A3%85%E5%8C%85-%E5%90%AF%E5%8A%A8%E8%AE%A4%E8%AF%81%E7%A8%8B%E5%BA%8F

Comment and share

John Doe

author.bio


author.job


Changchun, China