Compare commits

24 Commits

Author SHA1 Message Date
82ad565448 AtomTranslation 改为以超胞晶格矢为单位,而不是以埃为单位 2026-01-08 12:36:14 +08:00
cb7dd54e7f 一些小修改,应该不影响结果 2026-01-08 12:36:14 +08:00
19736049c9 fix build 2026-01-05 15:21:35 +08:00
chn
dc6b431bf9 fix raman tensor scaling 2025-11-05 12:05:12 +08:00
chn
32febb2843 Revert "fix raman tensor calculation scaling"
This reverts commit a54e769b24.
2025-11-05 12:02:52 +08:00
chn
a54e769b24 fix raman tensor calculation scaling 2025-11-05 11:37:44 +08:00
chn
8a137d5342 a little improvement in code (should not change result) 2025-10-11 14:45:59 +08:00
chn
b21a8fb198 Revert "fix raman"
This reverts commit 2ddfca800a.
2025-10-11 10:18:55 +08:00
chn
2ddfca800a fix raman 2025-10-10 16:29:16 +08:00
chn
745353d089 project-to-atom: fix 2025-04-01 18:15:43 +08:00
chn
c4c549c669 project-to-atom: add test 2025-04-01 17:30:01 +08:00
chn
388c77db5f remove unused config 2025-04-01 17:22:26 +08:00
chn
d4578c7991 fix 2024-12-24 17:47:02 +08:00
chn
f871c28768 修改拉曼输出文件布局 2024-12-22 15:53:11 +08:00
chn
d584951fd7 fix raman, add test 2024-12-04 15:21:09 +08:00
chn
6e64054ffc fix 2024-12-03 19:11:48 +08:00
chn
0c073b73b5 clean up 2024-12-03 18:15:28 +08:00
chn
bf3c8430ed 解决bug 2024-12-03 17:56:22 +08:00
chn
eb6b903417 写了 project_to_mode 但还没调试好 2024-12-03 16:25:17 +08:00
chn
3f98ce8378 add zpp-to-yaml test 2024-12-02 18:51:17 +08:00
chn
7b4ea3bbf3 add unfold test 2024-12-02 18:27:40 +08:00
chn
db1d3befd6 重构 2024-12-02 15:47:33 +08:00
chn
b94823f8aa add raman support 2024-11-24 22:13:29 +08:00
chn
28e4d29f2c init 2024-11-21 16:18:06 +08:00
104 changed files with 1750 additions and 2718 deletions

2
.envrc
View File

@@ -1 +1 @@
use flake
use flake nixos#ufo

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
*.hdf5 filter=lfs diff=lfs merge=lfs -text
*.zpp filter=lfs diff=lfs merge=lfs -text

18
.gitignore vendored
View File

