Loading...
Searching...
No Matches
eonc::io Namespace Reference

Classes

struct  ConMetadataValue
struct  ConMetadataText

Enumerations

enum class  IoStatus : std::uint8_t {
  Ok = 0 , ReadError = 1 , WriteError = 2 , AppendError = 3 ,
  OpenError = 4 , InvalidArgument = 5
}
 Structured I/O result for the client surface (nanobind-friendly). More...

Functions

readcon::ConFrame matterToConFrame (Matter &m, const ConFrameMetadata *metadata=nullptr)
 Build a single stamped ConFrame from Matter (same builder as matter2con).
void set_write_con_forces (bool enabled) noexcept
 Whether written .con frames carry "Forces of Component" sections.
bool write_con_forces () noexcept
ConFrameMetadata metadata_from_frame (const readcon::ConFrame &frame)
 Extract known frame-level fields from a parsed readcon frame.
std::pair< std::array< double, 3 >, std::array< double, 3 > > cell_to_lengths_angles (const Matter &m)
void resetConAppendState ()
 Drop the per-path bookkeeping that lets append-mode writes skip re-parsing frames this process wrote.
IoStatus matter2con (Matter &m, std::string filename, bool append=false, const ConFrameMetadata *metadata=nullptr)
 Append a frame to a .con, or truncate and write one frame.
IoStatus con2matter (Matter &m, std::string filename)
IoStatus con2matter (Matter &m, const readcon::ConFrame &frame, ConFrameMetadata *out_metadata)
IoStatus matter2convel (Matter &m, std::string filename)
IoStatus convel2matter (Matter &m, std::string filename)
IoStatus matter2xyz (Matter &m, std::string filename, bool append=false)
 Write one extended-XYZ frame: Lattice= cell and 17-digit coordinates.
IoStatus writeTibble (Matter &m, std::string filename)
 Debug table (positions, optional cached forces). Not a structure format.
std::vector< readcon::ConFrame > buildNebPathFrames (const std::vector< std::shared_ptr< Matter > > &path, const std::vector< ConFrameMetadata > &metadata_per_image)
 Build NEB band ConFrames without writing (clone builder path of writeNebPath).
IoStatus writeConFrames (std::string filename, const std::vector< readcon::ConFrame > &frames)
 Write already-built ConFrames to a multi-frame .con (temp or durable).
IoStatus writeNebPath (std::string filename, const std::vector< std::shared_ptr< Matter > > &path, const std::vector< ConFrameMetadata > &metadata_per_image)
 Write a full NEB path as one multi-frame .con using ConFrameBuilder::clone().
constexpr bool io_ok (IoStatus s) noexcept
constexpr const char * io_status_name (IoStatus s) noexcept
 Human-readable label for logging / bindings.

Enumeration Type Documentation

◆ IoStatus

enum class eonc::io::IoStatus : std::uint8_t
strong

Structured I/O result for the client surface (nanobind-friendly).

Prefer comparing to IoStatus::Ok rather than treating as bool.

Enumerator
Ok 
ReadError 
WriteError 
AppendError 
OpenError 
InvalidArgument 

Definition at line 29 of file ConFileIO.h.

29 : std::uint8_t {
30 Ok = 0,
31 ReadError = 1,
32 WriteError = 2,
33 AppendError = 3,
34 OpenError = 4,
36};

Function Documentation

◆ buildNebPathFrames()

std::vector< readcon::ConFrame > eonc::io::buildNebPathFrames ( const std::vector< std::shared_ptr< Matter > > & path,
const std::vector< ConFrameMetadata > & metadata_per_image )
nodiscard

Build NEB band ConFrames without writing (clone builder path of writeNebPath).

Empty vector on invalid input.

Parameters
pathlength must equal metadata_per_image (endpoints included)

Definition at line 872 of file ConFileIO.cpp.

