Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b3b620b
docs: add Doxygen/Breathe infrastructure for C++ API reference
Apr 22, 2026
3ef8876
fix(conf.py): guard doxygen call — skip gracefully when not installed
Apr 22, 2026
f158a62
ci: add docs-build job (Doxygen + Sphinx/Breathe HTML)
Apr 22, 2026
8c525f3
docs: add Doxygen API docs to dirwalk.hpp
CV-GPhL Apr 23, 2026
dcf1bad
docs: add Doxygen API docs to fileutil.hpp
CV-GPhL Apr 23, 2026
cb65e83
docs: add Doxygen API docs to gz.hpp
CV-GPhL Apr 23, 2026
8d24f7c
docs: add Doxygen API docs to glob.hpp
CV-GPhL Apr 23, 2026
841e31a
docs: add Doxygen API docs to fstream.hpp
CV-GPhL Apr 23, 2026
11c0b1f
docs: add Doxygen API docs to input.hpp
CV-GPhL Apr 23, 2026
f0a3c07
docs: add Doxygen API docs to logger.hpp
CV-GPhL Apr 23, 2026
d2a70be
docs: add Doxygen API docs to pdb_id.hpp
CV-GPhL Apr 23, 2026
c91837d
docs: add Doxygen API docs to util.hpp
CV-GPhL Apr 23, 2026
cc81831
docs: clarify try_pdbid param type in dirwalk.hpp
CV-GPhL Apr 23, 2026
3311bc8
docs: add Doxygen API docs to span.hpp
CV-GPhL Apr 23, 2026
6a29bed
docs: add Doxygen API docs to iterator.hpp
CV-GPhL Apr 23, 2026
639c8f9
docs: add Doxygen API docs to fail.hpp
CV-GPhL Apr 23, 2026
5d8322c
docs: add Doxygen API docs to atof.hpp
CV-GPhL Apr 23, 2026
0328c07
docs: add Doxygen API docs to atox.hpp
CV-GPhL Apr 23, 2026
f86313a
docs: add Doxygen API docs to version.hpp
CV-GPhL Apr 23, 2026
177b2d0
docs: fix invalid @param and duplicate description in input/logger
CV-GPhL Apr 23, 2026
40e2315
docs: add Doxygen API docs to addends.hpp
CV-GPhL Apr 23, 2026
21c8825
docs: add Doxygen API docs to bond_idx.hpp
CV-GPhL Apr 23, 2026
9177c0c
docs: add Doxygen API docs to dsn6.hpp
CV-GPhL Apr 23, 2026
4d858a7
docs: add Doxygen API docs to enumstr.hpp
CV-GPhL Apr 23, 2026
842b2f3
docs: add Doxygen API docs to sprintf.hpp
CV-GPhL Apr 23, 2026
15a4ba1
docs: add Doxygen API docs to stats.hpp
CV-GPhL Apr 23, 2026
2a34cda
docs: add Doxygen API docs to pymol_select.hpp
CV-GPhL Apr 23, 2026
669df83
docs: restrict psimpl docs to Node base class in pymol_select.hpp
CV-GPhL Apr 23, 2026
79267e5
docs: add api.rst entries for PR 10 (io-utils and stragglers)
CV-GPhL Apr 23, 2026
63db7c5
ci: add breathe to AppVeyor pip install
CV-GPhL Apr 23, 2026
2685107
Merge branch 'master' into api-docs/io-utils
wojdyr Apr 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,94 @@ Map and Grid Data
.. doxygenfile:: grid.hpp
:project: gemmi

I/O and Filesystem Utilities
------------------------------

File and directory traversal, gzip support, stream abstractions, PDB path
utilities, and general-purpose string and container helpers.

*(Full documentation added in PR 10.)*

.. doxygenfile:: dirwalk.hpp
:project: gemmi

.. doxygenfile:: fileutil.hpp
:project: gemmi

.. doxygenfile:: fstream.hpp
:project: gemmi

.. doxygenfile:: gz.hpp
:project: gemmi

.. doxygenfile:: input.hpp
:project: gemmi

.. doxygenfile:: glob.hpp
:project: gemmi