@@ -1,18 +1,4 @@
.vscode
.direnv
main
plot
out.yaml
.ccls-cache
build
.vscode
.cache
build/
test/*
!test/14.2.2.4.unfold.yaml
!test/14.2.2.5.unfold.yaml
!test/14.2.2.5.plot.yaml
!test/14.2.4.4.unfold.yaml
!test/14.2.4.4.plot.yaml
!test/14.2.5.5.unfold.yaml
!test/14.2.5.5.plot.yaml
!test/14.2.6.4.unfold.yaml
!test/14.2.6.4.plot.yaml

View File

@@ -1,36 +1,51 @@
cmake_minimum_required(VERSION 3.14)
project(ufo VERSION 0.0.0 LANGUAGES CXX)
project(ufo VERSION 0 LANGUAGES CXX)
enable_testing()
include(GNUInstallDirs)
set_property(GLOBAL PROPERTY CXX_STANDARD 23)
set_property(GLOBAL PROPERTY CXX_STANDARD_REQUIRED ON)
set_property(GLOBAL PROPERTY CXX_EXTENSIONS OFF)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message("Setting build type to 'Release' as none was specified.")
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
find_package(yaml-cpp REQUIRED)
find_package(Eigen3 REQUIRED)
find_package(fmt REQUIRED)
find_package(concurrencpp REQUIRED)
find_package(HighFive REQUIRED)
find_package(TBB REQUIRED)
find_package(glad REQUIRED)
# find_package(Matplot++ REQUIRED COMPONENTS matplot_opengl)
find_package(Matplot++ REQUIRED)
find_path(ZPP_BITS_INCLUDE_DIR zpp_bits.h REQUIRED)
find_package(biu REQUIRED)
find_package(Threads REQUIRED)
message("biu dir: ${biu_DIR}")
add_executable(ufo src/solver.cpp src/fold.cpp src/unfold.cpp src/plot.cpp src/main.cpp)
target_include_directories(ufo PRIVATE ${PROJECT_SOURCE_DIR}/include ${ZPP_BITS_INCLUDE_DIR})
target_link_libraries(ufo PRIVATE
yaml-cpp Eigen3::Eigen fmt::fmt concurrencpp::concurrencpp HighFive_HighFive TBB::tbb Matplot++::matplot
Matplot++::matplot_opengl)
# target_compile_definitions(ufo PRIVATE $<$<CONFIG:Debug>:_GLIBCXX_DEBUG>)
add_executable(ufo src/fold.cpp src/unfold.cpp src/project-to-mode.cpp
src/plot.cpp src/raman.cpp src/project-to-atom.cpp src/main.cpp)
target_include_directories(ufo PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_link_libraries(ufo PRIVATE TBB::tbb Matplot++::matplot biu::biu)
target_compile_features(ufo PRIVATE cxx_std_23)
target_compile_definitions(ufo PRIVATE BIU_LOGGER_SOURCE_ROOT="${CMAKE_SOURCE_DIR}")
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(ufo PRIVATE BIU_LOGGER_DEBUG)
target_compile_options(ufo PRIVATE -ftemplate-backtrace-limit=0)
endif()
install(TARGETS ufo RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
get_property(ImportedTargets DIRECTORY "${CMAKE_SOURCE_DIR}" PROPERTY IMPORTED_TARGETS)
message("Imported targets: ${ImportedTargets}")
message("List of compile features: ${CMAKE_CXX_COMPILE_FEATURES}")
message("CMake build type: ${CMAKE_BUILD_TYPE}")
include(CTest)
add_test(NAME fold WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test/fold COMMAND ufo fold config.yaml)
add_test(NAME unfold WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test/unfold COMMAND ufo unfold config.yaml)
add_test(NAME zpp-to-yaml WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test/zpp-to-yaml
COMMAND sh -c "${CMAKE_BINARY_DIR}/ufo zpp-to-yaml < data.zpp > data.yaml")
add_test(NAME project-to-mode WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test/project-to-mode
COMMAND ufo project-to-mode config.yaml)
add_test(NAME project-to-atom WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test/project-to-atom
COMMAND ufo project-to-atom config.yaml)
add_test(NAME raman-create-displacement WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test/raman-create-displacement
COMMAND ufo raman-create-displacement config.yaml)
add_test(NAME raman-extract WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test/raman-extract
COMMAND ufo raman-extract .)
add_test(NAME raman-apply-contribution WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test/raman-apply-contribution
COMMAND ufo raman-apply-contribution config.yaml)

View File

@@ -1,17 +0,0 @@
This is a program to approximately unfold phonon spectra.
Currently, there is no documentation, and some not core features are in other branches
and implemented in a temporary way, e.g., directly writing data in source file.
I will finish the documentation and merge these features into main branch ASAP.
The program is packaged in a standard way of cmake, so that you can build it just like other cmake projects:
```bash
mkdir build
cd build
cmake ..
make
```
and view source files for input and output format.

11
default.nix Normal file
View File

@@ -0,0 +1,11 @@
{
stdenv, cmake, pkg-config, version ? null,
tbb, matplotplusplus, biu
}: stdenv.mkDerivation
{
name = "ufo";
src = ./.;
buildInputs = [ tbb matplotplusplus biu ];
nativeBuildInputs = [ cmake pkg-config ];
doCheck = true;
}

45
doc/README.md Normal file
View File

@@ -0,0 +1,45 @@
分为几个功能:
* fold根据要计算的单胞的 q 点路径,计算超胞中对应的 q 点路径,生成的路径再交给 phonopy 计算。
* unfold根据 phonopy 计算的结果,将超胞的结果展开到单胞中。
* plot对计算结果画图。
主要的输入输出格式均为 yaml。对于数据特别大的情况也可以从 hdf5 中读取一部分数据或者将一部分数据写入到 hdf5 文件中。
# fold
## 输入
```yaml
# 三个整数组成的向量,表示从单胞到超胞,三个晶格矢量的倍数
# 必写
SuperCellMultiplier: [2, 2, 2]
# 一个变换矩阵,表明超胞经历了怎样的扭曲。
# 可选,默认值为单位矩阵
SuperCellDeformation: [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
# 一个由三个浮点数组成的向量,表示考虑的 q 点
# 必写
Qpoints:
- [0, 0, 0]
- [0.1, 0, 0]
- [0.2, 0, 0]
- [0.3, 0, 0]
- [0.4, 0, 0]
- [0.5, 0, 0]
# 一个 DataFile 类型的对象,表明输出结果到哪个文件
# 必写
OutputFile:
```
## 输出
```yaml
# 得到的 q 点坐标
Qpoints:
- [0, 0, 0]
- [0.1, 0, 0]
- [0.2, 0, 0]
- [0.3, 0, 0]
- [0.4, 0, 0]
- [0.5, 0, 0]
```

1370
flake.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +0,0 @@
{
inputs.nixos.url = "github:CHN-beta/nixos";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
outputs = inputs:
let
# pkgs = inputs.nixpkgs.legacyPackages.x86_64-linux;
pkgs = import inputs.nixpkgs
{
localSystem = { system = "x86_64-linux"; gcc = { arch = "alderlake"; tune = "alderlake"; }; };
config.allowUnfree = true;
};
localPackages = import "${inputs.nixos}/local/pkgs" { inherit pkgs; inherit (inputs.nixpkgs) lib; };
in
{
devShell.x86_64-linux = pkgs.mkShell.override { stdenv = pkgs.gcc13Stdenv; }
{
packages = with pkgs; [ pkg-config cmake ninja ];
buildInputs = (with pkgs; [ eigen yaml-cpp fmt highfive tbb_2021_8.dev glfw libGL ])
++ (with localPackages; [ concurrencpp matplotplusplus zpp-bits glad ]);
hardeningDisable = [ "all" ];
# NIX_DEBUG = "1";
};
};
}

104
include/ufo.hpp Normal file
View File

@@ -0,0 +1,104 @@
# pragma once
# include <biu.hpp>
namespace ufo
{
// 在相位中, 约定为使用 $\exp (2 \pi i \vec{q} \cdot \vec{r})$ 来表示原子的运动状态
// (而不是 $\exp (-2 \pi i \vec{q} \cdot \vec{r})$)
// 一些书定义的倒格矢中包含了 $2 \pi$ 的部分, 我们这里约定不包含这部分.
// 也就是说, 正格子与倒格子的转置相乘, 得到单位矩阵.
using namespace biu::literals;
using namespace biu::stream_operators;
// 许多函数通用的一个结构体
struct CommonData
{
// 原胞相关信息,其中存储的信息是直接读入的,而不是反折叠后的结果
struct
{
// 晶胞,单位为埃,每行代表一个格矢
Eigen::Matrix3d Cell;
// 原子种类及其个数
std::vector<std::pair<std::string, std::size_t>> AtomType;
// 原子位置,单位为原胞格矢
Eigen::MatrixX3d AtomPosition;
// 单胞中每个 q 点的信息
struct QpointType
{
// q 点的坐标,单位为倒格矢
Eigen::Vector3d Qpoint;
struct ModeType
{
// 振动频率,单位为 THz
double Frequency;
// 振动模式,单位为埃
Eigen::MatrixX3cd EigenVector;
// 拉曼张量
// TODO: 单位是什么?
std::optional<double> WeightOnRaman;
// work around for fpr
std::optional<std::array<std::array<double, 3>, 3>> RamanTensor;
using serialize = zpp::bits::members<4>;
};
// 各个模式的的信息
std::vector<ModeType> Mode;
using serialize = zpp::bits::members<2>;
};
std::vector<QpointType> Qpoint;
using serialize = zpp::bits::members<4>;
} Primative;
// 超胞相关信息,包括反折叠后的信息
struct
{
// 超胞的晶胞,单位为埃,每行代表一个格矢
Eigen::Matrix3d Cell;
// 单胞与到超胞的变换
Eigen::Vector3i CellMultiplier;
Eigen::Matrix3d CellDeformation;
std::vector<std::pair<std::string, std::size_t>> AtomType;
Eigen::MatrixX3d AtomPosition;
std::optional<std::array<double, 3>> AtomTranslation;
struct QpointType
{
Eigen::Vector3d Qpoint;
std::vector<Eigen::Vector3d> SubQpoint;
struct ModeType
{
double Frequency;
Eigen::MatrixX3cd EigenVector;
std::vector<double> WeightOnUnfold;
std::optional<double> WeightOnSelectedAtom;
std::optional<double> WeightOnRaman;
std::optional<std::array<std::array<double, 3>, 3>> RamanTensor;
using serialize = zpp::bits::members<6>;
};
std::vector<ModeType> Mode;
using serialize = zpp::bits::members<3>;
};
std::vector<QpointType> Qpoint;
using serialize = zpp::bits::members<7>;
} Super;
// 选择的原子
std::optional<std::set<std::size_t>> SelectedAtom;
// 拉曼散射选择的偏振方向
std::optional<std::array<Eigen::Vector3d, 2>> RamanPolarization;
// 各种原子的质量
std::map<std::string, double> AtomMass;
using serialize = zpp::bits::members<5>;
};
void fold(std::string config_file);
void unfold(std::string config_file);
void project_to_atom(std::string config_file);
void project_to_mode(std::string config_file);
void plot_band(std::string config_file);
void plot_point(std::string config_file);
void raman_create_displacement(std::string config_file);
void raman_extract(std::string path);
void raman_apply_contribution(std::string config_file);
}

View File

@@ -1,37 +0,0 @@
# pragma once
# include <ufo/solver.hpp>
namespace ufo
{
class FoldSolver : public Solver
{
public:
struct InputType
{
Eigen::Vector<unsigned, 3> SuperCellMultiplier;
std::optional<Eigen::Matrix<double, 3, 3>> SuperCellDeformation;
std::vector<Eigen::Vector3d> Qpoints;
DataFile OutputFile;
InputType(std::string config_file);
};
struct OutputType
{
std::vector<Eigen::Vector3d> Qpoints;
void write(std::string filename) const;
};
protected:
InputType Input_;
std::optional<OutputType> Output_;
public:
FoldSolver(std::string config_file);
FoldSolver& operator()() override;
// return value: QpointInReciprocalSuperCellByReciprocalSuperCell
static Eigen::Vector3d fold
(
Eigen::Vector3d qpoint_in_reciprocal_primitive_cell_by_reciprocal_primitive_cell,
Eigen::Vector<unsigned, 3> super_cell_multiplier,
std::optional<Eigen::Matrix<double, 3, 3>> super_cell_deformation
);
};
}

View File

@@ -1,77 +0,0 @@
# pragma once
# include <ufo/unfold.hpp>
namespace ufo
{
class PlotSolver : public Solver
{
public:
struct InputType
{
Eigen::Matrix3d PrimativeCell;
struct FigureConfigType
{
std::vector<std::vector<Eigen::Vector3d>> Qpoints;
std::pair<unsigned, unsigned> Resolution;
std::pair<double, double> Range;
std::optional<std::vector<double>> YTicks;
DataFile PictureFile;
std::optional<std::vector<DataFile>> DataFiles;
};
std::vector<FigureConfigType> Figures;
struct UnfoldedDataType : public UnfoldSolver::OutputType
{
UnfoldedDataType(std::string filename);
UnfoldedDataType() = default;
};
DataFile UnfoldedDataFile;
UnfoldedDataType UnfoldedData;
InputType(std::string config_file);
};
struct OutputType
{
std::vector<std::vector<double>> Values;
std::vector<double> XTicks;
std::vector<double> YTicks;
std::pair<unsigned, unsigned> Resolution;
std::pair<double, double> Range;
OutputType() = default;
const OutputType& write(std::string filename, std::string format) const;
using serialize = zpp::bits::members<5>;
};
protected:
InputType Input_;
std::optional<std::vector<OutputType>> Output_;
public:
PlotSolver(std::string config_file);
PlotSolver& operator()() override;
// 根据 q 点路径, 搜索要使用的 q 点
static std::vector<std::reference_wrapper<const UnfoldSolver::OutputType::QpointDataType>> search_qpoints
(
const std::pair<Eigen::Vector3d, Eigen::Vector3d>& path,
const decltype(InputType::UnfoldedDataType::QpointData)& available_qpoints,
double threshold, bool exclude_endpoint = false
);
// 根据搜索到的 q 点, 计算每个点的数值
static std::tuple<std::vector<std::vector<double>>, std::vector<double>> calculate_values
(
const Eigen::Matrix3d primative_cell,
const std::vector<std::pair<Eigen::Vector3d, Eigen::Vector3d>>& path,
const std::vector<std::vector<std::reference_wrapper<const UnfoldSolver::OutputType::QpointDataType>>>& qpoints,
const decltype(InputType::FigureConfigType::Resolution)& resolution,
const decltype(InputType::FigureConfigType::Range)& range
);
// 根据数值, 画图
static void plot
(
const std::vector<std::vector<double>>& values,
const std::string& filename,
const std::vector<double>& x_ticks, const std::vector<double>& y_ticks
);
};
}

View File

@@ -1,141 +0,0 @@
# 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>
# include <matplot/backend/opengl.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>{}}}; }
namespace detail_
{
template <typename T> struct SpecializationOfBitsMembersHelper : std::false_type {};
template <std::size_t N> struct SpecializationOfBitsMembersHelper<zpp::bits::members<N>> : std::true_type {};
}
template <typename T> concept ZppSerializable
= requires() { detail_::SpecializationOfBitsMembersHelper<T>::value == true; };
class Solver
{
public:
virtual Solver& operator()() = 0;
virtual ~Solver() = default;
static concurrencpp::generator<std::pair<Eigen::Vector<unsigned, 3>, unsigned>>
triplet_sequence(Eigen::Vector<unsigned, 3> range);
template <ZppSerializable T> inline static void zpp_write(const T& object, std::string filename)
{
auto [data, out] = zpp::bits::data_out();
out(object).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());
}
template <ZppSerializable T> inline static T zpp_read(std::string filename)
{
auto input = std::ifstream(filename, std::ios::binary | std::ios::in);
input.exceptions(std::ios::badbit | std::ios::failbit);
static_assert(sizeof(std::byte) == sizeof(char));
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);
T output;
in(output).or_throw();
return output;
}
class Hdf5file
{
public:
inline Hdf5file& open_for_read(std::string filename)
{
File_ = HighFive::File(filename, HighFive::File::ReadOnly);
return *this;
}
inline Hdf5file& open_for_write(std::string filename)
{
File_ = HighFive::File(filename, HighFive::File::ReadWrite | HighFive::File::Create
| HighFive::File::Truncate);
return *this;
}
template <typename T> inline Hdf5file& read(T& object, std::string name)
{
object = File_->getDataSet(name).read<std::remove_cvref_t<decltype(object)>>();
return *this;
}
template <typename T> inline Hdf5file& write(const T& object, std::string name)
{
File_->createDataSet(name, object);
return *this;
}
protected:
std::optional<HighFive::File> File_;
};
struct DataFile
{
std::string Filename;
std::string Format;
std::map<std::string, std::any> ExtraParameters;
inline DataFile() = default;
DataFile
(
YAML::Node node, std::set<std::string> supported_format,
std::string config_file, bool allow_same_as_config_file = false
);
};
};
}
HIGHFIVE_REGISTER_TYPE(ufo::PhonopyComplex, ufo::create_compound_complex)

View File

@@ -1,164 +0,0 @@
# 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;
// 从哪个文件读入 AtomPosition, 以及这个文件的格式, 格式可选值包括 "yaml"
DataFile AtomPositionInputFile;
// 从哪个文件读入 QpointData, 以及这个文件的格式, 格式可选值包括 "yaml" 和 "hdf5"
DataFile 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<DataFile> 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::SuperCellMultiplier)& super_cell_multiplier,
const decltype(InputType::SuperCellDeformation)& super_cell_deformation,
const std::vector<std::reference_wrapper<const decltype
(InputType::QpointDataType::Qpoint)>>& meta_qpoint_by_reciprocal_super_cell,
const std::vector<std::vector<std::reference_wrapper<const decltype
(InputType::QpointDataType::ModeDataType::Frequency)>>>& frequency,
const ProjectionCoefficientType_& projection_coefficient
);
};
}

View File

@@ -1,86 +1,56 @@
# include <ufo/fold.hpp>
# include <ufo.hpp>
namespace ufo
void ufo::fold(std::string config_file)
{
FoldSolver::InputType::InputType(std::string config_file)
struct Config
{
auto input = YAML::LoadFile(config_file);
for (unsigned i = 0; i < 3; i++)
SuperCellMultiplier(i) = input["SuperCellMultiplier"][i].as<unsigned>();
if (input["SuperCellDeformation"])
{
SuperCellDeformation.emplace();
for (unsigned i = 0; i < 3; i++)
for (unsigned j = 0; j < 3; j++)
(*SuperCellDeformation)(i, j) = input["SuperCellDeformation"][i][j].as<double>();
}
for (auto& qpoint : input["Qpoints"].as<std::vector<std::vector<double>>>())
Qpoints.push_back(Eigen::Vector3d
{{qpoint.at(0)}, {qpoint.at(1)}, {qpoint.at(2)}});
OutputFile = DataFile(input["OutputFile"], {"yaml"}, config_file);
}
void FoldSolver::OutputType::write(std::string filename) const
{
std::ofstream(filename) << [&]
{
std::stringstream print;
print << "Qpoints:\n";
for (auto& qpoint : Qpoints)
print << fmt::format(" - [ {:.8f}, {:.8f}, {:.8f} ]\n", qpoint(0), qpoint(1), qpoint(2));
return print.str();
}();
}
Eigen::Matrix3d SuperCellDeformation;
Eigen::Vector3i SuperCellMultiplier;
std::vector<Eigen::Vector3d> Qpoints;
};
FoldSolver::FoldSolver(std::string config_file) : Input_(config_file) {}
FoldSolver& FoldSolver::operator()()
{
if (!Output_)
{
Output_.emplace();
for (auto& qpoint : Input_.Qpoints)
Output_->Qpoints.push_back(fold
(
qpoint, Input_.SuperCellMultiplier,
Input_.SuperCellDeformation
));
}
Output_->write(Input_.OutputFile.Filename);
return *this;
}
Eigen::Vector3d FoldSolver::fold
auto fold = []
(
Eigen::Vector3d qpoint_in_reciprocal_primitive_cell_by_reciprocal_primitive_cell,
Eigen::Vector<unsigned, 3> super_cell_multiplier,
std::optional<Eigen::Matrix<double, 3, 3>> super_cell_deformation
)
Eigen::Matrix3d super_cell_deformation,
Eigen::Vector3i super_cell_multiplier,
Eigen::Vector3d qpoint_in_reciprocal_primitive_cell_by_reciprocal_primitive_cell
) -> Eigen::Vector3d
{
// 首先需要将 q 点转移到 ModifiedSuperCell 的倒格子中
// 将 q 点坐标扩大, 然后取小数部分, 就可以了
auto qpoint_by_reciprocal_modified_super_cell = super_cell_multiplier.cast<double>().asDiagonal()
* qpoint_in_reciprocal_primitive_cell_by_reciprocal_primitive_cell;
auto qpoint_in_reciprocal_modified_super_cell_by_reciprocal_modified_super_cell =
(qpoint_by_reciprocal_modified_super_cell.array() - qpoint_by_reciprocal_modified_super_cell.array().floor())
.matrix();
if (!super_cell_deformation)
return qpoint_in_reciprocal_modified_super_cell_by_reciprocal_modified_super_cell;
/*
对 q 点平移数个 SupreCell, 直到它落在超胞的倒格子中
这等价于直接将 q 点坐标用 SuperCell 的倒格子表示, 然后取小数部分.
ModifiedSuperCell = SuperCellMultiplier * PrimativeCell
SuperCell = SuperCellDeformation * ModifiedSuperCell
ReciprocalModifiedSuperCell = ModifiedSuperCell.inverse().transpose()
ReciprocalSuperCell = SuperCell.inverse().transpose()
Qpoint = QpointByReciprocalModifiedSuperCell.transpose() * ReciprocalModifiedSuperCell
Qpoint = QpointByReciprocalSuperCell.transpose() * ReciprocalSuperCell
首先需要将 q 点坐标的单位转换为 ModifiedSuperCell 的格矢,可知:
QpointByReciprocalModifiedSuperCell =
SuperCellMultiplier.cast<double>().asDiagonal() * QpointByReciprocalPrimitiveCell;
接下来考虑将 q 点坐标的单位转换为 SuperCell 的格矢
ModifiedSuperCell = SuperCellMultiplier.transpose() * PrimativeCell;
SuperCell = SuperCellDeformation * ModifiedSuperCell;
ReciprocalModifiedSuperCell = ModifiedSuperCell.inverse().transpose();
ReciprocalSuperCell = SuperCell.inverse().transpose();
Qpoint = QpointByReciprocalModifiedSuperCell.transpose() * ReciprocalModifiedSuperCell;
Qpoint = QpointByReciprocalSuperCell.transpose() * ReciprocalSuperCell;
整理可以得到:
QpointByReciprocalSuperCell = SuperCellDeformation * QpointByReciprocalModifiedSuperCell
QpointByReciprocalSuperCell = SuperCellDeformation * QpointByReciprocalModifiedSuperCell;
两个式子结合,可以得到:
QpointByReciprocalSuperCell =
SuperCellDeformation * SuperCellMultiplier.cast<double>().asDiagonal() * QpointByReciprocalPrimitiveCell;
*/
auto qpoint_in_reciprocal_modified_super_cell_by_reciprocal_super_cell =
(*super_cell_deformation * qpoint_in_reciprocal_modified_super_cell_by_reciprocal_modified_super_cell).eval();
auto qpoint_in_reciprocal_super_cell_by_reciprocal_super_cell =
qpoint_in_reciprocal_modified_super_cell_by_reciprocal_super_cell.array()
- qpoint_in_reciprocal_modified_super_cell_by_reciprocal_super_cell.array().floor();
return qpoint_in_reciprocal_super_cell_by_reciprocal_super_cell;
}
auto qpoint_in_reciprocal_primitive_cell_by_reciprocal_super_cell =
(
super_cell_deformation * super_cell_multiplier.cast<double>().asDiagonal()
* qpoint_in_reciprocal_primitive_cell_by_reciprocal_primitive_cell
).eval();
/*
到目前为止,我们还没有移动过 q 点的坐标。现在,我们将它移动整数个 ReciprocalSuperCell直到它落在超胞的倒格子中。
这等价于直接取 QpointByReciprocalSuperCell - QpointByReciprocalSuperCell.floor()。
*/
return (qpoint_in_reciprocal_primitive_cell_by_reciprocal_super_cell.array()
- qpoint_in_reciprocal_primitive_cell_by_reciprocal_super_cell.array().floor()).matrix();
};
biu::Logger::Guard log(config_file);
auto input = YAML::LoadFile(config_file).as<Config>();
for (const auto& qpoint : input.Qpoints) log.info("{} -> {}"_f
(
qpoint,
fold(input.SuperCellDeformation, input.SuperCellMultiplier, qpoint)
));
}

View File

@@ -1,17 +1,18 @@
# include <ufo/fold.hpp>
# include <ufo/unfold.hpp>
# include <ufo/plot.hpp>
# include <ufo.hpp>
int main(int argc, const char** argv)
{
if (argc != 3)
throw std::runtime_error(fmt::format("Usage: {} task config.yaml", argv[0]));
if (argv[1] == std::string("fold"))
ufo::FoldSolver{argv[2]}();
else if (argv[1] == std::string("unfold"))
ufo::UnfoldSolver{argv[2]}();
else if (argv[1] == std::string("plot"))
ufo::PlotSolver{argv[2]}();
else
throw std::runtime_error(fmt::format("Unknown task: {}", argv[1]));
using namespace biu::literals;
if (argv[1] == "fold"s) ufo::fold(argv[2]);
else if (argv[1] == "unfold"s) ufo::unfold(argv[2]);
else if (argv[1] == "zpp-to-yaml"s) std::cout << YAML::Node(biu::deserialize<ufo::CommonData>
(biu::read<std::byte>(std::cin)));
else if (argv[1] == "project-to-atom"s) ufo::project_to_atom(argv[2]);
else if (argv[1] == "project-to-mode"s) ufo::project_to_mode(argv[2]);
else if (argv[1] == "raman-create-displacement"s) ufo::raman_create_displacement(argv[2]);
else if (argv[1] == "raman-extract"s) ufo::raman_extract(argv[2]);
else if (argv[1] == "raman-apply-contribution"s) ufo::raman_apply_contribution(argv[2]);
else if (argv[1] == "plot-band"s) ufo::plot_band(argv[2]);
else if (argv[1] == "plot-point"s) ufo::plot_point(argv[2]);
else throw std::runtime_error("Unknown task: {}"_f(argv[1]));
}

View File

