# include namespace ufo { UnfoldSolver::InputType::InputType(std::string filename) { // read main input file { auto node = YAML::LoadFile(filename); for (unsigned i = 0; i < 3; i++) for (unsigned j = 0; j < 3; j++) PrimativeCell(i, j) = node["PrimativeCell"][i][j].as(); for (unsigned i = 0; i < 3; i++) SuperCellMultiplier(i) = node["SuperCellMultiplier"][i].as(); if (auto value = node["SuperCellDeformation"]) { SuperCellDeformation.emplace(); for (unsigned i = 0; i < 3; i++) for (unsigned j = 0; j < 3; j++) (*SuperCellDeformation)(i, j) = value[i][j].as(); } for (unsigned i = 0; i < 3; i++) PrimativeCellBasisNumber(i) = node["PrimativeCellBasisNumber"][i].as(); auto read_file_config = [filename](YAML::Node source, InputOutputFile& config) { if (auto _ = source["SameAsConfigFile"]) { auto __ = _.as(); config.ExtraParameters["SameAsConfigFile"] = __; if (__) { config.FileName = filename; config.Format = "yaml"; return; } } config.FileName = source["FileName"].as(); config.Format = source["Format"].as(); if (auto _ = source["RelativeToConfigFile"]) { auto __ = _.as(); config.ExtraParameters["RelativeToConfigFile"] = __; if (__) config.FileName = std::filesystem::path(filename).parent_path() / config.FileName; } }; read_file_config(node["AtomPositionInputFile"], AtomPositionInputFile); if (!std::set{"yaml"}.contains(AtomPositionInputFile.Format)) throw std::runtime_error(fmt::format ("Unknown AtomPositionInputFile.Format: {}, should be \"yaml\".", AtomPositionInputFile.Format)); read_file_config(node["QPointDataInputFile"], QPointDataInputFile); if (!std::set{"yaml", "hdf5"}.contains(QPointDataInputFile.Format)) throw std::runtime_error(fmt::format ("Unknown QPointDataInputFile.Format: {}, should be \"yaml\" or \"hdf5\".", QPointDataInputFile.Format)); if (auto value = node["QPointDataOutputFile"]) { QPointDataOutputFile.resize(value.size()); for (unsigned i = 0; i < value.size(); i++) { read_file_config(value[i], QPointDataOutputFile[i]); if ( QPointDataOutputFile[i].ExtraParameters.contains("SameAsConfigFile") && std::any_cast(QPointDataOutputFile[i].ExtraParameters["SameAsConfigFile"]) ) throw std::runtime_error("QPointDataOutputFile.SameAsConfigFile should not be set."); if ( !std::set{"yaml", "yaml-human-readable", "zpp"} .contains(QPointDataOutputFile[i].Format) ) throw std::runtime_error(fmt::format ( "Unknown QPointDataOutputFile[{}].Format: {}, should be \"yaml\", \"yaml-human-readable\" or \"zpp\".", i, QPointDataOutputFile[i].Format )); } } } if (AtomPositionInputFile.Format == "yaml") { auto node = YAML::LoadFile(AtomPositionInputFile.FileName); std::vector points; if (auto _ = node["points"]) points = _.as>(); else points = node["unit_cell"]["points"].as>(); 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(); auto super_cell = (SuperCellDeformation.value_or(Eigen::Matrix3d::Identity()) * SuperCellMultiplier.cast().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>(); 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(); auto band = phonon[i]["band"].as>(); QPointData[i].ModeData.resize(band.size()); for (unsigned j = 0; j < band.size(); j++) { QPointData[i].ModeData[j].Frequency = band[j]["frequency"].as(); auto eigenvector_vectors = band[j]["eigenvector"] .as>>>(); 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 * 1i // * (atom_position_to_super_cell * input.QPointData[i].QPoint)).array().exp(); // AtomMovement /= AtomMovement.norm(); // phonopy 似乎已经进行了相位的转换!为什么? AtomMovement = eigenvectors / eigenvectors.norm(); } } } else if (QPointDataInputFile.Format == "hdf5") { HighFive::File file(QPointDataInputFile.FileName, HighFive::File::ReadOnly); auto size = file.getDataSet("/frequency").getDimensions(); auto frequency = file.getDataSet("/frequency") .read>>>(); auto eigenvector_vector = file.getDataSet("/eigenvector") .read>>>>(); auto path = file.getDataSet("/path") .read>>>(); QPointData.resize(size[0] * size[1]); for (unsigned i = 0; i < size[0]; i++) for (unsigned j = 0; j < size[1]; j++) { QPointData[i * size[1] + j].QPoint = Eigen::Vector3d(path[i][j].data()); QPointData[i * size[1] + j].ModeData.resize(size[2]); for (unsigned k = 0; k < size[2]; k++) { QPointData[i * size[1] + j].ModeData[k].Frequency = frequency[i][j][k]; Eigen::MatrixX3cd eigenvectors(AtomPosition.rows(), 3); for (unsigned l = 0; l < AtomPosition.rows(); l++) for (unsigned m = 0; m < 3; m++) eigenvectors(l, m) = eigenvector_vector[i][j][l * 3 + m][k].r + eigenvector_vector[i][j][l * 3 + m][k].i * 1i; QPointData[i * size[1] + j].ModeData[k].AtomMovement = eigenvectors / eigenvectors.norm(); } } } } void UnfoldSolver::OutputType::write (decltype(InputType::QPointDataOutputFile) output_files) const { for (auto& output_file : output_files) write(output_file.FileName, output_file.Format); } void UnfoldSolver::OutputType::write(std::string filename, std::string format, unsigned percision) const { if (format == "yaml") std::ofstream(filename) << [&] { std::stringstream print; print << "QPointData:\n"; for (auto& qpoint: QPointData) { print << fmt::format(" - QPoint: [ {1:.{0}f}, {2:.{0}f}, {3:.{0}f} ]\n", percision, qpoint.QPoint[0], qpoint.QPoint[1], qpoint.QPoint[2]); print << fmt::format(" Source: [ {1:.{0}f}, {2:.{0}f}, {3:.{0}f} ]\n", percision, qpoint.Source[0], qpoint.Source[1], qpoint.Source[2]); print << " ModeData:\n"; for (auto& mode: qpoint.ModeData) print << fmt::format(" - {{ Frequency: {1:.{0}f}, Weight: {2:.{0}f} }}\n", percision, mode.Frequency, mode.Weight); } return print.str(); }(); else if (format == "yaml-human-readable") { std::remove_cvref_t output; std::map> 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 frequency_to_weight; for (unsigned i_of_mode = 0; i_of_mode < qpoint->ModeData.size(); i_of_mode++) { auto frequency = qpoint->ModeData[i_of_mode].Frequency; auto weight = qpoint->ModeData[i_of_mode].Weight; auto it_lower = frequency_to_weight.lower_bound(frequency - 0.1); auto it_upper = frequency_to_weight.upper_bound(frequency + 0.1); if (it_lower == it_upper) frequency_to_weight[frequency] = weight; else { auto frequency_sum = std::accumulate(it_lower, it_upper, 0., [](const auto& a, const auto& b) { return a + b.first * b.second; }); auto weight_sum = std::accumulate(it_lower, it_upper, 0., [](const auto& a, const auto& b) { return a + b.second; }); frequency_sum += frequency * weight; weight_sum += weight; frequency_to_weight.erase(it_lower, it_upper); frequency_to_weight[frequency_sum / weight_sum] = weight_sum; } } auto& _ = output.QPointData.emplace_back(); _.QPoint = qpoint->QPoint; _.Source = qpoint->Source; _.SourceIndex_ = qpoint->SourceIndex_; for (auto [frequency, weight] : frequency_to_weight) if (weight > 0.1) { auto& __ = _.ModeData.emplace_back(); __.Frequency = frequency; __.Weight = weight; } } output.write(filename, "yaml", 3); } else if (format == "zpp") { auto [data, out] = zpp::bits::data_out(); out(*this).or_throw(); static_assert(sizeof(char) == sizeof(std::byte)); std::ofstream file(filename, std::ios::binary | std::ios::out); file.exceptions(std::ios::badbit | std::ios::failbit); file.write(reinterpret_cast(data.data()), data.size()); } } UnfoldSolver::UnfoldSolver(std::string config_file) : Input_([&] { std::clog << "Reading input file... " << std::flush; return config_file; }()) { std::clog << "Done." << std::endl; } UnfoldSolver& UnfoldSolver::operator()() { if (!Basis_) { std::clog << "Constructing basis... " << std::flush; Basis_ = construct_basis ( Input_.PrimativeCell, Input_.SuperCellMultiplier, Input_.PrimativeCellBasisNumber, Input_.AtomPosition ); std::clog << "Done." << std::endl; } if (!Output_) { std::clog << "Calculating projection coefficient... " << std::flush; std::vector> mode_data; for (auto& qpoint : Input_.QPointData) for (auto& mode : qpoint.ModeData) mode_data.emplace_back(mode.AtomMovement); std::atomic number_of_finished_modes(0); std::thread print_thread([&] { unsigned n; while ((n = number_of_finished_modes) < mode_data.size()) { std::osyncstream(std::cerr) << fmt::format("\rCalculating projection coefficient... ({}/{})", number_of_finished_modes, mode_data.size()) << std::flush; std::this_thread::sleep_for(100ms); number_of_finished_modes.wait(n); } }); auto projection_coefficient = construct_projection_coefficient (*Basis_, mode_data, number_of_finished_modes); number_of_finished_modes = mode_data.size(); print_thread.join(); std::clog << "\rCalculating projection coefficient... Done." << std::endl; std::clog << "Constructing output... " << std::flush; std::vector> qpoint; std::vector>> frequency; for (auto& qpoint_data : Input_.QPointData) { qpoint.emplace_back(qpoint_data.QPoint); frequency.emplace_back(); for (auto& mode_data : qpoint_data.ModeData) frequency.back().emplace_back(mode_data.Frequency); } Output_ = construct_output ( Input_.PrimativeCell, Input_.SuperCellMultiplier, Input_.SuperCellDeformation, qpoint, frequency, projection_coefficient ); std::clog << "Done." << std::endl; } std::clog << "Writing output... " << std::flush; Output_->write(Input_.QPointDataOutputFile); std::clog << "Done." << std::endl; return *this; } UnfoldSolver::BasisType UnfoldSolver::construct_basis ( const decltype(InputType::PrimativeCell)& primative_cell, const decltype(InputType::SuperCellMultiplier)& super_cell_multiplier, const decltype(InputType::PrimativeCellBasisNumber)& primative_cell_basis_number, const decltype(InputType::AtomPosition)& atom_position ) { BasisType basis(super_cell_multiplier.prod()); // 每个 q 点对应的一组 sub qpoint。不同的 q 点所对应的 sub qpoint 是不一样的,但 sub qpoint 与 q 点的相对位置一致。 // 这里 xyz_of_diff_of_sub_qpoint 即表示这个相对位置,单位为超胞的倒格矢 for (auto [xyz_of_diff_of_sub_qpoint_by_reciprocal_modified_super_cell, i_of_sub_qpoint] : triplet_sequence(super_cell_multiplier)) { basis[i_of_sub_qpoint].resize(primative_cell_basis_number.prod()); for (auto [xyz_of_basis, i_of_basis] : triplet_sequence(primative_cell_basis_number)) { // 计算 q 点的坐标, 单位为单胞的倒格矢 auto diff_of_sub_qpoint_by_reciprocal_primative_cell = xyz_of_basis.cast() + super_cell_multiplier.cast().cwiseInverse().asDiagonal() * xyz_of_diff_of_sub_qpoint_by_reciprocal_modified_super_cell.cast(); // 将 q 点坐标转换为埃^-1 auto qpoint = (diff_of_sub_qpoint_by_reciprocal_primative_cell.transpose() * (primative_cell.transpose().inverse())).transpose(); // 计算基矢 basis[i_of_sub_qpoint][i_of_basis] = (2i * std::numbers::pi_v * (atom_position * qpoint)).array().exp(); } } return basis; } std::vector> UnfoldSolver::construct_projection_coefficient ( const BasisType& basis, const std::vector>& mode_data, std::atomic& number_of_finished_modes ) { // 第一层下标对应不同模式, 第二层下标对应这个模式在反折叠后的 q 点(sub qpoint) std::vector> projection_coefficient(mode_data.size()); // 对每个模式并行 std::transform ( std::execution::par, mode_data.begin(), mode_data.end(), projection_coefficient.begin(), [&](const auto& mode_data) { // 这里, mode_data 和 projection_coefficient 均指对应于一个模式的数据 std::vector projection_coefficient(basis.size()); for (unsigned i_of_sub_qpoint = 0; i_of_sub_qpoint < basis.size(); i_of_sub_qpoint++) // 对于 basis 中, 对应于单胞倒格子的部分, 以及对应于不同方向的部分, 分别求内积, 然后求模方和 for (unsigned i_of_basis = 0; i_of_basis < basis[i_of_sub_qpoint].size(); i_of_basis++) projection_coefficient[i_of_sub_qpoint] += (basis[i_of_sub_qpoint][i_of_basis].transpose().conjugate() * mode_data.get()) .array().abs2().sum(); // 如果是严格地将向量分解到一组完备的基矢上, 那么不需要对计算得到的权重再做归一化处理 // 但这里并不是这样一个严格的概念. 因此对分解到各个 sub qpoint 上的权重做归一化处理 auto sum = std::accumulate (projection_coefficient.begin(), projection_coefficient.end(), 0.); for (auto& _ : projection_coefficient) _ /= sum; number_of_finished_modes++; return projection_coefficient; } ); return projection_coefficient; } UnfoldSolver::OutputType UnfoldSolver::construct_output ( const decltype(InputType::PrimativeCell)& primative_cell, const decltype(InputType::SuperCellMultiplier)& super_cell_multiplier, const decltype(InputType::SuperCellDeformation)& super_cell_deformation, const std::vector>& qpoint, const std::vector>>& frequency, const ProjectionCoefficientType_& projection_coefficient ) { OutputType output; for (unsigned i_of_qpoint = 0, num_of_mode_in_all_qpoint = 0; i_of_qpoint < qpoint.size(); i_of_qpoint++) { // 当 SuperCellDeformation 不是单位矩阵时, input.QPointData[i_of_qpoint].QPoint 不一定在 reciprocal_primative_cell 中 // 需要首先将 q 点平移数个周期, 进入不包含 SuperCellDeformation 的超胞的倒格子中 auto qpoint_by_reciprocal_modified_super_cell_in_modified_reciprocal_super_cell = !super_cell_deformation ? qpoint[i_of_qpoint].get() : [&] { auto current_qpoint = qpoint[i_of_qpoint].get(); // 给一个 q 点打分 // 计算这个 q 点以 modified_reciprocal_supre_cell 为单位的坐标, 依次考虑每个维度, 总分为每个维度之和. // 如果这个坐标大于 0 小于 1, 则打 0 分. // 如果这个坐标小于 0, 则打这个坐标的相反数分. // 如果这个坐标大于 1, 则打这个坐标减去 1 的分. auto score = [&](Eigen::Vector3d qpoint_by_reciprocal_super_cell) { // SuperCell = SuperCellDeformation * SuperCellMultiplier.asDiagonal() * PrimativeCell // ModifiedSuperCell = SuperCellMultiplier.asDiagonal() * PrimativeCell // ReciprocalSuperCell = SuperCell.inverse().transpose() // ReciprocalModifiedSuperCell = ModifiedSuperCell.inverse().transpose() // qpoint.transpose() = qpoint_by_reciprocal_super_cell.transpose() * ReciprocalSuperCell // qpoint.transpose() = qpoint_by_reciprocal_modified_super_cell.transpose() * ReciprocalModifiedSuperCell auto qpoint_by_reciprocal_modified_super_cell = (super_cell_deformation->inverse() * qpoint_by_reciprocal_super_cell).eval(); double score = 0; for (unsigned i = 0; i < 3; i++) { auto coordinate = qpoint_by_reciprocal_modified_super_cell[i]; if (coordinate < 0) score -= coordinate; else if (coordinate > 1) score += coordinate - 1; } return score; }; while (score(current_qpoint) > 0) { double min_score = std::numeric_limits::max(); Eigen::Vector3d min_score_qpoint; for (int x = -1; x <= 1; x++) for (int y = -1; y <= 1; y++) for (int z = -1; z <= 1; z++) { auto this_qpoint = current_qpoint + Eigen::Matrix{{x}, {y}, {z}}.cast(); auto this_score = score(this_qpoint); if (this_score < min_score) { min_score = this_score; min_score_qpoint = this_qpoint; } } current_qpoint = min_score_qpoint; } return super_cell_deformation->inverse() * current_qpoint; }(); for (auto [xyz_of_diff_of_sub_qpoint_by_reciprocal_modified_super_cell, i_of_sub_qpoint] : triplet_sequence(super_cell_multiplier)) { auto& _ = output.QPointData.emplace_back(); auto reciprocal_modified_super_cell = (super_cell_multiplier.cast().asDiagonal() * primative_cell).inverse().transpose(); // sub qpoint 的坐标,单位为埃^-1 auto sub_qpoint = ((xyz_of_diff_of_sub_qpoint_by_reciprocal_modified_super_cell.cast() + qpoint_by_reciprocal_modified_super_cell_in_modified_reciprocal_super_cell) .transpose() * reciprocal_modified_super_cell).transpose(); // 将坐标转换为相对于单胞的倒格矢的坐标并写入 // 由 sub_qpoint.transpose() = sub_qpoint_by_reciprocal_primative_cell.transpose() // * PrimativeCell.transpose().inverse() // 得到 sub_qpoint_by_reciprocal_primative_cell = PrimativeCell * sub_qpoint _.QPoint = primative_cell * sub_qpoint; _.Source = qpoint[i_of_qpoint]; _.SourceIndex_ = i_of_qpoint; for (unsigned i_of_mode = 0; i_of_mode < frequency[i_of_qpoint].size(); i_of_mode++, num_of_mode_in_all_qpoint++) { auto& __ = _.ModeData.emplace_back(); __.Frequency = frequency[i_of_qpoint][i_of_mode]; __.Weight = projection_coefficient[num_of_mode_in_all_qpoint][i_of_sub_qpoint]; } } } return output; } } // inline Output::Output(std::string filename) // { // auto input = std::ifstream(filename, std::ios::binary | std::ios::in); // input.exceptions(std::ios::badbit | std::ios::failbit); // std::vector data; // { // std::vector string(std::istreambuf_iterator(input), {}); // data.assign // ( // reinterpret_cast(string.data()), // reinterpret_cast(string.data() + string.size()) // ); // } // auto in = zpp::bits::in(data); // in(*this).or_throw(); // }