873 {
874 std::vector<readcon::ConFrame> frames;
875 if (path.empty() || path.size() != metadata_per_image.size()) {
877 "buildNebPathFrames: path/metadata size mismatch (path={}, meta={})",
878 path.size(), metadata_per_image.size());
879 return frames;
880 }
881 for (const auto &img : path) {
882 if (!img) {
883 EONC_LOG_ERROR("buildNebPathFrames: null Matter in path");
884 return {};
885 }
886 }
887
888 Matter &template_m = *path.front();
890
891 std::vector<uint64_t> atom_ids;
892 std::array<std::string, 2> prebox;
893 std::array<std::string, 2> postbox;
894 collect_ids_headers(template_m, atom_ids, prebox, postbox);
895
896 frames.reserve(path.size());
897 try {
898 auto seed = seed_builder(template_m, prebox, postbox, atom_ids);
899 for (size_t i = 0; i < path.size(); ++i) {
900 Matter &img = *path[i];
902 if (img.numberOfAtoms() != template_m.numberOfAtoms()) {
904 "buildNebPathFrames: image {} atom count {} != template {}", i,
905 img.numberOfAtoms(), template_m.numberOfAtoms());
906 return {};
907 }
908 auto builder = seed.clone();
909 apply_frame_metadata(builder, &metadata_per_image[i]);
910 apply_geometry(builder, img, /*with_velocities=*/false,
911 &metadata_per_image[i]);
912 frames.push_back(builder.build());
913 }
914 } catch (const std::exception &e) {
915 EONC_LOG_ERROR("buildNebPathFrames failed: {}", e.what());
916 return {};
917 }
918 return frames;
919}
#define EONC_LOG_ERROR(...)
Definition EonLogger.h:262
void applyPeriodicBoundaryIfEnabled()
Apply MIC wrap when periodic boundaries are enabled (I/O path).
Definition Matter.h:327
long int numberOfAtoms() const
Definition Matter.cpp:209

◆ cell_to_lengths_angles()

std::pair< std::array< double, 3 >, std::array< double, 3 > > eonc::io::cell_to_lengths_angles ( const Matter & m)

Definition at line 515 of file ConFileIO.cpp.

515 {
516 const Matrix3d cell = m.getCell();
517 std::array<double, 3> lengths;
518 lengths[0] = cell.row(0).norm();
519 lengths[1] = cell.row(1).norm();
520 lengths[2] = cell.row(2).norm();
521 // CON header line 4 is alpha beta gamma:
522 // alpha = angle(b,c), beta = angle(a,c), gamma = angle(a,b).
523 std::array<double, 3> angles;
525 cell.row(1).dot(cell.row(2)), lengths[1] * lengths[2])) *
526 180.0 / eonc::helpers::pi;
528 cell.row(0).dot(cell.row(2)), lengths[0] * lengths[2])) *
529 180.0 / eonc::helpers::pi;
531 cell.row(0).dot(cell.row(1)), lengths[0] * lengths[1])) *
532 180.0 / eonc::helpers::pi;
533 return {lengths, angles};
534}
Eigen::Matrix< double, 3, 3, eOnStorageOrder > Matrix3d
Definition Eigen.h:35
Matrix3d getCell() const
Definition Matter.cpp:211
constexpr double pi
constexpr double safe_div(double num, double denom, double fallback=0.0)
Definition SafeMath.h:21
double safe_acos(double x)
Definition SafeMath.h:34

◆ con2matter() [1/2]

IoStatus eonc::io::con2matter ( Matter & m,
const readcon::ConFrame & frame,
ConFrameMetadata * out_metadata )
nodiscard

Definition at line 615 of file ConFileIO.cpp.

