32#include <sys/resource.h>
42 return v1 -
matDot(v1, v2) * eonc::safemath::safe_normalized(v2);
47 using namespace std::chrono;
48 auto now = steady_clock::now();
49 *real = duration<double>(now.time_since_epoch()).count();
57 struct rusage r_usage;
58 if (getrusage(RUSAGE_SELF, &r_usage) != 0) {
62 *user =
static_cast<double>(r_usage.ru_utime.tv_sec) +
63 static_cast<double>(r_usage.ru_utime.tv_usec) / 1e6;
66 *sys =
static_cast<double>(r_usage.ru_stime.tv_sec) +
67 static_cast<double>(r_usage.ru_stime.tv_usec) / 1e6;
73 return std::filesystem::exists(filename);
77 string filenameRelevant;
78 string filenamePrefix;
79 string filenamePostfix;
82 int i = filename.rfind(
".");
83 filenamePrefix.assign(filename, 0, i);
84 filenamePostfix.assign(filename, i, filename.size());
85 filenameRelevant = filenamePrefix +
"_cp" + filenamePostfix;
87 return filenameRelevant;
90 filenameRelevant = filenamePrefix +
"_in" + filenamePostfix;
92 return filenameRelevant;
99 ifstream massFile(filename.c_str());
100 if (!massFile.is_open()) {
102 throw std::runtime_error(std::format(
"cannot open {}", filename));
105 VectorXd masses(nAtoms);
106 for (
int i = 0; i < nAtoms; i++) {
108 if (!(massFile >> mass)) {
110 throw std::runtime_error(
111 std::format(
"{} ended after {} of {} masses", filename, i, nAtoms));
123 mode.resize(nAtoms, 3);
125 for (
int i = 0; i < nAtoms; i++) {
126 if (fscanf(modeFile,
"%lf %lf %lf", &mode(i, 0), &mode(i, 1),
129 throw std::runtime_error(
130 std::format(
"mode file ended after {} of {} atoms", i, nAtoms));
138 auto closer = [](
FILE *f) {
142 std::unique_ptr<
FILE,
decltype(closer)> modeFile(
143 std::fopen(filename.c_str(),
"rb"), closer);
146 throw std::runtime_error(std::format(
"cannot open {}", filename));
148 return loadMode(modeFile.get(), nAtoms);
152 Matter &target,
const Matter &initial,
const std::string &displacementPath,
153 const std::string &modePath,
double scale) {
165 for (
long i = 0; i < n; i++) {
167 pos.row(i) = initPos.row(i);
178 const double norm = mode.norm();
182 mode *= (scale / norm);
188 for (
long i = 0; i < n; i++) {
190 pos.row(i) = initPos.row(i);
194 EONC_LOG_INFO(
"Synthesized displacement from pos.con + scale {:.6g} * unit "
195 "mode in {} (missing {})",
196 scale, modePath, displacementPath);
203 long const nAtoms = matter->numberOfAtoms();
204 for (
long i = 0; i < nAtoms; ++i) {
205 fprintf(modeFile,
"%.17g\t%.17g\t%.17g\n", free(i, 0) * mode(i, 0),
206 free(i, 1) * mode(i, 1), free(i, 2) * mode(i, 2));
212 std::shared_ptr<Matter> matter,
AtomMatrix mode) {
213 std::ofstream out(filename);
217 long const nAtoms = matter->numberOfAtoms();
218 for (
long i = 0; i < nAtoms; ++i) {
219 out << std::format(
"{:.17g}\t{:.17g}\t{:.17g}\n", free(i, 0) * mode(i, 0),
220 free(i, 1) * mode(i, 1), free(i, 2) * mode(i, 2));
226 std::vector<int> list;
231 size_t end = s.find_first_of(delim);
232 while (start < s.size()) {
233 auto token = s.substr(start, end - start);
234 if (!token.empty()) {
236 list.push_back(std::stoi(token));
237 }
catch (
const std::exception &) {
241 if (end == std::string::npos)
244 end = s.find_first_of(delim, start);
249std::optional<std::string_view>
251 if (metric ==
"max_atom") {
252 return "Max atom force";
254 if (metric ==
"max_component") {
255 return "Max force comp";
257 if (metric ==
"norm") {
264 std::string_view context) {
268 throw std::invalid_argument(
269 std::format(
"{} unknown convergence_metric: {}", context, metric));
280 params.optimizer_options.convergence_metric,
"[Matter]");
282 ~MatterObjectiveFunction() =
default;
284 VectorXd getGradient(
bool fdstep =
false) {
291 return getConvergence() < params.optimizer_options.converged_force;
293 double getConvergence() {
294 if (params.optimizer_options.convergence_metric ==
"norm") {
296 }
else if (params.optimizer_options.convergence_metric ==
"max_atom") {
298 }
else if (params.optimizer_options.convergence_metric ==
"max_component") {
302 params.optimizer_options.convergence_metric);
303 throw std::invalid_argument(
304 std::format(
"[Matter] unknown convergence_metric: {}",
305 params.optimizer_options.convergence_metric));
308 VectorXd difference(
const VectorXd &a,
const VectorXd &b) {
309 return m_matter.
pbcV(a - b);
315 bool quiet,
bool writeMovie,
bool checkpoint,
316 std::string prefixMovie,
317 std::string prefixCheckpoint,
318 std::vector<readcon::ConFrame> *outFrames) {
320 auto objf = std::make_shared<MatterObjectiveFunction>(matter, params);
324 std::ostringstream min;
326 std::string minDatFilename = prefixMovie +
".dat";
327 auto write_movie_frame = [&](uint64_t frameIndex,
bool append,
332 metadata.
scalars.push_back({
"step_size", stepSize});
333 metadata.
scalars.push_back({
"convergence", objf->getConvergence()});
339 QUILL_LOG_WARNING(m_log,
"Failed to write movie frame {}", min.str());
344 std::ofstream minDat(minDatFilename,
345 append ? (std::ios::binary | std::ios::app)
349 minDat <<
"iteration\tstep_size\tconvergence\tenergy\n";
351 minDat << std::format(
"{}\t{:.5e}\t{:.5e}\t{:.6f}\n", frameIndex,
352 stepSize, objf->getConvergence(),
357 if (writeMovie || outFrames) {
358 write_movie_frame(0,
false, 0.0);
363 QUILL_LOG_DEBUG(m_log,
"{} {:10s} {:14s} {:18s} {:13s}\n",
"[Matter]",
367 QUILL_LOG_DEBUG(m_log,
"{} {:10} {:14.5e} {:18.5e} {:13.5f}\n",
368 "[Matter]", iteration, 0.0, objf->getConvergence(),
372 while (!objf->isConverged() &&
384 QUILL_LOG_DEBUG(m_log,
"{} {:10} {:14.5e} {:18.5e} {:13.5f}",
385 "[Matter]", iteration, stepSize, objf->getConvergence(),
389 if (writeMovie || outFrames) {
390 write_movie_frame(
static_cast<uint64_t
>(iteration),
true, stepSize);
394 std::ostringstream chk;
395 chk << prefixCheckpoint <<
"_cp";
397 QUILL_LOG_WARNING(m_log,
"Failed to write checkpoint {}", chk.str());
402 if (iteration == 0) {
404 QUILL_LOG_DEBUG(m_log,
"{} {:10} {:14.5e} {:18.5e} {:13.5f}",
405 "[Matter]", iteration, 0.0, objf->getConvergence(),
409 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...
double getPotentialEnergy() const
void setPositionsFreeV(const VectorXd &pos)
const AtomMatrix & getForces() const
double maxForce(void) const
AtomMatrix pbc(const AtomMatrix &diff) const
AtomMatrix getPositionsCopy() const
long int numberOfAtoms() const
VectorXd pbcV(const VectorXd &diff) const
VectorXd getForcesFreeV() const
VectorXd getPositionsFreeV() const
long int numberOfFreeAtoms() const
void setPositions(const AtomMatrix &pos)
io::IoStatus matter2con(std::string filename, bool append=false, const io::ConFrameMetadata *metadata=nullptr)
io::IoStatus con2matter(std::string filename)
const AtomMatrix & getPositions() const
int getFixed(long int atom) const
1 if every Cartesian axis of the atom is fixed, else 0.
struct eonc::Parameters::optimizer_options_t optimizer_options
struct eonc::Parameters::debug_options_t debug_options
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 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)
bool loadOrSynthesizeDisplacement(Matter &target, const Matter &initial, const std::string &displacementPath, const std::string &modePath, double scale)
AtomMatrix loadMode(FILE *modeFile, int nAtoms)
std::string getRelevantFile(std::string filename)
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 three the optim...
std::vector< int > split_string_int(std::string s, std::string delim)
void requireKnownConvergenceMetric(std::string_view metric, std::string_view context)
Throws std::invalid_argument naming context when metric is unrecognized.
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
bool write_deprecated_outs
std::string convergence_metric_label
RAII helper for class-scoped logging.