.. doxygenfile:: logger.hpp
:project: gemmi

.. doxygenfile:: pdb_id.hpp
:project: gemmi

.. doxygenfile:: util.hpp
:project: gemmi

Low-level Primitives
--------------------

Span and range views, custom iterators, error utilities, fast numeric parsing,
and version information.

*(Full documentation added in PR 10.)*

.. doxygenfile:: span.hpp
:project: gemmi

.. doxygenfile:: iterator.hpp
:project: gemmi

.. doxygenfile:: fail.hpp
:project: gemmi

.. doxygenfile:: atof.hpp
:project: gemmi

.. doxygenfile:: atox.hpp
:project: gemmi

.. doxygenfile:: version.hpp
:project: gemmi

Miscellaneous
-------------

Anomalous scattering addends, bond index, DSN6/BRIX map format, enum/string
conversions, string formatting, statistics, and PyMOL selection language.

*(Full documentation added in PR 10.)*

.. doxygenfile:: addends.hpp
:project: gemmi

.. doxygenfile:: bond_idx.hpp
:project: gemmi

.. doxygenfile:: dsn6.hpp
:project: gemmi

.. doxygenfile:: enumstr.hpp
:project: gemmi

.. doxygenfile:: sprintf.hpp
:project: gemmi

.. doxygenfile:: stats.hpp
:project: gemmi

.. doxygenfile:: pymol_select.hpp
Scattering, Math, and Geometry
-------------------------------

Expand Down
18 changes: 18 additions & 0 deletions include/gemmi/addends.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,34 @@