@@ -1,233 +1,179 @@
# include <ufo/plot.hpp>
# include <ufo.hpp>
# include <matplot/matplot.h>
# include <boost/container/flat_map.hpp>
namespace ufo
void ufo::plot_band(std::string config_file)
{
PlotSolver::InputType::UnfoldedDataType::UnfoldedDataType(std::string filename)
struct Config
{
static_cast<UnfoldSolver::OutputType&>(*this) = zpp_read<UnfoldSolver::OutputType>(filename);
}
std::string InputDataFile;
// 要画图的 q 点路径列表
// 内层表示一个路径上的 q 点,外层表示不同的路径
// 单位为倒格矢
std::vector<std::vector<Eigen::Vector3d>> Qpoints;
// 插值时使用的分辨率(不影响画出来图片的分辨率和横纵比)
std::array<std::size_t, 2> InterpolationResolution;
// 画图区域的y轴和x轴的比例。如果不指定则由matplot++自动调整(通常调整为正方形,即 1
std::optional<double> AspectRatio;
// 整张图片的分辨率
std::optional<std::array<std::size_t, 2>> PictureResolution;
// 画图的频率范围
std::array<double, 2> FrequencyRange;
// 搜索 q 点时的阈值,单位为埃^-1
std::optional<double> ThresholdWhenSearchingQpoints;
// 是否要在 y 轴上作一些标记
std::optional<std::vector<std::pair<double, std::string>>> YTicks;
// 是否输出图片
std::optional<std::string> OutputPictureFile;
// 是否输出数据,可以进一步使用 matplotlib 画图
std::optional<std::string> OutputDataFile;
};
PlotSolver::InputType::InputType(std::string config_file)
{
auto input = YAML::LoadFile(config_file);
for (unsigned i = 0; i < 3; i++)
for (unsigned j = 0; j < 3; j++)
PrimativeCell(i, j) = input["PrimativeCell"][i][j].as<double>();
for (auto& figure : input["Figures"].as<std::vector<YAML::Node>>())
{
Figures.emplace_back();
auto qpoints = figure["Qpoints"]
.as<std::vector<std::vector<std::vector<double>>>>();
for (auto& line : qpoints)
{
Figures.back().Qpoints.emplace_back();
for (auto& point : line)
Figures.back().Qpoints.back().emplace_back(point.at(0), point.at(1), point.at(2));
if (Figures.back().Qpoints.back().size() < 2)
throw std::runtime_error("Not enough points in a line");
}
if (Figures.back().Qpoints.size() < 1)
throw std::runtime_error("Not enough lines in a figure");
Figures.back().Resolution = figure["Resolution"].as<std::pair<unsigned, unsigned>>();
Figures.back().Range = figure["Range"].as<std::pair<double, double>>();
Figures.back().PictureFile
= DataFile(figure["PictureFile"], {"png"}, config_file);
if (figure["YTicks"])
Figures.back().YTicks = figure["YTicks"].as<std::vector<double>>();
if (figure["DataFiles"])
{
Figures.back().DataFiles.emplace();
for (auto& data_file : figure["DataFiles"].as<std::vector<YAML::Node>>())
Figures.back().DataFiles->emplace_back()
= DataFile(data_file, {"hdf5", "zpp"}, config_file);
}
}
UnfoldedDataFile = DataFile(input["UnfoldedDataFile"], {"zpp"}, config_file);
UnfoldedData = UnfoldedDataType(UnfoldedDataFile.Filename);
}
const PlotSolver::OutputType& PlotSolver::OutputType::write(std::string filename, std::string format) const
{
if (format == "zpp")
zpp_write(*this, filename);
else if (format == "hdf5")
{
std::vector resolution{ Resolution.first, Resolution.second };
std::vector range{ Range.first, Range.second };
Hdf5file{}.open_for_write(filename).write(Values, "Values")
.write(XTicks, "XTicks")
.write(YTicks, "YTicks")
.write(resolution, "Resolution")
.write(range, "Range");
}
return *this;
}
PlotSolver::PlotSolver(std::string config_file) : Input_(config_file) {}
PlotSolver& PlotSolver::operator()()
{
Output_.emplace();
for (auto& figure : Input_.Figures)
{
// 外层表示不同的线段的端点,内层表示这个线段上的 q 点
std::vector<std::vector<std::reference_wrapper<const UnfoldSolver::OutputType::QpointDataType>>> qpoints;
std::vector<std::pair<Eigen::Vector3d, Eigen::Vector3d>> lines;
for (auto& path : figure.Qpoints)
for (unsigned i = 0; i < path.size() - 1; i++)
{
lines.emplace_back(path[i], path[i + 1]);
qpoints.push_back(search_qpoints
(
lines.back(), Input_.UnfoldedData.QpointData,
0.001, i != path.size() - 2
));
}
auto [values, x_ticks] = calculate_values
(
Input_.PrimativeCell, lines, qpoints, figure.Resolution, figure.Range
);
auto y_ticks = figure.YTicks.value_or(std::vector<double>{});
for (auto& _ : y_ticks)
_ = (_ - figure.Range.first) / (figure.Range.second - figure.Range.first) * figure.Resolution.second;
plot(values, figure.PictureFile.Filename, x_ticks, y_ticks);
Output_->emplace_back();
Output_->back().Values = std::move(values);
Output_->back().XTicks = std::move(x_ticks);
Output_->back().YTicks = std::move(y_ticks);
Output_->back().Resolution = figure.Resolution;
Output_->back().Range = figure.Range;
if (figure.DataFiles)
for (auto& data_file : *figure.DataFiles)
Output_->back().write(data_file.Filename, data_file.Format);
}
return *this;
}
std::vector<std::reference_wrapper<const UnfoldSolver::OutputType::QpointDataType>> PlotSolver::search_qpoints
// 根据 q 点路径, 搜索要使用的 q 点,返回的是超胞中 Qpint 的索引和 SubQpoint 的索引,以及到路径起点的距离,以及这段路径的总长度
auto search_qpoints = []
(
const Eigen::Matrix3d& primative_cell,
const std::pair<Eigen::Vector3d, Eigen::Vector3d>& path,
const decltype(InputType::UnfoldedDataType::QpointData)& available_qpoints,
double threshold, bool exclude_endpoint
const std::vector<std::vector<Eigen::Vector3d>>& qpoints,
double threshold, bool exclude_endpoint = false
)
{
std::multimap<double, std::reference_wrapper<const UnfoldSolver::OutputType::QpointDataType>> selected_qpoints;
// 对于 output 中的每一个点, 检查这个点是否在路径上. 如果在, 把它加入到 selected_qpoints 中
for (auto& qpoint : available_qpoints)
{
// 计算三点围成的三角形的面积的两倍
auto area = (path.second - path.first).cross(qpoint.Qpoint - path.first).norm();
// 计算这个点到前两个点所在直线的距离
auto distance = area / (path.second - path.first).norm();
// 如果这个点到前两个点所在直线的距离小于阈值, 则认为这个点在路径上
if (distance < threshold)
// 键为这个点到起点的距离
boost::container::flat_map<double, std::pair<std::size_t, std::size_t>> selected_qpoints;
auto begin = (path.first.transpose() * primative_cell.reverse()).transpose().eval();
auto end = (path.second.transpose() * primative_cell.reverse()).transpose().eval();
for (std::size_t i = 0; i < qpoints.size(); i++) for (std::size_t j = 0; j < qpoints[i].size(); j++)
for (auto cell_shift
: biu::sequence(Eigen::Vector3i(-1, -1, -1), Eigen::Vector3i(2, 2, 2)))
{
// 计算这个点到前两个点的距离, 两个距离都应该小于两点之间的距离
auto distance1 = (qpoint.Qpoint - path.first).norm();
auto distance2 = (qpoint.Qpoint - path.second).norm();
auto distance3 = (path.second - path.first).norm();
if (distance1 < distance3 + threshold && distance2 < distance3 + threshold)
// 如果这个点不在终点处, 或者不排除终点, 则加入
if (distance2 > threshold || !exclude_endpoint)
selected_qpoints.emplace(distance1, std::ref(qpoint));
Eigen::Vector3d qpoint
= primative_cell.reverse().transpose() * (qpoints[i][j] + cell_shift.first.cast<double>());
// 计算这个点到前两个点所在直线的距离
auto distance = (end - begin).cross(qpoint - begin).norm()
/ (path.second - path.first).norm();
// 如果这个点到前两个点所在直线的距离小于阈值, 则认为这个点在这条直线上,但不一定在这两个点之间
if (distance < threshold)
{
// 计算这个点到前两个点的距离, 两个距离都应该小于两点之间的距离
auto distance1 = (qpoint - begin).norm();
auto distance2 = (qpoint - end).norm();
auto distance3 = (end - begin).norm();
if (distance1 < distance3 + threshold && distance2 < distance3 + threshold)
// 如果这个点不在终点处, 或者不排除终点, 则加入
if (distance2 > threshold || !exclude_endpoint)
selected_qpoints.emplace(distance1, std::pair{i, j});
}
}
}
// 去除非常接近的点
for (auto it = selected_qpoints.begin(); it != selected_qpoints.end();)
{
auto next = std::next(it);
if (next == selected_qpoints.end())
break;
else if (next->first - it->first < threshold)
selected_qpoints.erase(next);
else
it = next;
if (next == selected_qpoints.end()) break;
else if (next->first - it->first < threshold) selected_qpoints.erase(next);
else it = next;
}
if (selected_qpoints.empty())
throw std::runtime_error("No q points found");
std::vector<std::reference_wrapper<const UnfoldSolver::OutputType::QpointDataType>> result;
for (auto& qpoint : selected_qpoints)
result.push_back(qpoint.second);
return result;
}
if (selected_qpoints.empty()) throw std::runtime_error("No q points found");
return std::make_pair(selected_qpoints, (end - begin).norm());
};
std::tuple<std::vector<std::vector<double>>, std::vector<double>> PlotSolver::calculate_values
// 根据搜索到的 q 点, 计算图中每个点的值
auto calculate_values = []
(
const Eigen::Matrix3d primative_cell,
const std::vector<std::pair<Eigen::Vector3d, Eigen::Vector3d>>& path,
const std::vector<std::vector<std::reference_wrapper<const UnfoldSolver::OutputType::QpointDataType>>>& qpoints,
const decltype(InputType::FigureConfigType::Resolution)& resolution,
const decltype(InputType::FigureConfigType::Range)& range
// search_qpoints 的第一个返回值
const boost::container::flat_map<double, std::pair<std::size_t, std::size_t>>& path,
// 每一条连续路径的第一个 q 点的索引
const std::set<std::size_t>& path_begin,
// 各个模式的频率和权重几个维度分别为MetaQpointIndex, ModeIndex
const std::vector<std::vector<double>>& frequency,
// 各个模式的权重几个维度分别为MetaQpointIndex, ModeIndex, SubQpointIndex
const std::vector<std::vector<std::vector<double>>>& weight,
// 用于插值的分辨率和范围
const std::array<std::size_t, 2>& resolution,
const std::array<double, 2>& frequency_range,
// 路径的总长度
double total_distance
)
{
// 整理输入
std::map<double, std::reference_wrapper<const UnfoldSolver::OutputType::QpointDataType>> qpoints_with_distance;
double total_distance = 0;
std::vector<double> x_ticks;
for (unsigned i = 0; i < path.size(); i++)
{
for (auto& _ : qpoints[i])
qpoints_with_distance.emplace
(
total_distance
+ ((_.get().Qpoint - path[i].first).transpose() * primative_cell.inverse().transpose()).norm(),
_
);
total_distance += ((path[i].second - path[i].first).transpose() * primative_cell.inverse().transpose()).norm();
if (i != path.size() - 1)
x_ticks.push_back(total_distance);
}
for (auto& _ : x_ticks)
_ = _ / total_distance * resolution.first;
// 插值
std::vector<std::vector<double>> values;
auto blend = []
// 按比例混合两个 q 点的结果,得到可以用于画图的那一列数据
auto blend = [&]
(
const UnfoldSolver::OutputType::QpointDataType& a,
const UnfoldSolver::OutputType::QpointDataType& b,
double ratio, unsigned resolution, std::pair<double, double> range
// 两个点的索引
std::pair<std::size_t, std::size_t> a, std::pair<std::size_t, std::size_t> b,
// 按照连续路径混合还是按照断开的路径混合
bool continuous,
// 第一个点占的比例
double ratio,
std::size_t resolution, std::array<double, 2> frequency_range
) -> std::vector<double>
{
// 计算插值结果
std::vector<double> frequency, weight;
for (unsigned i = 0; i < a.ModeData.size(); i++)
// 混合得到的频率和权重
std::vector<double> frequency_result, weight_result;
// 如果是连续路径,将每个模式的频率和权重按照比例混合
if (continuous)
{
frequency.push_back(a.ModeData[i].Frequency * ratio + b.ModeData[i].Frequency * (1 - ratio));
weight.push_back(a.ModeData[i].Weight * ratio + b.ModeData[i].Weight * (1 - ratio));
for (std::size_t i = 0; i < frequency[a.first].size(); i++)
{
frequency_result.push_back(frequency[a.first][i] * ratio + frequency[b.first][i] * (1 - ratio));
weight_result.push_back(weight[a.first][i][a.second] * ratio + weight[b.first][i][b.second] * (1 - ratio));
}
}
// 如果是不连续路径,将每个模式的权重乘以比例,最后相加
else
{
for (std::size_t i = 0; i < frequency[a.first].size(); i++)
{
frequency_result.push_back(frequency[a.first][i]);
weight_result.push_back(weight[a.first][i][a.second] * ratio);
}
for (std::size_t i = 0; i < frequency[b.first].size(); i++)
{
frequency_result.push_back(frequency[b.first][i]);
weight_result.push_back(weight[b.first][i][b.second] * (1 - ratio));
}
}
std::vector<double> result(resolution);
for (unsigned i = 0; i < frequency.size(); i++)
for (std::size_t i = 0; i < frequency_result.size(); i++)
{
int index = (frequency[i] - range.first) / (range.second - range.first) * resolution;
if (index >= 0 && index < static_cast<int>(resolution))
result[index] += weight[i];
std::ptrdiff_t index = (frequency_result[i] - frequency_range[0]) / (frequency_range[1] - frequency_range[0])
* resolution;
if (index >= 0 && index < static_cast<std::ptrdiff_t>(resolution)) result[index] += weight_result[i];
}
return result;
};
for (unsigned i = 0; i < resolution.first; i++)
std::vector<std::vector<double>> values;
for (std::size_t i = 0; i < resolution[0]; i++)
{
auto current_distance = total_distance * i / resolution.first;
auto it = qpoints_with_distance.lower_bound(current_distance);
if (it == qpoints_with_distance.begin())
values.push_back(blend(it->second.get(), it->second.get(), 1, resolution.second, range));
else if (it == qpoints_with_distance.end())
values.push_back(blend(std::prev(it)->second.get(), std::prev(it)->second.get(), 1, resolution.second,
range));
else
values.push_back(blend
(
std::prev(it)->second.get(), it->second.get(),
(it->first - current_distance) / (it->first - std::prev(it)->first),
resolution.second, range)
);
auto current_distance = total_distance * i / resolution[0];
auto it = path.lower_bound(current_distance);
if (it == path.begin()) values.push_back(blend
(it->second, it->second, true, 1, resolution[1], frequency_range));
else if (it == path.end()) values.push_back(blend
(
std::prev(it)->second, std::prev(it)->second, true, 1,
resolution[1], frequency_range
));
else values.push_back(blend
(
std::prev(it)->second, it->second,
!path_begin.contains(std::distance(path.begin(), it)),
(it->first - current_distance) / (it->first - std::prev(it)->first),
resolution[1], frequency_range
));
}
return {values, x_ticks};
}
void PlotSolver::plot
return values;
};
// 根据数值, 画图
auto plot = []
(
const std::vector<std::vector<double>>& values,
const std::string& filename,
const std::vector<double>& x_ticks, const std::vector<double>& y_ticks
const std::vector<double>& x_ticks, const std::vector<double>& y_ticks,
const std::vector<std::string>& y_ticklabels,
const std::optional<double>& aspect_ratio,
const std::optional<std::array<std::size_t, 2>>& resolution
)
{
std::vector<std::vector<double>>
@@ -235,32 +181,285 @@ namespace ufo
g(values[0].size(), std::vector<double>(values.size(), 0)),
b(values[0].size(), std::vector<double>(values.size(), 0)),
a(values[0].size(), std::vector<double>(values.size(), 0));
for (unsigned i = 0; i < values[0].size(); i++)
for (unsigned j = 0; j < values.size(); j++)
for (std::size_t i = 0; i < values[0].size(); i++)
for (std::size_t j = 0; j < values.size(); j++)
{
auto v = values[j][i];
if (v < 0.05)
v = 0;
if (v < 0.05) v = 0;
a[i][j] = v * 100 * 255;
if (a[i][j] > 255)
a[i][j] = 255;
if (a[i][j] > 255) a[i][j] = 255;
r[i][j] = 255 - v * 2 * 255;
if (r[i][j] < 0)
r[i][j] = 0;
if (r[i][j] < 0) r[i][j] = 0;
g[i][j] = 255 - v * 2 * 255;
if (g[i][j] < 0)
g[i][j] = 0;
if (g[i][j] < 0) g[i][j] = 0;
b[i][j] = 255;
}
auto f = matplot::figure<matplot::backend::gnuplot>(true);
auto f = matplot::figure(true);
auto ax = f->current_axes();
auto image = ax->image(std::tie(r, g, b));
image->matrix_a(a);
ax->y_axis().reverse(false);
ax->x_axis().tick_values(x_ticks);
ax->x_axis().tick_length(1);
ax->x_axis().ticklabels(std::vector<std::string>(x_ticks.size()));
ax->y_axis().tick_values(y_ticks);
ax->y_axis().tick_length(1);
ax->y_axis().ticklabels(y_ticklabels);
if (aspect_ratio)
{
ax->axes_aspect_ratio_auto(false);
ax->axes_aspect_ratio(*aspect_ratio);
}
if (resolution)
{
f->width((*resolution)[0]);
f->height((*resolution)[1]);
}
f->save(filename, "png");
};
auto config = YAML::LoadFile(config_file).as<Config>();
auto input = biu::deserialize<CommonData>
(biu::read<std::byte>(config.InputDataFile));
// 搜索画图需要用到的 q 点
// key 到起点的距离value 为 q 点在 QpointData 中的索引
boost::container::flat_map<double, std::pair<std::size_t, std::size_t>> path;
// 每一条连续路径的第一个 q 点在 path 中的索引
std::set<std::size_t> path_begin;
// x 轴的刻度,为 path 中的索引
std::set<std::size_t> x_ticks_index;
double total_distance = 0;
for (auto& line : config.Qpoints)
{
assert(line.size() >= 2);
path_begin.insert(path.size());
for (std::size_t i = 0; i < line.size() - 1; i++)
{
x_ticks_index.insert(path.size());
auto [this_path, this_distance] = search_qpoints
(
input.Primative.Cell, {line[i], line[i + 1]},
input.Super.Qpoint
| ranges::views::transform([](auto& qpoint) { return qpoint.SubQpoint; })
| ranges::to_vector,
config.ThresholdWhenSearchingQpoints.value_or(0.001),
i != line.size() - 2
);
path.merge
(
this_path
| ranges::views::transform([&](auto& p)
{ return std::make_pair(p.first + total_distance, p.second); })
| ranges::to<boost::container::flat_map>
);
total_distance += this_distance;
}
}
// 计算画图的数据
auto values = calculate_values
(
path, path_begin,
input.Super.Qpoint
| ranges::views::transform([](auto& qpoint)
{
return qpoint.Mode | ranges::views::transform([](auto&& mode) { return mode.Frequency; }) | ranges::to_vector;
})
| ranges::to_vector,
input.Super.Qpoint
| ranges::views::transform([](auto& qpoint)
{
return qpoint.Mode
| ranges::views::transform([](auto&& mode)
{
return mode.WeightOnUnfold
| ranges::views::transform([&](auto&& weight)
{ return weight * mode.WeightOnSelectedAtom.value_or(1) * mode.WeightOnRaman.value_or(1); })
| ranges::to_vector;
})
| ranges::to_vector;
})
| ranges::to_vector,
config.InterpolationResolution, config.FrequencyRange, total_distance
);
auto x_ticks = x_ticks_index | ranges::views::transform([&](auto i)
{ return path.nth(i)->first / total_distance * config.InterpolationResolution[0]; }) | ranges::to_vector;
auto y_ticks = config.YTicks.value_or(std::vector<std::pair<double, std::string>>{})
| biu::toLvalue | ranges::views::keys
| ranges::views::transform([&](auto i)
{
return (i - config.FrequencyRange[0]) / (config.FrequencyRange[1] - config.FrequencyRange[0])
* config.InterpolationResolution[1];
})
| ranges::to_vector;
auto y_ticklabels = config.YTicks.value_or(std::vector<std::pair<double, std::string>>{})
| biu::toLvalue | ranges::views::values | ranges::to_vector;
if (config.OutputPictureFile) plot
(
values, config.OutputPictureFile.value(),
x_ticks, y_ticks, y_ticklabels, config.AspectRatio, config.PictureResolution
);
if (config.OutputDataFile)
biu::Hdf5file(config.OutputDataFile.value(), true)
.write("Values", values)
.write("XTicks", x_ticks)
.write("YTicks", y_ticks)
.write("YTickLabels", y_ticklabels)
.write("InterpolationResolution", config.InterpolationResolution)
.write("FrequencyRange", config.FrequencyRange);
}
void ufo::plot_point(std::string config_file)
{
struct Config
{
std::string UnfoldedDataFile;
// 要画图的 q 点
Eigen::Vector3d Qpoint;
// 插值的分辨率
std::size_t InterpolationResolution;
std::optional<double> AspectRatio;
std::optional<std::array<std::size_t, 2>> PictureResolution;
// 画图的频率范围
std::array<double, 2> FrequencyRange;
// 搜索 q 点时的阈值,单位为埃^-1
std::optional<double> ThresholdWhenSearchingQpoints;
// 是否要在 z 轴上作一些标记
std::optional<std::vector<std::pair<double, std::string>>> XTicks;
// 是否输出图片
std::optional<std::string> OutputPictureFile;
// 是否输出插值后数据,可以进一步使用 matplotlib 画图
std::optional<std::string> OutputDataFile;
// 是否输出插值前数据,可以配合 phonopy 结果深入研究
std::optional<std::string> OutputRawDataFile;
};
// 根据 q 点路径, 搜索要使用的 q 点,返回的是 q 点在 QpointData 中的索引
auto search_qpoints = []
(
const Eigen::Matrix3d& primative_cell,
const Eigen::Vector3d& qpoint, const std::vector<std::vector<Eigen::Vector3d>>& qpoints,
double threshold
)
{
biu::Logger::Guard log(qpoint);
// 对于 output 中的每一个点, 检查这个点是否与所寻找的点足够近,如果足够近则返回
for (std::size_t i = 0; i < qpoints.size(); i++) for (std::size_t j = 0; j < qpoints[i].size(); j++)
for (auto cell_shift
: biu::sequence(Eigen::Vector3i(-1, -1, -1), Eigen::Vector3i(2, 2, 2)))
{
auto this_qpoint
= (primative_cell.reverse().transpose() * (qpoints[i][j] + cell_shift.first.cast<double>())).eval();
if ((this_qpoint - primative_cell.reverse().transpose() * qpoint).norm() < threshold)
return log.rtn(std::pair(i, j));
}
throw std::runtime_error("No q points found");
};
// 根据搜索到的 q 点, 计算图中每个点的值
auto calculate_values = []
(
// q 点的数据(需要用到它的频率和权重)
const std::vector<double>& frequency,
const std::vector<double>& weight,
// 用于插值的分辨率和范围
std::size_t resolution,
const std::array<double, 2>& frequency_range
)
{
biu::Logger::Guard log;
std::vector<double> result(resolution);
for (std::size_t i = 0; i < frequency.size(); i++)
{
double index_double = (frequency[i] - frequency_range[0]) / (frequency_range[1] - frequency_range[0])
* (resolution - 1);
std::ptrdiff_t index = std::round(index_double);
if (index >= 0 && index < static_cast<std::ptrdiff_t>(resolution)) result[index] += weight[i];
}
return log.rtn(result);
};
// 根据数值, 画图
auto plot = []
(
const std::vector<double>& values, const std::string& filename,
const std::vector<double>& x_ticks, const std::vector<std::string>& x_ticklabels,
const std::optional<double>& aspect_ratio, const std::optional<std::array<std::size_t, 2>>& resolution
)
{
biu::Logger::Guard log;
auto f = matplot::figure(true);
auto ax = f->current_axes();
auto image = ax->area(values, 0, false, "");
ax->y_axis().reverse(false);
ax->x_axis().tick_values(x_ticks);
ax->x_axis().tick_length(1);
ax->x_axis().ticklabels(x_ticklabels);
ax->y_axis().tick_values({});
if (aspect_ratio)
{
ax->axes_aspect_ratio_auto(false);
ax->axes_aspect_ratio(*aspect_ratio);
}
if (resolution)
{
f->width((*resolution)[0]);
f->height((*resolution)[1]);
}
f->save(filename, "png");
};
biu::Logger::Guard log;
auto config = YAML::LoadFile(config_file).as<Config>();
auto input = biu::deserialize<CommonData>
(biu::read<std::byte>(config.UnfoldedDataFile));
auto qpoint_index = search_qpoints
(
input.Primative.Cell, config.Qpoint,
input.Super.Qpoint
| ranges::views::transform([](auto& qpoint) { return qpoint.SubQpoint; })
| ranges::to_vector,
config.ThresholdWhenSearchingQpoints.value_or(0.001)
);
auto values = calculate_values
(
input.Super.Qpoint[qpoint_index.first].Mode
| ranges::views::transform([](auto&& mode) { return mode.Frequency; })
| ranges::to_vector,
input.Super.Qpoint[qpoint_index.first].Mode
| ranges::views::transform([&](auto&& mode)
{
return mode.WeightOnUnfold[qpoint_index.second]
* mode.WeightOnSelectedAtom.value_or(1) * mode.WeightOnRaman.value_or(1);
})
| ranges::to_vector,
config.InterpolationResolution, config.FrequencyRange
);
auto x_ticks = config.XTicks.value_or(std::vector<std::pair<double, std::string>>{})
| biu::toLvalue | ranges::views::keys
| ranges::views::transform([&](auto i)
{
return (i - config.FrequencyRange[0]) / (config.FrequencyRange[1] - config.FrequencyRange[0])
* config.InterpolationResolution;
})
| ranges::to_vector;
auto x_ticklabels = config.XTicks.value_or(std::vector<std::pair<double, std::string>>{})
| biu::toLvalue | ranges::views::values | ranges::to_vector;
if (config.OutputPictureFile) plot
(
values, config.OutputPictureFile.value(),
x_ticks, x_ticklabels, config.AspectRatio, config.PictureResolution
);
if (config.OutputDataFile)
biu::Hdf5file(config.OutputDataFile.value(), true)
.write("Values", values)
.write("XTicks", x_ticks)
.write("XTickLabels", x_ticklabels)
.write("InterpolationResolution", config.InterpolationResolution)
.write("FrequencyRange", config.FrequencyRange);
// TODO: pfr + optional + Eigen Matrix = failed
if (config.OutputRawDataFile)
std::ofstream(*config.OutputRawDataFile) << YAML::Node(values);
}