616 {
617 try {
618 const auto &atoms = frame.atoms();
619 const auto &lengths = frame.cell();
620 const auto &angles_deg = frame.angles();
621 const auto &prebox = frame.prebox_header();
622 const auto &postbox = frame.postbox_header();
623
624 m.headerCon[0] = prebox[0] + "\n";
625 m.headerCon[1] = prebox[1] + "\n";
626 m.headerCon[3] = postbox[0] + "\n";
627 m.headerCon[4] = postbox[1] + "\n";
628
629 double angles[3] = {angles_deg[0], angles_deg[1], angles_deg[2]};
630 if (angles[0] == 90.0 && angles[1] == 90.0 && angles[2] == 90.0) {
631 Matrix3d cell = Matrix3d::Zero();
632 cell(0, 0) = lengths[0];
633 cell(1, 1) = lengths[1];
634 cell(2, 2) = lengths[2];
635 m.setCell(cell);
636 } else {
637 angles[0] *= eonc::helpers::pi / 180.0;
638 angles[1] *= eonc::helpers::pi / 180.0;
639 angles[2] *= eonc::helpers::pi / 180.0;
640 const double alpha = angles[0];
641 const double beta = angles[1];
642 const double gamma = angles[2];
643
644 Matrix3d cell = Matrix3d::Zero();
645 cell(0, 0) = 1.0;
646 cell(1, 0) = cos(gamma);
647 cell(1, 1) = sin(gamma);
648 cell(2, 0) = cos(beta);
649 cell(2, 1) = (cos(alpha) - cell(1, 0) * cell(2, 0)) / cell(1, 1);
650 cell(2, 2) = eonc::safemath::safe_sqrt(1.0 - pow(cell(2, 0), 2) -
651 pow(cell(2, 1), 2));
652
653 cell(0, 0) *= lengths[0];
654 cell(1, 0) *= lengths[1];
655 cell(1, 1) *= lengths[1];
656 cell(2, 0) *= lengths[2];
657 cell(2, 1) *= lengths[2];
658 cell(2, 2) *= lengths[2];
659 m.setCell(cell);
660 }
661 m.headerCon[2] =
662 std::format("{} {} {}\n", angles_deg[0], angles_deg[1], angles_deg[2]);
663
664 const auto n = static_cast<Eigen::Index>(atoms.size());
665 m.resize(static_cast<long>(atoms.size()));
666
667 // Undo the species grouping the .con format imposes, so an index into
668 // Matter addresses the same atom as the matching row of mode.dat.
669 const std::vector<size_t> order = matter_order(atoms);
670
671 AtomMatrix positions = AtomMatrix::Zero(n, 3);
672 AtomMatrix forces = AtomMatrix::Zero(n, 3);
673 AtomMatrix velocities = AtomMatrix::Zero(n, 3);
674 VectorXd masses = VectorXd::Zero(n);
675 VectorXi atomic_nrs = VectorXi::Zero(n);
676 bool any_force = false;
677 bool any_velocity = false;
678
679 for (Eigen::Index i = 0; i < n; ++i) {
680 const auto &atom = atoms[order[static_cast<size_t>(i)]];
681 positions(i, 0) = atom.x;
682 positions(i, 1) = atom.y;
683 positions(i, 2) = atom.z;
684 masses(i) = atom.mass;
685 atomic_nrs(i) = static_cast<int>(atom.atomic_number);
686 const auto fixed = atom.fixed_mask();
687 m.setFixedMask(static_cast<long>(i), {fixed[0], fixed[1], fixed[2]});
688 m.setAtomIndex(static_cast<long>(i),
689 static_cast<std::int64_t>(atom.atom_id));
690
691 if (auto vel = atom.velocity()) {
692 any_velocity = true;
693 velocities(i, 0) = (*vel)[0];
694 velocities(i, 1) = (*vel)[1];
695 velocities(i, 2) = (*vel)[2];
696 }
697 if (auto force = atom.force()) {
698 any_force = true;
699 forces(i, 0) = (*force)[0];
700 forces(i, 1) = (*force)[1];
701 forces(i, 2) = (*force)[2];
702 }
703 }
704
705 m.setMasses(masses);
706 m.setAtomicNrs(atomic_nrs);
707 m.setPositions(positions);
708
709 if (any_velocity || frame.has_velocities()) {
710 m.setVelocities(velocities);
711 }
712
713 const auto meta = metadata_from_frame(frame);
714 if (out_metadata != nullptr) {
715 *out_metadata = meta;
716 }
717
718 // Trust file energy+forces only when both are present. Energy-only must not
719 // mark the pot clean with a zero force matrix (optimizer footgun). Prefer
720 // writing raw forces (friend) so fixed-atom components survive RT; then
721 // mark clean without setComputedPotential's net-force adjustment on zeros.
722 const bool has_force_section = any_force || frame.has_forces();
723 if (meta.energy && has_force_section) {
724 m.forces = forces;
725 m.potentialEnergy = *meta.energy;
726 m.energyVariance = 0.0;
727 m.recomputePotential = false;
728 m.recomputeMaskedForces = true;
729 } else if (has_force_section) {
730 m.forces = forces;
731 m.recomputePotential = true;
732 m.recomputeMaskedForces = true;
733 } else {
734 // Classic geometry-only files: always recompute pot (main-era behavior).
735 m.recomputePotential = true;
736 }
737
738 // setPositions already applied PBC when enabled; no second wrap here.
739 return IoStatus::Ok;
740 } catch (const std::exception &e) {
741 EONC_LOG_ERROR("Failed to convert frame to matter: {}", e.what());
742 return IoStatus::ReadError;
743 }
744}
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
double potentialEnergy
Definition Matter.h:386
void setAtomicNrs(const VectorXi &atmnrs)
Definition Matter.cpp:571
void setMasses(const VectorXd &massesIn)
Definition Matter.cpp:386
bool recomputeMaskedForces
Definition Matter.h:381
void setVelocities(const AtomMatrix &v)
Definition Matter.cpp:604
void setAtomIndex(long int atom, std::int64_t index)
Definition Matter.h:303
void resize(long int nAtoms)
Definition Matter.cpp:173
AtomMatrix forces
Definition Matter.h:369
bool recomputePotential
Definition Matter.h:348
void setCell(const Matrix3d &newCell)
Definition Matter.cpp:213
std::array< std::string, 5 > headerCon
Definition Matter.h:354
void setFixedMask(long int atom, std::array< bool, 3 > mask)
Definition Matter.cpp:433
double energyVariance
Definition Matter.h:384
void setPositions(const AtomMatrix &pos)
Definition Matter.cpp:273
ConFrameMetadata metadata_from_frame(const readcon::ConFrame &frame)
Extract known frame-level fields from a parsed readcon frame.
double safe_sqrt(double x)
Definition SafeMath.h:38

