Skip to content

Commit 60f2cd9

Browse files
committed
Enable direct reading of UPF version >= 2 PP files
1 parent bd9bb45 commit 60f2cd9

File tree

4 files changed

+332
-5
lines changed

4 files changed

+332
-5
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ find_package(HDF5 REQUIRED C)
134134
include(cmake/hdf5_target.cmake)
135135
find_package(SpFFT 1.0.2 CONFIG REQUIRED)
136136
find_package(SPLA 1.4.0 CONFIG REQUIRED)
137+
find_package(pugixml REQUIRED)
137138

138139
# Check if SpFFT support single precision if required
139140
if(NOT SIRIUS_USE_FP32 STREQUAL "OFF")

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ endif()
105105
target_link_libraries(sirius_cxx PUBLIC ${GSL_LIBRARY}
106106
MPI::MPI_CXX
107107
sirius::libxc
108+
pugixml::pugixml
108109
sirius::libspg
109110
sirius::hdf5
110111
costa::costa

src/unit_cell/atom_type.cpp

Lines changed: 316 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include "atom_type.hpp"
1515
#include "core/ostream_tools.hpp"
16+
#include <algorithm>
1617

1718
namespace sirius {
1819

@@ -689,14 +690,277 @@ Atom_type::read_pseudo_paw(nlohmann::json const& parser)
689690
}
690691
}
691692