36
src/project-to-atom.cpp Normal file
View File

@@ -0,0 +1,36 @@
# include <ufo.hpp>
# include <ranges>
void ufo::project_to_atom(std::string config_file)
{
struct Config
{
std::set<std::size_t> SelectedAtom;
std::string InputFile;
std::string OutputFile;
};
biu::Logger::Guard log(config_file);
auto config = YAML::LoadFile(config_file).as<Config>();
auto input = biu::deserialize<CommonData>(biu::read<std::byte>(config.InputFile));
input.SelectedAtom = config.SelectedAtom;
for (auto& qpoint : input.Super.Qpoint) for (auto& mode : qpoint.Mode)
mode.WeightOnSelectedAtom = mode.EigenVector.rowwise().squaredNorm().cwiseProduct
(
ranges::views::iota(0ul, input.Super.AtomPosition.rows() * 1ul)
| ranges::views::transform([&](auto i)
{ return config.SelectedAtom.contains(i) ? 1. : 0.; })
| ranges::to_vector | biu::toEigen<>
).sum() * (input.Super.AtomPosition.rows() * 1. / config.SelectedAtom.size());
std::ofstream(config.OutputFile, std::ios::binary) << biu::serialize<char>(input);
log.info("Summary:");
for (auto i_of_qpoint : std::views::iota(0ul, input.Super.Qpoint.size()))
for (auto i_of_mode : std::views::iota(0ul, input.Super.Qpoint[i_of_qpoint].Mode.size()))
if (*input.Super.Qpoint[i_of_qpoint].Mode[i_of_mode].WeightOnSelectedAtom > 0.1)
log.info("{}:{:.2f}:{}:{:.2f}"_f
(
i_of_qpoint, fmt::join(input.Super.Qpoint[i_of_qpoint].Qpoint, ", "),
i_of_mode, input.Super.Qpoint[i_of_qpoint].Mode[i_of_mode].Frequency,
*input.Super.Qpoint[i_of_qpoint].Mode[i_of_mode].WeightOnSelectedAtom
));
}

133
src/project-to-mode.cpp Normal file
View File