◆ con2matter() [2/2]

IoStatus eonc::io::con2matter ( Matter & m,
std::string filename )
nodiscard

Definition at line 604 of file ConFileIO.cpp.

604 {
605 filename = ensure_extension(std::move(filename), ".con");
606 try {
607 auto frame = readcon::read_first_frame(filename);
608 return con2matter(m, frame, nullptr);
609 } catch (const std::exception &e) {
610 EONC_LOG_ERROR("Failed to read {}: {}", filename, e.what());
611 return IoStatus::ReadError;
612 }
613}
IoStatus con2matter(Matter &m, std::string filename)

◆ convel2matter()

IoStatus eonc::io::convel2matter ( Matter & m,
std::string filename )
nodiscard

Definition at line 762 of file ConFileIO.cpp.

762 {
763 filename = ensure_extension(std::move(filename), ".convel");
764 try {
765 auto frame = readcon::read_first_frame(filename);
766 return con2matter(m, frame, nullptr);
767 } catch (const std::exception &e) {
768 EONC_LOG_ERROR("Failed to read convel {}: {}", filename, e.what());
769 return IoStatus::ReadError;
770 }
771}

◆ io_ok()

bool eonc::io::io_ok ( IoStatus s)
nodiscardconstexprnoexcept

Definition at line 38 of file ConFileIO.h.

38 {
39 return s == IoStatus::Ok;
40}