693+
bool
694+
is_json_file(std::string const& str__)
695+
{
696+
const std::string ftype = ".json";
697+
std::string lcstr__ = str__;
698+
std::transform(lcstr__.begin(), lcstr__.end(), lcstr__.begin(), ::tolower);
699+
return lcstr__.compare(lcstr__.size()-ftype.size(), ftype.size(), ftype) == 0;
700+
}
701+
702+
template<typename T>
703+
std::vector<T>
704+
vec_from_str(std::string const& str__, T const scaling)
705+
{
706+
std::vector<T> vec;
707+
std::istringstream iss(str__);
708+
709+
std::copy(std::istream_iterator<T>(iss), std::istream_iterator<T>(), std::back_inserter(vec));
710+
std::transform(vec.begin(), vec.end(), vec.begin(), [&scaling](T val){return val*scaling;});
711+
712+
return vec;
713+
}
714+
715+
692716
void
693-
Atom_type::read_input(std::string const& str__)
717+
Atom_type::read_pseudo_uspp(pugi::xml_node const& upf)
694718
{
695-
auto parser = read_json_from_file_or_string(str__);
696719

697-
if (parser.empty()) {
698-
return;
720+
pugi::xml_node header = upf.child("PP_HEADER");
721+
symbol_ = header.attribute("element").as_string();
722+
723+
double zp;
724+
zp = header.attribute("z_valence").as_double();
725+
zn_ = int(zp + 1e-10);
726+
727+
int nmtp = header.attribute("mesh_size").as_int();
728+
729+
auto rgrid = vec_from_str<double>(upf.child("PP_MESH").child("PP_R").child_value(), 1.0);
730+
if (static_cast<int>(rgrid.size()) != nmtp) {
731+
RTE_THROW("wrong mesh size");
732+
}
733+
/* set the radial grid */
734+
set_radial_grid(nmtp, rgrid.data());
735+
736+
local_potential(vec_from_str<double>(upf.child("PP_LOCAL").child_value(), 0.5));
737+
738+
ps_core_charge_density(vec_from_str<double>(upf.child("PP_NLCC").child_value(), 1.0));
739+
740+
ps_total_charge_density(vec_from_str<double>(upf.child("PP_RHOATOM").child_value(), 1.0));
741+
742+
if (local_potential().size() != rgrid.size() || ps_core_charge_density().size() != rgrid.size() ||
743+
ps_total_charge_density().size() != rgrid.size()) {
744+
std::cout << local_potential().size() << " " << ps_core_charge_density().size() << " "
745+
<< ps_total_charge_density().size() << std::endl;
746+
RTE_THROW("wrong array size");
747+
}
748+
749+
spin_orbit_coupling_ = header.attribute("has_so").as_bool();
750+
751+
int nbf = header.attribute("number_of_proj").as_int();
752+
753+
for (int i = 0; i < nbf; i++) {
754+
std::string bstr = "PP_BETA."+std::to_string(i+1);
755+
pugi::xml_node beta_node = upf.child("PP_NONLOCAL").child(bstr.data());
756+
auto beta_raw = vec_from_str<double>(beta_node.child_value(), 1.0);
757+
758+
int nr = beta_node.attribute("cutoff_radius_index").as_int();
759+
if (nr == 0) {
760+
for(int j = beta_raw.size()-1; j >= 0; j--) {
761+
if (abs(beta_raw[j]) > 1.0e-80) {
762+
nr = j+1;
763+
break;
764+
}
765+
}
766+
}
767+
if (nr == 0) {
768+
nr = beta_raw.size();
769+
}
770+
771+
std::vector<double> beta(nr);
772+
std::copy(beta_raw.begin(), beta_raw.begin() + nr, beta.begin());
773+
774+
if (static_cast<int>(beta.size()) > num_mt_points()) {
775+
std::stringstream s;
776+
s << "wrong size of beta functions for atom type " << symbol_ << " (label: " << label_ << ")" << std::endl
777+
<< "size of beta radial functions in the file: " << beta.size() << std::endl
778+
<< "radial grid size: " << num_mt_points();
779+
RTE_THROW(s);
780+
}
781+
int l = beta_node.attribute("angular_momentum").as_int();
782+
if (spin_orbit_coupling_) {
783+
// we encode the fact that the total angular momentum j = l
784+
// -1/2 or l + 1/2 by changing the sign of l
785+
std::string bstr = "PP_RELBETA."+std::to_string(i+1);
786+
pugi::xml_node so_node = upf.child("PP_SPIN_ORB").child(bstr.data());
787+
788+
double j = so_node.attribute("jjj").as_double();
789+
if (j < (double)l) {
790+
l *= -1;
791+
}
792+
}
793+
// add_beta_radial_function(l, beta);
794+
if (spin_orbit_coupling_) {
795+
if (l >= 0) {
796+
add_beta_radial_function(angular_momentum(l, 1), beta);
797+
} else {
798+
add_beta_radial_function(angular_momentum(-l, -1), beta);
799+
}
800+
} else {
801+
add_beta_radial_function(angular_momentum(l), beta);
802+
}
803+
}
804+
805+
mdarray<double, 2> d_mtrx({nbf, nbf});
806+
d_mtrx.zero();
807+
auto v = vec_from_str<double>(upf.child("PP_NONLOCAL").child("PP_DIJ").child_value(), 0.5);
808+
809+
for (int i = 0; i < nbf; i++) {
810+
for (int j = 0; j < nbf; j++) {
811+
d_mtrx(i, j) = v[j * nbf + i];
812+
}
813+
}
814+
d_mtrx_ion(d_mtrx);
815+
816+
pugi::xml_node nl_node = upf.child("PP_NONLOCAL");
817+
if (!nl_node.child("PP_AUGMENTATION").empty()) {
818+
for (int i = 0; i<nbf; i++){
819+
std::string istr = "PP_BETA."+std::to_string(i+1);
820+
pugi::xml_node inode = nl_node.child(istr.data());
821+
int li = inode.attribute("angular_momentum").as_int();
822+
823+
for (int j = i; j<nbf; j++){
824+
std::string jstr = "PP_BETA."+std::to_string(j+1);
825+
pugi::xml_node jnode = nl_node.child(jstr.data());
826+
int lj = jnode.attribute("angular_momentum").as_int();
827+
828+
for (int l = abs(li - lj); l<li + lj + 1; l++){
829+
if ((li + lj + l )%2 != 0) {continue;}
830+
831+
std::string ijl_str = "PP_QIJL." + std::to_string(i+1) + "."
832+
+ std::to_string(j+1) + "." + std::to_string(l);
833+
pugi::xml_node ijl_node = nl_node.child("PP_AUGMENTATION").child(ijl_str.data());
834+
835+
auto qij = vec_from_str<double>(ijl_node.child_value(), 1.0);
836+
if ((int)qij.size() != num_mt_points()) {
837+
RTE_THROW("wrong size of qij");
838+
}
839+
add_q_radial_function(i, j, l, qij);
840+
}
841+
}
842+
}
843+
}
844+
845+
/* read starting wave functions ( UPF CHI ) */
846+
if (!header.attribute("number_of_wfc").empty()) {
847+
/* total number of pseudo atomic wave-functions */
848+
size_t nwf = header.attribute("number_of_wfc").as_int();
849+
/* loop over wave-functions */
850+
for (size_t k = 0; k < nwf; k++) {
851+
std::string wstr = "PP_CHI."+std::to_string(k+1);
852+
pugi::xml_node wfc_node = upf.child("PP_PSWFC").child(wstr.data());
853+
854+
auto v = vec_from_str<double>(wfc_node.child_value(), 1.0);
855+
856+
if ((int)v.size() != num_mt_points()) {
857+
std::stringstream s;
858+
s << "wrong size of atomic functions for atom type " << symbol_ << " (label: " << label_ << ")"
859+
<< std::endl
860+
<< "size of atomic radial functions in the file: " << v.size() << std::endl
861+
<< "radial grid size: " << num_mt_points();
862+
RTE_THROW(s);
863+
}
864+
865+
int l = wfc_node.attribute("l").as_int();
866+
int n = -1;
867+
double occ{0};
868+
if (!wfc_node.attribute("occupation").empty()){
869+
occ = wfc_node.attribute("occupation").as_double();
870+
}
871+
872+
if (!wfc_node.attribute("label").empty()) {
873+
auto c1 = wfc_node.attribute("label").as_string();
874+
std::istringstream iss(std::string(1, c1[0]));
875+
iss >> n;
876+
}
877+
878+
if (spin_orbit_coupling()){
879+
std::string bstr = "PP_RELWFC."+std::to_string(k+1);
880+
pugi::xml_node so_node = upf.child("PP_SPIN_ORB").child(bstr.data());
881+
if (!so_node.attribute("jchi").empty() && l != 0) {
882+
883+
std::string wstr = "PP_CHI."+std::to_string(k+2);
884+
pugi::xml_node wfc_node_p1 = upf.child("PP_PSWFC").child(wstr.data());
885+
886+
auto v1 = vec_from_str<double>(wfc_node_p1.child_value(), 1.0);
887+
double occ1{0};
888+
if (!wfc_node_p1.attribute("occupation").empty()) {
889+
occ1 = wfc_node_p1.attribute("occupation").as_double();
890+
}
891+
occ += occ1;
892+
for (int ir = 0; ir < num_mt_points(); ir++) {
893+
v[ir] = 0.5 * v[ir] + 0.5 * v1[ir];
894+
}
895+
k += 1;
896+
}
897+
}
898+
add_ps_atomic_wf(n, angular_momentum(l), v, occ);
899+
}
900+
}
901+
}
902+
903+
void
904+
Atom_type::read_pseudo_paw(pugi::xml_node const& upf)
905+
{
906+
is_paw_ = true;
907+
908+
/* read core energy */
909+
if (!upf.child("PP_PAW").attribute("core_energy").empty()) {
910+
paw_core_energy(0.5*upf.child("PP_PAW").attribute("core_energy").as_double());
911+
} else {
912+
paw_core_energy(0);
913+
}
914+
915+
/* cutoff index */
916+
int cutoff_radius_index = upf.child("PP_NONLOCAL").child("PP_AUGMENTATION")
917+
.attribute("cutoff_r_index").as_int();
918+
919+
/* read core density and potential */
920+
paw_ae_core_charge_density(
921+
vec_from_str<double>(upf.child("PP_PAW").child("PP_AE_NLCC").child_value(), 1.0));
922+
923+
/* read occupations */
924+
paw_wf_occ(
925+
vec_from_str<double>(upf.child("PP_PAW").child("PP_OCCUPATIONS").child_value(), 1.0));
926+
927+
/* setups for reading AE and PS basis wave functions */
928+
int num_wfc = num_beta_radial_functions();
929+
930+
/* read ae and ps wave functions */
931+
for (int i = 0; i < num_wfc; i++) {
932+
/* read ae wave func */
933+
std::string wstr = "PP_AEWFC."+std::to_string(i+1);
934+
pugi::xml_node wfc_node = upf.child("PP_FULL_WFC").child(wstr.data());
935+
auto wfc = vec_from_str<double>(wfc_node.child_value(), 1.0);
936+
937+
if ((int)wfc.size() > num_mt_points()) {
938+
std::stringstream s;
939+
s << "wrong size of ae_wfc functions for atom type " << symbol_ << " (label: " << label_ << ")" << std::endl
940+
<< "size of ae_wfc radial functions in the file: " << wfc.size() << std::endl
941+
<< "radial grid size: " << num_mt_points();
942+
RTE_THROW(s);
943+
}
944+
945+
add_ae_paw_wf(std::vector<double>(wfc.begin(), wfc.begin() + cutoff_radius_index));
946+
947+
wstr = "PP_PSWFC."+std::to_string(i+1);
948+
wfc_node = upf.child("PP_FULL_WFC").child(wstr.data());
949+
wfc = vec_from_str<double>(wfc_node.child_value(), 1.0);
950+
951+
if ((int)wfc.size() > num_mt_points()) {
952+
std::stringstream s;
953+
s << "wrong size of ps_wfc functions for atom type " << symbol_ << " (label: " << label_ << ")" << std::endl
954+
<< "size of ps_wfc radial functions in the file: " << wfc.size() << std::endl
955+
<< "radial grid size: " << num_mt_points();
956+
RTE_THROW(s);
957+
}
958+
959+
add_ps_paw_wf(std::vector<double>(wfc.begin(), wfc.begin() + cutoff_radius_index));
699960
}
961+
}
962+
963+
void Atom_type::read_input(nlohmann::json const& parser){
700964

701965
if (!parameters_.full_potential()) {
702966
read_pseudo_uspp(parser);
@@ -736,6 +1000,54 @@ Atom_type::read_input(std::string const& str__)
7361000
free_atom_radial_grid_ = Radial_grid_ext<double>(static_cast<int>(fa_r.size()), fa_r.data());
7371001
/* read density */
7381002
free_atom_density_ = parser["free_atom"]["density"].get<std::vector<double>>();
1003+
1004+
if (!parser) {
1005+
return;
1006+
}
1007+
}
1008+
}
1009+
1010+
void
1011+
Atom_type::read_input(pugi::xml_node const& upf){
1012+
if (!parameters_.full_potential()) {
1013+
read_pseudo_uspp(upf);
1014+
1015+
std::string pseudo_type = upf.child("PP_HEADER").attribute("pseudo_type").as_string();
1016+
if (pseudo_type == "PAW") {
1017+
read_pseudo_paw(upf);
1018+
}
1019+
} else {
1020+
RTE_THROW("Full potential calculations require JSON potential files.");
1021+
}
1022+
}
1023+
1024+
void
1025+
Atom_type::read_input(std::string const& str__)
1026+
{
1027+
// Read from SIRIUS json potential file
1028+
if (is_json_file(str__)) {
1029+
auto parser = read_json_from_file_or_string(str__);
1030+
1031+
if (parser.empty()) {
1032+
return;
1033+
}
1034+
1035+
read_input(parser);
1036+
1037+
// Read from standard UPF version 2 xml files
1038+
} else {
1039+
pugi::xml_document doc;
1040+
pugi::xml_parse_result parser = doc.load_file(str__.data());
1041+
1042+
if (!parser){
1043+
return;
1044+
}
1045+
1046+
pugi::xml_node upf = doc.child("UPF");
1047+
if (upf.empty()){
1048+
RTE_THROW("SIRIUS can only read UPF files with version >= 2. Use the upf_to_json tool.");
1049+
}
1050+
read_input(upf);
7391051
}
7401052

7411053
/* it is already done in input.h; here the different constans are initialized */

src/unit_cell/atom_type.hpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "core/r3/r3.hpp"
2626
#include "core/profiler.hpp"
2727
#include "core/packed_index.hpp"
28+
#include "pugixml.hpp"
2829

2930
namespace sirius {
3031

@@ -274,13 +275,25 @@ class Atom_type
274275
inline void
275276
read_pseudo_uspp(nlohmann::json const& parser);
276277

278+
inline void
279+
read_pseudo_uspp(pugi::xml_node const& upf);
280+
277281
inline void
278282
read_pseudo_paw(nlohmann::json const& parser);
279283

280-
/// Read atomic parameters from json file or string.
284+
inline void
285+
read_pseudo_paw(pugi::xml_node const& upf);
286+
287+
/// Read atomic parameters from json file.
281288
inline void
282289
read_input(std::string const& str__);
283290

291+
inline void
292+
read_input(nlohmann::json const& parser);
293+
294+
inline void
295+
read_input(pugi::xml_node const& upf);
296+
284297
/// Initialize descriptors of the augmented-wave radial functions.
285298
inline void
286299
init_aw_descriptors()

0 commit comments

Comments
 (0)