37#include <sys/resource.h>
44 return v1 -
matDot(v1, v2) * eonc::safemath::safe_normalized(v2);
48 using namespace std::chrono;
49 auto now = steady_clock::now();
51 *real = duration<double>(now.time_since_epoch()).count();
60 struct rusage r_usage;
61 if (getrusage(RUSAGE_SELF, &r_usage) != 0) {
65 *user =
static_cast<double>(r_usage.ru_utime.tv_sec) +
66 static_cast<double>(r_usage.ru_utime.tv_usec) / 1e6;
69 *sys =
static_cast<double>(r_usage.ru_stime.tv_sec) +
70 static_cast<double>(r_usage.ru_stime.tv_usec) / 1e6;
76 return std::filesystem::exists(filename);
79std::optional<std::string>
82 std::filesystem::current_path(std::filesystem::path{jobPath});
83 }
catch (
const std::filesystem::filesystem_error &err) {
84 return std::string{err.what()};
90 std::string_view name) {
91 namespace fs = std::filesystem;
92 if (name.empty() || name ==
"." || name ==
".." ||
93 name.find(
'/') != std::string_view::npos ||
94 name.find(
'\\') != std::string_view::npos) {
97 const fs::path dest{std::string{name}};
99 const bool destFile = fs::is_regular_file(dest, ec);
100 if (logHome.empty()) {
103 const fs::path src = fs::path{std::string{logHome}} / dest.filename();
105 if (!fs::is_regular_file(src, ec)) {
109 if (destFile && fs::equivalent(src, dest, ec)) {
113 fs::copy_file(src, dest, fs::copy_options::overwrite_existing, ec);
115 EONC_LOG_ERROR(
"stageReturnLog: cannot copy {} to {}: {}", src.string(),
116 dest.string(), ec.message());
117 return fs::is_regular_file(dest);
123 const auto dot = filename.rfind(
'.');
124 const std::string prefix =
125 (dot == std::string::npos) ? filename : filename.substr(0, dot);
126 const std::string postfix =
127 (dot == std::string::npos) ? std::string{} : filename.substr(dot);
128 std::string filenameRelevant = prefix +
"_cp" + postfix;
130 return filenameRelevant;
132 filenameRelevant = prefix +
"_in" + postfix;
134 return filenameRelevant;
140 std::ifstream massFile(filename.c_str());
141 if (!massFile.is_open()) {
143 throw std::runtime_error(std::format(
"cannot open {}", filename));
146 VectorXd masses(nAtoms);
147 for (
int i = 0; i < nAtoms; i++) {
149 if (!(massFile >> mass)) {
151 throw std::runtime_error(
152 std::format(
"{} ended after {} of {} masses", filename, i, nAtoms));
164 mode.resize(nAtoms, 3);
166 for (
int i = 0; i < nAtoms; i++) {
167 if (fscanf(modeFile,
"%lf %lf %lf", &mode(i, 0), &mode(i, 1),
170 throw std::runtime_error(
171 std::format(
"mode file ended after {} of {} atoms", i, nAtoms));
179 auto closer = [](
FILE *f) {
183 std::unique_ptr<
FILE,
decltype(closer)> modeFile(
184 std::fopen(filename.c_str(),
"rb"), closer);
187 throw std::runtime_error(std::format(
"cannot open {}", filename));
189 return loadMode(modeFile.get(), nAtoms);
193 Matter &target,
const Matter &initial,
const std::string &displacementPath,
194 const std::string &modePath,
double scale) {
207 std::vector<long> fileMap(
static_cast<size_t>(n));
208 for (
long i = 0; i < n; i++) {
210 pos.row(i) = initPos.row(i);
213 fileMap[
static_cast<size_t>(i)] = initial.
mapFileRow(i);
224 const double norm = mode.norm();
228 mode *= (scale / norm);
234 for (
long i = 0; i < n; i++) {
236 pos.row(i) = initPos.row(i);
240 EONC_LOG_INFO(
"Synthesized displacement from pos.con + scale {:.6g} * unit "
241 "mode in {} (missing {})",
242 scale, modePath, displacementPath);
275 const double radius = opt.displace_radius;
276 const double mag = opt.displace_magnitude;
279 for (
long i = 0; i < n; ++i) {
283 const double dist = (i == epicenter) ? 0.0 : initial.
distance(epicenter, i);
284 if (dist <= radius) {
285 for (
int a = 0; a < 3; ++a) {
290 const double norm = mode.norm();
294 }
else if (epicenter >= 0 && epicenter < n && !initial.
getFixed(epicenter)) {
295 mode(epicenter, 0) = 1.0;
296 pos(epicenter, 0) += mag;
299 if (modeOut !=
nullptr) {
300 *modeOut = std::move(mode);
308 long const nAtoms = matter->numberOfAtoms();
309 for (
long i = 0; i < nAtoms; ++i) {
310 fprintf(modeFile,
"%.17g\t%.17g\t%.17g\n", free(i, 0) * mode(i, 0),
311 free(i, 1) * mode(i, 1), free(i, 2) * mode(i, 2));
317 std::shared_ptr<Matter> matter,
AtomMatrix mode) {
318 std::ofstream out(filename);
322 long const nAtoms = matter->numberOfAtoms();
323 for (
long i = 0; i < nAtoms; ++i) {
324 out << std::format(
"{:.17g}\t{:.17g}\t{:.17g}\n", free(i, 0) * mode(i, 0),
325 free(i, 1) * mode(i, 1), free(i, 2) * mode(i, 2));
331 std::vector<int> list;
336 size_t end = s.find_first_of(delim);
337 while (start < s.size()) {
338 auto token = s.substr(start, end - start);
339 if (!token.empty()) {
341 list.push_back(std::stoi(token));
342 }
catch (
const std::exception &) {
346 if (end == std::string::npos)
349 end = s.find_first_of(delim, start);
354std::optional<std::string_view>
356 if (metric ==
"max_atom") {
357 return "Max atom force";
359 if (metric ==
"max_component") {
360 return "Max force comp";
362 if (metric ==
"norm") {
365 if (metric ==
"rms") {
372 std::string_view context) {
376 throw std::invalid_argument(
377 std::format(
"{} unknown convergence_metric: {}", context, metric));
389 params.optimizer_options().convergence_metric,
"[Matter]");
391 ~MatterObjectiveFunction() =
default;
393 VectorXd getGradient(
bool fdstep =
false) {
400 return getConvergence() < params.optimizer_options().converged_force;
402 double getConvergence() {
403 if (params.optimizer_options().convergence_metric ==
"norm") {
405 }
else if (params.optimizer_options().convergence_metric ==
"rms") {
407 const auto n = f.size();
408 return n > 0 ? f.norm() / std::sqrt(
static_cast<double>(n)) : 0.0;
409 }
else if (params.optimizer_options().convergence_metric ==
"max_atom") {
411 }
else if (params.optimizer_options().convergence_metric ==
413 return m_matter.
getForces().cwiseAbs().maxCoeff();
416 params.optimizer_options().convergence_metric);
417 throw std::invalid_argument(
418 std::format(
"[Matter] unknown convergence_metric: {}",
419 params.optimizer_options().convergence_metric));
422 VectorXd difference(
const VectorXd &a,
const VectorXd &b) {
423 return m_matter.
pbcV(a - b);
425 VectorXd getMasses()
const override {
427 const auto mask = m_matter.
getFree();
431 if (mask.row(i).sum() > 0.5) {
437 bool getPeriodic()
const override {
return m_matter.
getPeriodic(); }
438 void minimumImage(Eigen::Ref<Eigen::Vector3d> dr)
const override {
440 m.row(0) = dr.transpose();
442 dr = m.row(0).transpose();
448 bool quiet,
bool writeMovie,
bool checkpoint,
449 std::string prefixMovie,
450 std::string prefixCheckpoint,
451 std::vector<readcon::ConFrame> *outFrames) {
453 auto objf = std::make_shared<MatterObjectiveFunction>(matter, params);
457 std::ostringstream min;
459 std::string minDatFilename = prefixMovie +
".dat";
460 auto write_movie_frame = [&](uint64_t frameIndex,
bool append,
465 metadata.
scalars.push_back({
"step_size", stepSize});
466 metadata.
scalars.push_back({
"convergence", objf->getConvergence()});
472 QUILL_LOG_WARNING(m_log,
"Failed to write movie frame {}", min.str());
477 std::ofstream minDat(minDatFilename,
478 append ? (std::ios::binary | std::ios::app)
482 minDat <<
"iteration\tstep_size\tconvergence\tenergy\n";
484 minDat << std::format(
"{}\t{:.5e}\t{:.5e}\t{:.6f}\n", frameIndex,
485 stepSize, objf->getConvergence(),
490 if (writeMovie || outFrames) {
491 write_movie_frame(0,
false, 0.0);
496 QUILL_LOG_DEBUG(m_log,
"{} {:10s} {:14s} {:18s} {:13s}\n",
"[Matter]",
500 QUILL_LOG_DEBUG(m_log,
"{} {:10} {:14.5e} {:18.5e} {:13.5f}\n",
501 "[Matter]", iteration, 0.0, objf->getConvergence(),
505 while (!objf->isConverged() &&
517 QUILL_LOG_DEBUG(m_log,
"{} {:10} {:14.5e} {:18.5e} {:13.5f}",
518 "[Matter]", iteration, stepSize, objf->getConvergence(),
522 if (writeMovie || outFrames) {
523 write_movie_frame(
static_cast<uint64_t
>(iteration),
true, stepSize);
527 std::ostringstream chk;
528 chk << prefixCheckpoint <<
"_cp";
530 QUILL_LOG_WARNING(m_log,
"Failed to write checkpoint {}", chk.str());
535 if (iteration == 0) {
537 QUILL_LOG_DEBUG(m_log,
"{} {:10} {:14.5e} {:18.5e} {:13.5f}",
538 "[Matter]", iteration, 0.0, objf->getConvergence(),
542 return objf->isConverged();
double matDot(const AtomMatrix &a, const AtomMatrix &b)
SIMD-optimized dot product for contiguous Eigen matrices.
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
#define EONC_LOG_ERROR(...)
#define EONC_LOG_WARNING(...)
#define EONC_LOG_INFO(...)
#define EONC_LOG_CRITICAL(...)
The optimizer class is used to serve as an abstract class for all optimizers, as well as to call an o...
const AtomMatrix & getPositions() const
VectorXd getForcesFreeV() const
void setPositions(const AtomMatrix &pos)
AtomMatrix getFree() const
bool getPeriodic() const noexcept
void setPositionsFreeV(const VectorXd &pos)
long int numberOfAtoms() const
AtomMatrix pbc(const AtomMatrix &diff) const
VectorXd getPositionsFreeV() const
void setAtomIndex(long int atom, std::int64_t index)
VectorXd pbcV(const VectorXd &diff) const
Eigen::Matrix< double, Eigen::Dynamic, 1 > getMasses() const
double distance(long index1, long index2) const
double getPotentialEnergy() const
long mapFileRow(long file_row) const
Map a CON file-order row onto the Matter row after matter_order.
long int numberOfFreeAtoms() const
AtomMatrix getPositionsCopy() const
std::int64_t getAtomIndex(long int atom) const
.con column-5 index (pre-grouping); public for I/O / bindings.
const AtomMatrix & getForces() const
void setFileToMatter(std::vector< long > map)
io::IoStatus matter2con(std::string filename, bool append=false, const io::ConFrameMetadata *metadata=nullptr)
int getFixed(long int atom) const
1 if every Cartesian axis of the atom is fixed, else 0.
io::IoStatus con2matter(std::string filename)
double maxForce(void) const
const saddle_search_options_t & saddle_search_options() const
const debug_options_t & debug_options() const
const structure_comparison_options_t & structure_comparison_options() const
const optimizer_options_t & optimizer_options() const
long lastAtom(const Matter *matter)
const char DISP_LISTED_ATOMS[]
const char DISP_NOT_FCC_OR_HCP[]
const char DISP_MIN_COORDINATED[]
long cnaEpiCenter(const Matter *matter, double neighborCutoff)
const char DISP_LAST_ATOM[]
long randomFreeAtomEpiCenter(const Matter *matter)
long listedAtomEpiCenter(const Matter *matter, const std::vector< long > &atomList)
long minCoordinatedEpiCenter(const Matter *matter, double neighborCutoff)
double maxAtomMotion(const AtomMatrix v1)
std::unique_ptr< Optimizer > mkOptim(std::shared_ptr< ObjectiveFunction > a_objf, OptType a_otype, const Parameters &a_params)
VectorXd loadMasses(std::string filename, int nAtoms)
bool applyClientDisplacement(Matter &target, const Matter &initial, const Parameters ¶ms, AtomMatrix *modeOut)
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)
std::string getRelevantFile(std::string filename)
bool loadOrSynthesizeDisplacement(Matter &target, const Matter &initial, const std::string &displacementPath, const std::string &modePath, double scale)
AtomMatrix loadMode(FILE *modeFile, int nAtoms)
void saveMode(FILE *modeFile, std::shared_ptr< Matter > matter, AtomMatrix mode)
Write a mode; constrained axes are emitted as 0.
std::optional< std::string_view > convergenceMetricLabel(std::string_view metric)
Display label for a force-convergence metric, or nullopt when the name is none of the four the optimi...
std::vector< int > split_string_int(std::string s, std::string delim)
bool stageReturnLog(std::string_view logHome, std::string_view name)
Copy name from logHome into the current directory when that file is not already this directory's copy...
void requireKnownConvergenceMetric(std::string_view metric, std::string_view context)
Throws std::invalid_argument naming context when metric is unrecognized.
std::optional< std::string > enterJobDirectory(std::string_view jobPath)
Enter jobPath as the working directory.
void getTime(double *real, double *user, double *sys)
AtomMatrix makeOrthogonal(const AtomMatrix v1, const AtomMatrix v2)
bool existsFile(std::string filename)
readcon::ConFrame matterToConFrame(Matter &m, const ConFrameMetadata *metadata)
Build a single stamped ConFrame from Matter (same builder as matter2con).
constexpr bool io_ok(IoStatus s) noexcept
double gaussRandom(double avg, double std)
RAII resource manager for the ARTn C library with global synchronization.
bool write_deprecated_outs
RAII helper for class-scoped logging.
std::string convergence_metric_label
std::string displace_type