◆ io_status_name()

const char * eonc::io::io_status_name ( IoStatus s)
nodiscardconstexprnoexcept

Human-readable label for logging / bindings.

Definition at line 43 of file ConFileIO.h.

43 {
44 switch (s) {
45 case IoStatus::Ok:
46 return "ok";
48 return "read_error";
50 return "write_error";
52 return "append_error";
54 return "open_error";
56 return "invalid_argument";
57 }
58 return "unknown";
59}

◆ matter2con()

IoStatus eonc::io::matter2con ( Matter & m,
std::string filename,
bool append = false,
const ConFrameMetadata * metadata = nullptr )
nodiscard

Append a frame to a .con, or truncate and write one frame.

With append, an uncompressed target is extended in place: the new frame is serialized on its own and concatenated, so N appends cost N frame writes rather than N(N+1)/2. Earlier frames are never re-serialized, and the file is flushed before the call returns, so every intermediate state on disk is a complete multi-frame .con.

A target that eOn did not write, or that changed size or mtime since eOn last wrote it, is parsed once before anything is added; an unparseable target yields IoStatus::AppendError with its bytes untouched. Gzip and zstd targets keep the read-all-and-rewrite path because a compressed member cannot be extended in place.

Definition at line 541 of file ConFileIO.cpp.

542 {
543 filename = ensure_extension(std::move(filename), ".con");
544
546
547 const fs::path path(filename);
548 const auto compression =
549 readcon::ConFrameWriter::compression_from_extension(path);
550 const bool streamable =
551 compression == readcon::ConFrameWriter::Compression::None;
552
553 // The lock covers the stamp table and the read-then-extend window on one
554 // path. Nothing under it evaluates a potential: frame_from_matter reads
555 // cached energy and forces only while the pot is clean.
556 const std::lock_guard<std::mutex> guard(append_mutex());
557 const auto key = append_key(path);
558 const bool exists = fs::exists(path);
559 const bool concatenate = append && exists && streamable;
560
561 // ConFrame is move-only (no default ctor), so frames carries the new frame
562 // either way. A gzip or zstd target cannot be concatenated: readcon-core
563 // reads a single gzip member, so appended members would be invisible
564 // on read-back, and the streaming writers cannot be flushed frame by frame.
565 // Those targets keep the whole-file rewrite, which stays O(N) per append.
566 std::vector<readcon::ConFrame> frames;
567 if (append && exists && !streamable) {
568 try {
569 // Prefer read_all_frames (single ownership hand-off) over the iterator
570 // path for append rewrite; simpler lifetime for ASAN/CI envs.
571 frames = readcon::read_all_frames(path);
572 } catch (const std::exception &e) {
573 EONC_LOG_ERROR("Failed to append to {}: {}", filename, e.what());
575 }
576 } else if (concatenate && !tail_is_ours(key, path)) {
577 // Refuse to extend a file eOn cannot parse, matching the rewrite path:
578 // the target keeps its bytes and the caller sees AppendError. Frames this
579 // process wrote and nobody touched since need no such check.
580 try {
581 [[maybe_unused]] const auto existing = readcon::read_all_frames(path);
582 } catch (const std::exception &e) {
583 EONC_LOG_ERROR("Failed to append to {}: {}", filename, e.what());
585 }
586 }
587 try {
588 frames.push_back(frame_from_matter(m, metadata, /*with_velocities=*/false));
589 } catch (const std::exception &e) {
590 EONC_LOG_ERROR("Failed to build frame for {}: {}", filename, e.what());
592 }
593
594 const auto status = concatenate ? append_frames(path, frames, kConPrecision)
595 : write_frames(path, frames, kConPrecision);
596 if (io_ok(status)) {
597 remember_stamp(key, path);
598 } else {
599 append_stamps().erase(key);
600 }
601 return status;
602}
constexpr bool io_ok(IoStatus s) noexcept
Definition ConFileIO.h:38

◆ matter2convel()