@@ -0,0 +1,133 @@
# include <ufo.hpp>
void ufo::project_to_mode(std::string config_file)
{
// 将选定的超胞中的某一个 q 点上的每一个模式到反折叠后的某个 q 点的投影,投射到单胞中的对应 q 点的模式上
struct Config
{
// 在单胞内取几个平面波的基矢
Eigen::Vector<std::size_t, 3> PrimativeCellBasisNumber;
// 要选定的超胞中的 q 点sub qpoint以及单胞中的 q 点
std::size_t SuperQpointIndex, SubQpointIndex, PrimativeQpointIndex;
// 输入输出文件的路径,直接输出 yaml因为这些数据不再需要后处理
std::string InputDataFile, OutputFile;
};
struct Output
{
Eigen::Vector3d SuperQpoint, SubQpoint, PrimativeQpoint;
std::vector<std::vector<double>> Coefficient;
};
biu::Logger::Guard log(config_file);
auto config = YAML::LoadFile(config_file).as<Config>();
auto input = biu::deserialize<CommonData>
(biu::read<std::byte>(config.InputDataFile));
Output output
{
.SuperQpoint = input.Super.Qpoint[config.SuperQpointIndex].Qpoint,
.SubQpoint = input.Super.Qpoint[config.SuperQpointIndex].SubQpoint[config.SubQpointIndex],
.PrimativeQpoint = input.Primative.Qpoint[config.PrimativeQpointIndex].Qpoint,
.Coefficient = std::vector<std::vector<double>>(input.Super.Qpoint[config.SuperQpointIndex].Mode.size(),
std::vector<double>(input.Primative.Qpoint[config.PrimativeQpointIndex].Mode.size()))
};
if ((output.SubQpoint - output.PrimativeQpoint).norm() > 0.01)
log.error("sub qpoint {} != primative qpoint {}"_f(output.SubQpoint, output.PrimativeQpoint));
// 基在原胞中的表示
auto primative_basis = [&]
{
biu::Logger::Guard log;
std::vector<Eigen::VectorXcd> basis(config.PrimativeCellBasisNumber.array().prod());
for (auto [xyz_of_basis, i_of_basis]
: biu::sequence(config.PrimativeCellBasisNumber))
{
// 波矢就是 xyz_of_basis, 单位为单胞的倒格矢
// 将它的单位转换成埃^-1
auto wavevector =
(xyz_of_basis.transpose().cast<double>() * input.Primative.Cell.inverse().transpose()).transpose().eval();
log.debug("xyz_of_basis: {}"_f(xyz_of_basis));
log.debug("wavevector: {:.3f}"_f(fmt::join(wavevector, ", ")));
// 将原子坐标单位也转换为埃
auto atom_position = (input.Primative.AtomPosition * input.Primative.Cell).eval();
// 计算基矢
basis[i_of_basis] = (2i * std::numbers::pi_v<double> * (atom_position * wavevector)).array().exp();
}
return basis;
}();
// 基在超胞中的表示
auto super_basis = [&]
{
biu::Logger::Guard log;
std::vector<Eigen::VectorXcd> basis(config.PrimativeCellBasisNumber.array().prod());
auto diff_of_sub_qpoint_by_reciprocal_modified_super_cell = [&]
{
for
(
auto [diff_of_sub_qpoint_by_reciprocal_modified_super_cell, i_of_sub_qpoint]
: biu::sequence(input.Super.CellMultiplier)
)
if (i_of_sub_qpoint == config.SubQpointIndex) return diff_of_sub_qpoint_by_reciprocal_modified_super_cell;
std::unreachable();
}();
log.debug("diff_of_sub_qpoint_by_reciprocal_modified_super_cell: {}"_f
(diff_of_sub_qpoint_by_reciprocal_modified_super_cell));
for (auto [xyz_of_basis, i_of_basis]
: biu::sequence(config.PrimativeCellBasisNumber.cast<int>().eval()))
{
// 计算波矢, 单位为单胞的倒格矢
auto wavevector_by_reciprocal_primative_cell = xyz_of_basis.cast<double>()
+ input.Super.CellMultiplier.cast<double>().cwiseInverse().asDiagonal()
* diff_of_sub_qpoint_by_reciprocal_modified_super_cell.cast<double>();
// 将单位转换为埃^-1
auto wavevector = input.Primative.Cell.inverse() * wavevector_by_reciprocal_primative_cell;
log.debug("wavevector: {}"_f(wavevector));
// 将原子坐标单位也转换为埃
auto atom_translation =
(input.Super.AtomTranslation.value_or(std::array{0., 0., 0.}) | biu::toEigen<>)
.transpose().replicate(input.Super.AtomPosition.rows(), 1).eval();
Eigen::MatrixX3d atom_position = (input.Super.AtomPosition - atom_translation)
* (input.Super.CellDeformation * input.Super.CellMultiplier.cast<double>().asDiagonal() * input.Primative.Cell);
// 计算基矢
basis[i_of_basis] = (2i * std::numbers::pi_v<double> * (atom_position * wavevector)).array().exp();
}
return basis;
}();
// 将所有模式投影到基矢上
auto primative_projection_coefficient =
input.Primative.Qpoint[config.PrimativeQpointIndex].Mode
| ranges::views::transform([&](const auto& mode)
{
return primative_basis
| ranges::views::transform([&](const auto& basis)
{ return (basis.transpose().conjugate() * mode.EigenVector).eval() | biu::fromEigen; })
| ranges::to_vector
| biu::toEigen<>;
})
| ranges::to_vector;
auto super_projection_coefficient =
input.Super.Qpoint[config.SuperQpointIndex].Mode
| ranges::views::transform([&](const auto& mode)
{
return super_basis
| ranges::views::transform([&](const auto& basis)
{ return (basis.transpose().conjugate() * mode.EigenVector).eval() | biu::fromEigen; })
| ranges::to_vector
| biu::toEigen<>;
})
| ranges::to_vector;
// 将它们互相投影,得到结果
for (auto [i_of_super_mode, super_mode] : ranges::views::enumerate(super_projection_coefficient))
{
for (auto [i_of_primative_mode, primative_mode]
: ranges::views::enumerate(primative_projection_coefficient))
output.Coefficient[i_of_super_mode][i_of_primative_mode] =
std::norm((super_mode.transpose().conjugate() * primative_mode).trace());
auto sum = ranges::accumulate(output.Coefficient[i_of_super_mode], 0.);
for (auto& coefficient : output.Coefficient[i_of_super_mode])
coefficient *=
input.Super.Qpoint[config.SuperQpointIndex].Mode[i_of_super_mode].WeightOnUnfold[config.SubQpointIndex] / sum;
}
// 输出
std::ofstream(config.OutputFile) << YAML::Node(output);
}

197
src/raman.cpp Normal file
View File

@@ -0,0 +1,197 @@
# include <ufo.hpp>
namespace ufo
{
struct RamanData
{
std::map<std::pair<std::size_t, std::size_t>, double> ModeRatio;
double MaxDisplacement;
enum { Primative, Super } Cell;
using serialize = zpp::bits::members<3>;
};
void raman_create_displacement(std::string config_file)
{
struct Config
{
// 要计算的是原胞还是超胞
decltype(RamanData::Cell) Cell;
// 要计算哪个 q 点的哪些模式,总是假定是 gamma 点的模式
std::map<std::size_t, std::set<std::size_t>> SelectedModes;
// 原子最大位移大小,单位为埃
double MaxDisplacement;
// 输出的POSCAR所在的目录
std::string OutputPoscarDirectory;
// 输入的数据文件名
std::string InputDataFile;
std::string OutputDataFile;
};
auto generate_poscar = []
(
Eigen::Matrix3d SuperCell, Eigen::MatrixX3d AtomPosition,
std::vector<std::pair<std::string, std::size_t>> AtomType
)
{
std::stringstream ss;
ss << "some random comment to make VASP happy\n1.0\n";
for (std::size_t i = 0; i < 3; i++)
{
for (std::size_t j = 0; j < 3; j++) ss << SuperCell(i, j) << " ";
ss << std::endl;
}
ss << "{}\n"_f(ranges::accumulate
(
AtomType | ranges::views::transform([](auto&& atom) { return atom.first; }),
""s, [](auto&& a, auto&& b) { return a + " " + b; }
));
ss << "{}\n"_f(ranges::accumulate
(
AtomType | ranges::views::transform([](auto&& atom) { return atom.second; }),
""s, [](auto&& a, auto&& b) { return a + " " + std::to_string(b); }
));
ss << "Direct\n";
for (const auto& position : AtomPosition.rowwise())
{
for (std::size_t i = 0; i < 3; i++) ss << position(i) << " ";
ss << std::endl;
}
return ss.str();
};
biu::Logger::Guard log(config_file);
auto config = YAML::LoadFile(config_file).as<Config>();
auto input = biu::deserialize<CommonData>
(biu::read<std::byte>(config.InputDataFile));
RamanData output;
output.MaxDisplacement = config.MaxDisplacement;
output.Cell = config.Cell;
auto process = [&](auto& cell)
{
auto mass = cell.AtomType
| ranges::views::transform([&](auto&& atom)
{ return ranges::views::repeat_n(input.AtomMass[atom.first], atom.second); })
| ranges::views::join | ranges::to_vector | biu::toEigen<>;
for (auto i_of_qpoint : config.SelectedModes | ranges::views::keys)
for (auto i_of_mode : config.SelectedModes[i_of_qpoint])
{
// 假定虚部总是为零
auto move_eigenvector = cell.Qpoint[i_of_qpoint].Mode[i_of_mode].EigenVector.real()
.cwiseProduct(mass.cwiseSqrt().cwiseInverse().rowwise().replicate(3)).eval();
// 归一化
auto ratio = config.MaxDisplacement / move_eigenvector.rowwise().norm().maxCoeff();
auto atom_movement = (move_eigenvector * ratio).eval();
// 输出
for (auto direction : {1, -1})
{
auto path = "{}/{}/{}/{}"_f
(config.OutputPoscarDirectory, i_of_qpoint, i_of_mode, direction > 0 ? "+" : "-");
std::filesystem::create_directories(path);
std::ofstream("{}/POSCAR"_f(path)) << generate_poscar
(cell.Cell, cell.AtomPosition + atom_movement * cell.Cell.inverse() * direction, cell.AtomType);
}
output.ModeRatio[{i_of_qpoint, i_of_mode}] = ratio;
log.debug("Write mode {} {} {}"_f(i_of_qpoint, i_of_mode, atom_movement.rowwise().norm().eval()));
}
};
if (config.Cell == RamanData::Primative) process(input.Primative);
else process(input.Super);
std::filesystem::create_directories("{}/{}"_f(config.OutputPoscarDirectory, "origin"));
std::ofstream("{}/origin/POSCAR"_f(config.OutputPoscarDirectory)) << generate_poscar
(input.Super.Cell, input.Super.AtomPosition, input.Super.AtomType);
std::ofstream(config.OutputDataFile, std::ios::binary) << biu::serialize<char>(output);
}
void raman_extract(std::string path)
{
biu::Logger::Guard log(path);
auto get_dielectric_tensor = [](std::filesystem::path file)
{
biu::Logger::Guard log(file);
auto in_stream = std::ifstream(file);
std::string line;
// search line containing: MACROSCOPIC STATIC DIELECTRIC TENSOR
while (std::getline(in_stream, line))
{
if (line.find("MACROSCOPIC STATIC DIELECTRIC TENSOR") != std::string::npos)
{
log.debug("find in {}"_f(file));
// skip 1 line
std::getline(in_stream, line);
Eigen::Matrix3d dielectric_tensor;
for (std::size_t i = 0; i < 3; i++) for (std::size_t j = 0; j < 3; j++)
in_stream >> dielectric_tensor(i, j);
log.debug("read tensor {}"_f(dielectric_tensor));
return dielectric_tensor;
}
}
// test if the file is read correctly
throw std::runtime_error("Error reading file: {}"_f(file));
};
auto search_path = [&](this auto self, std::size_t indent, std::string path) -> void
{
if (std::filesystem::exists(path + "/OUTCAR"))
{
auto e = get_dielectric_tensor(path + "/OUTCAR");
for (std::size_t i = 0; i < 3; i++) std::cout << "{}- [ {}, {}, {} ]\n"_f
(std::string(indent * 2, ' '), e(i, 0), e(i, 1), e(i, 2));
}
else for (auto entry : std::filesystem::directory_iterator(path)) if (entry.is_directory())
{
std::cout << "{}{}:\n"_f
(
std::string(indent * 2, ' '),
(std::set{"+"s, "-"s}.contains(entry.path().filename()))
? "\"{}\""_f(entry.path().filename()) : entry.path().filename().string()
);
self(indent + 1, entry.path());
}
};
search_path(0, path);
}
void raman_apply_contribution(std::string config_file)
{
struct Config
{
Eigen::Matrix3d OriginalSusceptibility;
std::map<std::size_t, std::map<std::size_t, std::map<std::string, Eigen::Matrix3d>>> Susceptibility;
std::array<Eigen::Vector3d, 2> Polarization;
std::string InputDataFile;
std::string RamanInputDataFile;
std::string OutputDataFile;
};
biu::Logger::Guard log(config_file);
auto config = YAML::LoadFile(config_file).as<Config>();
auto input = biu::deserialize<CommonData>
(biu::read<std::byte>(config.InputDataFile));
auto raman_input = biu::deserialize<RamanData>
(biu::read<std::byte>(config.RamanInputDataFile));
input.RamanPolarization = config.Polarization;
auto process = [&](auto& cell)
{
for (auto&& [i, ratio] : raman_input.ModeRatio)
{
auto&& [i_of_qpoint, i_of_mode] = i;
auto&& _ = cell.Qpoint[i_of_qpoint].Mode[i_of_mode];
auto&& susceptibility = config.Susceptibility[i_of_qpoint][i_of_mode];
Eigen::Matrix3d raman_tensor = (susceptibility["+"] - susceptibility["-"]) / 2 / ratio;
_.RamanTensor = raman_tensor | biu::fromEigen;
_.WeightOnRaman = config.Polarization[0].transpose() * raman_tensor * config.Polarization[1];
log.info("{}:{:.2f}:{}:{:.2f} {}"_f
(
i_of_qpoint, fmt::join(cell.Qpoint[i_of_qpoint].Qpoint, ", "),
i_of_mode, _.Frequency, *_.WeightOnRaman
));
}
};
if (raman_input.Cell == RamanData::Primative) process(input.Primative);
else process(input.Super);
std::ofstream(config.OutputDataFile, std::ios::binary) << biu::serialize<char>(input);
}
}

View File

@@ -1,47 +0,0 @@
# include <ufo/solver.hpp>
namespace ufo
{
concurrencpp::generator<std::pair<Eigen::Vector<unsigned, 3>, unsigned>> Solver::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
};
}
Solver::DataFile::DataFile
(YAML::Node node, std::set<std::string> supported_format, std::string config_file, bool allow_same_as_config_file)
{
if (auto _ = node["SameAsConfigFile"])
{
auto __ = _.as<bool>();
if (__ && !allow_same_as_config_file)
throw std::runtime_error("\"SameAsConfigFile: true\" is not allowed here.");
ExtraParameters["SameAsConfigFile"] = __;
if (__)
{
Filename = config_file;
Format = "yaml";
return;
}
}
Filename = node["Filename"].as<std::string>();
Format = node["Format"].as<std::string>();
if (!supported_format.contains(Format))
throw std::runtime_error(fmt::format("Unsupported format: \"{}\"", Format));
if (auto _ = node["RelativeToConfigFile"])
{
auto __ = _.as<bool>();
ExtraParameters["RelativeToConfigFile"] = __;
if (__)
Filename = std::filesystem::path(config_file).parent_path() / Filename;
}
};
}