namespace gemmi {

/// @brief Container for anomalous scattering correction addends
/// Stores addend values for each element used in density and structure factor calculations.
struct Addends {
std::array<float, (int)El::END> values = {};

/// @brief Set the addend value for a given element
/// @param el the chemical element
/// @param val the addend value to set
void set(Element el, float val) { values[el.ordinal()] = val; }

/// @brief Get the addend value for a given element
/// @param el the chemical element
/// @return the addend value for the element
float get(Element el) const { return values[el.ordinal()]; }

/// @brief Get the total number of elements in the array
/// @return the size of the addends array
size_t size() const { return values.size(); }

/// @brief Clear all addend values to zero
void clear() {
for (size_t i = 0; i != size(); ++i)
values[i] = 0.;
}

/// @brief Subtract atomic number Z from each element's addend value
/// Optionally preserves hydrogen and deuterium values.
/// @param except_hydrogen if true, skip subtracting from hydrogen and deuterium
void subtract_z(bool except_hydrogen=false) {
for (int z = 2; z < (int)El::D; ++z)
values[z] -= z;
Expand Down
16 changes: 16 additions & 0 deletions include/gemmi/atof.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,15 @@

namespace gemmi {

/// @brief Result type from fast_float::from_chars.
using fast_float::from_chars_result;

/// @brief Fast locale-independent string to double conversion with range.
/// @param start pointer to string start
/// @param end pointer to one-past-end
/// @param d reference to output double
/// @return from_chars_result with ptr field pointing to first non-converted character
/// @details Skips leading whitespace and optional '+' sign before parsing.
inline from_chars_result fast_from_chars(const char* start, const char* end, double& d) {
while (start < end && is_space(*start))
++start;
Expand All @@ -21,6 +28,11 @@ inline from_chars_result fast_from_chars(const char* start, const char* end, dou
return fast_float::from_chars(start, end, d);
}

/// @brief Fast locale-independent string to double conversion (null-terminated).
/// @param start pointer to null-terminated string
/// @param d reference to output double
/// @return from_chars_result with ptr field pointing to first non-converted character
/// @details Skips leading whitespace and optional '+' sign before parsing.
inline from_chars_result fast_from_chars(const char* start, double& d) {
while (is_space(*start))
++start;
Expand All @@ -29,6 +41,10 @@ inline from_chars_result fast_from_chars(const char* start, double& d) {
return fast_float::from_chars(start, start + std::strlen(start), d);
}

/// @brief Fast locale-independent string to double conversion with optional end pointer.
/// @param p pointer to string (null-terminated)
/// @param endptr optional pointer to receive end of parsed string (may be nullptr)
/// @return the parsed double value
inline double fast_atof(const char* p, const char** endptr=nullptr) {
double d = 0;
auto result = fast_from_chars(p, d);
Expand Down
45 changes: 41 additions & 4 deletions include/gemmi/atox.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@

namespace gemmi {

// equivalent of std::isspace for C locale (no handling of EOF)
/// @brief Locale-independent isspace equivalent (C locale only, no EOF handling).
/// @param c character to test
/// @return true if c is whitespace (tab, newline, vertical tab, form feed, carriage return, or space)
inline bool is_space(char c) {
static const std::uint8_t table[256] = { // 1 for 9-13 and 32
0,0,0,0,0,0,0,0, 0,1,1,1,1,1,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
Expand All @@ -34,42 +36,64 @@ inline bool is_space(char c) {
return table[(std::uint8_t)c] != 0;
}

// equivalent of std::isblank for C locale (no handling of EOF)
/// @brief Locale-independent isblank equivalent (C locale only, no EOF handling).
/// @param c character to test
/// @return true if c is space or tab
inline bool is_blank(char c) {
return c == ' ' || c == '\t';
}

// equivalent of std::isdigit for C locale (no handling of EOF)
/// @brief Locale-independent isdigit equivalent (C locale only, no EOF handling).
/// @param c character to test
/// @return true if c is a decimal digit (0-9)
inline bool is_digit(char c) {
return c >= '0' && c <= '9';
}

/// @brief Skip leading blank characters (spaces and tabs).
/// @param p pointer to string (may be null)
/// @return pointer to first non-blank character, or end of string
inline const char* skip_blank(const char* p) {
if (p)
while (is_blank(*p))
++p;
return p;
}

/// @brief Skip word (non-whitespace characters).
/// @param p pointer to string (may be null)
/// @return pointer to first whitespace or null terminator
inline const char* skip_word(const char* p) {
if (p)
while (*p != '\0' && !is_space(*p))
++p;
return p;
}

/// @brief Read a word from the start of a line (skipping leading blanks).
/// @param line pointer to start of line
/// @return string containing the word
inline std::string read_word(const char* line) {
line = skip_blank(line);
return std::string(line, skip_word(line));
}

/// @brief Read a word from the start of a line with end pointer.
/// @param line pointer to start of line
/// @param endptr pointer to receive pointer to character after word
/// @return string containing the word
inline std::string read_word(const char* line, const char** endptr) {
line = skip_blank(line);
*endptr = skip_word(line);
return std::string(line, *endptr);
}

// no checking for overflow
/// @brief Convert string to signed integer (locale-independent, no overflow checking).
/// @param p pointer to string
/// @param checked if true, throw std::invalid_argument if string is not a valid integer
/// @param length max length to parse (0 = unlimited)
/// @return the converted integer
/// @throws std::invalid_argument if checked=true and string is invalid
inline int string_to_int(const char* p, bool checked, size_t length=0) {
int mult = -1;
int n = 0;
Expand Down Expand Up @@ -98,10 +122,19 @@ inline int string_to_int(const char* p, bool checked, size_t length=0) {
return mult * n;
}

/// @brief Convert std::string to signed integer (checked version).
/// @param str the string to convert
/// @param checked if true, throw std::invalid_argument if string is not a valid integer
/// @return the converted integer
/// @throws std::invalid_argument if checked=true and string is invalid
inline int string_to_int(const std::string& str, bool checked) {
return string_to_int(str.c_str(), checked);
}

/// @brief Fast atoi-like conversion with optional end pointer (unchecked, allows partial parse).
/// @param p pointer to null-terminated string
/// @param endptr optional pointer to receive pointer to first non-digit character
/// @return the converted integer
inline int simple_atoi(const char* p, const char** endptr=nullptr) {
int mult = -1;
int n = 0;
Expand All @@ -120,6 +153,10 @@ inline int simple_atoi(const char* p, const char** endptr=nullptr) {
return mult * n;
}

/// @brief Fast atoi-like conversion without sign (positive only, unchecked).
/// @param p pointer to null-terminated string
/// @param endptr optional pointer to receive pointer to first non-digit character
/// @return the converted non-negative integer
inline int no_sign_atoi(const char* p, const char** endptr=nullptr) {
int n = 0;
while (is_space(*p))
Expand Down
47 changes: 42 additions & 5 deletions include/gemmi/bond_idx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,63 @@

namespace gemmi {

/// @brief Index for efficient bond topology queries in a crystal structure
/// Enables checking atom connectivity and calculating graph distances, including
/// handling of atoms in different unit cell images.
struct BondIndex {
const Model& model;

/// @brief Represents an atom and whether it's in the same unit cell image
struct AtomImage {
int atom_serial;
bool same_image;
int atom_serial; ///< Serial number of the atom
bool same_image; ///< True if atom is in the same unit cell image as reference
/// @brief Equality comparison
/// @param o the other AtomImage to compare
/// @return true if both serial and image flag match
bool operator==(const AtomImage& o) const {
return atom_serial == o.atom_serial && same_image == o.same_image;
}
};
std::map<int, std::vector<AtomImage>> index;

/// @brief Construct a BondIndex for the given model
/// @details Initializes the index with all atoms from the model.
/// Fails if duplicate atom serial numbers are found.
/// @param model_ the crystallographic model to index
BondIndex(const Model& model_) : model(model_) {
for (const_CRA cra : model.all())
if (!index.emplace(cra.atom->serial, std::vector<AtomImage>()).second)
fail("duplicated serial numbers");
}

/// @brief Add a unidirectional bond link between two atoms
/// @details Does not add the reverse link (a->b without b->a).
/// Does not add duplicate links.
/// @param a the first atom
/// @param b the second atom
/// @param same_image whether both atoms are in the same unit cell image
void add_oneway_link(const Atom& a, const Atom& b, bool same_image) {
std::vector<AtomImage>& list_a = index.at(a.serial);
AtomImage ai{b.serial, same_image};
if (!in_vector(ai, list_a))
list_a.push_back(ai);
}

/// @brief Add a bidirectional bond link between two atoms
/// @param a the first atom
/// @param b the second atom
/// @param same_image whether both atoms are in the same unit cell image
void add_link(const Atom& a, const Atom& b, bool same_image) {
add_oneway_link(a, b, same_image);
add_oneway_link(b, a, same_image);
}

// add_monomer_bonds() is not aware of modifications associated with links.
// Modifications that add bonds are rare, but to be more correct, use bonds
// from topology (Topo::bonds).
/// @brief Add bonds from monomer library restraints to the index
/// @details Populates the bond index with standard bonds defined for each
/// residue type in the monomer library. Does not handle custom
/// bond modifications; for more accurate results, use bonds from
/// topology (Topo::bonds) which accounts for modifications.
/// @param monlib the monomer library containing bond definitions
void add_monomer_bonds(MonLib& monlib) {
for (const Chain& chain : model.chains)
for (const Residue& res : chain.residues) {
Expand All @@ -65,10 +89,23 @@ struct BondIndex {
}
}

/// @brief Check if two atoms are directly bonded
/// @param a the first atom
/// @param b the second atom
/// @param same_image whether both atoms should be in the same unit cell image
/// @return true if a direct bond exists between the atoms
bool are_linked(const Atom& a, const Atom& b, bool same_image) const {
return in_vector({b.serial, same_image}, index.at(a.serial));
}

/// @brief Calculate the minimum graph distance between two atoms
/// @details Uses breadth-first search to find the shortest path through bonds.
/// Automatically handles transitions between unit cell images.
/// @param a the starting atom
/// @param b the target atom
/// @param same_image whether both atoms should be in the same unit cell image
/// @param max_distance maximum distance to search (default 4)
/// @return the graph distance in bonds, or (max_distance + 1) if no path exists
int graph_distance(const Atom& a, const Atom& b, bool same_image,
int max_distance=4) const {
std::vector<AtomImage> neighbors(1, {a.serial, true});
Expand Down
Loading
Loading