.. _program_listing_file_src_interpolatordual.hpp: Program Listing for File interpolatordual.hpp ============================================= |exhale_lsh| :ref:`Return to documentation for file ` (``src/interpolatordual.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp /* This file is part of brille. Copyright © 2019,2020 Greg Tucker 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 . */ #ifndef BRILLE_DUALINTERPOLATOR_H_ #define BRILLE_DUALINTERPOLATOR_H_ #include "interpolator2.hpp" namespace brille { template class DualInterpolator{ public: using ind_t = brille::ind_t; template using element_t = std::array; private: Interpolator values_; Interpolator vectors_; PermutationTable permutation_table_; public: DualInterpolator(): values_(), vectors_(), permutation_table_(0,0) {}; DualInterpolator(Interpolator& val, Interpolator& vec) : values_(val), vectors_(vec), permutation_table_(0,0) { if (values_.size() != vectors_.size() || values_.branches() != vectors_.branches()) throw std::runtime_error("Inconsistent values and vectors provided to DualInterpolator"); } // void validate_values() { if (values_.size()!=vectors_.size() || values_.branches()!=vectors_.branches()) values_.setup_fake(vectors_.size(), vectors_.branches()); } void validate_vectors() { if (values_.size()!=vectors_.size() || values_.branches()!=vectors_.branches()) vectors_.setup_fake(values_.size(), values_.branches()); } // size_t size() const { assert(values_.size() == vectors_.size()); return values_.size(); } const Interpolator& values() const {return this->values_;} const Interpolator& vectors() const {return this->vectors_;} ind_t branches() const { assert(values_.branches() == vectors_.branches()); return values_.branches(); } void rotateslike(const RotatesLike values, const RotatesLike vectors) { values_.rotateslike(values); vectors_.rotateslike(vectors); } RotatesLike values_rotate_like(const RotatesLike a){ return values_.rotateslike(a); } RotatesLike vectors_rotate_like(const RotatesLike a){ return vectors_.rotateslike(a); } // // template class A> // void // interpolate_at(const std::vector& indices, const std::vector& weights, A& values_out, A& vectors_out, const ind_t to) const { // auto permutations = this->get_permutations(indices); // values_.interpolate_at(permutations, indices, weights, values_out, to, false); // vectors_.interpolate_at(permutations, indices, weights, vectors_out, to, true); // } template class A> void interpolate_at(const std::vector>& indices_weights, A& values_out, A& vectors_out, const ind_t to) const { auto permutations = this->get_permutations(indices_weights); values_.interpolate_at(permutations, indices_weights, values_out, to, false); vectors_.interpolate_at(permutations, indices_weights, vectors_out, to, true); } // template::value> > std::vector get_permutation(const I, const I) const; template::value> > std::vector> get_permutations(const std::vector&) const; template::value> > std::vector> get_permutations(const std::vector>&) const; // // Replace the data within this object. void replace_data(Interpolator& val, Interpolator& vec){ if (vec.size() != val.size() || vec.branches() != val.branches()) throw std::runtime_error("The values and vectors must have matching number of points and branches"); values_ = val; vectors_ = vec; this->update_permutation_table(); } template void replace_value_data(A... args) { values_.replace_data(args...); this->validate_vectors(); this->update_permutation_table(); } template void replace_vector_data(A... args) { vectors_.replace_data(args...); this->validate_values(); this->update_permutation_table(); } // used during holding-object initialisation void initialize_permutation_table(const size_t nverts, const std::set& keys){ this->permutation_table_ = PermutationTable(nverts, this->branches(), keys); } void update_permutation_table(){ // preserve the keys in the permutation table, if possible this->permutation_table_.refresh(this->size(), this->branches()); } // void set_value_cost_info(const int csf, const int cvf, const element_t& elcost){ values_.set_cost_info(csf, cvf, elcost); } void set_vector_cost_info(const int csf, const int cvf, const element_t& elcost){ vectors_.set_cost_info(csf, cvf, elcost); } // create a string representation of the values and vectors std::string to_string() const { std::string str = "value " + values_.to_string() + " vector " + vectors_.to_string(); return str; } // Calculate the Debye-Waller factor for the provided Q points and ion masses // This returns a 1-D brille::Array (so it can't be a brille::Array2) template class A> brille::Array debye_waller(const A& Qpts, const std::vector& Masses, const double t_K) const; // template::type, typename=std::enable_if_t::value> > std::vector cost_matrix(const I i0, const I i1) const; template::value>> void permute_modes(const I i, const std::vector& p){ values_.permute_modes(i, p); vectors_.permute_modes(i, p); } template::value>> void inverse_permute_modes(const I i, const std::vector& p){ values_.inverse_permute_modes(i, p); vectors_.inverse_permute_modes(i, p); } template::value>> bool is_degenerate(const I idx) { return values_.any_equal_modes(idx); // we could try and do something fancier, but it's probaby not useful. } void sort(void); template::value>> bool determine_permutation_ij(const I i, const I j, std::mutex& map_mutex); size_t bytes_per_point() const { return values_.bytes_per_point() + vectors_.bytes_per_point(); } private: bArray debye_waller_sum(const bArray& Qpts, const double t_K) const; bArray debye_waller_sum(const LQVec& Qpts, const double beta) const{ return this->debye_waller_sum(Qpts.get_xyz(), beta); } }; template bArray DualInterpolator::debye_waller_sum(const bArray& Qpts, const double t_K) const { const double hbar = 6.582119569E-13; // meV⋅s const double kB = 8.617333252E-2; // meV⋅K⁻¹ size_t nQ = Qpts.size(0); element_t vector_elements = vectors_.elements(); size_t nIons = vector_elements[1] / 3u; // already checked to be correct bArray WdQ(nQ,nIons); // Wᵈ(Q) has nIons entries per Q point double coth_en, Q_dot_e_2; size_t vector_nq = vectors_.size(); ind_t nbr = vectors_.branches(); const double beta = kB*t_K; // meV const double pref{hbar*hbar/static_cast(2*vector_nq)}; // meV²⋅s² const auto& val{ values_.data()}; const auto& vec{vectors_.data()}; ind_t vecsp = vectors_.branch_span(); // for each input Q point for (size_t Qidx=0; Qidx/ħωₛ ≡ coth(2ħωₛβ)/ħωₛ coth_en = brille::utils::coth_over_en(val.val(q,j), beta); // and find |Q⋅ϵₛ|². Note: brille::utils::vector_product(x,y) *is* |x⋅y|² Q_dot_e_2 = brille::utils::vector_product(3u, Qpts.ptr(Qidx), vec.ptr(q,j*vecsp+3u*d)); // adding |Q⋅ϵₛ|²coth(2ħωₛβ)/ħωₛ to the sum over s for [Qidx, d] qj_sum += Q_dot_e_2 * coth_en; } } // with the sum over s complete, normalize by ħ²/2 divided by the number // of points in the Brillouin zone and store the result at W[Qidx, d]; WdQ.val(Qidx,d) = qj_sum*pref; } } return WdQ; } template template class A> brille::Array DualInterpolator::debye_waller(const A& Qpts, const std::vector& Masses, const double t_K) const { element_t vector_elements = vectors_.elements(); size_t nIons = vector_elements[1] / 3u; if (0 == nIons || vector_elements[1] != nIons*3u) throw std::runtime_error("Debye-Waller factor requires 3-vector eigenvector(s)."); if (Masses.size() != nIons) throw std::runtime_error("Debye-Waller factor requires an equal number of ions and masses."); auto WdQ = this->debye_waller_sum(Qpts, t_K); // {nQ, nAtoms} brille::shape_t fshape{Qpts.size(0)}; // (nQ,) brille::Array factor(fshape); // we don't want an Array2 here! double d_sum; for (size_t Qidx=0; Qidx template // std::vector // DualInterpolator::get_permutation(const I i, const I j) const { // std::vector perm; // /* // Since missing permutations are added, and might be the same for two threads, // we can not allow multiple threads to perform their get call at the same time // otherwise the table might end up with Nthread copies of every unique // permutation vector. // This is probably an unacceptable performance hit. // */ // #pragma omp critical // { // perm = permutation_table_.safe_get(i, j); // // perm is empty if i:j is not present in the table // if (perm.empty()){ // jv_permutation_fill(this->cost_matrix(i, j), perm); // permutation_table_.set(i, j, perm); // } // } // // jv_permutation_fill(this->cost_matrix(i, j), perm); // // perm = jv_permutation(this->cost_matrix(i, j)); // // and return it // return perm; // } template template std::vector DualInterpolator::get_permutation(const I i, const I j) const { return permutation_table_.safe_get(i, j); } template template std::vector> DualInterpolator::get_permutations(const std::vector& indices) const { std::vector> perms; // find the minimum index so that permutation(pvt,idx) is always ordered I pvt{indices[0]}; //for (const I idx: indices) if (idx < pvt) pvt = idx; for (const I idx: indices) perms.push_back(this->get_permutation(pvt, idx)); return perms; } template template std::vector> DualInterpolator::get_permutations(const std::vector>& iw) const { std::vector> perms; I pvt{iw[0].first}; //for (const auto piw: iw) if (piw.first < pvt) pvt = piw.first; for (const auto piw: iw) perms.push_back(this->get_permutation(pvt, piw.first)); return perms; } template template std::vector DualInterpolator::cost_matrix(const I i0, const I i1) const { ind_t Nbr{this->branches()}; std::vector cost(Nbr*Nbr, S(0)); if (i0==i1){ for (ind_t j=0; j void DualInterpolator::sort(void){ std::set keys = permutation_table_.keys(); // find the keys corresponding to one triangular part of the matrix (i> tri_ij; tri_ij.reserve(keys.size()/2); size_t no = this->size(); for (const auto & key: keys){ size_t i = key/no; if (i*(no+1) < key) tri_ij.push_back({i, key-i*no}); } debug_update("Finding permutations for ",keys.size()," connections between the ",no," vertices"); // now find the permutations in parallel std::mutex m; long long nok = brille::utils::u2s(tri_ij.size()); #pragma omp parallel for default(none) shared(tri_ij, m, nok) for (long long sk=0; sk(sk); this->determine_permutation_ij(tri_ij[k][0], tri_ij[k][1], m); } debug_update("Done"); } template template bool DualInterpolator::determine_permutation_ij(const I i, const I j, std::mutex& map_mutex){ // if (!permutation_table_.value_needed(i,j)) return false; std::vector row, col; // jv_permutation has difficulty with unsigned integers jv_permutation_fill(this->cost_matrix(i,j), row, col); std::unique_lock map_lock(map_mutex); permutation_table_.overwrite(i, j, row); permutation_table_.overwrite(j, i, col); map_lock.unlock(); return true; } } // namespace brille #endif