View File

@@ -1,429 +1,310 @@
# include <ufo/unfold.hpp>
# include <ufo.hpp>
# include <thread>
# include <syncstream>
# include <execution>
# include <ranges>
namespace ufo
void ufo::unfold(std::string config_file)
{
UnfoldSolver::InputType::InputType(std::string filename)
// 反折叠的原理: 将超胞中的原子运动状态, 投影到一组平面波构成的基矢中.
// 每一个平面波的波矢由两部分相加得到: 一部分是单胞倒格子的整数倍, 所取的个数有一定任意性, 论文中建议取大约单胞中原子个数那么多个,
// 但我觉得可以多取一些;这些平面波以原胞为周期,它用于尽可能描述
// 另一部分是超胞倒格子的整数倍, 取 n 个, n 为超胞对应的单胞的倍数, 其实也就是倒空间中单胞对应倒格子中超胞的格点.
// 只要第一部分取得足够多, 那么单胞中原子的状态就可以完全被这些平面波描述.
// 将超胞中原子的运动状态投影到这些基矢上, 计算出投影的系数, 就可以将超胞的原子运动状态分解到单胞中的多个 q 点上.
struct Config
{
// read main input file
// 单胞到超胞的格矢转换时用到的矩阵
// 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::Vector3i SuperCellMultiplier;
Eigen::Matrix3d SuperCellDeformation;
std::optional<std::array<double, 3>> AtomTranslation;
// 在单胞内取几个平面波的基矢
Eigen::Vector<std::size_t, 3> PrimativeCellBasisNumber;
// 单胞的 phonopy 输出的 phonopy.yaml用来读入单胞的晶格、原子坐标、原子类型、原子质量
std::string PrimativePhonopy;
// 单胞的 phonopy 输出的 band.hdf5 或 qpoint.hdf5用来读入 q 点和振动模式
std::string PrimativeQpoint;
// 同上,但是是超胞里的结果
std::string SuperPhonopy;
std::string SuperQpoint;
std::string OutputFile;
};
// 从文件中读取 q 点数据
// data 为 CommonData::Primative 或 CommonData::Super
// 返回值为原子类型和原子质量的对应关系
auto read_qpoint = [](std::string phonopy_file, std::string qpoint_file, auto& data)
{
biu::Logger::Guard log(phonopy_file, qpoint_file, data);
// phonopy 的输出有两种可能。
// 直接指定计算的 q 点时frequency 是 2 维,这时第一个维度是 q 点,第二个维度是不同模式。
// 计算能带时frequency 是 3 维,相比于二维的情况多了第一个维度,表示 q 点所在路径。
// qpoint 或 path以及 eigenvector 也有类似的变化。
// 除此以外eigenvector 后两个维度分别指示模式的特征向量的各个维度和各个模式(而不是各个模式和特征向量的各个维度),
// 因为后两个维度的尺寸总是一样的(模式个数等于原子坐标个数),非常容易搞错。
std::vector<std::array<double, 3>> qpoint;
std::vector<std::vector<double>> frequency;
std::vector<std::vector<std::vector<std::complex<double>>>> eigenvector_vector;
auto file = biu::Hdf5file(qpoint_file);
if (file.File.getDataSet("/frequency").getDimensions().size() == 2)
file.read("/frequency", frequency)
.read("/eigenvector", eigenvector_vector)
.read("/qpoint", qpoint);
else
{
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>();
std::vector<std::vector<std::array<double, 3>>> temp_path;
std::vector<std::vector<std::vector<double>>> temp_frequency;
std::vector<std::vector<std::vector<std::vector<std::complex<double>>>>> temp_eigenvector_vector;
file.read("/frequency", temp_frequency)
.read("/eigenvector", temp_eigenvector_vector)
.read("/path", temp_path);
frequency = temp_frequency | ranges::views::join | ranges::to_vector;
qpoint = temp_path | ranges::views::join | ranges::to_vector;
eigenvector_vector = temp_eigenvector_vector | ranges::views::join | ranges::to_vector;
}
for (unsigned i = 0; i < 3; i++)
SuperCellMultiplier(i) = node["SuperCellMultiplier"][i].as<int>();
if (auto value = node["SuperCellDeformation"])
// 整理并写入得到结果
auto number_of_qpoints = frequency.size(), number_of_modes = frequency[0].size();
data.Qpoint.resize(number_of_qpoints);
for (auto i : std::views::iota(0u, number_of_qpoints))
{
data.Qpoint[i].Qpoint = qpoint[i] | biu::toEigen<>;
data.Qpoint[i].Mode.resize(number_of_modes);
for (auto j : std::views::iota(0u, number_of_modes))
{
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>();
AtomPositionInputFile = DataFile
(
node["AtomPositionInputFile"], {"yaml"},
filename, true
);
QpointDataInputFile = DataFile
(
node["QpointDataInputFile"], {"yaml", "hdf5"},
filename, true
);
if (auto value = node["QpointDataOutputFile"])
{
QpointDataOutputFile.resize(value.size());
for (unsigned i = 0; i < value.size(); i++)
QpointDataOutputFile[i] = DataFile
(
value[i], {"yaml", "yaml-human-readable", "zpp", "hdf5"},
filename, false
);
data.Qpoint[i].Mode[j].Frequency = frequency[i][j];
auto number_of_atoms = number_of_modes / 3;
Eigen::MatrixX3cd eigenvector(number_of_atoms, 3);
for (auto k : std::views::iota(0u, number_of_atoms))
for (auto l : std::views::iota(0u, 3u))
eigenvector(k, l) = eigenvector_vector[i][k * 3 + l][j];
// 原则上讲,需要对读入的原子运动状态作相位转换, 使得它们与我们的约定一致(对超胞周期性重复),但这个转换 phonopy 已经做了
// 这里还要需要做归一化处理 (指将数据简单地作为向量处理的归一化)
data.Qpoint[i].Mode[j].EigenVector = eigenvector / eigenvector.norm();
}
}
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")
{
std::vector<std::vector<std::vector<double>>> frequency, path;
std::vector<std::vector<std::vector<std::vector<PhonopyComplex>>>> eigenvector_vector;
Hdf5file{}.open_for_read(QpointDataInputFile.Filename).read(frequency, "/frequency")
.read(eigenvector_vector, "/eigenvector")
.read(path, "/path");
std::vector size = { frequency.size(), frequency[0].size(), frequency[0][0].size() };
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();
}
}
}
}
// 读取并写入其它数据
YAML::Node phonopy = YAML::LoadFile(phonopy_file);
data.Cell = phonopy["unit_cell"]["lattice"].as<Eigen::Matrix3d>();
auto points = phonopy["unit_cell"]["points"].as<std::vector<YAML::Node>>();
data.AtomType = points
| ranges::views::transform([](const YAML::Node& point) { return point["symbol"].as<std::string>(); })
| ranges::views::chunk_by(std::ranges::equal_to{})
| ranges::views::transform([](const auto& chunk) { return std::pair{chunk[0], chunk.size()}; })
| ranges::to_vector;
data.AtomPosition = points
| ranges::views::transform([](const YAML::Node& point)
{ return point["coordinates"].as<std::array<double, 3>>(); })
| ranges::to_vector
| biu::toEigen<>;
return points
| ranges::views::transform([](const YAML::Node& point)
{ return std::pair(point["symbol"].as<std::string>(), point["mass"].as<double>()); })
| ranges::views::chunk_by(std::ranges::equal_to{})
| ranges::views::transform([](const auto& chunk) { return chunk[0]; })
| ranges::to<std::map<std::string, double>>;
};
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")
zpp_write(*this, filename);
else if (format == "hdf5")
{
std::vector<std::vector<double>> Qpoint, Source, Frequency, Weight;
for (auto& qpoint : QpointData)
{
Qpoint.emplace_back(qpoint.Qpoint.data(), qpoint.Qpoint.data() + 3);
Source.emplace_back(qpoint.Source.data(), qpoint.Source.data() + 3);
Frequency.emplace_back();
Weight.emplace_back();
for (auto& mode : qpoint.ModeData)
{
Frequency.back().push_back(mode.Frequency);
Weight.back().push_back(mode.Weight);
}
}
Hdf5file{}.open_for_write(filename).write(Qpoint, "/Qpoint")
.write(Source, "/Source")
.write(Frequency, "/Frequency")
.write(Weight, "/Weight");
}
}
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 << "\33[2K\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_.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
// 构建基
// 每个 q 点对应一组 sub qpoint。不同的 q 点所对应的 sub qpoint 是不一样的,但 sub qpoint 与 q 点的相对位移在不同 q 点之间是相同的。
// 由于基只与这个相对位置有关(也就是说,不同 q 点的基是一样的),因此可以先计算出所有的基,这样降低计算量。
// 外层下标对应超胞倒格子的整数倍那部分(第二部分), 也就是不同的 sub qpoint
// 内层下标对应单胞倒格子的整数倍那部分(第一部分), 也就是 sub qpoint 上的不同平面波(取的数量越多,结果越精确)
auto 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
Eigen::Matrix3d primative_cell, Eigen::Vector3i super_cell_multiplier,
Eigen::Vector<std::size_t, 3> primative_cell_basis_number, Eigen::MatrixX3d 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))
biu::Logger::Guard log;
std::vector<std::vector<Eigen::VectorXcd>> basis(super_cell_multiplier.prod());
// diff_of_sub_qpoint 表示 sub qpoint 与 qpoint 的相对位置,单位为超胞的倒格矢
for (auto [diff_of_sub_qpoint_by_reciprocal_modified_super_cell, i_of_sub_qpoint]
: biu::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))
for (auto [xyz_of_basis, i_of_basis]
: biu::sequence(primative_cell_basis_number))
{
// 计算 q 点的坐标, 单位为单胞的倒格矢
auto diff_of_sub_qpoint_by_reciprocal_primative_cell = xyz_of_basis.cast<double>()
// 计算波矢, 单位为单胞的倒格矢
auto wavevector_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();
* diff_of_sub_qpoint_by_reciprocal_modified_super_cell.cast<double>();
// 将单位转换为埃^-1
auto wavevector = primative_cell.inverse() * wavevector_by_reciprocal_primative_cell;
// 计算基矢
basis[i_of_sub_qpoint][i_of_basis]
= (2i * std::numbers::pi_v<double> * (atom_position * qpoint)).array().exp();
= (2i * std::numbers::pi_v<double> * (atom_position * wavevector)).array().exp();
}
}
return basis;
}
};
std::vector<std::vector<double>> UnfoldSolver::construct_projection_coefficient
// 计算从超胞到原胞的投影系数(不是分原子的投影系数),是反折叠的核心步骤
// 返回的投影系数是一个三维数组,第一维对应不同的 q 点,第二维对应不同的模式,第三维对应不同的 sub qpoint
auto 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
const std::vector<std::vector<Eigen::VectorXcd>>& basis,
const std::vector<std::reference_wrapper<const Eigen::MatrixX3cd>>& modes,
std::atomic<std::size_t>& number_of_finished_modes
)
{
// 第一层下标对应不同模式, 第二层下标对应这个模式在反折叠后的 q 点(sub qpoint)
std::vector<std::vector<double>> projection_coefficient(mode_data.size());
std::vector<std::vector<double>> projection_coefficient(modes.size());
// 对每个模式并行
std::transform
(
std::execution::par, mode_data.begin(), mode_data.end(),
std::execution::par, modes.begin(), modes.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++)
for (auto i_of_sub_qpoint : std::views::iota(0u, basis.size()))
// 对于 basis 中, 对应于单胞倒格子的部分, 以及对应于不同方向的部分, 分别求内积, 然后求模方和
for (unsigned i_of_basis = 0; i_of_basis < basis[i_of_sub_qpoint].size(); i_of_basis++)
for (auto i_of_basis : std::views::iota(0u, basis[i_of_sub_qpoint].size()))
projection_coefficient[i_of_sub_qpoint] +=
(basis[i_of_sub_qpoint][i_of_basis].transpose().conjugate() * mode_data.get())
.array().abs2().sum();
(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;
auto sum = ranges::accumulate(projection_coefficient, 0.);
for (auto& _ : projection_coefficient) _ /= sum;
number_of_finished_modes++;
return projection_coefficient;
}
);
return projection_coefficient;
}
};
UnfoldSolver::OutputType UnfoldSolver::construct_output
(
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)>>& meta_qpoint_by_reciprocal_super_cell,
const std::vector<std::vector<std::reference_wrapper<const decltype
(InputType::QpointDataType::ModeDataType::Frequency)>>>& frequency,
const ProjectionCoefficientType_& projection_coefficient
)
{
OutputType output;
biu::Logger::Guard log;
log.info("Reading input file...");
auto config = YAML::LoadFile(config_file).as<Config>();
CommonData output;
output.AtomMass = read_qpoint
(config.PrimativePhonopy, config.PrimativeQpoint, output.Primative);
output.AtomMass.merge(read_qpoint
(config.SuperPhonopy, config.SuperQpoint, output.Super));
output.Super.CellDeformation = config.SuperCellDeformation;
output.Super.CellMultiplier = config.SuperCellMultiplier;
output.Super.AtomTranslation = config.AtomTranslation;
// 填充 SubQpoint
for (auto i_of_super_qpoint : std::views::iota(0u, output.Super.Qpoint.size()))
for
(
unsigned i_of_meta_qpoint = 0, num_of_mode_manipulated = 0;
i_of_meta_qpoint < meta_qpoint_by_reciprocal_super_cell.size();
i_of_meta_qpoint++
auto [diff_of_sub_qpoint_by_reciprocal_modified_super_cell, i_of_sub_qpoint]
: biu::sequence(config.SuperCellMultiplier)
)
{
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();
/*
SubQpointByReciprocalModifiedSuperCell = XyzOfDiffOfSubQpointByReciprocalModifiedSuperCell +
MetaQpointByReciprocalModifiedSuperCell;
SubQpoint = SubQpointByReciprocalModifiedSuperCell.transpose() * ReciprocalModifiedSuperCell;
SubQpoint = SubQpointByReciprocalPrimativeCell.transpose() * ReciprocalPrimativeCell;
ReciprocalModifiedSuperCell = ModifiedSuperCell.inverse().transpose();
ReciprocalPrimativeCell = PrimativeCell.inverse().transpose();
ModifiedSuperCell = SuperCellMultiplier.asDiagonal() * PrimativeCell;
MetaQpoint = MetaQpointByReciprocalModifiedSuperCell.transpose() * ReciprocalModifiedSuperCell;
MetaQpoint = MetaQpointByReciprocalSuperCell.transpose() * ReciprocalSuperCell;
ReciprocalSuperCell = SuperCell.inverse().transpose();
ModifiedSuperCell = SuperCellDeformation * SuperCell;
SuperCell = SuperCellMultiplier.asDiagonal() * PrimativeCell;
整理可以得到:
SubQpointByReciprocalPrimativeCell = SuperCellMultiplier.asDiagonal().inverse() *
(XyzOfDiffOfSubQpointByReciprocalModifiedSuperCell +
SuperCellDeformation.inverse() * MetaQpointByReciprocalSuperCell);
但注意到, 这样得到的 SubQpoint 可能不在 ReciprocalPrimativeCell
(当 SuperCellDeformation 不是单位矩阵时, 边界附近的一两条 SubQpoint 会出现这种情况).
解决办法是, 在赋值时, 仅取 SubQpointByReciprocalPrimativeCell 的小数部分.
*/
auto sub_qpoint_by_reciprocal_primative_cell =
(
super_cell_multiplier.cast<double>().cwiseInverse().asDiagonal()
* (
xyz_of_diff_of_sub_qpoint_by_reciprocal_modified_super_cell.cast<double>()
+ super_cell_deformation.value_or(Eigen::Matrix3d::Identity()).inverse()
* meta_qpoint_by_reciprocal_super_cell[i_of_meta_qpoint].get().cast<double>()
)
).eval();
_.Qpoint = sub_qpoint_by_reciprocal_primative_cell.array()
- sub_qpoint_by_reciprocal_primative_cell.array().floor();
_.Source = meta_qpoint_by_reciprocal_super_cell[i_of_meta_qpoint];
_.SourceIndex_ = i_of_meta_qpoint;
for (unsigned i_of_mode = 0; i_of_mode < frequency[i_of_meta_qpoint].size(); i_of_mode++)
{
auto& __ = _.ModeData.emplace_back();
__.Frequency = frequency[i_of_meta_qpoint][i_of_mode];
__.Weight = projection_coefficient[num_of_mode_manipulated + i_of_mode][i_of_sub_qpoint];
}
}
num_of_mode_manipulated += frequency[i_of_meta_qpoint].size();
/*
SubQpointByReciprocalModifiedSuperCell = XyzOfDiffOfSubQpointByReciprocalModifiedSuperCell +
MetaQpointByReciprocalModifiedSuperCell;
SubQpoint = SubQpointByReciprocalModifiedSuperCell.transpose() * ReciprocalModifiedSuperCell;
SubQpoint = SubQpointByReciprocalPrimativeCell.transpose() * ReciprocalPrimativeCell;
ReciprocalModifiedSuperCell = ModifiedSuperCell.inverse().transpose();
ReciprocalPrimativeCell = PrimativeCell.inverse().transpose();
ModifiedSuperCell = SuperCellMultiplier.asDiagonal() * PrimativeCell;
MetaQpoint = MetaQpointByReciprocalModifiedSuperCell.transpose() * ReciprocalModifiedSuperCell;
MetaQpoint = MetaQpointByReciprocalSuperCell.transpose() * ReciprocalSuperCell;
ReciprocalSuperCell = SuperCell.inverse().transpose();
ModifiedSuperCell = SuperCellDeformation * SuperCell;
SuperCell = SuperCellMultiplier.asDiagonal() * PrimativeCell;
整理可以得到:
SubQpointByReciprocalPrimativeCell = SuperCellMultiplier.asDiagonal().inverse() *
(XyzOfDiffOfSubQpointByReciprocalModifiedSuperCell +
SuperCellDeformation.inverse() * MetaQpointByReciprocalSuperCell);
但注意到, 这样得到的 SubQpoint 可能不在 ReciprocalPrimativeCell 中
(当 SuperCellDeformation 不是单位矩阵时, 边界附近的一两条 SubQpoint 会出现这种情况).
解决办法是, 在赋值时, 仅取 SubQpointByReciprocalPrimativeCell 的小数部分.
*/
auto sub_qpoint_by_reciprocal_primative_cell =
(
config.SuperCellMultiplier.cast<double>().cwiseInverse().asDiagonal()
* (
diff_of_sub_qpoint_by_reciprocal_modified_super_cell.cast<double>()
+ config.SuperCellDeformation.inverse() * output.Super.Qpoint[i_of_super_qpoint].Qpoint
)
).eval();
output.Super.Qpoint[i_of_super_qpoint].SubQpoint.push_back(sub_qpoint_by_reciprocal_primative_cell.array()
- sub_qpoint_by_reciprocal_primative_cell.array().floor());
}
return output;
log.info("Done.");
log.info("Constructing basis...");
auto basis = construct_basis
(
output.Primative.Cell, output.Super.CellMultiplier,
config.PrimativeCellBasisNumber,
output.Super.AtomPosition
* (output.Super.CellDeformation * output.Super.CellMultiplier.cast<double>().asDiagonal() * output.Primative.Cell)
);
log.info("Done.");
std::clog << "Calculating projection coefficient... " << std::flush;
// 将所有模式放到一维来处理
{
auto modes = output.Super.Qpoint
| ranges::views::transform([](const auto& qpoint)
{ return qpoint.Mode | ranges::views::transform([](const auto& mode)
{ return std::cref(mode.EigenVector); }); })
| ranges::views::join
| ranges::to_vector;
std::atomic<std::size_t> number_of_finished_modes(0);
std::thread print_thread([&]
{
while (true)
{
std::osyncstream(std::clog)
<< "\rCalculating projection coefficient... ({}/{})"_f(number_of_finished_modes, modes.size())
<< std::flush;
std::this_thread::sleep_for(100ms);
if (number_of_finished_modes == modes.size()) break;
}
});
auto projection_coefficient = construct_projection_coefficient
(basis, modes, number_of_finished_modes);
std::size_t i_of_modes = 0;
for (auto& qpoint : output.Super.Qpoint) for (auto& mode : qpoint.Mode)
mode.WeightOnUnfold = projection_coefficient[i_of_modes++];
print_thread.join();
}
std::clog << "\33[2K\rCalculating projection coefficient... Done." << std::endl;
log.info("Writing data... ");
std::ofstream(config.OutputFile, std::ios::binary) << biu::serialize<char>(output);
log.info("Done.");
log.info("Summary:");
for (auto i_of_qpoint : std::views::iota(0u, output.Super.Qpoint.size()))
for (auto i_of_mode : std::views::iota(0u, output.Super.Qpoint[i_of_qpoint].Mode.size()))
for
(
auto i_of_sub_qpoint
: std::views::iota(0u, output.Super.Qpoint[i_of_qpoint].SubQpoint.size())
)
if (output.Super.Qpoint[i_of_qpoint].Mode[i_of_mode].WeightOnUnfold[i_of_sub_qpoint] > 0.01)
log.info("{}:{:.2f}:{}:{:.2f} -> {:.2f} {:.2f}"_f
(
i_of_qpoint, fmt::join(output.Super.Qpoint[i_of_qpoint].Qpoint, ", "),
i_of_mode, output.Super.Qpoint[i_of_qpoint].Mode[i_of_mode].Frequency,
fmt::join(output.Super.Qpoint[i_of_qpoint].SubQpoint[i_of_sub_qpoint], ", "),
output.Super.Qpoint[i_of_qpoint].Mode[i_of_mode].WeightOnUnfold[i_of_sub_qpoint]
));
}