IoStatus eonc::io::matter2convel ( Matter & m,
std::string filename )
nodiscard

Definition at line 746 of file ConFileIO.cpp.

746 {
747 filename = ensure_extension(std::move(filename), ".convel");
748
750
751 try {
752 auto frame = frame_from_matter(m, nullptr, /*with_velocities=*/true);
753 std::vector<readcon::ConFrame> frames;
754 frames.push_back(std::move(frame));
755 return write_frames(filename, frames, kConvelPrecision);
756 } catch (const std::exception &e) {
757 EONC_LOG_ERROR("Failed to write convel {}: {}", filename, e.what());
759 }
760}

◆ matter2xyz()

IoStatus eonc::io::matter2xyz ( Matter & m,
std::string filename,
bool append = false )
nodiscard

Write one extended-XYZ frame: Lattice= cell and 17-digit coordinates.

append concatenates another frame. A target whose last frame has a different atom count yields InvalidArgument and is left unchanged. Chemfiles in readcon is ingress (XYZ/PDB/GRO into ConFrame), not XYZ egress, so this writer emits the Lattice= comment ASE and chemfiles already read.

Definition at line 773 of file ConFileIO.cpp.

773 {
774 filename = ensure_extension(std::move(filename), ".xyz");
775
777 const long n = m.numberOfAtoms();
778
779 if (append && fs::exists(filename)) {
780 std::error_code ec;
781 const auto sz = fs::file_size(filename, ec);
782 if (!ec && sz > 0) {
783 const auto prev = last_xyz_natoms(filename);
784 if (!prev) {
785 EONC_LOG_ERROR("matter2xyz: cannot parse existing {}", filename);
787 }
788 if (*prev != n) {
790 "matter2xyz: append atom count {} != last frame {} in {}", n, *prev,
791 filename);
793 }
794 }
795 }
796
797 std::ofstream out;
798 out.open(filename,
799 append ? (std::ios::out | std::ios::app | std::ios::binary)
800 : (std::ios::out | std::ios::trunc | std::ios::binary));
801 if (!out) {
802 EONC_LOG_ERROR("matter2xyz: cannot open {}", filename);
803 return IoStatus::OpenError;
804 }
805
806 const Matrix3d cell = m.getCell();
807 out << std::format(
808 "{}\nLattice=\"{:.17g} {:.17g} {:.17g} {:.17g} {:.17g} {:.17g} "
809 "{:.17g} {:.17g} {:.17g}\" Properties=species:S:1:pos:R:3 Generated "
810 "by eOn\n",
811 n, cell(0, 0), cell(0, 1), cell(0, 2), cell(1, 0), cell(1, 1), cell(1, 2),
812 cell(2, 0), cell(2, 1), cell(2, 2));
813 const AtomMatrix pos = m.getPositions();
814 for (long i = 0; i < n; ++i) {
815 out << std::format("{}\t{:.17g}\t{:.17g}\t{:.17g}\n",
816 symbol_for_z(m.getAtomicNr(i)), pos(i, 0), pos(i, 1),
817 pos(i, 2));
818 }
819 // Close before testing: the destructor's flush is where a full disk or a
820 // short write surfaces, and by then the state is gone.
821 out.close();
822 if (!out) {
823 EONC_LOG_ERROR("matter2xyz: failed to write {}", filename);
825 }
826 return IoStatus::Ok;
827}
long getAtomicNr(long int atom) const
Definition Matter.cpp:392
const AtomMatrix & getPositions() const
Definition Matter.cpp:236

◆ matterToConFrame()

readcon::ConFrame eonc::io::matterToConFrame ( Matter & m,
const ConFrameMetadata * metadata = nullptr )
nodiscard

Build a single stamped ConFrame from Matter (same builder as matter2con).

Does not write to disk.

Definition at line 485 of file ConFileIO.cpp.

486 {
487 return frame_from_matter(m, metadata, /*with_velocities=*/false);
488}

