-
Antti Soininen authoredAntti Soininen authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
UnitCell.cpp 29.59 KiB
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source
// & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
#include "MantidGeometry/Crystal/UnitCell.h"
#include "MantidKernel/Matrix.h"
#include "MantidKernel/StringTokenizer.h"
#include "MantidKernel/System.h"
#include "MantidKernel/V3D.h"
#include <cfloat>
#include <iomanip>
#include <ios>
#include <stdexcept>
#include <boost/lexical_cast.hpp>
namespace Mantid {
namespace Geometry {
using Mantid::Kernel::DblMatrix;
using Mantid::Kernel::V3D;
/** Default constructor.
\f$ a = b = c = 1 \mbox{\AA, } \alpha = \beta = \gamma = 90^\circ \f$ */
UnitCell::UnitCell()
: da(6), ra(6), errorda(6), G(3, 3), Gstar(3, 3), B(3, 3), ModHKL(3, 3),
errorModHKL(3, 3) {
da[0] = da[1] = da[2] = 1.;
da[3] = da[4] = da[5] = deg2rad * 90.0;
errorda[0] = errorda[1] = errorda[2] = errorda[3] = errorda[4] = errorda[5] =
0.0;
MaxOrder = 0;
CrossTerm = false;
recalculate();
}
/** Constructor
@param _a, _b, _c :: lattice parameters \f$ a, b, c \f$ \n
with \f$\alpha = \beta = \gamma = 90^\circ \f$*/
UnitCell::UnitCell(double _a, double _b, double _c)
: da(6), ra(6), errorda(6), G(3, 3), Gstar(3, 3), B(3, 3), ModHKL(3, 3),
errorModHKL(3, 3) {
da[0] = _a;
da[1] = _b;
da[2] = _c;
// Angles are 90 degrees in radians ->Pi/2
da[3] = da[4] = da[5] = 0.5 * M_PI;
errorda[0] = errorda[1] = errorda[2] = errorda[3] = errorda[4] = errorda[5] =
0.0;
MaxOrder = 0;
CrossTerm = false;
recalculate();
}
/** Constructor
@param _a, _b, _c, _alpha, _beta, _gamma :: lattice parameters\n
@param angleunit :: units for angle, of type #AngleUnits. Default is degrees.
*/
UnitCell::UnitCell(double _a, double _b, double _c, double _alpha, double _beta,
double _gamma, const int angleunit)
: da(6), ra(6), errorda(6), G(3, 3), Gstar(3, 3), B(3, 3), ModHKL(3, 3),
errorModHKL(3, 3) {
da[0] = _a;
da[1] = _b;
da[2] = _c;
// Angle transformed in radians
if (angleunit == angDegrees) {
da[3] = deg2rad * _alpha;
da[4] = deg2rad * _beta;
da[5] = deg2rad * _gamma;
} else {
da[3] = _alpha;
da[4] = _beta;
da[5] = _gamma;
}
errorda[0] = errorda[1] = errorda[2] = errorda[3] = errorda[4] = errorda[5] =
0.0;
MaxOrder = 0;
CrossTerm = false;
recalculate();
}
/** Get lattice parameter
@return a1 :: lattice parameter \f$ a \f$ (in \f$ \mbox{\AA} \f$ )
@see a()*/
double UnitCell::a1() const { return da[0]; }
/** Get lattice parameter
@return a2 :: lattice parameter \f$ b \f$ (in \f$ \mbox{\AA} \f$ )
@see b()*/
double UnitCell::a2() const { return da[1]; }
/** Get lattice parameter
@return a3 :: lattice parameter \f$ c \f$ (in \f$ \mbox{\AA} \f$ )
@see c()*/
double UnitCell::a3() const { return da[2]; }
/** Get lattice parameter a1-a3 as function of index (0-2)
@return a_n :: lattice parameter \f$ a,b or c \f$ (in \f$ \mbox{\AA} \f$ )
*/
double UnitCell::a(int nd) const {
if (nd < 0 || nd > 2)
throw(std::invalid_argument(
"lattice parameter index can change from 0 to 2 "));
return da[nd];
}
/** Get lattice parameter
@return alpha1 :: lattice parameter \f$ \alpha \f$ (in radians)
@see alpha()*/
double UnitCell::alpha1() const { return da[3]; }
/** Get lattice parameter
@return alpha2 :: lattice parameter \f$ \beta \f$ (in radians)
@see beta()*/
double UnitCell::alpha2() const { return da[4]; }
/** Get lattice parameter
@return alpha3 :: lattice parameter \f$ \gamma \f$ (in radians)
@see gamma()*/
double UnitCell::alpha3() const { return da[5]; }
/** Get lattice parameter
@return a :: lattice parameter \f$ a \f$ (in \f$ \mbox{\AA} \f$ )
@see a1()*/
double UnitCell::a() const { return da[0]; }
/** Get lattice parameter
@return b :: lattice parameter \f$ b \f$ (in \f$ \mbox{\AA} \f$ )
@see a2()*/
double UnitCell::b() const { return da[1]; }
/** Get lattice parameter
@return c :: lattice parameter \f$ c \f$ (in \f$ \mbox{\AA} \f$ )
@see a3()*/
double UnitCell::c() const { return da[2]; }
/** Get lattice parameter
@return alpha :: lattice parameter \f$ \alpha \f$ (in degrees)
@see alpha1()*/
double UnitCell::alpha() const { return da[3] * rad2deg; }
/** Get lattice parameter
@return beta :: lattice parameter \f$ \beta \f$ (in degrees)
@see alpha2()*/
double UnitCell::beta() const { return da[4] * rad2deg; }
/** Get lattice parameter
@return gamma :: lattice parameter \f$ \gamma \f$ (in degrees)
@see alpha3()*/
double UnitCell::gamma() const { return da[5] * rad2deg; }
/** Get reciprocal lattice parameter
@return b1 :: lattice parameter \f$ a^{*} \f$ (in \f$ \mbox{\AA}^{-1} \f$ )
@see astar()*/
double UnitCell::b1() const { return ra[0]; }
/** Get reciprocal lattice parameter
@return b2 :: lattice parameter \f$ b^{*} \f$ (in \f$ \mbox{\AA}^{-1} \f$ )
@see bstar()*/
double UnitCell::b2() const { return ra[1]; }
/** Get reciprocal lattice parameter
@return b3 :: lattice parameter \f$ c^{*} \f$ (in \f$ \mbox{\AA}^{-1} \f$ )
@see cstar()*/
double UnitCell::b3() const { return ra[2]; }
/** Get reciprocal lattice parameter
@return beta1 :: lattice parameter \f$ \alpha^{*} \f$ (in radians)
@see alphastar()*/
double UnitCell::beta1() const { return ra[3]; }
/** Get reciprocal lattice parameter
@return beta2 :: lattice parameter \f$ \beta^{*} \f$ (in radians)
@see betastar()*/
double UnitCell::beta2() const { return ra[4]; }
/** Get reciprocal lattice parameter
@return beta3 :: lattice parameter \f$ \gamma^{*} \f$ (in radians)
@see gammastar()*/
double UnitCell::beta3() const { return ra[5]; }
/** Get reciprocal lattice parameter
@return astar :: lattice parameter \f$ a^{*} \f$ (in \f$ \mbox{\AA}^{-1} \f$ )
@see b1()*/
double UnitCell::astar() const { return ra[0]; }
/** Get reciprocal lattice parameter
@return bstar :: lattice parameter \f$ b^{*} \f$ (in \f$ \mbox{\AA}^{-1} \f$ )
@see b2()*/
double UnitCell::bstar() const { return ra[1]; }
/** Get reciprocal lattice parameter
@return cstar :: lattice parameter \f$ c^{*} \f$ (in \f$ \mbox{\AA}^{-1} \f$ )
@see b3()*/
double UnitCell::cstar() const { return ra[2]; }
/** Get reciprocal lattice parameter
@return alphastar :: lattice parameter \f$ \alpha^{*} \f$ (in degrees)
@see beta1()*/
double UnitCell::alphastar() const { return ra[3] * rad2deg; }
/** Get reciprocal lattice parameter
@return betastar:: lattice parameter \f$ \beta^{*} \f$ (in degrees)
@see beta2()*/
double UnitCell::betastar() const { return ra[4] * rad2deg; }
/** Get reciprocal lattice parameter
@return gammastar:: lattice parameter \f$ \gamma^{*} \f$ (in degrees)
@see beta3()*/
double UnitCell::gammastar() const { return ra[5] * rad2deg; }
/** Get lattice parameter error
@return errora :: errorlattice parameter \f$ a \f$ (in \f$ \mbox{\AA} \f$ )
*/
double UnitCell::errora() const { return errorda[0]; }
/** Get lattice parameter error
@return errorb :: errorlattice parameter \f$ b \f$ (in \f$ \mbox{\AA} \f$ )
*/
double UnitCell::errorb() const { return errorda[1]; }
/** Get lattice parameter error
@return errorc :: errorlattice parameter \f$ c \f$ (in \f$ \mbox{\AA} \f$ )
*/
double UnitCell::errorc() const { return errorda[2]; }
/** Get lattice parameter error
@param angleunit :: units for angle, of type #AngleUnits . Default is degrees.
@return erroralpha :: errorlattice parameter \f$ alpha \f$ (in degrees or
radians )
*/
double UnitCell::erroralpha(const int angleunit) const {
if (angleunit == angDegrees) {
return errorda[3] * rad2deg;
} else {
return errorda[3];
}
}
/** Get lattice parameter error
@param angleunit :: units for angle, of type #AngleUnits . Default is degrees.
@return erroralpha :: errorlattice parameter \f$ beta \f$ (in degrees or
radians
)
*/
double UnitCell::errorbeta(const int angleunit) const {
if (angleunit == angDegrees) {
return errorda[4] * rad2deg;
} else {
return errorda[4];
}
}
/** Get lattice parameter error
@param angleunit :: units for angle, of type #AngleUnits . Default is degrees.
@return erroralpha :: errorlattice parameter \f$ gamma \f$ (in degrees or
radians )
*/
double UnitCell::errorgamma(const int angleunit) const {
if (angleunit == angDegrees) {
return errorda[5] * rad2deg;
} else {
return errorda[5];
}
}
/** Get lattice parameter error
@return errorc :: errorlattice parameter \f$ volume \f$ (in \f$ \mbox{\AA} \f$
)
*/
double UnitCell::errorvolume() const {
// From latcon.py by Art Schultz
double V = volume();
double delta_V_alphaV = 0.0;
if (erroralpha() > 0.0) {
double alpha1 = alpha() - 0.5 * erroralpha();
double Va1 = UnitCell(a(), b(), c(), alpha1, beta(), gamma()).volume();
double alpha2 = alpha() + 0.5 * erroralpha();
double Va2 = UnitCell(a(), b(), c(), alpha2, beta(), gamma()).volume();
delta_V_alphaV = (Va2 - Va1) / V;
}
double delta_V_betaV = 0.0;
if (errorbeta() > 0.0) {
double beta1 = beta() - 0.5 * errorbeta();
double Va1 = UnitCell(a(), b(), c(), alpha(), beta1, gamma()).volume();
double beta2 = beta() + 0.5 * errorbeta();
double Va2 = UnitCell(a(), b(), c(), alpha(), beta2, gamma()).volume();
delta_V_betaV = (Va2 - Va1) / V;
}
double delta_V_gammaV = 0.0;
if (errorgamma() > 0.0) {
double gamma1 = gamma() - 0.5 * errorgamma();
double Va1 = UnitCell(a(), b(), c(), alpha(), beta(), gamma1).volume();
double gamma2 = gamma() + 0.5 * errorgamma();
double Va2 = UnitCell(a(), b(), c(), alpha(), beta(), gamma2).volume();
delta_V_gammaV = (Va2 - Va1) / V;
}
return V * sqrt(std::pow(errora() / a(), 2) + std::pow(errorb() / b(), 2) +
std::pow(errorc() / c(), 2) + std::pow(delta_V_alphaV, 2) +
std::pow(delta_V_betaV, 2) + std::pow(delta_V_gammaV, 2));
}
/** Set lattice parameters
@param _a, _b, _c, _alpha, _beta, _gamma :: lattice parameters\n
@param angleunit :: units for angle, of type #AngleUnits . Default is degrees.
*/
void UnitCell::set(double _a, double _b, double _c, double _alpha, double _beta,
double _gamma, const int angleunit) {
da[0] = _a;
da[1] = _b;
da[2] = _c;
if (angleunit == angDegrees) {
da[3] = deg2rad * _alpha;
da[4] = deg2rad * _beta;
da[5] = deg2rad * _gamma;
} else {
da[3] = _alpha;
da[4] = _beta;
da[5] = _gamma;
}
recalculate();
}
/** Set lattice parameter errors
@param _aerr, _berr, _cerr, _alphaerr, _betaerr, _gammaerr :: lattice
parameter errors\n
@param angleunit :: units for angle, of type #AngleUnits . Default is degrees.
*/
void UnitCell::setError(double _aerr, double _berr, double _cerr,
double _alphaerr, double _betaerr, double _gammaerr,
const int angleunit) {
errorda[0] = _aerr;
errorda[1] = _berr;
errorda[2] = _cerr;
if (angleunit == angDegrees) {
errorda[3] = deg2rad * _alphaerr;
errorda[4] = deg2rad * _betaerr;
errorda[5] = deg2rad * _gammaerr;
} else {
errorda[3] = _alphaerr;
errorda[4] = _betaerr;
errorda[5] = _gammaerr;
}
}
/** Set modulation vectors for satellites
@param _dh1 offset for H for first vector
@param _dk1 offset for K for first vector
@param _dl1 offset for l for first vector
@param _dh2 offset for H for second vector
@param _dk2 offset for K for second vector
@param _dl2 offset for l for second vector
@param _dh3 offset for H for third vector
@param _dk3 offset for K for third vector
@param _dl3 offset for l for third vector
*/
void UnitCell::setModHKL(double _dh1, double _dk1, double _dl1, double _dh2,
double _dk2, double _dl2, double _dh3, double _dk3,
double _dl3) {
ModHKL[0][0] = _dh1;
ModHKL[1][0] = _dk1;
ModHKL[2][0] = _dl1;
ModHKL[0][1] = _dh2;
ModHKL[1][1] = _dk2;
ModHKL[2][1] = _dl2;
ModHKL[0][2] = _dh3;
ModHKL[1][2] = _dk3;
ModHKL[2][2] = _dl3;
}
/** Set modulation vectors for satellites
@param newModHKL modulation vectors for HKL for three vectors
*/
void UnitCell::setModHKL(const DblMatrix &newModHKL) { ModHKL = newModHKL; }
/** Set modulation vectors for satellites
@param newErrorModHKL errors for modulation vectors for HKL for three vectors
*/
void UnitCell::setErrorModHKL(const DblMatrix &newErrorModHKL) {
errorModHKL = newErrorModHKL;
}
/** Set modulation vectors for satellites
@param _dh1err error for offset for H for first vector
@param _dk1err error for offset for K for first vector
@param _dl1err error for offset for l for first vector
@param _dh2err error for offset for H for second vector
@param _dk2err error for offset for K for second vector
@param _dl2err error for offset for l for second vector
@param _dh3err error for offset for H for third vector
@param _dk3err error for offset for K for third vector
@param _dl3err error for offset for l for third vector
*/
void UnitCell::setErrorModHKL(double _dh1err, double _dk1err, double _dl1err,
double _dh2err, double _dk2err, double _dl2err,
double _dh3err, double _dk3err, double _dl3err) {
errorModHKL[0][0] = _dh1err;
errorModHKL[1][0] = _dk1err;
errorModHKL[2][0] = _dl1err;
errorModHKL[0][1] = _dh2err;
errorModHKL[1][1] = _dk2err;
errorModHKL[2][1] = _dl2err;
errorModHKL[0][2] = _dh3err;
errorModHKL[1][2] = _dk3err;
errorModHKL[2][2] = _dl3err;
}
/** Set modulation vectors for satellites
@param _dh1 offset for H for first vector
@param _dk1 offset for K for first vector
@param _dl1 offset for l for first vector
*/
void UnitCell::setModVec1(double _dh1, double _dk1, double _dl1) {
ModHKL[0][0] = _dh1;
ModHKL[1][0] = _dk1;
ModHKL[2][0] = _dl1;
}
/** Set modulation vectors for satellites
@param _dh2 offset for H for second vector
@param _dk2 offset for K for second vector
@param _dl2 offset for l for second vector
*/
void UnitCell::setModVec2(double _dh2, double _dk2, double _dl2) {
ModHKL[0][1] = _dh2;
ModHKL[1][1] = _dk2;
ModHKL[2][1] = _dl2;
}
/** Set modulation vectors for satellites
@param _dh3 offset for H for third vector
@param _dk3 offset for K for third vector
@param _dl3 offset for l for third vector
*/
void UnitCell::setModVec3(double _dh3, double _dk3, double _dl3) {
ModHKL[0][2] = _dh3;
ModHKL[1][2] = _dk3;
ModHKL[2][2] = _dl3;
}
/** Set modulation vectors for satellites
@param newModVec modulation vectors for HKL for first vector
*/
void UnitCell::setModVec1(const V3D &newModVec) {
ModHKL[0][0] = newModVec[0];
ModHKL[1][0] = newModVec[1];
ModHKL[2][0] = newModVec[2];
}
/** Set modulation vectors for satellites
@param newModVec modulation vectors for HKL for second vector
*/
void UnitCell::setModVec2(const V3D &newModVec) {
ModHKL[0][1] = newModVec[0];
ModHKL[1][1] = newModVec[1];
ModHKL[2][1] = newModVec[2];
}
/** Set modulation vectors for satellites
@param newModVec modulation vectors for HKL for third vector
*/
void UnitCell::setModVec3(const V3D &newModVec) {
ModHKL[0][2] = newModVec[0];
ModHKL[1][2] = newModVec[1];
ModHKL[2][2] = newModVec[2];
}
/** Set modulation vectors for satellites
@param i index of vector to set
@param _dherr error for offset for H for ith vector
@param _dkerr error for offset for K for ith vector
@param _dlerr error for offset for l for ith vector
*/
void UnitCell::setModerr(int i, double _dherr, double _dkerr, double _dlerr) {
errorModHKL[0][i] = _dherr;
errorModHKL[1][i] = _dkerr;
errorModHKL[2][i] = _dlerr;
}
/** Set modulation vectors for satellites
@param _dh1err error for offset for H for first vector
@param _dk1err error for offset for K for first vector
@param _dl1err error for offset for l for first vector
*/
void UnitCell::setModerr1(double _dh1err, double _dk1err, double _dl1err) {
errorModHKL[0][0] = _dh1err;
errorModHKL[1][0] = _dk1err;
errorModHKL[2][0] = _dl1err;
}
/** Set modulation vectors for satellites
@param _dh2err error for offset for H for second vector
@param _dk2err error for offset for K for second vector
@param _dl2err error for offset for l for second vector
*/
void UnitCell::setModerr2(double _dh2err, double _dk2err, double _dl2err) {
errorModHKL[0][1] = _dh2err;
errorModHKL[1][1] = _dk2err;
errorModHKL[2][1] = _dl2err;
}
/** Set modulation vectors for satellites
@param _dh3err error for offset for H for third vector
@param _dk3err error for offset for K for third vector
@param _dl3err error for offset for l for third vector
*/
void UnitCell::setModerr3(double _dh3err, double _dk3err, double _dl3err) {
errorModHKL[0][2] = _dh3err;
errorModHKL[1][2] = _dk3err;
errorModHKL[2][2] = _dl3err;
}
/** Set modulation vectors for satellites
@param MaxO maximum order of modulation vectors
*/
void UnitCell::setMaxOrder(int MaxO) { MaxOrder = MaxO; }
/** Set modulation vectors for satellites
@param CT if true, use cross terms
*/
void UnitCell::setCrossTerm(bool CT) { CrossTerm = CT; }
/** Get modulation vectors for satellites
@param j index of vector to get
@return ModVec :: modulation vector
*/
const Kernel::V3D UnitCell::getModVec(int j) const {
return V3D(getdh(j), getdk(j), getdl(j));
}
/** Get errors for modulation vectors for satellites
@param j index of vector to get
@return VecErr :: error of modulation vector
*/
const Kernel::V3D UnitCell::getVecErr(int j) const {
return V3D(getdherr(j), getdkerr(j), getdlerr(j));
}
/** Get modulation vectors for satellites
@return ModHKL :: modulation vectors
*/
const Kernel::DblMatrix &UnitCell::getModHKL() const { return ModHKL; }
/** Get modulation vectors for satellites
@param j index of vector to get
@return dh :: dh of modulation vector
*/
double UnitCell::getdh(int j) const { return ModHKL[0][j]; }
/** Get modulation vectors for satellites
@param j index of vector to get
@return ModVec :: modulation vector
*/
double UnitCell::getdk(int j) const { return ModHKL[1][j]; }
/** Get modulation vectors for satellites
@param j index of vector to get
@return ModVec :: modulation vector
*/
double UnitCell::getdl(int j) const { return ModHKL[2][j]; }
/** Get error of modulation vectors for satellites
@param j index of vector to get
@return ModVecErr :: error of modulation vector
*/
double UnitCell::getdherr(int j) const { return errorModHKL[0][j]; }
/** Get error of modulation vectors for satellites
@param j index of vector to get
@return ModVecErr :: error of modulation vector
*/
double UnitCell::getdkerr(int j) const { return errorModHKL[1][j]; }
/** Get error of modulation vectors for satellites
@param j index of vector to get
@return ModVecErr :: error of modulation vector
*/
double UnitCell::getdlerr(int j) const { return errorModHKL[2][j]; }
/** Get max order
@return MaxOrder :: maximum order
*/
int UnitCell::getMaxOrder() const { return MaxOrder; }
/** Get cross term boolean
@return CrossTerm :: if true, use cross terms
*/
bool UnitCell::getCrossTerm() const { return CrossTerm; }
/** Set lattice parameter
@param _a :: lattice parameter \f$ a \f$ (in \f$ \mbox{\AA} \f$ )*/
void UnitCell::seta(double _a) {
da[0] = _a;
recalculate();
}
/** Set lattice parameter error
@param _aerr :: lattice parameter \f$ a \f$ error (in \f$ \mbox{\AA} \f$ )*/
void UnitCell::setErrora(double _aerr) { errorda[0] = _aerr; }
/** Set lattice parameter
@param _b :: lattice parameter \f$ b \f$ (in \f$ \mbox{\AA} \f$ )*/
void UnitCell::setb(double _b) {
da[1] = _b;
recalculate();
}
/** Set lattice parameter error
@param _berr :: lattice parameter \f$ b \f$ error (in \f$ \mbox{\AA} \f$ )*/
void UnitCell::setErrorb(double _berr) { errorda[1] = _berr; }
/** Set lattice parameter
@param _c :: lattice parameter \f$ c \f$ (in \f$ \mbox{\AA} \f$ )*/
void UnitCell::setc(double _c) {
da[2] = _c;
recalculate();
}
/** Set lattice parameter error
@param _cerr :: lattice parameter \f$ c \f$ error (in \f$ \mbox{\AA} \f$ )*/
void UnitCell::setErrorc(double _cerr) { errorda[2] = _cerr; }
/** Set lattice parameter
@param _alpha :: lattice parameter \f$ \alpha \f$
@param angleunit :: units for angle, of type #AngleUnits. Default is degrees.
*/
void UnitCell::setalpha(double _alpha, const int angleunit) {
if (angleunit == angDegrees)
da[3] = deg2rad * _alpha;
else
da[3] = _alpha;
recalculate();
}
/** Set lattice parameter error
@param _alphaerr :: lattice parameter \f$ \alpha \f$ error
@param angleunit :: units for angle, of type #AngleUnits. Default is degrees.
*/
void UnitCell::setErroralpha(double _alphaerr, const int angleunit) {
if (angleunit == angDegrees)
errorda[3] = deg2rad * _alphaerr;
else
errorda[3] = _alphaerr;
}
/** Set lattice parameter
@param _beta :: lattice parameter \f$ \beta \f$
@param angleunit :: units for angle, of type #AngleUnits. Default is degrees.
*/
void UnitCell::setbeta(double _beta, const int angleunit) {
if (angleunit == angDegrees)
da[4] = deg2rad * _beta;
else
da[4] = _beta;
recalculate();
}
/** Set lattice parameter error
@param _betaerr :: lattice parameter \f$ \beta \f$ error
@param angleunit :: units for angle, of type #AngleUnits. Default is degrees.
*/
void UnitCell::setErrorbeta(double _betaerr, const int angleunit) {
if (angleunit == angDegrees)
errorda[4] = deg2rad * _betaerr;
else
errorda[4] = _betaerr;
}
/** Set lattice parameter
@param _gamma :: lattice parameter \f$ \gamma \f$
@param angleunit :: units for angle, of type #AngleUnits. Default is degrees.
*/
void UnitCell::setgamma(double _gamma, const int angleunit) {
if (angleunit == angDegrees)
da[5] = deg2rad * _gamma;
else
da[5] = _gamma;
recalculate();
}
/** Set lattice parameter error
@param _gammaerr :: lattice parameter \f$ \gamma \f$ error
@param angleunit :: units for angle, of type #AngleUnits. Default is degrees.
*/
void UnitCell::setErrorgamma(double _gammaerr, const int angleunit) {
if (angleunit == angDegrees)
errorda[5] = deg2rad * _gammaerr;
else
errorda[5] = _gammaerr;
}
/// Return d-spacing (\f$ \mbox{ \AA } \f$) for a given h,k,l coordinate
double UnitCell::d(double h, double k, double l) const {
return 1.0 / dstar(V3D(h, k, l));
}
/// Return d-spacing (\f$ \mbox{ \AA } \f$) for a given h,k,l coordinate
double UnitCell::d(const V3D &hkl) const { return 1.0 / dstar(hkl); }
/// Return d*=1/d (\f$ \mbox{ \AA }^{-1} \f$) for a given h,k,l coordinate
double UnitCell::dstar(double h, double k, double l) const {
return dstar(V3D(h, k, l)); // create a V3D vector h,k,l
}
/// Return d*=1/d (\f$ \mbox{ \AA }^{-1} \f$) for a given h,k,l coordinate
double UnitCell::dstar(const V3D &hkl) const {
V3D Q = B * hkl; // transform into $AA^-1$
return Q.norm();
}
/// Calculate the angle in degrees or radians between two reciprocal vectors
/// (h1,k1,l1) and (h2,k2,l2)
double UnitCell::recAngle(double h1, double k1, double l1, double h2, double k2,
double l2, const int angleunit) const {
V3D Q1(h1, k1, l1), Q2(h2, k2, l2);
double E, ang;
Q1 = Gstar * Q1;
E = Q1.scalar_prod(Q2);
double temp = E / dstar(h1, k1, l1) / dstar(h2, k2, l2);
if (temp > 1)
ang = 0.;
else if (temp < -1)
ang = M_PI;
else
ang = acos(temp);
if (angleunit == angDegrees)
return rad2deg * ang;
else
return ang;
}
/// Volume of the direct unit-cell
double UnitCell::volume() const {
double volume = G.determinant();
return sqrt(volume);
}
/// Volume of the reciprocal lattice
double UnitCell::recVolume() const {
double recvolume = Gstar.determinant();
return sqrt(recvolume);
}
/// Get the metric tensor
/// @return G :: metric tensor
const Kernel::DblMatrix &UnitCell::getG() const { return G; }
/// Get the reciprocal metric tensor
/// @return Gstar :: metric tensor of the reciprocal lattice
const Kernel::DblMatrix &UnitCell::getGstar() const { return Gstar; }
/// Get the B-matrix
/// @return B :: B matrix in Busing-Levy convention
const Kernel::DblMatrix &UnitCell::getB() const { return B; }
/// Get the inverse of the B-matrix
/// @return Binv :: inverse of the B matrix in Busing-Levy convention
const Kernel::DblMatrix &UnitCell::getBinv() const { return Binv; }
/// Private function, called at initialization or whenever lattice parameters
/// are changed
void UnitCell::recalculate() {
if ((da[3] > da[4] + da[5]) || (da[4] > da[3] + da[5]) ||
(da[5] > da[4] + da[3])) {
throw std::invalid_argument("Invalid angles");
}
calculateG();
calculateGstar();
calculateReciprocalLattice();
calculateB();
}
void UnitCell::calculateG() {
G[0][0] = da[0] * da[0];
G[1][1] = da[1] * da[1];
G[2][2] = da[2] * da[2];
G[0][1] = da[0] * da[1] * cos(da[5]);
G[0][2] = da[0] * da[2] * cos(da[4]);
G[1][2] = da[1] * da[2] * cos(da[3]);
G[1][0] = G[0][1];
G[2][0] = G[0][2];
G[2][1] = G[1][2];
}
/// Private function to calculate #Gstar matrix
void UnitCell::calculateGstar() {
// Reciprocal metrix tensor is simply the inverse of the direct one
double det = G.determinant();
if (det == 0) {
throw std::range_error("UnitCell not properly initialized");
}
Gstar = G;
if (Gstar.Invert() == 0) {
throw std::range_error("UnitCell not properly initialized");
}
}
/// Private function to calculate reciprocal lattice parameters
void UnitCell::calculateReciprocalLattice() {
ra[0] = sqrt(Gstar[0][0]); // a*
ra[1] = sqrt(Gstar[1][1]); // b*
ra[2] = sqrt(Gstar[2][2]); // c*
ra[3] = acos(Gstar[1][2] / ra[1] / ra[2]); // alpha*
ra[4] = acos(Gstar[0][2] / ra[0] / ra[2]); // beta*
ra[5] = acos(Gstar[0][1] / ra[0] / ra[1]); // gamma*
}
/// Private function to calculate #B matrix
void UnitCell::calculateB() {
// B matrix using a right handed coordinate system with b1 along x and y in
// the (b1,b2) plane.
// This is the convention in Busing and Levy.
// | b1 b2cos(beta3) b3cos(beta2) |
// | 0 b2sin(beta3) -b3sin(beta2)cos(alpha1) |
// | 0 0 1/a3 |
B[0][0] = ra[0];
B[0][1] = ra[1] * cos(ra[5]);
B[0][2] = ra[2] * cos(ra[4]);
B[1][0] = 0.;
B[1][1] = ra[1] * sin(ra[5]);
B[1][2] = -ra[2] * sin(ra[4]) * cos(da[3]);
B[2][0] = 0.;
B[2][1] = 0.;
B[2][2] = 1. / da[2];
/// Now let's cache the inverse B
Binv = B;
Binv.Invert();
}
/// Recalculate lattice from reciprocal metric tensor (Gstar=transpose(UB)*UB)
void UnitCell::recalculateFromGstar(const DblMatrix &NewGstar) {
if (NewGstar.numRows() != 3 || NewGstar.numCols() != 3) {
std::ostringstream msg;
msg << "UnitCell::recalculateFromGstar - Expected a 3x3 matrix but was "
"given a "
<< NewGstar.numRows() << "x" << NewGstar.numCols();
throw std::invalid_argument(msg.str());
}
if (NewGstar[0][0] * NewGstar[1][1] * NewGstar[2][2] <= 0.)
throw std::invalid_argument("NewGstar");
Gstar = NewGstar;
calculateReciprocalLattice();
G = Gstar;
G.Invert();
da[0] = sqrt(G[0][0]); // a
da[1] = sqrt(G[1][1]); // b
da[2] = sqrt(G[2][2]); // c
da[3] = acos(G[1][2] / da[1] / da[2]); // alpha
da[4] = acos(G[0][2] / da[0] / da[2]); // beta
da[5] = acos(G[0][1] / da[0] / da[1]); // gamma
calculateB();
}
std::ostream &operator<<(std::ostream &out, const UnitCell &unitCell) {
// always show the lattice constants
out << "Lattice Parameters:" << std::fixed << std::setprecision(6)
<< std::setw(12) << unitCell.a() << std::fixed << std::setprecision(6)
<< std::setw(12) << unitCell.b() << std::fixed << std::setprecision(6)
<< std::setw(12) << unitCell.c() << std::fixed << std::setprecision(6)
<< std::setw(12) << unitCell.alpha() << std::fixed << std::setprecision(6)
<< std::setw(12) << unitCell.beta() << std::fixed << std::setprecision(6)
<< std::setw(12) << unitCell.gamma() << std::fixed << std::setprecision(6)
<< " " << std::setw(12) << unitCell.volume();
// write out the uncertainty if there is a positive one somewhere
if ((unitCell.errora() > 0) || (unitCell.errorb() > 0) ||
(unitCell.errorc() > 0) || (unitCell.erroralpha() > 0) ||
(unitCell.errorbeta() > 0) || (unitCell.errorgamma() > 0))
out << "\nParameter Errors :" << std::fixed << std::setprecision(6)
<< std::setw(12) << unitCell.errora() << std::fixed
<< std::setprecision(6) << std::setw(12) << unitCell.errorb()
<< std::fixed << std::setprecision(6) << std::setw(12)
<< unitCell.errorc() << std::fixed << std::setprecision(6)
<< std::setw(12) << unitCell.erroralpha() << std::fixed
<< std::setprecision(6) << std::setw(12) << unitCell.errorbeta()
<< std::fixed << std::setprecision(6) << std::setw(12)
<< unitCell.errorgamma() << std::fixed << std::setprecision(6)
<< std::setw(12) << unitCell.errorvolume();
return out;
}
std::string unitCellToStr(const UnitCell &unitCell) {
std::ostringstream stream;
stream << std::setprecision(9);
stream << unitCell.a() << " " << unitCell.b() << " " << unitCell.c() << " "
<< unitCell.alpha() << " " << unitCell.beta() << " "
<< unitCell.gamma();
return stream.str();
}
UnitCell strToUnitCell(const std::string &unitCellString) {
Mantid::Kernel::StringTokenizer cellTokens(
unitCellString, " ", Mantid::Kernel::StringTokenizer::TOK_IGNORE_EMPTY);
std::vector<double> components;
components.reserve(cellTokens.size());
for (const auto &token : cellTokens) {
components.emplace_back(boost::lexical_cast<double>(token));
}
switch (components.size()) {
case 3:
return UnitCell(components[0], components[1], components[2]);
case 6:
return UnitCell(components[0], components[1], components[2], components[3],
components[4], components[5]);
default:
throw std::runtime_error("Failed to parse unit cell input string: " +
unitCellString);
}
}
} // namespace Geometry
} // namespace Mantid