40 Eigen::Matrix<std::int64_t, Eigen::Dynamic, 1>
atomIndex;
56 structComp{params.structure_comparison_options()},
69void checkAtom(
long nAtoms,
long indexAtom,
const char *fn) {
70 if (indexAtom < 0 || indexAtom >= nAtoms) {
71 throw std::out_of_range(std::string(fn) +
": atom index out of range");
74void checkAxis(
int axis,
const char *fn) {
75 if (axis < 0 || axis > 2) {
76 throw std::out_of_range(std::string(fn) +
": axis out of range");
87 if (
this == &matter) {
101 impl_->cellInverse = matter.
impl_->cellInverse;
137 :
impl_{std::make_unique<Impl>()} {
138 operator=(std::move(other));
142 if (
this == &other) {
155 impl_->positions = std::move(other.impl_->positions);
156 impl_->velocities = std::move(other.impl_->velocities);
157 impl_->forces = std::move(other.impl_->forces);
158 impl_->biasForces = std::move(other.impl_->biasForces);
160 other.biasPotential =
nullptr;
161 impl_->masses = std::move(other.impl_->masses);
162 impl_->atomicNrs = std::move(other.impl_->atomicNrs);
163 impl_->isFixed = std::move(other.impl_->isFixed);
164 impl_->atomIndex = std::move(other.impl_->atomIndex);
166 impl_->freeMask = std::move(other.impl_->freeMask);
167 impl_->maskedForces = std::move(other.impl_->maskedForces);
171 impl_->cell = std::move(other.impl_->cell);
172 impl_->cellInverse = std::move(other.impl_->cellInverse);
178 other.recomputePotential =
true;
179 other.recomputeFreeMask =
true;
180 other.recomputeMaskedForces =
true;
187 if (
structComp.check_rotation && indistinguishable) {
190 }
else if (indistinguishable) {
208 throw std::invalid_argument(
"Matter::distanceTo: size mismatch");
210 return pbc(
impl_->positions - matter.
impl_->positions).norm();
216 double max_distance = 0.0;
220 for (i = 0; i <
nAtoms; i++) {
221 max_distance = std::max(diff.row(i).norm(), max_distance);
229 throw std::invalid_argument(
"Matter::resize: negative atom count");
233 const bool keepAtomIds =
234 (length ==
nAtoms &&
impl_->atomIndex.size() == length &&
239 impl_->positions.resize(length, 3);
240 impl_->positions.setZero();
242 impl_->velocities.resize(length, 3);
243 impl_->velocities.setZero();
245 impl_->biasForces.resize(length, 3);
246 impl_->biasForces.setZero();
248 impl_->forces.resize(length, 3);
249 impl_->forces.setZero();
251 impl_->masses.resize(length);
252 impl_->masses.setZero();
254 impl_->atomicNrs.resize(length);
255 impl_->atomicNrs.setZero();
257 impl_->isFixed.resize(length, 3);
258 impl_->isFixed.setZero();
261 impl_->atomIndex.resize(length);
263 for (
long i = 0; i < length; i++) {
264 impl_->atomIndex(i) =
static_cast<std::int64_t
>(i);
278 impl_->cell = newCell;
285 checkAtom(
nAtoms, indexAtom,
"Matter::getPosition");
286 checkAxis(axis,
"Matter::getPosition");
287 return impl_->positions(indexAtom, axis);
291 checkAtom(
nAtoms, indexAtom,
"Matter::setPosition");
292 checkAxis(axis,
"Matter::setPosition");
293 impl_->positions(indexAtom, axis) = position;
302 checkAtom(
nAtoms, indexAtom,
"Matter::setVelocity");
303 checkAxis(axis,
"Matter::setVelocity");
304 impl_->velocities(indexAtom, axis) = vel;
327 VectorXi ret(
static_cast<Eigen::Index
>(
freeIndices.size()));
335 std::string prefixMovie, std::string prefixCheckpoint,
336 bool retainMovieFrames) {
337 if (retainMovieFrames) {
341 *
this, *
parameters, quiet, writeMovie, checkpoint, prefixMovie,
342 prefixCheckpoint, retainMovieFrames ? &
movie_frames_ :
nullptr);
351 if (pos.rows() !=
nAtoms) {
352 throw std::invalid_argument(
"Matter::setPositions: row count mismatch");
354 impl_->positions = pos;
420 return impl_->maskedForces;
425 return impl_->forces;
437 ret.row(
static_cast<long>(j)) = allForces.row(
freeIndices[j]);
449 checkAtom(
nAtoms, index1,
"Matter::distance");
450 checkAtom(
nAtoms, index2,
"Matter::distance");
451 return pbc(
impl_->positions.row(index1) -
impl_->positions.row(index2))
458 checkAtom(
nAtoms, index1,
"Matter::pdistance");
459 checkAtom(
nAtoms, index2,
"Matter::pdistance");
460 checkAxis(axis,
"Matter::pdistance");
461 Matrix<double, 1, 3> ret;
464 impl_->positions(index1, axis) -
impl_->positions(index2, axis);
472 checkAtom(
nAtoms, index,
"Matter::distance");
473 checkAtom(matter.
nAtoms, index,
"Matter::distance");
479 checkAtom(
nAtoms, indexAtom,
"Matter::getMass");
480 return (
impl_->masses[indexAtom]);
484 checkAtom(
nAtoms, indexAtom,
"Matter::setMass");
485 impl_->masses[indexAtom] = mass;
489 if (massesIn.size() !=
nAtoms) {
490 throw std::invalid_argument(
"Matter::setMasses: size mismatch");
492 impl_->masses = massesIn;
496 return (
impl_->atomicNrs[indexAtom]);
500 impl_->atomicNrs[indexAtom] = atomicNr;
506 checkAtom(
nAtoms, indexAtom,
"Matter::getFixed");
507 return (
impl_->isFixed(indexAtom, 0) > 0.5 &&
508 impl_->isFixed(indexAtom, 1) > 0.5 &&
509 impl_->isFixed(indexAtom, 2) > 0.5)
515 checkAtom(
nAtoms, indexAtom,
"Matter::getFixed");
516 checkAxis(axis,
"Matter::getFixed");
517 return impl_->isFixed(indexAtom, axis) > 0.5 ? 1 : 0;
521 checkAtom(
nAtoms, indexAtom,
"Matter::getFixedMask");
522 return {
impl_->isFixed(indexAtom, 0) > 0.5,
523 impl_->isFixed(indexAtom, 1) > 0.5,
524 impl_->isFixed(indexAtom, 2) > 0.5};
528 checkAtom(
nAtoms, indexAtom,
"Matter::setFixed");
529 const double v = isFixed_passed ? 1.0 : 0.0;
530 impl_->isFixed(indexAtom, 0) = v;
531 impl_->isFixed(indexAtom, 1) = v;
532 impl_->isFixed(indexAtom, 2) = v;
538 checkAtom(
nAtoms, indexAtom,
"Matter::setFixed");
539 checkAxis(axis,
"Matter::setFixed");
540 impl_->isFixed(indexAtom, axis) = isFixed_passed ? 1.0 : 0.0;
546 checkAtom(
nAtoms, indexAtom,
"Matter::setFixedMask");
547 impl_->isFixed(indexAtom, 0) = mask[0] ? 1.0 : 0.0;
548 impl_->isFixed(indexAtom, 1) = mask[1] ? 1.0 : 0.0;
549 impl_->isFixed(indexAtom, 2) = mask[2] ? 1.0 : 0.0;
565 Eigen::VectorXd speed2 = vfree.rowwise().squaredNorm();
566 return 0.5 * (
impl_->masses.array() * speed2.array()).sum();
575 for (
long i = 0; i <
nAtoms; ++i) {
599 throw std::runtime_error(
600 "Potential requires an isolated (non-periodic) molecular layout "
601 "(NWChem/ORCA-class). Disable periodic boundaries before optimizing; "
602 "PBC wraps can tear non-centered molecules (issue #188).");
609 throw std::runtime_error(
610 "Matter::computePotential called without a potential");
615 auto surrogatePotential =
617 auto [freePE, freeForces, vari] = surrogatePotential->
get_ef_var(
621 for (
long idx{0}, jdx{0}; idx <
nAtoms; idx++) {
623 impl_->forces.row(idx) = freeForces.row(jdx);
632 const auto n =
static_cast<size_t>(
nAtoms);
637 potential->force(std::span<const double>(
impl_->positions.data(), n * 3),
638 std::span<const int>(
impl_->atomicNrs.data(), n),
639 std::span<double>(
impl_->forces.data(), n * 3),
641 std::span<const double>(force_cell.data(), 9));
646 throw std::runtime_error(
647 "Potential returned non-finite energy or forces");
655 Vector3d tempForce =
impl_->forces.colwise().sum() /
nAtoms;
656 for (
long int i = 0; i <
nAtoms; i++) {
657 impl_->forces.row(i) -= tempForce.transpose();
670 if (std::abs(
impl_->cell.determinant()) < 1e-30) {
678 if (rows.rows() !=
nAtoms) {
679 throw std::invalid_argument(
680 "Matter::maxFreeAtomForce: row count does not match atom count");
694 if (atmnrs.size() != this->nAtoms) {
695 throw std::invalid_argument(
696 "Vector of atomic numbers not equal to the number of atoms");
698 this->
impl_->atomicNrs = atmnrs;
707 impl_->freeMask = 1.0 -
impl_->isFixed.array();
710 for (
long i = 0; i <
nAtoms; i++) {
711 if (
impl_->freeMask.row(i).sum() > 0.5) {
717 return impl_->freeMask;
743 auto invMass =
impl_->masses.array().inverse();
744 ret.col(0).array() *= invMass;
745 ret.col(1).array() *= invMass;
746 ret.col(2).array() *= invMass;
751 return impl_->masses;
765 "Disabled PBC for isolated-molecule potential (NWChem/ORCA-class); "
766 "re-enabling PBC will throw (issue #188)");
781 Vector3d tempForce =
impl_->forces.colwise().sum() /
nAtoms;
782 for (
long int i = 0; i <
nAtoms; i++) {
783 impl_->forces.row(i) -= tempForce.transpose();
789 return this->
potential->forceCallCounter;
796 throw std::logic_error(
797 "Matter::cauchyStress requires a potential that reports stress");
802 if (!sigma.allFinite()) {
803 throw std::runtime_error(
"Potential returned a non-finite stress tensor");
827 return impl_->atomIndex(atom);
831 impl_->atomIndex(atom) = index;
836 impl_->forces = fileForces;
848 std::vector<Matter *> dirty;
849 for (
Matter *m : systems) {
850 if (m !=
nullptr && m->needsForceUpdate()) {
857 if (dirty.size() == 1 || !
pot.supportsBatchEvaluation()) {
863 const long n = dirty.size();
864 const long atoms = dirty.front()->numberOfAtoms();
865 std::vector<VectorXi> nrs(n);
866 std::vector<Matrix3d> boxes(n);
867 std::vector<const double *> posPtr, boxPtr;
868 std::vector<const int *> nrsPtr;
869 std::vector<double *> frcPtr;
870 for (
long j = 0; j < n; ++j) {
873 throw std::invalid_argument(
874 "evaluateTogether: systems differ in atom count");
881 for (
long j = 0; j < n; ++j) {
882 posPtr.push_back(dirty[j]->getPositions().data());
883 nrsPtr.push_back(nrs[j].data());
884 frcPtr.push_back(dirty[j]->forcesData());
885 boxPtr.push_back(boxes[j].data());
887 std::vector<double> energies(n), variances(n);
888 pot.forceBatch(n, atoms, posPtr.data(), nrsPtr.data(), frcPtr.data(),
889 energies.data(), variances.data(), boxPtr.data());
890 for (
long j = 0; j < n; ++j) {
891 dirty[j]->setComputedPotential(energies[j], variances[j]);
Eigen::Matrix< double, 3, 3, eOnStorageOrder > Matrix3d
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
#define EONC_LOG_WARNING(...)
Functionality relying on the conjugate gradients algorithm.
void applyPeriodicBoundary()
void setBiasPotential(BondBoost *bondBoost)
double getKineticEnergy() const
bool getWriteConForces() const noexcept
Parameters.main_options().writeConForces for this Matter, if bound.
VectorXi getAtomicNrs() const
void setComputedPotential(double energy, double variance)
Set energy/variance from external batched evaluation and mark forces as up-to-date (recomputePotentia...
void setForces(const AtomMatrix &f)
void setFixed(long int atom, int isFixed)
Broadcast a whole-atom flag onto all three axes.
std::shared_ptr< Potential > getPotential()
BondBoost * getBiasPotential() const
The bias potential added to this Matter's forces, or nullptr.
double maxFreeAtomForce(const AtomMatrix &rows) const
Max per-atom Euclidean norm of rows.
void setFixedMask(long int atom, std::array< bool, 3 > mask)
void setPosition(long int atom, int axis, double position)
PbcConvention pbcConvention
const AtomMatrix & getPositions() const
bool relax(bool quiet=false, bool writeMovie=false, bool checkpoint=false, std::string prefixMovie=std::string(), std::string prefixCheckpoint=std::string(), bool retainMovieFrames=false)
double distanceTo(const Matter &matter)
VectorXd getForcesFreeV() const
void setPositions(const AtomMatrix &pos)
void setAtomicNr(long int atom, long atomicNr)
double pdistance(long index1, long index2, int axis) const
AtomMatrix getForcesFree() const
const Matter & operator=(const Matter &matter)
std::vector< readcon::ConFrame > movie_frames_
bool compare(const Matter &matter, bool indistinguishable=false)
void setPositionsV(const VectorXd &pos)
void setCell(const Matrix3d &newCell)
std::vector< int > freeIndices
AtomMatrix getFree() const
bool getPeriodic() const noexcept
void setVelocity(long int atom, int axis, double velocity)
void computePotential() const
void setPotential(std::shared_ptr< Potential > pot)
void setPositionsFreeV(const VectorXd &pos)
bool recomputeMaskedForces
const Parameters * parameters
long int numberOfAtoms() const
Matrix3d cauchyStress()
Cauchy stress in eV/Angstrom^3.
AtomMatrix pbc(const AtomMatrix &diff) const
std::shared_ptr< Potential > potential
long getForceCalls() const
VectorXd getPositionsFreeV() const
BondBoost * biasPotential
void setAtomicNrs(const VectorXi &atmnrs)
AtomMatrix getVelocities() const
void setVelocities(const AtomMatrix &v)
double getMechanicalEnergy() const
void setAtomIndex(long int atom, std::int64_t index)
double perAtomNorm(const Matter &matter)
const AtomMatrix & getForcesRaw() const
void resize(long int nAtoms)
VectorXd pbcV(const VectorXd &diff) const
AtomMatrix getPositionsFree() const
double getPosition(long int atom, int axis) const
size_t getPotentialCalls() const
VectorXi getAtomicNrsFree() const
void restoreFileForces(const AtomMatrix &fileForces, bool trustEnergy, double energy)
Write .con forces without the fixed-atom mask setForces applies.
Eigen::Matrix< double, Eigen::Dynamic, 1 > getMasses() const
long int numberOfFixedAtoms() const
std::unique_ptr< Impl > impl_
double distance(long index1, long index2) const
double getMass(long int atom) const
void setMasses(const VectorXd &massesIn)
void setMass(long int atom, double mass)
double getPotentialEnergy() const
AtomMatrix getAccelerations()
double * forcesData()
Mutable access to force storage for batched potential evaluation.
AtomMatrix getBiasForces()
std::array< std::string, 5 > headerCon
StructureComparisonOptions structComp
bool usePeriodicBoundaries
long int numberOfFreeAtoms() const
VectorXd getFreeV() const
Matter(std::shared_ptr< Potential > pot, const Parameters ¶ms)
long getAtomicNr(long int atom) const
double getEnergyVariance() const
AtomMatrix getPositionsCopy() const
std::array< bool, 3 > getFixedMask(long int atom) const
Per-axis CON column-4 mask (bit0=x, bit1=y, bit2=z).
void setBiasForces(const AtomMatrix &bf)
void setPositionsFree(const AtomMatrix &pos)
std::vector< long > fileToMatter
std::int64_t getAtomIndex(long int atom) const
.con column-5 index (pre-grouping); public for I/O / bindings.
void assertIsolatedMoleculeLayoutSafe() const
Throw if pot forbids PBC (isolated molecular QM backends, issue #188).
VectorXd getPositionsV() const
void assignKeepingBias(const Matter &other)
Copy another structure in and keep this Matter's own bias potential.
const AtomMatrix & getForces() const
int getFixed(long int atom) const
1 if every Cartesian axis of the atom is fixed, else 0.
VectorXd getForcesV() const
double maxForce(void) const
void on_force_call(PotType t) noexcept override
static PotRegistry & get() noexcept
Process-lifetime singleton.
std::tuple< double, AtomMatrix, double > get_ef_var(const AtomMatrix pos, const VectorXi atmnrs, const Matrix3d box)
bool rotationMatch(const Matter &m1, const Matter &m2, const double max_diff)
bool identical(const Matter &m1, const Matter &m2, const double distanceDifference)
bool sortedR(const Matter &m1, const Matter &m2, const double distanceDifference)
void translationRemove(Matter &m1, const AtomMatrix r1)
bool relaxMatter(Matter &matter, const Parameters ¶ms, bool quiet=false, bool writeMovie=false, bool checkpoint=false, std::string prefixMovie=std::string(), std::string prefixCheckpoint=std::string(), std::vector< readcon::ConFrame > *outFrames=nullptr)
AtomMatrix apply(const AtomMatrix &diff, const Matrix3d &cell, const Matrix3d &cellInverse)
AtomMatrix applyPositions(const AtomMatrix &coords, const Matrix3d &cell, const Matrix3d &cellInverse, PbcConvention convention)
VectorXd applyV(const VectorXd &diffVector, const Matrix3d &cell, const Matrix3d &cellInverse)
RAII resource manager for the ARTn C library with global synchronization.
void evaluateTogether(Potential &pot, std::span< Matter *const > systems)
Evaluates every system that needs a force update.
double maxFreeAtomForceNorm(const double *forces, const double *fixed, long nAtoms)
Max Euclidean norm over N x 3 row-major force rows.
Eigen::Matrix< std::int64_t, Eigen::Dynamic, 1 > atomIndex