The following is a list of pull requests relevant to LAMMPS in the Colvars repository since 2024-08-06: - 752 New tool poisson_integrator_conv https://github.com/Colvars/colvars/pull/752 (@jhenin) - 733 Custom grids for all biases https://github.com/Colvars/colvars/pull/733 (@giacomofiorin, @jhenin) - 776 Avoid error in acos and asin with fast-math https://github.com/Colvars/colvars/pull/776 (@jhenin) - 773 fix: fix the clang build test failure of OPES https://github.com/Colvars/colvars/pull/773 (@HanatoK) - 768 fix: clamp the input values of asin and acos in case of fast math on aarch64 https://github.com/Colvars/colvars/pull/768 (@HanatoK) - 761 Add debug code for the Jacobi failure https://github.com/Colvars/colvars/pull/761 (@HanatoK) - 759 min_image fix; Saves long runs from crashes; https://github.com/Colvars/colvars/pull/759 (@PolyachenkoYA) - 757 Fix MSVC OpenMP issue https://github.com/Colvars/colvars/pull/757 (@HanatoK) - 755 Fix indentation of 'Init CVC' message in standard output https://github.com/Colvars/colvars/pull/755 (@jhenin) - 750 Optimize and simplify the calculation of dihedral gradients https://github.com/Colvars/colvars/pull/750 (@HanatoK) - 749 Add references to new Colvars paper https://github.com/Colvars/colvars/pull/749 (@jhenin, @giacomofiorin) - 740 Report the specific C++ standard at init time, stop warning about C++97/03 https://github.com/Colvars/colvars/pull/740 (@giacomofiorin) - 731 Improve detection of hard/mathematical boundaries https://github.com/Colvars/colvars/pull/731 (@giacomofiorin) - 729 Optimize the fit gradients https://github.com/Colvars/colvars/pull/729 (@HanatoK, @jhenin) - 728 Fix undefined behavior when getting the current working directory from std::filesystem https://github.com/Colvars/colvars/pull/728 (@giacomofiorin) - 727 Add patchversion scripting command https://github.com/Colvars/colvars/pull/727 (@giacomofiorin) - 724 Fix gradients and metric functions of distanceDir https://github.com/Colvars/colvars/pull/724 (@giacomofiorin) - 715 Add missing rotation in orientation component https://github.com/Colvars/colvars/pull/715 (@giacomofiorin) - 713 fix: try to solve #87 for non-scala components https://github.com/Colvars/colvars/pull/713 (@HanatoK) - 709 Implementation of OPES in Colvars https://github.com/Colvars/colvars/pull/709 (@HanatoK, @giacomofiorin, @jhenin) - 706 BUGFIX for Segmentation fault in colvarbias_meta::calc_energy() with useGrids off https://github.com/Colvars/colvars/pull/706 (@alphataubio) - 570 enable use of CVs defined by PyTorch neural network models https://github.com/Colvars/colvars/pull/570 (@zwpku, @giacomofiorin, @HanatoK, @jhenin) Authors: @alphataubio, @EzryStIago, @giacomofiorin, @HanatoK, @jhenin, @PolyachenkoYA, @zwpku
508 lines
13 KiB
C++
508 lines
13 KiB
C++
// -*- c++ -*-
|
|
|
|
// This file is part of the Collective Variables module (Colvars).
|
|
// The original version of Colvars and its updates are located at:
|
|
// https://github.com/Colvars/colvars
|
|
// Please update all Colvars source files before making any changes.
|
|
// If you wish to distribute your changes, please submit them to the
|
|
// Colvars repository at GitHub.
|
|
|
|
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
|
|
// Using access() to check if a file exists (until we can assume C++14/17)
|
|
#include <direct.h>
|
|
|
|
#if defined(__has_include)
|
|
# if __has_include(<filesystem>)
|
|
# include <filesystem> // MSVC only defines __cpp_lib_filesystem after include
|
|
# endif
|
|
#endif
|
|
|
|
#else
|
|
|
|
#include <unistd.h>
|
|
|
|
#ifdef __cpp_lib_filesystem
|
|
#include <filesystem>
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if defined(_WIN32)
|
|
#include <io.h>
|
|
#endif
|
|
|
|
#include <cerrno>
|
|
#include <cstdio>
|
|
|
|
#include <list>
|
|
#include <map>
|
|
#include <sstream>
|
|
#include <fstream>
|
|
|
|
#include "colvarmodule.h"
|
|
#include "colvarproxy_io.h"
|
|
|
|
|
|
colvarproxy_io::colvarproxy_io()
|
|
{
|
|
restart_frequency_engine = 0;
|
|
input_stream_error_ = new std::istringstream();
|
|
input_stream_error_->setstate(std::ios::badbit);
|
|
output_stream_error_ = new std::ostringstream();
|
|
output_stream_error_->setstate(std::ios::badbit);
|
|
}
|
|
|
|
|
|
colvarproxy_io::~colvarproxy_io()
|
|
{
|
|
delete input_stream_error_;
|
|
close_input_streams();
|
|
delete output_stream_error_;
|
|
close_output_streams();
|
|
}
|
|
|
|
|
|
bool colvarproxy_io::io_available()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
int colvarproxy_io::get_frame(long int&)
|
|
{
|
|
return COLVARS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
int colvarproxy_io::set_frame(long int)
|
|
{
|
|
return COLVARS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
std::string colvarproxy_io::get_current_work_dir() const
|
|
{
|
|
#ifdef __cpp_lib_filesystem
|
|
|
|
return std::filesystem::current_path().string();
|
|
|
|
#else
|
|
|
|
// Legacy code
|
|
size_t constexpr buf_size = 3001;
|
|
char buf[buf_size];
|
|
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
char *getcwd_result = ::_getcwd(buf, buf_size);
|
|
#else
|
|
char *getcwd_result = ::getcwd(buf, buf_size);
|
|
#endif
|
|
|
|
if (getcwd_result == nullptr) {
|
|
cvm::error("Error: cannot read the current working directory.\n", COLVARS_INPUT_ERROR);
|
|
return std::string("");
|
|
}
|
|
|
|
return std::string(getcwd_result);
|
|
#endif
|
|
}
|
|
|
|
|
|
std::string colvarproxy_io::join_paths(std::string const &path1, std::string const &path2) const
|
|
{
|
|
#ifdef __cpp_lib_filesystem
|
|
|
|
return (std::filesystem::path(path1) / std::filesystem::path(path2)).string();
|
|
|
|
#else
|
|
|
|
// Legacy code
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
return (path1 + "\\" + path2);
|
|
#else
|
|
return (path1 + "/" + path2);
|
|
#endif
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
int colvarproxy_io::backup_file(char const *filename)
|
|
{
|
|
// Simplified version of NAMD_file_exists()
|
|
int exit_code;
|
|
do {
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
// We could use _access_s here, but it is probably too new
|
|
exit_code = _access(filename, 00);
|
|
#else
|
|
exit_code = access(filename, F_OK);
|
|
#endif
|
|
} while ((exit_code != 0) && (errno == EINTR));
|
|
if (exit_code != 0) {
|
|
if (errno == ENOENT) {
|
|
// File does not exist
|
|
return COLVARS_OK;
|
|
} else {
|
|
return cvm::error("Unknown error while checking if file \""+
|
|
std::string(filename)+"\" exists.\n", COLVARS_ERROR);
|
|
}
|
|
}
|
|
|
|
// The file exists, then rename it
|
|
if (std::string(filename).rfind(std::string(".colvars.state")) !=
|
|
std::string::npos) {
|
|
return rename_file(filename, (std::string(filename)+".old").c_str());
|
|
} else {
|
|
return rename_file(filename, (std::string(filename)+".BAK").c_str());
|
|
}
|
|
}
|
|
|
|
|
|
int colvarproxy_io::remove_file(char const *filename)
|
|
{
|
|
int error_code = COLVARS_OK;
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
// Because the file may be open by other processes, rename it to filename.old
|
|
std::string const renamed_file(std::string(filename)+".old");
|
|
// It may still be there from an interrupted run, so remove it to be safe
|
|
std::remove(renamed_file.c_str());
|
|
int rename_exit_code = 0;
|
|
while ((rename_exit_code = std::rename(filename,
|
|
renamed_file.c_str())) != 0) {
|
|
if (errno == EINTR) continue;
|
|
error_code |= COLVARS_FILE_ERROR;
|
|
break;
|
|
}
|
|
// Ask to remove filename.old, but ignore any errors raised
|
|
std::remove(renamed_file.c_str());
|
|
#else
|
|
if (std::remove(filename)) {
|
|
if (errno != ENOENT) {
|
|
error_code |= COLVARS_FILE_ERROR;
|
|
}
|
|
}
|
|
#endif
|
|
if (error_code != COLVARS_OK) {
|
|
return cvm::error("Error: in removing file \""+std::string(filename)+
|
|
"\".\n.",
|
|
error_code);
|
|
}
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
|
|
int colvarproxy_io::rename_file(char const *filename, char const *newfilename)
|
|
{
|
|
int error_code = COLVARS_OK;
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
// On straight Windows, must remove the destination before renaming it
|
|
if (_access(newfilename, 00) == 0) {
|
|
error_code |= remove_file(newfilename);
|
|
}
|
|
#endif
|
|
int rename_exit_code = 0;
|
|
while ((rename_exit_code = std::rename(filename, newfilename)) != 0) {
|
|
if (errno == EINTR) continue;
|
|
// Call log() instead of error to allow the next try
|
|
cvm::log("Error: in renaming file \""+std::string(filename)+"\" to \""+
|
|
std::string(newfilename)+"\".\n.");
|
|
error_code |= COLVARS_FILE_ERROR;
|
|
if (errno == EXDEV) continue;
|
|
break;
|
|
}
|
|
return rename_exit_code ? error_code : COLVARS_OK;
|
|
}
|
|
|
|
|
|
int colvarproxy_io::set_input_prefix(std::string const &prefix)
|
|
{
|
|
// set input restart name and strip the extension, if present
|
|
input_prefix_str = prefix;
|
|
if (input_prefix_str.rfind(".colvars.state") != std::string::npos) {
|
|
input_prefix_str.erase(input_prefix_str.rfind(".colvars.state"),
|
|
std::string(".colvars.state").size());
|
|
}
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
|
|
int colvarproxy_io::set_output_prefix(std::string const &prefix)
|
|
{
|
|
// set input restart name and strip the extension, if present
|
|
output_prefix_str = prefix;
|
|
if (output_prefix_str.rfind(".colvars.state") != std::string::npos) {
|
|
output_prefix_str.erase(output_prefix_str.rfind(".colvars.state"),
|
|
std::string(".colvars.state").size());
|
|
}
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
|
|
int colvarproxy_io::set_restart_output_prefix(std::string const &prefix)
|
|
{
|
|
// set input restart name and strip the extension, if present
|
|
restart_output_prefix_str = prefix;
|
|
if (restart_output_prefix_str.rfind(".colvars.state") != std::string::npos) {
|
|
restart_output_prefix_str.erase(restart_output_prefix_str.rfind(".colvars.state"),
|
|
std::string(".colvars.state").size());
|
|
}
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
|
|
int colvarproxy_io::set_default_restart_frequency(int freq)
|
|
{
|
|
// TODO check for compatibility with colvarsRestartFrequency
|
|
restart_frequency_engine = freq;
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
|
|
|
|
std::istream &colvarproxy_io::input_stream(std::string const &input_name,
|
|
std::string const description,
|
|
bool error_on_fail)
|
|
{
|
|
if (!io_available()) {
|
|
cvm::error("Error: trying to access an input file/channel "
|
|
"from the wrong thread.\n", COLVARS_BUG_ERROR);
|
|
return *input_stream_error_;
|
|
}
|
|
|
|
if (colvarproxy_io::input_stream_exists(input_name)) {
|
|
std::ifstream *ifs =
|
|
dynamic_cast<std::ifstream *>(input_streams_[input_name]);
|
|
if (ifs && !ifs->is_open()) {
|
|
// This file was opened before, re-open it. Using std::ios::binary to
|
|
// work around differences in line termination conventions
|
|
// See https://github.com/Colvars/colvars/commit/8236879f7de4
|
|
ifs->open(input_name.c_str(), std::ios::binary);
|
|
}
|
|
} else {
|
|
input_streams_[input_name] = new std::ifstream(input_name.c_str(),
|
|
std::ios::binary);
|
|
}
|
|
|
|
if (input_streams_[input_name]->fail() && error_on_fail) {
|
|
cvm::error("Error: cannot open "+description+" \""+input_name+"\".\n",
|
|
COLVARS_FILE_ERROR);
|
|
}
|
|
|
|
return *(input_streams_[input_name]);
|
|
}
|
|
|
|
|
|
std::istream &
|
|
colvarproxy_io::input_stream_from_string(std::string const &input_name,
|
|
std::string const &content,
|
|
std::string const description)
|
|
{
|
|
if (!io_available()) {
|
|
cvm::error("Error: trying to access an input file/channel "
|
|
"from the wrong thread.\n", COLVARS_BUG_ERROR);
|
|
return *input_stream_error_;
|
|
}
|
|
|
|
if (colvarproxy_io::input_stream_exists(input_name)) {
|
|
|
|
std::istringstream *iss =
|
|
dynamic_cast<std::istringstream *>(input_streams_[input_name]);
|
|
if (iss) {
|
|
// If there is already a stringstream, replace it
|
|
delete iss;
|
|
} else {
|
|
std::ifstream *ifs =
|
|
dynamic_cast<std::ifstream *>(input_streams_[input_name]);
|
|
if (ifs) {
|
|
if (ifs->is_open()) {
|
|
ifs->close();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
input_streams_[input_name] = new std::istringstream(content);
|
|
|
|
return *(input_streams_[input_name]);
|
|
}
|
|
|
|
|
|
bool colvarproxy_io::input_stream_exists(std::string const &input_name)
|
|
{
|
|
return (input_streams_.count(input_name) > 0);
|
|
}
|
|
|
|
|
|
int colvarproxy_io::close_input_stream(std::string const &input_name)
|
|
{
|
|
if (colvarproxy_io::input_stream_exists(input_name)) {
|
|
std::ifstream *ifs = dynamic_cast<std::ifstream *>(input_streams_[input_name]);
|
|
if (ifs) {
|
|
if (ifs->is_open()) {
|
|
ifs->close();
|
|
}
|
|
} else {
|
|
// From a string, just rewind to the begining
|
|
std::istringstream * iss = dynamic_cast<std::istringstream *>(input_streams_[input_name]);
|
|
if (iss) {
|
|
iss->clear();
|
|
iss->seekg(0);
|
|
}
|
|
}
|
|
return COLVARS_OK;
|
|
}
|
|
return cvm::error("Error: input file/channel \""+input_name+
|
|
"\" does not exist.\n", COLVARS_FILE_ERROR);
|
|
}
|
|
|
|
|
|
int colvarproxy_io::delete_input_stream(std::string const &input_name)
|
|
{
|
|
if (colvarproxy_io::close_input_stream(input_name) == COLVARS_OK) {
|
|
std::ifstream *ifs = dynamic_cast<std::ifstream *>(input_streams_[input_name]);
|
|
if (ifs) {
|
|
delete ifs;
|
|
} else {
|
|
std::istringstream * iss = dynamic_cast<std::istringstream *>(input_streams_[input_name]);
|
|
if (iss) {
|
|
delete iss;
|
|
}
|
|
}
|
|
input_streams_.erase(input_name);
|
|
return COLVARS_OK;
|
|
}
|
|
return cvm::error("Error: input file/channel \""+input_name+
|
|
"\" does not exist.\n", COLVARS_FILE_ERROR);
|
|
}
|
|
|
|
|
|
int colvarproxy_io::close_input_streams()
|
|
{
|
|
for (std::map<std::string,
|
|
std::istream *>::iterator ii = input_streams_.begin();
|
|
ii != input_streams_.end();
|
|
ii++) {
|
|
delete ii->second;
|
|
}
|
|
input_streams_.clear();
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
|
|
std::list<std::string> colvarproxy_io::list_input_stream_names() const
|
|
{
|
|
std::list<std::string> result;
|
|
for (std::map<std::string,
|
|
std::istream *>::const_iterator ii = input_streams_.begin();
|
|
ii != input_streams_.end();
|
|
ii++) {
|
|
result.push_back(ii->first);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
std::ostream & colvarproxy_io::output_stream(std::string const &output_name,
|
|
std::string const description)
|
|
{
|
|
if (cvm::debug()) {
|
|
cvm::log("Using colvarproxy_io::output_stream()\n");
|
|
}
|
|
|
|
if (!io_available()) {
|
|
cvm::error("Error: trying to access an output file/channel "
|
|
"from the wrong thread.\n", COLVARS_BUG_ERROR);
|
|
return *output_stream_error_;
|
|
}
|
|
|
|
if (colvarproxy_io::output_stream_exists(output_name)) {
|
|
return *(output_streams_[output_name]);
|
|
}
|
|
|
|
backup_file(output_name.c_str());
|
|
|
|
output_streams_[output_name] = new std::ofstream(output_name.c_str(), std::ios::binary);
|
|
if (!*(output_streams_[output_name])) {
|
|
cvm::error("Error: cannot write to "+description+" \""+output_name+"\".\n",
|
|
COLVARS_FILE_ERROR);
|
|
}
|
|
|
|
return *(output_streams_[output_name]);
|
|
}
|
|
|
|
|
|
bool colvarproxy_io::output_stream_exists(std::string const &output_name)
|
|
{
|
|
return (output_streams_.count(output_name) > 0);
|
|
}
|
|
|
|
|
|
int colvarproxy_io::flush_output_stream(std::string const &output_name)
|
|
{
|
|
if (!io_available()) {
|
|
// No-op
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
if (colvarproxy_io::output_stream_exists(output_name)) {
|
|
(dynamic_cast<std::ofstream *>(output_streams_[output_name]))->flush();
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
|
|
int colvarproxy_io::flush_output_streams()
|
|
{
|
|
if (!io_available()) {
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
for (std::map<std::string, std::ostream *>::iterator osi = output_streams_.begin();
|
|
osi != output_streams_.end();
|
|
osi++) {
|
|
(dynamic_cast<std::ofstream *>(osi->second))->flush();
|
|
}
|
|
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
|
|
int colvarproxy_io::close_output_stream(std::string const &output_name)
|
|
{
|
|
if (!io_available()) {
|
|
return cvm::error("Error: trying to access an output file/channel "
|
|
"from the wrong thread.\n", COLVARS_BUG_ERROR);
|
|
}
|
|
|
|
if (colvarproxy_io::output_stream_exists(output_name)) {
|
|
(dynamic_cast<std::ofstream *>(output_streams_[output_name]))->close();
|
|
delete output_streams_[output_name];
|
|
output_streams_.erase(output_name);
|
|
}
|
|
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
|
|
int colvarproxy_io::close_output_streams()
|
|
{
|
|
if (! io_available()) {
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
for (std::map<std::string, std::ostream *>::iterator osi = output_streams_.begin();
|
|
osi != output_streams_.end();
|
|
osi++) {
|
|
(dynamic_cast<std::ofstream *>(osi->second))->close();
|
|
delete osi->second;
|
|
}
|
|
output_streams_.clear();
|
|
|
|
return COLVARS_OK;
|
|
}
|