.. _program_listing_file_src_hall_symbol.cpp: Program Listing for File hall_symbol.cpp ======================================== |exhale_lsh| :ref:`Return to documentation for file ` (``src/hall_symbol.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp /* This file is part of brille. Copyright © 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 . */ #include "hall_symbol.hpp" #include "bravais.hpp" // for bravais_letter using namespace brille; bool HallSymbol::from_ascii(const std::string& s){ char c; bool hassubsup; this->L = Bravais::_; hassubsup = s.find('^') != std::string::npos; hassubsup |= s.find('_') != std::string::npos; // bool hasspace = s.find(' ') != std::string::npos; bool isneg = false, issup = false, issub = false; SeitzSymbol ssym; std::istringstream stream(s); while (stream.good()){ c = char(stream.get()); // if c is one of the Lattice symbols: if (std::strchr("PABCIRF",c)){ this->setl(c, isneg); if (' '==stream.peek()) c = char(stream.get()); isneg = false; } // the subscripts a, b, c, n, u, v, w, d, 1, 2, 3, 4, 5 are all translation // specifications. All but 2, 3, 4 are unambiguous so add them to the // Setiz symbol and reset the subscript flag. // if a subscript specification has been given or the temporary Seitz symbol // has non-zero order then a translation symbol should be added for sure // // non-subscripted 1, 2, 3, 4, 6 are all rotation order symbols // they *should* specify the start of a Seitz symbol, so if the temporary // Seitz symbol has non-zero order we should add it to the list and start a // new temporary Setiz symbol // // any of x, y, z, ', ", or * represents an axis specification // add the axis to the Seitz symbol and reset the superscript flag if (hassubsup){ // superscript follows -- advance to the next character if ('^'==c) { issup = true; c = char(stream.get()); } // subscript follows -- advance to the next character if ('_'==c) { issub = true; c = char(stream.get()); } if (issub && std::strchr("abcnuvwd12345",c)) { ssym.add_tran(c); issub = false; } else if (std::strchr("12346",c)) { if (ssym.get_order()) this->addsymbol(ssym); ssym = SeitzSymbol((isneg?-1:1)*(c-'0')); isneg = false; issup = false; } if (issup && std::strchr("xyz'\"*",c)){ ssym.add_axis(c); issup = false; } } else /* without ^ or _ we can only distinguish symbols by spaces */{ if (ssym.get_order()){ if (std::strchr("abcnuvwd12345",c)) ssym.add_tran(c); if (std::strchr("xyz'\"*",c)) ssym.add_axis(c); } else if (std::strchr("12346",c)) { ssym = SeitzSymbol((isneg?-1:1)*(c-'0')); isneg = false; } } // capture what should have been an overbar but in ASCII is a preceeding - if ('-'==c) isneg = true; // a space between parts of the Hall symbol signifies a completed (set of) // Seitz matrix(s) if (' '==c) { // if the temporary Seitz symbol has non-zero order store it away if (ssym.get_order()) this->addsymbol(ssym); // reset everything for a possible next symbol ssym = SeitzSymbol(); issup = false; issub = false; isneg = false; } if ('('==c){ std::string tmp; std::getline(stream, tmp, ')'); this->V.from_ascii(tmp, true); } } // At the end of the string we might still have a valid temporary Seitz symbol if (ssym.get_order()) this->addsymbol(ssym); return this->validate(); } bool HallSymbol::validate(){ // verify that everything went ok bool ok = brille::bravais_is_known(this->L); for (auto ss: this->symbols) ok &= ss.validate(); // further validation could be possible by finding the full spacegroup return ok; } Symmetry HallSymbol::get_generators() const { Symmetry gen; Matrix r{{1,0,0, 0,1,0, 0,0,1}}; Vectors ts; double h{0.5}, t{1./3.}, d{2./3.}; switch (L){ case Bravais::P: ts = {{0,0,0}}; break; case Bravais::A: ts = {{0,0,0},{0,h,h}}; break; case Bravais::B: ts = {{0,0,0},{h,0,h}}; break; case Bravais::C: ts = {{0,0,0},{h,h,0}}; break; case Bravais::I: ts = {{0,0,0},{h,h,h}}; break; case Bravais::R: ts = {{0,0,0},{t,d,d},{d,t,t}}; break; case Bravais::F: ts = {{0,0,0},{0,h,h},{h,0,h},{h,h,0}}; break; default: throw std::runtime_error("Unknown lattice type"); } for (auto & onet: ts) gen.add(r, onet); if (centrosymmetric) { r = {{-1,0,0, 0,-1,0, 0,0,-1}}; for (auto & onet: ts) gen.add(r, onet); } // copy the symbols and ensure all axis specifiers are explicit: std::vector expsym; if (symbols.size()>0){ expsym.push_back(SeitzSymbol(symbols[0].get_order(), symbols[0].get_tran(), symbols[0].implicit_axis())); for (size_t i=1; i0){ gen.add(expsym[0].getr(), expsym[0].gett()); for (size_t i=1; iV.has_identity_rotation()) throw std::logic_error("HallSymbol can only handle origin shift change of basis, but can be extened if necessary."); if (!this->V.has_identity_translation()){ Vector invT = this->V.gett(); for (auto & x: invT) x *= -1; Motion invV(invT); // the rotation part is set to 𝟙 Symmetry::Motions motions; // each motion is replaced by V M V⁻¹ for (auto m: gen.getallm()) motions.push_back(this->V*(m*invV)); gen = Symmetry(motions); } return gen; } std::string HallSymbol::to_ascii() const { std::string s; if (this->centrosymmetric) s += "-"; s += brille::bravais_letter(this->L); for (auto z: this->symbols) s += " " + z.to_ascii(); return s; } std::string SeitzSymbol::to_ascii() const { std::string s = std::to_string(this->N) + this->A + this->T; return s; } bool SeitzSymbol::validate() { // If we include E and -E, N can be ±1, ±2, ±3, ±4, or ±6 if (N == 0 || N < -6 || N > 6 || N == -5 || N == 5) return false; // check the axis specification(s) int primes{0}, xyz{0}; for (char c: A) switch (c) { case '*': primes += 3; if (xyz) return false; break; case '"': primes += 2; if (xyz) return false; break; case '\'': ++primes; if (xyz) return false; break; case 'x': case 'y': case 'z': if (primes || xyz++) return false; break; // only one x, y, or z specification allowed default: return false; // non x, y, z, ', ", * not allowed } // replace valid combinations of ' and " if (primes==2) A = "\""; // ensure '' ≡ " if (primes==3) A = "*"; // and ''' ≡ *, if (primes>3) return false; // higher than 3 is a syntax error // if (xyz) -- everything should be fine // check the translation specification(s) std::string subscripts = "abcnuvwd12345"; // a full check isn't possible at this point due to (possibly) implied // rotation axes. instead just ensure that there aren't multiples of any // of the allowed subscripts if (T.size()>0) for (char c: subscripts) if (std::count_if(T.begin(), T.end(), [c](const char& d){return d==c;}) > 1) return false; // getting this far *should* mean that the SeitzSymbol is valid return true; } Matrix SeitzSymbol::getr() const { return this->inner_getr(this->implicit_axis(), '!'); } Matrix SeitzSymbol::getr(const SeitzSymbol& pre) const{ return this->inner_getr(this->implicit_axis(pre), pre.implicit_axis()); } Matrix SeitzSymbol::inner_getr(const char ax, const char pax) const { Matrix out{{0,0,0, 0,0,0, 0,0,0}}; switch (std::abs(N)){ case 1: out = { 1, 0, 0, 0, 1, 0, 0, 0, 1}; break; case 2: switch (ax) { case 'x': out = { 1, 0, 0, 0,-1, 0, 0, 0,-1}; break; case 'y': out = {-1, 0, 0, 0, 1, 0, 0, 0,-1}; break; case 'z': out = {-1, 0, 0, 0,-1, 0, 0, 0, 1}; break; // the special 2 preceeded by 3 or 6 case -> a-b (same as 2' preceeded by z) case '?': out = { 0,-1, 0, -1, 0, 0, 0, 0,-1}; break; case '\'': switch (pax) { case 'x': out = {-1, 0, 0, 0, 0,-1, 0,-1, 0}; break; case 'y': out = { 0, 0,-1, 0,-1, 0, -1, 0, 0}; break; case 'z': out = { 0,-1, 0, -1, 0, 0, 0, 0,-1}; break; default: throw std::runtime_error("Impossible pre-axis for ' in Seitz symbol"); } break; case '"': switch (pax) { case 'x': out = {-1, 0, 0, 0, 0, 1, 0, 1, 0}; break; case 'y': out = { 0, 0, 1, 0,-1, 0, 1, 0, 0}; break; case 'z': out = { 0, 1, 0, 1, 0, 0, 0, 0,-1}; break; default: throw std::runtime_error("Impossible pre-axis for \" in Seitz symbol"); } break; } break; case 3: switch (ax) { case 'x': out = { 1, 0, 0, 0, 0,-1, 0, 1,-1}; break; case 'y': out = {-1, 0, 1, 0, 1, 0, -1, 0, 0}; break; case 'z': out = { 0,-1, 0, 1,-1, 0, 0, 0, 1}; break; case '*': out = { 0, 0, 1, 1, 0, 0, 0, 1, 0}; break; } break; case 4: switch (ax) { case 'x': out = { 1, 0, 0, 0, 0,-1, 0, 1, 0}; break; case 'y': out = { 0, 0, 1, 0, 1, 0, -1, 0, 0}; break; case 'z': out = { 0,-1, 0, 1, 0, 0, 0, 0, 1}; break; } break; case 6: switch (ax) { case 'x': out = { 1, 0, 0, 0, 1,-1, 0, 1, 0}; break; case 'y': out = { 0, 0, 1, 0, 1, 0, -1, 0, 1}; break; case 'z': out = { 1,-1, 0, 1, 0, 0, 0, 0, 1}; break; } break; default: throw std::runtime_error("Impossible rotation order in Seitz symbol"); } // space invert the rotation if necessary if (N<0) for (int i=0; i<9; ++i) out[i] *= -1; return out; } Vector SeitzSymbol::gett() const { return this->inner_gett(std::abs(this->get_order()), this->implicit_axis(), '!'); } Vector SeitzSymbol::gett(const SeitzSymbol& pre) const { return this->inner_gett(std::abs(this->get_order()), this->implicit_axis(pre), pre.implicit_axis()); } Vector SeitzSymbol::inner_gett(const int an, const char ax, const char pax) const { Vector a{{0,0,0}}, t{{0,0,0}}; if (std::any_of(T.begin(), T.end(), [](const char c){return std::strchr("12345",c);})){ switch (ax) { case 'x': a = {1,0,0}; break; case 'y': a = {0,1,0}; break; case 'z': a = {0,0,1}; break; case '*': a = {1,1,1}; break; case '\'': switch (pax) { case 'x': a = {0,1,-1}; break; case 'y': a = {1,0,-1}; break; case 'z': a = {1,-1,0}; break; } break; case '"': switch (pax) { case 'x': a = {0,1,1}; break; case 'y': a = {1,0,1}; break; case 'z': a = {1,1,0}; break; } } } double h{0.5}, q{0.25}; for (auto & c: T) switch (c) { case 'a': t[0] += h; break; case 'b': t[1] += h; break; case 'c': t[2] += h; break; case 'n': t[0] += h; t[1] += h; t[2] += h; break; case 'u': t[0] += q; break; case 'v': t[1] += q; break; case 'w': t[2] += q; break; case 'd': t[0] += q; t[1] += q; t[2] += q; break; case '1': for (int i=0; i < 3; ++i) t[i] += a[i]/static_cast(an); break; case '2': for (int i=0; i < 3; ++i) t[i] += 2*a[i]/static_cast(an); break; case '3': for (int i=0; i < 3; ++i) t[i] += 3*a[i]/static_cast(an); break; case '4': for (int i=0; i < 3; ++i) t[i] += 4*a[i]/static_cast(an); break; case '5': for (int i=0; i < 3; ++i) t[i] += 5*a[i]/static_cast(an); break; } // translations are bounded by [0,1) for (int i=0; i < 3; ++i) t[i] -= std::floor(t[i]); return t; }