ufo/main.cpp

245 lines
11 KiB
C++

# include <iostream>
# include <array>
# include <numbers>
# include <numeric>
# include <fstream>
# include <yaml-cpp/yaml.h>
# include <eigen3/Eigen/Dense>
using namespace std::literals;
// 在相位中, 约定为使用 $\exp (2 \pi i \vec{q} \cdot \vec{r})$ 来表示原子的运动状态
// (而不是 $\exp (-2 \pi i \vec{q} \cdot \vec{r})$)
// 一些书定义的倒格矢中包含了 $2 \pi$ 的部分, 我们这里约定不包含这部分.
// 也就是说, 正格子与倒格子的转置相乘, 得到单位矩阵.
struct Input
{
// 单胞的三个格矢,每行表示一个格矢的坐标,单位为埃
Eigen::Matrix3d PrimativeCell;
// 超胞在各个方向上是单胞的多少倍,这是一个对角矩阵
Eigen::Matrix3i SuperCellMultiplier;
// 在单胞内取几个平面波的基矢
Eigen::Vector3i PrimativeCellBasisNumber;
// 超胞中原子的坐标,每行表示一个原子的坐标,单位为埃
Eigen::MatrixX3d AtomPosition;
// 关于各个 Q 点的数据
struct QPointDataType_
{
// Q 点的坐标,单位为超胞的倒格矢
Eigen::Vector3d QPoint;
// 关于这个 Q 点上各个模式的数据
struct ModeDataType_
{
// 模式的频率,单位为 THz
double Frequency;
// 模式中各个原子的运动状态
// 这个数据是这样得到的: phonopy 输出的动态矩阵的 eigenvector 乘以 $\exp(-2 \pi i \vec q \cdot \vec r)$
// 这个数据可以认为是原子位移中, 关于超胞有周期性的那一部分, 再乘以原子质量的开方.
// 这个数据在读入后会立即被归一化处理.
Eigen::MatrixX3cd AtomMovement;
};
std::vector<ModeDataType_> ModeData;
};
std::vector<QPointDataType_> QPointData;
// 从文件中读取输入, 文件中应当包含: (大多数据可以直接从 phonopy 的输出中复制)
// 单胞的格矢: lattice 单位为埃 直接从 phonopy 的输出中复制
// 超胞的倍数: SuperCellMultiplier 手动输入, 为一个包含三个整数的数组
// 平面波的基矢个数: PrimativeCellBasisNumber 手动输入, 为一个包含三个整数的数组
// 超胞中原子的坐标: points[*].coordinates 单位为超胞的格矢 直接从 phonopy 的输出中复制
// 各个 Q 点的坐标: phonon[*].q-position 单位为超胞的倒格子的格矢 直接从 phonopy 的输出中复制
// 各个模式的频率: phonon[*].band[*].frequency 单位为 THz 直接从 phonopy 的输出中复制
// 各个模式的原子运动状态: phonon[*].band[*].eigenvector 直接从 phonopy 的输出中复制
// 文件中可以有多余的项目, 多余的项目不管.
Input(std::string filename) : Input(YAML::LoadFile(filename)) {}
Input(YAML::Node node);
};
struct Output
{
// 关于各个 Q 点的数据
struct QPointDataType_
{
// Q 点的坐标,单位为超胞的倒格矢
Eigen::Vector3d QPoint;
// 关于这个 Q 点上各个模式的数据
struct ModeDataType_
{
// 模式的频率,单位为 THz
double Frequency;
// 模式的权重
double Weight;
};
std::vector<ModeDataType_> ModeData;
};
std::vector<QPointDataType_> QPointData;
// 将数据写入文件
void write(std::string filename) const;
};
int main(int argc, const char** argv)
{
if (argc != 3)
throw std::runtime_error("Usage: " + std::string(argv[0]) + " input.yaml output.yaml");
Input input(argv[1]);
// 反折叠的原理: 将超胞中的原子运动状态, 投影到一组平面波构成的基矢中.
// 每一个平面波的波矢由两部分相加得到: 一部分是单胞倒格子的整数倍, 要取得足够多, 取大约单胞中原子个数那么多个;
// 一部分是超胞倒格子的整数倍, 取 n 个, n 为超胞对应的单胞的倍数, 其实也就是倒空间中单胞对应倒格子中超胞的格点.
// 只要第一部分取得足够多, 那么单胞中原子的状态就可以完全被这些平面波描述.
// 将超胞中原子的运动状态投影到这些基矢上, 计算出投影的系数, 就可以将超胞的原子运动状态分解到单胞中的多个 q 点上.
// 构建基
// 外层下标对应超胞倒格子的整数倍那部分(第二部分), 也就是对应不同反折叠后的 q 点
// 内层下标对应单胞倒格子的整数倍那部分(第一部分), 也就是对应同一个反折叠后的 q 点上的不同平面波
std::vector<std::vector<Eigen::VectorXcd>> basis;
basis.resize(input.SuperCellMultiplier.determinant());
for (int i = 0; i < input.SuperCellMultiplier(0, 0); i++)
for (int j = 0; j < input.SuperCellMultiplier(1, 1); j++)
for (int k = 0; k < input.SuperCellMultiplier(2, 2); k++)
{
// 反折叠后的某个 q 点 对应的所有的基矢
auto& basis_at_unfolded_qpoint
= basis[i * input.SuperCellMultiplier(1, 1) * input.SuperCellMultiplier(2, 2)
+ j * input.SuperCellMultiplier(2, 2) + k];
basis_at_unfolded_qpoint.resize(input.PrimativeCellBasisNumber.prod());
for (int x = 0; x < input.PrimativeCellBasisNumber(0); x++)
for (int y = 0; y < input.PrimativeCellBasisNumber(1); y++)
for (int z = 0; z < input.PrimativeCellBasisNumber(2); z++)
{
// 计算 q 点的坐标, 单位为相对于超胞的倒格矢
auto qpoint_relative_to_super_cell =
Eigen::Vector3i({{i}, {j}, {k}})
+ input.SuperCellMultiplier * Eigen::Vector3i({{x}, {y}, {z}});
// 将 q 点坐标转换为埃^-1
auto qpoint = (qpoint_relative_to_super_cell.transpose().cast<double>()
* (input.SuperCellMultiplier.cast<double>().inverse().transpose())).transpose();
// 计算基矢
auto& single_basis = basis_at_unfolded_qpoint
[x * input.PrimativeCellBasisNumber(1) * input.PrimativeCellBasisNumber(2)
+ y * input.PrimativeCellBasisNumber(2) + z];
single_basis = (-2 * std::numbers::pi_v<double> * 1i * (input.AtomPosition * qpoint)).array().exp();
}
}
// 计算投影的结果
// 最外层下标对应反折叠前的 q 点, 第二层下标对应不同模式, 第三层下标对应这个模式在反折叠后的 q 点
std::vector<std::vector<std::vector<double>>> projection_coefficient;
projection_coefficient.resize(input.QPointData.size());
for (int i_of_folded_qpoint = 0; i_of_folded_qpoint < input.QPointData.size(); i_of_folded_qpoint++)
{
projection_coefficient[i_of_folded_qpoint].resize(input.QPointData[i_of_folded_qpoint].ModeData.size());
for (int i_of_mode = 0; i_of_mode < projection_coefficient[i_of_folded_qpoint].size(); i_of_mode++)
{
auto& coefficient_at_mode = projection_coefficient[i_of_folded_qpoint][i_of_mode];
coefficient_at_mode.resize(input.SuperCellMultiplier.determinant());
for
(int i_of_unfolded_qpoint = 0; i_of_unfolded_qpoint < coefficient_at_mode.size(); i_of_unfolded_qpoint++)
// 对于 basis 中, 对应于单胞倒格子的部分, 以及对应于不同方向的部分, 分别求内积, 然后求绝对值, 然后求和
for
(
int i_of_basis_in_primary_cell = 0;
i_of_basis_in_primary_cell < basis[i_of_unfolded_qpoint].size();
i_of_basis_in_primary_cell++
)
coefficient_at_mode[i_of_unfolded_qpoint] +=
std::sqrt((
basis[i_of_unfolded_qpoint][i_of_basis_in_primary_cell].transpose()
* input.QPointData[i_of_folded_qpoint].ModeData[i_of_mode].AtomMovement
).array().abs2().sum());
}
}
// 填充输出对象
Output output;
output.QPointData.reserve(input.QPointData.size() * input.SuperCellMultiplier.determinant());
for (int i_of_folded_qpoint = 0; i_of_folded_qpoint < input.QPointData.size(); i_of_folded_qpoint++)
// for each unfolded q point corresponding to this folded q point
for (int x = 0; x < input.SuperCellMultiplier(0, 0); x++)
for (int y = 0; y < input.SuperCellMultiplier(1, 1); y++)
for (int z = 0; z < input.SuperCellMultiplier(2, 2); z++)
{
auto& unfolded_qpoint = output.QPointData.emplace_back();
unfolded_qpoint.QPoint = input.SuperCellMultiplier.cast<double>().inverse() *
(input.QPointData[i_of_folded_qpoint].QPoint
+ Eigen::Vector3i({{x}, {y}, {z}}).cast<double>());
for (int i_of_mode = 0; i_of_mode < input.QPointData[i_of_folded_qpoint].ModeData.size(); i_of_mode++)
{
auto& mode = unfolded_qpoint.ModeData.emplace_back();
mode.Frequency = input.QPointData[i_of_folded_qpoint].ModeData[i_of_mode].Frequency;
mode.Weight = projection_coefficient[i_of_folded_qpoint][i_of_mode]
[x * input.SuperCellMultiplier(1, 1) * input.SuperCellMultiplier(2, 2)
+ y * input.SuperCellMultiplier(2, 2) + z];
}
}
output.write(argv[2]);
}
Input::Input(YAML::Node root)
{
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
PrimativeCell(i, j) = root["lattice"][i][j].as<double>();
SuperCellMultiplier.setZero();
for (int i = 0; i < 3; i++)
SuperCellMultiplier(i, i) = root["SuperCellMultiplier"][i].as<int>();
for (int i = 0; i < 3; i++)
PrimativeCellBasisNumber(i) = root["PrimativeCellBasisNumber"][i].as<int>();
auto points = root["points"].as<std::vector<YAML::Node>>();
AtomPosition.resize(points.size(), 3);
for (int i = 0; i < points.size(); i++)
for (int j = 0; j < 3; j++)
AtomPosition(i, j) = points[i]["coordinates"][j].as<double>();
auto phonon = root["phonon"].as<std::vector<YAML::Node>>();
QPointData.resize(phonon.size());
for (int i = 0; i < phonon.size(); i++)
{
QPointData[i].QPoint.resize(3);
for (int j = 0; j < 3; j++)
QPointData[i].QPoint(j) = phonon[i]["q-position"][j].as<double>();
auto band = phonon[i]["band"].as<std::vector<YAML::Node>>();
QPointData[i].ModeData.resize(band.size());
for (int j = 0; j < band.size(); j++)
{
QPointData[i].ModeData[j].Frequency = band[j]["frequency"].as<double>();
QPointData[i].ModeData[j].AtomMovement.resize(AtomPosition.rows(), 3);
auto eigenvector = band[j]["eigenvector"]
.as<std::vector<std::vector<std::vector<double>>>>();
for (int k = 0; k < AtomPosition.rows(); k++)
for (int l = 0; l < 3; l++)
QPointData[i].ModeData[j].AtomMovement(k, l) = eigenvector[k][l][0] + 1i * eigenvector[k][l][1];
// 需要对读入的原子运动状态作相位转换, 使得它们与我们的约定一致(对超胞周期性重复)
for (int k = 0; k < AtomPosition.rows(); k++)
QPointData[i].ModeData[j].AtomMovement.row(k) *=
std::exp(-2 * std::numbers::pi_v<double> * 1i * (QPointData[i].QPoint.dot(AtomPosition.row(k))));
}
}
}
void Output::write(std::string filename) const
{
YAML::Node root;
root["QPointData"] = YAML::Node(YAML::NodeType::Sequence);
for (int i = 0; i < QPointData.size(); i++)
{
root["QPointData"][i]["QPoint"] = YAML::Node(YAML::NodeType::Sequence);
for (int j = 0; j < 3; j++)
root["QPointData"][i]["QPoint"][j] = QPointData[i].QPoint(j);
root["QPointData"][i]["ModeData"] = YAML::Node(YAML::NodeType::Sequence);
for (int j = 0; j < QPointData[i].ModeData.size(); j++)
{
root["QPointData"][i]["ModeData"][j]["Frequency"] = QPointData[i].ModeData[j].Frequency;
root["QPointData"][i]["ModeData"][j]["Weight"] = QPointData[i].ModeData[j].Weight;
}
}
std::ofstream(filename) << root;
}