// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "MonomerLibrary.h" #include #include #include #include "gemmi/numb.hpp" // as_number #include "gemmi/read_cif.hpp" // read_cif_from_memory // Generated by CMakeLists.txt from monomers/*.cif (MonomerLibraryData.cpp in the build tree). extern const EmbeddedMonomerFile embedded_monomer_files[]; extern const size_t embedded_monomer_file_count; namespace { struct MetalRow { gemmi::El metal, ligand; int coordination; double distance, std; int count; }; struct EmbeddedLibrary { gemmi::MonLib monlib; std::vector metals; // metals.cif }; EmbeddedLibrary ReadEmbeddedMonomerLibrary() { EmbeddedLibrary lib; gemmi::MonLib &monlib = lib.monlib; for (size_t i = 0; i < embedded_monomer_file_count; i++) { const EmbeddedMonomerFile &f = embedded_monomer_files[i]; std::string text; for (size_t k = 0; k < f.n_pieces; k++) text += f.pieces[k]; gemmi::cif::Document doc = gemmi::read_cif_from_memory(text.data(), text.size(), f.name); if (std::strcmp(f.name, "ener_lib.cif") == 0) monlib.ener_lib.read(doc); else if (std::strcmp(f.name, "metals.cif") == 0) { for (const auto &row : doc.blocks.at(0).find("_metal_coordination.", {"metal", "ligand", "coordination", "distance", "std", "count"})) lib.metals.push_back({gemmi::find_element(row[0].c_str()), gemmi::find_element(row[1].c_str()), gemmi::cif::as_int(row[2]), gemmi::cif::as_number(row[3]), gemmi::cif::as_number(row[4]), gemmi::cif::as_int(row[5])}); } else monlib.read_monomer_doc(doc); } return lib; } const EmbeddedLibrary &Library() { static const EmbeddedLibrary lib = ReadEmbeddedMonomerLibrary(); return lib; } } // namespace const gemmi::MonLib &EmbeddedMonomerLibrary() { return Library().monlib; } bool EmbeddedMetalDistance(gemmi::El metal, gemmi::El ligand, int coordination, double &distance, double &sigma) { // the rows of the pair resting on enough sites: the nearest at or below the coordination and above it const MetalRow *lo = nullptr, *hi = nullptr; for (const MetalRow &r : Library().metals) { if (r.metal != metal || r.ligand != ligand || r.count < METAL_MIN_SITES) continue; if (r.coordination <= coordination && (lo == nullptr || r.coordination > lo->coordination)) lo = &r; if (r.coordination > coordination && (hi == nullptr || r.coordination < hi->coordination)) hi = &r; } if (lo == nullptr && hi == nullptr) return false; if (lo != nullptr && (lo->coordination == coordination || hi == nullptr)) { distance = lo->distance; sigma = lo->std; } else if (lo == nullptr) { distance = hi->distance; sigma = hi->std; } else { // between two coordination numbers: linear in the coordination const double t = static_cast(coordination - lo->coordination) / (hi->coordination - lo->coordination); distance = lo->distance + t * (hi->distance - lo->distance); sigma = lo->std + t * (hi->std - lo->std); } sigma = std::max(sigma, METAL_MIN_SIGMA); return true; } bool InEmbeddedMonomerLibrary(const std::string &code) { return EmbeddedMonomerLibrary().monomers.count(code) > 0; }