[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/pplab/abacus-develop/develop/source/source_io/cif_io.cpp [Back]  [Original]

#include 
#include 
#include 
#include 
#include "source_base/formatter.h"
#include "source_io/cif_io.h"
#include 
#include 
#include "source_base/tool_quit.h"
#ifdef __MPI
#include "source_base/parallel_common.h"
#endif

double deg2rad(double deg) { return deg * M_PI / 180.0; }
double rad2deg(double rad) { return rad * 180.0 / M_PI; }

void _build_chem_formula(const int natom, 
                         const std::string* atom_site_labels,
                         std::string& sum,
                         std::string& structural)
{
    sum.clear();
    structural.clear();
    std::vector kinds;
    std::vector labels(natom);
    std::copy(atom_site_labels, atom_site_labels + natom, labels.begin());
    for (int i = 0; i < natom; ++i)
    {
        if (std::find(kinds.begin(), kinds.end(), labels[i]) == kinds.end())
        {
            kinds.push_back(labels[i]);
        }
    }
    std::vector counts(kinds.size());
    std::transform(kinds.begin(), kinds.end(), counts.begin(), [&labels](const std::string& kind) {
        return std::count(labels.begin(), labels.end(), kind);
    });
    for (size_t i = 0; i < kinds.size(); ++i)
    {
        sum += kinds[i];
        structural += kinds[i];
        if (counts[i] > 1)
        {
            sum += std::to_string(counts[i]);
        }
    }
}

void vec_to_abc_angles(const double* vec, double* abc_angles)
{
    const std::vector a = {vec[0], vec[1], vec[2]};
    const std::vector b = {vec[3], vec[4], vec[5]};
    const std::vector c = {vec[6], vec[7], vec[8]};
    const double anorm = std::sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
    const double bnorm = std::sqrt(b[0] * b[0] + b[1] * b[1] + b[2] * b[2]);
    const double cnorm = std::sqrt(c[0] * c[0] + c[1] * c[1] + c[2] * c[2]);
    const double alpha = std::acos((b[0] * c[0] + b[1] * c[1] + b[2] * c[2]) / (bnorm * cnorm));
    const double beta = std::acos((a[0] * c[0] + a[1] * c[1] + a[2] * c[2]) / (anorm * cnorm));
    const double gamma = std::acos((a[0] * b[0] + a[1] * b[1] + a[2] * b[2]) / (anorm * bnorm));
    abc_angles[0] = anorm;
    abc_angles[1] = bnorm;
    abc_angles[2] = cnorm;
    abc_angles[3] = rad2deg(alpha);
    abc_angles[4] = rad2deg(beta);
    abc_angles[5] = rad2deg(gamma);
}
void abc_angles_to_vec(const double* abc_angles, double* vec)
{
    const double a = abc_angles[0];
    const double b = abc_angles[1];
    const double c = abc_angles[2];
    const double alpha = abc_angles[3];
    const double beta = abc_angles[4];
    const double gamma = abc_angles[5];
    vec[0] = a;
    vec[1] = 0.0;
    vec[2] = 0.0;
    vec[3] = b * std::cos(deg2rad(gamma));
    vec[4] = b * std::sin(deg2rad(gamma));
    vec[5] = 0.0;
    vec[6] = c * std::cos(deg2rad(beta));
    vec[7] = c * (std::cos(deg2rad(alpha)) - std::cos(deg2rad(beta)) * std::cos(deg2rad(gamma))) / std::sin(deg2rad(gamma));
    vec[8] = std::sqrt(c * c - vec[6] * vec[6] - vec[7] * vec[7]);
}
double vec_to_volume(const double* vec)
{
    // vector's mixed product
    return vec[0] * (vec[4] * vec[8] - vec[5] * vec[7]) - vec[1] * (vec[3] * vec[8] - vec[5] * vec[6]) + vec[2] * (vec[3] * vec[7] - vec[4] * vec[6]);
}
double abc_angles_to_volume(const double* abc_angles)
{
    std::vector vec(9);
    abc_angles_to_vec(abc_angles, vec.data());
    return vec_to_volume(vec.data());
}
std::vector _split_outside_enclose(const std::string& in, 
                                                const std::string& delim,
                                                const std::vector& enclose)
{
    // a very naive impl. for only CIF possible cases
    assert(enclose.size() == 2); // other complicated case not implemented yet
    // first split with delim. then scan all fragments, if there are enclose symbol, then will first meet
    // a fragment startswith the opening, then after fragments, there will be a one ends with closing.
    // between them, fragments will be concatenated with delim.
    std::vector out;
    std::string cache;
    std::vector words = FmtCore::split(in, delim);
    bool in_enclose = false;
    for (auto& word: words)
    {
        if (FmtCore::startswith(word, enclose[0]))
        {
            in_enclose = true;
            cache += word + delim;
        }
        else if (FmtCore::endswith(word, enclose[1]))
        {
            in_enclose = false;
            cache += word;
            out.push_back(cache);
            cache.clear();
        }
        else
        {
            if (in_enclose)
            {
                cache += word + delim;
            }
            else
            {
                out.push_back(word);
            }
        }
    }
    return out;
}

// the second step, for each block, split with words starting with "_"
std::vector _split_loop_block(const std::string& block)
{
    std::vector out;
    std::string word, cache;
    std::stringstream ss(FmtCore::strip(FmtCore::strip(block, "\n")));
    while (ss.good())
    {
        ss >> word;
        if (FmtCore::startswith(word, "_"))
        {
            if (!cache.empty())
            {
                out.push_back(FmtCore::strip(cache));
                cache.clear();
            }
            out.push_back(FmtCore::strip(word));
        }
        else
        {
            cache += word + " ";
        }
    }
    // the last word
    if (!cache.empty())
    {
        out.push_back(FmtCore::strip(cache));
    }
    return out;
}

std::map _build_table(const std::vector& keys,
                                                             const std::vector& values)
{
    std::map out;
    const size_t ncols = keys.size();
    assert(values.size() % ncols == 0);
    const size_t nrows = values.size() / ncols;
    for (size_t i = 0; i < ncols; i++)
    {
        std::vector col(nrows);
        for (size_t j = 0; j < nrows; j++)
        {
            col[j] = values[j * ncols + i];
        }
        out[keys[i]] = col;
    }
    return out;
}

std::map _build_block_data(const std::vector& block)
{
    // after calling the _split_loop_block, the data now composed of elements that either startswith "_"
    // or not. Between elements startswith "_", there is at most one element that does not startswith "_".
    // a scan can be performed to group those keys.
    std::vector keys;
    std::vector kcache;
    std::vector values;
    // first drop all elements that does not startswith "_" before the first element that startswith "_"
    std::vector block_ = block;
    auto it = std::find_if(block.begin(), block.end(), [](const std::string& s) { return FmtCore::startswith(s, "_"); });
    if (it != block.begin())
    {
        block_.erase(block_.begin(), it);
    }
    for (auto& elem: block_)
    {
        if (FmtCore::startswith(elem, "_"))
        {
            kcache.push_back(elem);
        }
        else
        {
            keys.push_back(kcache);
            values.push_back(elem);
            kcache.clear();
        }
    }
    assert(keys.size() == values.size()); // ensure the number of keys and values are the same
    // then for each elem in keys, if there are more than one element, then it is a table. Make it a table
    // , otherwise it is a simple key-value pair, directly add it to the output.
    std::map out;
    for (size_t i = 0; i < keys.size(); i++)
    {
        if (keys[i].size() > 1)
        {
            const std::vector words = _split_outside_enclose(values[i], " ", {"'", "'"});
            std::map table = _build_table(keys[i], words);
            out.insert(table.begin(), table.end());
        }
        else
        {
            out[keys[i][0]] = {values[i]};
        }
    }
    
    return out;
}

void bcast_cifmap(std::map& map, // the map to be broadcasted 
                  const int rank = 0)   // source rank: from which rank to broadcast
{
#ifdef __MPI
    int myrank;
    MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
    // use Parallel_Common namespace bcast_int and bcast_string to broadcast the size of map and key, value pairs
    int size = map.size();
    Parallel_Common::bcast_int(size); // seems it can only broadcast from rank 0, so presently no need to specify
                                      // the rank to broadcast
    std::vector keys(size);
    std::vector values(size);
    int i = 0;
    if (myrank == rank) // if the rank is the source rank, then pack the map to keys and values
    {
        for (auto& elem: map)
        {
            keys[i] = elem.first;
            values[i] = elem.second;
            i++;
        }
    }
    for (int i = 0; i < size; i++)
    {
        Parallel_Common::bcast_string(keys[i]);
        int valsize = values[i].size();
        Parallel_Common::bcast_int(valsize);
        values[i].resize(valsize);
        Parallel_Common::bcast_string(values[i].data(), valsize);
    }
    if (myrank != rank) // if the rank is not the source rank, then unpack the keys and values to map
    {
        map.clear();
        for (int i = 0; i < size; i++)
        {
            map[keys[i]] = values[i];
        }
    }
#endif
}

void ModuleIO::CifParser::_unpack_ucell(const UnitCell& ucell,
                                        std::vector& veca,
                                        std::vector& vecb,
                                        std::vector& vecc,
                                        int& natom,
                                        std::vector& atom_site_labels,
                                        std::vector& atom_site_fract_coords)
{
    const double bohr2angstrom = 0.52917721067;
    const double lat0 = ucell.lat.lat0;
    veca.resize(3);
    vecb.resize(3);
    vecc.resize(3);
    veca[0] = ucell.a1.x;
    veca[1] = ucell.a1.y;
    veca[2] = ucell.a1.z;
    vecb[0] = ucell.a2.x;
    vecb[1] = ucell.a2.y;
    vecb[2] = ucell.a2.z;
    vecc[0] = ucell.a3.x;
    vecc[1] = ucell.a3.y;
    vecc[2] = ucell.a3.z;
    std::for_each(veca.begin(), veca.end(), [lat0, bohr2angstrom](double& x) { x *= lat0 * bohr2angstrom; });
    std::for_each(vecb.begin(), vecb.end(), [lat0, bohr2angstrom](double& x) { x *= lat0 * bohr2angstrom; });
    std::for_each(vecc.begin(), vecc.end(), [lat0, bohr2angstrom](double& x) { x *= lat0 * bohr2angstrom; });
    natom = ucell.nat;
    assert(natom > 0); // ensure the number of atoms is positive
    atom_site_labels.resize(natom);
    atom_site_fract_coords.resize(3 * natom);
    for (int i = 0; i < natom; ++i)
    {
        atom_site_labels[i] = ucell.atoms[ucell.iat2it[i]].ncpp.psd; // the most standard label
        atom_site_labels[i] = atom_site_labels[i].empty() ? ucell.atom_label[ucell.iat2it[i]]: atom_site_labels[i];
        atom_site_labels[i] = atom_site_labels[i].empty() ? ucell.atoms[ucell.iat2it[i]].label: atom_site_labels[i];
        assert(!atom_site_labels[i].empty()); // ensure the label is not empty
        atom_site_fract_coords[3 * i] = ucell.atoms[ucell.iat2it[i]].taud[ucell.iat2ia[i]].x;
        atom_site_fract_coords[3 * i + 1] = ucell.atoms[ucell.iat2it[i]].taud[ucell.iat2ia[i]].y;
        atom_site_fract_coords[3 * i + 2] = ucell.atoms[ucell.iat2it[i]].taud[ucell.iat2ia[i]].z;
    }
}

void ModuleIO::CifParser::write(const std::string& fcif,
                                const double* abc_angles,
                                const int natom,
                                const std::string* atom_site_labels,
                                const double* atom_site_fract_coords,
                                const std::string& title,
                                const std::string& data_tag,
                                const int rank,
                                const double* atom_site_occups,
                                const std::string& cell_formula_units_z)
{
#ifdef __MPI // well...very simple...
    int myrank;
    MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
    if (myrank != rank) // if present rank is not the rank assigned to write the cif file, then return
    {
        return;
    }
#endif
    std::ofstream ofs(fcif);
    if (!ofs)
    {
        ModuleBase::WARNING_QUIT("ModuleIO::CifParser::write", "Cannot open file " + fcif);
    }
    ofs 

Web Proxy Viewer  |  New URL  |  Original Page