From a019241b97acd9fb46dd06369694832be99865cc Mon Sep 17 00:00:00 2001 From: Roy Kid Date: Thu, 5 Oct 2023 11:56:46 +0200 Subject: [PATCH 1/6] start to add lmpmol; find no def for # types, can not read mass etc. section in regular way --- include/chemfiles/formats/LAMMPSMol.hpp | 64 +++++ src/formats/LAMMPSMol.cpp | 299 ++++++++++++++++++++++++ 2 files changed, 363 insertions(+) create mode 100644 include/chemfiles/formats/LAMMPSMol.hpp create mode 100644 src/formats/LAMMPSMol.cpp diff --git a/include/chemfiles/formats/LAMMPSMol.hpp b/include/chemfiles/formats/LAMMPSMol.hpp new file mode 100644 index 000000000..32b23044b --- /dev/null +++ b/include/chemfiles/formats/LAMMPSMol.hpp @@ -0,0 +1,64 @@ +// Chemfiles, a modern library for chemistry file reading and writing +// Copyright (C) Guillaume Fraux and contributors -- BSD license + +#ifndef CHEMFILES_FORMAT_LAMMPSMol_HPP +#define CHEMFILES_FORMAT_LAMMPSMol_HPP + +#include +#include +#include + +#include "chemfiles/File.hpp" +#include "chemfiles/Format.hpp" + +#include "chemfiles/external/optional.hpp" + +namespace chemfiles { +class Frame; +class MemoryBuffer; +class FormatMetadata; + +/// LAMMPSMol file format reader and writer. +/// +class LAMMPSMolFormat final: public TextFormat { +public: + LAMMPSMolFormat(std::string path, File::Mode mode, File::Compression compression): + TextFormat(std::move(path), mode, compression), current_section_(HEADER){} + + LAMMPSMolFormat(std::shared_ptr memory, File::Mode mode, File::Compression compression) : + TextFormat(std::move(memory), mode, compression), current_section_(HEADER){} + + void read_next(Frame& frame) override; + void write_next(const Frame& frame) override; + optional forward() override; + +private: + enum section_t { + HEADER, + COORDS, + TYPES, + MOLECULES, + FRAGMENTS, + CHARGES, + DIAMETERS, + MASSES, + BONDS, + ANGLES, + DIHEDRALS, + IMPROPERS, + SPECIAL_BONDS_COUNTS, + SPECIAL_BONDS, + SHAKE_FLAGS, + SHAKE_ATOMS, + NOT_A_SECTION, + IGNORED; + } current_section_; + + +}; + +template<> const FormatMetadata& format_metadata(); + +} // namespace chemfiles + +#endif diff --git a/src/formats/LAMMPSMol.cpp b/src/formats/LAMMPSMol.cpp new file mode 100644 index 000000000..8da3ef5f1 --- /dev/null +++ b/src/formats/LAMMPSMol.cpp @@ -0,0 +1,299 @@ +// Chemfiles, a modern library for chemistry file reading and writing +// Copyright (C) Guillaume Fraux and contributors -- BSD license + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "chemfiles/types.hpp" +#include "chemfiles/parse.hpp" +#include "chemfiles/utils.hpp" +#include "chemfiles/warnings.hpp" +#include "chemfiles/error_fmt.hpp" +#include "chemfiles/string_view.hpp" +#include "chemfiles/unreachable.hpp" +#include "chemfiles/external/optional.hpp" + +#include "chemfiles/File.hpp" +#include "chemfiles/Atom.hpp" +#include "chemfiles/Frame.hpp" +#include "chemfiles/Topology.hpp" +#include "chemfiles/Property.hpp" +#include "chemfiles/UnitCell.hpp" +#include "chemfiles/FormatMetadata.hpp" + +#include "chemfiles/formats/LAMMPSMol.hpp" + +using namespace chemfiles; + +template<> const FormatMetadata& chemfiles::format_metadata() { + static FormatMetadata metadata; + metadata.name = "LAMMPSMol"; + metadata.extension = ".mol"; + metadata.description = "LAMMPSMol molecule template"; + metadata.reference = "http://lammps.sandia.gov/doc/molecule.html"; + + metadata.read = true; + metadata.write = true; + metadata.memory = false; + + metadata.positions = true; + metadata.velocities = true; + metadata.unit_cell = false; + metadata.atoms = true; + metadata.bonds = true; + metadata.residues = false; + return metadata; +} + +void LAMMPSMolFormat::read_next(Frame& frame) { + if (file_.tellpos() != 0) { + throw format_error("LAMMPSMol format can only contain one frame"); + } + + auto comment = file_.readline(); + while (!file_.eof()) { + switch (current_section_) { + case HEADER: + read_header(frame); + break; + case COORDS: + read_coords(frame); + break; + case MASSES: + read_masses(); + break; + case BONDS: + read_bonds(frame); + break; + case ANGLES: + read_angles(frame); + break; + case DIHEDRALS: + read_dihedrals(frame); + break; + case IMPROPERS: + read_impropers(frame); + break; + case TYPES: + read_types(frame); + break; + + } + } + setup_masses(frame); + setup_names(frame); +} + +/// Remove the comment from `line` and return it. +static string_view split_comment(string_view& line); +/// Check if the line is an unused header value +static bool is_unused_header(string_view line); + +string_view split_comment(string_view& line) { + auto position = line.find('#'); + if (position != std::string::npos) { + auto comment = line.substr(position + 1); + line.remove_suffix(line.size() - position); + return comment; + } else { + return ""; + } +} + +bool is_unused_header(string_view line) { + return (line.find("atom types") != std::string::npos) || + (line.find("bond types") != std::string::npos) || + (line.find("angle types") != std::string::npos) || + (line.find("dihedral types") != std::string::npos); +} + +void LAMMPSMolFormat::read_header(Frame& frame) { + assert(current_section_ == HEADER); + + while (!file_.eof()) { + auto line = file_.readline(); + auto content = line; + split_comment(content); + if (content.empty() || is_unused_header(content)) { + // Nothing to do + } else if (content.find("atoms") != std::string::npos) { + natoms_ = read_header_integer(content, "atoms"); + } else if (content.find("bonds") != std::string::npos) { + nbonds_ = read_header_integer(content, "bonds"); + } // else if angles + else { + // End of the header, get the section and break + current_section_ = get_section(line); + assert (current_section_ != NOT_A_SECTION); + break; + } + } +} + +size_t LAMMPSDataFormat::read_header_integer(string_view line, const std::string& context) { + auto splitted = split(line, ' '); + if (splitted.size() < 2) { + throw format_error( + "invalid header value: expected ' {}', got '{}'", context, line + ); + } + return parse(splitted[0]); +} + +static std::unordered_set IGNORED_SECTIONS = { + "Fragments", "Diameters", "Special Bond Count", "Special Bonds", "Shake Flags", "Shake Atoms", "Shake Bond Types" +}; + +LAMMPSMolFormat::section_t LAMMPSDataFormat::get_section(string_view line) { + auto comment = split_comment(line); + auto section = trim(line); + if (section == "Coords") { + return COORDS; + } else if (section == "Bonds") { + return BONDS; + } else if (section == "Types") { + return VELOCITIES; + } else if (section == "Masses") { + return MASSES; + } else if (section == "Charges") { + return CHARGES; + } else if (IGNORED_SECTIONS.find(section) != IGNORED_SECTION.end()) { + return IGNORED; + } + else { + return NOT_A_SECTION; + } +} + +void LAMMPSMolFormat::read_coords(Frame& frame) { + assert(current_section_ == COORDS); + if (natoms_ == 0) { + throw format_error("missing atoms count in header"); + } + + frame.resize(natoms_); + auto positions = frame.positions(); + auto residues = std::unordered_map(); + + size_t n = 0; + while (n < natoms_ && !file._eof()) { + auto line = file_.readline(); + auto comment = split_comment(line); + if (line.empty()) {continue;} + + auto splitted = split(line, ' '); + auto index = parse(splitted[0]); + auto x = parse(splitted[1]); + auto y = parse(splitted[2]); + auto z = parse(splitted[3]); + frame[index] = Atom(""); + positions[index][0] = x; + positions[index][1] = y; + positions[index][2] = z; + + if (!comment.empty()) { + // Read the first string after the comment, and use it as atom name + auto name = split(comment, ' ')[0]; + if (names_.empty()) { + names_.resize(natoms_); + } + names_[data.index] = name.to_string(); + } + + n++; + } + + get_next_section(); +} + +void LAMMPSMolFormat::read_masses() { + assert(current_section_ == MASSES); + if (natom_types_ == 0) { + throw format_error("missing atom types count in header"); + } + size_t n = 0; + auto line = file_.readline(); + split_comment(line); + while (line.size() != 0 && !file_.eof()) { + auto line = file_.readline(); + split_comment(line); + if (line.empty()) {continue;} + + auto splitted = split(line, ' '); + if (splitted.size() != 2) { + throw format_error("bad mass specification '{}'", line); + } + + auto type = splitted[0]; + auto mass = parse(splitted[1]); + masses_.emplace(type.to_string(), mass); + n++; + } + + get_next_section(); +} + +void LAMMPSMolFormat::read_types() { + assert(current_section_ == TYPES); + if (natom_types_ == 0) { + throw format_error("missing atom types count in header"); + } + size_t n = 0; + while (n < natom_types_ && !file_.eof()) { + auto line = file_.readline(); + split_comment(line); + if (line.empty()) {continue;} + + auto splitted = split(line, ' '); + if (splitted.size() != 2) { + throw format_error("bad mass specification '{}'", line); + } + + auto type = splitted[0]; + auto mass = parse(splitted[1]); + masses_.emplace(type.to_string(), mass); + n++; + } + + get_next_section(); +} + +void LAMMPSMolFormat::get_next_section() { + while (!file_.eof()) { + auto line = file_.readline(); + if (!line.empty()) { + auto section = get_section(line); + if (section == NOT_A_SECTION) { + throw format_error("expected section name, got '{}'", line); + } else { + current_section_ = section; + break; + } + } + } +} + +void LAMMPSDataFormat::skip_to_next_section() { + while (!file_.eof()) { + auto line = file_.readline(); + if (!line.empty()) { + auto section = get_section(line); + if (section == NOT_A_SECTION) { + continue; + } else { + current_section_ = section; + break; + } + } + } +} From 808547b570ff2a56a52665fede2a5a5b4b192df8 Mon Sep 17 00:00:00 2001 From: Roy Kid Date: Thu, 5 Oct 2023 15:05:57 +0200 Subject: [PATCH 2/6] complete mol template format roughly; need to consider about type mapping --- include/chemfiles/formats/LAMMPSData.hpp | 2 - include/chemfiles/formats/LAMMPSMol.hpp | 82 +++++++++- src/formats/LAMMPSMol.cpp | 185 +++++++++++++++++++++-- 3 files changed, 251 insertions(+), 18 deletions(-) diff --git a/include/chemfiles/formats/LAMMPSData.hpp b/include/chemfiles/formats/LAMMPSData.hpp index 5905591d6..08797fd6d 100644 --- a/include/chemfiles/formats/LAMMPSData.hpp +++ b/include/chemfiles/formats/LAMMPSData.hpp @@ -219,8 +219,6 @@ class LAMMPSDataFormat final: public TextFormat { size_t natom_types_ = 0; /// Number of bonds in the file size_t nbonds_ = 0; - /// Optional masses, indexed by atomic type - std::unordered_map masses_; /// Optional atomic names, indexed by atomic indexes std::vector names_; }; diff --git a/include/chemfiles/formats/LAMMPSMol.hpp b/include/chemfiles/formats/LAMMPSMol.hpp index 32b23044b..dddc26286 100644 --- a/include/chemfiles/formats/LAMMPSMol.hpp +++ b/include/chemfiles/formats/LAMMPSMol.hpp @@ -18,7 +18,63 @@ class Frame; class MemoryBuffer; class FormatMetadata; -/// LAMMPSMol file format reader and writer. +class DataTypes { +public: + DataTypes(const Topology& topology = Topology()); + + const sorted_set& atoms() const {return atoms_;} + const sorted_set& bonds() const {return bonds_;} + const sorted_set& angles() const {return angles_;} + const sorted_set& dihedrals() const {return dihedrals_;} + const sorted_set& impropers() const {return impropers_;} + + /// Get the atom type number for the given atom. + /// + /// The atom must be in the topology used to construct this `DataTypes` + /// instance. The index numbering starts at zero, and can be used to index + /// the vector backing the `sorted_set` returned by `atoms()`. + size_t atom_type_id(const Atom& atom) const; + + /// Get the bond type number for the bond type i-j. + /// + /// The bond type must be in the topology used to construct this `DataTypes` + /// instance. The index numbering starts at zero, and can be used to index + /// the vector backing the `sorted_set` returned by `bonds()`. + size_t bond_type_id(size_t type_i, size_t type_j) const; + + /// Get the angle type number for the angle type i-j-k. + /// + /// The angle type must be in the topology used to construct this `DataTypes` + /// instance. The index numbering starts at zero, and can be used to index + /// the vector backing the `sorted_set` returned by `angles()`. + size_t angle_type_id(size_t type_i, size_t type_j, size_t type_k) const; + + /// Get the dihedral type number for the dihedral type i-j-k-m. + /// + /// The dihedral type must be in the topology used to construct this + /// `DataTypes` instance. The index numbering starts at zero, and can be + /// used to index the vector backing the `sorted_set` + /// returned by `dihedrals()`. + size_t dihedral_type_id(size_t type_i, size_t type_j, size_t type_k, size_t type_m) const; + + /// Get the improper type number for the improper type i-j-k-m. + /// + /// The improper type must be in the topology used to construct this + /// `DataTypes` instance. The index numbering starts at zero, and can be + /// used to index the vector backing the `sorted_set` + /// returned by `impropers()`. + size_t improper_type_id(size_t type_i, size_t type_j, size_t type_k, size_t type_m) const; + +private: + sorted_set atoms_; + sorted_set bonds_; + sorted_set angles_; + sorted_set dihedrals_; + sorted_set impropers_; +}; + + +/// LAMMPS molecule template format reader and writer. /// class LAMMPSMolFormat final: public TextFormat { public: @@ -54,6 +110,30 @@ class LAMMPSMolFormat final: public TextFormat { IGNORED; } current_section_; + /// Get the section corresponding to a given line + section_t get_section(string_view line); + + /// Read the header section + void read_header(Frame& frame); + size_t read_header_integer(string_view line, const std::string& context); + + /// Get the section name from the next non-empty line + void get_next_section(); + /// Skip all lines that are not sections names, and get the next section + void skip_to_next_section(); + + /// Read the atoms section + void read_coords(Frame& frame); + /// Read the masses section + void read_masses(Frame& frame); + /// Read the bonds section + void read_bonds(Frame& frame); + + /// Write the header + void write_header(const Frame& frame); + + size_t natoms_ = 0; + size_t nbonds_ = 0; }; diff --git a/src/formats/LAMMPSMol.cpp b/src/formats/LAMMPSMol.cpp index 8da3ef5f1..23f5d6b2c 100644 --- a/src/formats/LAMMPSMol.cpp +++ b/src/formats/LAMMPSMol.cpp @@ -90,8 +90,6 @@ void LAMMPSMolFormat::read_next(Frame& frame) { } } - setup_masses(frame); - setup_names(frame); } /// Remove the comment from `line` and return it. @@ -202,12 +200,9 @@ void LAMMPSMolFormat::read_coords(Frame& frame) { positions[index][2] = z; if (!comment.empty()) { - // Read the first string after the comment, and use it as atom name - auto name = split(comment, ' ')[0]; - if (names_.empty()) { - names_.resize(natoms_); - } - names_[data.index] = name.to_string(); + // Read the first string after the comment, and use it as atom name + auto name = split(comment, ' ')[0]; + frame[index].set_name(name); } n++; @@ -216,7 +211,7 @@ void LAMMPSMolFormat::read_coords(Frame& frame) { get_next_section(); } -void LAMMPSMolFormat::read_masses() { +void LAMMPSMolFormat::read_masses(Frame& frame) { assert(current_section_ == MASSES); if (natom_types_ == 0) { throw format_error("missing atom types count in header"); @@ -224,7 +219,7 @@ void LAMMPSMolFormat::read_masses() { size_t n = 0; auto line = file_.readline(); split_comment(line); - while (line.size() != 0 && !file_.eof()) { + while (line.size() == 2 && !file_.eof()) { auto line = file_.readline(); split_comment(line); if (line.empty()) {continue;} @@ -234,9 +229,9 @@ void LAMMPSMolFormat::read_masses() { throw format_error("bad mass specification '{}'", line); } - auto type = splitted[0]; + auto index = splitted[0]; auto mass = parse(splitted[1]); - masses_.emplace(type.to_string(), mass); + frame[index].set_mass(mass); n++; } @@ -249,7 +244,8 @@ void LAMMPSMolFormat::read_types() { throw format_error("missing atom types count in header"); } size_t n = 0; - while (n < natom_types_ && !file_.eof()) { + auto line = file_.readline(); + while (line.size() == 2 && !file_.eof()) { auto line = file_.readline(); split_comment(line); if (line.empty()) {continue;} @@ -260,14 +256,43 @@ void LAMMPSMolFormat::read_types() { } auto type = splitted[0]; - auto mass = parse(splitted[1]); - masses_.emplace(type.to_string(), mass); + auto type = parse(splitted[1]); + frame[index].set_type(type); n++; } get_next_section(); } +void LAMMPSMolFormat::read_bonds(Frame& frame) { + assert(current_section_ == BONDS); + if (nbonds_ == 0) { + throw format_error("missing bonds count in header"); + } + size_t n = 0; + while (n < nbonds_ && !file_.eof()) { + auto line = file_.readline(); + split_comment(line); + if (line.empty()) {continue;} + + auto splitted = split(line, ' '); + if (splitted.size() != 4) { + throw format_error("bad bond specification '{}'", line); + } + // LAMMPS use 1-based indexing + auto i = parse(splitted[2]) - 1; + auto j = parse(splitted[3]) - 1; + frame.add_bond(i, j); + n++; + } + + if (file_.eof() && n < nbonds_) { + throw format_error("end of file found before getting all bonds"); + } + + get_next_section(); +} + void LAMMPSMolFormat::get_next_section() { while (!file_.eof()) { auto line = file_.readline(); @@ -297,3 +322,133 @@ void LAMMPSDataFormat::skip_to_next_section() { } } } + +void write_next(const Frame& frame) override +{ + if (file_.tellpos() != 0) { + throw format_error("LAMMPS molecule template can only contain one frame"); + } + + auto types = DataTypes(frame.topology()); + + write_header(frame); + write_coords(frame); + write_types(frame); + write_charges(frame); + auto& topology = frame.topology(); + write_bonds(topogology); + +} + +void LAMMPSMolFormat::write_header(const Frame& frame) +{ + file_.print("LAMMPS molecule template -- generated by chemfiles\n\n"); + file_.print("{} atoms\n", frame.size()); + file_.print("{} bonds\n", frame.topology().bonds().size()); + + file_.print("\n"); +} + +void LAMMPSMolFormat::write_coords(const Frame& frame) +{ + file_.print("Coords\n\n"); + auto positions = frame.positions(); + for (size_t i=0; i guess_molecules(const Frame& frame) { + // Initialize the molids vector with each atom in its own molecule + auto molids = std::vector(); + molids.reserve(frame.size()); + for (size_t i=0; i molids[j]) { + new_id = molids[j]; + old_id = molids[i]; + } + + for (auto& molid: molids) { + if (molid == old_id) { + molid = new_id; + } + } + } + + // Make sure the molids are consecutive + std::unordered_map molids_mapping; + for (auto& molid: molids) { + auto it = molids_mapping.find(molid); + if (it != molids_mapping.end()) { + molid = it->second; + } else { + // We've not found this id yet + auto new_id = molids_mapping.size(); + molids_mapping.insert({molid, new_id}); + molid = new_id; + } + } + + return molids; +} + +optional LAMMPSDataFormat::forward() { + // LAMMPS moltemplate only supports one step, so always act like there is only one + auto position = file_.tellpos(); + if (position == 0) { + // advance the pointer for the next call + file_.readline(); + return position; + } else { + return nullopt; + } +} From 4a8049ea7b9fe87cd95ea73ac293bbdd532834d6 Mon Sep 17 00:00:00 2001 From: Roy Kid Date: Tue, 10 Oct 2023 19:09:09 +0200 Subject: [PATCH 3/6] update --- external/CMakeLists.txt | 5 + include/chemfiles/Connectivity.hpp | 24 +- include/chemfiles/Frame.hpp | 55 +- include/chemfiles/Topology.hpp | 99 +- include/chemfiles/config.h | 36 + include/chemfiles/exports.h | 42 + include/chemfiles/formats/LAMMPSData.hpp | 53 +- include/chemfiles/formats/LAMMPSMol.hpp | 288 +- src/Connectivity.cpp | 59 +- src/Topology.cpp | 51 +- src/formats/LAMMPSData.cpp | 254 +- src/formats/LAMMPSMol.cpp | 906 +- tests/external/CMakeLists.txt | 8 - tests/external/catch.hpp | 17966 --------------------- tests/external/helpers.cpp | 202 - tests/external/helpers.hpp | 70 - tests/formats/lammps-data.cpp | 130 + 17 files changed, 1335 insertions(+), 18913 deletions(-) create mode 100644 include/chemfiles/config.h create mode 100644 include/chemfiles/exports.h delete mode 100644 tests/external/CMakeLists.txt delete mode 100644 tests/external/catch.hpp delete mode 100644 tests/external/helpers.cpp delete mode 100644 tests/external/helpers.hpp diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index b456ea429..a198e6532 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -4,6 +4,11 @@ unset(CMAKE_C_STANDARD) # symbols (such as strdup) undefined. string(REPLACE "-std=c99" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +# TODO: update all the external projects to use this and remove it from here +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_C_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) + set(EXTERNAL_FLAGS "") # Disable warnings about external code diff --git a/include/chemfiles/Connectivity.hpp b/include/chemfiles/Connectivity.hpp index fa4ba8b46..fbbb8ce53 100644 --- a/include/chemfiles/Connectivity.hpp +++ b/include/chemfiles/Connectivity.hpp @@ -7,6 +7,7 @@ #include #include #include // IWYU pragma: keep +#include #include "chemfiles/sorted_set.hpp" #include "chemfiles/exports.h" @@ -317,7 +318,17 @@ class Connectivity final { const sorted_set& impropers() const; /// Add a bond between the atoms `i` and `j` - void add_bond(size_t i, size_t j, Bond::BondOrder bond_order = Bond::UNKNOWN); + void add_bond(size_t i, size_t j, Bond::BondOrder bond_order = Bond::UNKNOWN, std::string bond_type = ""); + + /// Add an angle between the atoms `i`, `j` and `k` + void add_angle(size_t i, size_t j, size_t k, std::string angle_type = ""); + + /// Add a dihedral angle between the atoms `i`, `j`, `k` and `l` + void add_dihedral(size_t i, size_t j, size_t k, size_t l, std::string dihedral_type = ""); + + /// Add an improper dihedral angle between the atoms `i`, `j`, `k` and `l` + void add_improper(size_t i, size_t j, size_t k, size_t l, std::string improper_type = ""); + /// Remove any bond between the atoms `i` and `j` void remove_bond(size_t i, size_t j); @@ -330,6 +341,9 @@ class Connectivity final { /// Get the bond order of the bond between i and j Bond::BondOrder bond_order(size_t i, size_t j) const; + + /// Get the bond type of the bond between i and j + const std::string& bond_type(size_t i, size_t j) const; private: /// Recalculate the angles and the dihedrals from the bond list void recalculate() const; @@ -349,6 +363,14 @@ class Connectivity final { mutable bool uptodate_ = false; /// Store the bond orders std::vector bond_orders_; + /// Store the bond types + std::vector bond_types_; + /// Store the angle types + std::vector angle_types_; + /// Store the dihedral types + std::vector dihedral_types_; + /// Store the improper types + std::vector improper_types_; }; } // namespace chemfiles diff --git a/include/chemfiles/Frame.hpp b/include/chemfiles/Frame.hpp index 245eed0fa..db2a1be3d 100644 --- a/include/chemfiles/Frame.hpp +++ b/include/chemfiles/Frame.hpp @@ -4,6 +4,7 @@ #ifndef CHEMFILES_FRAME_HPP #define CHEMFILES_FRAME_HPP +#include #include #include @@ -246,8 +247,58 @@ class CHFL_EXPORT Frame final { /// @param bond_order the bond order of the new bond /// @throws OutOfBounds if `atom_i` or `atom_j` are greater than `size()` /// @throws Error if `atom_i == atom_j`, as this is an invalid bond - void add_bond(size_t atom_i, size_t atom_j, Bond::BondOrder bond_order = Bond::UNKNOWN) { - topology_.add_bond(atom_i, atom_j, bond_order); + void add_bond(size_t atom_i, size_t atom_j, Bond::BondOrder bond_order = Bond::UNKNOWN, std::string bond_type = "") { + topology_.add_bond(atom_i, atom_j, bond_order, bond_type); + } + + /// Add an angle in the system, between the atoms at index `atom_i`, + /// `atom_j` and `atom_k`. + /// + /// @example{frame/add_angle.cpp} + /// + /// @param atom_i the index of the first atom in the angle + /// @param atom_j the index of the second atom in the angle + /// @param atom_k the index of the third atom in the angle + /// @param angle_type the angle order of the new angle + /// @throws OutOfBounds if `atom_i`, `atom_j` and `atom_k` are + /// greater than `size()` + /// @throws Error if any two indices are equal as this is an invalid angle + void add_angle(size_t atom_i, size_t atom_j, size_t atom_k, std::string angle_type = "") { + topology_.add_angle(atom_i, atom_j, atom_k, angle_type); + } + + /// Add a dihedral in the system, between the atoms at index `atom_i`, + /// `atom_j`, `atom_k`, and `atom_l`. + /// + /// @example{frame/add_dihedral.cpp} + /// + /// @param atom_i the index of the first atom in the dihedral + /// @param atom_j the index of the second atom in the dihedral + /// @param atom_k the index of the third atom in the dihedral + /// @param atom_l the index of the forth atom in the dihedral + /// @param dihedral_type the dihedral order of the new dihedral + /// @throws OutOfBounds if `atom_i`, `atom_j` and `atom_k` are + /// greater than `size()` + /// @throws Error if any two indices are equal as this is an invalid dihedral + void add_dihedral(size_t atom_i, size_t atom_j, size_t atom_k, size_t atom_l, std::string dihedral_type = "") { + topology_.add_dihedral(atom_i, atom_j, atom_k, atom_l, dihedral_type); + } + + /// Add a improper in the system, between the atoms at index `atom_i`, + /// `atom_j` and `atom_k`. + /// + /// @example{frame/add_improper.cpp} + /// + /// @param atom_i the index of the first atom in the improper + /// @param atom_j the index of the second atom in the improper + /// @param atom_k the index of the third atom in the improper + /// @param atom_l the index of the forth atom in the improper + /// @param improper_type the improper order of the new improper + /// @throws OutOfBounds if `atom_i`, `atom_j` and `atom_k` are + /// greater than `size()` + /// @throws Error if any two indices are equal as this is an invalid improper + void add_improper(size_t atom_i, size_t atom_j, size_t atom_k, size_t atom_l, std::string improper_type = "") { + topology_.add_improper(atom_i, atom_j, atom_k, atom_l, improper_type); } /// Remove a bond in the system, between the atoms at index `atom_i` and diff --git a/include/chemfiles/Topology.hpp b/include/chemfiles/Topology.hpp index 034b5c720..e775f2006 100644 --- a/include/chemfiles/Topology.hpp +++ b/include/chemfiles/Topology.hpp @@ -4,6 +4,7 @@ #ifndef CHEMFILES_TOPOLOGY_HPP #define CHEMFILES_TOPOLOGY_HPP +#include #include #include #include @@ -113,9 +114,54 @@ class CHFL_EXPORT Topology final { /// @param atom_i the index of the first atom in the bond /// @param atom_j the index of the second atom in the bond /// @param bond_order the bond order for the bond added + /// @param bond_type the bond type for the bond added /// @throws OutOfBounds if `atom_i` or `atom_j` are greater than `size()` /// @throws Error if `atom_i == atom_j`, as this is an invalid bond - void add_bond(size_t atom_i, size_t atom_j, Bond::BondOrder bond_order = Bond::UNKNOWN); + void add_bond(size_t atom_i, size_t atom_j, Bond::BondOrder bond_order = Bond::UNKNOWN, std::string bond_type = ""); + + /// Add an angle in the system, between the atoms at index `atom_i`, + /// `atom_j` and `atom_k` + /// + /// @example{topology/add_angle.cpp} + /// + /// @param atom_i the index of the first atom in the angle + /// @param atom_j the index of the second atom in the angle + /// @param atom_k the index of the third atom in the angle + /// @param angle_type the angle type for the angle added + /// @throws OutOfBounds if any index is greater than `size()` + /// @throws Error if any atom index is duplicate, + /// as this is an invalid angle + void add_angle(size_t atom_i, size_t atom_j, size_t atom_k, std::string angle_type = ""); + + /// Add a dihedral in the system, between the atoms at index `atom_i`, + /// `atom_j` and `atom_k` + /// + /// @example{topology/add_dihedral.cpp} + /// + /// @param atom_i the index of the first atom in the dihedral + /// @param atom_j the index of the second atom in the dihedral + /// @param atom_k the index of the third atom in the dihedral + /// @param atom_l the index of the forth atom in the dihedral + /// @param dihedral_type the dihedral type for the dihedral added + /// @throws OutOfBounds if any index is greater than `size()` + /// @throws Error if any atom index is duplicate, + /// as this is an invalid dihedral + void add_dihedral(size_t atom_i, size_t atom_j, size_t atom_k, size_t atom_l, std::string dihedral_type = ""); + + /// Add an improper in the system, between the atoms at index `atom_i`, + /// `atom_j` and `atom_k` + /// + /// @example{topology/add_improper.cpp} + /// + /// @param atom_i the index of the first atom in the improper + /// @param atom_j the index of the second atom in the improper + /// @param atom_k the index of the third atom in the improper + /// @param atom_l the index of the forth atom in the improper + /// @param improper_type the improper type for the improper added + /// @throws OutOfBounds if any index is greater than `size()` + /// @throws Error if any atom index is duplicate, + /// as this is an invalid improper + void add_improper(size_t atom_i, size_t atom_j, size_t atom_k, size_t atom_l, std::string improper_type = ""); /// Remove a bond in the system, between the atoms at index `atom_i` and /// `atom_j`. @@ -141,6 +187,57 @@ class CHFL_EXPORT Topology final { /// @throws Error if no bond between `atom_i` and `atom_j` exists. Bond::BondOrder bond_order(size_t atom_i, size_t atom_j) const; + /// Get the bond type for the given bond + /// + /// If the bond does not exist, this will thrown an Error. + /// + /// @example{topology/bond_type.cpp} + /// + /// @param atom_i the index of the first atom in the bond + /// @param atom_j the index of the second atom in the bond + /// @throws OutOfBounds if `atom_i` or `atom_j` are greater than `size()` + /// @throws Error if no bond between `atom_i` and `atom_j` exists. + const std::string& bond_type(size_t atom_i, size_t atom_j) const; + + /// Get the angle type for the given angle + /// + /// If the angle does not exist, this will thrown an Error. + /// + /// @example{topology/angle_type.cpp} + /// + /// @param atom_i the index of the first atom in the angle + /// @param atom_j the index of the second atom in the angle + /// @param atom_k the index of the third atom in the angle + /// @throws OutOfBounds if any index is greater than `size()` + /// @throws Error if no angle exists. + const std::string& angle_type(size_t atom_i, size_t atom_j, size_t atom_k) const; + + /// Get the dihedral type for the given dihedral + /// + /// If the dihedral does not exist, this will thrown an Error. + /// + /// @example{topology/dihedral_type.cpp} + /// + /// @param atom_i the index of the first atom in the dihedral + /// @param atom_j the index of the second atom in the dihedral + /// @param atom_k the index of the third atom in the dihedral + /// @throws OutOfBounds if any index is greater than `size()` + /// @throws Error if no dihedral exists. + const std::string& dihedral_type(size_t atom_i, size_t atom_j, size_t atom_k, size_t atom_l) const; + + /// Get the improper type for the given improper + /// + /// If the improper does not exist, this will thrown an Error. + /// + /// @example{topology/angle_type.cpp} + /// + /// @param atom_i the index of the first atom in the improper + /// @param atom_j the index of the second atom in the improper + /// @param atom_k the index of the third atom in the improper + /// @throws OutOfBounds if any index is greater than `size()` + /// @throws Error if no improper exists. + const std::string& improper_type(size_t atom_i, size_t atom_j, size_t atom_k, size_t atom_l) const; + /// Get the number of atoms in the topology /// /// @example{topology/size.cpp} diff --git a/include/chemfiles/config.h b/include/chemfiles/config.h new file mode 100644 index 000000000..7e68a75fe --- /dev/null +++ b/include/chemfiles/config.h @@ -0,0 +1,36 @@ +// Chemfiles, a modern library for chemistry file reading and writing +// Copyright (C) Guillaume Fraux and contributors -- BSD license + +// clang-format off +#ifndef CHEMFILES_CONFIG_HPP +#define CHEMFILES_CONFIG_HPP + +/// An integer containing the major (x.0.0) version number +#define CHEMFILES_VERSION_MAJOR 0 +/// An integer containing the minor (0.y.0) version number +#define CHEMFILES_VERSION_MINOR 11 +/// An integer containing the patch (0.0.z) version number +#define CHEMFILES_VERSION_PATCH 0 +/// The full version of chemfiles ("x.y.z"), as a string +#define CHEMFILES_VERSION "0.11.0-dev" + +#define CHEMFILES_SIZEOF_VOID_P 8 + +/// Are we building code on Windows? +/* #undef CHEMFILES_WINDOWS */ + +// Should we include GEMMI code? +/* #undef CHFL_DISABLE_GEMMI */ + +/// thread_local implementation +#ifdef __cplusplus + #if 1 + #define CHFL_THREAD_LOCAL thread_local + #else + #define CHFL_THREAD_LOCAL + #endif +#endif + +// clang-format on + +#endif diff --git a/include/chemfiles/exports.h b/include/chemfiles/exports.h new file mode 100644 index 000000000..259574ff3 --- /dev/null +++ b/include/chemfiles/exports.h @@ -0,0 +1,42 @@ + +#ifndef CHFL_EXPORT_H +#define CHFL_EXPORT_H + +#ifdef CHFL_STATIC_DEFINE +# define CHFL_EXPORT +# define CHFL_NO_EXPORT +#else +# ifndef CHFL_EXPORT +# ifdef chemfiles_EXPORTS + /* We are building this library */ +# define CHFL_EXPORT +# else + /* We are using this library */ +# define CHFL_EXPORT +# endif +# endif + +# ifndef CHFL_NO_EXPORT +# define CHFL_NO_EXPORT +# endif +#endif + +#ifndef CHFL_DEPRECATED +# define CHFL_DEPRECATED __attribute__ ((__deprecated__)) +#endif + +#ifndef CHFL_DEPRECATED_EXPORT +# define CHFL_DEPRECATED_EXPORT CHFL_EXPORT CHFL_DEPRECATED +#endif + +#ifndef CHFL_DEPRECATED_NO_EXPORT +# define CHFL_DEPRECATED_NO_EXPORT CHFL_NO_EXPORT CHFL_DEPRECATED +#endif + +#if 0 /* DEFINE_NO_DEPRECATED */ +# ifndef CHFL_NO_DEPRECATED +# define CHFL_NO_DEPRECATED +# endif +#endif + +#endif /* CHFL_EXPORT_H */ diff --git a/include/chemfiles/formats/LAMMPSData.hpp b/include/chemfiles/formats/LAMMPSData.hpp index 08797fd6d..705abd79e 100644 --- a/include/chemfiles/formats/LAMMPSData.hpp +++ b/include/chemfiles/formats/LAMMPSData.hpp @@ -69,10 +69,10 @@ class DataTypes { DataTypes(const Topology& topology = Topology()); const sorted_set& atoms() const {return atoms_;} - const sorted_set& bonds() const {return bonds_;} - const sorted_set& angles() const {return angles_;} - const sorted_set& dihedrals() const {return dihedrals_;} - const sorted_set& impropers() const {return impropers_;} + const sorted_set>& bonds() const {return bonds_;} + const sorted_set>& angles() const {return angles_;} + const sorted_set>& dihedrals() const {return dihedrals_;} + const sorted_set>& impropers() const {return impropers_;} /// Get the atom type number for the given atom. /// @@ -86,37 +86,37 @@ class DataTypes { /// The bond type must be in the topology used to construct this `DataTypes` /// instance. The index numbering starts at zero, and can be used to index /// the vector backing the `sorted_set` returned by `bonds()`. - size_t bond_type_id(size_t type_i, size_t type_j) const; + std::string bond_type_id(size_t type_i, size_t type_j) const; /// Get the angle type number for the angle type i-j-k. /// /// The angle type must be in the topology used to construct this `DataTypes` /// instance. The index numbering starts at zero, and can be used to index - /// the vector backing the `sorted_set` returned by `angles()`. - size_t angle_type_id(size_t type_i, size_t type_j, size_t type_k) const; + /// the vector backing the `sorted_set` returned by `angles()`. + std::string angle_type_id(size_t type_i, size_t type_j, size_t type_k) const; /// Get the dihedral type number for the dihedral type i-j-k-m. /// /// The dihedral type must be in the topology used to construct this /// `DataTypes` instance. The index numbering starts at zero, and can be - /// used to index the vector backing the `sorted_set` + /// used to index the vector backing the `sorted_set` /// returned by `dihedrals()`. - size_t dihedral_type_id(size_t type_i, size_t type_j, size_t type_k, size_t type_m) const; + std::string dihedral_type_id(size_t type_i, size_t type_j, size_t type_k, size_t type_m) const; /// Get the improper type number for the improper type i-j-k-m. /// /// The improper type must be in the topology used to construct this /// `DataTypes` instance. The index numbering starts at zero, and can be - /// used to index the vector backing the `sorted_set` + /// used to index the vector backing the `sorted_set` /// returned by `impropers()`. - size_t improper_type_id(size_t type_i, size_t type_j, size_t type_k, size_t type_m) const; + std::string improper_type_id(size_t type_i, size_t type_j, size_t type_k, size_t type_m) const; private: sorted_set atoms_; - sorted_set bonds_; - sorted_set angles_; - sorted_set dihedrals_; - sorted_set impropers_; + sorted_set> bonds_; + sorted_set> angles_; + sorted_set> dihedrals_; + sorted_set> impropers_; }; /// LAMMPS Data file format reader and writer. @@ -157,6 +157,9 @@ class LAMMPSDataFormat final: public TextFormat { ATOMS, MASSES, BONDS, + ANGLES, + DIHEDRALS, + IMPROPERS, VELOCITIES, IGNORED, NOT_A_SECTION, @@ -181,6 +184,12 @@ class LAMMPSDataFormat final: public TextFormat { void read_masses(); /// Read the bonds section void read_bonds(Frame& frame); + /// Read the angles section + void read_angles(Frame& frame); + /// Read the dihedrals section + void read_dihedrals(Frame& frame); + /// Read the impropers section + void read_impropers(Frame& frame); /// Read the velocities section void read_velocities(Frame& frame); /// Setup masses of the frame with previously read values. This function @@ -219,6 +228,20 @@ class LAMMPSDataFormat final: public TextFormat { size_t natom_types_ = 0; /// Number of bonds in the file size_t nbonds_ = 0; + /// Number of bond types in the file + /// size_t nbond_types_ = 0; + /// Number of angles in the file + size_t nangles_ = 0; + /// Number of angle types in the file + /// size_t nangle_types_ = 0; + /// Number of dihedrals in the file + size_t ndihedrals_ = 0; + /// Number of dihedral types in the file + /// size_t ndihedral_types_ = 0; + /// Number of impropers in the file + size_t nimpropers_ = 0; + /// Optional masses, indexed by atomic type + std::unordered_map masses_; /// Optional atomic names, indexed by atomic indexes std::vector names_; }; diff --git a/include/chemfiles/formats/LAMMPSMol.hpp b/include/chemfiles/formats/LAMMPSMol.hpp index dddc26286..398a3f54c 100644 --- a/include/chemfiles/formats/LAMMPSMol.hpp +++ b/include/chemfiles/formats/LAMMPSMol.hpp @@ -1,144 +1,144 @@ -// Chemfiles, a modern library for chemistry file reading and writing -// Copyright (C) Guillaume Fraux and contributors -- BSD license - -#ifndef CHEMFILES_FORMAT_LAMMPSMol_HPP -#define CHEMFILES_FORMAT_LAMMPSMol_HPP - -#include -#include -#include - -#include "chemfiles/File.hpp" -#include "chemfiles/Format.hpp" - -#include "chemfiles/external/optional.hpp" - -namespace chemfiles { -class Frame; -class MemoryBuffer; -class FormatMetadata; - -class DataTypes { -public: - DataTypes(const Topology& topology = Topology()); - - const sorted_set& atoms() const {return atoms_;} - const sorted_set& bonds() const {return bonds_;} - const sorted_set& angles() const {return angles_;} - const sorted_set& dihedrals() const {return dihedrals_;} - const sorted_set& impropers() const {return impropers_;} - - /// Get the atom type number for the given atom. - /// - /// The atom must be in the topology used to construct this `DataTypes` - /// instance. The index numbering starts at zero, and can be used to index - /// the vector backing the `sorted_set` returned by `atoms()`. - size_t atom_type_id(const Atom& atom) const; - - /// Get the bond type number for the bond type i-j. - /// - /// The bond type must be in the topology used to construct this `DataTypes` - /// instance. The index numbering starts at zero, and can be used to index - /// the vector backing the `sorted_set` returned by `bonds()`. - size_t bond_type_id(size_t type_i, size_t type_j) const; - - /// Get the angle type number for the angle type i-j-k. - /// - /// The angle type must be in the topology used to construct this `DataTypes` - /// instance. The index numbering starts at zero, and can be used to index - /// the vector backing the `sorted_set` returned by `angles()`. - size_t angle_type_id(size_t type_i, size_t type_j, size_t type_k) const; - - /// Get the dihedral type number for the dihedral type i-j-k-m. - /// - /// The dihedral type must be in the topology used to construct this - /// `DataTypes` instance. The index numbering starts at zero, and can be - /// used to index the vector backing the `sorted_set` - /// returned by `dihedrals()`. - size_t dihedral_type_id(size_t type_i, size_t type_j, size_t type_k, size_t type_m) const; - - /// Get the improper type number for the improper type i-j-k-m. - /// - /// The improper type must be in the topology used to construct this - /// `DataTypes` instance. The index numbering starts at zero, and can be - /// used to index the vector backing the `sorted_set` - /// returned by `impropers()`. - size_t improper_type_id(size_t type_i, size_t type_j, size_t type_k, size_t type_m) const; - -private: - sorted_set atoms_; - sorted_set bonds_; - sorted_set angles_; - sorted_set dihedrals_; - sorted_set impropers_; -}; - - -/// LAMMPS molecule template format reader and writer. -/// -class LAMMPSMolFormat final: public TextFormat { -public: - LAMMPSMolFormat(std::string path, File::Mode mode, File::Compression compression): - TextFormat(std::move(path), mode, compression), current_section_(HEADER){} - - LAMMPSMolFormat(std::shared_ptr memory, File::Mode mode, File::Compression compression) : - TextFormat(std::move(memory), mode, compression), current_section_(HEADER){} - - void read_next(Frame& frame) override; - void write_next(const Frame& frame) override; - optional forward() override; - -private: - enum section_t { - HEADER, - COORDS, - TYPES, - MOLECULES, - FRAGMENTS, - CHARGES, - DIAMETERS, - MASSES, - BONDS, - ANGLES, - DIHEDRALS, - IMPROPERS, - SPECIAL_BONDS_COUNTS, - SPECIAL_BONDS, - SHAKE_FLAGS, - SHAKE_ATOMS, - NOT_A_SECTION, - IGNORED; - } current_section_; - - /// Get the section corresponding to a given line - section_t get_section(string_view line); - - /// Read the header section - void read_header(Frame& frame); - size_t read_header_integer(string_view line, const std::string& context); - - /// Get the section name from the next non-empty line - void get_next_section(); - /// Skip all lines that are not sections names, and get the next section - void skip_to_next_section(); - - /// Read the atoms section - void read_coords(Frame& frame); - /// Read the masses section - void read_masses(Frame& frame); - /// Read the bonds section - void read_bonds(Frame& frame); - - /// Write the header - void write_header(const Frame& frame); - - size_t natoms_ = 0; - size_t nbonds_ = 0; - -}; - -template<> const FormatMetadata& format_metadata(); - -} // namespace chemfiles - -#endif +// // Chemfiles, a modern library for chemistry file reading and writing +// // Copyright (C) Guillaume Fraux and contributors -- BSD license + +// #ifndef CHEMFILES_FORMAT_LAMMPSMol_HPP +// #define CHEMFILES_FORMAT_LAMMPSMol_HPP + +// #include +// #include +// #include + +// #include "chemfiles/File.hpp" +// #include "chemfiles/Format.hpp" + +// #include "chemfiles/external/optional.hpp" + +// namespace chemfiles { +// class Frame; +// class MemoryBuffer; +// class FormatMetadata; + +// class DataTypes { +// public: +// DataTypes(const Topology& topology = Topology()); + +// const sorted_set& atoms() const {return atoms_;} +// const sorted_set& bonds() const {return bonds_;} +// const sorted_set& angles() const {return angles_;} +// const sorted_set& dihedrals() const {return dihedrals_;} +// const sorted_set& impropers() const {return impropers_;} + +// /// Get the atom type number for the given atom. +// /// +// /// The atom must be in the topology used to construct this `DataTypes` +// /// instance. The index numbering starts at zero, and can be used to index +// /// the vector backing the `sorted_set` returned by `atoms()`. +// size_t atom_type_id(const Atom& atom) const; + +// /// Get the bond type number for the bond type i-j. +// /// +// /// The bond type must be in the topology used to construct this `DataTypes` +// /// instance. The index numbering starts at zero, and can be used to index +// /// the vector backing the `sorted_set` returned by `bonds()`. +// size_t bond_type_id(size_t type_i, size_t type_j) const; + +// /// Get the angle type number for the angle type i-j-k. +// /// +// /// The angle type must be in the topology used to construct this `DataTypes` +// /// instance. The index numbering starts at zero, and can be used to index +// /// the vector backing the `sorted_set` returned by `angles()`. +// size_t angle_type_id(size_t type_i, size_t type_j, size_t type_k) const; + +// /// Get the dihedral type number for the dihedral type i-j-k-m. +// /// +// /// The dihedral type must be in the topology used to construct this +// /// `DataTypes` instance. The index numbering starts at zero, and can be +// /// used to index the vector backing the `sorted_set` +// /// returned by `dihedrals()`. +// size_t dihedral_type_id(size_t type_i, size_t type_j, size_t type_k, size_t type_m) const; + +// /// Get the improper type number for the improper type i-j-k-m. +// /// +// /// The improper type must be in the topology used to construct this +// /// `DataTypes` instance. The index numbering starts at zero, and can be +// /// used to index the vector backing the `sorted_set` +// /// returned by `impropers()`. +// size_t improper_type_id(size_t type_i, size_t type_j, size_t type_k, size_t type_m) const; + +// private: +// sorted_set atoms_; +// sorted_set bonds_; +// sorted_set angles_; +// sorted_set dihedrals_; +// sorted_set impropers_; +// }; + + +// /// LAMMPS molecule template format reader and writer. +// /// +// class LAMMPSMolFormat final: public TextFormat { +// public: +// LAMMPSMolFormat(std::string path, File::Mode mode, File::Compression compression): +// TextFormat(std::move(path), mode, compression), current_section_(HEADER){} + +// LAMMPSMolFormat(std::shared_ptr memory, File::Mode mode, File::Compression compression) : +// TextFormat(std::move(memory), mode, compression), current_section_(HEADER){} + +// void read_next(Frame& frame) override; +// void write_next(const Frame& frame) override; +// optional forward() override; + +// private: +// enum section_t { +// HEADER, +// COORDS, +// TYPES, +// MOLECULES, +// FRAGMENTS, +// CHARGES, +// DIAMETERS, +// MASSES, +// BONDS, +// ANGLES, +// DIHEDRALS, +// IMPROPERS, +// SPECIAL_BONDS_COUNTS, +// SPECIAL_BONDS, +// SHAKE_FLAGS, +// SHAKE_ATOMS, +// NOT_A_SECTION, +// IGNORED; +// } current_section_; + +// /// Get the section corresponding to a given line +// section_t get_section(string_view line); + +// /// Read the header section +// void read_header(Frame& frame); +// size_t read_header_integer(string_view line, const std::string& context); + +// /// Get the section name from the next non-empty line +// void get_next_section(); +// /// Skip all lines that are not sections names, and get the next section +// void skip_to_next_section(); + +// /// Read the atoms section +// void read_coords(Frame& frame); +// /// Read the masses section +// void read_masses(Frame& frame); +// /// Read the bonds section +// void read_bonds(Frame& frame); + +// /// Write the header +// void write_header(const Frame& frame); + +// size_t natoms_ = 0; +// size_t nbonds_ = 0; + +// }; + +// template<> const FormatMetadata& format_metadata(); + +// } // namespace chemfiles + +// #endif diff --git a/src/Connectivity.cpp b/src/Connectivity.cpp index 5229d297a..15e32f7a7 100644 --- a/src/Connectivity.cpp +++ b/src/Connectivity.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -186,7 +187,7 @@ const sorted_set& Connectivity::impropers() const { return impropers_; } -void Connectivity::add_bond(size_t i, size_t j, Bond::BondOrder bond_order) { +void Connectivity::add_bond(size_t i, size_t j, Bond::BondOrder bond_order, std::string bond_type) { uptodate_ = false; auto result = bonds_.emplace(i, j); if (i > biggest_atom_) {biggest_atom_ = i;} @@ -195,6 +196,48 @@ void Connectivity::add_bond(size_t i, size_t j, Bond::BondOrder bond_order) { if (result.second) { auto diff = std::distance(bonds_.cbegin(), result.first); bond_orders_.insert(bond_orders_.begin() + diff, bond_order); + bond_types_.insert(bond_types_.begin() + diff, std::move(bond_type)); + } +} + +void Connectivity::add_angle(size_t i, size_t j, size_t k, std::string angle_type) { + uptodate_ = true; + auto result = angles_.emplace(i, j, k); + if (i > biggest_atom_) {biggest_atom_ = i;} + if (j > biggest_atom_) {biggest_atom_ = j;} + if (k > biggest_atom_) {biggest_atom_ = k;} + + if (result.second) { + auto diff = std::distance(angles_.cbegin(), result.first); + angle_types_.insert(angle_types_.begin() + diff, std::move(angle_type)); + } +} + +void Connectivity::add_dihedral(size_t i, size_t j, size_t k, size_t l, std::string dihedral_type) { + uptodate_ = true; + auto result = dihedrals_.emplace(i, j, k, l); + if (i > biggest_atom_) {biggest_atom_ = i;} + if (j > biggest_atom_) {biggest_atom_ = j;} + if (k > biggest_atom_) {biggest_atom_ = k;} + if (l > biggest_atom_) {biggest_atom_ = l;} + + if (result.second) { + auto diff = std::distance(dihedrals_.cbegin(), result.first); + dihedral_types_.insert(dihedral_types_.begin() + diff, std::move(dihedral_type)); + } +} + +void Connectivity::add_improper(size_t i, size_t j, size_t k, size_t l, std::string improper_type) { + uptodate_ = true; + auto result = impropers_.emplace(i, j, k, l); + if (i > biggest_atom_) {biggest_atom_ = i;} + if (j > biggest_atom_) {biggest_atom_ = j;} + if (k > biggest_atom_) {biggest_atom_ = k;} + if (l > biggest_atom_) {biggest_atom_ = l;} + + if (result.second) { + auto diff = std::distance(impropers_.cbegin(), result.first); + improper_types_.insert(improper_types_.begin() + diff, std::move(improper_type)); } } @@ -255,3 +298,17 @@ Bond::BondOrder Connectivity::bond_order(size_t i, size_t j) const { i, j ); } + +const std::string& Connectivity::bond_type(size_t i, size_t j) const { + auto pos = bonds_.find(Bond(i, j)); + if (pos != bonds_.end()) { + auto diff = std::distance(bonds_.cbegin(), pos); + return bond_types_[static_cast(diff)]; + } + + throw error( + "out of bounds atomic index in `Connectivity::bond_order`: " + "No bond between {} and {} exists", + i, j + ); +} diff --git a/src/Topology.cpp b/src/Topology.cpp index 27eaf2bd5..eb715471f 100644 --- a/src/Topology.cpp +++ b/src/Topology.cpp @@ -36,7 +36,7 @@ void Topology::reserve(size_t size) { atoms_.reserve(size); } -void Topology::add_bond(size_t atom_i, size_t atom_j, Bond::BondOrder bond_order) { +void Topology::add_bond(size_t atom_i, size_t atom_j, Bond::BondOrder bond_order, std::string bond_type) { if (atom_i >= size() || atom_j >= size()) { throw out_of_bounds( "out of bounds atomic index in `Topology::add_bond`: " @@ -44,7 +44,42 @@ void Topology::add_bond(size_t atom_i, size_t atom_j, Bond::BondOrder bond_order size(), atom_i, atom_j ); } - connect_.add_bond(atom_i, atom_j, bond_order); + connect_.add_bond(atom_i, atom_j, bond_order, std::move(bond_type)); +} + +void Topology::add_angle(size_t atom_i, size_t atom_j, size_t atom_k, std::string angle_type) { + if (atom_i >= size() || atom_j >= size() || atom_k >= size()) { + throw out_of_bounds( + "out of bounds atomic index in `Topology::add_angle`: " + "we have {} atoms, but the angle indexes are {}, {} and {}", + size(), atom_i, atom_j, atom_k + ); + } + // NOTO: (Roy) since angle etc. are calculated from bonds, we don't need to + // store it explicitly. + connect_.add_angle(atom_i, atom_j, atom_k, std::move(angle_type)); +} + +void Topology::add_dihedral(size_t atom_i, size_t atom_j, size_t atom_k, size_t atom_m, std::string dihedral_type) { + if (atom_i >= size() || atom_j >= size() || atom_k >= size() || atom_m >= size()) { + throw out_of_bounds( + "out of bounds atomic index in `Topology::add_dihedral`: " + "we have {} atoms, but the dihedral indexes are {}, {}, {} and {}", + size(), atom_i, atom_j, atom_k, atom_m + ); + } + connect_.add_dihedral(atom_i, atom_j, atom_k, atom_m, std::move(dihedral_type)); +} + +void Topology::add_improper(size_t atom_i, size_t atom_j, size_t atom_k, size_t atom_m, std::string improper_type) { + if (atom_i >= size() || atom_j >= size() || atom_k >= size() || atom_m >= size()) { + throw out_of_bounds( + "out of bounds atomic index in `Topology::add_improper`: " + "we have {} atoms, but the improper indexes are {}, {}, {} and {}", + size(), atom_i, atom_j, atom_k, atom_m + ); + } + connect_.add_improper(atom_i, atom_j, atom_k, atom_m, std::move(improper_type)); } void Topology::remove_bond(size_t atom_i, size_t atom_j) { @@ -70,6 +105,18 @@ Bond::BondOrder Topology::bond_order(size_t atom_i, size_t atom_j) const { return connect_.bond_order(atom_i, atom_j); } +const std::string& Topology::bond_type(size_t atom_i, size_t atom_j) const { + if (atom_i >= size() || atom_j >= size()) { + throw out_of_bounds( + "out of bounds atomic index in `Topology::bond_order`: " + "we have {} atoms, but the bond indexes are {} and {}", + size(), atom_i, atom_j + ); + } + + return connect_.bond_type(atom_i, atom_j); +} + void Topology::remove(size_t i) { if (i >= size()) { throw out_of_bounds( diff --git a/src/formats/LAMMPSData.cpp b/src/formats/LAMMPSData.cpp index a7aa90a41..8d983c903 100644 --- a/src/formats/LAMMPSData.cpp +++ b/src/formats/LAMMPSData.cpp @@ -1,6 +1,7 @@ // Chemfiles, a modern library for chemistry file reading and writing // Copyright (C) Guillaume Fraux and contributors -- BSD license +#include #include #include #include @@ -240,6 +241,15 @@ void LAMMPSDataFormat::read_next(Frame& frame) { case BONDS: read_bonds(frame); break; + case ANGLES: + read_angles(frame); + break; + case DIHEDRALS: + read_dihedrals(frame); + break; + case IMPROPERS: + read_impropers(frame); + break; case VELOCITIES: read_velocities(frame); break; @@ -270,6 +280,12 @@ void LAMMPSDataFormat::read_header(Frame& frame) { natoms_ = read_header_integer(content, "atoms"); } else if (content.find("bonds") != std::string::npos) { nbonds_ = read_header_integer(content, "bonds"); + } else if (content.find("angles") != std::string::npos) { + nangles_ = read_header_integer(content, "angles"); + } else if (content.find("dihedrals") != std::string::npos) { + ndihedrals_ = read_header_integer(content, "dihedrals"); + } else if (content.find("impropers") != std::string::npos) { + nimpropers_ = read_header_integer(content, "impropers"); } else if (content.find("atom types") != std::string::npos) { natom_types_ = read_header_integer(content, "atom types"); } else if (content.find("xlo xhi") != std::string::npos) { @@ -467,9 +483,10 @@ void LAMMPSDataFormat::read_bonds(Frame& frame) { throw format_error("bad bond specification '{}'", line); } // LAMMPS use 1-based indexing + auto bond_type = parse(splitted[1]); auto i = parse(splitted[2]) - 1; auto j = parse(splitted[3]) - 1; - frame.add_bond(i, j); + frame.add_bond(i, j, Bond::UNKNOWN, bond_type); n++; } @@ -480,6 +497,101 @@ void LAMMPSDataFormat::read_bonds(Frame& frame) { get_next_section(); } +void LAMMPSDataFormat::read_angles(Frame& frame) { + assert(current_section_ == ANGLES); + if (nangles_ == 0) { + throw format_error("missing angles count in header"); + } + size_t n = 0; + while (n < nangles_ && !file_.eof()) { + auto line = file_.readline(); + split_comment(line); + if (line.empty()) {continue;} + + auto splitted = split(line, ' '); + if (splitted.size() != 5) { + throw format_error("bad angle specification '{}'", line); + } + // LAMMPS use 1-based indexing + auto angle_type = parse(splitted[1]); + auto i = parse(splitted[2]) - 1; + auto j = parse(splitted[3]) - 1; + auto k = parse(splitted[4]) - 1; + frame.add_angle(i, j, k, angle_type); + n++; + } + + if (file_.eof() && n < nangles_) { + throw format_error("end of file found before getting all angles"); + } + + get_next_section(); +} + +void LAMMPSDataFormat::read_dihedrals(Frame& frame) { + assert(current_section_ == DIHEDRALS); + if (ndihedrals_ == 0) { + throw format_error("missing dihedrals count in header"); + } + size_t n = 0; + while (n < ndihedrals_ && !file_.eof()) { + auto line = file_.readline(); + split_comment(line); + if (line.empty()) {continue;} + + auto splitted = split(line, ' '); + if (splitted.size() != 6) { + throw format_error("bad dihedral specification '{}'", line); + } + // LAMMPS use 1-based indexing + auto dihedral_type = parse(splitted[1]); + auto i = parse(splitted[2]) - 1; + auto j = parse(splitted[3]) - 1; + auto k = parse(splitted[4]) - 1; + auto m = parse(splitted[5]) - 1; + frame.add_dihedral(i, j, k, m, dihedral_type); + n++; + } + + if (file_.eof() && n < ndihedrals_) { + throw format_error("end of file found before getting all dihedrals"); + } + + get_next_section(); +} + +void LAMMPSDataFormat::read_impropers(Frame& frame) { + assert(current_section_ == IMPROPERS); + if (nimpropers_ == 0) { + throw format_error("missing impropers count in header"); + } + size_t n = 0; + while (n < nimpropers_ && !file_.eof()) { + auto line = file_.readline(); + split_comment(line); + if (line.empty()) {continue;} + + auto splitted = split(line, ' '); + if (splitted.size() != 6) { + throw format_error("bad improper specification '{}'", line); + } + // LAMMPS use 1-based indexing + auto improper_type = parse(splitted[1]); + auto i = parse(splitted[2]) - 1; + auto j = parse(splitted[3]) - 1; + auto k = parse(splitted[4]) - 1; + auto m = parse(splitted[5]) - 1; + frame.add_improper(i, j, k, m, improper_type); + n++; + } + + if (file_.eof() && n < nimpropers_) { + throw format_error("end of file found before getting all impropers"); + } + + get_next_section(); +} + void LAMMPSDataFormat::read_velocities(Frame& frame) { assert(current_section_ == VELOCITIES); if (natoms_ == 0) { @@ -629,14 +741,14 @@ DataTypes::DataTypes(const Topology& topology) { for (auto& bond: topology.bonds()) { auto i = atom_type_id(topology[bond[0]]); auto j = atom_type_id(topology[bond[1]]); - bonds_.insert(normalize_bond_type(i, j)); + bonds_.insert({normalize_bond_type(i, j), topology.bond_type(i, j)}); } for (auto& angle: topology.angles()) { auto i = atom_type_id(topology[angle[0]]); auto j = atom_type_id(topology[angle[1]]); auto k = atom_type_id(topology[angle[2]]); - angles_.insert(normalize_angle_type(i, j, k)); + angles_.insert({normalize_angle_type(i, j, k), topology.angle_type(i, j, k)}); } for (auto& dihedral: topology.dihedrals()) { @@ -644,7 +756,7 @@ DataTypes::DataTypes(const Topology& topology) { auto j = atom_type_id(topology[dihedral[1]]); auto k = atom_type_id(topology[dihedral[2]]); auto m = atom_type_id(topology[dihedral[3]]); - dihedrals_.insert(normalize_dihedral_type(i, j, k, m)); + dihedrals_.insert({normalize_dihedral_type(i, j, k, m), topology.dihedral_type(i, j, k, m)}); } for (auto& improper: topology.impropers()) { @@ -652,7 +764,7 @@ DataTypes::DataTypes(const Topology& topology) { auto j = atom_type_id(topology[improper[1]]); auto k = atom_type_id(topology[improper[2]]); auto m = atom_type_id(topology[improper[3]]); - impropers_.insert(normalize_improper_type(i, j, k, m)); + impropers_.insert({normalize_improper_type(i, j, k, m), topology.improper_type(i, j, k, m)}); } } @@ -665,37 +777,45 @@ size_t DataTypes::atom_type_id(const Atom& atom) const { } } -size_t DataTypes::bond_type_id(size_t type_i, size_t type_j) const { - auto it = bonds_.find(normalize_bond_type(type_i, type_j)); +std::string DataTypes::bond_type_id(size_t type_i, size_t type_j) const { + auto it = std::find_if(bonds_.begin(), bonds_.end(), [&type_i, &type_j](const std::tuple& pair) { + return std::get<0>(pair) == normalize_bond_type(type_i, type_j); + }); if (it != bonds_.end()) { - return static_cast(it - bonds_.begin()); + return std::get<1>(*it); } else { throw error("invalid bond type passed to bond_type_id, this is a bug"); } } -size_t DataTypes::angle_type_id(size_t type_i, size_t type_j, size_t type_k) const { - auto it = angles_.find(normalize_angle_type(type_i, type_j, type_k)); +std::string DataTypes::angle_type_id(size_t type_i, size_t type_j, size_t type_k) const { + auto it = std::find_if(angles_.begin(), angles_.end(), [&type_i, &type_j, &type_k](const std::tuple& pair) { + return std::get<0>(pair) == normalize_angle_type(type_i, type_j, type_k); + }); if (it != angles_.end()) { - return static_cast(it - angles_.begin()); + return std::get<1>(*it); } else { throw error("invalid angle type passed to angle_type_id, this is a bug"); } } -size_t DataTypes::dihedral_type_id(size_t type_i, size_t type_j, size_t type_k, size_t type_m) const { - auto it = dihedrals_.find(normalize_dihedral_type(type_i, type_j, type_k, type_m)); +std::string DataTypes::dihedral_type_id(size_t type_i, size_t type_j, size_t type_k, size_t type_m) const { + auto it = std::find_if(dihedrals_.begin(), dihedrals_.end(), [&type_i, &type_j, &type_k, &type_m](const std::tuple& pair) { + return std::get<0>(pair) == normalize_dihedral_type(type_i, type_j, type_k, type_m); + }); if (it != dihedrals_.end()) { - return static_cast(it - dihedrals_.begin()); + return std::get<1>(*it); } else { throw error("invalid dihedral type passed to dihedral_type_id, this is a bug"); } } -size_t DataTypes::improper_type_id(size_t type_i, size_t type_j, size_t type_k, size_t type_m) const { - auto it = impropers_.find(normalize_improper_type(type_i, type_j, type_k, type_m)); +std::string DataTypes::improper_type_id(size_t type_i, size_t type_j, size_t type_k, size_t type_m) const { + auto it = std::find_if(impropers_.begin(), impropers_.end(), [&type_i, &type_j, &type_k, &type_m](const std::tuple& pair) { + return std::get<0>(pair) == normalize_improper_type(type_i, type_j, type_k, type_m); + }); if (it != impropers_.end()) { - return static_cast(it - impropers_.begin()); + return std::get<1>(*it); } else { throw error("invalid improper type passed to improper_type_id, this is a bug"); } @@ -765,9 +885,10 @@ void LAMMPSDataFormat::write_types(const DataTypes& types) { if (!bonds.empty()) { file_.print("\n# Bond Coeffs\n"); for (size_t i=0; i(bonds[i]); file_.print("# {} {}-{}\n", i + 1, - atoms[std::get<0>(bonds[i])].first, - atoms[std::get<1>(bonds[i])].first + atoms[std::get<0>(bond_index)].first, + atoms[std::get<1>(bond_index)].first ); } } @@ -776,10 +897,11 @@ void LAMMPSDataFormat::write_types(const DataTypes& types) { if (!angles.empty()) { file_.print("\n# Angle Coeffs\n"); for (size_t i=0; i(angles[i]); file_.print("# {} {}-{}-{}\n", i + 1, - atoms[std::get<0>(angles[i])].first, - atoms[std::get<1>(angles[i])].first, - atoms[std::get<2>(angles[i])].first + atoms[std::get<0>(angle_index)].first, + atoms[std::get<1>(angle_index)].first, + atoms[std::get<2>(angle_index)].first ); } } @@ -788,11 +910,12 @@ void LAMMPSDataFormat::write_types(const DataTypes& types) { if (!dihedrals.empty()) { file_.print("\n# Dihedrals Coeffs\n"); for (size_t i=0; i(dihedrals[i]); file_.print("# {} {}-{}-{}-{}\n", i + 1, - atoms[std::get<0>(dihedrals[i])].first, - atoms[std::get<1>(dihedrals[i])].first, - atoms[std::get<2>(dihedrals[i])].first, - atoms[std::get<3>(dihedrals[i])].first + atoms[std::get<0>(dihedral_index)].first, + atoms[std::get<1>(dihedral_index)].first, + atoms[std::get<2>(dihedral_index)].first, + atoms[std::get<3>(dihedral_index)].first ); } } @@ -801,11 +924,12 @@ void LAMMPSDataFormat::write_types(const DataTypes& types) { if (!impropers.empty()) { file_.print("\n# Impropers Coeffs\n"); for (size_t i=0; i(impropers[i]); file_.print("# {} {}-{}-{}-{}\n", i + 1, - atoms[std::get<0>(impropers[i])].first, - atoms[std::get<1>(impropers[i])].first, - atoms[std::get<2>(impropers[i])].first, - atoms[std::get<3>(impropers[i])].first + atoms[std::get<0>(improper_index)].first, + atoms[std::get<1>(improper_index)].first, + atoms[std::get<2>(improper_index)].first, + atoms[std::get<3>(improper_index)].first ); } } @@ -851,13 +975,21 @@ void LAMMPSDataFormat::write_bonds(const DataTypes& types, const Topology& topol file_.print("\nBonds\n\n"); size_t bond_id = 1; + size_t bond_type_id; for (auto bond: topology.bonds()) { auto type_i = types.atom_type_id(topology[bond[0]]); auto type_j = types.atom_type_id(topology[bond[1]]); - auto bond_type_id = types.bond_type_id(type_i, type_j); - file_.print("{} {} {} {}\n", - bond_id, bond_type_id + 1, bond[0] + 1, bond[1] + 1 - ); + auto bond_type_str = types.bond_type_id(type_i, type_j); + if (bond_type_str.empty()) { + bond_type_id = std::stoul(bond_type_str) + 1; + file_.print("{} {} {} {} {}\n", + bond_id, bond_type_id, bond[0] + 1, bond[1] + 1 + ); + } else { + file_.print("{} {} {} {} {}\n", + bond_id, bond_type_str, bond[0] + 1, bond[1] + 1 + ); + } bond_id++; } } @@ -867,14 +999,22 @@ void LAMMPSDataFormat::write_angles(const DataTypes& types, const Topology& topo file_.print("\nAngles\n\n"); size_t angle_id = 1; + size_t angle_type_id; for (auto angle: topology.angles()) { auto type_i = types.atom_type_id(topology[angle[0]]); auto type_j = types.atom_type_id(topology[angle[1]]); auto type_k = types.atom_type_id(topology[angle[2]]); - auto angle_type_id = types.angle_type_id(type_i, type_j, type_k); - file_.print("{} {} {} {} {}\n", - angle_id, angle_type_id + 1, angle[0] + 1, angle[1] + 1, angle[2] + 1 - ); + auto angle_type_str = types.angle_type_id(type_i, type_j, type_k); + if (angle_type_str.empty()) { + angle_type_id = std::stoul(angle_type_str) + 1; + file_.print("{} {} {} {} {}\n", + angle_id, angle_type_id, angle[0] + 1, angle[1] + 1, angle[2] + 1 + ); + } else { + file_.print("{} {} {} {} {}\n", + angle_id, angle_type_str, angle[0] + 1, angle[1] + 1, angle[2] + 1 + ); + } angle_id++; } } @@ -884,16 +1024,25 @@ void LAMMPSDataFormat::write_dihedrals(const DataTypes& types, const Topology& t file_.print("\nDihedrals\n\n"); size_t dihedral_id = 1; + size_t dihedral_type_id; for (auto dihedral: topology.dihedrals()) { auto type_i = types.atom_type_id(topology[dihedral[0]]); auto type_j = types.atom_type_id(topology[dihedral[1]]); auto type_k = types.atom_type_id(topology[dihedral[2]]); auto type_m = types.atom_type_id(topology[dihedral[3]]); - auto dihedral_type_id = types.dihedral_type_id(type_i, type_j, type_k, type_m); - file_.print("{} {} {} {} {} {}\n", - dihedral_id, dihedral_type_id + 1, - dihedral[0] + 1, dihedral[1] + 1, dihedral[2] + 1, dihedral[3] + 1 - ); + auto dihedral_type_str = types.dihedral_type_id(type_i, type_j, type_k, type_m); + if (dihedral_type_str.empty()) { + dihedral_type_id = std::stoul(dihedral_type_str) + 1; + file_.print("{} {} {} {} {} {}\n", + dihedral_id, dihedral_type_id, + dihedral[0] + 1, dihedral[1] + 1, dihedral[2] + 1, dihedral[3] + 1 + ); + } else { + file_.print("{} {} {} {} {} {}\n", + dihedral_id, dihedral_type_str, + dihedral[0] + 1, dihedral[1] + 1, dihedral[2] + 1, dihedral[3] + 1 + ); + } dihedral_id++; } } @@ -903,16 +1052,25 @@ void LAMMPSDataFormat::write_impropers(const DataTypes& types, const Topology& t file_.print("\nImpropers\n\n"); size_t improper_id = 1; + size_t improper_type_id; for (auto improper: topology.impropers()) { auto type_i = types.atom_type_id(topology[improper[0]]); auto type_j = types.atom_type_id(topology[improper[1]]); auto type_k = types.atom_type_id(topology[improper[2]]); auto type_m = types.atom_type_id(topology[improper[3]]); - auto improper_type_id = types.improper_type_id(type_i, type_j, type_k, type_m); - file_.print("{} {} {} {} {} {}\n", - improper_id, improper_type_id + 1, - improper[0] + 1, improper[1] + 1, improper[2] + 1, improper[3] + 1 - ); + auto improper_type_str = types.improper_type_id(type_i, type_j, type_k, type_m); + if (improper_type_str.empty()) { + improper_type_id = std::stoul(improper_type_str) + 1; + file_.print("{} {} {} {} {} {}\n", + improper_id, improper_type_id, + improper[0] + 1, improper[1] + 1, improper[2] + 1, improper[3] + 1 + ); + } else { + file_.print("{} {} {} {} {} {}\n", + improper_id, improper_type_str, + improper[0] + 1, improper[1] + 1, improper[2] + 1, improper[3] + 1 + ); + } improper_id++; } } diff --git a/src/formats/LAMMPSMol.cpp b/src/formats/LAMMPSMol.cpp index 23f5d6b2c..4ace051b1 100644 --- a/src/formats/LAMMPSMol.cpp +++ b/src/formats/LAMMPSMol.cpp @@ -1,454 +1,454 @@ -// Chemfiles, a modern library for chemistry file reading and writing -// Copyright (C) Guillaume Fraux and contributors -- BSD license - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "chemfiles/types.hpp" -#include "chemfiles/parse.hpp" -#include "chemfiles/utils.hpp" -#include "chemfiles/warnings.hpp" -#include "chemfiles/error_fmt.hpp" -#include "chemfiles/string_view.hpp" -#include "chemfiles/unreachable.hpp" -#include "chemfiles/external/optional.hpp" - -#include "chemfiles/File.hpp" -#include "chemfiles/Atom.hpp" -#include "chemfiles/Frame.hpp" -#include "chemfiles/Topology.hpp" -#include "chemfiles/Property.hpp" -#include "chemfiles/UnitCell.hpp" -#include "chemfiles/FormatMetadata.hpp" - -#include "chemfiles/formats/LAMMPSMol.hpp" - -using namespace chemfiles; - -template<> const FormatMetadata& chemfiles::format_metadata() { - static FormatMetadata metadata; - metadata.name = "LAMMPSMol"; - metadata.extension = ".mol"; - metadata.description = "LAMMPSMol molecule template"; - metadata.reference = "http://lammps.sandia.gov/doc/molecule.html"; - - metadata.read = true; - metadata.write = true; - metadata.memory = false; - - metadata.positions = true; - metadata.velocities = true; - metadata.unit_cell = false; - metadata.atoms = true; - metadata.bonds = true; - metadata.residues = false; - return metadata; -} - -void LAMMPSMolFormat::read_next(Frame& frame) { - if (file_.tellpos() != 0) { - throw format_error("LAMMPSMol format can only contain one frame"); - } - - auto comment = file_.readline(); - while (!file_.eof()) { - switch (current_section_) { - case HEADER: - read_header(frame); - break; - case COORDS: - read_coords(frame); - break; - case MASSES: - read_masses(); - break; - case BONDS: - read_bonds(frame); - break; - case ANGLES: - read_angles(frame); - break; - case DIHEDRALS: - read_dihedrals(frame); - break; - case IMPROPERS: - read_impropers(frame); - break; - case TYPES: - read_types(frame); - break; - - } - } -} - -/// Remove the comment from `line` and return it. -static string_view split_comment(string_view& line); -/// Check if the line is an unused header value -static bool is_unused_header(string_view line); - -string_view split_comment(string_view& line) { - auto position = line.find('#'); - if (position != std::string::npos) { - auto comment = line.substr(position + 1); - line.remove_suffix(line.size() - position); - return comment; - } else { - return ""; - } -} - -bool is_unused_header(string_view line) { - return (line.find("atom types") != std::string::npos) || - (line.find("bond types") != std::string::npos) || - (line.find("angle types") != std::string::npos) || - (line.find("dihedral types") != std::string::npos); -} - -void LAMMPSMolFormat::read_header(Frame& frame) { - assert(current_section_ == HEADER); +// // Chemfiles, a modern library for chemistry file reading and writing +// // Copyright (C) Guillaume Fraux and contributors -- BSD license + +// #include +// #include + +// #include +// #include +// #include +// #include +// #include +// #include +// #include + +// #include + +// #include "chemfiles/types.hpp" +// #include "chemfiles/parse.hpp" +// #include "chemfiles/utils.hpp" +// #include "chemfiles/warnings.hpp" +// #include "chemfiles/error_fmt.hpp" +// #include "chemfiles/string_view.hpp" +// #include "chemfiles/unreachable.hpp" +// #include "chemfiles/external/optional.hpp" + +// #include "chemfiles/File.hpp" +// #include "chemfiles/Atom.hpp" +// #include "chemfiles/Frame.hpp" +// #include "chemfiles/Topology.hpp" +// #include "chemfiles/Property.hpp" +// #include "chemfiles/UnitCell.hpp" +// #include "chemfiles/FormatMetadata.hpp" + +// #include "chemfiles/formats/LAMMPSMol.hpp" + +// using namespace chemfiles; + +// template<> const FormatMetadata& chemfiles::format_metadata() { +// static FormatMetadata metadata; +// metadata.name = "LAMMPSMol"; +// metadata.extension = ".mol"; +// metadata.description = "LAMMPSMol molecule template"; +// metadata.reference = "http://lammps.sandia.gov/doc/molecule.html"; + +// metadata.read = true; +// metadata.write = true; +// metadata.memory = false; + +// metadata.positions = true; +// metadata.velocities = true; +// metadata.unit_cell = false; +// metadata.atoms = true; +// metadata.bonds = true; +// metadata.residues = false; +// return metadata; +// } + +// void LAMMPSMolFormat::read_next(Frame& frame) { +// if (file_.tellpos() != 0) { +// throw format_error("LAMMPSMol format can only contain one frame"); +// } + +// auto comment = file_.readline(); +// while (!file_.eof()) { +// switch (current_section_) { +// case HEADER: +// read_header(frame); +// break; +// case COORDS: +// read_coords(frame); +// break; +// case MASSES: +// read_masses(); +// break; +// case BONDS: +// read_bonds(frame); +// break; +// case ANGLES: +// read_angles(frame); +// break; +// case DIHEDRALS: +// read_dihedrals(frame); +// break; +// case IMPROPERS: +// read_impropers(frame); +// break; +// case TYPES: +// read_types(frame); +// break; + +// } +// } +// } + +// /// Remove the comment from `line` and return it. +// static string_view split_comment(string_view& line); +// /// Check if the line is an unused header value +// static bool is_unused_header(string_view line); + +// string_view split_comment(string_view& line) { +// auto position = line.find('#'); +// if (position != std::string::npos) { +// auto comment = line.substr(position + 1); +// line.remove_suffix(line.size() - position); +// return comment; +// } else { +// return ""; +// } +// } + +// bool is_unused_header(string_view line) { +// return (line.find("atom types") != std::string::npos) || +// (line.find("bond types") != std::string::npos) || +// (line.find("angle types") != std::string::npos) || +// (line.find("dihedral types") != std::string::npos); +// } + +// void LAMMPSMolFormat::read_header(Frame& frame) { +// assert(current_section_ == HEADER); - while (!file_.eof()) { - auto line = file_.readline(); - auto content = line; - split_comment(content); - if (content.empty() || is_unused_header(content)) { - // Nothing to do - } else if (content.find("atoms") != std::string::npos) { - natoms_ = read_header_integer(content, "atoms"); - } else if (content.find("bonds") != std::string::npos) { - nbonds_ = read_header_integer(content, "bonds"); - } // else if angles - else { - // End of the header, get the section and break - current_section_ = get_section(line); - assert (current_section_ != NOT_A_SECTION); - break; - } - } -} - -size_t LAMMPSDataFormat::read_header_integer(string_view line, const std::string& context) { - auto splitted = split(line, ' '); - if (splitted.size() < 2) { - throw format_error( - "invalid header value: expected ' {}', got '{}'", context, line - ); - } - return parse(splitted[0]); -} - -static std::unordered_set IGNORED_SECTIONS = { - "Fragments", "Diameters", "Special Bond Count", "Special Bonds", "Shake Flags", "Shake Atoms", "Shake Bond Types" -}; - -LAMMPSMolFormat::section_t LAMMPSDataFormat::get_section(string_view line) { - auto comment = split_comment(line); - auto section = trim(line); - if (section == "Coords") { - return COORDS; - } else if (section == "Bonds") { - return BONDS; - } else if (section == "Types") { - return VELOCITIES; - } else if (section == "Masses") { - return MASSES; - } else if (section == "Charges") { - return CHARGES; - } else if (IGNORED_SECTIONS.find(section) != IGNORED_SECTION.end()) { - return IGNORED; - } - else { - return NOT_A_SECTION; - } -} - -void LAMMPSMolFormat::read_coords(Frame& frame) { - assert(current_section_ == COORDS); - if (natoms_ == 0) { - throw format_error("missing atoms count in header"); - } - - frame.resize(natoms_); - auto positions = frame.positions(); - auto residues = std::unordered_map(); - - size_t n = 0; - while (n < natoms_ && !file._eof()) { - auto line = file_.readline(); - auto comment = split_comment(line); - if (line.empty()) {continue;} - - auto splitted = split(line, ' '); - auto index = parse(splitted[0]); - auto x = parse(splitted[1]); - auto y = parse(splitted[2]); - auto z = parse(splitted[3]); - frame[index] = Atom(""); - positions[index][0] = x; - positions[index][1] = y; - positions[index][2] = z; - - if (!comment.empty()) { - // Read the first string after the comment, and use it as atom name - auto name = split(comment, ' ')[0]; - frame[index].set_name(name); - } - - n++; - } - - get_next_section(); -} - -void LAMMPSMolFormat::read_masses(Frame& frame) { - assert(current_section_ == MASSES); - if (natom_types_ == 0) { - throw format_error("missing atom types count in header"); - } - size_t n = 0; - auto line = file_.readline(); - split_comment(line); - while (line.size() == 2 && !file_.eof()) { - auto line = file_.readline(); - split_comment(line); - if (line.empty()) {continue;} - - auto splitted = split(line, ' '); - if (splitted.size() != 2) { - throw format_error("bad mass specification '{}'", line); - } - - auto index = splitted[0]; - auto mass = parse(splitted[1]); - frame[index].set_mass(mass); - n++; - } - - get_next_section(); -} - -void LAMMPSMolFormat::read_types() { - assert(current_section_ == TYPES); - if (natom_types_ == 0) { - throw format_error("missing atom types count in header"); - } - size_t n = 0; - auto line = file_.readline(); - while (line.size() == 2 && !file_.eof()) { - auto line = file_.readline(); - split_comment(line); - if (line.empty()) {continue;} - - auto splitted = split(line, ' '); - if (splitted.size() != 2) { - throw format_error("bad mass specification '{}'", line); - } - - auto type = splitted[0]; - auto type = parse(splitted[1]); - frame[index].set_type(type); - n++; - } - - get_next_section(); -} - -void LAMMPSMolFormat::read_bonds(Frame& frame) { - assert(current_section_ == BONDS); - if (nbonds_ == 0) { - throw format_error("missing bonds count in header"); - } - size_t n = 0; - while (n < nbonds_ && !file_.eof()) { - auto line = file_.readline(); - split_comment(line); - if (line.empty()) {continue;} - - auto splitted = split(line, ' '); - if (splitted.size() != 4) { - throw format_error("bad bond specification '{}'", line); - } - // LAMMPS use 1-based indexing - auto i = parse(splitted[2]) - 1; - auto j = parse(splitted[3]) - 1; - frame.add_bond(i, j); - n++; - } - - if (file_.eof() && n < nbonds_) { - throw format_error("end of file found before getting all bonds"); - } - - get_next_section(); -} - -void LAMMPSMolFormat::get_next_section() { - while (!file_.eof()) { - auto line = file_.readline(); - if (!line.empty()) { - auto section = get_section(line); - if (section == NOT_A_SECTION) { - throw format_error("expected section name, got '{}'", line); - } else { - current_section_ = section; - break; - } - } - } -} - -void LAMMPSDataFormat::skip_to_next_section() { - while (!file_.eof()) { - auto line = file_.readline(); - if (!line.empty()) { - auto section = get_section(line); - if (section == NOT_A_SECTION) { - continue; - } else { - current_section_ = section; - break; - } - } - } -} - -void write_next(const Frame& frame) override -{ - if (file_.tellpos() != 0) { - throw format_error("LAMMPS molecule template can only contain one frame"); - } - - auto types = DataTypes(frame.topology()); - - write_header(frame); - write_coords(frame); - write_types(frame); - write_charges(frame); - auto& topology = frame.topology(); - write_bonds(topogology); - -} - -void LAMMPSMolFormat::write_header(const Frame& frame) -{ - file_.print("LAMMPS molecule template -- generated by chemfiles\n\n"); - file_.print("{} atoms\n", frame.size()); - file_.print("{} bonds\n", frame.topology().bonds().size()); - - file_.print("\n"); -} - -void LAMMPSMolFormat::write_coords(const Frame& frame) -{ - file_.print("Coords\n\n"); - auto positions = frame.positions(); - for (size_t i=0; i guess_molecules(const Frame& frame) { - // Initialize the molids vector with each atom in its own molecule - auto molids = std::vector(); - molids.reserve(frame.size()); - for (size_t i=0; i molids[j]) { - new_id = molids[j]; - old_id = molids[i]; - } - - for (auto& molid: molids) { - if (molid == old_id) { - molid = new_id; - } - } - } - - // Make sure the molids are consecutive - std::unordered_map molids_mapping; - for (auto& molid: molids) { - auto it = molids_mapping.find(molid); - if (it != molids_mapping.end()) { - molid = it->second; - } else { - // We've not found this id yet - auto new_id = molids_mapping.size(); - molids_mapping.insert({molid, new_id}); - molid = new_id; - } - } - - return molids; -} - -optional LAMMPSDataFormat::forward() { - // LAMMPS moltemplate only supports one step, so always act like there is only one - auto position = file_.tellpos(); - if (position == 0) { - // advance the pointer for the next call - file_.readline(); - return position; - } else { - return nullopt; - } -} +// while (!file_.eof()) { +// auto line = file_.readline(); +// auto content = line; +// split_comment(content); +// if (content.empty() || is_unused_header(content)) { +// // Nothing to do +// } else if (content.find("atoms") != std::string::npos) { +// natoms_ = read_header_integer(content, "atoms"); +// } else if (content.find("bonds") != std::string::npos) { +// nbonds_ = read_header_integer(content, "bonds"); +// } // else if angles +// else { +// // End of the header, get the section and break +// current_section_ = get_section(line); +// assert (current_section_ != NOT_A_SECTION); +// break; +// } +// } +// } + +// size_t LAMMPSDataFormat::read_header_integer(string_view line, const std::string& context) { +// auto splitted = split(line, ' '); +// if (splitted.size() < 2) { +// throw format_error( +// "invalid header value: expected ' {}', got '{}'", context, line +// ); +// } +// return parse(splitted[0]); +// } + +// static std::unordered_set IGNORED_SECTIONS = { +// "Fragments", "Diameters", "Special Bond Count", "Special Bonds", "Shake Flags", "Shake Atoms", "Shake Bond Types" +// }; + +// LAMMPSMolFormat::section_t LAMMPSDataFormat::get_section(string_view line) { +// auto comment = split_comment(line); +// auto section = trim(line); +// if (section == "Coords") { +// return COORDS; +// } else if (section == "Bonds") { +// return BONDS; +// } else if (section == "Types") { +// return VELOCITIES; +// } else if (section == "Masses") { +// return MASSES; +// } else if (section == "Charges") { +// return CHARGES; +// } else if (IGNORED_SECTIONS.find(section) != IGNORED_SECTION.end()) { +// return IGNORED; +// } +// else { +// return NOT_A_SECTION; +// } +// } + +// void LAMMPSMolFormat::read_coords(Frame& frame) { +// assert(current_section_ == COORDS); +// if (natoms_ == 0) { +// throw format_error("missing atoms count in header"); +// } + +// frame.resize(natoms_); +// auto positions = frame.positions(); +// auto residues = std::unordered_map(); + +// size_t n = 0; +// while (n < natoms_ && !file._eof()) { +// auto line = file_.readline(); +// auto comment = split_comment(line); +// if (line.empty()) {continue;} + +// auto splitted = split(line, ' '); +// auto index = parse(splitted[0]); +// auto x = parse(splitted[1]); +// auto y = parse(splitted[2]); +// auto z = parse(splitted[3]); +// frame[index] = Atom(""); +// positions[index][0] = x; +// positions[index][1] = y; +// positions[index][2] = z; + +// if (!comment.empty()) { +// // Read the first string after the comment, and use it as atom name +// auto name = split(comment, ' ')[0]; +// frame[index].set_name(name); +// } + +// n++; +// } + +// get_next_section(); +// } + +// void LAMMPSMolFormat::read_masses(Frame& frame) { +// assert(current_section_ == MASSES); +// if (natom_types_ == 0) { +// throw format_error("missing atom types count in header"); +// } +// size_t n = 0; +// auto line = file_.readline(); +// split_comment(line); +// while (line.size() == 2 && !file_.eof()) { +// auto line = file_.readline(); +// split_comment(line); +// if (line.empty()) {continue;} + +// auto splitted = split(line, ' '); +// if (splitted.size() != 2) { +// throw format_error("bad mass specification '{}'", line); +// } + +// auto index = splitted[0]; +// auto mass = parse(splitted[1]); +// frame[index].set_mass(mass); +// n++; +// } + +// get_next_section(); +// } + +// void LAMMPSMolFormat::read_types() { +// assert(current_section_ == TYPES); +// if (natom_types_ == 0) { +// throw format_error("missing atom types count in header"); +// } +// size_t n = 0; +// auto line = file_.readline(); +// while (line.size() == 2 && !file_.eof()) { +// auto line = file_.readline(); +// split_comment(line); +// if (line.empty()) {continue;} + +// auto splitted = split(line, ' '); +// if (splitted.size() != 2) { +// throw format_error("bad mass specification '{}'", line); +// } + +// auto type = splitted[0]; +// auto type = parse(splitted[1]); +// frame[index].set_type(type); +// n++; +// } + +// get_next_section(); +// } + +// void LAMMPSMolFormat::read_bonds(Frame& frame) { +// assert(current_section_ == BONDS); +// if (nbonds_ == 0) { +// throw format_error("missing bonds count in header"); +// } +// size_t n = 0; +// while (n < nbonds_ && !file_.eof()) { +// auto line = file_.readline(); +// split_comment(line); +// if (line.empty()) {continue;} + +// auto splitted = split(line, ' '); +// if (splitted.size() != 4) { +// throw format_error("bad bond specification '{}'", line); +// } +// // LAMMPS use 1-based indexing +// auto i = parse(splitted[2]) - 1; +// auto j = parse(splitted[3]) - 1; +// frame.add_bond(i, j); +// n++; +// } + +// if (file_.eof() && n < nbonds_) { +// throw format_error("end of file found before getting all bonds"); +// } + +// get_next_section(); +// } + +// void LAMMPSMolFormat::get_next_section() { +// while (!file_.eof()) { +// auto line = file_.readline(); +// if (!line.empty()) { +// auto section = get_section(line); +// if (section == NOT_A_SECTION) { +// throw format_error("expected section name, got '{}'", line); +// } else { +// current_section_ = section; +// break; +// } +// } +// } +// } + +// void LAMMPSDataFormat::skip_to_next_section() { +// while (!file_.eof()) { +// auto line = file_.readline(); +// if (!line.empty()) { +// auto section = get_section(line); +// if (section == NOT_A_SECTION) { +// continue; +// } else { +// current_section_ = section; +// break; +// } +// } +// } +// } + +// void write_next(const Frame& frame) override +// { +// if (file_.tellpos() != 0) { +// throw format_error("LAMMPS molecule template can only contain one frame"); +// } + +// auto types = DataTypes(frame.topology()); + +// write_header(frame); +// write_coords(frame); +// write_types(frame); +// write_charges(frame); +// auto& topology = frame.topology(); +// write_bonds(topogology); + +// } + +// void LAMMPSMolFormat::write_header(const Frame& frame) +// { +// file_.print("LAMMPS molecule template -- generated by chemfiles\n\n"); +// file_.print("{} atoms\n", frame.size()); +// file_.print("{} bonds\n", frame.topology().bonds().size()); + +// file_.print("\n"); +// } + +// void LAMMPSMolFormat::write_coords(const Frame& frame) +// { +// file_.print("Coords\n\n"); +// auto positions = frame.positions(); +// for (size_t i=0; i guess_molecules(const Frame& frame) { +// // Initialize the molids vector with each atom in its own molecule +// auto molids = std::vector(); +// molids.reserve(frame.size()); +// for (size_t i=0; i molids[j]) { +// new_id = molids[j]; +// old_id = molids[i]; +// } + +// for (auto& molid: molids) { +// if (molid == old_id) { +// molid = new_id; +// } +// } +// } + +// // Make sure the molids are consecutive +// std::unordered_map molids_mapping; +// for (auto& molid: molids) { +// auto it = molids_mapping.find(molid); +// if (it != molids_mapping.end()) { +// molid = it->second; +// } else { +// // We've not found this id yet +// auto new_id = molids_mapping.size(); +// molids_mapping.insert({molid, new_id}); +// molid = new_id; +// } +// } + +// return molids; +// } + +// optional LAMMPSDataFormat::forward() { +// // LAMMPS moltemplate only supports one step, so always act like there is only one +// auto position = file_.tellpos(); +// if (position == 0) { +// // advance the pointer for the next call +// file_.readline(); +// return position; +// } else { +// return nullopt; +// } +// } diff --git a/tests/external/CMakeLists.txt b/tests/external/CMakeLists.txt deleted file mode 100644 index b0f693aee..000000000 --- a/tests/external/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -add_library(test_helpers STATIC helpers.cpp) -target_include_directories(test_helpers PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -# We can not direcly link to chemfiles, but we still need it's headers -target_include_directories(test_helpers PUBLIC $) - -if(${EMSCRIPTEN}) - target_link_libraries(test_helpers nodefs.js) -endif() diff --git a/tests/external/catch.hpp b/tests/external/catch.hpp deleted file mode 100644 index db1fed3b9..000000000 --- a/tests/external/catch.hpp +++ /dev/null @@ -1,17966 +0,0 @@ -/* - * Catch v2.13.8 - * Generated: 2022-01-03 21:20:09.589503 - * ---------------------------------------------------------- - * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -// start catch.hpp - - -#define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 13 -#define CATCH_VERSION_PATCH 8 - -#ifdef __clang__ -# pragma clang system_header -#elif defined __GNUC__ -# pragma GCC system_header -#endif - -// start catch_suppress_warnings.h - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(push) -# pragma warning(disable: 161 1682) -# else // __ICC -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wswitch-enum" -# pragma clang diagnostic ignored "-Wcovered-switch-default" -# endif -#elif defined __GNUC__ - // Because REQUIREs trigger GCC's -Wparentheses, and because still - // supported version of g++ have only buggy support for _Pragmas, - // Wparentheses have to be suppressed globally. -# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details - -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wpadded" -#endif -// end catch_suppress_warnings.h -#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) -# define CATCH_IMPL -# define CATCH_CONFIG_ALL_PARTS -#endif - -// In the impl file, we want to have access to all parts of the headers -// Can also be used to sanely support PCHs -#if defined(CATCH_CONFIG_ALL_PARTS) -# define CATCH_CONFIG_EXTERNAL_INTERFACES -# if defined(CATCH_CONFIG_DISABLE_MATCHERS) -# undef CATCH_CONFIG_DISABLE_MATCHERS -# endif -# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) -# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER -# endif -#endif - -#if !defined(CATCH_CONFIG_IMPL_ONLY) -// start catch_platform.h - -// See e.g.: -// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html -#ifdef __APPLE__ -# include -# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ - (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) -# define CATCH_PLATFORM_MAC -# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) -# define CATCH_PLATFORM_IPHONE -# endif - -#elif defined(linux) || defined(__linux) || defined(__linux__) -# define CATCH_PLATFORM_LINUX - -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) -# define CATCH_PLATFORM_WINDOWS -#endif - -// end catch_platform.h - -#ifdef CATCH_IMPL -# ifndef CLARA_CONFIG_MAIN -# define CLARA_CONFIG_MAIN_NOT_DEFINED -# define CLARA_CONFIG_MAIN -# endif -#endif - -// start catch_user_interfaces.h - -namespace Catch { - unsigned int rngSeed(); -} - -// end catch_user_interfaces.h -// start catch_tag_alias_autoregistrar.h - -// start catch_common.h - -// start catch_compiler_capabilities.h - -// Detect a number of compiler features - by compiler -// The following features are defined: -// -// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? -// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? -// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? -// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? -// **************** -// Note to maintainers: if new toggles are added please document them -// in configuration.md, too -// **************** - -// In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -#ifdef __cplusplus - -# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -# define CATCH_CPP14_OR_GREATER -# endif - -# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define CATCH_CPP17_OR_GREATER -# endif - -#endif - -// Only GCC compiler should be used in this block, so other compilers trying to -// mask themselves as GCC should be ignored. -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) - -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) - -#endif - -#if defined(__clang__) - -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) - -// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug -// which results in calls to destructors being emitted for each temporary, -// without a matching initialization. In practice, this can result in something -// like `std::string::~string` being called on an uninitialized value. -// -// For example, this code will likely segfault under IBM XL: -// ``` -// REQUIRE(std::string("12") + "34" == "1234") -// ``` -// -// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. -# if !defined(__ibmxl__) && !defined(__CUDACC__) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ -# endif - -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") - -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) - -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// Assume that non-Windows platforms support posix signals by default -#if !defined(CATCH_PLATFORM_WINDOWS) - #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS -#endif - -//////////////////////////////////////////////////////////////////////////////// -// We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) - #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -#endif - -#ifdef __OS400__ -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# define CATCH_CONFIG_COLOUR_NONE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Android somehow still does not support std::to_string -#if defined(__ANDROID__) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Not all Windows environments support SEH properly -#if defined(__MINGW32__) -# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH -#endif - -//////////////////////////////////////////////////////////////////////////////// -// PS4 -#if defined(__ORBIS__) -# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Cygwin -#ifdef __CYGWIN__ - -// Required for some versions of Cygwin to declare gettimeofday -// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin -# define _BSD_SOURCE -// some versions of cygwin (most) do not support std::to_string. Use the libstd check. -// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 -# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ - && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) - -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING - -# endif -#endif // __CYGWIN__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#if defined(_MSC_VER) - -// Universal Windows platform does not support SEH -// Or console colours (or console at all...) -# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) -# define CATCH_CONFIG_COLOUR_NONE -# else -# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH -# endif - -# if !defined(__clang__) // Handle Clang masquerading for msvc - -// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ -// _MSVC_TRADITIONAL == 0 means new conformant preprocessor -// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -# endif // MSVC_TRADITIONAL - -// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) -# endif // __clang__ - -#endif // _MSC_VER - -#if defined(_REENTRANT) || defined(_MSC_VER) -// Enable async processing, as -pthread is specified or no additional linking is required -# define CATCH_INTERNAL_CONFIG_USE_ASYNC -#endif // _MSC_VER - -//////////////////////////////////////////////////////////////////////////////// -// Check if we are compiled with -fno-exceptions or equivalent -#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) -# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED -#endif - -//////////////////////////////////////////////////////////////////////////////// -// DJGPP -#ifdef __DJGPP__ -# define CATCH_INTERNAL_CONFIG_NO_WCHAR -#endif // __DJGPP__ - -//////////////////////////////////////////////////////////////////////////////// -// Embarcadero C++Build -#if defined(__BORLANDC__) - #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// Use of __COUNTER__ is suppressed during code analysis in -// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly -// handled by it. -// Otherwise all supported compilers support COUNTER macro, -// but user still might want to turn it off -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// RTX is a special version of Windows that is real time. -// This means that it is detected as Windows, but does not provide -// the same set of capabilities as real Windows does. -#if defined(UNDER_RTSS) || defined(RTX64_BUILD) - #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH - #define CATCH_INTERNAL_CONFIG_NO_ASYNC - #define CATCH_CONFIG_COLOUR_NONE -#endif - -#if !defined(_GLIBCXX_USE_C99_MATH_TR1) -#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER -#endif - -// Various stdlib support checks that require __has_include -#if defined(__has_include) - // Check if string_view is available and usable - #if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW - #endif - - // Check if optional is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if byte is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # include - # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) - # define CATCH_INTERNAL_CONFIG_CPP17_BYTE - # endif - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if variant is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 - # include - # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # define CATCH_CONFIG_NO_CPP17_VARIANT - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__clang__) && (__clang_major__ < 8) - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // defined(__has_include) - -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) -# define CATCH_CONFIG_COUNTER -#endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH -#endif -// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_CONFIG_POSIX_SIGNALS -#endif -// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. -#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) -# define CATCH_CONFIG_WCHAR -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) -# define CATCH_CONFIG_CPP11_TO_STRING -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) -# define CATCH_CONFIG_CPP17_OPTIONAL -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) -# define CATCH_CONFIG_CPP17_STRING_VIEW -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) -# define CATCH_CONFIG_CPP17_VARIANT -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) -# define CATCH_CONFIG_CPP17_BYTE -#endif - -#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) -# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) -# define CATCH_CONFIG_NEW_CAPTURE -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -# define CATCH_CONFIG_DISABLE_EXCEPTIONS -#endif - -#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) -# define CATCH_CONFIG_POLYFILL_ISNAN -#endif - -#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) -# define CATCH_CONFIG_USE_ASYNC -#endif - -#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) -# define CATCH_CONFIG_ANDROID_LOGWRITE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) -# define CATCH_CONFIG_GLOBAL_NEXTAFTER -#endif - -// Even if we do not think the compiler has that warning, we still have -// to provide a macro that can be used by the code. -#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION -#endif -#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS -#endif - -// The goal of this macro is to avoid evaluation of the arguments, but -// still have the compiler warn on problems inside... -#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) -#endif - -#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#elif defined(__clang__) && (__clang_major__ < 5) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -#define CATCH_TRY if ((true)) -#define CATCH_CATCH_ALL if ((false)) -#define CATCH_CATCH_ANON(type) if ((false)) -#else -#define CATCH_TRY try -#define CATCH_CATCH_ALL catch (...) -#define CATCH_CATCH_ANON(type) catch (type) -#endif - -#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) -#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#endif - -// end catch_compiler_capabilities.h -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#include -#include -#include - -// We need a dummy global operator<< so we can bring it into Catch namespace later -struct Catch_global_namespace_dummy {}; -std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); - -namespace Catch { - - struct CaseSensitive { enum Choice { - Yes, - No - }; }; - - class NonCopyable { - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable && ) = delete; - NonCopyable& operator = ( NonCopyable const& ) = delete; - NonCopyable& operator = ( NonCopyable && ) = delete; - - protected: - NonCopyable(); - virtual ~NonCopyable(); - }; - - struct SourceLineInfo { - - SourceLineInfo() = delete; - SourceLineInfo( char const* _file, std::size_t _line ) noexcept - : file( _file ), - line( _line ) - {} - - SourceLineInfo( SourceLineInfo const& other ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo( SourceLineInfo&& ) noexcept = default; - SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; - - bool empty() const noexcept { return file[0] == '\0'; } - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; - - char const* file; - std::size_t line; - }; - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - - // Bring in operator<< from global namespace into Catch namespace - // This is necessary because the overload of operator<< above makes - // lookup stop at namespace Catch - using ::operator<<; - - // Use this in variadic streaming macros to allow - // >> +StreamEndStop - // as well as - // >> stuff +StreamEndStop - struct StreamEndStop { - std::string operator+() const; - }; - template - T const& operator + ( T const& value, StreamEndStop ) { - return value; - } -} - -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) - -// end catch_common.h -namespace Catch { - - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; - -} // end namespace Catch - -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - -// end catch_tag_alias_autoregistrar.h -// start catch_test_registry.h - -// start catch_interfaces_testcase.h - -#include - -namespace Catch { - - class TestSpec; - - struct ITestInvoker { - virtual void invoke () const = 0; - virtual ~ITestInvoker(); - }; - - class TestCase; - struct IConfig; - - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - bool isThrowSafe( TestCase const& testCase, IConfig const& config ); - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} - -// end catch_interfaces_testcase.h -// start catch_stringref.h - -#include -#include -#include -#include - -namespace Catch { - - /// A non-owning string class (similar to the forthcoming std::string_view) - /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. - class StringRef { - public: - using size_type = std::size_t; - using const_iterator = const char*; - - private: - static constexpr char const* const s_empty = ""; - - char const* m_start = s_empty; - size_type m_size = 0; - - public: // construction - constexpr StringRef() noexcept = default; - - StringRef( char const* rawChars ) noexcept; - - constexpr StringRef( char const* rawChars, size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} - - StringRef( std::string const& stdString ) noexcept - : m_start( stdString.c_str() ), - m_size( stdString.size() ) - {} - - explicit operator std::string() const { - return std::string(m_start, m_size); - } - - public: // operators - auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != (StringRef const& other) const noexcept -> bool { - return !(*this == other); - } - - auto operator[] ( size_type index ) const noexcept -> char { - assert(index < m_size); - return m_start[index]; - } - - public: // named queries - constexpr auto empty() const noexcept -> bool { - return m_size == 0; - } - constexpr auto size() const noexcept -> size_type { - return m_size; - } - - // Returns the current start pointer. If the StringRef is not - // null-terminated, throws std::domain_exception - auto c_str() const -> char const*; - - public: // substrings and searches - // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, size()). - // If start > size(), then the substring is empty. - auto substr( size_type start, size_type length ) const noexcept -> StringRef; - - // Returns the current start pointer. May not be null-terminated. - auto data() const noexcept -> char const*; - - constexpr auto isNullTerminated() const noexcept -> bool { - return m_start[m_size] == '\0'; - } - - public: // iterators - constexpr const_iterator begin() const { return m_start; } - constexpr const_iterator end() const { return m_start + m_size; } - }; - - auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; - auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - - constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); - } -} // namespace Catch - -constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { - return Catch::StringRef( rawChars, size ); -} - -// end catch_stringref.h -// start catch_preprocessor.hpp - - -#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ -#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) - -#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ -// MSVC needs more evaluations -#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) -#else -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) -#endif - -#define CATCH_REC_END(...) -#define CATCH_REC_OUT - -#define CATCH_EMPTY() -#define CATCH_DEFER(id) id CATCH_EMPTY() - -#define CATCH_REC_GET_END2() 0, CATCH_REC_END -#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 -#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 -#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT -#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) -#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) - -#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) - -#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) - -// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, -// and passes userdata as the first parameter to each invocation, -// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) -#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) -#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ -#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ -#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) -#else -// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) -#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) -#endif - -#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ -#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) - -#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) - -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) -#else -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) -#endif - -#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ - CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) - -#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) -#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) -#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) -#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) -#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) -#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) -#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) -#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) -#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) -#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) -#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) - -#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N - -#define INTERNAL_CATCH_TYPE_GEN\ - template struct TypeList {};\ - template\ - constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ - template class...> struct TemplateTypeList{};\ - template class...Cs>\ - constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ - template\ - struct append;\ - template\ - struct rewrap;\ - template class, typename...>\ - struct create;\ - template class, typename>\ - struct convert;\ - \ - template \ - struct append { using type = T; };\ - template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ - struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ - template< template class L1, typename...E1, typename...Rest>\ - struct append, TypeList, Rest...> { using type = L1; };\ - \ - template< template class Container, template class List, typename...elems>\ - struct rewrap, List> { using type = TypeList>; };\ - template< template class Container, template class List, class...Elems, typename...Elements>\ - struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ - \ - template