mirror of
https://github.com/CHN-beta/ufo.git
synced 2024-10-22 19:58:44 +08:00
整理构建逻辑
This commit is contained in:
parent
a8987bcbfd
commit
ed60d9ab05
@ -22,7 +22,7 @@ find_package(TBB REQUIRED)
|
|||||||
find_package(Matplot++ REQUIRED)
|
find_package(Matplot++ REQUIRED)
|
||||||
find_path(ZPP_BITS_INCLUDE_DIR zpp_bits.h REQUIRED)
|
find_path(ZPP_BITS_INCLUDE_DIR zpp_bits.h REQUIRED)
|
||||||
|
|
||||||
add_executable(ufo src/ufo.cpp)
|
add_executable(ufo src/unfold.cpp src/main.cpp)
|
||||||
target_include_directories(ufo PRIVATE ${PROJECT_SOURCE_DIR}/include ${ZPP_BITS_INCLUDE_DIR})
|
target_include_directories(ufo PRIVATE ${PROJECT_SOURCE_DIR}/include ${ZPP_BITS_INCLUDE_DIR})
|
||||||
target_link_libraries(ufo PRIVATE
|
target_link_libraries(ufo PRIVATE
|
||||||
yaml-cpp Eigen3::Eigen fmt::fmt concurrencpp::concurrencpp HighFive_HighFive TBB::tbb Matplot++::matplot)
|
yaml-cpp Eigen3::Eigen fmt::fmt concurrencpp::concurrencpp HighFive_HighFive TBB::tbb Matplot++::matplot)
|
||||||
|
70
include/ufo/solver.hpp
Normal file
70
include/ufo/solver.hpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# pragma once
|
||||||
|
# include <iostream>
|
||||||
|
# include <array>
|
||||||
|
# include <numbers>
|
||||||
|
# include <numeric>
|
||||||
|
# include <fstream>
|
||||||
|
# include <optional>
|
||||||
|
# include <array>
|
||||||
|
# include <utility>
|
||||||
|
# include <execution>
|
||||||
|
# include <syncstream>
|
||||||
|
# include <any>
|
||||||
|
# include <map>
|
||||||
|
# include <vector>
|
||||||
|
# include <span>
|
||||||
|
# include <yaml-cpp/yaml.h>
|
||||||
|
# include <Eigen/Dense>
|
||||||
|
# include <concurrencpp/concurrencpp.h>
|
||||||
|
# include <fmt/format.h>
|
||||||
|
# include <fmt/std.h>
|
||||||
|
# include <fmt/ranges.h>
|
||||||
|
# include <highfive/H5File.hpp>
|
||||||
|
# include <zpp_bits.h>
|
||||||
|
# include <matplot/matplot.h>
|
||||||
|
|
||||||
|
// 在相位中, 约定为使用 $\exp (2 \pi i \vec{q} \cdot \vec{r})$ 来表示原子的运动状态
|
||||||
|
// (而不是 $\exp (-2 \pi i \vec{q} \cdot \vec{r})$)
|
||||||
|
// 一些书定义的倒格矢中包含了 $2 \pi$ 的部分, 我们这里约定不包含这部分.
|
||||||
|
// 也就是说, 正格子与倒格子的转置相乘, 得到单位矩阵.
|
||||||
|
|
||||||
|
namespace Eigen
|
||||||
|
{
|
||||||
|
constexpr inline auto serialize(auto & archive, Eigen::Matrix3d& matrix)
|
||||||
|
{ return archive(std::span(matrix.data(), matrix.size())); }
|
||||||
|
constexpr inline auto serialize(auto & archive, const Eigen::Matrix3d& matrix)
|
||||||
|
{ return archive(std::span(matrix.data(), matrix.size())); }
|
||||||
|
constexpr inline auto serialize(auto & archive, Eigen::Vector3d& vector)
|
||||||
|
{ return archive(std::span(vector.data(), vector.size())); }
|
||||||
|
constexpr inline auto serialize(auto & archive, const Eigen::Vector3d& vector)
|
||||||
|
{ return archive(std::span(vector.data(), vector.size())); }
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ufo
|
||||||
|
{
|
||||||
|
using namespace std::literals;
|
||||||
|
struct PhonopyComplex { double r, i; };
|
||||||
|
inline HighFive::CompoundType create_compound_complex()
|
||||||
|
{ return {{"r", HighFive::AtomicType<double>{}}, {"i", HighFive::AtomicType<double>{}}}; }
|
||||||
|
class Solver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual Solver& operator()() = 0;
|
||||||
|
~Solver() = default;
|
||||||
|
|
||||||
|
inline static concurrencpp::generator<std::pair<Eigen::Vector<unsigned, 3>, unsigned>>
|
||||||
|
triplet_sequence(Eigen::Vector<unsigned, 3> range)
|
||||||
|
{
|
||||||
|
for (unsigned x = 0; x < range[0]; x++)
|
||||||
|
for (unsigned y = 0; y < range[1]; y++)
|
||||||
|
for (unsigned z = 0; z < range[2]; z++)
|
||||||
|
co_yield
|
||||||
|
{
|
||||||
|
Eigen::Vector<unsigned, 3>{{x}, {y}, {z}},
|
||||||
|
x * range[1] * range[2] + y * range[2] + z
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
HIGHFIVE_REGISTER_TYPE(ufo::PhonopyComplex, ufo::create_compound_complex)
|
@ -1,155 +0,0 @@
|
|||||||
# include <iostream>
|
|
||||||
# include <array>
|
|
||||||
# include <numbers>
|
|
||||||
# include <numeric>
|
|
||||||
# include <fstream>
|
|
||||||
# include <optional>
|
|
||||||
# include <array>
|
|
||||||
# include <utility>
|
|
||||||
# include <execution>
|
|
||||||
# include <syncstream>
|
|
||||||
# include <any>
|
|
||||||
# include <map>
|
|
||||||
# include <vector>
|
|
||||||
# include <span>
|
|
||||||
# include <yaml-cpp/yaml.h>
|
|
||||||
# include <Eigen/Dense>
|
|
||||||
# include <concurrencpp/concurrencpp.h>
|
|
||||||
# include <fmt/format.h>
|
|
||||||
# include <fmt/std.h>
|
|
||||||
# include <fmt/ranges.h>
|
|
||||||
# include <highfive/H5File.hpp>
|
|
||||||
# include <zpp_bits.h>
|
|
||||||
# include <matplot/matplot.h>
|
|
||||||
|
|
||||||
using namespace std::literals;
|
|
||||||
|
|
||||||
struct PhonopyComplex { double r, i; };
|
|
||||||
HighFive::CompoundType create_compound_complex();
|
|
||||||
HIGHFIVE_REGISTER_TYPE(PhonopyComplex, create_compound_complex)
|
|
||||||
|
|
||||||
namespace Eigen
|
|
||||||
{
|
|
||||||
constexpr inline auto serialize(auto & archive, Eigen::Matrix3d& matrix)
|
|
||||||
{ return archive(std::span(matrix.data(), matrix.size())); }
|
|
||||||
constexpr inline auto serialize(auto & archive, const Eigen::Matrix3d& matrix)
|
|
||||||
{ return archive(std::span(matrix.data(), matrix.size())); }
|
|
||||||
constexpr inline auto serialize(auto & archive, Eigen::Vector3d& vector)
|
|
||||||
{ return archive(std::span(vector.data(), vector.size())); }
|
|
||||||
constexpr inline auto serialize(auto & archive, const Eigen::Vector3d& vector)
|
|
||||||
{ return archive(std::span(vector.data(), vector.size())); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在相位中, 约定为使用 $\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;
|
|
||||||
// 单胞到超胞的格矢转换时用到的矩阵
|
|
||||||
// SuperCellMultiplier 是一个三维列向量且各个元素都是整数,表示单胞在各个方向扩大到多少倍之后,可以得到和超胞一样的体积
|
|
||||||
// SuperCellDeformation 是一个行列式为 1 的矩阵,它表示经过 SuperCellMultiplier 扩大后,还需要怎样的变换才能得到超胞
|
|
||||||
// SuperCell = (SuperCellDeformation * SuperCellMultiplier.asDiagonal()) * PrimativeCell
|
|
||||||
// ReciprocalPrimativeCell = (SuperCellDeformation * SuperCellMultiplier.asDiagonal()).transpose()
|
|
||||||
// * ReciprocalSuperCell
|
|
||||||
// Position = PositionToCell(line vector) * Cell
|
|
||||||
// InversePosition = InversePositionToCell(line vector) * ReciprocalCell
|
|
||||||
// PositionToSuperCell(line vector) * SuperCell = PositionToPrimativeCell(line vector) * PrimativeCell
|
|
||||||
// ReciprocalPositionToSuperCell(line vector) * ReciprocalSuperCell
|
|
||||||
// = ReciprocalPositionToPrimativeCell(line vector) * ReciprocalPrimativeCell
|
|
||||||
Eigen::Vector<unsigned, 3> SuperCellMultiplier;
|
|
||||||
std::optional<Eigen::Matrix<double, 3, 3>> SuperCellDeformation;
|
|
||||||
// 在单胞内取几个平面波的基矢
|
|
||||||
Eigen::Vector<unsigned, 3> PrimativeCellBasisNumber;
|
|
||||||
|
|
||||||
struct InputOutputFile_
|
|
||||||
{
|
|
||||||
std::string FileName;
|
|
||||||
std::string Format;
|
|
||||||
std::map<std::string, std::any> ExtraParameters;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 从哪个文件读入 AtomPosition, 以及这个文件的格式, 格式可选值包括 "yaml"
|
|
||||||
InputOutputFile_ AtomPositionInputFile;
|
|
||||||
// 从哪个文件读入 QPointData, 以及这个文件的格式, 格式可选值包括 "yaml" 和 "hdf5"
|
|
||||||
InputOutputFile_ QPointDataInputFile;
|
|
||||||
|
|
||||||
// 超胞中原子的坐标,每行表示一个原子的坐标,单位为埃
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 输出到哪些文件, 以及使用怎样的格式, 格式可选值包括:
|
|
||||||
// yaml: 使用 yaml 格式输出
|
|
||||||
// yaml-human-readable: 使用 yaml 格式输出, 但是输出的结果更适合人类阅读, 包括合并相近的模式, 去除权重过小的模式, 限制输出的小数位数.
|
|
||||||
// zpp: 使用 zpp-bits 序列化, 可以直接被 plot.cpp 读取
|
|
||||||
std::vector<InputOutputFile_> QPointDataOutputFile;
|
|
||||||
|
|
||||||
// 从文件中读取输入 (包括一个较小的配置文件, 和一个 hdf5 或者一个 yaml 文件), 文件中应当包含:
|
|
||||||
// 单胞的格矢: PrimativeCell 单位为埃 直接从 phonopy 的输出中复制
|
|
||||||
// 超胞的倍数: SuperCellMultiplier 手动输入, 为一个包含三个整数的数组
|
|
||||||
// 超胞的变形: SuperCellDeformation 手动输入, 为一个三阶方阵
|
|
||||||
// 平面波的基矢个数: PrimativeCellBasisNumber 手动输入, 为一个包含三个整数的数组
|
|
||||||
// 另外还有一个文件, 直接将 phonopy 的输出复制过来即可, 如果是 yaml, 应该包含下面的内容:
|
|
||||||
// 超胞中原子的坐标: points[*].coordinates 单位为超胞的格矢 直接从 phonopy 的输出中复制
|
|
||||||
// 各个 Q 点的坐标: phonon[*].q-position 单位为超胞的倒格子的格矢 直接从 phonopy 的输出中复制
|
|
||||||
// 各个模式的频率: phonon[*].band[*].frequency 单位为 THz 直接从 phonopy 的输出中复制
|
|
||||||
// 各个模式的原子运动状态: phonon[*].band[*].eigenvector 直接从 phonopy 的输出中复制
|
|
||||||
// 文件中可以有多余的项目, 多余的项目不管.
|
|
||||||
Input(std::string filename);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Output
|
|
||||||
{
|
|
||||||
// 关于各个 Q 点的数据
|
|
||||||
struct QPointDataType_
|
|
||||||
{
|
|
||||||
// Q 点的坐标,单位为单胞的倒格矢
|
|
||||||
Eigen::Vector3d QPoint;
|
|
||||||
|
|
||||||
// 来源于哪个 Q 点, 单位为超胞的倒格矢
|
|
||||||
Eigen::Vector3d Source;
|
|
||||||
std::size_t SourceIndex_;
|
|
||||||
|
|
||||||
// 关于这个 Q 点上各个模式的数据
|
|
||||||
struct ModeDataType_
|
|
||||||
{
|
|
||||||
// 模式的频率,单位为 THz
|
|
||||||
double Frequency;
|
|
||||||
// 模式的权重
|
|
||||||
double Weight;
|
|
||||||
};
|
|
||||||
std::vector<ModeDataType_> ModeData;
|
|
||||||
};
|
|
||||||
std::vector<QPointDataType_> QPointData;
|
|
||||||
|
|
||||||
void write(std::string filename, std::string format, unsigned percision = 10) const;
|
|
||||||
Output() = default;
|
|
||||||
Output(std::string filename);
|
|
||||||
|
|
||||||
using serialize = zpp::bits::members<1>;
|
|
||||||
};
|
|
||||||
|
|
||||||
concurrencpp::generator<std::pair<Eigen::Vector<unsigned, 3>, unsigned>>
|
|
||||||
triplet_sequence(Eigen::Vector<unsigned, 3> range);
|
|
@ -1,268 +0,0 @@
|
|||||||
# include <ufo/ufo.hpp>
|
|
||||||
|
|
||||||
inline HighFive::CompoundType create_compound_complex()
|
|
||||||
{ return {{"r", HighFive::AtomicType<double>{}}, {"i", HighFive::AtomicType<double>{}}}; }
|
|
||||||
|
|
||||||
inline Input::Input(std::string filename)
|
|
||||||
{
|
|
||||||
// read main input file
|
|
||||||
{
|
|
||||||
auto node = YAML::LoadFile(filename);
|
|
||||||
for (unsigned i = 0; i < 3; i++)
|
|
||||||
for (unsigned j = 0; j < 3; j++)
|
|
||||||
PrimativeCell(i, j) = node["PrimativeCell"][i][j].as<double>();
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < 3; i++)
|
|
||||||
SuperCellMultiplier(i) = node["SuperCellMultiplier"][i].as<int>();
|
|
||||||
|
|
||||||
if (auto value = node["SuperCellDeformation"])
|
|
||||||
{
|
|
||||||
SuperCellDeformation.emplace();
|
|
||||||
for (unsigned i = 0; i < 3; i++)
|
|
||||||
for (unsigned j = 0; j < 3; j++)
|
|
||||||
(*SuperCellDeformation)(i, j) = value[i][j].as<double>();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < 3; i++)
|
|
||||||
PrimativeCellBasisNumber(i) = node["PrimativeCellBasisNumber"][i].as<int>();
|
|
||||||
|
|
||||||
auto read_file_config = [filename](YAML::Node source, InputOutputFile_& config)
|
|
||||||
{
|
|
||||||
if (auto _ = source["SameAsConfigFile"])
|
|
||||||
{
|
|
||||||
auto __ = _.as<bool>();
|
|
||||||
config.ExtraParameters["SameAsConfigFile"] = __;
|
|
||||||
if (__)
|
|
||||||
{
|
|
||||||
config.FileName = filename;
|
|
||||||
config.Format = "yaml";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config.FileName = source["FileName"].as<std::string>();
|
|
||||||
config.Format = source["Format"].as<std::string>();
|
|
||||||
if (auto _ = source["RelativeToConfigFile"])
|
|
||||||
{
|
|
||||||
auto __ = _.as<bool>();
|
|
||||||
config.ExtraParameters["RelativeToConfigFile"] = __;
|
|
||||||
if (__)
|
|
||||||
config.FileName = std::filesystem::path(filename).parent_path() / config.FileName;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
read_file_config(node["AtomPositionInputFile"], AtomPositionInputFile);
|
|
||||||
if (!std::set<std::string>{"yaml"}.contains(AtomPositionInputFile.Format))
|
|
||||||
throw std::runtime_error(fmt::format
|
|
||||||
("Unknown AtomPositionInputFile.Format: {}, should be \"yaml\".", AtomPositionInputFile.Format));
|
|
||||||
read_file_config(node["QPointDataInputFile"], QPointDataInputFile);
|
|
||||||
if (!std::set<std::string>{"yaml", "hdf5"}.contains(QPointDataInputFile.Format))
|
|
||||||
throw std::runtime_error(fmt::format
|
|
||||||
("Unknown QPointDataInputFile.Format: {}, should be \"yaml\" or \"hdf5\".", QPointDataInputFile.Format));
|
|
||||||
if (auto value = node["QPointDataOutputFile"])
|
|
||||||
{
|
|
||||||
QPointDataOutputFile.resize(value.size());
|
|
||||||
for (unsigned i = 0; i < value.size(); i++)
|
|
||||||
{
|
|
||||||
read_file_config(value[i], QPointDataOutputFile[i]);
|
|
||||||
if
|
|
||||||
(
|
|
||||||
QPointDataOutputFile[i].ExtraParameters.contains("SameAsConfigFile")
|
|
||||||
&& std::any_cast<bool>(QPointDataOutputFile[i].ExtraParameters["SameAsConfigFile"])
|
|
||||||
)
|
|
||||||
throw std::runtime_error("QPointDataOutputFile.SameAsConfigFile should not be set.");
|
|
||||||
if
|
|
||||||
(
|
|
||||||
!std::set<std::string>{"yaml", "yaml-human-readable", "zpp"}
|
|
||||||
.contains(QPointDataOutputFile[i].Format)
|
|
||||||
)
|
|
||||||
throw std::runtime_error(fmt::format
|
|
||||||
(
|
|
||||||
"Unknown QPointDataOutputFile[{}].Format: {}, should be \"yaml\", \"yaml-human-readable\" or \"zpp\".",
|
|
||||||
i, QPointDataOutputFile[i].Format
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AtomPositionInputFile.Format == "yaml")
|
|
||||||
{
|
|
||||||
auto node = YAML::LoadFile(AtomPositionInputFile.FileName);
|
|
||||||
std::vector<YAML::Node> points;
|
|
||||||
if (auto _ = node["points"])
|
|
||||||
points = _.as<std::vector<YAML::Node>>();
|
|
||||||
else
|
|
||||||
points = node["unit_cell"]["points"].as<std::vector<YAML::Node>>();
|
|
||||||
auto atom_position_to_super_cell = Eigen::MatrixX3d(points.size(), 3);
|
|
||||||
for (unsigned i = 0; i < points.size(); i++)
|
|
||||||
for (unsigned j = 0; j < 3; j++)
|
|
||||||
atom_position_to_super_cell(i, j) = points[i]["coordinates"][j].as<double>();
|
|
||||||
auto super_cell = (SuperCellDeformation.value_or(Eigen::Matrix3d::Identity())
|
|
||||||
* SuperCellMultiplier.cast<double>().asDiagonal() * PrimativeCell).eval();
|
|
||||||
AtomPosition = atom_position_to_super_cell * super_cell;
|
|
||||||
}
|
|
||||||
if (QPointDataInputFile.Format == "yaml")
|
|
||||||
{
|
|
||||||
auto node = YAML::LoadFile(QPointDataInputFile.FileName);
|
|
||||||
auto phonon = node["phonon"].as<std::vector<YAML::Node>>();
|
|
||||||
QPointData.resize(phonon.size());
|
|
||||||
for (unsigned i = 0; i < phonon.size(); i++)
|
|
||||||
{
|
|
||||||
for (unsigned 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 (unsigned j = 0; j < band.size(); j++)
|
|
||||||
{
|
|
||||||
QPointData[i].ModeData[j].Frequency = band[j]["frequency"].as<double>();
|
|
||||||
auto eigenvector_vectors = band[j]["eigenvector"]
|
|
||||||
.as<std::vector<std::vector<std::vector<double>>>>();
|
|
||||||
Eigen::MatrixX3cd eigenvectors(AtomPosition.rows(), 3);
|
|
||||||
for (unsigned k = 0; k < AtomPosition.rows(); k++)
|
|
||||||
for (unsigned l = 0; l < 3; l++)
|
|
||||||
eigenvectors(k, l)
|
|
||||||
= eigenvector_vectors[k][l][0] + 1i * eigenvector_vectors[k][l][1];
|
|
||||||
// 需要对读入的原子运动状态作相位转换, 使得它们与我们的约定一致(对超胞周期性重复)
|
|
||||||
// 这里还要需要做归一化处理 (指将数据简单地作为向量处理的归一化)
|
|
||||||
auto& AtomMovement = QPointData[i].ModeData[j].AtomMovement;
|
|
||||||
// AtomMovement = eigenvectors.array().colwise() * (-2 * std::numbers::pi_v<double> * 1i
|
|
||||||
// * (atom_position_to_super_cell * input.QPointData[i].QPoint)).array().exp();
|
|
||||||
// AtomMovement /= AtomMovement.norm();
|
|
||||||
// phonopy 似乎已经进行了相位的转换!为什么?
|
|
||||||
AtomMovement = eigenvectors / eigenvectors.norm();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (QPointDataInputFile.Format == "hdf5")
|
|
||||||
{
|
|
||||||
HighFive::File file(QPointDataInputFile.FileName, HighFive::File::ReadOnly);
|
|
||||||
auto size = file.getDataSet("/frequency").getDimensions();
|
|
||||||
auto frequency = file.getDataSet("/frequency")
|
|
||||||
.read<std::vector<std::vector<std::vector<double>>>>();
|
|
||||||
auto eigenvector_vector = file.getDataSet("/eigenvector")
|
|
||||||
.read<std::vector<std::vector<std::vector<std::vector<PhonopyComplex>>>>>();
|
|
||||||
auto path = file.getDataSet("/path")
|
|
||||||
.read<std::vector<std::vector<std::vector<double>>>>();
|
|
||||||
QPointData.resize(size[0] * size[1]);
|
|
||||||
for (unsigned i = 0; i < size[0]; i++)
|
|
||||||
for (unsigned j = 0; j < size[1]; j++)
|
|
||||||
{
|
|
||||||
QPointData[i * size[1] + j].QPoint = Eigen::Vector3d(path[i][j].data());
|
|
||||||
QPointData[i * size[1] + j].ModeData.resize(size[2]);
|
|
||||||
for (unsigned k = 0; k < size[2]; k++)
|
|
||||||
{
|
|
||||||
QPointData[i * size[1] + j].ModeData[k].Frequency = frequency[i][j][k];
|
|
||||||
Eigen::MatrixX3cd eigenvectors(AtomPosition.rows(), 3);
|
|
||||||
for (unsigned l = 0; l < AtomPosition.rows(); l++)
|
|
||||||
for (unsigned m = 0; m < 3; m++)
|
|
||||||
eigenvectors(l, m)
|
|
||||||
= eigenvector_vector[i][j][l * 3 + m][k].r + eigenvector_vector[i][j][l * 3 + m][k].i * 1i;
|
|
||||||
QPointData[i * size[1] + j].ModeData[k].AtomMovement = eigenvectors / eigenvectors.norm();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Output::write(std::string filename, std::string format, unsigned percision) const
|
|
||||||
{
|
|
||||||
if (format == "yaml")
|
|
||||||
std::ofstream(filename) << [&]
|
|
||||||
{
|
|
||||||
std::stringstream print;
|
|
||||||
print << "QPointData:\n";
|
|
||||||
for (auto& qpoint: QPointData)
|
|
||||||
{
|
|
||||||
print << fmt::format(" - QPoint: [ {1:.{0}f}, {2:.{0}f}, {3:.{0}f} ]\n",
|
|
||||||
percision, qpoint.QPoint[0], qpoint.QPoint[1], qpoint.QPoint[2]);
|
|
||||||
print << fmt::format(" Source: [ {1:.{0}f}, {2:.{0}f}, {3:.{0}f} ]\n",
|
|
||||||
percision, qpoint.Source[0], qpoint.Source[1], qpoint.Source[2]);
|
|
||||||
print << " ModeData:\n";
|
|
||||||
for (auto& mode: qpoint.ModeData)
|
|
||||||
print << fmt::format(" - {{ Frequency: {1:.{0}f}, Weight: {2:.{0}f} }}\n",
|
|
||||||
percision, mode.Frequency, mode.Weight);
|
|
||||||
}
|
|
||||||
return print.str();
|
|
||||||
}();
|
|
||||||
else if (format == "yaml-human-readable")
|
|
||||||
{
|
|
||||||
Output output;
|
|
||||||
std::map<unsigned, std::vector<decltype(QPointData)::const_iterator>>
|
|
||||||
meta_qpoint_to_sub_qpoint_iterators;
|
|
||||||
for (auto it = QPointData.begin(); it != QPointData.end(); it++)
|
|
||||||
meta_qpoint_to_sub_qpoint_iterators[it->SourceIndex_].push_back(it);
|
|
||||||
for (auto [meta_qpoint_index, sub_qpoint_iterators] : meta_qpoint_to_sub_qpoint_iterators)
|
|
||||||
for (auto& qpoint : sub_qpoint_iterators)
|
|
||||||
{
|
|
||||||
std::map<double, double> frequency_to_weight;
|
|
||||||
for (unsigned i_of_mode = 0; i_of_mode < qpoint->ModeData.size(); i_of_mode++)
|
|
||||||
{
|
|
||||||
auto frequency = qpoint->ModeData[i_of_mode].Frequency;
|
|
||||||
auto weight = qpoint->ModeData[i_of_mode].Weight;
|
|
||||||
auto it_lower = frequency_to_weight.lower_bound(frequency - 0.1);
|
|
||||||
auto it_upper = frequency_to_weight.upper_bound(frequency + 0.1);
|
|
||||||
if (it_lower == it_upper)
|
|
||||||
frequency_to_weight[frequency] = weight;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto frequency_sum = std::accumulate(it_lower, it_upper, 0.,
|
|
||||||
[](const auto& a, const auto& b) { return a + b.first * b.second; });
|
|
||||||
auto weight_sum = std::accumulate(it_lower, it_upper, 0.,
|
|
||||||
[](const auto& a, const auto& b) { return a + b.second; });
|
|
||||||
frequency_sum += frequency * weight;
|
|
||||||
weight_sum += weight;
|
|
||||||
frequency_to_weight.erase(it_lower, it_upper);
|
|
||||||
frequency_to_weight[frequency_sum / weight_sum] = weight_sum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto& _ = output.QPointData.emplace_back();
|
|
||||||
_.QPoint = qpoint->QPoint;
|
|
||||||
_.Source = qpoint->Source;
|
|
||||||
_.SourceIndex_ = qpoint->SourceIndex_;
|
|
||||||
for (auto [frequency, weight] : frequency_to_weight)
|
|
||||||
if (weight > 0.1)
|
|
||||||
{
|
|
||||||
auto& __ = _.ModeData.emplace_back();
|
|
||||||
__.Frequency = frequency;
|
|
||||||
__.Weight = weight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output.write(filename, "yaml", 3);
|
|
||||||
}
|
|
||||||
else if (format == "zpp")
|
|
||||||
{
|
|
||||||
auto [data, out] = zpp::bits::data_out();
|
|
||||||
out(*this).or_throw();
|
|
||||||
static_assert(sizeof(char) == sizeof(std::byte));
|
|
||||||
std::ofstream file(filename, std::ios::binary | std::ios::out);
|
|
||||||
file.exceptions(std::ios::badbit | std::ios::failbit);
|
|
||||||
file.write(reinterpret_cast<const char*>(data.data()), data.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Output::Output(std::string filename)
|
|
||||||
{
|
|
||||||
auto input = std::ifstream(filename, std::ios::binary | std::ios::in);
|
|
||||||
input.exceptions(std::ios::badbit | std::ios::failbit);
|
|
||||||
std::vector<std::byte> data;
|
|
||||||
{
|
|
||||||
std::vector<char> string(std::istreambuf_iterator<char>(input), {});
|
|
||||||
data.assign
|
|
||||||
(
|
|
||||||
reinterpret_cast<std::byte*>(string.data()),
|
|
||||||
reinterpret_cast<std::byte*>(string.data() + string.size())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
auto in = zpp::bits::in(data);
|
|
||||||
in(*this).or_throw();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline concurrencpp::generator<std::pair<Eigen::Vector<unsigned, 3>, unsigned>>
|
|
||||||
triplet_sequence(Eigen::Vector<unsigned, 3> range)
|
|
||||||
{
|
|
||||||
for (unsigned x = 0; x < range[0]; x++)
|
|
||||||
for (unsigned y = 0; y < range[1]; y++)
|
|
||||||
for (unsigned z = 0; z < range[2]; z++)
|
|
||||||
co_yield
|
|
||||||
{
|
|
||||||
Eigen::Vector<unsigned, 3>{{x}, {y}, {z}},
|
|
||||||
x * range[1] * range[2] + y * range[2] + z
|
|
||||||
};
|
|
||||||
}
|
|
172
include/ufo/unfold.hpp
Normal file
172
include/ufo/unfold.hpp
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
# pragma once
|
||||||
|
# include <ufo/solver.hpp>
|
||||||
|
|
||||||
|
namespace ufo
|
||||||
|
{
|
||||||
|
// 反折叠的原理: 将超胞中的原子运动状态, 投影到一组平面波构成的基矢中.
|
||||||
|
// 每一个平面波的波矢由两部分相加得到: 一部分是单胞倒格子的整数倍, 所取的个数有一定任意性, 论文中建议取大约单胞中原子个数那么多个;
|
||||||
|
// 对于没有缺陷的情况, 取一个应该就足够了.
|
||||||
|
// 另一部分是超胞倒格子的整数倍, 取 n 个, n 为超胞对应的单胞的倍数, 其实也就是倒空间中单胞对应倒格子中超胞的格点.
|
||||||
|
// 只要第一部分取得足够多, 那么单胞中原子的状态就可以完全被这些平面波描述.
|
||||||
|
// 将超胞中原子的运动状态投影到这些基矢上, 计算出投影的系数, 就可以将超胞的原子运动状态分解到单胞中的多个 q 点上.
|
||||||
|
class UnfoldSolver : public Solver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct InputType
|
||||||
|
{
|
||||||
|
// 单胞的三个格矢,每行表示一个格矢的坐标,单位为埃
|
||||||
|
Eigen::Matrix3d PrimativeCell;
|
||||||
|
// 单胞到超胞的格矢转换时用到的矩阵
|
||||||
|
// SuperCellMultiplier 是一个三维列向量且各个元素都是整数,表示单胞在各个方向扩大到多少倍之后,可以得到和超胞一样的体积
|
||||||
|
// SuperCsolver.hpp>mation 是一个行列式为 1 的矩阵,它表示经过 SuperCellMultiplier 扩大后,还需要怎样的变换才能得到超胞
|
||||||
|
// SuperCell = (SuperCellDeformation * SuperCellMultiplier.asDiagonal()) * PrimativeCell
|
||||||
|
// ReciprocalPrimativeCell = (SuperCellDeformation * SuperCellMultiplier.asDiagonal()).transpose()
|
||||||
|
// * ReciprocalSuperCell
|
||||||
|
// Position = PositionToCell(line vector) * Cell
|
||||||
|
// InversePosition = InversePositionToCell(line vector) * ReciprocalCell
|
||||||
|
// PositionToSuperCell(line vector) * SuperCell = PositionToPrimativeCell(line vector) * PrimativeCell
|
||||||
|
// ReciprocalPositionToSuperCell(line vector) * ReciprocalSuperCell
|
||||||
|
// = ReciprocalPositionToPrimativeCell(line vector) * ReciprocalPrimativeCell
|
||||||
|
Eigen::Vector<unsigned, 3> SuperCellMultiplier;
|
||||||
|
std::optional<Eigen::Matrix<double, 3, 3>> SuperCellDeformation;
|
||||||
|
// 在单胞内取几个平面波的基矢
|
||||||
|
Eigen::Vector<unsigned, 3> PrimativeCellBasisNumber;
|
||||||
|
|
||||||
|
struct InputOutputFile
|
||||||
|
{
|
||||||
|
std::string FileName;
|
||||||
|
std::string Format;
|
||||||
|
std::map<std::string, std::any> ExtraParameters;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 从哪个文件读入 AtomPosition, 以及这个文件的格式, 格式可选值包括 "yaml"
|
||||||
|
InputOutputFile AtomPositionInputFile;
|
||||||
|
// 从哪个文件读入 QPointData, 以及这个文件的格式, 格式可选值包括 "yaml" 和 "hdf5"
|
||||||
|
InputOutputFile QPointDataInputFile;
|
||||||
|
|
||||||
|
// 超胞中原子的坐标,每行表示一个原子的坐标,单位为埃
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 输出到哪些文件, 以及使用怎样的格式, 格式可选值包括:
|
||||||
|
// yaml: 使用 yaml 格式输出
|
||||||
|
// yaml-human-readable: 使用 yaml 格式输出, 但是输出的结果更适合人类阅读,
|
||||||
|
// 包括合并相近的模式, 去除权重过小的模式, 限制输出的小数位数.
|
||||||
|
// zpp: 使用 zpp-bits 序列化, 可以直接被 plot.cpp 读取
|
||||||
|
std::vector<InputOutputFile> QPointDataOutputFile;
|
||||||
|
|
||||||
|
// 从文件中读取输入 (包括一个较小的配置文件, 和一个 hdf5 或者一个 yaml 文件), 文件中应当包含:
|
||||||
|
// 单胞的格矢: PrimativeCell 单位为埃 直接从 phonopy 的输出中复制
|
||||||
|
// 超胞的倍数: SuperCellMultiplier 手动输入, 为一个包含三个整数的数组
|
||||||
|
// 超胞的变形: SuperCellDeformation 手动输入, 为一个三阶方阵
|
||||||
|
// 平面波的基矢个数: PrimativeCellBasisNumber 手动输入, 为一个包含三个整数的数组
|
||||||
|
// 另外还有一个文件, 直接将 phonopy 的输出复制过来即可, 如果是 yaml, 应该包含下面的内容:
|
||||||
|
// 超胞中原子的坐标: points[*].coordinates 单位为超胞的格矢 直接从 phonopy 的输出中复制
|
||||||
|
// 各个 Q 点的坐标: phonon[*].q-position 单位为超胞的倒格子的格矢 直接从 phonopy 的输出中复制
|
||||||
|
// 各个模式的频率: phonon[*].band[*].frequency 单位为 THz 直接从 phonopy 的输出中复制
|
||||||
|
// 各个模式的原子运动状态: phonon[*].band[*].eigenvector 直接从 phonopy 的输出中复制
|
||||||
|
// 文件中可以有多余的项目, 多余的项目不管.
|
||||||
|
InputType(std::string filename);
|
||||||
|
};
|
||||||
|
struct OutputType
|
||||||
|
{
|
||||||
|
// 关于各个 Q 点的数据
|
||||||
|
struct QPointDataType
|
||||||
|
{
|
||||||
|
// Q 点的坐标,单位为单胞的倒格矢
|
||||||
|
Eigen::Vector3d QPoint;
|
||||||
|
|
||||||
|
// 来源于哪个 Q 点, 单位为超胞的倒格矢
|
||||||
|
Eigen::Vector3d Source;
|
||||||
|
std::size_t SourceIndex_;
|
||||||
|
|
||||||
|
// 关于这个 Q 点上各个模式的数据
|
||||||
|
struct ModeDataType
|
||||||
|
{
|
||||||
|
// 模式的频率,单位为 THz
|
||||||
|
double Frequency;
|
||||||
|
// 模式的权重
|
||||||
|
double Weight;
|
||||||
|
};
|
||||||
|
std::vector<ModeDataType> ModeData;
|
||||||
|
};
|
||||||
|
std::vector<QPointDataType> QPointData;
|
||||||
|
|
||||||
|
void write(decltype(InputType::QPointDataOutputFile) output_files) const;
|
||||||
|
void write(std::string filename, std::string format, unsigned percision = 10) const;
|
||||||
|
|
||||||
|
using serialize = zpp::bits::members<1>;
|
||||||
|
|
||||||
|
virtual ~OutputType() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 第一层是不同的 sub qpoint, 第二层是单胞内不同的平面波
|
||||||
|
using BasisType = std::vector<std::vector<Eigen::VectorXcd>>;
|
||||||
|
protected:
|
||||||
|
InputType Input_;
|
||||||
|
std::optional<OutputType> Output_;
|
||||||
|
std::optional<BasisType> Basis_;
|
||||||
|
|
||||||
|
// 第一层是不同的模式, 第二层是不同的 sub qpoint
|
||||||
|
using ProjectionCoefficientType_ = std::vector<std::vector<double>>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UnfoldSolver(std::string config_file);
|
||||||
|
UnfoldSolver& operator()() override;
|
||||||
|
|
||||||
|
// 构建基
|
||||||
|
// 每个 q 点对应的一组 sub qpoint。不同的 q 点所对应的 sub qpoint 是不一样的,但 sub qpoint 与 q 点的相对位置一致。
|
||||||
|
// 这里 xyz_of_diff_of_sub_qpoint 即表示这个相对位置。
|
||||||
|
// 由于基只与这个相对位置有关(也就是说,不同 q 点的基是一样的),因此可以先计算出所有的基,这样降低计算量。
|
||||||
|
// 外层下标对应超胞倒格子的整数倍那部分(第二部分), 也就是不同的 sub qpoint
|
||||||
|
// 内层下标对应单胞倒格子的整数倍那部分(第一部分), 也就是 sub qpoint 上的不同平面波(取的数量越多,结果越精确)
|
||||||
|
static BasisType construct_basis
|
||||||
|
(
|
||||||
|
const decltype(InputType::PrimativeCell)& primative_cell,
|
||||||
|
const decltype(InputType::SuperCellMultiplier)& super_cell_multiplier,
|
||||||
|
const decltype(InputType::PrimativeCellBasisNumber)&
|
||||||
|
primative_cell_basis_number,
|
||||||
|
const decltype(InputType::AtomPosition)& atom_position
|
||||||
|
);
|
||||||
|
|
||||||
|
// 计算投影系数, 是反折叠的核心步骤
|
||||||
|
ProjectionCoefficientType_ construct_projection_coefficient
|
||||||
|
(
|
||||||
|
const BasisType& basis,
|
||||||
|
const std::vector<std::reference_wrapper<const decltype
|
||||||
|
(InputType::QPointDataType::ModeDataType::AtomMovement)>>& mode_data,
|
||||||
|
std::atomic<unsigned>& number_of_finished_modes
|
||||||
|
);
|
||||||
|
|
||||||
|
OutputType construct_output
|
||||||
|
(
|
||||||
|
const decltype(InputType::PrimativeCell)& primative_cell,
|
||||||
|
const decltype(InputType::SuperCellMultiplier)& super_cell_multiplier,
|
||||||
|
const decltype(InputType::SuperCellDeformation)& super_cell_deformation,
|
||||||
|
const std::vector<std::reference_wrapper<const decltype
|
||||||
|
(InputType::QPointDataType::QPoint)>>& qpoint,
|
||||||
|
const std::vector<std::vector<std::reference_wrapper<const decltype
|
||||||
|
(InputType::QPointDataType::ModeDataType::Frequency)>>>& frequency,
|
||||||
|
const ProjectionCoefficientType_& projection_coefficient
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
9
src/main.cpp
Normal file
9
src/main.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# include <ufo/unfold.hpp>
|
||||||
|
|
||||||
|
int main(int argc, const char** argv)
|
||||||
|
{
|
||||||
|
if (argc != 2)
|
||||||
|
throw std::runtime_error(fmt::format("Usage: {} config.yaml", argv[0]));
|
||||||
|
|
||||||
|
ufo::UnfoldSolver{argv[1]}();
|
||||||
|
}
|
175
src/ufo.cpp
175
src/ufo.cpp
@ -1,175 +0,0 @@
|
|||||||
# include <ufo/ufo.impl.hpp>
|
|
||||||
|
|
||||||
int main(int argc, const char** argv)
|
|
||||||
{
|
|
||||||
if (argc != 2)
|
|
||||||
throw std::runtime_error(fmt::format("Usage: {} config.yaml", argv[0]));
|
|
||||||
|
|
||||||
std::cerr << "Reading input file..." << std::flush;
|
|
||||||
Input input(argv[1]);
|
|
||||||
std::cerr << " Done." << std::endl;
|
|
||||||
|
|
||||||
// 反折叠的原理: 将超胞中的原子运动状态, 投影到一组平面波构成的基矢中.
|
|
||||||
// 每一个平面波的波矢由两部分相加得到: 一部分是单胞倒格子的整数倍, 所取的个数有一定任意性, 论文中建议取大约单胞中原子个数那么多个;
|
|
||||||
// 对于没有缺陷的情况, 取一个应该就足够了.
|
|
||||||
// 另一部分是超胞倒格子的整数倍, 取 n 个, n 为超胞对应的单胞的倍数, 其实也就是倒空间中单胞对应倒格子中超胞的格点.
|
|
||||||
// 只要第一部分取得足够多, 那么单胞中原子的状态就可以完全被这些平面波描述.
|
|
||||||
// 将超胞中原子的运动状态投影到这些基矢上, 计算出投影的系数, 就可以将超胞的原子运动状态分解到单胞中的多个 q 点上.
|
|
||||||
|
|
||||||
// 构建基
|
|
||||||
// 每个 q 点对应的一组 sub qpoint。不同的 q 点所对应的 sub qpoint 是不一样的,但 sub qpoint 与 q 点的相对位置一致。
|
|
||||||
// 这里 xyz_of_diff_of_sub_qpoint 即表示这个相对位置。
|
|
||||||
// 由于基只与这个相对位置有关(也就是说,不同 q 点的基是一样的),因此可以先计算出所有的基,这样降低计算量。
|
|
||||||
// 外层下标对应超胞倒格子的整数倍那部分(第二部分), 也就是不同的 sub qpoint
|
|
||||||
// 内层下标对应单胞倒格子的整数倍那部分(第一部分), 也就是 sub qpoint 上的不同平面波(取的数量越多,结果越精确)
|
|
||||||
std::cerr << "Calculating basis..." << std::flush;
|
|
||||||
std::vector<std::vector<Eigen::VectorXcd>> basis(input.SuperCellMultiplier.prod());
|
|
||||||
// 每个 q 点对应的一组 sub qpoint。不同的 q 点所对应的 sub qpoint 是不一样的,但 sub qpoint 与 q 点的相对位置一致。
|
|
||||||
// 这里 xyz_of_diff_of_sub_qpoint 即表示这个相对位置,单位为超胞的倒格矢
|
|
||||||
for (auto [xyz_of_diff_of_sub_qpoint_by_reciprocal_modified_super_cell, i_of_sub_qpoint]
|
|
||||||
: triplet_sequence(input.SuperCellMultiplier))
|
|
||||||
{
|
|
||||||
basis[i_of_sub_qpoint].resize(input.PrimativeCellBasisNumber.prod());
|
|
||||||
for (auto [xyz_of_basis, i_of_basis] : triplet_sequence(input.PrimativeCellBasisNumber))
|
|
||||||
{
|
|
||||||
// 计算 q 点的坐标, 单位为单胞的倒格矢
|
|
||||||
auto diff_of_sub_qpoint_by_reciprocal_primative_cell = xyz_of_basis.cast<double>()
|
|
||||||
+ input.SuperCellMultiplier.cast<double>().cwiseInverse().asDiagonal()
|
|
||||||
* xyz_of_diff_of_sub_qpoint_by_reciprocal_modified_super_cell.cast<double>();
|
|
||||||
// 将 q 点坐标转换为埃^-1
|
|
||||||
auto qpoint = (diff_of_sub_qpoint_by_reciprocal_primative_cell.transpose()
|
|
||||||
* (input.PrimativeCell.transpose().inverse())).transpose();
|
|
||||||
// 计算基矢
|
|
||||||
basis[i_of_sub_qpoint][i_of_basis]
|
|
||||||
= (2i * std::numbers::pi_v<double> * (input.AtomPosition * qpoint)).array().exp();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::cerr << "Done." << std::endl;
|
|
||||||
|
|
||||||
// 计算投影的结果
|
|
||||||
// 最外层下标对应反折叠前的 q 点, 第二层下标对应不同模式, 第三层下标对应这个模式在反折叠后的 q 点(sub qpoint)
|
|
||||||
std::vector<std::vector<std::vector<double>>> projection_coefficient(input.QPointData.size());
|
|
||||||
std::atomic<unsigned> finished_qpoint(0);
|
|
||||||
// 对每个 q 点并行
|
|
||||||
std::transform
|
|
||||||
(
|
|
||||||
std::execution::par, input.QPointData.begin(), input.QPointData.end(),
|
|
||||||
projection_coefficient.begin(), [&](const auto& qpoint_data)
|
|
||||||
{
|
|
||||||
std::osyncstream(std::cerr) << fmt::format("\rCalculating projection coefficient... ({}/{})",
|
|
||||||
finished_qpoint, input.QPointData.size()) << std::flush;
|
|
||||||
std::vector<std::vector<double>> projection_coefficient(qpoint_data.ModeData.size());
|
|
||||||
// 这里, qpoint_data 和 projection_coefficient 均指对应于一个 q 点的数据
|
|
||||||
for (unsigned i_of_mode = 0; i_of_mode < qpoint_data.ModeData.size(); i_of_mode++)
|
|
||||||
{
|
|
||||||
auto& _ = projection_coefficient[i_of_mode];
|
|
||||||
_.resize(input.SuperCellMultiplier.prod());
|
|
||||||
for (unsigned i_of_sub_qpoint = 0; i_of_sub_qpoint < input.SuperCellMultiplier.prod(); i_of_sub_qpoint++)
|
|
||||||
// 对于 basis 中, 对应于单胞倒格子的部分, 以及对应于不同方向的部分, 分别求内积, 然后求模方和
|
|
||||||
for (unsigned i_of_basis = 0; i_of_basis < input.PrimativeCellBasisNumber.prod(); i_of_basis++)
|
|
||||||
_[i_of_sub_qpoint] +=
|
|
||||||
(basis[i_of_sub_qpoint][i_of_basis].transpose().conjugate() * qpoint_data.ModeData[i_of_mode].AtomMovement)
|
|
||||||
.array().abs2().sum();
|
|
||||||
// 如果是严格地将向量分解到一组完备的基矢上, 那么不需要对计算得到的权重再做归一化处理
|
|
||||||
// 但这里并不是这样一个严格的概念. 因此对分解到各个 sub qpoint 上的权重做归一化处理
|
|
||||||
auto sum = std::accumulate(_.begin(), _.end(), 0.);
|
|
||||||
for (auto& __ : _)
|
|
||||||
__ /= sum;
|
|
||||||
}
|
|
||||||
finished_qpoint++;
|
|
||||||
std::osyncstream(std::cerr) << fmt::format("\rCalculating projection coefficient...({}/{})",
|
|
||||||
finished_qpoint, input.QPointData.size()) << std::flush;
|
|
||||||
return projection_coefficient;
|
|
||||||
});
|
|
||||||
std::cerr << " Done." << std::endl;
|
|
||||||
|
|
||||||
// 填充输出对象
|
|
||||||
std::cerr << "Filling output object..." << std::flush;
|
|
||||||
Output output;
|
|
||||||
for (unsigned i_of_qpoint = 0; i_of_qpoint < input.QPointData.size(); i_of_qpoint++)
|
|
||||||
{
|
|
||||||
// 当 SuperCellDeformation 不是单位矩阵时, input.QPointData[i_of_qpoint].QPoint 不一定在 reciprocal_primative_cell 中
|
|
||||||
// 需要首先将 q 点平移数个周期, 进入不包含 SuperCellDeformation 的超胞的倒格子中
|
|
||||||
auto qpoint_by_reciprocal_modified_super_cell_in_modified_reciprocal_super_cell
|
|
||||||
= !input.SuperCellDeformation ? input.QPointData[i_of_qpoint].QPoint : [&]
|
|
||||||
{
|
|
||||||
auto current_qpoint = input.QPointData[i_of_qpoint].QPoint;
|
|
||||||
// 给一个 q 点打分
|
|
||||||
// 计算这个 q 点以 modified_reciprocal_supre_cell 为单位的坐标, 依次考虑每个维度, 总分为每个维度之和.
|
|
||||||
// 如果这个坐标大于 0 小于 1, 则打 0 分.
|
|
||||||
// 如果这个坐标小于 0, 则打这个坐标的相反数分.
|
|
||||||
// 如果这个坐标大于 1, 则打这个坐标减去 1 的分.
|
|
||||||
auto score = [&](Eigen::Vector3d qpoint_by_reciprocal_super_cell)
|
|
||||||
{
|
|
||||||
// SuperCell = SuperCellDeformation * SuperCellMultiplier.asDiagonal() * PrimativeCell
|
|
||||||
// ModifiedSuperCell = SuperCellMultiplier.asDiagonal() * PrimativeCell
|
|
||||||
// ReciprocalSuperCell = SuperCell.inverse().transpose()
|
|
||||||
// ReciprocalModifiedSuperCell = ModifiedSuperCell.inverse().transpose()
|
|
||||||
// qpoint.transpose() = qpoint_by_reciprocal_super_cell.transpose() * ReciprocalSuperCell
|
|
||||||
// qpoint.transpose() = qpoint_by_reciprocal_modified_super_cell.transpose() * ReciprocalModifiedSuperCell
|
|
||||||
auto qpoint_by_reciprocal_modified_super_cell =
|
|
||||||
(input.SuperCellDeformation->inverse() * qpoint_by_reciprocal_super_cell).eval();
|
|
||||||
double score = 0;
|
|
||||||
for (unsigned i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
auto coordinate = qpoint_by_reciprocal_modified_super_cell[i];
|
|
||||||
if (coordinate < 0)
|
|
||||||
score -= coordinate;
|
|
||||||
else if (coordinate > 1)
|
|
||||||
score += coordinate - 1;
|
|
||||||
}
|
|
||||||
return score;
|
|
||||||
};
|
|
||||||
while (score(current_qpoint) > 0)
|
|
||||||
{
|
|
||||||
double min_score = std::numeric_limits<double>::max();
|
|
||||||
Eigen::Vector3d min_score_qpoint;
|
|
||||||
for (int x = -1; x <= 1; x++)
|
|
||||||
for (int y = -1; y <= 1; y++)
|
|
||||||
for (int z = -1; z <= 1; z++)
|
|
||||||
{
|
|
||||||
auto this_qpoint = current_qpoint
|
|
||||||
+ Eigen::Matrix<int, 3, 1>{{x}, {y}, {z}}.cast<double>();
|
|
||||||
auto this_score = score(this_qpoint);
|
|
||||||
if (this_score < min_score)
|
|
||||||
{
|
|
||||||
min_score = this_score;
|
|
||||||
min_score_qpoint = this_qpoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
current_qpoint = min_score_qpoint;
|
|
||||||
}
|
|
||||||
return input.SuperCellDeformation->inverse() * current_qpoint;
|
|
||||||
}();
|
|
||||||
for (auto [xyz_of_diff_of_sub_qpoint_by_reciprocal_modified_super_cell, i_of_sub_qpoint]
|
|
||||||
: triplet_sequence(input.SuperCellMultiplier))
|
|
||||||
{
|
|
||||||
auto& _ = output.QPointData.emplace_back();
|
|
||||||
auto reciprocal_modified_super_cell =
|
|
||||||
(input.SuperCellMultiplier.cast<double>().asDiagonal() * input.PrimativeCell).inverse().transpose();
|
|
||||||
// sub qpoint 的坐标,单位为埃^-1
|
|
||||||
auto sub_qpoint = ((xyz_of_diff_of_sub_qpoint_by_reciprocal_modified_super_cell.cast<double>()
|
|
||||||
+ qpoint_by_reciprocal_modified_super_cell_in_modified_reciprocal_super_cell)
|
|
||||||
.transpose() * reciprocal_modified_super_cell).transpose();
|
|
||||||
// 将坐标转换为相对于单胞的倒格矢的坐标并写入
|
|
||||||
// 由 sub_qpoint.transpose() = sub_qpoint_by_reciprocal_primative_cell.transpose()
|
|
||||||
// * PrimativeCell.transpose().inverse()
|
|
||||||
// 得到 sub_qpoint_by_reciprocal_primative_cell = PrimativeCell * sub_qpoint
|
|
||||||
_.QPoint = input.PrimativeCell * sub_qpoint;
|
|
||||||
_.Source = input.QPointData[i_of_qpoint].QPoint;
|
|
||||||
_.SourceIndex_ = i_of_qpoint;
|
|
||||||
for (unsigned i_of_mode = 0; i_of_mode < input.QPointData[i_of_qpoint].ModeData.size(); i_of_mode++)
|
|
||||||
{
|
|
||||||
auto& __ = _.ModeData.emplace_back();
|
|
||||||
__.Frequency = input.QPointData[i_of_qpoint].ModeData[i_of_mode].Frequency;
|
|
||||||
__.Weight = projection_coefficient[i_of_qpoint][i_of_mode][i_of_sub_qpoint];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::cerr << " Done." << std::endl;
|
|
||||||
|
|
||||||
std::cerr << "Writing output file..." << std::flush;
|
|
||||||
for (auto& output_file : input.QPointDataOutputFile)
|
|
||||||
output.write(output_file.FileName, output_file.Format);
|
|
||||||
std::cerr << " Done." << std::endl;
|
|
||||||
}
|
|
498
src/unfold.cpp
Normal file
498
src/unfold.cpp
Normal file
@ -0,0 +1,498 @@
|
|||||||
|
# include <ufo/unfold.hpp>
|
||||||
|
|
||||||
|
namespace ufo
|
||||||
|
{
|
||||||
|
UnfoldSolver::InputType::InputType(std::string filename)
|
||||||
|
{
|
||||||
|
// read main input file
|
||||||
|
{
|
||||||
|
auto node = YAML::LoadFile(filename);
|
||||||
|
for (unsigned i = 0; i < 3; i++)
|
||||||
|
for (unsigned j = 0; j < 3; j++)
|
||||||
|
PrimativeCell(i, j) = node["PrimativeCell"][i][j].as<double>();
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 3; i++)
|
||||||
|
SuperCellMultiplier(i) = node["SuperCellMultiplier"][i].as<int>();
|
||||||
|
|
||||||
|
if (auto value = node["SuperCellDeformation"])
|
||||||
|
{
|
||||||
|
SuperCellDeformation.emplace();
|
||||||
|
for (unsigned i = 0; i < 3; i++)
|
||||||
|
for (unsigned j = 0; j < 3; j++)
|
||||||
|
(*SuperCellDeformation)(i, j) = value[i][j].as<double>();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 3; i++)
|
||||||
|
PrimativeCellBasisNumber(i) = node["PrimativeCellBasisNumber"][i].as<int>();
|
||||||
|
|
||||||
|
auto read_file_config = [filename](YAML::Node source, InputOutputFile& config)
|
||||||
|
{
|
||||||
|
if (auto _ = source["SameAsConfigFile"])
|
||||||
|
{
|
||||||
|
auto __ = _.as<bool>();
|
||||||
|
config.ExtraParameters["SameAsConfigFile"] = __;
|
||||||
|
if (__)
|
||||||
|
{
|
||||||
|
config.FileName = filename;
|
||||||
|
config.Format = "yaml";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.FileName = source["FileName"].as<std::string>();
|
||||||
|
config.Format = source["Format"].as<std::string>();
|
||||||
|
if (auto _ = source["RelativeToConfigFile"])
|
||||||
|
{
|
||||||
|
auto __ = _.as<bool>();
|
||||||
|
config.ExtraParameters["RelativeToConfigFile"] = __;
|
||||||
|
if (__)
|
||||||
|
config.FileName = std::filesystem::path(filename).parent_path() / config.FileName;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
read_file_config(node["AtomPositionInputFile"], AtomPositionInputFile);
|
||||||
|
if (!std::set<std::string>{"yaml"}.contains(AtomPositionInputFile.Format))
|
||||||
|
throw std::runtime_error(fmt::format
|
||||||
|
("Unknown AtomPositionInputFile.Format: {}, should be \"yaml\".", AtomPositionInputFile.Format));
|
||||||
|
read_file_config(node["QPointDataInputFile"], QPointDataInputFile);
|
||||||
|
if (!std::set<std::string>{"yaml", "hdf5"}.contains(QPointDataInputFile.Format))
|
||||||
|
throw std::runtime_error(fmt::format
|
||||||
|
("Unknown QPointDataInputFile.Format: {}, should be \"yaml\" or \"hdf5\".", QPointDataInputFile.Format));
|
||||||
|
if (auto value = node["QPointDataOutputFile"])
|
||||||
|
{
|
||||||
|
QPointDataOutputFile.resize(value.size());
|
||||||
|
for (unsigned i = 0; i < value.size(); i++)
|
||||||
|
{
|
||||||
|
read_file_config(value[i], QPointDataOutputFile[i]);
|
||||||
|
if
|
||||||
|
(
|
||||||
|
QPointDataOutputFile[i].ExtraParameters.contains("SameAsConfigFile")
|
||||||
|
&& std::any_cast<bool>(QPointDataOutputFile[i].ExtraParameters["SameAsConfigFile"])
|
||||||
|
)
|
||||||
|
throw std::runtime_error("QPointDataOutputFile.SameAsConfigFile should not be set.");
|
||||||
|
if
|
||||||
|
(
|
||||||
|
!std::set<std::string>{"yaml", "yaml-human-readable", "zpp"}
|
||||||
|
.contains(QPointDataOutputFile[i].Format)
|
||||||
|
)
|
||||||
|
throw std::runtime_error(fmt::format
|
||||||
|
(
|
||||||
|
"Unknown QPointDataOutputFile[{}].Format: {}, should be \"yaml\", \"yaml-human-readable\" or \"zpp\".",
|
||||||
|
i, QPointDataOutputFile[i].Format
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AtomPositionInputFile.Format == "yaml")
|
||||||
|
{
|
||||||
|
auto node = YAML::LoadFile(AtomPositionInputFile.FileName);
|
||||||
|
std::vector<YAML::Node> points;
|
||||||
|
if (auto _ = node["points"])
|
||||||
|
points = _.as<std::vector<YAML::Node>>();
|
||||||
|
else
|
||||||
|
points = node["unit_cell"]["points"].as<std::vector<YAML::Node>>();
|
||||||
|
auto atom_position_to_super_cell = Eigen::MatrixX3d(points.size(), 3);
|
||||||
|
for (unsigned i = 0; i < points.size(); i++)
|
||||||
|
for (unsigned j = 0; j < 3; j++)
|
||||||
|
atom_position_to_super_cell(i, j) = points[i]["coordinates"][j].as<double>();
|
||||||
|
auto super_cell = (SuperCellDeformation.value_or(Eigen::Matrix3d::Identity())
|
||||||
|
* SuperCellMultiplier.cast<double>().asDiagonal() * PrimativeCell).eval();
|
||||||
|
AtomPosition = atom_position_to_super_cell * super_cell;
|
||||||
|
}
|
||||||
|
if (QPointDataInputFile.Format == "yaml")
|
||||||
|
{
|
||||||
|
auto node = YAML::LoadFile(QPointDataInputFile.FileName);
|
||||||
|
auto phonon = node["phonon"].as<std::vector<YAML::Node>>();
|
||||||
|
QPointData.resize(phonon.size());
|
||||||
|
for (unsigned i = 0; i < phonon.size(); i++)
|
||||||
|
{
|
||||||
|
for (unsigned 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 (unsigned j = 0; j < band.size(); j++)
|
||||||
|
{
|
||||||
|
QPointData[i].ModeData[j].Frequency = band[j]["frequency"].as<double>();
|
||||||
|
auto eigenvector_vectors = band[j]["eigenvector"]
|
||||||
|
.as<std::vector<std::vector<std::vector<double>>>>();
|
||||||
|
Eigen::MatrixX3cd eigenvectors(AtomPosition.rows(), 3);
|
||||||
|
for (unsigned k = 0; k < AtomPosition.rows(); k++)
|
||||||
|
for (unsigned l = 0; l < 3; l++)
|
||||||
|
eigenvectors(k, l)
|
||||||
|
= eigenvector_vectors[k][l][0] + 1i * eigenvector_vectors[k][l][1];
|
||||||
|
// 需要对读入的原子运动状态作相位转换, 使得它们与我们的约定一致(对超胞周期性重复)
|
||||||
|
// 这里还要需要做归一化处理 (指将数据简单地作为向量处理的归一化)
|
||||||
|
auto& AtomMovement = QPointData[i].ModeData[j].AtomMovement;
|
||||||
|
// AtomMovement = eigenvectors.array().colwise() * (-2 * std::numbers::pi_v<double> * 1i
|
||||||
|
// * (atom_position_to_super_cell * input.QPointData[i].QPoint)).array().exp();
|
||||||
|
// AtomMovement /= AtomMovement.norm();
|
||||||
|
// phonopy 似乎已经进行了相位的转换!为什么?
|
||||||
|
AtomMovement = eigenvectors / eigenvectors.norm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (QPointDataInputFile.Format == "hdf5")
|
||||||
|
{
|
||||||
|
HighFive::File file(QPointDataInputFile.FileName, HighFive::File::ReadOnly);
|
||||||
|
auto size = file.getDataSet("/frequency").getDimensions();
|
||||||
|
auto frequency = file.getDataSet("/frequency")
|
||||||
|
.read<std::vector<std::vector<std::vector<double>>>>();
|
||||||
|
auto eigenvector_vector = file.getDataSet("/eigenvector")
|
||||||
|
.read<std::vector<std::vector<std::vector<std::vector<PhonopyComplex>>>>>();
|
||||||
|
auto path = file.getDataSet("/path")
|
||||||
|
.read<std::vector<std::vector<std::vector<double>>>>();
|
||||||
|
QPointData.resize(size[0] * size[1]);
|
||||||
|
for (unsigned i = 0; i < size[0]; i++)
|
||||||
|
for (unsigned j = 0; j < size[1]; j++)
|
||||||
|
{
|
||||||
|
QPointData[i * size[1] + j].QPoint = Eigen::Vector3d(path[i][j].data());
|
||||||
|
QPointData[i * size[1] + j].ModeData.resize(size[2]);
|
||||||
|
for (unsigned k = 0; k < size[2]; k++)
|
||||||
|
{
|
||||||
|
QPointData[i * size[1] + j].ModeData[k].Frequency = frequency[i][j][k];
|
||||||
|
Eigen::MatrixX3cd eigenvectors(AtomPosition.rows(), 3);
|
||||||
|
for (unsigned l = 0; l < AtomPosition.rows(); l++)
|
||||||
|
for (unsigned m = 0; m < 3; m++)
|
||||||
|
eigenvectors(l, m)
|
||||||
|
= eigenvector_vector[i][j][l * 3 + m][k].r + eigenvector_vector[i][j][l * 3 + m][k].i * 1i;
|
||||||
|
QPointData[i * size[1] + j].ModeData[k].AtomMovement = eigenvectors / eigenvectors.norm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnfoldSolver::OutputType::write
|
||||||
|
(decltype(InputType::QPointDataOutputFile) output_files) const
|
||||||
|
{
|
||||||
|
for (auto& output_file : output_files)
|
||||||
|
write(output_file.FileName, output_file.Format);
|
||||||
|
}
|
||||||
|
void UnfoldSolver::OutputType::write(std::string filename, std::string format, unsigned percision) const
|
||||||
|
{
|
||||||
|
if (format == "yaml")
|
||||||
|
std::ofstream(filename) << [&]
|
||||||
|
{
|
||||||
|
std::stringstream print;
|
||||||
|
print << "QPointData:\n";
|
||||||
|
for (auto& qpoint: QPointData)
|
||||||
|
{
|
||||||
|
print << fmt::format(" - QPoint: [ {1:.{0}f}, {2:.{0}f}, {3:.{0}f} ]\n",
|
||||||
|
percision, qpoint.QPoint[0], qpoint.QPoint[1], qpoint.QPoint[2]);
|
||||||
|
print << fmt::format(" Source: [ {1:.{0}f}, {2:.{0}f}, {3:.{0}f} ]\n",
|
||||||
|
percision, qpoint.Source[0], qpoint.Source[1], qpoint.Source[2]);
|
||||||
|
print << " ModeData:\n";
|
||||||
|
for (auto& mode: qpoint.ModeData)
|
||||||
|
print << fmt::format(" - {{ Frequency: {1:.{0}f}, Weight: {2:.{0}f} }}\n",
|
||||||
|
percision, mode.Frequency, mode.Weight);
|
||||||
|
}
|
||||||
|
return print.str();
|
||||||
|
}();
|
||||||
|
else if (format == "yaml-human-readable")
|
||||||
|
{
|
||||||
|
std::remove_cvref_t<decltype(*this)> output;
|
||||||
|
std::map<unsigned, std::vector<decltype(QPointData)::const_iterator>>
|
||||||
|
meta_qpoint_to_sub_qpoint_iterators;
|
||||||
|
for (auto it = QPointData.begin(); it != QPointData.end(); it++)
|
||||||
|
meta_qpoint_to_sub_qpoint_iterators[it->SourceIndex_].push_back(it);
|
||||||
|
for (auto [meta_qpoint_index, sub_qpoint_iterators] : meta_qpoint_to_sub_qpoint_iterators)
|
||||||
|
for (auto& qpoint : sub_qpoint_iterators)
|
||||||
|
{
|
||||||
|
std::map<double, double> frequency_to_weight;
|
||||||
|
for (unsigned i_of_mode = 0; i_of_mode < qpoint->ModeData.size(); i_of_mode++)
|
||||||
|
{
|
||||||
|
auto frequency = qpoint->ModeData[i_of_mode].Frequency;
|
||||||
|
auto weight = qpoint->ModeData[i_of_mode].Weight;
|
||||||
|
auto it_lower = frequency_to_weight.lower_bound(frequency - 0.1);
|
||||||
|
auto it_upper = frequency_to_weight.upper_bound(frequency + 0.1);
|
||||||
|
if (it_lower == it_upper)
|
||||||
|
frequency_to_weight[frequency] = weight;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto frequency_sum = std::accumulate(it_lower, it_upper, 0.,
|
||||||
|
[](const auto& a, const auto& b) { return a + b.first * b.second; });
|
||||||
|
auto weight_sum = std::accumulate(it_lower, it_upper, 0.,
|
||||||
|
[](const auto& a, const auto& b) { return a + b.second; });
|
||||||
|
frequency_sum += frequency * weight;
|
||||||
|
weight_sum += weight;
|
||||||
|
frequency_to_weight.erase(it_lower, it_upper);
|
||||||
|
frequency_to_weight[frequency_sum / weight_sum] = weight_sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto& _ = output.QPointData.emplace_back();
|
||||||
|
_.QPoint = qpoint->QPoint;
|
||||||
|
_.Source = qpoint->Source;
|
||||||
|
_.SourceIndex_ = qpoint->SourceIndex_;
|
||||||
|
for (auto [frequency, weight] : frequency_to_weight)
|
||||||
|
if (weight > 0.1)
|
||||||
|
{
|
||||||
|
auto& __ = _.ModeData.emplace_back();
|
||||||
|
__.Frequency = frequency;
|
||||||
|
__.Weight = weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output.write(filename, "yaml", 3);
|
||||||
|
}
|
||||||
|
else if (format == "zpp")
|
||||||
|
{
|
||||||
|
auto [data, out] = zpp::bits::data_out();
|
||||||
|
out(*this).or_throw();
|
||||||
|
static_assert(sizeof(char) == sizeof(std::byte));
|
||||||
|
std::ofstream file(filename, std::ios::binary | std::ios::out);
|
||||||
|
file.exceptions(std::ios::badbit | std::ios::failbit);
|
||||||
|
file.write(reinterpret_cast<const char*>(data.data()), data.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UnfoldSolver::UnfoldSolver(std::string config_file) : Input_([&]
|
||||||
|
{
|
||||||
|
std::clog << "Reading input file... " << std::flush;
|
||||||
|
return config_file;
|
||||||
|
}())
|
||||||
|
{
|
||||||
|
std::clog << "Done." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnfoldSolver& UnfoldSolver::operator()()
|
||||||
|
{
|
||||||
|
if (!Basis_)
|
||||||
|
{
|
||||||
|
std::clog << "Constructing basis... " << std::flush;
|
||||||
|
Basis_ = construct_basis
|
||||||
|
(
|
||||||
|
Input_.PrimativeCell, Input_.SuperCellMultiplier,
|
||||||
|
Input_.PrimativeCellBasisNumber, Input_.AtomPosition
|
||||||
|
);
|
||||||
|
std::clog << "Done." << std::endl;
|
||||||
|
}
|
||||||
|
if (!Output_)
|
||||||
|
{
|
||||||
|
std::clog << "Calculating projection coefficient... " << std::flush;
|
||||||
|
std::vector<std::reference_wrapper<const decltype
|
||||||
|
(InputType::QPointDataType::ModeDataType::AtomMovement)>> mode_data;
|
||||||
|
for (auto& qpoint : Input_.QPointData)
|
||||||
|
for (auto& mode : qpoint.ModeData)
|
||||||
|
mode_data.emplace_back(mode.AtomMovement);
|
||||||
|
std::atomic<unsigned> number_of_finished_modes(0);
|
||||||
|
std::thread print_thread([&]
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
while ((n = number_of_finished_modes) < mode_data.size())
|
||||||
|
{
|
||||||
|
std::osyncstream(std::cerr) << fmt::format("\rCalculating projection coefficient... ({}/{})",
|
||||||
|
number_of_finished_modes, mode_data.size()) << std::flush;
|
||||||
|
std::this_thread::sleep_for(100ms);
|
||||||
|
number_of_finished_modes.wait(n);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
auto projection_coefficient = construct_projection_coefficient
|
||||||
|
(*Basis_, mode_data, number_of_finished_modes);
|
||||||
|
number_of_finished_modes = mode_data.size();
|
||||||
|
print_thread.join();
|
||||||
|
std::clog << "\rCalculating projection coefficient... Done." << std::endl;
|
||||||
|
|
||||||
|
std::clog << "Constructing output... " << std::flush;
|
||||||
|
std::vector<std::reference_wrapper<const decltype(InputType::QPointDataType::QPoint)>> qpoint;
|
||||||
|
std::vector<std::vector<std::reference_wrapper<const
|
||||||
|
decltype(InputType::QPointDataType::ModeDataType::Frequency)>>> frequency;
|
||||||
|
for (auto& qpoint_data : Input_.QPointData)
|
||||||
|
{
|
||||||
|
qpoint.emplace_back(qpoint_data.QPoint);
|
||||||
|
frequency.emplace_back();
|
||||||
|
for (auto& mode_data : qpoint_data.ModeData)
|
||||||
|
frequency.back().emplace_back(mode_data.Frequency);
|
||||||
|
}
|
||||||
|
Output_ = construct_output
|
||||||
|
(
|
||||||
|
Input_.PrimativeCell, Input_.SuperCellMultiplier,
|
||||||
|
Input_.SuperCellDeformation, qpoint, frequency, projection_coefficient
|
||||||
|
);
|
||||||
|
std::clog << "Done." << std::endl;
|
||||||
|
}
|
||||||
|
std::clog << "Writing output... " << std::flush;
|
||||||
|
Output_->write(Input_.QPointDataOutputFile);
|
||||||
|
std::clog << "Done." << std::endl;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnfoldSolver::BasisType UnfoldSolver::construct_basis
|
||||||
|
(
|
||||||
|
const decltype(InputType::PrimativeCell)& primative_cell,
|
||||||
|
const decltype(InputType::SuperCellMultiplier)& super_cell_multiplier,
|
||||||
|
const decltype(InputType::PrimativeCellBasisNumber)& primative_cell_basis_number,
|
||||||
|
const decltype(InputType::AtomPosition)& atom_position
|
||||||
|
)
|
||||||
|
{
|
||||||
|
BasisType basis(super_cell_multiplier.prod());
|
||||||
|
// 每个 q 点对应的一组 sub qpoint。不同的 q 点所对应的 sub qpoint 是不一样的,但 sub qpoint 与 q 点的相对位置一致。
|
||||||
|
// 这里 xyz_of_diff_of_sub_qpoint 即表示这个相对位置,单位为超胞的倒格矢
|
||||||
|
for (auto [xyz_of_diff_of_sub_qpoint_by_reciprocal_modified_super_cell, i_of_sub_qpoint]
|
||||||
|
: triplet_sequence(super_cell_multiplier))
|
||||||
|
{
|
||||||
|
basis[i_of_sub_qpoint].resize(primative_cell_basis_number.prod());
|
||||||
|
for (auto [xyz_of_basis, i_of_basis] : triplet_sequence(primative_cell_basis_number))
|
||||||
|
{
|
||||||
|
// 计算 q 点的坐标, 单位为单胞的倒格矢
|
||||||
|
auto diff_of_sub_qpoint_by_reciprocal_primative_cell = xyz_of_basis.cast<double>()
|
||||||
|
+ super_cell_multiplier.cast<double>().cwiseInverse().asDiagonal()
|
||||||
|
* xyz_of_diff_of_sub_qpoint_by_reciprocal_modified_super_cell.cast<double>();
|
||||||
|
// 将 q 点坐标转换为埃^-1
|
||||||
|
auto qpoint = (diff_of_sub_qpoint_by_reciprocal_primative_cell.transpose()
|
||||||
|
* (primative_cell.transpose().inverse())).transpose();
|
||||||
|
// 计算基矢
|
||||||
|
basis[i_of_sub_qpoint][i_of_basis]
|
||||||
|
= (2i * std::numbers::pi_v<double> * (atom_position * qpoint)).array().exp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return basis;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<double>> UnfoldSolver::construct_projection_coefficient
|
||||||
|
(
|
||||||
|
const BasisType& basis,
|
||||||
|
const std::vector<std::reference_wrapper<const decltype
|
||||||
|
(InputType::QPointDataType::ModeDataType::AtomMovement)>>& mode_data,
|
||||||
|
std::atomic<unsigned>& number_of_finished_modes
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// 第一层下标对应不同模式, 第二层下标对应这个模式在反折叠后的 q 点(sub qpoint)
|
||||||
|
std::vector<std::vector<double>> projection_coefficient(mode_data.size());
|
||||||
|
// 对每个模式并行
|
||||||
|
std::transform
|
||||||
|
(
|
||||||
|
std::execution::par, mode_data.begin(), mode_data.end(), projection_coefficient.begin(),
|
||||||
|
[&](const auto& mode_data)
|
||||||
|
{
|
||||||
|
// 这里, mode_data 和 projection_coefficient 均指对应于一个模式的数据
|
||||||
|
std::vector<double> projection_coefficient(basis.size());
|
||||||
|
for (unsigned i_of_sub_qpoint = 0; i_of_sub_qpoint < basis.size(); i_of_sub_qpoint++)
|
||||||
|
// 对于 basis 中, 对应于单胞倒格子的部分, 以及对应于不同方向的部分, 分别求内积, 然后求模方和
|
||||||
|
for (unsigned i_of_basis = 0; i_of_basis < basis[i_of_sub_qpoint].size(); i_of_basis++)
|
||||||
|
projection_coefficient[i_of_sub_qpoint] +=
|
||||||
|
(basis[i_of_sub_qpoint][i_of_basis].transpose().conjugate() * mode_data.get())
|
||||||
|
.array().abs2().sum();
|
||||||
|
// 如果是严格地将向量分解到一组完备的基矢上, 那么不需要对计算得到的权重再做归一化处理
|
||||||
|
// 但这里并不是这样一个严格的概念. 因此对分解到各个 sub qpoint 上的权重做归一化处理
|
||||||
|
auto sum = std::accumulate
|
||||||
|
(projection_coefficient.begin(), projection_coefficient.end(), 0.);
|
||||||
|
for (auto& _ : projection_coefficient)
|
||||||
|
_ /= sum;
|
||||||
|
number_of_finished_modes++;
|
||||||
|
return projection_coefficient;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return projection_coefficient;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnfoldSolver::OutputType UnfoldSolver::construct_output
|
||||||
|
(
|
||||||
|
const decltype(InputType::PrimativeCell)& primative_cell,
|
||||||
|
const decltype(InputType::SuperCellMultiplier)& super_cell_multiplier,
|
||||||
|
const decltype(InputType::SuperCellDeformation)& super_cell_deformation,
|
||||||
|
const std::vector<std::reference_wrapper<const decltype
|
||||||
|
(InputType::QPointDataType::QPoint)>>& qpoint,
|
||||||
|
const std::vector<std::vector<std::reference_wrapper<const decltype
|
||||||
|
(InputType::QPointDataType::ModeDataType::Frequency)>>>& frequency,
|
||||||
|
const ProjectionCoefficientType_& projection_coefficient
|
||||||
|
)
|
||||||
|
{
|
||||||
|
OutputType output;
|
||||||
|
for (unsigned i_of_qpoint = 0, num_of_mode_in_all_qpoint = 0; i_of_qpoint < qpoint.size(); i_of_qpoint++)
|
||||||
|
{
|
||||||
|
// 当 SuperCellDeformation 不是单位矩阵时, input.QPointData[i_of_qpoint].QPoint 不一定在 reciprocal_primative_cell 中
|
||||||
|
// 需要首先将 q 点平移数个周期, 进入不包含 SuperCellDeformation 的超胞的倒格子中
|
||||||
|
auto qpoint_by_reciprocal_modified_super_cell_in_modified_reciprocal_super_cell
|
||||||
|
= !super_cell_deformation ? qpoint[i_of_qpoint].get() : [&]
|
||||||
|
{
|
||||||
|
auto current_qpoint = qpoint[i_of_qpoint].get();
|
||||||
|
// 给一个 q 点打分
|
||||||
|
// 计算这个 q 点以 modified_reciprocal_supre_cell 为单位的坐标, 依次考虑每个维度, 总分为每个维度之和.
|
||||||
|
// 如果这个坐标大于 0 小于 1, 则打 0 分.
|
||||||
|
// 如果这个坐标小于 0, 则打这个坐标的相反数分.
|
||||||
|
// 如果这个坐标大于 1, 则打这个坐标减去 1 的分.
|
||||||
|
auto score = [&](Eigen::Vector3d qpoint_by_reciprocal_super_cell)
|
||||||
|
{
|
||||||
|
// SuperCell = SuperCellDeformation * SuperCellMultiplier.asDiagonal() * PrimativeCell
|
||||||
|
// ModifiedSuperCell = SuperCellMultiplier.asDiagonal() * PrimativeCell
|
||||||
|
// ReciprocalSuperCell = SuperCell.inverse().transpose()
|
||||||
|
// ReciprocalModifiedSuperCell = ModifiedSuperCell.inverse().transpose()
|
||||||
|
// qpoint.transpose() = qpoint_by_reciprocal_super_cell.transpose() * ReciprocalSuperCell
|
||||||
|
// qpoint.transpose() = qpoint_by_reciprocal_modified_super_cell.transpose() * ReciprocalModifiedSuperCell
|
||||||
|
auto qpoint_by_reciprocal_modified_super_cell =
|
||||||
|
(super_cell_deformation->inverse() * qpoint_by_reciprocal_super_cell).eval();
|
||||||
|
double score = 0;
|
||||||
|
for (unsigned i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
auto coordinate = qpoint_by_reciprocal_modified_super_cell[i];
|
||||||
|
if (coordinate < 0)
|
||||||
|
score -= coordinate;
|
||||||
|
else if (coordinate > 1)
|
||||||
|
score += coordinate - 1;
|
||||||
|
}
|
||||||
|
return score;
|
||||||
|
};
|
||||||
|
while (score(current_qpoint) > 0)
|
||||||
|
{
|
||||||
|
double min_score = std::numeric_limits<double>::max();
|
||||||
|
Eigen::Vector3d min_score_qpoint;
|
||||||
|
for (int x = -1; x <= 1; x++)
|
||||||
|
for (int y = -1; y <= 1; y++)
|
||||||
|
for (int z = -1; z <= 1; z++)
|
||||||
|
{
|
||||||
|
auto this_qpoint = current_qpoint
|
||||||
|
+ Eigen::Matrix<int, 3, 1>{{x}, {y}, {z}}.cast<double>();
|
||||||
|
auto this_score = score(this_qpoint);
|
||||||
|
if (this_score < min_score)
|
||||||
|
{
|
||||||
|
min_score = this_score;
|
||||||
|
min_score_qpoint = this_qpoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current_qpoint = min_score_qpoint;
|
||||||
|
}
|
||||||
|
return super_cell_deformation->inverse() * current_qpoint;
|
||||||
|
}();
|
||||||
|
for (auto [xyz_of_diff_of_sub_qpoint_by_reciprocal_modified_super_cell, i_of_sub_qpoint]
|
||||||
|
: triplet_sequence(super_cell_multiplier))
|
||||||
|
{
|
||||||
|
auto& _ = output.QPointData.emplace_back();
|
||||||
|
auto reciprocal_modified_super_cell =
|
||||||
|
(super_cell_multiplier.cast<double>().asDiagonal() * primative_cell).inverse().transpose();
|
||||||
|
// sub qpoint 的坐标,单位为埃^-1
|
||||||
|
auto sub_qpoint = ((xyz_of_diff_of_sub_qpoint_by_reciprocal_modified_super_cell.cast<double>()
|
||||||
|
+ qpoint_by_reciprocal_modified_super_cell_in_modified_reciprocal_super_cell)
|
||||||
|
.transpose() * reciprocal_modified_super_cell).transpose();
|
||||||
|
// 将坐标转换为相对于单胞的倒格矢的坐标并写入
|
||||||
|
// 由 sub_qpoint.transpose() = sub_qpoint_by_reciprocal_primative_cell.transpose()
|
||||||
|
// * PrimativeCell.transpose().inverse()
|
||||||
|
// 得到 sub_qpoint_by_reciprocal_primative_cell = PrimativeCell * sub_qpoint
|
||||||
|
_.QPoint = primative_cell * sub_qpoint;
|
||||||
|
_.Source = qpoint[i_of_qpoint];
|
||||||
|
_.SourceIndex_ = i_of_qpoint;
|
||||||
|
for (unsigned i_of_mode = 0; i_of_mode < frequency[i_of_qpoint].size(); i_of_mode++, num_of_mode_in_all_qpoint++)
|
||||||
|
{
|
||||||
|
auto& __ = _.ModeData.emplace_back();
|
||||||
|
__.Frequency = frequency[i_of_qpoint][i_of_mode];
|
||||||
|
__.Weight = projection_coefficient[num_of_mode_in_all_qpoint][i_of_sub_qpoint];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// inline Output::Output(std::string filename)
|
||||||
|
// {
|
||||||
|
// auto input = std::ifstream(filename, std::ios::binary | std::ios::in);
|
||||||
|
// input.exceptions(std::ios::badbit | std::ios::failbit);
|
||||||
|
// std::vector<std::byte> data;
|
||||||
|
// {
|
||||||
|
// std::vector<char> string(std::istreambuf_iterator<char>(input), {});
|
||||||
|
// data.assign
|
||||||
|
// (
|
||||||
|
// reinterpret_cast<std::byte*>(string.data()),
|
||||||
|
// reinterpret_cast<std::byte*>(string.data() + string.size())
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// auto in = zpp::bits::in(data);
|
||||||
|
// in(*this).or_throw();
|
||||||
|
// }
|
||||||
|
|
Loading…
Reference in New Issue
Block a user