◆ metadata_from_frame()

ConFrameMetadata eonc::io::metadata_from_frame ( const readcon::ConFrame & frame)

Extract known frame-level fields from a parsed readcon frame.

Definition at line 498 of file ConFileIO.cpp.

498 {
499 ConFrameMetadata meta;
500 meta.energy = frame.energy_opt();
501 meta.frame_index = frame.frame_index_opt();
502 meta.time = frame.time_opt();
503 meta.timestep = frame.timestep_opt();
504 meta.neb_bead = frame.neb_bead_opt();
505 meta.neb_band = frame.neb_band_opt();
506 meta.potential_type = frame.potential_type();
507 const auto json = frame.metadata_json();
508 if (!json.empty() && json != "{}") {
509 meta.raw_json = json;
510 }
511 return meta;
512}
nlohmann::json json
std::optional< uint64_t > frame_index
Definition ConFileIO.h:72
std::optional< uint64_t > neb_band
Definition ConFileIO.h:77
std::optional< double > energy
Definition ConFileIO.h:73
std::optional< double > timestep
Definition ConFileIO.h:75
std::optional< double > time
Definition ConFileIO.h:74
std::optional< std::string > raw_json
Definition ConFileIO.h:81
std::optional< std::string > potential_type
Definition ConFileIO.h:78
std::optional< uint64_t > neb_bead
Definition ConFileIO.h:76

◆ resetConAppendState()

void eonc::io::resetConAppendState ( )

Drop the per-path bookkeeping that lets append-mode writes skip re-parsing frames this process wrote.

Appends already re-parse a target whose size or mtime moved, so this only matters when a file is replaced with different content of the same size inside one filesystem timestamp tick.

Definition at line 536 of file ConFileIO.cpp.

536 {
537 const std::lock_guard<std::mutex> guard(append_mutex());
538 append_stamps().clear();
539}

◆ set_write_con_forces()

void eonc::io::set_write_con_forces ( bool enabled)
noexcept

Whether written .con frames carry "Forces of Component" sections.

Force sections enable force+energy co-loading on re-read (warm NEB restarts without re-evaluating the potential) but are not part of the classic con layout: external readers such as ASE's eon parser reject frames that carry them. Off unless [Main] write_con_forces enables it. A ConFrameMetadata.write_con_forces value, then the Matter Parameters field, override this process-wide flag for a single write. Reading force-bearing frames is always supported regardless of this flag.

Definition at line 490 of file ConFileIO.cpp.

490 {
491 g_write_con_forces.store(enabled, std::memory_order_relaxed);
492}

◆ write_con_forces()

bool eonc::io::write_con_forces ( )
nodiscardnoexcept

Definition at line 494 of file ConFileIO.cpp.

494 {
495 return g_write_con_forces.load(std::memory_order_relaxed);
496}

◆ writeConFrames()

IoStatus eonc::io::writeConFrames ( std::string filename,
const std::vector< readcon::ConFrame > & frames )
nodiscard

Write already-built ConFrames to a multi-frame .con (temp or durable).

Definition at line 921 of file ConFileIO.cpp.

922 {
923 if (frames.empty()) {
925 }
926 filename = ensure_extension(std::move(filename), ".con");
927 const fs::path path(filename);
928 const std::lock_guard<std::mutex> guard(append_mutex());
929 const auto key = append_key(path);
930 const auto status = write_frames(path, frames, kConPrecision);
931 if (io_ok(status)) {
932 remember_stamp(key, path);
933 } else {
934 append_stamps().erase(key);
935 }
936 return status;
937}

◆ writeNebPath()

IoStatus eonc::io::writeNebPath ( std::string filename,
const std::vector< std::shared_ptr< Matter > > & path,
const std::vector< ConFrameMetadata > & metadata_per_image )
nodiscard

Write a full NEB path as one multi-frame .con using ConFrameBuilder::clone().

