#ifndef READCON_PLUS_PLUS_H
#define READCON_PLUS_PLUS_H
#pragma once
#if defined(__cplusplus) && __cplusplus < 201703L
#error "readcon-core.hpp requires C++17 or later"
#endif
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <filesystem>
#include <iterator>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>
#include "readcon-core.h"
namespace readcon {
struct Atom {
uint64_t atomic_number;
double x;
double y;
double z;
uint64_t atom_id;
double mass;
[[deprecated("Use fixed_x/fixed_y/fixed_z or fixed_mask() instead")]]
bool is_fixed;
bool fixed_x;
bool fixed_y;
bool fixed_z;
[[deprecated("Use velocity() (returns std::optional) instead")]]
double vx;
[[deprecated("Use velocity() (returns std::optional) instead")]]
double vy;
[[deprecated("Use velocity() (returns std::optional) instead")]]
double vz;
[[deprecated("Use velocity() (returns std::optional) instead")]]
bool has_velocity;
[[deprecated("Use force() (returns std::optional) instead")]]
double fx;
[[deprecated("Use force() (returns std::optional) instead")]]
double fy;
[[deprecated("Use force() (returns std::optional) instead")]]
double fz;
[[deprecated("Use force() (returns std::optional) instead")]]
bool has_forces;
double energy;
bool has_energy;
std::array<bool, 3> fixed_mask() const {
return {fixed_x, fixed_y, fixed_z};
}
std::optional<std::array<double, 3>> velocity() const {
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
if (has_velocity)
return std::array<double, 3>{vx, vy, vz};
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
return std::nullopt;
}
std::optional<std::array<double, 3>> force() const {
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
if (has_forces)
return std::array<double, 3>{fx, fy, fz};
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
return std::nullopt;
}
std::optional<double> energy_value() const {
if (has_energy)
return energy;
return std::nullopt;
}
};
class ConFrame;
class ConFrameWriter;
class ConFrameBuilder;
class SelectionResult;
struct Bond {
uint32_t i = 0;
uint32_t j = 0;
std::optional<int32_t> order;
};
class ConFrameIterator {
public:
class Iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = ConFrame;
using difference_type = std::ptrdiff_t;
using pointer = ConFrame *;
using reference = ConFrame &;
reference operator*();
pointer operator->();
Iterator &operator++();
bool operator==(const Iterator &other) const;
bool operator!=(const Iterator &other) const;
private:
friend class ConFrameIterator;
explicit Iterator(CConFrameIterator *iterator_ptr);
void fetch_next_frame();
CConFrameIterator *iterator_ptr_ = nullptr;
std::unique_ptr<ConFrame> current_frame_;
};
explicit ConFrameIterator(const std::filesystem::path &path);
Iterator begin();
Iterator end();
private:
struct IteratorDeleter {
void operator()(CConFrameIterator *ptr) const {
if (ptr) {
free_con_frame_iterator(ptr);
}
}
};
std::unique_ptr<CConFrameIterator, IteratorDeleter> iterator_ptr_;
};
class ConFrame {
public:
friend class ConFrameIterator::Iterator;
friend class ConFrameWriter;
friend class ConFrameBuilder;
friend ConFrame read_first_frame(const std::filesystem::path &);
friend std::vector<ConFrame> read_all_frames(const std::filesystem::path &);
ConFrame(const ConFrame &) = delete;
ConFrame &operator=(const ConFrame &) = delete;
ConFrame(ConFrame &&) = default;
ConFrame &operator=(ConFrame &&) = default;
const std::array<double, 3> &cell() const;
const std::array<double, 3> &angles() const;
const std::vector<Atom> &atoms() const;
const std::array<std::string, 2> &prebox_header() const;
const std::array<std::string, 2> &postbox_header() const;
bool has_velocities() const;
bool has_forces() const;
bool has_energies() const;
std::optional<size_t> atom_index_by_id(uint64_t atom_id) const;
uint32_t spec_version() const;
std::string metadata_json() const;
double energy() const;
uint64_t frame_index() const;
double time() const;
double timestep() const;
uint64_t neb_bead() const;
uint64_t neb_band() const;
std::optional<double> energy_opt() const;
std::optional<uint64_t> frame_index_opt() const;
std::optional<double> time_opt() const;
std::optional<double> timestep_opt() const;
std::optional<uint64_t> neb_bead_opt() const;
std::optional<uint64_t> neb_band_opt() const;
std::optional<std::string> potential_type() const;
std::vector<Bond> bonds() const;
bool has_bonds() const;
SelectionResult select(std::string_view selection) const;
std::vector<size_t> select_atom_indices(std::string_view selection) const;
const RKRConFrame *get_handle() const { return frame_handle_.get(); }
private:
struct FrameDeleter {
void operator()(RKRConFrame *ptr) const {
if (ptr)
free_rkr_frame(ptr);
}
};
explicit ConFrame(RKRConFrame *frame_handle);
std::unique_ptr<RKRConFrame, FrameDeleter> frame_handle_;
void cache_data() const;
mutable bool is_cached_ = false;
mutable std::vector<Atom> atoms_cache_;
mutable std::array<double, 3> cell_cache_;
mutable std::array<double, 3> angles_cache_;
mutable std::array<std::string, 2> prebox_header_cache_;
mutable std::array<std::string, 2> postbox_header_cache_;
mutable bool has_velocities_cache_ = false;
mutable bool has_forces_cache_ = false;
mutable bool has_energies_cache_ = false;
};
struct SelectionMatch {
uint32_t size = 0;
std::array<uint64_t, 4> atoms{{UINT64_MAX, UINT64_MAX, UINT64_MAX, UINT64_MAX}};
};
class SelectionResult {
public:
SelectionResult() = default;
SelectionResult(const SelectionResult &) = delete;
SelectionResult &operator=(const SelectionResult &) = delete;
SelectionResult(SelectionResult &&) = default;
SelectionResult &operator=(SelectionResult &&) = default;
explicit SelectionResult(RKRSelectionResult *handle) : handle_(handle) {}
uint64_t match_count() const {
return handle_ ? rkr_selection_result_match_count(handle_.get()) : 0;
}
uint32_t context_size() const {
return handle_ ? rkr_selection_result_context_size(handle_.get()) : 0;
}
SelectionMatch match_at(uint64_t index) const {
SelectionMatch m;
if (!handle_) {
throw std::runtime_error("SelectionResult: null handle");
}
uint32_t size = 0;
RKRStatus st = rkr_selection_result_match_at(handle_.get(), index, m.atoms.data(), &size);
if (st != RKR_STATUS_SUCCESS) {
throw std::runtime_error(std::string("rkr_selection_result_match_at: ") +
rkr_status_message(st));
}
m.size = size;
return m;
}
std::vector<uint64_t> primary_indices() const {
if (!handle_) {
return {};
}
const uint64_t n = match_count();
std::vector<uint64_t> out(static_cast<size_t>(n));
if (n == 0) {
return out;
}
uint64_t written = 0;
RKRStatus st =
rkr_selection_result_primary_indices(handle_.get(), out.data(), n, &written);
if (st != RKR_STATUS_SUCCESS) {
throw std::runtime_error(std::string("rkr_selection_result_primary_indices: ") +
rkr_status_message(st));
}
out.resize(static_cast<size_t>(written));
return out;
}
const RKRSelectionResult *get_handle() const { return handle_.get(); }
private:
struct Deleter {
void operator()(RKRSelectionResult *p) const {
if (p)
rkr_selection_result_free(p);
}
};
std::unique_ptr<RKRSelectionResult, Deleter> handle_;
};
inline bool has_chemfiles_support() { return rkr_has_chemfiles_support() != 0; }
inline SelectionResult ConFrame::select(std::string_view selection) const {
if (!has_chemfiles_support()) {
throw std::runtime_error(
"readcon::ConFrame::select requires a chemfiles-enabled readcon_core build");
}
std::string sel(selection);
RKRSelectionResult *raw = nullptr;
RKRStatus st = rkr_frame_select(frame_handle_.get(), sel.c_str(), &raw);
if (st != RKR_STATUS_SUCCESS) {
throw std::runtime_error(std::string("rkr_frame_select: ") + rkr_status_message(st));
}
return SelectionResult(raw);
}
inline std::vector<size_t> ConFrame::select_atom_indices(std::string_view selection) const {
SelectionResult res = select(selection);
if (res.context_size() != 1) {
throw std::runtime_error("select_atom_indices requires an atom-context selection");
}
auto prim = res.primary_indices();
std::vector<size_t> out;
out.reserve(prim.size());
for (auto i : prim) {
out.push_back(static_cast<size_t>(i));
}
std::sort(out.begin(), out.end());
out.erase(std::unique(out.begin(), out.end()), out.end());
return out;
}
class ConFrameWriter {
public:
enum class Compression { None, Gzip, Zstd };
explicit ConFrameWriter(const std::filesystem::path &path,
uint8_t precision = 6);
explicit ConFrameWriter(const std::filesystem::path &path,
Compression compression, uint8_t precision = 6);
static Compression compression_from_extension(
const std::filesystem::path &path);
void extend(const std::vector<ConFrame> &frames);
private:
struct WriterDeleter {
void operator()(RKRConFrameWriter *ptr) const {
if (ptr)
free_rkr_writer(ptr);
}
};
std::unique_ptr<RKRConFrameWriter, WriterDeleter> writer_handle_;
};
class ConFrameBuilder {
public:
ConFrameBuilder(const std::array<double, 3> &cell,
const std::array<double, 3> &angles,
const std::array<std::string, 2> &prebox = {"", ""},
const std::array<std::string, 2> &postbox = {"", ""});
~ConFrameBuilder();
ConFrameBuilder(const ConFrameBuilder &) = delete;
ConFrameBuilder &operator=(const ConFrameBuilder &) = delete;
ConFrameBuilder(ConFrameBuilder &&other) noexcept;
ConFrameBuilder &operator=(ConFrameBuilder &&other) noexcept;
[[nodiscard]] ConFrameBuilder clone() const;
ConFrameBuilder &set_metadata_json(const std::string &metadata_json);
ConFrameBuilder &set_scalar_metadata(const std::string &key, double value);
ConFrameBuilder &set_string_metadata(const std::string &key,
const std::string &value);
ConFrameBuilder &set_energy(double energy);
ConFrameBuilder &set_frame_index(uint64_t idx);
ConFrameBuilder &set_time(double time);
ConFrameBuilder &set_timestep(double dt);
ConFrameBuilder &set_neb_bead(uint64_t bead);
ConFrameBuilder &set_neb_band(uint64_t band);
ConFrameBuilder &add_atom(
const std::string &symbol, double x, double y, double z,
const std::array<bool, 3> &fixed, uint64_t atom_id, double mass,
std::optional<std::array<double, 3>> velocity = std::nullopt,
std::optional<std::array<double, 3>> force = std::nullopt);
ConFrameBuilder &add_atom(
const std::string &symbol, double x, double y, double z, bool is_fixed,
uint64_t atom_id, double mass,
std::optional<std::array<double, 3>> velocity = std::nullopt,
std::optional<std::array<double, 3>> force = std::nullopt);
ConFrameBuilder &with_velocity(const std::array<double, 3> &v);
ConFrameBuilder &with_force(const std::array<double, 3> &f);
ConFrameBuilder &with_energy(double energy);
[[nodiscard]] size_t atom_count() const;
ConFrameBuilder &set_atom_position(size_t i, double x, double y, double z);
ConFrameBuilder &set_atom_velocity(size_t i,
const std::array<double, 3> &v);
ConFrameBuilder &set_atom_force(size_t i, const std::array<double, 3> &f);
ConFrameBuilder &set_atom_energy(size_t i, double energy);
ConFrameBuilder &set_atom_fixed(size_t i, const std::array<bool, 3> &fixed);
ConFrameBuilder &set_atom_mass(size_t i, double mass);
ConFrameBuilder &set_atom_id(size_t i, uint64_t atom_id);
ConFrameBuilder &clear_atom_velocity(size_t i);
ConFrameBuilder &clear_atom_force(size_t i);
ConFrameBuilder &clear_atom_energy(size_t i);
ConFrameBuilder &set_positions_from_flat(const std::vector<double> &positions);
ConFrameBuilder &set_forces_from_flat(const std::vector<double> &forces);
ConFrameBuilder &set_atom_energies_from_flat(const std::vector<double> &energies);
[[nodiscard]] std::array<double, 3> get_atom_position(size_t i) const;
[[nodiscard]] std::optional<std::array<double, 3>>
get_atom_velocity(size_t i) const;
[[nodiscard]] std::optional<std::array<double, 3>>
get_atom_force(size_t i) const;
[[nodiscard]] std::optional<double> get_atom_energy(size_t i) const;
[[nodiscard]] double get_atom_mass(size_t i) const;
[[nodiscard]] double *positions_data() noexcept;
[[nodiscard]] double *velocities_data() noexcept;
[[nodiscard]] double *forces_data() noexcept;
[[nodiscard]] double *atom_energies_data() noexcept;
[[nodiscard]] double *masses_data() noexcept;
[[nodiscard]] const uint64_t *atom_ids_data() const noexcept;
ConFrame build();
private:
RKRConFrameBuilder *builder_handle_ = nullptr;
};
inline std::string status_message(RKRStatus status) {
const char *message = rkr_status_message(status);
return message ? std::string(message) : std::string("unknown status");
}
inline uint64_t symbol_to_z(const std::string &symbol) {
return rkr_symbol_to_z(symbol.c_str());
}
inline std::string z_to_symbol(uint64_t z) {
const char *symbol = rkr_z_to_symbol(z);
return symbol ? std::string(symbol) : std::string("X");
}
inline void throw_on_error(RKRStatus status, const std::string &operation) {
if (status != RKRStatus::RKR_STATUS_SUCCESS) {
throw std::runtime_error(operation + ": " + status_message(status));
}
}
inline ConFrame read_first_frame(const std::filesystem::path &path) {
RKRConFrame *handle = rkr_read_first_frame(path.string().c_str());
if (!handle) {
throw std::runtime_error("Failed to read first frame from: " +
path.string());
}
return ConFrame(handle);
}
inline std::vector<ConFrame> read_all_frames(const std::filesystem::path &path) {
size_t num_frames = 0;
RKRConFrame **handles = rkr_read_all_frames(path.string().c_str(), &num_frames);
if (!handles) {
throw std::runtime_error("Failed to read frames from: " +
path.string());
}
std::vector<ConFrame> frames;
frames.reserve(num_frames);
for (size_t i = 0; i < num_frames; ++i) {
frames.emplace_back(ConFrame(handles[i]));
}
for (size_t i = 0; i < num_frames; ++i) {
handles[i] = nullptr;
}
free_rkr_frame_array(handles, num_frames);
return frames;
}
inline ConFrameIterator::ConFrameIterator(const std::filesystem::path &path) {
CConFrameIterator *iter_ptr = read_con_file_iterator(path.string().c_str());
if (!iter_ptr) {
throw std::runtime_error("Failed to open .con file for iteration: " +
path.string());
}
iterator_ptr_.reset(iter_ptr);
}
inline ConFrameIterator::Iterator ConFrameIterator::begin() {
return Iterator(iterator_ptr_.get());
}
inline ConFrameIterator::Iterator ConFrameIterator::end() {
return Iterator(nullptr);
}
inline bool
ConFrameIterator::Iterator::operator==(const Iterator &other) const {
return current_frame_ == other.current_frame_;
}
inline bool
ConFrameIterator::Iterator::operator!=(const Iterator &other) const {
return !(*this == other);
}
inline ConFrameIterator::Iterator::Iterator(CConFrameIterator *iterator_ptr)
: iterator_ptr_(iterator_ptr) {
if (iterator_ptr_)
fetch_next_frame();
}
inline void ConFrameIterator::Iterator::fetch_next_frame() {
RKRConFrame *frame_handle = con_frame_iterator_next(iterator_ptr_);
if (frame_handle) {
current_frame_ = std::unique_ptr<ConFrame>(new ConFrame(frame_handle));
} else {
current_frame_ = nullptr;
}
}
inline ConFrame &ConFrameIterator::Iterator::operator*() {
return *current_frame_;
}
inline ConFrame *ConFrameIterator::Iterator::operator->() {
return current_frame_.get();
}
inline ConFrameIterator::Iterator &ConFrameIterator::Iterator::operator++() {
fetch_next_frame();
return *this;
}
inline ConFrame::ConFrame(RKRConFrame *frame_handle)
: frame_handle_(frame_handle) {}
inline void ConFrame::cache_data() const {
if (is_cached_) {
return;
}
CFrame *c_frame = rkr_frame_to_c_frame(frame_handle_.get());
if (!c_frame) {
throw std::runtime_error(
"Failed to extract CFrame from handle for caching.");
}
cell_cache_ = {c_frame->cell[0], c_frame->cell[1], c_frame->cell[2]};
angles_cache_ = {c_frame->angles[0], c_frame->angles[1],
c_frame->angles[2]};
has_velocities_cache_ = c_frame->has_velocities;
has_forces_cache_ = c_frame->has_forces;
has_energies_cache_ = c_frame->has_energies;
atoms_cache_.reserve(c_frame->num_atoms);
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
for (size_t i = 0; i < c_frame->num_atoms; ++i) {
const CAtom &c_atom = c_frame->atoms[i];
atoms_cache_.emplace_back(
Atom{c_atom.atomic_number, c_atom.x, c_atom.y, c_atom.z,
c_atom.atom_id, c_atom.mass, c_atom.is_fixed,
c_atom.fixed_x, c_atom.fixed_y, c_atom.fixed_z,
c_atom.vx, c_atom.vy, c_atom.vz, c_atom.has_velocity,
c_atom.fx, c_atom.fy, c_atom.fz, c_atom.has_forces,
c_atom.energy, c_atom.has_energy});
}
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
free_c_frame(c_frame);
auto get_and_free_string =
[frame_handle = frame_handle_.get()](bool is_prebox, size_t index) {
char *c_str =
rkr_frame_get_header_line_cpp(frame_handle, is_prebox, index);
if (!c_str) {
return std::string();
}
std::string result(c_str);
rkr_free_string(c_str);
return result;
};
prebox_header_cache_[0] = get_and_free_string(true, 0);
prebox_header_cache_[1] = get_and_free_string(true, 1);
postbox_header_cache_[0] = get_and_free_string(false, 0);
postbox_header_cache_[1] = get_and_free_string(false, 1);
is_cached_ = true;
}
inline const std::array<double, 3> &ConFrame::cell() const {
cache_data();
return cell_cache_;
}
inline const std::array<double, 3> &ConFrame::angles() const {
cache_data();
return angles_cache_;
}
inline const std::vector<Atom> &ConFrame::atoms() const {
cache_data();
return atoms_cache_;
}
inline const std::array<std::string, 2> &ConFrame::prebox_header() const {
cache_data();
return prebox_header_cache_;
}
inline const std::array<std::string, 2> &ConFrame::postbox_header() const {
cache_data();
return postbox_header_cache_;
}
inline bool ConFrame::has_velocities() const {
cache_data();
return has_velocities_cache_;
}
inline bool ConFrame::has_forces() const {
cache_data();
return has_forces_cache_;
}
inline bool ConFrame::has_energies() const {
cache_data();
return has_energies_cache_;
}
inline std::optional<size_t>
ConFrame::atom_index_by_id(uint64_t atom_id) const {
uint64_t idx = rkr_frame_atom_index_by_id(frame_handle_.get(), atom_id);
if (idx == UINT64_MAX) {
return std::nullopt;
}
return static_cast<size_t>(idx);
}
inline uint32_t ConFrame::spec_version() const {
return rkr_frame_spec_version(frame_handle_.get());
}
inline std::string ConFrame::metadata_json() const {
char *json = rkr_frame_metadata_json(frame_handle_.get());
if (!json)
return "";
std::string result(json);
rkr_free_string(json);
return result;
}
inline double ConFrame::energy() const {
return rkr_frame_energy(frame_handle_.get());
}
inline uint64_t ConFrame::frame_index() const {
return rkr_frame_frame_index(frame_handle_.get());
}
inline double ConFrame::time() const {
return rkr_frame_time(frame_handle_.get());
}
inline double ConFrame::timestep() const {
return rkr_frame_timestep(frame_handle_.get());
}
inline uint64_t ConFrame::neb_bead() const {
return rkr_frame_neb_bead(frame_handle_.get());
}
inline uint64_t ConFrame::neb_band() const {
return rkr_frame_neb_band(frame_handle_.get());
}
inline std::optional<double> ConFrame::energy_opt() const {
double v = rkr_frame_energy(frame_handle_.get());
if (std::isnan(v))
return std::nullopt;
return v;
}
inline std::optional<uint64_t> ConFrame::frame_index_opt() const {
uint64_t v = rkr_frame_frame_index(frame_handle_.get());
if (v == UINT64_MAX)
return std::nullopt;
return v;
}
inline std::optional<double> ConFrame::time_opt() const {
double v = rkr_frame_time(frame_handle_.get());
if (std::isnan(v))
return std::nullopt;
return v;
}
inline std::optional<double> ConFrame::timestep_opt() const {
double v = rkr_frame_timestep(frame_handle_.get());
if (std::isnan(v))
return std::nullopt;
return v;
}
inline std::optional<uint64_t> ConFrame::neb_bead_opt() const {
uint64_t v = rkr_frame_neb_bead(frame_handle_.get());
if (v == UINT64_MAX)
return std::nullopt;
return v;
}
inline std::optional<uint64_t> ConFrame::neb_band_opt() const {
uint64_t v = rkr_frame_neb_band(frame_handle_.get());
if (v == UINT64_MAX)
return std::nullopt;
return v;
}
inline std::optional<std::string> ConFrame::potential_type() const {
char *p = rkr_frame_potential_type(frame_handle_.get());
if (!p)
return std::nullopt;
std::string s(p);
rkr_free_string(p);
return s;
}
inline std::vector<Bond> ConFrame::bonds() const {
uint64_t n = rkr_frame_bond_count(frame_handle_.get());
std::vector<Bond> out;
out.reserve(static_cast<size_t>(n));
for (uint64_t idx = 0; idx < n; ++idx) {
uint32_t i = 0, j = 0;
uint8_t has_order = 0;
int32_t order = 0;
RKRStatus st = rkr_frame_bond_at(frame_handle_.get(), idx, &i, &j, &has_order, &order);
if (st != RKR_STATUS_SUCCESS)
throw std::runtime_error(std::string("rkr_frame_bond_at: ") + rkr_status_message(st));
Bond b;
b.i = i;
b.j = j;
if (has_order)
b.order = order;
out.push_back(b);
}
return out;
}
inline bool ConFrame::has_bonds() const {
return rkr_frame_bond_count(frame_handle_.get()) > 0;
}
inline ConFrameWriter::ConFrameWriter(const std::filesystem::path &path,
uint8_t precision) {
if (precision == 6) {
writer_handle_.reset(create_writer_from_path_c(path.string().c_str()));
} else {
writer_handle_.reset(
create_writer_from_path_with_precision_c(path.string().c_str(), precision));
}
if (!writer_handle_) {
throw std::runtime_error("Failed to create writer for file: " +
path.string());
}
}
inline ConFrameWriter::ConFrameWriter(const std::filesystem::path &path,
Compression compression,
uint8_t precision) {
const std::string p = path.string();
RKRConFrameWriter *raw = nullptr;
switch (compression) {
case Compression::None:
raw = (precision == 6)
? create_writer_from_path_c(p.c_str())
: create_writer_from_path_with_precision_c(p.c_str(),
precision);
break;
case Compression::Gzip:
raw = (precision == 6)
? create_writer_gzip_c(p.c_str())
: create_writer_gzip_with_precision_c(p.c_str(), precision);
break;
case Compression::Zstd:
#if defined(READCON_CORE_HAS_ZSTD)
raw = (precision == 6)
? create_writer_zstd_c(p.c_str())
: create_writer_zstd_with_precision_c(p.c_str(), precision);
break;
#else
throw std::runtime_error(
"Zstd compression requires readcon-core built with the zstd "
"feature (READCON_CORE_HAS_ZSTD undefined): " +
p);
#endif
}
writer_handle_.reset(raw);
if (!writer_handle_) {
throw std::runtime_error("Failed to create writer for file: " + p);
}
}
inline ConFrameWriter::Compression
ConFrameWriter::compression_from_extension(
const std::filesystem::path &path) {
const std::filesystem::path ext = path.extension();
if (ext == ".gz") {
return Compression::Gzip;
}
if (ext == ".zst") {
return Compression::Zstd;
}
return Compression::None;
}
inline void ConFrameWriter::extend(const std::vector<ConFrame> &frames) {
if (frames.empty())
return;
std::vector<const RKRConFrame *> handles;
handles.reserve(frames.size());
for (const auto &frame : frames) {
handles.push_back(frame.get_handle());
}
throw_on_error(
rkr_writer_extend(writer_handle_.get(), handles.data(), handles.size()),
"Failed to write multiple frames");
}
inline ConFrameBuilder::ConFrameBuilder(
const std::array<double, 3> &cell, const std::array<double, 3> &angles,
const std::array<std::string, 2> &prebox,
const std::array<std::string, 2> &postbox) {
builder_handle_ =
rkr_frame_new(cell.data(), angles.data(), prebox[0].c_str(),
prebox[1].c_str(), postbox[0].c_str(), postbox[1].c_str());
if (!builder_handle_) {
throw std::runtime_error("Failed to create frame builder.");
}
}
inline ConFrameBuilder::~ConFrameBuilder() {
if (builder_handle_) {
free_rkr_frame_builder(builder_handle_);
}
}
inline ConFrameBuilder::ConFrameBuilder(ConFrameBuilder &&other) noexcept
: builder_handle_(other.builder_handle_) {
other.builder_handle_ = nullptr;
}
inline ConFrameBuilder &
ConFrameBuilder::operator=(ConFrameBuilder &&other) noexcept {
if (this != &other) {
if (builder_handle_) {
free_rkr_frame_builder(builder_handle_);
}
builder_handle_ = other.builder_handle_;
other.builder_handle_ = nullptr;
}
return *this;
}
inline ConFrameBuilder ConFrameBuilder::clone() const {
RKRConFrameBuilder *cloned_handle = rkr_frame_builder_clone(builder_handle_);
if (!cloned_handle) {
throw std::runtime_error("Failed to clone frame builder.");
}
ConFrameBuilder result({0.0, 0.0, 0.0}, {90.0, 90.0, 90.0});
free_rkr_frame_builder(result.builder_handle_);
result.builder_handle_ = cloned_handle;
return result;
}
inline ConFrameBuilder &
ConFrameBuilder::add_atom(const std::string &symbol, double x, double y,
double z, const std::array<bool, 3> &fixed,
uint64_t atom_id, double mass,
std::optional<std::array<double, 3>> velocity,
std::optional<std::array<double, 3>> force) {
const double *vptr = velocity ? velocity->data() : nullptr;
const double *fptr = force ? force->data() : nullptr;
throw_on_error(rkr_frame_add_atom_full(builder_handle_, symbol.c_str(), x,
y, z, fixed[0], fixed[1], fixed[2],
atom_id, mass, vptr, fptr),
"Failed to add atom to frame builder");
return *this;
}
inline ConFrameBuilder &
ConFrameBuilder::add_atom(const std::string &symbol, double x, double y,
double z, bool is_fixed, uint64_t atom_id,
double mass,
std::optional<std::array<double, 3>> velocity,
std::optional<std::array<double, 3>> force) {
return add_atom(symbol, x, y, z,
{is_fixed, is_fixed, is_fixed}, atom_id, mass,
std::move(velocity), std::move(force));
}
inline ConFrameBuilder &
ConFrameBuilder::with_velocity(const std::array<double, 3> &v) {
throw_on_error(
rkr_frame_builder_set_last_velocity(builder_handle_, v.data()),
"Failed to attach velocity to last atom");
return *this;
}
inline ConFrameBuilder &
ConFrameBuilder::with_force(const std::array<double, 3> &f) {
throw_on_error(
rkr_frame_builder_set_last_force(builder_handle_, f.data()),
"Failed to attach force to last atom");
return *this;
}
inline ConFrameBuilder &ConFrameBuilder::with_energy(double energy) {
throw_on_error(
rkr_frame_builder_set_last_energy(builder_handle_, energy),
"Failed to attach per-atom energy to last atom");
return *this;
}
inline size_t ConFrameBuilder::atom_count() const {
return rkr_frame_builder_atom_count(builder_handle_);
}
inline ConFrameBuilder &
ConFrameBuilder::set_atom_position(size_t i, double x, double y, double z) {
throw_on_error(
rkr_frame_builder_set_atom_position(builder_handle_, i, x, y, z),
"Failed to set atom position");
return *this;
}
inline ConFrameBuilder &
ConFrameBuilder::set_atom_velocity(size_t i, const std::array<double, 3> &v) {
throw_on_error(
rkr_frame_builder_set_atom_velocity(builder_handle_, i, v.data()),
"Failed to set atom velocity");
return *this;
}
inline ConFrameBuilder &
ConFrameBuilder::set_atom_force(size_t i, const std::array<double, 3> &f) {
throw_on_error(
rkr_frame_builder_set_atom_force(builder_handle_, i, f.data()),
"Failed to set atom force");
return *this;
}
inline ConFrameBuilder &ConFrameBuilder::set_atom_energy(size_t i,
double energy) {
throw_on_error(
rkr_frame_builder_set_atom_energy(builder_handle_, i, energy),
"Failed to set atom energy");
return *this;
}
inline ConFrameBuilder &
ConFrameBuilder::set_atom_fixed(size_t i, const std::array<bool, 3> &fixed) {
throw_on_error(rkr_frame_builder_set_atom_fixed(
builder_handle_, i, fixed[0], fixed[1], fixed[2]),
"Failed to set atom fixed flags");
return *this;
}
inline ConFrameBuilder &ConFrameBuilder::set_atom_mass(size_t i, double mass) {
throw_on_error(rkr_frame_builder_set_atom_mass(builder_handle_, i, mass),
"Failed to set atom mass");
return *this;
}
inline ConFrameBuilder &
ConFrameBuilder::set_atom_id(size_t i, uint64_t atom_id) {
throw_on_error(
rkr_frame_builder_set_atom_id(builder_handle_, i, atom_id),
"Failed to set atom id");
return *this;
}
inline ConFrameBuilder &ConFrameBuilder::clear_atom_velocity(size_t i) {
throw_on_error(
rkr_frame_builder_clear_atom_velocity(builder_handle_, i),
"Failed to clear atom velocity");
return *this;
}
inline ConFrameBuilder &ConFrameBuilder::clear_atom_force(size_t i) {
throw_on_error(rkr_frame_builder_clear_atom_force(builder_handle_, i),
"Failed to clear atom force");
return *this;
}
inline ConFrameBuilder &ConFrameBuilder::clear_atom_energy(size_t i) {
throw_on_error(rkr_frame_builder_clear_atom_energy(builder_handle_, i),
"Failed to clear atom energy");
return *this;
}
inline ConFrameBuilder &
ConFrameBuilder::set_positions_from_flat(const std::vector<double> &positions) {
throw_on_error(rkr_frame_builder_set_positions_from_flat(
builder_handle_, positions.data(), positions.size()),
"Failed to bulk-set positions");
return *this;
}
inline ConFrameBuilder &
ConFrameBuilder::set_forces_from_flat(const std::vector<double> &forces) {
throw_on_error(rkr_frame_builder_set_forces_from_flat(
builder_handle_, forces.data(), forces.size()),
"Failed to bulk-set forces");
return *this;
}
inline ConFrameBuilder &
ConFrameBuilder::set_atom_energies_from_flat(const std::vector<double> &energies) {
throw_on_error(rkr_frame_builder_set_atom_energies_from_flat(
builder_handle_, energies.data(), energies.size()),
"Failed to bulk-set per-atom energies");
return *this;
}
inline std::array<double, 3>
ConFrameBuilder::get_atom_position(size_t i) const {
std::array<double, 3> xyz{0.0, 0.0, 0.0};
throw_on_error(rkr_frame_builder_get_atom_position(builder_handle_, i,
xyz.data()),
"Failed to read atom position");
return xyz;
}
inline std::optional<std::array<double, 3>>
ConFrameBuilder::get_atom_velocity(size_t i) const {
std::array<double, 3> xyz{0.0, 0.0, 0.0};
bool has_value = false;
throw_on_error(rkr_frame_builder_get_atom_velocity(builder_handle_, i,
xyz.data(), &has_value),
"Failed to read atom velocity");
if (has_value) {
return xyz;
}
return std::nullopt;
}
inline std::optional<std::array<double, 3>>
ConFrameBuilder::get_atom_force(size_t i) const {
std::array<double, 3> xyz{0.0, 0.0, 0.0};
bool has_value = false;
throw_on_error(
rkr_frame_builder_get_atom_force(builder_handle_, i, xyz.data(),
&has_value),
"Failed to read atom force");
if (has_value) {
return xyz;
}
return std::nullopt;
}
inline std::optional<double>
ConFrameBuilder::get_atom_energy(size_t i) const {
double value = 0.0;
bool has_value = false;
throw_on_error(
rkr_frame_builder_get_atom_energy(builder_handle_, i, &value, &has_value),
"Failed to read atom energy");
if (has_value) {
return value;
}
return std::nullopt;
}
inline double ConFrameBuilder::get_atom_mass(size_t i) const {
double m = 0.0;
throw_on_error(rkr_frame_builder_get_atom_mass(builder_handle_, i, &m),
"Failed to read atom mass");
return m;
}
inline double *ConFrameBuilder::positions_data() noexcept {
return rkr_frame_builder_positions_data(builder_handle_);
}
inline double *ConFrameBuilder::velocities_data() noexcept {
return rkr_frame_builder_velocities_data(builder_handle_);
}
inline double *ConFrameBuilder::forces_data() noexcept {
return rkr_frame_builder_forces_data(builder_handle_);
}
inline double *ConFrameBuilder::atom_energies_data() noexcept {
return rkr_frame_builder_atom_energies_data(builder_handle_);
}
inline double *ConFrameBuilder::masses_data() noexcept {
return rkr_frame_builder_masses_data(builder_handle_);
}
inline const uint64_t *ConFrameBuilder::atom_ids_data() const noexcept {
return rkr_frame_builder_atom_ids_data(builder_handle_);
}
inline ConFrameBuilder &
ConFrameBuilder::set_metadata_json(const std::string &metadata_json) {
throw_on_error(rkr_frame_builder_set_metadata_json(builder_handle_,
metadata_json.c_str()),
"Failed to set builder metadata JSON");
return *this;
}
inline ConFrameBuilder &
ConFrameBuilder::set_scalar_metadata(const std::string &key, double value) {
throw_on_error(rkr_frame_builder_set_scalar_metadata(
builder_handle_, key.c_str(), value),
"Failed to set builder scalar metadata");
return *this;
}
inline ConFrameBuilder &
ConFrameBuilder::set_string_metadata(const std::string &key,
const std::string &value) {
throw_on_error(rkr_frame_builder_set_string_metadata(
builder_handle_, key.c_str(), value.c_str()),
"Failed to set builder string metadata");
return *this;
}
inline ConFrameBuilder &ConFrameBuilder::set_energy(double energy) {
throw_on_error(rkr_frame_builder_set_energy(builder_handle_, energy),
"Failed to set builder energy metadata");
return *this;
}
inline ConFrameBuilder &ConFrameBuilder::set_frame_index(uint64_t idx) {
throw_on_error(rkr_frame_builder_set_frame_index(builder_handle_, idx),
"Failed to set builder frame_index metadata");
return *this;
}
inline ConFrameBuilder &ConFrameBuilder::set_time(double time) {
throw_on_error(rkr_frame_builder_set_time(builder_handle_, time),
"Failed to set builder time metadata");
return *this;
}
inline ConFrameBuilder &ConFrameBuilder::set_timestep(double dt) {
throw_on_error(rkr_frame_builder_set_timestep(builder_handle_, dt),
"Failed to set builder timestep metadata");
return *this;
}
inline ConFrameBuilder &ConFrameBuilder::set_neb_bead(uint64_t bead) {
throw_on_error(rkr_frame_builder_set_neb_bead(builder_handle_, bead),
"Failed to set builder neb_bead metadata");
return *this;
}
inline ConFrameBuilder &ConFrameBuilder::set_neb_band(uint64_t band) {
throw_on_error(rkr_frame_builder_set_neb_band(builder_handle_, band),
"Failed to set builder neb_band metadata");
return *this;
}
inline ConFrame ConFrameBuilder::build() {
RKRConFrame *frame = rkr_frame_builder_build(builder_handle_);
builder_handle_ = nullptr; if (!frame) {
throw std::runtime_error("Failed to build frame from builder.");
}
return ConFrame(frame);
}
}
#endif