View File

@@ -1,12 +0,0 @@
PrimativeCell:
- [ 1.548010265167714, -2.681232429908652, 0.000000000000000 ] # a
- [ 1.548010265167714, 2.681232429908652, 0.000000000000000 ] # b
- [ 0.000000000000000, 0.000000000000000, 5.061224103556595 ] # c
SuperCellMultiplier: [ 3, 1, 1 ]
PrimativeCellBasisNumber: [ 8, 8, 8 ]
AtomPositionInputFile: { FileName: "/home/chn/Documents/lammps-SiC/14/14.2/14.2.2/14.2.2.4/band.yaml", Format: "yaml" }
QpointDataInputFile: { FileName: "/home/chn/Documents/lammps-SiC/14/14.2/14.2.2/14.2.2.4/band.yaml", Format: "yaml" }
QpointDataOutputFile:
- { FileName: "test/14.2.2.4.result.yaml", Format: "yaml" }
- { FileName: "test/14.2.2.4.result.human-readable.yaml", Format: "yaml-human-readable" }
- { FileName: "test/14.2.2.4.result.zpp", Format: "zpp" }

View File

@@ -1,14 +0,0 @@
PrimativeCell:
- [ 1.548010265167714, -2.681232429908652, 0.000000000000000 ] # a
- [ 1.548010265167714, 2.681232429908652, 0.000000000000000 ] # b
- [ 0.000000000000000, 0.000000000000000, 5.061224103556595 ] # c
SourceFilename: ./test/14.2.2.5.result.zpp
Figures:
- Qpoints:
- - [ 0, 0, 0 ]
- [ 0.5, 0, 0 ]
- [ 0.3333333, 0.3333333, 0 ]
- [ 0, 0, 0 ]
Resolution: [ 1024, 400 ]
Range: [ -5, 35 ]
Filename: ./test/14.2.2.5.png

View File

@@ -1,12 +0,0 @@
PrimativeCell:
- [ 1.548010265167714, -2.681232429908652, 0.000000000000000 ] # a
- [ 1.548010265167714, 2.681232429908652, 0.000000000000000 ] # b
- [ 0.000000000000000, 0.000000000000000, 5.061224103556595 ] # c
SuperCellMultiplier: [ 3, 1, 1 ]
PrimativeCellBasisNumber: [ 8, 8, 8 ]
AtomPositionInputFile: { FileName: "/home/chn/Documents/lammps-SiC/14/14.2/14.2.2/14.2.2.5/band.yaml", Format: "yaml" }
QpointDataInputFile: { FileName: "/home/chn/Documents/lammps-SiC/14/14.2/14.2.2/14.2.2.5/band.yaml", Format: "yaml" }
QpointDataOutputFile:
- { FileName: "test/14.2.2.5.result.yaml", Format: "yaml" }
- { FileName: "test/14.2.2.5.result.human-readable.yaml", Format: "yaml-human-readable" }
- { FileName: "test/14.2.2.5.result.zpp", Format: "zpp" }

View File

@@ -1,14 +0,0 @@
PrimativeCell:
- [ 1.548010265167714, -2.681232429908652, 0.000000000000000 ] # a
- [ 1.548010265167714, 2.681232429908652, 0.000000000000000 ] # b
- [ 0.000000000000000, 0.000000000000000, 5.061224103556595 ] # c
SourceFilename: ./test/14.2.4.4.result.zpp
Figures:
- Qpoints:
- - [ 0, 0, 0 ]
- [ 0.5, 0, 0 ]
- [ 0.3333333, 0.3333333, 0 ]
- [ 0, 0, 0 ]
Resolution: [ 1024, 400 ]
Range: [ -5, 35 ]
Filename: ./test/14.2.4.4.png

View File

@@ -1,14 +0,0 @@
PrimativeCell:
- [ 1.548010265167714, -2.681232429908652, 0.000000000000000 ] # a
- [ 1.548010265167714, 2.681232429908652, 0.000000000000000 ] # b
- [ 0.000000000000000, 0.000000000000000, 5.061224103556595 ] # c
SuperCellMultiplier: [ 2, 2, 1 ]
PrimativeCellBasisNumber: [ 8, 8, 8 ]
AtomPositionInputFile:
FileName: "/home/chn/Documents/lammps-SiC/14/14.2/14.2.4/14.2.4.4/phonopy.yaml"
Format: "yaml"
QpointDataInputFile: { FileName: "/home/chn/Documents/lammps-SiC/14/14.2/14.2.4/14.2.4.4/band.hdf5", Format: "hdf5" }
QpointDataOutputFile:
- { FileName: "test/14.2.4.4.result.yaml", Format: "yaml" }
- { FileName: "test/14.2.4.4.result.human-readable.yaml", Format: "yaml-human-readable" }
- { FileName: "test/14.2.4.4.result.zpp", Format: "zpp" }

View File

@@ -1,14 +0,0 @@
PrimativeCell:
- [ 1.548010265167714, -2.681232429908652, 0.000000000000000 ] # a
- [ 1.548010265167714, 2.681232429908652, 0.000000000000000 ] # b
- [ 0.000000000000000, 0.000000000000000, 5.061224103556595 ] # c
SourceFilename: ./test/14.2.5.5.result.zpp
Figures:
- Qpoints:
- - [ 0, 0, 0 ]
- [ 0.5, 0, 0 ]
- [ 0.3333333, 0.3333333, 0 ]
- [ 0, 0, 0 ]
Resolution: [ 1024, 400 ]
Range: [ -5, 35 ]
Filename: ./test/14.2.5.5.png

View File

@@ -1,18 +0,0 @@
PrimativeCell:
- [ 1.548010265167714, -2.681232429908652, 0.000000000000000 ] # a
- [ 1.548010265167714, 2.681232429908652, 0.000000000000000 ] # b
- [ 0.000000000000000, 0.000000000000000, 5.061224103556595 ] # c
SuperCellMultiplier: [ 1, 2, 1 ]
SuperCellDeformation:
- [ 1, 0, 0]
- [ 1, 1, 0]
- [ 0, 0, 1]
PrimativeCellBasisNumber: [ 8, 8, 8 ]
AtomPositionInputFile:
FileName: "/home/chn/Documents/lammps-SiC/14/14.2/14.2.5/14.2.5.5/phonopy.yaml"
Format: "yaml"
QpointDataInputFile: { FileName: "/home/chn/Documents/lammps-SiC/14/14.2/14.2.5/14.2.5.5/band.hdf5", Format: "hdf5" }
QpointDataOutputFile:
- { FileName: "test/14.2.5.5.result.yaml", Format: "yaml" }
- { FileName: "test/14.2.5.5.result.human-readable.yaml", Format: "yaml-human-readable" }
- { FileName: "test/14.2.5.5.result.zpp", Format: "zpp" }

View File

@@ -1,15 +0,0 @@
PrimativeCell:
- [ 1.548010265167714, -2.681232429908652, 0.000000000000000 ] # a
- [ 1.548010265167714, 2.681232429908652, 0.000000000000000 ] # b
- [ 0.000000000000000, 0.000000000000000, 5.061224103556595 ] # c
SourceFilename: ./test/14.2.6.4.result.zpp
Figures:
- Qpoints:
- - [ 0, 0, 0 ]
- [ 0.5, 0, 0 ]
- [ 0.3333333, 0.3333333, 0 ]
- [ 0, 0, 0 ]
Resolution: [ 1024, 1024 ]
Range: [ -5, 35 ]
Filename: ./test/14.2.6.4.png
YTicks: [ 0, 5, 10, 15, 20, 25, 30 ]