Seeds identity (symbols/fixed/mass/id/cell headers) from path[0] only, then for each image clones that template and bulk-updates positions/forces + metadata. Images must share atom count and topology with path[0] (standard NEB band invariant); heterogeneous multi-frame movies should not use this. Avoids re-reading the output file per image (legacy append path).

Parameters
pathlength must be numImages+2 (endpoints included)
metadata_per_imagelength must equal path.size()

Definition at line 939 of file ConFileIO.cpp.

941 {
942 auto frames = buildNebPathFrames(path, metadata_per_image);
943 if (frames.empty()) {
945 }
946 return writeConFrames(std::move(filename), frames);
947}
IoStatus writeConFrames(std::string filename, const std::vector< readcon::ConFrame > &frames)
Write already-built ConFrames to a multi-frame .con (temp or durable).
std::vector< readcon::ConFrame > buildNebPathFrames(const std::vector< std::shared_ptr< Matter > > &path, const std::vector< ConFrameMetadata > &metadata_per_image)
Build NEB band ConFrames without writing (clone builder path of writeNebPath).

◆ writeTibble()

IoStatus eonc::io::writeTibble ( Matter & m,
std::string fname )
nodiscard

Debug table (positions, optional cached forces). Not a structure format.

Definition at line 829 of file ConFileIO.cpp.

829 {
830 // Debug table, not a structure format. getForces()/getPotentialEnergy()
831 // run computePotential() on a dirty pot, which would charge a dump to
832 // the force-call count in results.dat. Cached values only; the header
833 // drops the columns it cannot fill. atmID is the CON column-5 id.
834 const bool have_forces = !m.needsForceUpdate();
835 if (!have_forces) {
837 "writeTibble: pot is dirty, {} omits the force and energy columns",
838 fname);
839 }
840 const AtomMatrix pos = m.getPositions();
841 std::ofstream out(fname);
842 if (!out) {
843 EONC_LOG_ERROR("writeTibble: cannot open {}", fname);
844 return IoStatus::OpenError;
845 }
846 out << (have_forces ? "x y z fx fy fz energy mass symbol atmID fixed\n"
847 : "x y z mass symbol atmID fixed\n");
848 const AtomMatrix fSys = have_forces ? m.getForces() : AtomMatrix();
849 const double eSys = have_forces ? m.getPotentialEnergy() : 0.0;
850 for (long idx = 0; idx < m.numberOfAtoms(); ++idx) {
851 out << std::format("{} {} {}", pos(idx, 0), pos(idx, 1), pos(idx, 2));
852 if (have_forces) {
853 out << std::format(" {} {} {} {}", fSys(idx, 0), fSys(idx, 1),
854 fSys(idx, 2), eSys);
855 }
856 const auto mask = m.getFixedMask(idx);
857 const int fixed_bits =
858 (mask[0] ? 1 : 0) | (mask[1] ? 2 : 0) | (mask[2] ? 4 : 0);
859 out << std::format(" {} {} {} {}\n", m.getMass(idx),
860 symbol_for_z(m.getAtomicNr(idx)), m.getAtomIndex(idx),
861 fixed_bits);
862 }
863 out.close();
864 if (!out) {
865 EONC_LOG_ERROR("writeTibble: failed to write {}", fname);
867 }
868 return IoStatus::Ok;
869}
#define EONC_LOG_WARNING(...)
Definition EonLogger.h:256
double getPotentialEnergy() const
Definition Matter.cpp:446
const AtomMatrix & getForces() const
Definition Matter.cpp:324
bool needsForceUpdate() const
Whether forces need recomputation (positions changed since last eval).
Definition Matter.h:232
std::array< bool, 3 > getFixedMask(long int atom) const
Per-axis CON column-4 mask (bit0=x, bit1=y, bit2=z).
Definition Matter.cpp:413
std::int64_t getAtomIndex(long int atom) const
.con column-5 index (pre-grouping); public for I/O / bindings.
Definition Matter.h:300
double getMass(long int atom) const
Definition Matter.cpp:380