AlphaFold3 / weight /_dep /libcifpp /src /utilities.cpp
wuxing0105's picture
Add files using upload-large-folder tool
2e3030d verified
Raw
History Blame Contribute Delete
20.8 kB
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++/utilities.hpp"
#include "revision.hpp"
#include <atomic>
#include <cassert>
#include <cmath>
#include <condition_variable>
#include <cstring>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <mutex>
#include <sstream>
#include <thread>
namespace fs = std::filesystem;
// --------------------------------------------------------------------
namespace cif
{
int VERBOSE = 0;
// --------------------------------------------------------------------
std::string get_version_nr()
{
std::ostringstream s;
write_version_string(s, false);
return s.str();
}
// --------------------------------------------------------------------
#ifdef _WIN32
}
#include <Windows.h>
#include <libloaderapi.h>
#include <wincon.h>
#include <codecvt>
namespace cif
{
uint32_t get_terminal_width()
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
::GetConsoleScreenBufferInfo(::GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
return csbi.srWindow.Right - csbi.srWindow.Left + 1;
}
#else
#include <sys/ioctl.h>
#include <termios.h>
#include <limits.h>
uint32_t get_terminal_width()
{
uint32_t result = 80;
if (isatty(STDOUT_FILENO))
{
struct winsize w;
ioctl(0, TIOCGWINSZ, &w);
result = w.ws_col;
}
return result;
}
#endif
// --------------------------------------------------------------------
struct progress_bar_impl
{
progress_bar_impl(int64_t inMax, const std::string &inAction)
: m_max_value(inMax)
, m_consumed(0)
, m_action(inAction)
, m_message(inAction)
, m_thread(std::bind(&progress_bar_impl::run, this))
{
}
progress_bar_impl(const progress_bar_impl&) = delete;
progress_bar_impl &operator=(const progress_bar_impl &) = delete;
~progress_bar_impl();
void run();
void consumed(int64_t n);
void progress(int64_t p);
void message(const std::string &msg);
void print_progress();
void print_done();
using time_point = std::chrono::time_point<std::chrono::system_clock>;
int64_t m_max_value;
std::atomic<int64_t> m_consumed;
int64_t m_last_consumed = 0;
int m_spinner_index = 0;
std::string m_action, m_message;
std::mutex m_mutex;
std::thread m_thread;
time_point m_start = std::chrono::system_clock::now();
time_point m_last = std::chrono::system_clock::now();
bool m_stop = false;
};
progress_bar_impl::~progress_bar_impl()
{
using namespace std::literals;
assert(m_thread.joinable());
m_stop = true;
m_thread.join();
}
void progress_bar_impl::run()
{
using namespace std::literals;
bool printedAny = false;
try
{
while (not m_stop)
{
auto now = std::chrono::system_clock::now();
if (now - m_start < 2s or now - m_last < 100ms)
{
std::this_thread::sleep_for(10ms);
continue;
}
std::lock_guard lock(m_mutex);
if (not printedAny and isatty(STDOUT_FILENO))
std::cout << "\x1b[?25l";
print_progress();
printedAny = true;
m_last = std::chrono::system_clock::now();
}
}
catch (...)
{
}
if (printedAny)
{
print_done();
if (isatty(STDOUT_FILENO))
std::cout << "\x1b[?25h";
}
}
void progress_bar_impl::consumed(int64_t n)
{
m_consumed += n;
}
void progress_bar_impl::progress(int64_t p)
{
m_consumed = p;
}
void progress_bar_impl::message(const std::string &msg)
{
std::unique_lock lock(m_mutex);
m_message = msg;
}
const char* kSpinner[] = {
// ".", "o", "O", "0", "O", "o", ".", " "
// "⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠"
".", "o", "O", "0", "@", "*", " "
};
const size_t kSpinnerCount = sizeof(kSpinner) / sizeof(char*);
const int kSpinnerTimeInterval = 100;
const uint32_t kMinBarWidth = 40, kMinMsgWidth = 12;
void progress_bar_impl::print_progress()
{
const char *kBlocks[] = {
// "▯", // 0
// "▮", // 1
"=",
"-"
};
uint32_t width = get_terminal_width();
float progress = static_cast<float>(m_consumed) / m_max_value;
if (width < kMinBarWidth)
std::cout << (100 * progress) << '%' << std::endl;
else
{
uint32_t bar_width = 7 * width / 10;
uint32_t pct_width = 7;
uint32_t msg_width = width - bar_width - pct_width - 1;
if (msg_width < kMinMsgWidth)
{
bar_width += kMinMsgWidth - msg_width;
msg_width = kMinMsgWidth;
}
std::ostringstream msg;
if (m_message.length() <= msg_width)
{
msg << m_message;
if (m_message.length() < msg_width)
msg << std::string(msg_width - m_message.length(), ' ');
}
else
msg << m_message.substr(0, msg_width - 3) << "...";
msg << ' ';
uint32_t pi = static_cast<uint32_t>(std::ceil(progress * bar_width));
for (uint32_t i = 0; i < bar_width; ++i)
msg << kBlocks[i > pi ? 1 : 0];
msg << ' ';
msg << std::setw(3) << static_cast<int>(std::ceil(progress * 100)) << "% ";
auto now = std::chrono::system_clock::now();
m_spinner_index = (std::chrono::duration_cast<std::chrono::milliseconds>(now - m_start).count() / kSpinnerTimeInterval) % kSpinnerCount;
msg << kSpinner[m_spinner_index];
std::cout << '\r' << msg.str();
std::cout.flush();
}
}
namespace
{
std::ostream &operator<<(std::ostream &os, const std::chrono::duration<double> &t)
{
uint64_t s = static_cast<uint64_t>(std::trunc(t.count()));
if (s > 24 * 60 * 60)
{
auto days = s / (24 * 60 * 60);
os << days << "d ";
s %= 24 * 60 * 60;
}
if (s > 60 * 60)
{
auto hours = s / (60 * 60);
os << hours << "h ";
s %= 60 * 60;
}
if (s > 60)
{
auto minutes = s / 60;
os << minutes << "m ";
s %= 60;
}
double ss = s + 1e-6 * (t.count() - s);
os << std::fixed << std::setprecision(1) << ss << 's';
return os;
}
} // namespace
void progress_bar_impl::print_done()
{
std::chrono::duration<double> elapsed = std::chrono::system_clock::now() - m_start;
std::ostringstream msgstr;
msgstr << m_action << " done in " << elapsed << " seconds";
auto msg = msgstr.str();
uint32_t width = get_terminal_width();
if (msg.length() < width)
msg += std::string(width - msg.length(), ' ');
std::cout << '\r' << msg << std::endl;
}
progress_bar::progress_bar(int64_t inMax, const std::string &inAction)
: m_impl(nullptr)
{
if (isatty(STDOUT_FILENO) and VERBOSE >= 0)
m_impl = new progress_bar_impl(inMax, inAction);
}
progress_bar::~progress_bar()
{
delete m_impl;
}
void progress_bar::consumed(int64_t inConsumed)
{
if (m_impl != nullptr)
m_impl->consumed(inConsumed);
}
void progress_bar::progress(int64_t inProgress)
{
if (m_impl != nullptr)
m_impl->progress(inProgress);
}
void progress_bar::message(const std::string &inMessage)
{
if (m_impl != nullptr)
m_impl->message(inMessage);
}
} // namespace cif
// --------------------------------------------------------------------
//
// Try to find a named resource. Can be either a local file with this name,
// a file located in our cache directory or a resource linked in with mrc.
//
// We have a special, private version of mrsrc here. To be able to create
// shared libraries and still be able to link when there's no mrc used.
namespace mrsrc
{
/// \brief Internal data structure as generated by mrc
struct rsrc_imp
{
unsigned int m_next;
unsigned int m_child;
unsigned int m_name;
unsigned int m_size;
unsigned int m_data;
};
} // namespace mrsrc
#if _WIN32
#if __MINGW32__
extern "C" __attribute__((weak, alias("gResourceIndexDefault"))) const mrsrc::rsrc_imp gResourceIndex[];
extern "C" __attribute__((weak, alias("gResourceDataDefault"))) const char gResourceData[];
extern "C" __attribute__((weak, alias("gResourceNameDefault"))) const char gResourceName[];
#else
extern "C" const mrsrc::rsrc_imp *gResourceIndexDefault[1] = {};
extern "C" const char *gResourceDataDefault[1] = {};
extern "C" const char *gResourceNameDefault[1] = {};
extern "C" const mrsrc::rsrc_imp gResourceIndex[];
extern "C" const char gResourceData[];
extern "C" const char gResourceName[];
#pragma comment(linker, "/alternatename:gResourceIndex=gResourceIndexDefault")
#pragma comment(linker, "/alternatename:gResourceData=gResourceDataDefault")
#pragma comment(linker, "/alternatename:gResourceName=gResourceNameDefault")
#endif
#else
extern const __attribute__((weak)) mrsrc::rsrc_imp gResourceIndex[];
extern const __attribute__((weak)) char gResourceData[];
extern const __attribute__((weak)) char gResourceName[];
const mrsrc::rsrc_imp gResourceIndex[1] = {};
const char gResourceData[1] = {};
const char gResourceName[1] = {};
#endif
namespace mrsrc
{
class rsrc_data
{
public:
static rsrc_data &instance()
{
static rsrc_data s_instance;
return s_instance;
}
const rsrc_imp *index() const { return m_index; }
const char *data(unsigned int offset) const
{
return m_data + offset;
}
const char *name(unsigned int offset) const
{
return m_name + offset;
}
private:
rsrc_data()
{
// if (gResourceIndex and (gResourceIndex[0].m_child > 0 or gResourceIndex[0].m_size > 0) and gResourceIndex and gResourceName)
if (gResourceIndex[0].m_child > 0 or gResourceIndex[0].m_size > 0)
{
m_index = gResourceIndex;
m_data = gResourceData;
m_name = gResourceName;
}
}
rsrc_imp m_dummy = {};
const rsrc_imp *m_index = &m_dummy;
const char *m_data = "";
const char *m_name = "";
};
/// \brief Class mrsrc::rsrc contains a pointer to the data in the
/// resource, as well as offering an iterator interface to its
/// children.
class rsrc
{
public:
rsrc()
: m_impl(rsrc_data::instance().index())
{
}
rsrc(const rsrc &other)
: m_impl(other.m_impl)
{
}
rsrc &operator=(const rsrc &other)
{
m_impl = other.m_impl;
return *this;
}
rsrc(std::filesystem::path path);
std::string name() const { return rsrc_data::instance().name(m_impl->m_name); }
const char *data() const { return rsrc_data::instance().data(m_impl->m_data); }
unsigned long size() const { return m_impl->m_size; }
explicit operator bool() const { return m_impl != NULL and m_impl->m_size > 0; }
template <typename RSRC>
class iterator_t
{
public:
using iterator_category = std::input_iterator_tag;
using value_type = RSRC;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
iterator_t(const rsrc_imp *cur)
: m_cur(cur)
{
}
iterator_t(const iterator_t &i)
: m_cur(i.m_cur)
{
}
iterator_t &operator=(const iterator_t &i)
{
m_cur = i.m_cur;
return *this;
}
reference operator*() { return m_cur; }
pointer operator->() { return &m_cur; }
iterator_t &operator++()
{
if (m_cur.m_impl->m_next)
m_cur.m_impl = rsrc_data::instance().index() + m_cur.m_impl->m_next;
else
m_cur.m_impl = nullptr;
return *this;
}
iterator_t operator++(int)
{
auto tmp(*this);
this->operator++();
return tmp;
}
bool operator==(const iterator_t &rhs) const { return m_cur.m_impl == rhs.m_cur.m_impl; }
bool operator!=(const iterator_t &rhs) const { return m_cur.m_impl != rhs.m_cur.m_impl; }
private:
value_type m_cur;
};
using iterator = iterator_t<rsrc>;
iterator begin() const
{
const rsrc_imp *impl = nullptr;
if (m_impl and m_impl->m_child)
impl = rsrc_data::instance().index() + m_impl->m_child;
return iterator(impl);
}
iterator end() const
{
return iterator(nullptr);
}
private:
rsrc(const rsrc_imp *imp)
: m_impl(imp)
{
}
const rsrc_imp *m_impl;
};
inline rsrc::rsrc(std::filesystem::path p)
{
m_impl = rsrc_data::instance().index();
// using std::filesytem::path would have been natural here of course...
auto pb = p.begin();
auto pe = p.end();
while (m_impl != nullptr and pb != pe)
{
auto name = *pb++;
const rsrc_imp *impl = nullptr;
for (rsrc child : *this)
{
if (child.name() == name)
{
impl = child.m_impl;
break;
}
}
m_impl = impl;
}
if (pb != pe) // not found
m_impl = nullptr;
}
// --------------------------------------------------------------------
template <typename CharT, typename Traits>
class basic_streambuf : public std::basic_streambuf<CharT, Traits>
{
public:
typedef CharT char_type;
typedef Traits traits_type;
typedef typename traits_type::int_type int_type;
typedef typename traits_type::pos_type pos_type;
typedef typename traits_type::off_type off_type;
/// \brief constructor taking a \a path to the resource in memory
basic_streambuf(const std::string &path)
: m_rsrc(path)
{
init();
}
/// \brief constructor taking a \a rsrc
basic_streambuf(const rsrc &rsrc)
: m_rsrc(rsrc)
{
init();
}
basic_streambuf(const basic_streambuf &) = delete;
basic_streambuf(basic_streambuf &&rhs)
: basic_streambuf(rhs.m_rsrc)
{
}
basic_streambuf &operator=(const basic_streambuf &) = delete;
basic_streambuf &operator=(basic_streambuf &&rhs)
{
swap(rhs);
return *this;
}
void swap(basic_streambuf &rhs)
{
std::swap(m_begin, rhs.m_begin);
std::swap(m_end, rhs.m_end);
std::swap(m_current, rhs.m_current);
}
private:
void init()
{
m_begin = reinterpret_cast<const char_type *>(m_rsrc.data());
m_end = reinterpret_cast<const char_type *>(m_rsrc.data() + m_rsrc.size());
m_current = m_begin;
}
int_type underflow()
{
if (m_current == m_end)
return traits_type::eof();
return traits_type::to_int_type(*m_current);
}
int_type uflow()
{
if (m_current == m_end)
return traits_type::eof();
return traits_type::to_int_type(*m_current++);
}
int_type pbackfail(int_type ch)
{
if (m_current == m_begin or (ch != traits_type::eof() and ch != m_current[-1]))
return traits_type::eof();
return traits_type::to_int_type(*--m_current);
}
std::streamsize showmanyc()
{
assert(std::less_equal<const char *>()(m_current, m_end));
return m_end - m_current;
}
pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which)
{
switch (dir)
{
case std::ios_base::beg:
m_current = m_begin + off;
break;
case std::ios_base::end:
m_current = m_end + off;
break;
case std::ios_base::cur:
m_current += off;
break;
default:
break;
}
if (m_current < m_begin)
m_current = m_begin;
if (m_current > m_end)
m_current = m_end;
return m_current - m_begin;
}
pos_type seekpos(pos_type pos, std::ios_base::openmode which)
{
m_current = m_begin + pos;
if (m_current < m_begin)
m_current = m_begin;
if (m_current > m_end)
m_current = m_end;
return m_current - m_begin;
}
private:
rsrc m_rsrc;
const char_type *m_begin;
const char_type *m_end;
const char_type *m_current;
};
using streambuf = basic_streambuf<char, std::char_traits<char>>;
// --------------------------------------------------------------------
// class mrsrc::istream
template <typename CharT, typename Traits>
class basic_istream : public std::basic_istream<CharT, Traits>
{
public:
typedef CharT char_type;
typedef Traits traits_type;
typedef typename traits_type::int_type int_type;
typedef typename traits_type::pos_type pos_type;
typedef typename traits_type::off_type off_type;
private:
using __streambuf_type = basic_streambuf<CharT, Traits>;
using __istream_type = std::basic_istream<CharT, Traits>;
__streambuf_type m_buffer;
public:
basic_istream(const std::string &path)
: __istream_type(&m_buffer)
, m_buffer(path)
{
this->init(&m_buffer);
}
basic_istream(rsrc &resource)
: __istream_type(&m_buffer)
, m_buffer(resource)
{
this->init(&m_buffer);
}
basic_istream(const basic_istream &) = delete;
basic_istream(basic_istream &&rhs)
: __istream_type(std::move(rhs))
, m_buffer(std::move(rhs.m_buffer))
{
__istream_type::set_rdbuf(&m_buffer);
}
basic_istream &operator=(const basic_istream &) = delete;
basic_istream &operator=(basic_istream &&rhs)
{
__istream_type::operator=(std::move(rhs));
m_buffer = std::move(rhs.m_buffer);
return *this;
}
void swap(basic_istream &rhs)
{
__istream_type::swap(rhs);
m_buffer.swap(rhs.m_buffer);
}
__streambuf_type *rdbuf() const
{
return const_cast<__streambuf_type *>(&m_buffer);
}
};
using istream = basic_istream<char, std::char_traits<char>>;
} // namespace mrsrc
// --------------------------------------------------------------------
namespace cif
{
// --------------------------------------------------------------------
class resource_pool
{
public:
static resource_pool &instance()
{
static std::unique_ptr<resource_pool> s_instance(new resource_pool);
return *s_instance;
}
void pushDir(fs::path dir)
{
std::error_code ec;
if (fs::exists(dir, ec) and not ec)
mDirs.push_front(dir);
}
void pushDir(const char *path)
{
if (path != nullptr)
pushDir(fs::path(path));
}
void pushAlias(const std::string &name, std::filesystem::path dataFile)
{
std::error_code ec;
if (not fs::exists(dataFile, ec) or ec)
throw std::runtime_error("Attempt to add a file resource for " + name + " that cannot be used (" + dataFile.string() + ") :" + ec.message());
mLocalResources[name] = dataFile;
}
std::unique_ptr<std::istream> load(fs::path name);
const auto data_directories() { return mDirs; }
const auto file_resources() { return mLocalResources; }
private:
resource_pool();
std::unique_ptr<std::ifstream> open(fs::path &p)
{
std::unique_ptr<std::ifstream> result;
try
{
if (fs::exists(p))
{
std::unique_ptr<std::ifstream> file(new std::ifstream(p, std::ios::binary));
if (file->is_open())
result.reset(file.release());
}
}
catch (...)
{
}
return result;
}
std::map<std::string, std::filesystem::path> mLocalResources;
std::deque<fs::path> mDirs;
};
resource_pool::resource_pool()
{
#if defined(DATA_DIR)
pushDir(DATA_DIR);
#endif
pushDir(getenv("LIBCIFPP_DATA_DIR"));
auto ccp4 = getenv("CCP4");
if (ccp4 != nullptr)
pushDir(fs::path(ccp4) / "share" / "libcifpp");
#if defined(CACHE_DIR)
pushDir(CACHE_DIR);
#endif
}
std::unique_ptr<std::istream> resource_pool::load(fs::path name)
{
std::unique_ptr<std::istream> result;
std::error_code ec;
fs::path p = name;
if (mLocalResources.count(name.string()))
result = open(mLocalResources[name.string()]);
if (fs::exists(p, ec) and not ec)
result = open(p);
for (auto di = mDirs.begin(); not result and di != mDirs.end(); ++di)
{
auto p2 = *di / p;
if (fs::exists(p2, ec) and not ec)
result = open(p2);
}
// if (not result and gResourceData)
if (not result and (gResourceIndex[0].m_child > 0 or gResourceIndex[0].m_size > 0))
{
mrsrc::rsrc rsrc(name);
if (rsrc)
result.reset(new mrsrc::istream(rsrc));
}
return result;
}
// --------------------------------------------------------------------
void add_data_directory(std::filesystem::path dataDir)
{
resource_pool::instance().pushDir(dataDir);
}
void add_file_resource(const std::string &name, std::filesystem::path dataFile)
{
resource_pool::instance().pushAlias(name, dataFile);
}
std::unique_ptr<std::istream> load_resource(std::filesystem::path name)
{
return resource_pool::instance().load(name);
}
void list_file_resources(std::ostream &os)
{
auto &file_resources = resource_pool::instance().file_resources();
if (not file_resources.empty())
{
os << "\nThe following named resources were loaded:\n";
for (const auto &[name, path] : file_resources)
os << name << " -> " << std::quoted(path.string()) << '\n';
}
}
void list_data_directories(std::ostream &os)
{
for (auto &p : resource_pool::instance().data_directories())
os << p << '\n';
}
} // namespace cif