View File

@@ -1,18 +0,0 @@
PrimativeCell:
- [ 1.548010265167714, -2.681232429908652, 0.000000000000000 ] # a
- [ 1.548010265167714, 2.681232429908652, 0.000000000000000 ] # b
- [ 0.000000000000000, 0.000000000000000, 5.061224103556595 ] # c
SuperCellMultiplier: [ 4, 8, 1 ]
SuperCellDeformation:
- [ 1, 0, 0]
- [ 1, 1, 0]
- [ 0, 0, 1]
PrimativeCellBasisNumber: [ 8, 8, 8 ]
AtomPositionInputFile:
FileName: "/home/chn/Documents/lammps-SiC/14/14.2/14.2.6/14.2.6.4/phonopy.yaml"
Format: "yaml"
QpointDataInputFile: { FileName: "/home/chn/Documents/lammps-SiC/14/14.2/14.2.6/14.2.6.4/band.hdf5", Format: "hdf5" }
QpointDataOutputFile:
- { FileName: "test/14.2.6.4.result.yaml", Format: "yaml" }
- { FileName: "test/14.2.6.4.result.human-readable.yaml", Format: "yaml-human-readable" }
- { FileName: "test/14.2.6.4.result.zpp", Format: "zpp" }

18
test/fold/config.yaml Normal file
View File

@@ -0,0 +1,18 @@
SuperCellMultiplier: [3, 4, 1]
SuperCellDeformation:
- [ 1, 0, 0 ]
- [ 0.6666, 1, 0 ]
- [ 0, 0, 1 ]
Qpoints:
- [0, 0, 0]
- [0.05, 0, 0]
- [0.1, 0, 0]
- [0.15, 0, 0]
- [0.2, 0, 0]
- [0.25, 0, 0]
- [0.3, 0, 0]
- [0.35, 0, 0]
- [0.4, 0, 0]
- [0.45, 0, 0]
- [0.5, 0, 0]
OutputFile: fold-output.yaml

1
test/project-to-atom/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
output.zpp

View File

@@ -0,0 +1,3 @@
SelectedAtom: [0, 2, 4]
InputFile: input.zpp
OutputFile: output.zpp

BIN
test/project-to-atom/input.zpp LFS Normal file

Binary file not shown.

1
test/project-to-mode/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
output.yaml

View File

@@ -0,0 +1,6 @@
PrimativeCellBasisNumber: [ 8, 8, 8 ]
SuperQpointIndex: 0
SubQpointIndex: 0
PrimativeQpointIndex: 0
InputDataFile: data.zpp
OutputFile: output.yaml

BIN
test/project-to-mode/data.zpp LFS Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
output.zpp

View File

@@ -0,0 +1,228 @@
OriginalSusceptibility:
- [ 7.006328, -3.3e-05, -2e-06 ]
- [ -3.4e-05, 7.002539, -2.4e-05 ]
- [ -3e-06, -1.2e-05, 7.303458 ]
Susceptibility:
0:
0:
"+":
- [ 7.006379, -5e-06, -2e-06 ]
- [ -1.6e-05, 7.002547, -3e-05 ]
- [ -1.1e-05, -6e-06, 7.303627 ]
"-":
- [ 7.006463, 4e-06, -4.5e-05 ]
- [ 1e-06, 7.002729, -1.7e-05 ]
- [ -4.9e-05, 6e-06, 7.303617 ]
1:
"+":
- [ 7.006331, 7e-06, 1.6e-05 ]
- [ 2e-06, 7.002539, -1.9e-05 ]
- [ 1.7e-05, -3e-06, 7.303612 ]
"-":
- [ 7.006366, -8e-06, 1.5e-05 ]
- [ -1.1e-05, 7.002548, -8e-06 ]
- [ 1.3e-05, 1.1e-05, 7.303606 ]
10:
"+":
- [ 7.00768, 0.004054, 1e-06 ]
- [ 0.004046, 7.003291, -4e-05 ]
- [ -1e-06, -1.7e-05, 7.305554 ]
"-":
- [ 7.007955, -0.004016, -1e-06 ]
- [ -0.004015, 7.003451, -9e-06 ]
- [ -4e-06, 7e-06, 7.305848 ]
107:
"+":
- [ 7.007532, -4.7e-05, -6e-06 ]
- [ -4.8e-05, 7.003828, -9e-06 ]
- [ -4e-06, 4e-06, 7.307759 ]
"-":
- [ 7.0076, -5e-06, -1.2e-05 ]
- [ -5e-06, 7.003875, -8e-06 ]
- [ -1e-05, 2e-06, 7.307945 ]
115:
"+":
- [ 7.007683, 5e-06, -5.3e-05 ]
- [ -0, 7.003913, 5e-06 ]
- [ -4e-05, 2.8e-05, 7.307388 ]
"-":
- [ 7.007917, 5e-06, -3e-06 ]
- [ 4e-06, 7.004128, -3e-06 ]
- [ -2e-06, 1.1e-05, 7.307671 ]
15:
"+":
- [ 7.010948, -8.3e-05, -0.007718 ]
- [ -8e-05, 7.004814, -3.4e-05 ]
- [ -0.00772, -1.5e-05, 7.3101 ]
"-":
- [ 7.011174, 3e-06, 0.00773 ]
- [ -5e-06, 7.005176, -7e-06 ]
- [ 0.007728, 1e-05, 7.3104 ]
16:
"+":
- [ 7.008787, 7.2e-05, -1.8e-05 ]
- [ 7.4e-05, 7.007313, 0.007694 ]
- [ -2.4e-05, 0.007696, 7.310191 ]
"-":
- [ 7.008947, -1.3e-05, -7e-06 ]
- [ -2.1e-05, 7.007438, -0.007742 ]
- [ -7e-06, -0.007727, 7.310434 ]
185:
"+":
- [ 7.018886, 1.8e-05, -5e-05 ]
- [ 2.7e-05, 7.015141, -3e-05 ]
- [ -5e-05, -1.7e-05, 7.307471 ]
"-":
- [ 7.003657, 1e-06, 9e-06 ]
- [ -1e-06, 6.999828, -1.4e-05 ]
- [ 1e-05, 5e-06, 7.326057 ]
2:
"+":
- [ 7.006421, -8e-06, -4e-05 ]
- [ -1.5e-05, 7.002682, -6.7e-05 ]
- [ -3.9e-05, -4.5e-05, 7.303477 ]
"-":
- [ 7.00633, 2.4e-05, -7.4e-05 ]
- [ 2.2e-05, 7.00253, -9.1e-05 ]
- [ -7.7e-05, -7.2e-05, 7.303417 ]
251:
"+":
- [ 7.008862, 4.9e-05, -8.8e-05 ]
- [ 4.8e-05, 7.011172, -0.00086 ]
- [ -7.8e-05, -0.000845, 7.305162 ]
"-":
- [ 7.009107, -5e-06, -2e-06 ]
- [ -8e-06, 7.011587, 0.000791 ]
- [ 3e-06, 0.000809, 7.305493 ]
252:
"+":
- [ 7.015066, -4.3e-05, -0.000838 ]
- [ -4.5e-05, 7.005147, -2.9e-05 ]
- [ -0.000841, -9e-06, 7.305197 ]
"-":
- [ 7.015285, -4e-06, 0.000833 ]
- [ -2e-06, 7.005368, -4e-06 ]
- [ 0.000824, 1.2e-05, 7.305474 ]
281:
"+":
- [ 6.981143, 1.4e-05, -5.8e-05 ]
- [ 1.1e-05, 7.038724, 4.3e-05 ]
- [ -5.7e-05, 5.9e-05, 7.306158 ]
"-":
- [ 7.037206, 2e-06, 0 ]
- [ 3e-06, 6.983191, -1e-05 ]
- [ 2e-06, 0, 7.306576 ]
282:
"+":
- [ 7.014582, -0.027613, 1.6e-05 ]
- [ -0.027632, 7.005279, -7.1e-05 ]
- [ 4e-06, -6.7e-05, 7.306158 ]
"-":
- [ 7.014711, 0.027632, -2e-06 ]
- [ 0.027633, 7.005478, -1.1e-05 ]
- [ -3e-06, 4e-06, 7.306578 ]
287:
"+":
- [ 7.032791, 2.6e-05, -4.1e-05 ]
- [ 3.2e-05, 7.028882, -1.7e-05 ]
- [ -3.6e-05, -2e-06, 7.250185 ]
"-":
- [ 6.985897, -3e-06, 4e-06 ]
- [ 5e-06, 6.982421, -1.4e-05 ]
- [ 2e-06, -3e-06, 7.382705 ]
292:
"+":
- [ 7.007262, 1.3e-05, 1.3e-05 ]
- [ 9e-06, 7.013971, -5.9e-05 ]
- [ 7e-06, -4.6e-05, 7.305084 ]
"-":
- [ 7.011688, 0, 2e-06 ]
- [ -2e-06, 7.009318, -7e-06 ]
- [ 4e-06, 9e-06, 7.306455 ]
293:
"+":
- [ 7.015281, 0.002384, -0 ]
- [ 0.002373, 7.005685, -4.4e-05 ]
- [ -4e-06, -2.8e-05, 7.3058 ]
"-":
- [ 7.015423, -0.002375, -9e-06 ]
- [ -0.00237, 7.005799, -7e-06 ]
- [ -8e-06, 1.1e-05, 7.305927 ]
311:
"+":
- [ 7.017108, 0.000122, -0.023832 ]
- [ 0.00013, 7.006382, -5.6e-05 ]
- [ -0.02384, -3.4e-05, 7.306937 ]
"-":
- [ 7.017506, 5e-06, 0.023741 ]
- [ -6e-06, 7.006811, -2e-06 ]
- [ 0.023733, 1.6e-05, 7.307537 ]
312:
"+":
- [ 7.010335, 7.8e-05, -4.6e-05 ]
- [ 8.4e-05, 7.013598, 0.023664 ]
- [ -4.1e-05, 0.023683, 7.307236 ]
"-":
- [ 7.010567, -1e-06, -3e-06 ]
- [ -9e-06, 7.013715, -0.023726 ]
- [ -4e-06, -0.023709, 7.307591 ]
327:
"+":
- [ 7.008517, 2.9e-05, -1.6e-05 ]
- [ 2.5e-05, 7.004613, -9.3e-05 ]
- [ -1.8e-05, -7.1e-05, 7.324773 ]
"-":
- [ 7.012664, -1e-05, 1e-06 ]
- [ -8e-06, 7.008951, -1.2e-05 ]
- [ 5e-06, 7e-06, 7.306262 ]
378:
"+":
- [ 7.012128, 1e-05, 1.8e-05 ]
- [ 1e-06, 7.008401, -1e-05 ]
- [ 1.3e-05, -1e-06, 7.322152 ]
"-":
- [ 7.012153, 1e-06, -1.3e-05 ]
- [ 9e-06, 7.008396, -1.4e-05 ]
- [ -1.9e-05, 3e-06, 7.322317 ]
379:
"+":
- [ 7.010873, -5.9e-05, -3.8e-05 ]
- [ -7.8e-05, 7.007166, -3.6e-05 ]
- [ -4.3e-05, -2.5e-05, 7.319279 ]
"-":
- [ 7.011186, -5e-06, -1.4e-05 ]
- [ 6e-06, 7.007443, -1e-06 ]
- [ -1.9e-05, 1.4e-05, 7.319705 ]
5:
"+":
- [ 7.007563, -0.001516, 5e-06 ]
- [ -0.001515, 7.00316, -5.5e-05 ]
- [ 0, -2.9e-05, 7.305665 ]
"-":
- [ 7.007833, 0.001555, -9e-06 ]
- [ 0.001554, 7.003468, -1.1e-05 ]
- [ -1.2e-05, 1e-05, 7.306111 ]
6:
"+":
- [ 7.005528, 1.8e-05, 4.1e-05 ]
- [ 1.2e-05, 7.005511, -3.7e-05 ]
- [ 3.7e-05, -1.5e-05, 7.30606 ]
"-":
- [ 7.008811, -2e-06, 4e-06 ]
- [ -5e-06, 7.002567, -3e-06 ]
- [ 7e-06, 1.7e-05, 7.306146 ]
9:
"+":
- [ 7.011048, 8e-05, -2.7e-05 ]
- [ 8.6e-05, 7.000001, 6e-06 ]
- [ -2.6e-05, 4.1e-05, 7.30555 ]
"-":
- [ 7.003087, -6e-06, 3e-06 ]
- [ -3e-06, 7.008199, -1e-06 ]
- [ 5e-06, 1.8e-05, 7.305867 ]
Polarization:
- [ 0, 1, 0 ]
- [ 0, 1, 0 ]
InputDataFile: data.zpp
RamanInputDataFile: raman.zpp
OutputDataFile: output.zpp

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,2 @@
POSCAR
output.zpp

View File

@@ -0,0 +1,7 @@
Cell: Super
SelectedModes:
0: [ 0, 1, 2, 5, 6, 9, 10, 15, 16, 107, 115, 185, 251, 252, 281, 282, 287, 292, 293, 311, 312, 327, 378, 379 ]
MaxDisplacement: 0.02
OutputPoscarDirectory: POSCAR
InputDataFile: data.zpp
OutputDataFile: output.zpp

Binary file not shown.

1
test/raman-extract/.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
OUTCAR filter=lfs diff=lfs merge=lfs -text

BIN
test/raman-extract/0/0/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/0/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/1/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/1/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/10/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/10/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/107/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/107/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/115/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/115/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/15/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/15/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/16/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/16/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/185/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/185/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/2/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/2/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/251/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/251/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/252/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/252/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/281/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/281/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/282/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/282/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/287/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/287/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/292/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/292/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/293/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/293/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/311/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/311/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/312/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/312/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/327/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/327/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/378/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/378/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/379/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/379/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/5/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/5/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/6/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/6/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/9/+/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/0/9/-/OUTCAR LFS Normal file

Binary file not shown.

BIN
test/raman-extract/origin/OUTCAR LFS Normal file

Binary file not shown.

1
test/unfold/.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*phonopy.yaml filter=lfs diff=lfs merge=lfs -text

1
test/unfold/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
unfold-output.zpp

13
test/unfold/config.yaml Normal file
View File

@@ -0,0 +1,13 @@
SuperCellMultiplier: [4, 4, 1]
SuperCellDeformation:
- [ 1, 0, 0 ]
- [ 0.5, 1, 0 ]
- [ 0, 0, 1 ]
AtomTranslation: [ 0, 0, 0.5 ]
PrimativeCellBasisNumber: [ 8, 8, 8 ]
PrimativePhonopy: primitive-phonopy.yaml
PrimativeQpoint: primitive-band.hdf5
SuperPhonopy: super-phonopy.yaml
SuperQpoint: super-qpoints.hdf5
OutputFile: unfold-output.zpp

BIN
test/unfold/primitive-band.hdf5 LFS Normal file

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More