Commit a96b1e07 by Maarten L. Hekkelman

Merge remote-tracking branch 'origin/develop' into trunk

parents f48c31bc d85ab93a
...@@ -27,11 +27,12 @@ cmake_minimum_required(VERSION 3.23) ...@@ -27,11 +27,12 @@ cmake_minimum_required(VERSION 3.23)
# set the project name # set the project name
project( project(
libcifpp libcifpp
VERSION 7.0.4 VERSION 7.0.5
LANGUAGES CXX) LANGUAGES CXX)
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(FindAtomic)
include(CheckFunctionExists) include(CheckFunctionExists)
include(CheckIncludeFiles) include(CheckIncludeFiles)
include(CheckLibraryExists) include(CheckLibraryExists)
...@@ -41,8 +42,6 @@ include(GenerateExportHeader) ...@@ -41,8 +42,6 @@ include(GenerateExportHeader)
include(CTest) include(CTest)
include(FetchContent) include(FetchContent)
message(STATUS "DESTDIR is '${DESTDIR}'")
# When building with ninja-multiconfig, build both debug and release by default # When building with ninja-multiconfig, build both debug and release by default
if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config") if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
set(CMAKE_CROSS_CONFIGS "Debug;Release") set(CMAKE_CROSS_CONFIGS "Debug;Release")
...@@ -188,10 +187,6 @@ if(GXX_LIBSTDCPP) ...@@ -188,10 +187,6 @@ if(GXX_LIBSTDCPP)
endif() endif()
endif() endif()
set(CMAKE_THREAD_PREFER_PTHREAD)
set(THREADS_PREFER_PTHREAD_FLAG)
find_package(Threads)
if(MSVC) if(MSVC)
# Avoid linking the shared library of zlib Search ZLIB_ROOT first if it is # Avoid linking the shared library of zlib Search ZLIB_ROOT first if it is
# set. # set.
...@@ -221,6 +216,7 @@ if(MSVC) ...@@ -221,6 +216,7 @@ if(MSVC)
endif() endif()
find_package(ZLIB QUIET) find_package(ZLIB QUIET)
find_package(Threads)
if(NOT ZLIB_FOUND) if(NOT ZLIB_FOUND)
message(FATAL_ERROR "The zlib development files were not found you this system, please install them and try again (hint: on debian/ubuntu use apt-get install zlib1g-dev)") message(FATAL_ERROR "The zlib development files were not found you this system, please install them and try again (hint: on debian/ubuntu use apt-get install zlib1g-dev)")
...@@ -260,6 +256,8 @@ if(CIFPP_RECREATE_SYMOP_DATA) ...@@ -260,6 +256,8 @@ if(CIFPP_RECREATE_SYMOP_DATA)
add_executable(symop-map-generator add_executable(symop-map-generator
"${CMAKE_CURRENT_SOURCE_DIR}/src/symop-map-generator.cpp") "${CMAKE_CURRENT_SOURCE_DIR}/src/symop-map-generator.cpp")
target_compile_features(symop-map-generator PUBLIC cxx_std_20)
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp
COMMAND COMMAND
...@@ -331,7 +329,7 @@ set(project_headers ...@@ -331,7 +329,7 @@ set(project_headers
include/cif++/validate.hpp include/cif++/validate.hpp
) )
add_library(cifpp STATIC) add_library(cifpp)
add_library(cifpp::cifpp ALIAS cifpp) add_library(cifpp::cifpp ALIAS cifpp)
target_sources(cifpp target_sources(cifpp
...@@ -371,7 +369,7 @@ target_include_directories( ...@@ -371,7 +369,7 @@ target_include_directories(
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
PRIVATE "${BOOST_REGEX_INCLUDE_DIR}" "${EIGEN_INCLUDE_DIR}") PRIVATE "${BOOST_REGEX_INCLUDE_DIR}" "${EIGEN_INCLUDE_DIR}")
target_link_libraries(cifpp PUBLIC Threads::Threads ZLIB::ZLIB) target_link_libraries(cifpp PUBLIC Threads::Threads ZLIB::ZLIB std::atomic)
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
target_link_options(cifpp PRIVATE -undefined dynamic_lookup) target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
...@@ -444,6 +442,10 @@ if(CIFPP_DATA_DIR) ...@@ -444,6 +442,10 @@ if(CIFPP_DATA_DIR)
target_compile_definitions(cifpp PUBLIC DATA_DIR="${CIFPP_DATA_DIR}") target_compile_definitions(cifpp PUBLIC DATA_DIR="${CIFPP_DATA_DIR}")
endif() endif()
if(NOT PROJECT_IS_TOP_LEVEL)
set(CIFPP_SHARE_DIR ${CIFPP_DATA_DIR} PARENT_SCOPE)
endif()
if(UNIX AND NOT BUILD_FOR_CCP4) if(UNIX AND NOT BUILD_FOR_CCP4)
if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local") if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local")
set(CIFPP_CACHE_DIR set(CIFPP_CACHE_DIR
...@@ -464,75 +466,72 @@ else() ...@@ -464,75 +466,72 @@ else()
unset(CIFPP_CACHE_DIR) unset(CIFPP_CACHE_DIR)
endif() endif()
# Avoid full installation in case we are not the top level target # Install rules
if(PROJECT_IS_TOP_LEVEL OR BUILD_FOR_CCP4) install(TARGETS cifpp
# Install rules
install(TARGETS cifpp
EXPORT cifpp EXPORT cifpp
FILE_SET cifpp_headers DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) FILE_SET cifpp_headers DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
if(MSVC AND BUILD_SHARED_LIBS) if(MSVC AND BUILD_SHARED_LIBS)
install( install(
FILES $<TARGET_PDB_FILE:cifpp> FILES $<TARGET_PDB_FILE:cifpp>
DESTINATION ${CMAKE_INSTALL_LIBDIR} DESTINATION ${CMAKE_INSTALL_LIBDIR}
OPTIONAL) OPTIONAL)
endif() endif()
# Clean up old config files (with old names) # Clean up old config files (with old names)
file(GLOB OLD_CONFIG_FILES file(GLOB OLD_CONFIG_FILES
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppConfig*.cmake ${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppConfig*.cmake
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppTargets*.cmake) ${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppTargets*.cmake)
if(OLD_CONFIG_FILES) if(OLD_CONFIG_FILES)
message( message(
STATUS "Installation will remove old config files: ${OLD_CONFIG_FILES}") STATUS "Installation will remove old config files: ${OLD_CONFIG_FILES}")
install(CODE "file(REMOVE ${OLD_CONFIG_FILES})") install(CODE "file(REMOVE ${OLD_CONFIG_FILES})")
endif() endif()
install(EXPORT cifpp install(EXPORT cifpp
NAMESPACE cifpp:: NAMESPACE cifpp::
FILE "cifpp-targets.cmake" FILE "cifpp-targets.cmake"
DESTINATION lib/cmake/cifpp) DESTINATION lib/cmake/cifpp)
if(CIFPP_DATA_DIR AND CIFPP_DOWNLOAD_CCD) install(
install(
FILES ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_ddl.dic FILES ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_ddl.dic
${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic
${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_ma.dic ${COMPONENTS_CIF} ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_ma.dic
DESTINATION ${CMAKE_INSTALL_DATADIR}/libcifpp) DESTINATION ${CMAKE_INSTALL_DATADIR}/libcifpp)
endif()
set(CONFIG_TEMPLATE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cifpp-config.cmake.in) if(CIFPP_DATA_DIR AND CIFPP_DOWNLOAD_CCD)
install(FILES ${COMPONENTS_CIF}
DESTINATION ${CMAKE_INSTALL_DATADIR}/libcifpp)
endif()
configure_package_config_file( set(CONFIG_TEMPLATE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cifpp-config.cmake.in)
configure_package_config_file(
${CONFIG_TEMPLATE_FILE} ${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake ${CONFIG_TEMPLATE_FILE} ${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake
INSTALL_DESTINATION lib/cmake/cifpp INSTALL_DESTINATION lib/cmake/cifpp
PATH_VARS CIFPP_DATA_DIR) PATH_VARS CIFPP_DATA_DIR)
install( install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake" FILES "${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake" "${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake"
DESTINATION lib/cmake/cifpp) DESTINATION lib/cmake/cifpp)
set_target_properties( set_target_properties(
cifpp cifpp
PROPERTIES VERSION ${PROJECT_VERSION} PROPERTIES VERSION ${PROJECT_VERSION}
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
INTERFACE_cifpp_MAJOR_VERSION ${PROJECT_VERSION_MAJOR}) INTERFACE_cifpp_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})
set_property( set_property(
TARGET cifpp TARGET cifpp
APPEND APPEND
PROPERTY COMPATIBLE_INTERFACE_STRING cifpp_MAJOR_VERSION) PROPERTY COMPATIBLE_INTERFACE_STRING cifpp_MAJOR_VERSION)
write_basic_package_version_file( write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake" "${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake"
VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion) COMPATIBILITY AnyNewerVersion)
else()
# Set this variable so that consumers can find the files in rsrc
set(CIFPP_SHARE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/rsrc PARENT_SCOPE)
endif()
if(BUILD_TESTING) if(BUILD_TESTING)
add_subdirectory(test) add_subdirectory(test)
......
...@@ -52,7 +52,7 @@ int main(int argc, char *argv[]) ...@@ -52,7 +52,7 @@ int main(int argc, char *argv[])
if (file.empty()) if (file.empty())
{ {
std::cerr << "Empty file" << std::endl; std::cerr << "Empty file\n";
exit(1); exit(1);
} }
...@@ -66,8 +66,8 @@ int main(int argc, char *argv[]) ...@@ -66,8 +66,8 @@ int main(int argc, char *argv[])
auto n = atom_site.count(cif::key("label_atom_id") == "OXT"); auto n = atom_site.count(cif::key("label_atom_id") == "OXT");
std::cout << "File contains " << atom_site.size() << " atoms of which " std::cout << "File contains " << atom_site.size() << " atoms of which "
<< n << (n == 1 ? " is" : " are") << " OXT" << std::endl << n << (n == 1 ? " is" : " are") << " OXT\n"
<< "residues with an OXT are:" << std::endl; << "residues with an OXT are:\n";
// Loop over all atoms with atom-id "OXT" and print out some info. // Loop over all atoms with atom-id "OXT" and print out some info.
// That info is extracted using structured binding in C++ // That info is extracted using structured binding in C++
...@@ -76,7 +76,7 @@ int main(int argc, char *argv[]) ...@@ -76,7 +76,7 @@ int main(int argc, char *argv[])
cif::key("label_atom_id") == "OXT", cif::key("label_atom_id") == "OXT",
"label_asym_id", "label_comp_id", "label_seq_id")) "label_asym_id", "label_comp_id", "label_seq_id"))
{ {
std::cout << asym << ' ' << comp << ' ' << seqnr << std::endl; std::cout << asym << ' ' << comp << ' ' << seqnr << '\n';
} }
return 0; return 0;
......
Version 7.0.5
- Fix case where category index was not updated for updated value
Version 7.0.4 Version 7.0.4
- Do not install headers and library in case we're not the top project - Do not install headers and library in case we're not the top project
......
# Simple check to see if we need a library for std::atomic
if(TARGET std::atomic)
return()
endif()
cmake_minimum_required(VERSION 3.10)
include(CMakePushCheckState)
include(CheckIncludeFileCXX)
include(CheckCXXSourceRuns)
cmake_push_check_state()
check_include_file_cxx("atomic" _CXX_ATOMIC_HAVE_HEADER)
mark_as_advanced(_CXX_ATOMIC_HAVE_HEADER)
set(code [[
#include <atomic>
int main(int argc, char** argv) {
std::atomic<long long> s;
++s;
return 0;
}
]])
check_cxx_source_runs("${code}" _CXX_ATOMIC_BUILTIN)
if(_CXX_ATOMIC_BUILTIN)
set(_found 1)
else()
list(APPEND CMAKE_REQUIRED_LIBRARIES atomic)
list(APPEND FOLLY_LINK_LIBRARIES atomic)
check_cxx_source_runs("${code}" _CXX_ATOMIC_LIB_NEEDED)
if (NOT _CXX_ATOMIC_LIB_NEEDED)
message(FATAL_ERROR "unable to link C++ std::atomic code: you may need \
to install GNU libatomic")
else()
set(_found 1)
endif()
endif()
if(_found)
add_library(std::atomic INTERFACE IMPORTED)
set_property(TARGET std::atomic APPEND PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_14)
if(_CXX_ATOMIC_BUILTIN)
# Nothing to add...
elseif(_CXX_ATOMIC_LIB_NEEDED)
set_target_properties(std::atomic PROPERTIES IMPORTED_LIBNAME atomic)
set(STDCPPATOMIC_LIBRARY atomic)
endif()
endif()
cmake_pop_check_state()
set(Atomic_FOUND ${_found} CACHE BOOL "TRUE if we can run a program using std::atomic" FORCE)
mark_as_advanced(Atomic_FOUND)
if(Atomic_FIND_REQUIRED AND NOT Atomic_FOUND)
message(FATAL_ERROR "Cannot run simple program using std::atomic")
endif()
...@@ -13,7 +13,7 @@ int main(int argc, char *argv[]) ...@@ -13,7 +13,7 @@ int main(int argc, char *argv[])
exit(1); exit(1);
} }
cif::file file = cif::pdb::read(argv[1]); cif::file file(argv[1]);
if (file.empty()) if (file.empty())
{ {
......
...@@ -138,7 +138,7 @@ struct compound_bond ...@@ -138,7 +138,7 @@ struct compound_bond
/// This information is derived from the CDD by default. /// This information is derived from the CDD by default.
/// ///
/// To create compounds, you use the factory method. You can add your own /// To create compounds, you use the factory method. You can add your own
/// compound definitions by calling the addExtraComponents function and /// compound definitions by calling the push_dictionary function and
/// pass it a valid CCD formatted file. /// pass it a valid CCD formatted file.
class compound class compound
......
...@@ -1285,6 +1285,19 @@ condition operator==(const key &key, const std::optional<T> &v) ...@@ -1285,6 +1285,19 @@ condition operator==(const key &key, const std::optional<T> &v)
} }
/** /**
* @brief Create a condition to search any item for a value @a v if @a v contains a value
* compare to null if not.
*/
template <typename T>
condition operator!=(const key &key, const std::optional<T> &v)
{
if (v.has_value())
return condition(new detail::not_condition_impl(condition(new detail::key_equals_condition_impl({ key.m_item_name, *v }))));
else
return condition(new detail::not_condition_impl(condition(new detail::key_is_empty_condition_impl(key.m_item_name))));
}
/**
* @brief Operator to create a boolean opposite of the condition in @a rhs * @brief Operator to create a boolean opposite of the condition in @a rhs
*/ */
inline condition operator not(condition &&rhs) inline condition operator not(condition &&rhs)
......
...@@ -378,7 +378,7 @@ struct item_handle ...@@ -378,7 +378,7 @@ struct item_handle
template <typename T> template <typename T>
item_handle &operator=(T &&value) item_handle &operator=(T &&value)
{ {
assign_value(item{ "", std::move(value) }.value()); assign_value(item{ "", std::forward<T>(value) }.value());
return *this; return *this;
} }
......
...@@ -350,7 +350,12 @@ class atom ...@@ -350,7 +350,12 @@ class atom
std::string get_pdb_ins_code() const { return get_property("pdbx_PDB_ins_code"); } ///< Return the pdb_ins_code property std::string get_pdb_ins_code() const { return get_property("pdbx_PDB_ins_code"); } ///< Return the pdb_ins_code property
/// Return true if this atom is an alternate /// Return true if this atom is an alternate
bool is_alternate() const { return not get_label_alt_id().empty(); } bool is_alternate() const
{
if (auto alt_id = get_label_alt_id(); alt_id.empty() or alt_id == ".")
return false;
return true;
}
/// Convenience method to return a string that might be ID in PDB space /// Convenience method to return a string that might be ID in PDB space
std::string pdb_id() const std::string pdb_id() const
...@@ -550,6 +555,9 @@ class residue ...@@ -550,6 +555,9 @@ class residue
/// \brief Return true if this residue has alternate atoms /// \brief Return true if this residue has alternate atoms
bool has_alternate_atoms() const; bool has_alternate_atoms() const;
/// \brief Return true if this residue has alternate atoms for the atom \a atomID
bool has_alternate_atoms_for(const std::string &atomID) const;
/// \brief Return the list of unique alt ID's present in this residue /// \brief Return the list of unique alt ID's present in this residue
std::set<std::string> get_alternate_ids() const; std::set<std::string> get_alternate_ids() const;
...@@ -572,6 +580,10 @@ class residue ...@@ -572,6 +580,10 @@ class residue
m_auth_seq_id == rhs.m_auth_seq_id); m_auth_seq_id == rhs.m_auth_seq_id);
} }
/// @brief Create a new atom and add it to the list
/// @return newly created atom
virtual atom create_new_atom(atom_type inType, const std::string &inAtomID, point inLocation);
protected: protected:
/** @cond */ /** @cond */
residue() {} residue() {}
...@@ -672,6 +684,8 @@ class monomer : public residue ...@@ -672,6 +684,8 @@ class monomer : public residue
return m_polymer == rhs.m_polymer and m_index == rhs.m_index; return m_polymer == rhs.m_polymer and m_index == rhs.m_index;
} }
atom create_new_atom(atom_type inType, const std::string &inAtomID, point inLocation) override;
private: private:
const polymer *m_polymer; const polymer *m_polymer;
std::size_t m_index; std::size_t m_index;
...@@ -1091,6 +1105,9 @@ class structure ...@@ -1091,6 +1105,9 @@ class structure
/// \brief emplace the moved atom @a atom /// \brief emplace the moved atom @a atom
atom &emplace_atom(atom &&atom); atom &emplace_atom(atom &&atom);
/// \brief Reorder atom_site atoms based on 'natural' ordering
void reorder_atoms();
private: private:
friend polymer; friend polymer;
friend residue; friend residue;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -1321,7 +1321,7 @@ void category::update_value(const std::vector<row_handle> &rows, std::string_vie ...@@ -1321,7 +1321,7 @@ void category::update_value(const std::vector<row_handle> &rows, std::string_vie
std::string oldValue{ parent[item_name].text() }; std::string oldValue{ parent[item_name].text() };
std::string value{ value_provider(oldValue) }; std::string value{ value_provider(oldValue) };
parent.assign(colIx, value, false); update_value(parent.get_row(), colIx, value, false, false);
for (auto &&[childCat, linked] : m_child_links) for (auto &&[childCat, linked] : m_child_links)
{ {
...@@ -1444,8 +1444,7 @@ void category::update_value(row *row, uint16_t item, std::string_view value, boo ...@@ -1444,8 +1444,7 @@ void category::update_value(row *row, uint16_t item, std::string_view value, boo
// before updating // before updating
bool reinsert = false; bool reinsert = false;
if (updateLinked and // an update of an Item's value if (m_index != nullptr and key_item_indices().count(item))
m_index != nullptr and key_item_indices().count(item))
{ {
reinsert = m_index->find(*this, row); reinsert = m_index->find(*this, row);
if (reinsert) if (reinsert)
...@@ -1698,6 +1697,12 @@ void category::swap_item(uint16_t item_ix, row_handle &a, row_handle &b) ...@@ -1698,6 +1697,12 @@ void category::swap_item(uint16_t item_ix, row_handle &a, row_handle &b)
auto &ra = *a.m_row; auto &ra = *a.m_row;
auto &rb = *b.m_row; auto &rb = *b.m_row;
while (ra.size() <= item_ix)
ra.emplace_back("");
while (rb.size() <= item_ix)
rb.emplace_back("");
std::swap(ra.at(item_ix), rb.at(item_ix)); std::swap(ra.at(item_ix), rb.at(item_ix));
} }
......
...@@ -496,7 +496,7 @@ compound *compound_factory_impl::create(const std::string &id) ...@@ -496,7 +496,7 @@ compound *compound_factory_impl::create(const std::string &id)
m_index = parser.index_datablocks(); m_index = parser.index_datablocks();
if (cif::VERBOSE > 1) if (cif::VERBOSE > 1)
std::cout << " done" << std::endl; std::cout << " done\n";
// reload the resource, perhaps this should be improved... // reload the resource, perhaps this should be improved...
if (m_file.empty()) if (m_file.empty())
...@@ -519,7 +519,7 @@ compound *compound_factory_impl::create(const std::string &id) ...@@ -519,7 +519,7 @@ compound *compound_factory_impl::create(const std::string &id)
parser.parse_single_datablock(id, m_index); parser.parse_single_datablock(id, m_index);
if (cif::VERBOSE > 1) if (cif::VERBOSE > 1)
std::cout << " done" << std::endl; std::cout << " done\n";
if (not file.empty()) if (not file.empty())
{ {
...@@ -545,20 +545,20 @@ class local_compound_factory_impl : public compound_factory_impl ...@@ -545,20 +545,20 @@ class local_compound_factory_impl : public compound_factory_impl
: compound_factory_impl(next) : compound_factory_impl(next)
, m_local_file(file) , m_local_file(file)
{ {
const std::regex peptideRx("(?:[lmp]-)?peptide", std::regex::icase); // const std::regex peptideRx("(?:[lmp]-)?peptide", std::regex::icase);
for (const auto &[id, name, threeLetterCode, group] : // for (const auto &[id, name, threeLetterCode, group] :
file["comp_list"]["chem_comp"].rows<std::string, std::string, std::string, std::string>("id", "name", "three_letter_code", "group")) // file["comp_list"]["chem_comp"].rows<std::string, std::string, std::string, std::string>("id", "name", "three_letter_code", "group"))
{ // {
auto &rdb = m_local_file["comp_" + id]; // auto &rdb = m_local_file["comp_" + id];
if (rdb.empty()) // if (rdb.empty())
{ // {
std::cerr << "Missing data in restraint file for id " + id + '\n'; // // std::cerr << "Missing data in restraint file for id " + id + '\n';
continue; // continue;
} // }
construct_compound(rdb, id, name, threeLetterCode, group); // construct_compound(rdb, id, name, threeLetterCode, group);
} // }
} }
compound *create(const std::string &id) override; compound *create(const std::string &id) override;
......
...@@ -49,6 +49,7 @@ void datablock::set_validator(const validator *v) ...@@ -49,6 +49,7 @@ void datablock::set_validator(const validator *v)
} }
catch (const std::exception &) catch (const std::exception &)
{ {
m_validator = nullptr;
throw_with_nested(std::runtime_error("Error while setting validator in datablock " + m_name)); throw_with_nested(std::runtime_error("Error while setting validator in datablock " + m_name));
} }
} }
......
...@@ -31,11 +31,32 @@ namespace cif ...@@ -31,11 +31,32 @@ namespace cif
{ {
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// TODO: This is wrong. A validator should be assigned to datablocks,
// not to a file. Since audit_conform is a category specifying the
// content of a datablock. Not the entire file.
void file::set_validator(const validator *v) void file::set_validator(const validator *v)
{ {
m_validator = v; m_validator = v;
for (auto &db : *this) for (bool first = true; auto &db : *this)
{
try
{
db.set_validator(v); db.set_validator(v);
}
catch (const std::exception &e)
{
if (first)
throw;
// Accept failure on secondary datablocks
// now that many mmCIF files have invalid
// restraint data concatenated.
std::cerr << e.what() << '\n';
}
first = false;
}
} }
bool file::is_valid() const bool file::is_valid() const
...@@ -125,7 +146,8 @@ void file::load_dictionary(std::string_view name) ...@@ -125,7 +146,8 @@ void file::load_dictionary(std::string_view name)
bool file::contains(std::string_view name) const bool file::contains(std::string_view name) const
{ {
return std::find_if(begin(), end(), [name](const datablock &db) { return iequals(db.name(), name); }) != end(); return std::find_if(begin(), end(), [name](const datablock &db)
{ return iequals(db.name(), name); }) != end();
} }
datablock &file::operator[](std::string_view name) datablock &file::operator[](std::string_view name)
......
...@@ -352,9 +352,42 @@ EntityType residue::entity_type() const ...@@ -352,9 +352,42 @@ EntityType residue::entity_type() const
void residue::add_atom(atom &atom) void residue::add_atom(atom &atom)
{ {
// update atom since it is now part of this residue
m_atoms.push_back(atom); m_atoms.push_back(atom);
} }
atom residue::create_new_atom(atom_type inType, const std::string &inAtomID, point inLocation)
{
auto &db = m_structure->get_datablock();
auto &atom_site = db["atom_site"];
auto ai = atom_site.emplace({
{ "group_PDB", "HETATM" },
{ "id", atom_site.get_unique_id("") },
{ "type_symbol", atom_type_traits(inType).symbol() },
{ "label_entity_id", get_entity_id() },
{ "label_atom_id", inAtomID },
{ "label_asym_id", m_asym_id },
{ "label_alt_id", "." },
{ "label_comp_id", m_compound_id },
{ "label_seq_id", m_seq_id },
{ "auth_asym_id", m_auth_asym_id },
{ "auth_atom_id", inAtomID },
{ "auth_comp_id", m_compound_id },
{ "auth_seq_id", m_auth_seq_id },
{ "occupancy", 1.0f, 2 },
{ "pdbx_PDB_model_num", m_structure->get_model_nr() },
});
atom a(db, *ai);
m_atoms.push_back(a);
a.set_location(inLocation);
return a;
}
std::vector<atom> residue::unique_atoms() const std::vector<atom> residue::unique_atoms() const
{ {
std::vector<atom> result; std::vector<atom> result;
...@@ -455,6 +488,12 @@ bool residue::has_alternate_atoms() const ...@@ -455,6 +488,12 @@ bool residue::has_alternate_atoms() const
{ return atom.is_alternate(); }) != m_atoms.end(); { return atom.is_alternate(); }) != m_atoms.end();
} }
bool residue::has_alternate_atoms_for(const std::string &atomID) const
{
return std::find_if(m_atoms.begin(), m_atoms.end(), [atomID](const atom &atom)
{ return atom.get_label_atom_id() == atomID and atom.is_alternate(); }) != m_atoms.end();
}
std::set<std::string> residue::get_atom_ids() const std::set<std::string> residue::get_atom_ids() const
{ {
std::set<std::string> ids; std::set<std::string> ids;
...@@ -673,25 +712,26 @@ float monomer::omega() const ...@@ -673,25 +712,26 @@ float monomer::omega() const
} }
const std::map<std::string, std::vector<std::string>> kChiAtomsMap = { const std::map<std::string, std::vector<std::string>> kChiAtomsMap = {
{"ASP", {"CG", "OD1"}}, { "ASP", { "CG", "OD1" } },
{"ASN", {"CG", "OD1"}}, { "ASN", { "CG", "OD1" } },
{"ARG", {"CG", "CD", "NE", "CZ"}}, { "ARG", { "CG", "CD", "NE", "CZ" } },
{"HIS", {"CG", "ND1"}}, { "HIS", { "CG", "ND1" } },
{"GLN", {"CG", "CD", "OE1"}}, { "GLN", { "CG", "CD", "OE1" } },
{"GLU", {"CG", "CD", "OE1"}}, { "GLU", { "CG", "CD", "OE1" } },
{"SER", {"OG"}}, { "SER", { "OG" } },
{"THR", {"OG1"}}, { "THR", { "OG1" } },
{"LYS", {"CG", "CD", "CE", "NZ"}}, { "LYS", { "CG", "CD", "CE", "NZ" } },
{"TYR", {"CG", "CD1"}}, { "TYR", { "CG", "CD1" } },
{"PHE", {"CG", "CD1"}}, { "PHE", { "CG", "CD1" } },
{"LEU", {"CG", "CD1"}}, { "LEU", { "CG", "CD1" } },
{"TRP", {"CG", "CD1"}}, { "TRP", { "CG", "CD1" } },
{"CYS", {"SG"}}, { "CYS", { "SG" } },
{"ILE", {"CG1", "CD1"}}, { "ILE", { "CG1", "CD1" } },
{"MET", {"CG", "SD", "CE"}}, { "MET", { "CG", "SD", "CE" } },
{"MSE", {"CG", "SE", "CE"}}, { "MSE", { "CG", "SE", "CE" } },
{"PRO", {"CG", "CD"}}, { "PRO", { "CG", "CD" } },
{"VAL", {"CG1"}}}; { "VAL", { "CG1" } }
};
std::size_t monomer::nr_of_chis() const std::size_t monomer::nr_of_chis() const
{ {
...@@ -713,7 +753,7 @@ float monomer::chi(std::size_t nr) const ...@@ -713,7 +753,7 @@ float monomer::chi(std::size_t nr) const
auto i = kChiAtomsMap.find(m_compound_id); auto i = kChiAtomsMap.find(m_compound_id);
if (i != kChiAtomsMap.end() and nr < i->second.size()) if (i != kChiAtomsMap.end() and nr < i->second.size())
{ {
std::vector<std::string> atoms{"N", "CA", "CB"}; std::vector<std::string> atoms{ "N", "CA", "CB" };
atoms.insert(atoms.end(), i->second.begin(), i->second.end()); atoms.insert(atoms.end(), i->second.begin(), i->second.end());
...@@ -839,7 +879,8 @@ bool monomer::are_bonded(const monomer &a, const monomer &b, float errorMargin) ...@@ -839,7 +879,8 @@ bool monomer::are_bonded(const monomer &a, const monomer &b, float errorMargin)
a.get_atom_by_atom_id("CA").get_location(), a.get_atom_by_atom_id("CA").get_location(),
a.get_atom_by_atom_id("C").get_location(), a.get_atom_by_atom_id("C").get_location(),
b.get_atom_by_atom_id("N").get_location(), b.get_atom_by_atom_id("N").get_location(),
b.get_atom_by_atom_id("CA").get_location()}; b.get_atom_by_atom_id("CA").get_location()
};
auto distanceCACA = distance(atoms[0], atoms[3]); auto distanceCACA = distance(atoms[0], atoms[3]);
double omega = dihedral_angle(atoms[0], atoms[1], atoms[2], atoms[3]); double omega = dihedral_angle(atoms[0], atoms[1], atoms[2], atoms[3]);
...@@ -880,6 +921,15 @@ bool monomer::is_cis(const monomer &a, const monomer &b) ...@@ -880,6 +921,15 @@ bool monomer::is_cis(const monomer &a, const monomer &b)
return std::abs(omega(a, b)) < 30.0f; return std::abs(omega(a, b)) < 30.0f;
} }
atom monomer::create_new_atom(atom_type inType, const std::string &inAtomID, point inLocation)
{
atom a = residue::create_new_atom(inType, inAtomID, inLocation);
a.set_property("group_PDB", "ATOM");
return a;
}
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// polymer // polymer
...@@ -916,7 +966,7 @@ polymer::polymer(structure &s, const std::string &entityID, const std::string &a ...@@ -916,7 +966,7 @@ polymer::polymer(structure &s, const std::string &entityID, const std::string &a
} }
else if (VERBOSE > 0) else if (VERBOSE > 0)
{ {
monomer m{*this, index, seqID, authSeqID, pdbInsCode, compoundID}; monomer m{ *this, index, seqID, authSeqID, pdbInsCode, compoundID };
std::cerr << "Dropping alternate residue " << m << '\n'; std::cerr << "Dropping alternate residue " << m << '\n';
} }
} }
...@@ -984,7 +1034,6 @@ sugar::sugar(sugar &&rhs) ...@@ -984,7 +1034,6 @@ sugar::sugar(sugar &&rhs)
: residue(std::forward<residue>(rhs)) : residue(std::forward<residue>(rhs))
, m_branch(rhs.m_branch) , m_branch(rhs.m_branch)
{ {
} }
sugar &sugar::operator=(sugar &&rhs) sugar &sugar::operator=(sugar &&rhs)
...@@ -1048,19 +1097,19 @@ cif::mm::atom sugar::add_atom(row_initializer atom_info) ...@@ -1048,19 +1097,19 @@ cif::mm::atom sugar::add_atom(row_initializer atom_info)
auto atom_id = atom_site.get_unique_id(""); auto atom_id = atom_site.get_unique_id("");
atom_info.set_value({"group_PDB", "HETATM"}); atom_info.set_value({ "group_PDB", "HETATM" });
atom_info.set_value({"id", atom_id}); atom_info.set_value({ "id", atom_id });
atom_info.set_value({"label_entity_id", m_branch->get_entity_id()}); atom_info.set_value({ "label_entity_id", m_branch->get_entity_id() });
atom_info.set_value({"label_asym_id", m_branch->get_asym_id()}); atom_info.set_value({ "label_asym_id", m_branch->get_asym_id() });
atom_info.set_value({"label_comp_id", m_compound_id}); atom_info.set_value({ "label_comp_id", m_compound_id });
atom_info.set_value({"label_seq_id", "."}); atom_info.set_value({ "label_seq_id", "." });
atom_info.set_value({"label_alt_id", "."}); atom_info.set_value({ "label_alt_id", "." });
atom_info.set_value({"auth_asym_id", m_branch->get_asym_id()}); atom_info.set_value({ "auth_asym_id", m_branch->get_asym_id() });
atom_info.set_value({"auth_comp_id", m_compound_id}); atom_info.set_value({ "auth_comp_id", m_compound_id });
atom_info.set_value({"auth_seq_id", m_auth_seq_id}); atom_info.set_value({ "auth_seq_id", m_auth_seq_id });
atom_info.set_value({"occupancy", 1.0, 2}); atom_info.set_value({ "occupancy", 1.0, 2 });
atom_info.set_value({"B_iso_or_equiv", 30.0, 2}); atom_info.set_value({ "B_iso_or_equiv", 30.0, 2 });
atom_info.set_value({"pdbx_PDB_model_num", 1}); atom_info.set_value({ "pdbx_PDB_model_num", 1 });
auto row = atom_site.emplace(std::move(atom_info)); auto row = atom_site.emplace(std::move(atom_info));
auto result = m_structure->emplace_atom(db, row); auto result = m_structure->emplace_atom(db, row);
...@@ -1133,7 +1182,8 @@ void branch::link_atoms() ...@@ -1133,7 +1182,8 @@ void branch::link_atoms()
sugar &branch::get_sugar_by_num(int nr) sugar &branch::get_sugar_by_num(int nr)
{ {
auto i = find_if(begin(), end(), [nr](const sugar &s) { return s.num() == nr; }); auto i = find_if(begin(), end(), [nr](const sugar &s)
{ return s.num() == nr; });
if (i == end()) if (i == end())
throw std::out_of_range("Sugar with num " + std::to_string(nr) + " not found in branch " + m_asym_id); throw std::out_of_range("Sugar with num " + std::to_string(nr) + " not found in branch " + m_asym_id);
...@@ -1157,32 +1207,29 @@ sugar &branch::construct_sugar(const std::string &compound_id) ...@@ -1157,32 +1207,29 @@ sugar &branch::construct_sugar(const std::string &compound_id)
auto r = chemComp.find(key("id") == compound_id); auto r = chemComp.find(key("id") == compound_id);
if (r.empty()) if (r.empty())
{ {
chemComp.emplace({ chemComp.emplace({ { "id", compound_id },
{"id", compound_id}, { "name", compound->name() },
{"name", compound->name()}, { "formula", compound->formula() },
{"formula", compound->formula()}, { "formula_weight", compound->formula_weight() },
{"formula_weight", compound->formula_weight()}, { "type", compound->type() } });
{"type", compound->type()}});
} }
sugar &result = emplace_back(*this, compound_id, m_asym_id, static_cast<int>(size() + 1)); sugar &result = emplace_back(*this, compound_id, m_asym_id, static_cast<int>(size() + 1));
db["pdbx_branch_scheme"].emplace({ db["pdbx_branch_scheme"].emplace({ { "asym_id", result.get_asym_id() },
{"asym_id", result.get_asym_id()}, { "entity_id", result.get_entity_id() },
{"entity_id", result.get_entity_id()}, { "num", result.num() },
{"num", result.num()}, { "mon_id", result.get_compound_id() },
{"mon_id", result.get_compound_id()},
{"pdb_asym_id", result.get_asym_id()}, { "pdb_asym_id", result.get_asym_id() },
{"pdb_seq_num", result.num()}, { "pdb_seq_num", result.num() },
{"pdb_mon_id", result.get_compound_id()}, { "pdb_mon_id", result.get_compound_id() },
{"auth_asym_id", result.get_auth_asym_id()}, { "auth_asym_id", result.get_auth_asym_id() },
{"auth_mon_id", result.get_compound_id()}, { "auth_mon_id", result.get_compound_id() },
{"auth_seq_num", result.get_auth_seq_id()}, { "auth_seq_num", result.get_auth_seq_id() },
{"hetero", "n"} { "hetero", "n" } });
});
return result; return result;
} }
...@@ -1200,8 +1247,7 @@ sugar &branch::construct_sugar(const std::string &compound_id, const std::string ...@@ -1200,8 +1247,7 @@ sugar &branch::construct_sugar(const std::string &compound_id, const std::string
auto &pdbx_entity_branch_link = db["pdbx_entity_branch_link"]; auto &pdbx_entity_branch_link = db["pdbx_entity_branch_link"];
auto linkID = pdbx_entity_branch_link.get_unique_id(""); auto linkID = pdbx_entity_branch_link.get_unique_id("");
db["pdbx_entity_branch_link"].emplace({ db["pdbx_entity_branch_link"].emplace({ { "link_id", linkID },
{ "link_id", linkID },
{ "entity_id", get_entity_id() }, { "entity_id", get_entity_id() },
{ "entity_branch_list_num_1", result.num() }, { "entity_branch_list_num_1", result.num() },
{ "comp_id_1", compound_id }, { "comp_id_1", compound_id },
...@@ -1211,8 +1257,7 @@ sugar &branch::construct_sugar(const std::string &compound_id, const std::string ...@@ -1211,8 +1257,7 @@ sugar &branch::construct_sugar(const std::string &compound_id, const std::string
{ "comp_id_2", linked.get_compound_id() }, { "comp_id_2", linked.get_compound_id() },
{ "atom_id_2", linked_atom_id }, { "atom_id_2", linked_atom_id },
{ "leaving_atom_id_2", "." }, { "leaving_atom_id_2", "." },
{ "value_order", "sing" } { "value_order", "sing" } });
});
return result; return result;
} }
...@@ -1321,7 +1366,7 @@ void structure::load_data() ...@@ -1321,7 +1366,7 @@ void structure::load_data()
{ {
auto &polySeqScheme = m_db["pdbx_poly_seq_scheme"]; auto &polySeqScheme = m_db["pdbx_poly_seq_scheme"];
for (const auto &[asym_id, auth_asym_id, entityID] : polySeqScheme.rows<std::string,std::string,std::string>("asym_id", "pdb_strand_id", "entity_id")) for (const auto &[asym_id, auth_asym_id, entityID] : polySeqScheme.rows<std::string, std::string, std::string>("asym_id", "pdb_strand_id", "entity_id"))
{ {
if (m_polymers.empty() or m_polymers.back().get_asym_id() != asym_id or m_polymers.back().get_entity_id() != entityID) if (m_polymers.empty() or m_polymers.back().get_asym_id() != asym_id or m_polymers.back().get_entity_id() != entityID)
m_polymers.emplace_back(*this, entityID, asym_id, auth_asym_id); m_polymers.emplace_back(*this, entityID, asym_id, auth_asym_id);
...@@ -1329,7 +1374,7 @@ void structure::load_data() ...@@ -1329,7 +1374,7 @@ void structure::load_data()
auto &branchScheme = m_db["pdbx_branch_scheme"]; auto &branchScheme = m_db["pdbx_branch_scheme"];
for (const auto &[asym_id, entity_id] : branchScheme.rows<std::string,std::string>("asym_id", "entity_id")) for (const auto &[asym_id, entity_id] : branchScheme.rows<std::string, std::string>("asym_id", "entity_id"))
{ {
if (m_branches.empty() or m_branches.back().get_asym_id() != asym_id) if (m_branches.empty() or m_branches.back().get_asym_id() != asym_id)
m_branches.emplace_back(*this, asym_id, entity_id); m_branches.emplace_back(*this, asym_id, entity_id);
...@@ -1337,8 +1382,8 @@ void structure::load_data() ...@@ -1337,8 +1382,8 @@ void structure::load_data()
auto &nonPolyScheme = m_db["pdbx_nonpoly_scheme"]; auto &nonPolyScheme = m_db["pdbx_nonpoly_scheme"];
for (const auto&[asym_id, monID, pdbStrandID, pdbSeqNum, pdbInsCode] : for (const auto &[asym_id, monID, pdbStrandID, pdbSeqNum, pdbInsCode] :
nonPolyScheme.rows<std::string,std::string,std::string,std::string,std::string>("asym_id", "mon_id", "pdb_strand_id", "pdb_seq_num", "pdb_ins_code")) nonPolyScheme.rows<std::string, std::string, std::string, std::string, std::string>("asym_id", "mon_id", "pdb_strand_id", "pdb_seq_num", "pdb_ins_code"))
m_non_polymers.emplace_back(*this, monID, asym_id, 0, pdbStrandID, pdbSeqNum, pdbInsCode); m_non_polymers.emplace_back(*this, monID, asym_id, 0, pdbStrandID, pdbSeqNum, pdbInsCode);
// place atoms in residues // place atoms in residues
...@@ -1349,18 +1394,18 @@ void structure::load_data() ...@@ -1349,18 +1394,18 @@ void structure::load_data()
for (auto &poly : m_polymers) for (auto &poly : m_polymers)
{ {
for (auto &res : poly) for (auto &res : poly)
resMap[{res.get_asym_id(), res.get_seq_id(), res.get_auth_seq_id()}] = &res; resMap[{ res.get_asym_id(), res.get_seq_id(), res.get_auth_seq_id() }] = &res;
} }
for (auto &res : m_non_polymers) for (auto &res : m_non_polymers)
resMap[{res.get_asym_id(), res.get_seq_id(), res.get_auth_seq_id()}] = &res; resMap[{ res.get_asym_id(), res.get_seq_id(), res.get_auth_seq_id() }] = &res;
std::set<std::string> sugars; std::set<std::string> sugars;
for (auto &branch : m_branches) for (auto &branch : m_branches)
{ {
for (auto &sugar : branch) for (auto &sugar : branch)
{ {
resMap[{sugar.get_asym_id(), sugar.get_seq_id(), sugar.get_auth_seq_id()}] = &sugar; resMap[{ sugar.get_asym_id(), sugar.get_seq_id(), sugar.get_auth_seq_id() }] = &sugar;
sugars.insert(sugar.get_compound_id()); sugars.insert(sugar.get_compound_id());
} }
} }
...@@ -1392,7 +1437,9 @@ void structure::load_data() ...@@ -1392,7 +1437,9 @@ void structure::load_data()
} }
// what the ... // what the ...
m_branches.erase(std::remove_if(m_branches.begin(), m_branches.end(), [](const branch &b) { return b.empty(); }), m_branches.end()); m_branches.erase(std::remove_if(m_branches.begin(), m_branches.end(), [](const branch &b)
{ return b.empty(); }),
m_branches.end());
for (auto &branch : m_branches) for (auto &branch : m_branches)
branch.link_atoms(); branch.link_atoms();
...@@ -1714,12 +1761,11 @@ std::string structure::insert_compound(const std::string &compoundID, bool is_en ...@@ -1714,12 +1761,11 @@ std::string structure::insert_compound(const std::string &compoundID, bool is_en
auto r = chemComp.find(key("id") == compoundID); auto r = chemComp.find(key("id") == compoundID);
if (r.empty()) if (r.empty())
{ {
chemComp.emplace({ chemComp.emplace({ { "id", compoundID },
{"id", compoundID}, { "name", compound->name() },
{"name", compound->name()}, { "formula", compound->formula() },
{"formula", compound->formula()}, { "formula_weight", compound->formula_weight() },
{"formula_weight", compound->formula_weight()}, { "type", compound->type() } });
{"type", compound->type()}});
} }
std::string entity_id; std::string entity_id;
...@@ -1735,16 +1781,14 @@ std::string structure::insert_compound(const std::string &compoundID, bool is_en ...@@ -1735,16 +1781,14 @@ std::string structure::insert_compound(const std::string &compoundID, bool is_en
auto &entity = m_db["entity"]; auto &entity = m_db["entity"];
entity_id = entity.get_unique_id(""); entity_id = entity.get_unique_id("");
entity.emplace({ entity.emplace({ { "id", entity_id },
{"id", entity_id}, { "type", "non-polymer" },
{"type", "non-polymer"}, { "pdbx_description", compound->name() },
{"pdbx_description", compound->name()}, { "formula_weight", compound->formula_weight() } });
{"formula_weight", compound->formula_weight()}});
pdbxEntityNonpoly.emplace({ pdbxEntityNonpoly.emplace({ { "entity_id", entity_id },
{"entity_id", entity_id}, { "name", compound->name() },
{"name", compound->name()}, { "comp_id", compoundID } });
{"comp_id", compoundID}});
} }
} }
...@@ -1829,19 +1873,15 @@ void structure::remove_atom(atom &a, bool removeFromResidue) ...@@ -1829,19 +1873,15 @@ void structure::remove_atom(atom &a, bool removeFromResidue)
for (std::string prefix : { "ptnr1_", "ptnr2_", "pdbx_ptnr3_" }) for (std::string prefix : { "ptnr1_", "ptnr2_", "pdbx_ptnr3_" })
{ {
if (a.get_label_seq_id() == 0) if (a.get_label_seq_id() == 0)
cond = std::move(cond) or ( cond = std::move(cond) or (cif::key(prefix + "label_asym_id") == a.get_label_asym_id() and
cif::key(prefix + "label_asym_id") == a.get_label_asym_id() and
cif::key(prefix + "label_seq_id") == null and cif::key(prefix + "label_seq_id") == null and
cif::key(prefix + "auth_seq_id") == a.get_auth_seq_id() and cif::key(prefix + "auth_seq_id") == a.get_auth_seq_id() and
cif::key(prefix + "label_atom_id") == a.get_label_atom_id() cif::key(prefix + "label_atom_id") == a.get_label_atom_id());
);
else else
cond = std::move(cond) or ( cond = std::move(cond) or (cif::key(prefix + "label_asym_id") == a.get_label_asym_id() and
cif::key(prefix + "label_asym_id") == a.get_label_asym_id() and
cif::key(prefix + "label_seq_id") == a.get_label_seq_id() and cif::key(prefix + "label_seq_id") == a.get_label_seq_id() and
cif::key(prefix + "auth_seq_id") == a.get_auth_seq_id() and cif::key(prefix + "auth_seq_id") == a.get_auth_seq_id() and
cif::key(prefix + "label_atom_id") == a.get_label_atom_id() cif::key(prefix + "label_atom_id") == a.get_label_atom_id());
);
} }
if (cond) if (cond)
...@@ -1947,15 +1987,15 @@ void structure::change_residue(residue &res, const std::string &newCompound, ...@@ -1947,15 +1987,15 @@ void structure::change_residue(residue &res, const std::string &newCompound,
if (entityID.empty()) if (entityID.empty())
{ {
entityID = entity.get_unique_id(""); entityID = entity.get_unique_id("");
entity.emplace({{"id", entityID}, entity.emplace({ { "id", entityID },
{"type", "non-polymer"}, { "type", "non-polymer" },
{"pdbx_description", compound->name()}, { "pdbx_description", compound->name() },
{"formula_weight", compound->formula_weight()}}); { "formula_weight", compound->formula_weight() } });
auto &pdbxEntityNonpoly = m_db["pdbx_entity_nonpoly"]; auto &pdbxEntityNonpoly = m_db["pdbx_entity_nonpoly"];
pdbxEntityNonpoly.emplace({{"entity_id", entityID}, pdbxEntityNonpoly.emplace({ { "entity_id", entityID },
{"name", compound->name()}, { "name", compound->name() },
{"comp_id", newCompound}}); { "comp_id", newCompound } });
} }
auto &pdbxNonPolyScheme = m_db["pdbx_nonpoly_scheme"]; auto &pdbxNonPolyScheme = m_db["pdbx_nonpoly_scheme"];
...@@ -1971,11 +2011,11 @@ void structure::change_residue(residue &res, const std::string &newCompound, ...@@ -1971,11 +2011,11 @@ void structure::change_residue(residue &res, const std::string &newCompound,
auto &chemComp = m_db["chem_comp"]; auto &chemComp = m_db["chem_comp"];
if (not chemComp.contains(key("id") == newCompound)) if (not chemComp.contains(key("id") == newCompound))
{ {
chemComp.emplace({{"id", newCompound}, chemComp.emplace({ { "id", newCompound },
{"name", compound->name()}, { "name", compound->name() },
{"formula", compound->formula()}, { "formula", compound->formula() },
{"formula_weight", compound->formula_weight()}, { "formula_weight", compound->formula_weight() },
{"type", compound->type()}}); { "type", compound->type() } });
} }
// update the struct_asym for the new entity // update the struct_asym for the new entity
...@@ -2105,7 +2145,7 @@ void structure::remove_residue(residue &res) ...@@ -2105,7 +2145,7 @@ void structure::remove_residue(residue &res)
case EntityType::Branched: case EntityType::Branched:
{ {
auto &s = dynamic_cast<sugar&>(res); auto &s = dynamic_cast<sugar &>(res);
remove_sugar(s); remove_sugar(s);
...@@ -2161,7 +2201,9 @@ void structure::remove_sugar(sugar &s) ...@@ -2161,7 +2201,9 @@ void structure::remove_sugar(sugar &s)
remove_atom(atom, false); remove_atom(atom, false);
} }
branch.erase(remove_if(branch.begin(), branch.end(), [dix](const sugar &s) { return dix.count(s.num()); }), branch.end()); branch.erase(remove_if(branch.begin(), branch.end(), [dix](const sugar &s)
{ return dix.count(s.num()); }),
branch.end());
auto entity_id = create_entity_for_branch(branch); auto entity_id = create_entity_for_branch(branch);
...@@ -2181,23 +2223,21 @@ void structure::remove_sugar(sugar &s) ...@@ -2181,23 +2223,21 @@ void structure::remove_sugar(sugar &s)
for (auto &sugar : branch) for (auto &sugar : branch)
{ {
pdbx_branch_scheme.emplace({ pdbx_branch_scheme.emplace({ { "asym_id", asym_id },
{"asym_id", asym_id}, { "entity_id", entity_id },
{"entity_id", entity_id}, { "num", sugar.num() },
{"num", sugar.num()}, { "mon_id", sugar.get_compound_id() },
{"mon_id", sugar.get_compound_id()},
{"pdb_asym_id", asym_id}, { "pdb_asym_id", asym_id },
{"pdb_seq_num", sugar.num()}, { "pdb_seq_num", sugar.num() },
{"pdb_mon_id", sugar.get_compound_id()}, { "pdb_mon_id", sugar.get_compound_id() },
// TODO: need fix, collect from nag_atoms? // TODO: need fix, collect from nag_atoms?
{"auth_asym_id", asym_id}, { "auth_asym_id", asym_id },
{"auth_mon_id", sugar.get_compound_id()}, { "auth_mon_id", sugar.get_compound_id() },
{"auth_seq_num", sugar.get_auth_seq_id()}, { "auth_seq_num", sugar.get_auth_seq_id() },
{"hetero", "n"} { "hetero", "n" } });
});
} }
} }
} }
...@@ -2232,13 +2272,11 @@ std::string structure::create_non_poly(const std::string &entity_id, const std:: ...@@ -2232,13 +2272,11 @@ std::string structure::create_non_poly(const std::string &entity_id, const std::
auto &struct_asym = m_db["struct_asym"]; auto &struct_asym = m_db["struct_asym"];
std::string asym_id = struct_asym.get_unique_id(); std::string asym_id = struct_asym.get_unique_id();
struct_asym.emplace({ struct_asym.emplace({ { "id", asym_id },
{"id", asym_id}, { "pdbx_blank_PDB_chainid_flag", "N" },
{"pdbx_blank_PDB_chainid_flag", "N"}, { "pdbx_modified", "N" },
{"pdbx_modified", "N"}, { "entity_id", entity_id },
{"entity_id", entity_id}, { "details", "?" } });
{"details", "?"}
});
std::string comp_id = m_db["pdbx_entity_nonpoly"].find1<std::string>("entity_id"_key == entity_id, "comp_id"); std::string comp_id = m_db["pdbx_entity_nonpoly"].find1<std::string>("entity_id"_key == entity_id, "comp_id");
...@@ -2250,29 +2288,27 @@ std::string structure::create_non_poly(const std::string &entity_id, const std:: ...@@ -2250,29 +2288,27 @@ std::string structure::create_non_poly(const std::string &entity_id, const std::
{ {
auto atom_id = atom_site.get_unique_id(""); auto atom_id = atom_site.get_unique_id("");
auto row = atom_site.emplace({ auto row = atom_site.emplace({ { "group_PDB", atom.get_property("group_PDB") },
{"group_PDB", atom.get_property("group_PDB")}, { "id", atom_id },
{"id", atom_id}, { "type_symbol", atom.get_property("type_symbol") },
{"type_symbol", atom.get_property("type_symbol")}, { "label_atom_id", atom.get_property("label_atom_id") },
{"label_atom_id", atom.get_property("label_atom_id")}, { "label_alt_id", atom.get_property("label_alt_id") },
{"label_alt_id", atom.get_property("label_alt_id")}, { "label_comp_id", comp_id },
{"label_comp_id", comp_id}, { "label_asym_id", asym_id },
{"label_asym_id", asym_id}, { "label_entity_id", entity_id },
{"label_entity_id", entity_id}, { "label_seq_id", "." },
{"label_seq_id", "."}, { "pdbx_PDB_ins_code", "" },
{"pdbx_PDB_ins_code", ""}, { "Cartn_x", atom.get_property("Cartn_x") },
{"Cartn_x", atom.get_property("Cartn_x")}, { "Cartn_y", atom.get_property("Cartn_y") },
{"Cartn_y", atom.get_property("Cartn_y")}, { "Cartn_z", atom.get_property("Cartn_z") },
{"Cartn_z", atom.get_property("Cartn_z")}, { "occupancy", atom.get_property("occupancy") },
{"occupancy", atom.get_property("occupancy")}, { "B_iso_or_equiv", atom.get_property("B_iso_or_equiv") },
{"B_iso_or_equiv", atom.get_property("B_iso_or_equiv")}, { "pdbx_formal_charge", atom.get_property("pdbx_formal_charge") },
{"pdbx_formal_charge", atom.get_property("pdbx_formal_charge")}, { "auth_seq_id", 1 },
{"auth_seq_id", 1}, { "auth_comp_id", comp_id },
{"auth_comp_id", comp_id}, { "auth_asym_id", asym_id },
{"auth_asym_id", asym_id}, { "auth_atom_id", atom.get_property("label_atom_id") },
{"auth_atom_id", atom.get_property("label_atom_id")}, { "pdbx_PDB_model_num", 1 } });
{"pdbx_PDB_model_num", 1}
});
auto &newAtom = emplace_atom(std::make_shared<atom::atom_impl>(m_db, atom_id)); auto &newAtom = emplace_atom(std::make_shared<atom::atom_impl>(m_db, atom_id));
res.add_atom(newAtom); res.add_atom(newAtom);
...@@ -2281,16 +2317,16 @@ std::string structure::create_non_poly(const std::string &entity_id, const std:: ...@@ -2281,16 +2317,16 @@ std::string structure::create_non_poly(const std::string &entity_id, const std::
auto &pdbx_nonpoly_scheme = m_db["pdbx_nonpoly_scheme"]; auto &pdbx_nonpoly_scheme = m_db["pdbx_nonpoly_scheme"];
std::size_t ndb_nr = pdbx_nonpoly_scheme.find("asym_id"_key == asym_id and "entity_id"_key == entity_id).size() + 1; std::size_t ndb_nr = pdbx_nonpoly_scheme.find("asym_id"_key == asym_id and "entity_id"_key == entity_id).size() + 1;
pdbx_nonpoly_scheme.emplace({ pdbx_nonpoly_scheme.emplace({
{"asym_id", asym_id}, { "asym_id", asym_id },
{"entity_id", entity_id}, { "entity_id", entity_id },
{"mon_id", comp_id}, { "mon_id", comp_id },
{"ndb_seq_num", ndb_nr}, { "ndb_seq_num", ndb_nr },
{"pdb_seq_num", res.get_auth_seq_id()}, { "pdb_seq_num", res.get_auth_seq_id() },
{"auth_seq_num", res.get_auth_seq_id()}, { "auth_seq_num", res.get_auth_seq_id() },
{"pdb_mon_id", comp_id}, { "pdb_mon_id", comp_id },
{"auth_mon_id", comp_id}, { "auth_mon_id", comp_id },
{"pdb_strand_id", asym_id}, { "pdb_strand_id", asym_id },
{"pdb_ins_code", "."}, { "pdb_ins_code", "." },
}); });
return asym_id; return asym_id;
...@@ -2303,13 +2339,11 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector ...@@ -2303,13 +2339,11 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector
auto &struct_asym = m_db["struct_asym"]; auto &struct_asym = m_db["struct_asym"];
std::string asym_id = struct_asym.get_unique_id(); std::string asym_id = struct_asym.get_unique_id();
struct_asym.emplace({ struct_asym.emplace({ { "id", asym_id },
{"id", asym_id}, { "pdbx_blank_PDB_chainid_flag", "N" },
{"pdbx_blank_PDB_chainid_flag", "N"}, { "pdbx_modified", "N" },
{"pdbx_modified", "N"}, { "entity_id", entity_id },
{"entity_id", entity_id}, { "details", "?" } });
{"details", "?"}
});
std::string comp_id = m_db["pdbx_entity_nonpoly"].find1<std::string>("entity_id"_key == entity_id, "comp_id"); std::string comp_id = m_db["pdbx_entity_nonpoly"].find1<std::string>("entity_id"_key == entity_id, "comp_id");
...@@ -2326,14 +2360,14 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector ...@@ -2326,14 +2360,14 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector
atom.set_value("auth_asym_id", asym_id); atom.set_value("auth_asym_id", asym_id);
atom.set_value("label_entity_id", entity_id); atom.set_value("label_entity_id", entity_id);
atom.set_value_if_empty({"group_PDB", "HETATM"}); atom.set_value_if_empty({ "group_PDB", "HETATM" });
atom.set_value_if_empty({"label_comp_id", comp_id}); atom.set_value_if_empty({ "label_comp_id", comp_id });
atom.set_value_if_empty({"label_seq_id", "."}); atom.set_value_if_empty({ "label_seq_id", "." });
atom.set_value_if_empty({"auth_comp_id", comp_id}); atom.set_value_if_empty({ "auth_comp_id", comp_id });
atom.set_value_if_empty({"auth_seq_id", 1}); atom.set_value_if_empty({ "auth_seq_id", 1 });
atom.set_value_if_empty({"pdbx_PDB_model_num", 1}); atom.set_value_if_empty({ "pdbx_PDB_model_num", 1 });
atom.set_value_if_empty({"label_alt_id", ""}); atom.set_value_if_empty({ "label_alt_id", "" });
atom.set_value_if_empty({"occupancy", 1.0, 2}); atom.set_value_if_empty({ "occupancy", 1.0, 2 });
auto row = atom_site.emplace(atom.begin(), atom.end()); auto row = atom_site.emplace(atom.begin(), atom.end());
...@@ -2344,16 +2378,16 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector ...@@ -2344,16 +2378,16 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector
auto &pdbx_nonpoly_scheme = m_db["pdbx_nonpoly_scheme"]; auto &pdbx_nonpoly_scheme = m_db["pdbx_nonpoly_scheme"];
std::size_t ndb_nr = pdbx_nonpoly_scheme.find("asym_id"_key == asym_id and "entity_id"_key == entity_id).size() + 1; std::size_t ndb_nr = pdbx_nonpoly_scheme.find("asym_id"_key == asym_id and "entity_id"_key == entity_id).size() + 1;
pdbx_nonpoly_scheme.emplace({ pdbx_nonpoly_scheme.emplace({
{"asym_id", asym_id}, { "asym_id", asym_id },
{"entity_id", entity_id}, { "entity_id", entity_id },
{"mon_id", comp_id}, { "mon_id", comp_id },
{"ndb_seq_num", ndb_nr}, { "ndb_seq_num", ndb_nr },
{"pdb_seq_num", res.get_auth_seq_id()}, { "pdb_seq_num", res.get_auth_seq_id() },
{"auth_seq_num", res.get_auth_seq_id()}, { "auth_seq_num", res.get_auth_seq_id() },
{"pdb_mon_id", comp_id}, { "pdb_mon_id", comp_id },
{"auth_mon_id", comp_id}, { "auth_mon_id", comp_id },
{"pdb_strand_id", asym_id}, { "pdb_strand_id", asym_id },
{"pdb_ins_code", "."}, { "pdb_ins_code", "." },
}); });
return asym_id; return asym_id;
...@@ -2375,13 +2409,11 @@ void structure::create_water(row_initializer atom) ...@@ -2375,13 +2409,11 @@ void structure::create_water(row_initializer atom)
{ {
asym_id = struct_asym.get_unique_id(); asym_id = struct_asym.get_unique_id();
struct_asym.emplace({ struct_asym.emplace({ { "id", asym_id },
{"id", asym_id}, { "pdbx_blank_PDB_chainid_flag", "N" },
{"pdbx_blank_PDB_chainid_flag", "N"}, { "pdbx_modified", "N" },
{"pdbx_modified", "N"}, { "entity_id", entity_id },
{"entity_id", entity_id}, { "details", "?" } });
{"details", "?"}
});
} }
auto &atom_site = m_db["atom_site"]; auto &atom_site = m_db["atom_site"];
...@@ -2397,13 +2429,13 @@ void structure::create_water(row_initializer atom) ...@@ -2397,13 +2429,13 @@ void structure::create_water(row_initializer atom)
atom.set_value("label_entity_id", entity_id); atom.set_value("label_entity_id", entity_id);
atom.set_value("auth_seq_id", std::to_string(auth_seq_id)); atom.set_value("auth_seq_id", std::to_string(auth_seq_id));
atom.set_value_if_empty({"group_PDB", "HETATM"}); atom.set_value_if_empty({ "group_PDB", "HETATM" });
atom.set_value_if_empty({"label_comp_id", "HOH"}); atom.set_value_if_empty({ "label_comp_id", "HOH" });
atom.set_value_if_empty({"label_seq_id", "."}); atom.set_value_if_empty({ "label_seq_id", "." });
atom.set_value_if_empty({"auth_comp_id", "HOH"}); atom.set_value_if_empty({ "auth_comp_id", "HOH" });
atom.set_value_if_empty({"pdbx_PDB_model_num", 1}); atom.set_value_if_empty({ "pdbx_PDB_model_num", 1 });
atom.set_value_if_empty({"label_alt_id", ""}); atom.set_value_if_empty({ "label_alt_id", "" });
atom.set_value_if_empty({"occupancy", 1.0, 2}); atom.set_value_if_empty({ "occupancy", 1.0, 2 });
auto row = atom_site.emplace(atom.begin(), atom.end()); auto row = atom_site.emplace(atom.begin(), atom.end());
...@@ -2412,16 +2444,16 @@ void structure::create_water(row_initializer atom) ...@@ -2412,16 +2444,16 @@ void structure::create_water(row_initializer atom)
auto &pdbx_nonpoly_scheme = m_db["pdbx_nonpoly_scheme"]; auto &pdbx_nonpoly_scheme = m_db["pdbx_nonpoly_scheme"];
int ndb_nr = pdbx_nonpoly_scheme.find_max<int>("ndb_seq_num") + 1; int ndb_nr = pdbx_nonpoly_scheme.find_max<int>("ndb_seq_num") + 1;
pdbx_nonpoly_scheme.emplace({ pdbx_nonpoly_scheme.emplace({
{"asym_id", asym_id}, { "asym_id", asym_id },
{"entity_id", entity_id}, { "entity_id", entity_id },
{"mon_id", "HOH"}, { "mon_id", "HOH" },
{"ndb_seq_num", ndb_nr}, { "ndb_seq_num", ndb_nr },
{"pdb_seq_num", auth_seq_id}, { "pdb_seq_num", auth_seq_id },
{"auth_seq_num", auth_seq_id}, { "auth_seq_num", auth_seq_id },
{"pdb_mon_id", "HOH"}, { "pdb_mon_id", "HOH" },
{"auth_mon_id", "HOH"}, { "auth_mon_id", "HOH" },
{"pdb_strand_id", asym_id}, { "pdb_strand_id", asym_id },
{"pdb_ins_code", "."}, { "pdb_ins_code", "." },
}); });
} }
...@@ -2433,18 +2465,14 @@ branch &structure::create_branch() ...@@ -2433,18 +2465,14 @@ branch &structure::create_branch()
auto entity_id = entity.get_unique_id(""); auto entity_id = entity.get_unique_id("");
auto asym_id = struct_asym.get_unique_id(); auto asym_id = struct_asym.get_unique_id();
entity.emplace({ entity.emplace({ { "id", entity_id },
{"id", entity_id}, { "type", "branched" } });
{"type", "branched"}
});
struct_asym.emplace({ struct_asym.emplace({ { "id", asym_id },
{"id", asym_id}, { "pdbx_blank_PDB_chainid_flag", "N" },
{"pdbx_blank_PDB_chainid_flag", "N"}, { "pdbx_modified", "N" },
{"pdbx_modified", "N"}, { "entity_id", entity_id },
{"entity_id", entity_id}, { "details", "?" } });
{"details", "?"}
});
return m_branches.emplace_back(*this, asym_id, entity_id); return m_branches.emplace_back(*this, asym_id, entity_id);
} }
...@@ -2641,22 +2669,19 @@ std::string structure::create_entity_for_branch(branch &branch) ...@@ -2641,22 +2669,19 @@ std::string structure::create_entity_for_branch(branch &branch)
if (VERBOSE) if (VERBOSE)
std::cout << "Creating new entity " << entityID << " for branched sugar " << entityName << '\n'; std::cout << "Creating new entity " << entityID << " for branched sugar " << entityName << '\n';
entity.emplace({ entity.emplace({ { "id", entityID },
{"id", entityID}, { "type", "branched" },
{"type", "branched"}, { "src_method", "man" },
{"src_method", "man"}, { "pdbx_description", entityName },
{"pdbx_description", entityName}, { "formula_weight", branch.weight() } });
{"formula_weight", branch.weight()}});
auto &pdbx_entity_branch_list = m_db["pdbx_entity_branch_list"]; auto &pdbx_entity_branch_list = m_db["pdbx_entity_branch_list"];
for (auto &sugar : branch) for (auto &sugar : branch)
{ {
pdbx_entity_branch_list.emplace({ pdbx_entity_branch_list.emplace({ { "entity_id", entityID },
{"entity_id", entityID}, { "comp_id", sugar.get_compound_id() },
{"comp_id", sugar.get_compound_id()}, { "num", sugar.num() },
{"num", sugar.num()}, { "hetero", "n" } });
{"hetero", "n"}
});
} }
auto &pdbx_entity_branch_link = m_db["pdbx_entity_branch_link"]; auto &pdbx_entity_branch_link = m_db["pdbx_entity_branch_link"];
...@@ -2670,19 +2695,17 @@ std::string structure::create_entity_for_branch(branch &branch) ...@@ -2670,19 +2695,17 @@ std::string structure::create_entity_for_branch(branch &branch)
auto &s2 = branch.at(stoi(l2.get_auth_seq_id()) - 1); auto &s2 = branch.at(stoi(l2.get_auth_seq_id()) - 1);
auto l1 = s2.get_atom_by_atom_id("C1"); auto l1 = s2.get_atom_by_atom_id("C1");
pdbx_entity_branch_link.emplace({ pdbx_entity_branch_link.emplace({ { "link_id", pdbx_entity_branch_link.get_unique_id("") },
{"link_id", pdbx_entity_branch_link.get_unique_id("")}, { "entity_id", entityID },
{"entity_id", entityID}, { "entity_branch_list_num_1", s1.get_auth_seq_id() },
{"entity_branch_list_num_1", s1.get_auth_seq_id()}, { "comp_id_1", s1.get_compound_id() },
{"comp_id_1", s1.get_compound_id()}, { "atom_id_1", l1.get_label_atom_id() },
{"atom_id_1", l1.get_label_atom_id()}, { "leaving_atom_id_1", "O1" },
{"leaving_atom_id_1", "O1"}, { "entity_branch_list_num_2", s2.get_auth_seq_id() },
{"entity_branch_list_num_2", s2.get_auth_seq_id()}, { "comp_id_2", s2.get_compound_id() },
{"comp_id_2", s2.get_compound_id()}, { "atom_id_2", l2.get_label_atom_id() },
{"atom_id_2", l2.get_label_atom_id()}, { "leaving_atom_id_2", "H" + l2.get_label_atom_id() },
{"leaving_atom_id_2", "H" + l2.get_label_atom_id()}, { "value_order", "sing" } });
{"value_order", "sing"}
});
} }
} }
...@@ -2725,12 +2748,26 @@ void structure::cleanup_empty_categories() ...@@ -2725,12 +2748,26 @@ void structure::cleanup_empty_categories()
obsoleteEntities.push_back(entity); obsoleteEntities.push_back(entity);
} }
auto validator = m_db.get_validator();
for (auto entity : obsoleteEntities) for (auto entity : obsoleteEntities)
{
std::string entityID = entity["id"].as<std::string>();
if (validator)
{
for (auto linked : validator->get_links_for_parent("entity"))
{
if (auto cat = m_db.get(linked->m_child_category))
cat->erase(cif::key(linked->m_child_keys.front()) == entityID);
}
}
entities.erase(entity); entities.erase(entity);
}
// the rest? // the rest?
for (const char *cat : {"pdbx_entity_nonpoly"}) for (const char *cat : { "pdbx_entity_nonpoly" })
{ {
auto &category = m_db[cat]; auto &category = m_db[cat];
...@@ -2836,4 +2873,89 @@ void structure::validate_atoms() const ...@@ -2836,4 +2873,89 @@ void structure::validate_atoms() const
assert(atoms.empty()); assert(atoms.empty());
} }
} // namespace pdbx static int compare_numbers(std::string_view a, std::string_view b)
{
int result = 0;
double da, db;
using namespace cif;
using namespace std;
std::from_chars_result ra, rb;
ra = selected_charconv<double>::from_chars(a.data(), a.data() + a.length(), da);
rb = selected_charconv<double>::from_chars(b.data(), b.data() + b.length(), db);
if (not(bool) ra.ec and not(bool) rb.ec)
{
auto d = da - db;
if (std::abs(d) > std::numeric_limits<double>::epsilon())
{
if (d > 0)
result = 1;
else if (d < 0)
result = -1;
}
}
else if ((bool)ra.ec)
result = 1;
else
result = -1;
return result;
}
void structure::reorder_atoms()
{
auto &atom_site = m_db["atom_site"];
atom_site.sort([](row_handle a, row_handle b)
{
int d;
// First by model number
d = a.get<int>("pdbx_PDB_model_num") - b.get<int>("pdbx_PDB_model_num");
if (d == 0)
d = a.get<std::string>("label_asym_id").compare(b.get<std::string>("label_asym_id"));
if (d == 0)
{
auto na = a.get<std::optional<int>>("label_seq_id");
auto nb = b.get<std::optional<int>>("label_seq_id");
if (na.has_value() and nb.has_value())
d = *na - *nb;
else if (na.has_value())
d = 1;
else if (nb.has_value())
d = -1;
}
if (d == 0)
{
auto na = a.get<std::optional<int>>("auth_seq_id");
auto nb = b.get<std::optional<int>>("auth_seq_id");
if (na.has_value() and nb.has_value())
d = *na - *nb;
else if (na.has_value())
d = 1;
else if (nb.has_value())
d = -1;
}
if (d == 0)
d = compare_numbers(a.get<std::string>("id"), b.get<std::string>("id"));
return d;
//
});
// atom_site.set_validator(nullptr, m_db);
// for (int nr = 1; auto r : atom_site)
// r["id"] = nr++;
// atom_site.set_validator(m_db.get_validator(), m_db);
}
} // namespace cif::mm
...@@ -144,9 +144,11 @@ void checkEntities(datablock &db) ...@@ -144,9 +144,11 @@ void checkEntities(datablock &db)
if (comp_id.has_value()) if (comp_id.has_value())
{ {
auto compound = cf.create(*comp_id); auto compound = cf.create(*comp_id);
assert(compound);
if (not compound) if (not compound)
throw std::runtime_error("missing information for compound " + *comp_id); {
std::cerr << "missing information for compound " << *comp_id << "\n";
continue;
}
formula_weight = compound->formula_weight(); formula_weight = compound->formula_weight();
} }
} }
...@@ -416,6 +418,8 @@ void checkAtomRecords(datablock &db) ...@@ -416,6 +418,8 @@ void checkAtomRecords(datablock &db)
for (int id : db["entity"].find<int>("type"_key == "polymer", "id")) for (int id : db["entity"].find<int>("type"_key == "polymer", "id"))
polymer_entities.insert(id); polymer_entities.insert(id);
std::set<std::string> missingCompounds;
for (auto row : atom_site) for (auto row : atom_site)
{ {
residue_key_type k = row.get<std::optional<std::string>, residue_key_type k = row.get<std::optional<std::string>,
...@@ -446,11 +450,18 @@ void checkAtomRecords(datablock &db) ...@@ -446,11 +450,18 @@ void checkAtomRecords(datablock &db)
std::string asym_id = get_asym_id(k); std::string asym_id = get_asym_id(k);
std::string comp_id = get_comp_id(k); std::string comp_id = get_comp_id(k);
if (missingCompounds.contains(comp_id))
continue;
bool is_polymer = polymer_entities.contains(row["label_entity_id"].as<int>()); bool is_polymer = polymer_entities.contains(row["label_entity_id"].as<int>());
auto compound = cf.create(comp_id); auto compound = cf.create(comp_id);
if (not compound) if (not compound)
throw std::runtime_error("Missing compound information for " + comp_id); {
missingCompounds.insert(comp_id);
std::cerr << "Missing compound information for " << comp_id << "\n";
continue;
}
auto chem_comp_entry = chem_comp.find_first("id"_key == comp_id); auto chem_comp_entry = chem_comp.find_first("id"_key == comp_id);
...@@ -590,18 +601,18 @@ void checkAtomAnisotropRecords(datablock &db) ...@@ -590,18 +601,18 @@ void checkAtomAnisotropRecords(datablock &db)
row["type_symbol"] = parent["type_symbol"].text(); row["type_symbol"] = parent["type_symbol"].text();
} }
if (row["pdbx_auth_alt_id"].empty()) if (row["pdbx_auth_alt_id"].empty() and not parent["pdbx_auth_alt_id"].empty())
row["pdbx_auth_alt_id"] = parent["pdbx_auth_alt_id"].text(); row["pdbx_auth_alt_id"] = parent["pdbx_auth_alt_id"].text();
if (row["pdbx_label_seq_id"].empty()) if (row["pdbx_label_seq_id"].empty() and not parent["pdbx_label_seq_id"].empty())
row["pdbx_label_seq_id"] = parent["label_seq_id"].text(); row["pdbx_label_seq_id"] = parent["label_seq_id"].text();
if (row["pdbx_label_asym_id"].empty()) if (row["pdbx_label_asym_id"].empty() and not parent["pdbx_label_asym_id"].empty())
row["pdbx_label_asym_id"] = parent["label_asym_id"].text(); row["pdbx_label_asym_id"] = parent["label_asym_id"].text();
if (row["pdbx_label_atom_id"].empty()) if (row["pdbx_label_atom_id"].empty() and not parent["pdbx_label_atom_id"].empty())
row["pdbx_label_atom_id"] = parent["label_atom_id"].text(); row["pdbx_label_atom_id"] = parent["label_atom_id"].text();
if (row["pdbx_label_comp_id"].empty()) if (row["pdbx_label_comp_id"].empty() and not parent["pdbx_label_comp_id"].empty())
row["pdbx_label_comp_id"] = parent["label_comp_id"].text(); row["pdbx_label_comp_id"] = parent["label_comp_id"].text();
if (row["pdbx_PDB_model_num"].empty()) // if (row["pdbx_PDB_model_num"].empty() and not parent["pdbx_PDB_model_num"].empty())
row["pdbx_PDB_model_num"] = parent["pdbx_PDB_model_num"].text(); // row["pdbx_PDB_model_num"] = parent["pdbx_PDB_model_num"].text();
} }
if (not to_be_deleted.empty()) if (not to_be_deleted.empty())
...@@ -811,6 +822,18 @@ void createEntityPoly(datablock &db) ...@@ -811,6 +822,18 @@ void createEntityPoly(datablock &db)
non_std_monomer = true; non_std_monomer = true;
} }
else
{
// c_type = "other";
letter_can = c->one_letter_code();
if (letter_can == 0)
letter_can = 'X';
letter = '(' + comp_id + ')';
non_std_monomer = true;
}
if (type.empty()) if (type.empty())
type = c_type; type = c_type;
...@@ -877,7 +900,7 @@ void createEntityPoly(datablock &db) ...@@ -877,7 +900,7 @@ void createEntityPoly(datablock &db)
void createEntityPolySeq(datablock &db) void createEntityPolySeq(datablock &db)
{ {
if (db.get("entity_poly") == nullptr) if (auto cat = db.get("entity_poly"); cat == nullptr or cat->empty())
createEntityPoly(db); createEntityPoly(db);
using namespace literals; using namespace literals;
...@@ -928,7 +951,10 @@ void createEntityPolySeq(datablock &db) ...@@ -928,7 +951,10 @@ void createEntityPolySeq(datablock &db)
void createPdbxPolySeqScheme(datablock &db) void createPdbxPolySeqScheme(datablock &db)
{ {
if (db.get("entity_poly_seq") == nullptr) if (auto cat = db.get("entity_poly"); cat == nullptr or cat->empty())
createEntityPoly(db);
if (auto cat = db.get("entity_poly_seq"); cat == nullptr or cat->empty())
createEntityPolySeq(db); createEntityPolySeq(db);
using namespace literals; using namespace literals;
...@@ -1065,7 +1091,7 @@ bool reconstruct_pdbx(file &file, std::string_view dictionary) ...@@ -1065,7 +1091,7 @@ bool reconstruct_pdbx(file &file, std::string_view dictionary)
// ... and any additional datablock will contain compound information // ... and any additional datablock will contain compound information
cif::compound_source cs(file); cif::compound_source cs(file);
if (db.get("atom_site") == nullptr) if (auto cat = db.get("atom_site"); cat == nullptr or cat->empty())
throw std::runtime_error("Cannot reconstruct PDBx file, atom data missing"); throw std::runtime_error("Cannot reconstruct PDBx file, atom data missing");
auto &validator = validator_factory::instance()[dictionary]; auto &validator = validator_factory::instance()[dictionary];
...@@ -1073,7 +1099,7 @@ bool reconstruct_pdbx(file &file, std::string_view dictionary) ...@@ -1073,7 +1099,7 @@ bool reconstruct_pdbx(file &file, std::string_view dictionary)
std::string entry_id; std::string entry_id;
// Phenix files do not have an entry record // Phenix files do not have an entry record
if (db.get("entry") == nullptr) if (auto cat = db.get("entry"); cat == nullptr or cat->empty())
{ {
entry_id = db.name(); entry_id = db.name();
category entry("entry"); category entry("entry");
...@@ -1327,19 +1353,19 @@ bool reconstruct_pdbx(file &file, std::string_view dictionary) ...@@ -1327,19 +1353,19 @@ bool reconstruct_pdbx(file &file, std::string_view dictionary)
// Now create any missing categories // Now create any missing categories
// Next make sure we have struct_asym records // Next make sure we have struct_asym records
if (db.get("struct_asym") == nullptr) if (auto cat = db.get("struct_asym"); cat == nullptr or cat->empty())
createStructAsym(db); createStructAsym(db);
if (db.get("entity") == nullptr) if (auto cat = db.get("entity"); cat == nullptr or cat->empty())
createEntity(db); createEntity(db);
// fill in missing formula_weight, e.g. // fill in missing formula_weight, e.g.
checkEntities(db); checkEntities(db);
if (db.get("pdbx_poly_seq_scheme") == nullptr) if (auto cat = db.get("pdbx_poly_seq_scheme"); cat == nullptr or cat->empty())
createPdbxPolySeqScheme(db); createPdbxPolySeqScheme(db);
if (db.get("ndb_poly_seq_scheme") != nullptr) if (auto cat = db.get("ndb_poly_seq_scheme"); cat == nullptr or cat->empty())
comparePolySeqSchemes(db); comparePolySeqSchemes(db);
// skip unknown categories for now // skip unknown categories for now
......
...@@ -189,6 +189,7 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary, std::erro ...@@ -189,6 +189,7 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary, std::erro
for (auto asym_id : struct_asym.find<std::string>("entity_id"_key == entity_id, "id")) for (auto asym_id : struct_asym.find<std::string>("entity_id"_key == entity_id, "id"))
{ {
if (pdbx_poly_seq_scheme.count( if (pdbx_poly_seq_scheme.count(
"entity_id"_key == entity_id and
"asym_id"_key == asym_id and "asym_id"_key == asym_id and
"mon_id"_key == mon_id and "mon_id"_key == mon_id and
"seq_id"_key == num and "seq_id"_key == num and
...@@ -202,6 +203,7 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary, std::erro ...@@ -202,6 +203,7 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary, std::erro
for (const auto &[seq_id, mon_id, hetero] : pdbx_poly_seq_scheme.find<int, std::string, bool>("entity_id"_key == entity_id, "seq_id", "mon_id", "hetero")) for (const auto &[seq_id, mon_id, hetero] : pdbx_poly_seq_scheme.find<int, std::string, bool>("entity_id"_key == entity_id, "seq_id", "mon_id", "hetero"))
{ {
if (entity_poly_seq.count( if (entity_poly_seq.count(
"entity_id"_key == entity_id and
"mon_id"_key == mon_id and "mon_id"_key == mon_id and
"num"_key == seq_id and "num"_key == seq_id and
"hetero"_key == hetero) != 1) "hetero"_key == hetero) != 1)
......
...@@ -235,7 +235,7 @@ void progress_bar_impl::print_progress() ...@@ -235,7 +235,7 @@ void progress_bar_impl::print_progress()
float progress = static_cast<float>(m_consumed) / m_max_value; float progress = static_cast<float>(m_consumed) / m_max_value;
if (width < kMinBarWidth) if (width < kMinBarWidth)
std::cout << (100 * progress) << '%' << std::endl; std::cout << (100 * progress) << "%\n";
else else
{ {
uint32_t bar_width = 7 * width / 10; uint32_t bar_width = 7 * width / 10;
...@@ -329,7 +329,7 @@ void progress_bar_impl::print_done() ...@@ -329,7 +329,7 @@ void progress_bar_impl::print_done()
if (msg.length() < width) if (msg.length() < width)
msg += std::string(width - msg.length(), ' '); msg += std::string(width - msg.length(), ' ');
std::cout << '\r' << msg << std::endl; std::cout << '\r' << msg << '\n';
} }
progress_bar::progress_bar(int64_t inMax, const std::string &inAction) progress_bar::progress_bar(int64_t inMax, const std::string &inAction)
......
# We're using the older version 2 of Catch2 # We're using the older version 2 of Catch2
find_package(Catch2 QUIET) if(NOT(Catch2_FOUND OR TARGET Catch2))
find_package(Catch2 QUIET)
if(NOT Catch2_FOUND)
include(FetchContent)
if(NOT Catch2_FOUND)
FetchContent_Declare( FetchContent_Declare(
Catch2 Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_REPOSITORY https://github.com/catchorg/Catch2.git
...@@ -11,6 +14,7 @@ if(NOT Catch2_FOUND) ...@@ -11,6 +14,7 @@ if(NOT Catch2_FOUND)
FetchContent_MakeAvailable(Catch2) FetchContent_MakeAvailable(Catch2)
set(Catch2_VERSION "2.13.9") set(Catch2_VERSION "2.13.9")
endif()
endif() endif()
list( list(
...@@ -49,8 +53,7 @@ foreach(CIFPP_TEST IN LISTS CIFPP_tests) ...@@ -49,8 +53,7 @@ foreach(CIFPP_TEST IN LISTS CIFPP_tests)
target_compile_definitions(${CIFPP_TEST} PUBLIC CATCH22=1) target_compile_definitions(${CIFPP_TEST} PUBLIC CATCH22=1)
endif() endif()
target_link_libraries(${CIFPP_TEST} PRIVATE Threads::Threads cifpp::cifpp target_link_libraries(${CIFPP_TEST} PRIVATE cifpp::cifpp Catch2::Catch2)
Catch2::Catch2)
target_include_directories(${CIFPP_TEST} PRIVATE "${EIGEN_INCLUDE_DIR}") target_include_directories(${CIFPP_TEST} PRIVATE "${EIGEN_INCLUDE_DIR}")
if(MSVC) if(MSVC)
......
...@@ -157,7 +157,7 @@ TEST_CASE("dh_q_0") ...@@ -157,7 +157,7 @@ TEST_CASE("dh_q_0")
}; };
auto a = cif::dihedral_angle(t[0], t[1], t[2], p); auto a = cif::dihedral_angle(t[0], t[1], t[2], p);
REQUIRE_THAT(a, Catch::Matchers::WithinRel(0, 0.01f)); REQUIRE_THAT(a, Catch::Matchers::WithinRel(0.f, 0.01f));
auto q = cif::construct_from_angle_axis(90, axis); auto q = cif::construct_from_angle_axis(90, axis);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment