Program Listing for File phonon.hpp

Return to documentation for file (src/phonon.hpp)

/* This file is part of brille.

Copyright © 2020 Greg Tucker <greg.tucker@stfc.ac.uk>

brille is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.

brille is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with brille. If not, see <https://www.gnu.org/licenses/>.            */
#ifndef BRILLE_PHONON_HPP_
#define BRILLE_PHONON_HPP_
#include <array>
#include <tuple>
#include "array_latvec.hpp" // defines bArray
namespace brille {

class RotateTable{};

class GammaTable: public RotateTable {
public:
  using ind_t = unsigned;
private:
  std::vector<ind_t> point2space_;
  // use std::vectors instead of std::map for the mapping since we know the
  // total number of keys and how to calculate their positions in the vector
  ind_t n_atoms;
  ind_t n_sym_ops;
  std::vector<ind_t> l_mapping;
  std::vector<ind_t> v_mapping;
  Direct lattice_;
  bArray<double> vectors_;
public:
  explicit GammaTable(): n_atoms(0), n_sym_ops(0) {
    l_mapping.resize(0);
    v_mapping.resize(0);
  }
  GammaTable(const Direct& dlat, const int time_reversal=0){
    this->construct(dlat, time_reversal);
  }
  bool construct(const Direct& dlat, const int time_reversal=0){
    lattice_ = dlat;
    PointSymmetry ps = dlat.get_pointgroup_symmetry(time_reversal);
    Symmetry spgsym = dlat.get_spacegroup_symmetry(time_reversal);
    Basis bs = dlat.get_basis();
    // resize all vectors/arrays
    n_atoms = bs.size();
    n_sym_ops = ps.size();
    point2space_.resize(n_sym_ops);
    l_mapping.resize(n_atoms*n_sym_ops);
    v_mapping.resize(n_atoms*n_sym_ops);
    vectors_ = bArray<double>({n_atoms*n_sym_ops+1u, 3u}, 0.); // always put (0,0,0) first
    // construct a mapping of pointgroup indices to spacegroup indices
    // -- this mapping is likely not invertable, but it shouldn't (doesn't?)
    //    matter. I think.
    for (ind_t i=0; i<ps.size(); ++i){
      point2space_[i] = spgsym.find_matrix_index(ps.get(i));
      if (point2space_[i]>=spgsym.size()){
        info_update("The point group operation\n",ps.get(i),"was not found in the spacegroup!");
        throw std::runtime_error("Something has gone wrong with the correspondence of spacegroup to pointgroup");
      }
    }
    ind_t count{1u}; // for (0,0,0)
    // fill in the mappings
    for (ind_t k=0; k<bs.size(); ++k) for (ind_t r=0; r<ps.size(); ++r){
      bool found;
      ind_t l;
      auto motion = spgsym.getm(point2space_[r]);
      std::tie(found,l) = bs.equivalent_after_operation(k, motion);
      if (!found){
        info_update(bs.to_string(),"\ndoes not have an equivalent atom to ",k," for symmetry operation\n",motion.getr(),"+",motion.gett());
        throw std::runtime_error("All atoms in the basis *must* be mapped to an equivalent atom for *all* symmetry operations");
      }
      // calculate (R⁻¹ ⃗rₗ - ⃗rₖ)
      std::array<double,3> vec = motion.inverse().move_point(bs.position(l));
      auto rk = bs.position(k);
      for (int i=0; i<3; ++i) vec[i] -= rk[i];
      // check if this vector is in vectors_
      // look for an equal vector within the first count vectors_ -- return its index, or count if none match
      // count >= 1, so view is fine:
      ind_t v = norm(vectors_.view(0,count) - vec).is(brille::cmp::eq, 0.).first();
      // store the vector if its not already present
      if (count == v) vectors_.set(count++, vec);
      ind_t key = this->calc_key(k, r);
      l_mapping[key] = l;
      v_mapping[key] = v;
    }
    vectors_.resize(count); // not really necessary memory copy?
    return true;
  }
  template<class Ik, class Ir>
  ind_t F0(Ik k, Ir r) const {
    return l_mapping[this->calc_key(k,r)];
  }
  const bArray<double>& vectors() const {return vectors_;}
  template<class Ik, class Ir>
  ind_t vector_index(Ik k, Ir r) const {
    return v_mapping[this->calc_key(k,r)];
  }
  template<class Ik, class Ir>
  bArray<double> vector(Ik k, Ir r) const {
    return vectors_.extract(this->vector_index(k,r));
    // aternatively
    // return vectors_.view(this->vector_index(k,r));
  }
  template<class Ik, class Ir>
  LDVec<double> ldvector(Ik k, Ir r) const {
    return LDVec<double>(lattice_, this->vector(k,r));
  }
  const Direct& lattice() const {return lattice_;}
private:
  template<class Ik, class Ir> ind_t calc_key(Ik k, Ir r) const {
    if (k<n_atoms && r<n_sym_ops)
      return static_cast<ind_t>(k)*n_sym_ops + static_cast<ind_t>(r);
    throw std::runtime_error("Attempting to access out of bounds mapping!");
  }
};

} // namespace brille
#endif