7#ifndef gbLAB_Lattice_cpp_
8#define gbLAB_Lattice_cpp_
10#include <Eigen/Eigenvalues>
48 ,reciprocalBasis(latticeBasis.inverse().transpose())
58 RLLL rlll(latticeBasis,0.75);
69 const double crossNorm(sqrt(G.determinant()));
72 std::cout<<
"input direction="<<d.normalized().transpose()<<std::endl;
73 std::cout<<
"lattice direction="<<temp.
cartesian().normalized().transpose()<<std::endl;
74 std::cout<<
"cross product norm="<<std::setprecision(15)<<std::scientific<<crossNorm<<std::endl;
75 std::cout<<
"tolerance="<<std::setprecision(15)<<std::scientific<<tol<<std::endl;
76 throw std::runtime_error(
"LATTICE DIRECTION NOT FOUND\n");
85 RLLL rlll(latticeBasis,0.75);
96 const double crossNorm(sqrt(abs(G.determinant())));
99 std::cout<<
"input direction="<<std::setprecision(15)<<std::scientific<<d.normalized().transpose()<<std::endl;
100 std::cout<<
"reciprocal lattice direction="<<std::setprecision(15)<<std::scientific<<temp.
cartesian().normalized().transpose()<<std::endl;
101 std::cout<<
"cross product norm="<<std::setprecision(15)<<std::scientific<<crossNorm<<std::endl;
102 std::cout<<
"tolerance="<<std::setprecision(15)<<std::scientific<<tol<<std::endl;
103 throw std::runtime_error(
"RECIPROCAL LATTICE DIRECTION NOT FOUND\n");
112 const double& magnitudeTol,
113 const double& directionTol)
const
119 if ((rld.
cartesian() - d).squaredNorm() > magnitudeTol)
121 std::cout <<
"input vector=" << d.transpose() << std::endl;
122 std::cout <<
"lattice direction=" << ld.cartesian().transpose() << std::endl;
123 std::cout <<
"rational=" << rat << std::endl;
124 std::cout <<
"d.norm()/ld.cartesian().norm()=" << d.norm() / ld.latticeVector().norm() << std::endl;
125 throw std::runtime_error(
"Rational Lattice DirectionType NOT FOUND\n");
135 const double& magnitudeTol,
136 const double& directionTol)
const
142 if ((rrld.
cartesian() - d).squaredNorm() > magnitudeTol)
144 std::cout <<
"input reciprocal vector=" << d.transpose() << std::endl;
145 std::cout <<
"reciprocal lattice direction=" << rld.
cartesian().transpose() << std::endl;
146 std::cout <<
"rational=" << rat << std::endl;
147 std::cout <<
"d.norm()/rld.cartesian().norm()=" << d.norm() / rld.
reciprocalLatticeVector().norm() << std::endl;
148 throw std::runtime_error(
"Rational Reciprocal Lattice DirectionType NOT FOUND\n");
170 const bool& useRLLL)
const
172 assert(
this == &l.
lattice &&
"Vectors belong to different Lattices.");
175 std::vector<LatticeDirection<dim>> out;
177 for(
const auto& column : matrix.colwise())
180 if (columnIndex != 0)
186 if(!useRLLL)
return out;
188 Eigen::MatrixXd planeParallelLatticeBasisCartesian(dim,dim-1);
190 for(
auto it=std::next(out.begin()); it!=out.end(); ++it)
192 planeParallelLatticeBasisCartesian.col(index)= (*it).cartesian();
197 const auto& rlll=
RLLL(planeParallelLatticeBasisCartesian, 0.75);
198 planeParallelLatticeBasisCartesian= rlll.reducedBasis();
200 for (
int i=0; i<dim; ++i)
201 outIntegerCoords.col(i)= out[i].latticeVector();
202 Eigen::Matrix<
IntScalarType,dim,dim-1> planeParallelLatticeBasisIntegerCoords= outIntegerCoords.block(0,1,dim,dim-1)*rlll.unimodularMatrix();
203 for (
int i=1; i<dim; ++i)
218 Eigen::MatrixXd pseudoInverse(dim-1,dim);
219 pseudoInverse= (planeParallelLatticeBasisCartesian.transpose() * planeParallelLatticeBasisCartesian).inverse() *
220 (planeParallelLatticeBasisCartesian.transpose());
223 for(
int i=0;i<dim-1;i++)
225 temp= temp + round(out[0].cartesian().dot(pseudoInverse.row(i)))*out[i+1].latticeVector();
236 const bool& useRLLL)
const
238 assert(
this == &l.
lattice &&
"Vectors belong to different Lattices.");
241 std::vector<ReciprocalLatticeDirection<dim>> out;
243 for(
const auto& column : matrix.colwise())
246 if (columnIndex != 0) {
255 if (!useRLLL)
return out;
257 Eigen::MatrixXd directionOrthogonalReciprocalLatticeBasisCartesian(dim,dim-1);
259 for(
auto it=std::next(out.begin()); it!=out.end(); ++it)
261 directionOrthogonalReciprocalLatticeBasisCartesian.col(index)= (*it).cartesian();
265 if constexpr(dim>2) {
266 const auto &rlll =
RLLL(directionOrthogonalReciprocalLatticeBasisCartesian, 0.75);
268 for (
int i = 0; i < dim; ++i)
269 outIntegerCoords.col(i) = out[i].reciprocalLatticeVector();
270 Eigen::Matrix<
IntScalarType, dim, dim - 1> directionOrthogonalReciprocalLatticeBasisIntegerCoords =
271 outIntegerCoords.block(0, 1, dim, dim - 1) * rlll.unimodularMatrix();
272 for (
int i = 1; i < dim; ++i)
274 directionOrthogonalReciprocalLatticeBasisIntegerCoords.col(i - 1), *
this);
277 Eigen::MatrixXd pseudoInverse(dim-1,dim);
278 pseudoInverse= (directionOrthogonalReciprocalLatticeBasisCartesian.transpose() * directionOrthogonalReciprocalLatticeBasisCartesian).inverse() *
279 (directionOrthogonalReciprocalLatticeBasisCartesian.transpose());
282 for(
int i=0;i<dim-1;i++)
284 temp= temp + round(out[0].cartesian().dot(pseudoInverse.row(i)))*out[i+1].reciprocalLatticeVector();
306 throw(std::runtime_error(
"The input reciprocal lattice vectors does not belong to the current lattice."));
310 template<
int dim>
template<
int dm>
311 typename std::enable_if<dm==3,std::vector<typename Lattice<dim>::MatrixDimD>>::type
314 std::vector<MatrixDimD> output;
315 std::map<IntScalarType,MatrixDimD> temp;
316 auto basis= planeParallelLatticeBasis(rd);
320 auto b1= basis[1].cartesian();
321 auto b2= basis[2].cartesian();
326 std::vector<std::pair<int,int>> coPrimePairs=
farey(N,
false);
327 for(
const auto& pair : coPrimePairs)
330 auto q2= pair.first*b1+pair.second*b2;
332 if (abs(q2.norm())<epsilon )
continue;
333 double ratio= q2.norm()/q1.norm();
335 double error= ratio-
static_cast<double>(bra.
num)/bra.
den;
336 if (abs(error) > epsilon)
340 double cosTheta= b1.dot(q2)/(q1.norm()*q2.norm());
341 if (cosTheta-1>-epsilon) cosTheta= 1.0;
342 if (cosTheta+1<epsilon) cosTheta= -1.0;
343 double theta= acos(cosTheta);
345 rotation= Eigen::AngleAxis<double>(theta,rd.
cartesian().normalized());
347 temp.insert(std::pair<IntScalarType,MatrixDimD>(key,rotation));
350 std::transform(temp.begin(), temp.end(),
351 std::back_inserter(output),
352 [](
const std::pair<IntScalarType,MatrixDimD>& p) {
359 template<
int dim>
template<
int dm>
360 typename std::enable_if<dm==2,std::vector<typename Lattice<dim>::MatrixDimD>>::type
363 std::vector<MatrixDimD> output(generateCoincidentLattices(*
this,maxStrain,maxDen,N));
368 template<
int dim>
template<
int dm>
369 typename std::enable_if<dm==2,std::vector<typename Lattice<dim>::MatrixDimD>>::type
371 const double& maxStrain,
375 int numberOfConfigurations= 0;
376 const int maxConfigurations= 80;
377 std::vector<MatrixDimD> output;
378 std::map<IntScalarType,MatrixDimD> temp;
381 std::vector<std::pair<int,int>> coPrimePairs=
farey(N,
false);
385 for(
const auto& pair1 : coPrimePairs)
388 pIndices << pair1.first, pair1.second;
390 double ratio= q2.
cartesian().norm() / latticeBasis.col(0).norm();
393 if (abs(maxStrain) < epsilon)
396 double error = ratio -
static_cast<double>(alpha.
num) / alpha.
den;
397 if (abs(error) > epsilon)
401 double cosTheta = latticeBasis.col(0).dot(q2.
cartesian()) / (latticeBasis.col(0).norm() * q2.
cartesian().norm());
402 if (cosTheta - 1 > -epsilon) cosTheta = 1.0;
403 if (cosTheta + 1 < epsilon) cosTheta = -1.0;
404 double theta = acos(cosTheta);
405 Eigen::Rotation2D<double> rotation(theta);
407 temp.insert(std::pair<IntScalarType,MatrixDimD>(key,rotation.toRotationMatrix()));
412 RationalApproximations <IntScalarType> alphaSequence(ratio, maxDen, ratio*maxStrain);
417 mn.col(0) = q2 * alpha.d;
418 md.col(0).setConstant(alpha.n);
421 (q2ByAlpha.
cartesian().squaredNorm() - latticeBasis.col(0).squaredNorm()) /
422 latticeBasis.col(0).squaredNorm();
423 if (abs(s1) > maxStrain)
continue;
425 for (
const auto &pair2: coPrimePairs) {
427 qIndices << pair2.first, pair2.second;
429 double ratio2= r2.
cartesian().norm() / latticeBasis.col(1).norm();
433 double s2 = (r2ByBeta.
cartesian().squaredNorm() - latticeBasis.col(1).squaredNorm()) /
434 latticeBasis.col(1).squaredNorm();
436 latticeBasis.col(0).dot(latticeBasis.col(1))) /
437 (latticeBasis.col(0).norm() * latticeBasis.col(1).norm());
438 if (abs(s2) > maxStrain || abs(s3) > maxStrain)
continue;
442 r2ByBeta.
cartesian() * reciprocalBasis.col(1).transpose();
443 if (F.determinant() < 0)
continue;
445 output.push_back( F );
446 numberOfConfigurations++;
447 std::cout << numberOfConfigurations << std::endl;
448 if (numberOfConfigurations == maxConfigurations)
456 if (abs(maxStrain) < epsilon)
457 std::transform(temp.begin(), temp.end(),
458 std::back_inserter(output),
459 [](
const std::pair<IntScalarType,MatrixDimD>& p) {
465 template<
int dim>
template<
int dm>
466 typename std::enable_if<dm==3,std::vector<LatticeVector<dim>>>::type
471 assert(
this == &boxVector.lattice &&
"Box vectors belong to different lattice.");
476 for (
int i=0; i<dim; ++i) {
477 C.col(i) = boxVectors[i].cartesian();
479 assert(C.determinant() != 0);
482 auto planeParallelBasis= planeParallelLatticeBasis(boxVectors[1].cross(boxVectors[2]),
true);
485 for(
int i=0; i<dim; ++i) {
486 A.col(i) = planeParallelBasis[i].cartesian();
487 mat.col(i)=planeParallelBasis[i].latticeVector();
507 int scale2= round(areaRatio/scale1);
508 int scale0= round(abs(C.determinant()/latticeBasis.determinant()/areaRatio));
510 std::vector<LatticeVector<dim>> output;
515 r0_temp= (boxVectors[1].cross(boxVectors[2])).reciprocalLatticeVector();
516 if (r0_temp.dot(boxVectors[0]) < 0)
518 r1_temp= (boxVectors[2].cross(boxVectors[0])).reciprocalLatticeVector();
519 if (r1_temp.dot(boxVectors[1]) < 0)
521 r2_temp= (boxVectors[0].cross(boxVectors[1])).reciprocalLatticeVector();
522 if (r2_temp.
dot(boxVectors[2]) < 0)
524 assert(r0_temp.dot(boxVectors[1])==0 && r0_temp.dot(boxVectors[2])==0);
525 assert(r1_temp.dot(boxVectors[0])==0 && r1_temp.dot(boxVectors[2])==0);
526 assert(r2_temp.
dot(boxVectors[0])==0 && r2_temp.
dot(boxVectors[1])==0);
529 for(
int i=0; i<scale0; ++i)
531 for(
int j=0; j<scale1; ++j)
533 for(
int k=0; k<scale2; ++k) {
535 vectorIn_l << i, 0, 0;
543 c0= c0/boxVectors[0].dot(r0);
546 c1= c1/boxVectors[1].dot(r1);
549 c2= c2/boxVectors[2].dot(r2);
550 vector= vector+c0*boxVectors[0]+c1*boxVectors[1]+c2*boxVectors[2];
551 output.push_back(vector);
556 if(!filename.empty()) {
559 if (!file) std::cerr <<
"Unable to open file";
560 file << output.size() << std::endl;
561 file <<
"Lattice=\"";
563 for(
const auto& vector:boxVectors) {
564 file << vector.
cartesian().transpose() <<
" ";
566 file <<
"\" Properties=atom_types:I:1:pos:R:3" << std::endl;
568 for (
const auto &vector: output)
569 file << 1 <<
" " << vector.cartesian().transpose() << std::endl;
577 template<
int dim>
template<
int dm>
578 typename std::enable_if<dm==2,std::vector<LatticeVector<dim>>>::type
583 assert(
this == &boxVector.lattice &&
"Box vectors belong to different lattice.");
588 for (
int i=0; i<dim; ++i) {
589 C.col(i) = boxVectors[i].cartesian();
591 assert(C.determinant() != 0);
592 Lattice<dim> boxLattice(C);
596 LatticeDirection<dim> v0(
600 ReciprocalLatticeVector<dim> temp(*
this);
601 temp << v0.latticeVector()(1),-v0.latticeVector()(0);
602 LatticeDirection<dim> v1(planeParallelLatticeBasis(temp,
true)[0]);
613 int areaRatio= round(abs(boxLattice.latticeBasis.determinant()/
614 (*this).latticeBasis.determinant()));
617 int scale2= round(areaRatio/scale1);
619 std::vector<LatticeVector<dim>> output;
622 ReciprocalLatticeVector<dim> r1_temp(*
this), r0_temp(*
this);
623 r1_temp << boxVectors[0](1),-boxVectors[0](0);
624 if (r1_temp.dot(boxVectors[1]) < 0)
626 r0_temp << boxVectors[1](1),-boxVectors[1](0);
627 if (r0_temp.dot(boxVectors[0]) < 0)
629 assert(r1_temp.dot(boxVectors[0])==0);
630 assert(r0_temp.dot(boxVectors[1])==0);
631 ReciprocalLatticeDirection<dim> r1(r1_temp), r0(r0_temp);
633 for(
int j=0; j<scale1; ++j)
635 for(
int k=0; k<scale2; ++k) {
636 LatticeVector<dim> vector(j*v0.latticeVector()+k*v1.latticeVector());
639 c1= c1/boxVectors[1].dot(r1);
642 c2= c2/boxVectors[0].dot(r0);
643 vector= vector+c1*boxVectors[1]+c2*boxVectors[0];
644 output.push_back(vector);
648 if(!filename.empty()) {
651 if (!file) std::cerr <<
"Unable to open file";
652 file << output.size() << std::endl;
653 file <<
"Lattice=\"";
655 for(
const auto& vector:boxVectors) {
656 file << vector.cartesian().transpose() <<
" 0 ";
659 file <<
"\" Properties=atom_types:I:1:pos:R:3" << std::endl;
661 for (
const auto &vector: output)
662 file << 1 <<
" " << vector.cartesian().transpose() <<
" 0 " << std::endl;
670 template class Lattice<1>;
672 template class Lattice<2>;
673 template std::vector<typename Lattice<2>::MatrixDimD> Lattice<2>::generateCoincidentLattices<2>(
674 const double &maxStrain,
const int &maxDen,
const int &N)
const;
675 template std::vector<typename Lattice<2>::MatrixDimD> Lattice<2>::generateCoincidentLattices<2>(
676 const Lattice<2> &undeformedLattice,
const double &maxStrain,
const int &maxDen,
const int &N)
const;
677 template std::vector<LatticeVector<2>> Lattice<2>::box<2>(
const std::vector<LatticeVector<2>> &boxVectors,
678 const std::string &filename)
const;
681 template class Lattice<3>;
682 template std::vector<typename Lattice<3>::MatrixDimD> Lattice<3>::generateCoincidentLattices<3>(
683 const ReciprocalLatticeDirection<3> &rd,
const double &maxDen,
const int& N)
const;
684 template std::vector<LatticeVector<3>> Lattice<3>::box<3>(
const std::vector<LatticeVector<3>> &boxVectors,
685 const std::string &filename)
const;
687 template class Lattice<4>;
688 template class Lattice<5>;
long long int LongIntType
typename LatticeCore< dim >::VectorDimD VectorDimD
RationalReciprocalLatticeDirection< dim > rationalReciprocalLatticeDirection(const VectorDimD &d, const typename BestRationalApproximation::LongIntType &maxDen, const double &magnitudeTol, const double &directionTol=FLT_EPSILON) const
double interPlanarSpacing(const ReciprocalLatticeDirection< dim > &r) const
Computes the interplanar spacing.
const MatrixDimD latticeBasis
const MatrixDimD reciprocalBasis
std::enable_if< dm==3, std::vector< MatrixDimD > >::type generateCoincidentLattices(const ReciprocalLatticeDirection< dim > &rd, const double &maxDen=100, const int &N=100) const
std::vector< ReciprocalLatticeDirection< dim > > directionOrthogonalReciprocalLatticeBasis(const LatticeDirection< dim > &l, const bool &useRLLL=false) const
Given a lattice direction , this function returns a direction-orthogonal reciprocal lattice basis ,...
typename LatticeCore< dim >::IntScalarType IntScalarType
typename LatticeCore< dim >::MatrixDimD MatrixDimD
ReciprocalLatticeVector< dim > reciprocalLatticeVector(const VectorDimD &p) const
Returns a reciprocal lattice vector (in the dual of the current lattice) with Cartesian coordinates p...
std::enable_if< dm==3, std::vector< LatticeVector< dim > > >::type box(const std::vector< LatticeVector< dim > > &boxVectors, const std::string &filename="") const
Lattice(const MatrixDimD &A, const MatrixDimD &Q=MatrixDimD::Identity())
std::vector< LatticeDirection< dim > > planeParallelLatticeBasis(const ReciprocalLatticeDirection< dim > &l, const bool &useRLLL=false) const
Given a reciprocal lattice direction , this function returns a plane-parallel lattice basis ,...
typename LatticeCore< dim >::MatrixDimI MatrixDimI
LatticeVector< dim > latticeVector(const VectorDimD &p) const
Returns a lattice vector (in the current lattice) with Cartesian coordinates p.
RationalLatticeDirection< dim > rationalLatticeDirection(const VectorDimD &d, const typename BestRationalApproximation::LongIntType &maxDen, const double &magnitudeTol, const double &directionTol=FLT_EPSILON) const
typename LatticeCore< dim >::VectorDimI VectorDimI
ReciprocalLatticeDirection< dim > reciprocalLatticeDirection(const VectorDimD &d, const double &tol=FLT_EPSILON) const
Returns the reciprocal lattice direction along a vector.
LatticeDirection< dim > latticeDirection(const VectorDimD &d, const double &tol=FLT_EPSILON) const
Returns the lattice direction along a vector.
VectorDimD cartesian() const
const Lattice< dim > & lattice
IntScalarType dot(const ReciprocalLatticeVector< dim > &other) const
const MatrixType & reducedBasis() const
const Eigen::Matrix< long long int, Eigen::Dynamic, Eigen::Dynamic > & unimodularMatrix() const
std::vector< Rational< IntScalarType > > approximations
IntScalarType dot(const LatticeVector< dim > &other) const
VectorDimD cartesian() const
const Lattice< dim > & lattice
std::vector< std::pair< int, int > > farey(int limit, const bool firstQuadrant)
static Eigen::Matrix< IntScalarType, Eigen::Dynamic, Eigen::Dynamic > ccum(const Eigen::MatrixBase< T > &qin)
static IntScalarType gcd(const IntScalarType &a, const IntScalarType &b)
static IntScalarType positive_modulo(IntScalarType i, IntScalarType n)
static Eigen::Vector< IntScalarType, Eigen::Dynamic > solveBezout(const Eigen::MatrixBase< T > &a)
static IntScalarType extended_gcd(IntScalarType a, IntScalarType b, IntScalarType &x, IntScalarType &y)
const LatticeVector< dim > & latticeVector() const
VectorDimD cartesian() const
VectorDimD cartesian() const
const ReciprocalLatticeVector< dim > & reciprocalLatticeVector() const
Returns a constant reference to the base class (ReciprocalLatticeVector)