26double wrapFrac(
double value) {
27 double wrapped = value - std::floor(value);
34AtomMatrix fractionalCoordinates(
const Matter &image) {
35 return image.getPositions() * image.getCell().inverse();
41 if (!(meanVolume > 0.0) || nAtoms < 1 || !(weight > 0.0)) {
42 throw std::invalid_argument(
43 "solid-state Jacobian needs a positive volume, atom count, and weight");
45 const double n =
static_cast<double>(nAtoms);
46 return std::cbrt(meanVolume / n) * std::sqrt(n) * weight;
50 const Vector3d a = cell.row(0);
51 const Vector3d b = cell.row(1);
52 const double na = a.norm();
56 const Vector3d e1 = a / na;
57 const Vector3d bPerp = b - b.dot(e1) * e1;
58 const double nb = bPerp.norm();
62 const Vector3d e2 = bPerp / nb;
63 const Vector3d e3 = e1.cross(e2);
70 if (positions.rows() > 0) {
71 positions = positions * rotation;
73 if (!(std::abs(oriented.determinant()) > 1e-18)) {
84 throw std::invalid_argument(
85 "solid_state NEB could not put the cell in lower-triangular form");
88 if (positions.rows() > 0) {
94 const auto n =
static_cast<long>(images.size());
96 throw std::invalid_argument(
97 "solid_state interpolation needs two endpoints and one image");
99 const Matrix3d h0 = images.front().getCell();
100 const Matrix3d h1 = images.back().getCell();
101 const AtomMatrix s0 = fractionalCoordinates(images.front());
102 AtomMatrix ds = fractionalCoordinates(images.back()) - s0;
103 for (
int atom = 0; atom < ds.rows(); ++atom) {
104 for (
int axis = 0; axis < 3; ++axis) {
105 ds(atom, axis) = wrapFrac(ds(atom, axis));
108 const double denom =
static_cast<double>(n - 1);
109 for (
long i = 1; i < n - 1; ++i) {
110 const double t =
static_cast<double>(i) / denom;
111 Matrix3d h = (1.0 - t) * h0 + t * h1;
114 images[
static_cast<size_t>(i)].setCell(h);
115 images[
static_cast<size_t>(i)].setPositions(fractional * h);
121 if (!(jacobian > 0.0)) {
122 throw std::invalid_argument(
123 "solid-state displacement needs a positive Jacobian");
127 AtomMatrix frac = fractionalCoordinates(to) - fractionalCoordinates(from);
128 for (
int atom = 0; atom < frac.rows(); ++atom) {
129 for (
int axis = 0; axis < 3; ++axis) {
130 frac(atom, axis) = wrapFrac(frac(atom, axis));
133 const Matrix3d average = 0.5 * (hFrom + hTo);
135 out.
atomic = frac * average;
136 const Matrix3d dh = jacobian * (hTo - hFrom);
137 out.
cell = 0.5 * (hFrom.inverse() * dh + hTo.inverse() * dh);
143 return std::hypot(block.
atomic.norm(), block.
cell.norm());
148 if (!(volume > 0.0) || !(jacobian > 0.0)) {
149 throw std::invalid_argument(
150 "solid-state cell force needs a positive volume and Jacobian");
152 Matrix3d force = -(volume / jacobian) * (cauchy + externalStress);
158 if (!(strainStep > 0.0)) {
159 throw std::invalid_argument(
160 "stress finite difference needs a positive step");
164 const double volume = std::abs(cell.determinant());
165 if (!(volume > 0.0)) {
166 throw std::invalid_argument(
167 "stress finite difference needs a nonzero cell");
170 const int rows[6] = {0, 1, 1, 2, 2, 2};
171 const int cols[6] = {0, 0, 1, 0, 1, 2};
172 for (
int comp = 0; comp < 6; ++comp) {
174 strain(rows[comp], cols[comp]) = strainStep;
175 const Matrix3d plus = Matrix3d::Identity() + strain;
176 const Matrix3d minus = Matrix3d::Identity() - strain;
183 const double derivative =
186 sigma(rows[comp], cols[comp]) = derivative / volume;
194 if (pressure == 0.0) {
199 const double volume = std::abs(h0.determinant());
200 return energy + pressure * strain.trace() * volume;
207 if (!(jacobian > 0.0)) {
208 throw std::invalid_argument(
"solid-state step needs a positive Jacobian");
211 throw std::invalid_argument(
"solid-state step force row count mismatch");
214 Matrix3d strain = cellForce / jacobian;
217 step.
cell = cell * strain;
218 zeroUpper(step.
cell);
Eigen::Matrix< double, 3, 3, eOnStorageOrder > Matrix3d
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
const AtomMatrix & getPositions() const
void setPositions(const AtomMatrix &pos)
void setCell(const Matrix3d &newCell)
long int numberOfAtoms() const
double getPotentialEnergy() const
int getFixed(long int atom) const
1 if every Cartesian axis of the atom is fixed, else 0.
bool orientCellLowerTriangular(Matrix3d &cell, AtomMatrix &positions)
Rotate the lab frame so the cell is lower triangular: the first lattice vector lies on x and the seco...
void interpolateSolidStateLinear(std::vector< Matter > &images)
Replace interior images by a fractional linear interpolation of the oriented endpoints.
CartesianStep solidStateCartesianStep(const Matter &image, const AtomMatrix &atomicForce, const Matrix3d &cellForce, double jacobian)
double jointNorm(const JointBlock &block)
JointBlock jointDisplacement(const Matter &from, const Matter &to, double jacobian)
Displacement of to relative to from in the joint metric.
double solidStateEnthalpy(const Matter &image, const Matter &reference, double pressure)
Potential energy plus P : (h0^{-1} (h-h0)) * V0.
double solidStateJacobian(double meanVolume, int nAtoms, double weight)
J = (V/N)^{1/3} * N^{1/2} * weight, with V the mean endpoint volume.
void orientSolidStateMatter(Matter &image)
Matrix3d finiteDifferenceCauchyStress(const Matter &image, double strainStep)
Central difference of the potential energy on the six lower strain components.
Matrix3d cellNebForce(const Matrix3d &cauchy, double volume, double jacobian, const Matrix3d &externalStress)
NEB force on the Jacobian-scaled strain.
One steepest step in Cartesian coordinates and the cell matrix.