Compare commits

..

3 Commits

Author SHA1 Message Date
Maarten L. Hekkelman
bed507bd60 Better from_chars implementation 2024-09-10 11:18:53 +02:00
Maarten L. Hekkelman
f4bc06a5c2 Work around weird behaviour in testing floats using catch2 2024-09-10 10:15:29 +02:00
Maarten L. Hekkelman
be7a1d75e1 Proposed fix for comparing floating points (needed on macOS) 2024-09-10 10:03:10 +02:00
34 changed files with 931 additions and 9476 deletions

View File

@@ -27,12 +27,11 @@ cmake_minimum_required(VERSION 3.23)
# set the project name
project(
libcifpp
VERSION 8.0.0
VERSION 7.0.4
LANGUAGES CXX)
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(FindAtomic)
include(CheckFunctionExists)
include(CheckIncludeFiles)
include(CheckLibraryExists)
@@ -41,12 +40,8 @@ include(CheckCXXSourceCompiles)
include(GenerateExportHeader)
include(CTest)
include(FetchContent)
include(ExternalProject)
# FindBoost, take care of it now.
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.30)
cmake_policy(SET CMP0167 NEW)
endif()
message(STATUS "DESTDIR is '${DESTDIR}'")
# When building with ninja-multiconfig, build both debug and release by default
if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
@@ -193,6 +188,10 @@ if(GXX_LIBSTDCPP)
endif()
endif()
set(CMAKE_THREAD_PREFER_PTHREAD)
set(THREADS_PREFER_PTHREAD_FLAG)
find_package(Threads)
if(MSVC)
# Avoid linking the shared library of zlib Search ZLIB_ROOT first if it is
# set.
@@ -222,7 +221,6 @@ if(MSVC)
endif()
find_package(ZLIB QUIET)
find_package(Threads)
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)")
@@ -237,17 +235,20 @@ if(Eigen3_FOUND AND TARGET Eigen3::Eigen)
get_target_property(EIGEN_INCLUDE_DIR Eigen3::Eigen
INTERFACE_INCLUDE_DIRECTORIES)
else()
# Use ExternalProject since FetchContent always tries to install the result...
ExternalProject_Add(my-eigen3
# Create a private copy of eigen3 and populate it only, no need to build
FetchContent_Declare(
my-eigen3
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.0
INSTALL_COMMAND "")
ExternalProject_Get_Property(my-eigen3 SOURCE_DIR)
set(EIGEN_INCLUDE_DIR ${SOURCE_DIR})
endif()
GIT_TAG 3.4.0)
message(STATUS "Eigen include dir is ${EIGEN_INCLUDE_DIR}")
FetchContent_GetProperties(my-eigen3)
if(NOT my-eigen3_POPULATED)
FetchContent_Populate(my-eigen3)
endif()
set(EIGEN_INCLUDE_DIR ${my-eigen3_SOURCE_DIR})
endif()
# Create a revision file, containing the current git version info
include(VersionString)
@@ -259,8 +260,6 @@ if(CIFPP_RECREATE_SYMOP_DATA)
add_executable(symop-map-generator
"${CMAKE_CURRENT_SOURCE_DIR}/src/symop-map-generator.cpp")
target_compile_features(symop-map-generator PUBLIC cxx_std_20)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp
COMMAND
@@ -332,13 +331,9 @@ set(project_headers
include/cif++/validate.hpp
)
add_library(cifpp)
add_library(cifpp STATIC)
add_library(cifpp::cifpp ALIAS cifpp)
if(TARGET my-eigen3)
add_dependencies(cifpp my-eigen3)
endif()
target_sources(cifpp
PRIVATE ${project_sources}
${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp
@@ -376,7 +371,7 @@ target_include_directories(
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
PRIVATE "${BOOST_REGEX_INCLUDE_DIR}" "${EIGEN_INCLUDE_DIR}")
target_link_libraries(cifpp PUBLIC Threads::Threads ZLIB::ZLIB $<$<TARGET_EXISTS:std::atomic>:std::atomic>)
target_link_libraries(cifpp PUBLIC Threads::Threads ZLIB::ZLIB)
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
@@ -447,11 +442,6 @@ endif()
if(CIFPP_DATA_DIR)
target_compile_definitions(cifpp PUBLIC DATA_DIR="${CIFPP_DATA_DIR}")
set_target_properties(cifpp PROPERTIES CIFPP_DATA_DIR ${CIFPP_DATA_DIR})
endif()
if(NOT PROJECT_IS_TOP_LEVEL)
set(CIFPP_SHARE_DIR ${CIFPP_DATA_DIR} PARENT_SCOPE)
endif()
if(UNIX AND NOT BUILD_FOR_CCP4)
@@ -474,74 +464,77 @@ else()
unset(CIFPP_CACHE_DIR)
endif()
# Install rules
install(TARGETS cifpp
EXPORT cifpp
FILE_SET cifpp_headers DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
# Avoid full installation in case we are not the top level target
if(PROJECT_IS_TOP_LEVEL OR BUILD_FOR_CCP4)
# Install rules
install(TARGETS cifpp
EXPORT cifpp
FILE_SET cifpp_headers DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
if(MSVC AND BUILD_SHARED_LIBS)
install(
FILES $<TARGET_PDB_FILE:cifpp>
DESTINATION ${CMAKE_INSTALL_LIBDIR}
OPTIONAL)
endif()
# Clean up old config files (with old names)
file(GLOB OLD_CONFIG_FILES
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppConfig*.cmake
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppTargets*.cmake)
if(OLD_CONFIG_FILES)
message(
STATUS "Installation will remove old config files: ${OLD_CONFIG_FILES}")
install(CODE "file(REMOVE ${OLD_CONFIG_FILES})")
endif()
install(EXPORT cifpp
NAMESPACE cifpp::
FILE "cifpp-targets.cmake"
DESTINATION lib/cmake/cifpp)
if(CIFPP_DATA_DIR AND CIFPP_DOWNLOAD_CCD)
install(
FILES ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_ddl.dic
${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic
${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_ma.dic ${COMPONENTS_CIF}
DESTINATION ${CMAKE_INSTALL_DATADIR}/libcifpp)
endif()
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
INSTALL_DESTINATION lib/cmake/cifpp
PATH_VARS CIFPP_DATA_DIR)
if(MSVC AND BUILD_SHARED_LIBS)
install(
FILES $<TARGET_PDB_FILE:cifpp>
DESTINATION ${CMAKE_INSTALL_LIBDIR}
OPTIONAL)
FILES "${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake"
DESTINATION lib/cmake/cifpp)
set_target_properties(
cifpp
PROPERTIES VERSION ${PROJECT_VERSION}
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
INTERFACE_cifpp_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})
set_property(
TARGET cifpp
APPEND
PROPERTY COMPATIBLE_INTERFACE_STRING cifpp_MAJOR_VERSION)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake"
VERSION ${PROJECT_VERSION}
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()
# Clean up old config files (with old names)
file(GLOB OLD_CONFIG_FILES
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppConfig*.cmake
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppTargets*.cmake)
if(OLD_CONFIG_FILES)
message(
STATUS "Installation will remove old config files: ${OLD_CONFIG_FILES}")
install(CODE "file(REMOVE ${OLD_CONFIG_FILES})")
endif()
install(EXPORT cifpp
NAMESPACE cifpp::
FILE "cifpp-targets.cmake"
DESTINATION lib/cmake/cifpp)
install(
FILES ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_ddl.dic
${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic
${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_ma.dic
DESTINATION ${CMAKE_INSTALL_DATADIR}/libcifpp)
if(CIFPP_DATA_DIR AND CIFPP_DOWNLOAD_CCD)
install(FILES ${COMPONENTS_CIF}
DESTINATION ${CMAKE_INSTALL_DATADIR}/libcifpp)
endif()
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
INSTALL_DESTINATION lib/cmake/cifpp
PATH_VARS CIFPP_DATA_DIR)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake"
DESTINATION lib/cmake/cifpp)
set_target_properties(
cifpp
PROPERTIES VERSION ${PROJECT_VERSION}
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
INTERFACE_cifpp_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})
set_property(
TARGET cifpp
APPEND
PROPERTY COMPATIBLE_INTERFACE_STRING cifpp_MAJOR_VERSION)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion)
if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)
if(BUILD_TESTING)
add_subdirectory(test)
endif()

View File

@@ -32,7 +32,7 @@ The documentation can be found at [github.io](https://pdb-redo.github.io/libcifp
## Synopsis
```cpp
```c++
// A simple program counting residues with an OXT atom
#include <filesystem>
@@ -52,7 +52,7 @@ int main(int argc, char *argv[])
if (file.empty())
{
std::cerr << "Empty file\n";
std::cerr << "Empty file" << std::endl;
exit(1);
}
@@ -66,8 +66,8 @@ int main(int argc, char *argv[])
auto n = atom_site.count(cif::key("label_atom_id") == "OXT");
std::cout << "File contains " << atom_site.size() << " atoms of which "
<< n << (n == 1 ? " is" : " are") << " OXT\n"
<< "residues with an OXT are:\n";
<< n << (n == 1 ? " is" : " are") << " OXT" << std::endl
<< "residues with an OXT are:" << std::endl;
// Loop over all atoms with atom-id "OXT" and print out some info.
// That info is extracted using structured binding in C++
@@ -76,7 +76,7 @@ int main(int argc, char *argv[])
cif::key("label_atom_id") == "OXT",
"label_asym_id", "label_comp_id", "label_seq_id"))
{
std::cout << asym << ' ' << comp << ' ' << seqnr << '\n';
std::cout << asym << ' ' << comp << ' ' << seqnr << std::endl;
}
return 0;

View File

@@ -1,36 +1,3 @@
Version 8.0.0
- A dictionary is for a datablock and a file can have
datablocks with differing dictionaries.
Version 7.0.10
- Deal with missing _entity.type in reconstructing mmCIF files
- Replace code creating quaternions from rotation matrices
that might sometimes give incorrect results. Or at least,
the test code failed on this particular kind of code. Sometimes.
- Fix reconstruction to build pdbx_nonpoly_scheme
Version 7.0.9
- Using cif::file::load_dictionary it is now possible to
load a dictionary along with its extensions in one go.
E.g. file.load_dictionary("mmcif_pdbx;dssp-extension")
- Fix in compound factory to avoid errors with lower case
compound id's
- Fix sac_parser's index to be case insensitive
Version 7.0.8
- Fix PDB Remark 3 parser
- Added three way comparison for point
Version 7.0.7
- Set CIFPP_DATA_DIR on target cifpp for use in projects that include
libcifpp directly
Version 7.0.6
- Fix linking to std::atomic
Version 7.0.5
- Fix case where category index was not updated for updated value
Version 7.0.4
- Do not install headers and library in case we're not the top project

View File

@@ -1,63 +0,0 @@
# 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()

View File

@@ -27,7 +27,7 @@ Using *libcifpp* is easy, if you are familiar with modern C++:
.. literalinclude:: ../README.md
:language: c++
:start-after: ```cpp
:start-after: ```c++
:end-before: ```
.. toctree::

View File

@@ -13,7 +13,7 @@ int main(int argc, char *argv[])
exit(1);
}
cif::file file(argv[1]);
cif::file file = cif::pdb::read(argv[1]);
if (file.empty())
{

View File

@@ -138,7 +138,7 @@ struct compound_bond
/// This information is derived from the CDD by default.
///
/// To create compounds, you use the factory method. You can add your own
/// compound definitions by calling the push_dictionary function and
/// compound definitions by calling the addExtraComponents function and
/// pass it a valid CCD formatted file.
class compound
@@ -180,7 +180,8 @@ class compound
friend class local_compound_factory_impl;
compound(cif::datablock &db);
compound(cif::datablock &db, int);
std::string m_id;
std::string m_name;
std::string m_type;

View File

@@ -1284,19 +1284,6 @@ condition operator==(const key &key, const std::optional<T> &v)
return condition(new detail::key_is_empty_condition_impl(key.m_item_name));
}
/**
* @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
*/

View File

@@ -98,19 +98,6 @@ class datablock : public std::list<category>
m_name = name;
}
/**
* @brief Attempt to load the dictionary specified in audit_conform category
*
*/
void load_dictionary();
/**
* @brief Load the dictionary named @a dict_name
*
* @param dict_name
*/
void load_dictionary(std::string_view dict_name);
/**
* @brief Set the validator object to @a v
*

View File

@@ -118,6 +118,19 @@ class file : public std::list<datablock>
/** @endcond */
/**
* @brief Set the validator object to @a v
*/
void set_validator(const validator *v);
/**
* @brief Get the validator object
*/
const validator *get_validator() const
{
return m_validator;
}
/**
* @brief Validate the content and return true if everything was valid.
*
@@ -152,6 +165,21 @@ class file : public std::list<datablock>
*/
bool validate_links() const;
/**
* @brief Attempt to load a dictionary (validator) based on
* the contents of the *audit_conform* category, if available.
*/
void load_dictionary();
/**
* @brief Attempt to load the named dictionary @a name and
* create a validator based on it.
*
* @param name The name of the dictionary to load
*/
void load_dictionary(std::string_view name);
/**
* @brief Return true if a datablock with the name @a name is part of this file
*/
@@ -204,18 +232,6 @@ class file : public std::list<datablock>
/** Load the data from @a is */
void load(std::istream &is);
/** Load the data from the file specified by @a p using validator @a v */
void load(const std::filesystem::path &p, const validator &v);
/** Load the data from @a is using validator @a v */
void load(std::istream &is, const validator &v);
/** Load the data from the file specified by @a p using a validator constructed from dictionary @a dict */
void load(const std::filesystem::path &p, std::string_view dict);
/** Load the data from @a is using a validator constructed from dictionary @a dict */
void load(std::istream &is, std::string_view dict);
/** Save the data to the file specified by @a p */
void save(const std::filesystem::path &p) const;
@@ -230,6 +246,9 @@ class file : public std::list<datablock>
f.save(os);
return os;
}
private:
const validator *m_validator = nullptr;
};
} // namespace cif

View File

@@ -378,7 +378,7 @@ struct item_handle
template <typename T>
item_handle &operator=(T &&value)
{
assign_value(item{ "", std::forward<T>(value) }.value());
assign_value(item{ "", std::move(value) }.value());
return *this;
}

View File

@@ -350,12 +350,7 @@ class atom
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
bool is_alternate() const
{
if (auto alt_id = get_label_alt_id(); alt_id.empty() or alt_id == ".")
return false;
return true;
}
bool is_alternate() const { return not get_label_alt_id().empty(); }
/// Convenience method to return a string that might be ID in PDB space
std::string pdb_id() const
@@ -555,9 +550,6 @@ class residue
/// \brief Return true if this residue has alternate atoms
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
std::set<std::string> get_alternate_ids() const;
@@ -580,10 +572,6 @@ class residue
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:
/** @cond */
residue() {}
@@ -684,8 +672,6 @@ class monomer : public residue
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:
const polymer *m_polymer;
std::size_t m_index;
@@ -1105,9 +1091,6 @@ class structure
/// \brief emplace the moved atom @a atom
atom &emplace_atom(atom &&atom);
/// \brief Reorder atom_site atoms based on 'natural' ordering
void reorder_atoms();
private:
friend polymer;
friend residue;

View File

@@ -39,8 +39,6 @@
namespace cif
{
class validator;
// --------------------------------------------------------------------
/** Exception that is thrown when the mmCIF file contains a parsing error */
@@ -75,15 +73,7 @@ class sac_parser
{
public:
/** @cond */
struct iless_op
{
bool operator()(std::string_view a, std::string_view b) const
{
return icompare(a, b) < 0;
}
};
using datablock_index = std::map<std::string, std::size_t, iless_op>;
using datablock_index = std::map<std::string, std::size_t>;
virtual ~sac_parser() = default;
/** @endcond */
@@ -309,14 +299,6 @@ class sac_parser
class parser : public sac_parser
{
public:
/// \brief constructor, generates data into @a file from @a is using validator @a v
parser(std::istream &is, file &file, const validator *v)
: sac_parser(is)
, m_file(file)
, m_validator(v)
{
}
/// \brief constructor, generates data into @a file from @a is
parser(std::istream &is, file &file)
: sac_parser(is)
@@ -337,7 +319,6 @@ class parser : public sac_parser
file &m_file;
datablock *m_datablock = nullptr;
category *m_category = nullptr;
const validator *m_validator = nullptr;
row_handle m_row;
/** @endcond */

View File

@@ -662,23 +662,12 @@ struct point_type
return std::make_tuple(std::ref(m_x), std::ref(m_y), std::ref(m_z));
}
#if defined(__cpp_impl_three_way_comparison)
/// \brief a default spaceship operator
constexpr auto operator<=>(const point_type &rhs) const = default;
#else
/// \brief a default equals operator
/// \brief Compare with @a rhs
constexpr bool operator==(const point_type &rhs) const
{
return m_x == rhs.m_x and m_y == rhs.m_y and m_z == rhs.m_z;
}
/// \brief a default not-equals operator
constexpr bool operator!=(const point_type &rhs) const
{
return not operator==(rhs);
}
#endif
// consider point as a vector... perhaps I should rename point?
/// \brief looking at the point as if it is a vector, return the squared length

File diff suppressed because it is too large Load Diff

View File

@@ -718,7 +718,7 @@ bool category::is_valid() const
auto iv = m_cat_validator->get_validator_for_item(col.m_name);
if (iv == nullptr)
{
m_validator->report_error(validation_error::unknown_item, m_name, col.m_name, false);
m_validator->report_error(validation_error::unknown_item, col.m_name, m_name, false);
result = false;
}
@@ -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 value{ value_provider(oldValue) };
update_value(parent.get_row(), colIx, value, false, false);
parent.assign(colIx, value, false);
for (auto &&[childCat, linked] : m_child_links)
{
@@ -1444,7 +1444,8 @@ void category::update_value(row *row, uint16_t item, std::string_view value, boo
// before updating
bool reinsert = false;
if (m_index != nullptr and key_item_indices().count(item))
if (updateLinked and // an update of an Item's value
m_index != nullptr and key_item_indices().count(item))
{
reinsert = m_index->find(*this, row);
if (reinsert)
@@ -1697,12 +1698,6 @@ void category::swap_item(uint16_t item_ix, row_handle &a, row_handle &b)
auto &ra = *a.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));
}

View File

@@ -177,6 +177,89 @@ compound::compound(cif::datablock &db)
}
}
compound::compound(cif::datablock &db, int)
{
auto &chemComp = db["chem_comp"];
if (chemComp.size() != 1)
throw std::runtime_error("Invalid compound file, chem_comp should contain a single row");
cif::tie(m_id, m_name) =
chemComp.front().get("id", "name");
cif::trim(m_name);
m_type = "NON-POLYMER";
auto &chemCompAtom = db["chem_comp_atom"];
for (auto row : chemCompAtom)
{
compound_atom atom;
std::string type_symbol;
cif::tie(atom.id, type_symbol, atom.charge, atom.x, atom.y, atom.z) =
row.get("atom_id", "type_symbol", "charge", "x", "y", "z");
atom.type_symbol = atom_type_traits(type_symbol).type();
m_formal_charge += atom.charge;
m_atoms.push_back(std::move(atom));
}
auto &chemCompBond = db["chem_comp_bond"];
for (auto row : chemCompBond)
{
compound_bond bond;
std::string btype;
cif::tie(bond.atom_id[0], bond.atom_id[1], btype, bond.aromatic) = row.get("atom_id_1", "atom_id_2", "type", "aromatic");
using cif::iequals;
if (iequals(btype, "single"))
bond.type = bond_type::sing;
else if (iequals(btype, "double"))
bond.type = bond_type::doub;
else if (iequals(btype, "triple"))
bond.type = bond_type::trip;
else if (iequals(btype, "deloc") or iequals(btype, "aromat") or iequals(btype, "aromatic"))
bond.type = bond_type::delo;
else
{
if (cif::VERBOSE > 0)
std::cerr << "Unimplemented chem_comp_bond.type " << btype << " in " << db.name() << '\n';
bond.type = bond_type::sing;
}
m_bonds.push_back(std::move(bond));
}
// reconstruct a formula and weight
m_formula_weight = 0;
std::map<atom_type, int> f;
for (auto &atom : m_atoms)
f[atom.type_symbol] += 1;
if (f.count(atom_type::C))
{
atom_type_traits att(atom_type::C);
m_formula += att.symbol() + std::to_string(f[atom_type::C]) + ' ';
m_formula_weight += att.weight() * f[atom_type::C];
}
for (const auto &[type, count] : f)
{
if (type == atom_type::C)
continue;
atom_type_traits att(type);
m_formula += att.symbol() + std::to_string(count) + ' ';
m_formula_weight += att.weight() * count;
}
if (not m_formula.empty())
m_formula.pop_back();
}
compound_atom compound::get_atom_by_atom_id(const std::string &atom_id) const
{
compound_atom result = {};
@@ -296,6 +379,8 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
compound *get(std::string id)
{
cif::to_upper(id);
std::shared_lock lock(mMutex);
compound *result = nullptr;
@@ -316,8 +401,7 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
break;
}
if (result == nullptr and
find_if(m_missing.begin(), m_missing.end(), [&id](const std::string &m_id) { return cif::iequals(id, m_id); }) == m_missing.end())
if (result == nullptr and m_missing.count(id) == 0)
{
for (auto impl = shared_from_this(); impl; impl = impl->m_next)
{
@@ -327,7 +411,7 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
}
if (result == nullptr)
m_missing.emplace_back(id);
m_missing.insert(id);
}
return result;
@@ -360,7 +444,7 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
cif::parser::datablock_index m_index;
std::vector<compound *> m_compounds;
std::vector<std::string> m_missing;
std::set<std::string> m_missing;
std::shared_ptr<compound_factory_impl> m_next;
};
@@ -412,7 +496,7 @@ compound *compound_factory_impl::create(const std::string &id)
m_index = parser.index_datablocks();
if (cif::VERBOSE > 1)
std::cout << " done\n";
std::cout << " done" << std::endl;
// reload the resource, perhaps this should be improved...
if (m_file.empty())
@@ -435,7 +519,7 @@ compound *compound_factory_impl::create(const std::string &id)
parser.parse_single_datablock(id, m_index);
if (cif::VERBOSE > 1)
std::cout << " done\n";
std::cout << " done" << std::endl;
if (not file.empty())
{
@@ -461,20 +545,20 @@ class local_compound_factory_impl : public compound_factory_impl
: compound_factory_impl(next)
, 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] :
// 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];
// if (rdb.empty())
// {
// // std::cerr << "Missing data in restraint file for id " + id + '\n';
// continue;
// }
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"))
{
auto &rdb = m_local_file["comp_" + id];
if (rdb.empty())
{
std::cerr << "Missing data in restraint file for id " + id + '\n';
continue;
}
// construct_compound(rdb, id, name, threeLetterCode, group);
// }
construct_compound(rdb, id, name, threeLetterCode, group);
}
}
compound *create(const std::string &id) override;
@@ -492,7 +576,7 @@ compound *local_compound_factory_impl::create(const std::string &id)
for (auto &db : m_local_file)
{
if (db.name() == id)
if (db.name() == "comp_" + id)
{
auto chem_comp = db.get("chem_comp");
if (not chem_comp)

View File

@@ -115,33 +115,27 @@ namespace detail
auto first = subs.front();
auto &fc = first->m_sub;
for (size_t fc_i = 0; fc_i < fc.size();)
for (auto c : fc)
{
auto c = fc[fc_i];
if (not found_in_range(c, subs.begin() + 1, subs.end())) {
++fc_i;
if (not found_in_range(c, subs.begin() + 1, subs.end()))
continue;
}
if (and_result == nullptr)
and_result = new and_condition_impl();
and_result->m_sub.push_back(c);
fc.erase(fc.begin() + fc_i);
fc.erase(remove(fc.begin(), fc.end(), c), fc.end());
for (auto sub : subs)
{
auto &ssub = sub->m_sub;
for (size_t ssub_i = 0; ssub_i < ssub.size();)
for (auto sc : ssub)
{
auto sc = ssub[ssub_i];
if (not sc->equals(c)) {
++ssub_i;
if (not sc->equals(c))
continue;
}
ssub.erase(ssub.begin() + ssub_i);
ssub.erase(remove(ssub.begin(), ssub.end(), sc), ssub.end());
delete sc;
break;
}

View File

@@ -38,35 +38,6 @@ datablock::datablock(const datablock &db)
cat.update_links(*this);
}
void datablock::load_dictionary()
{
if (auto *audit_conform = get("audit_conform"); audit_conform and not audit_conform->empty())
{
std::string name = audit_conform->front().get<std::string>("dict_name");
if (name == "mmcif_pdbx_v50")
name = "mmcif_pdbx.dic"; // we had a bug here in libcifpp...
if (not name.empty())
{
try
{
load_dictionary(name);
}
catch (const std::exception &ex)
{
if (VERBOSE)
std::cerr << "Failed to load dictionary " << std::quoted(name) << ": " << ex.what() << '\n';
}
}
}
}
void datablock::load_dictionary(std::string_view name)
{
set_validator(&validator_factory::instance()[name]);
}
void datablock::set_validator(const validator *v)
{
m_validator = v;
@@ -78,7 +49,6 @@ void datablock::set_validator(const validator *v)
}
catch (const std::exception &)
{
m_validator = nullptr;
throw_with_nested(std::runtime_error("Error while setting validator in datablock " + m_name));
}
}
@@ -91,7 +61,7 @@ const validator *datablock::get_validator() const
bool datablock::is_valid() const
{
if (m_validator == nullptr)
throw std::runtime_error("Validator not specified for datablock data_" + name());
throw std::runtime_error("Validator not specified");
bool result = true;
for (auto &cat : *this)
@@ -103,7 +73,7 @@ bool datablock::is_valid() const
bool datablock::is_valid()
{
if (m_validator == nullptr)
throw std::runtime_error("Validator not specified for datablock data_" + name());
throw std::runtime_error("Validator not specified");
bool result = true;
for (auto &cat : *this)

View File

@@ -30,8 +30,19 @@
namespace cif
{
// --------------------------------------------------------------------
void file::set_validator(const validator *v)
{
m_validator = v;
for (auto &db : *this)
db.set_validator(v);
}
bool file::is_valid() const
{
if (m_validator == nullptr)
std::runtime_error("No validator loaded explicitly, cannot continue");
bool result = true;
for (auto &d : *this)
result = d.is_valid() and result;
@@ -44,6 +55,14 @@ bool file::is_valid() const
bool file::is_valid()
{
if (m_validator == nullptr)
{
if (VERBOSE > 0)
std::cerr << "No dictionary loaded explicitly, loading default\n";
load_dictionary();
}
bool result = not empty();
for (auto &d : *this)
@@ -57,54 +76,56 @@ bool file::is_valid()
bool file::validate_links() const
{
if (m_validator == nullptr)
std::runtime_error("No validator loaded explicitly, cannot continue");
bool result = true;
for (auto &db : *this)
result = db.validate_links() and result;
return result;
}
// void file::load_dictionary()
// {
// if (not empty())
// {
// auto *audit_conform = front().get("audit_conform");
// if (audit_conform and not audit_conform->empty())
// {
// std::string name = audit_conform->front().get<std::string>("dict_name");
void file::load_dictionary()
{
if (not empty())
{
auto *audit_conform = front().get("audit_conform");
if (audit_conform and not audit_conform->empty())
{
std::string name = audit_conform->front().get<std::string>("dict_name");
// if (name == "mmcif_pdbx_v50")
// name = "mmcif_pdbx.dic"; // we had a bug here in libcifpp...
if (name == "mmcif_pdbx_v50")
name = "mmcif_pdbx.dic"; // we had a bug here in libcifpp...
// if (not name.empty())
// {
// try
// {
// load_dictionary(name);
// }
// catch (const std::exception &ex)
// {
// if (VERBOSE)
// std::cerr << "Failed to load dictionary " << std::quoted(name) << ": " << ex.what() << '\n';
// }
// }
// }
// }
if (not name.empty())
{
try
{
load_dictionary(name);
}
catch (const std::exception &ex)
{
if (VERBOSE)
std::cerr << "Failed to load dictionary " << std::quoted(name) << ": " << ex.what() << '\n';
}
}
}
}
// // if (not m_validator)
// // load_dictionary("mmcif_pdbx.dic"); // TODO: maybe incorrect? Perhaps improve?
// }
// if (not m_validator)
// load_dictionary("mmcif_pdbx.dic"); // TODO: maybe incorrect? Perhaps improve?
}
// void file::load_dictionary(std::string_view name)
// {
// set_validator(&validator_factory::instance()[name]);
// }
void file::load_dictionary(std::string_view name)
{
set_validator(&validator_factory::instance()[name]);
}
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)
@@ -144,7 +165,10 @@ std::tuple<file::iterator, bool> file::emplace(std::string_view name)
}
if (is_new)
{
i = insert(end(), { name });
i->set_validator(m_validator);
}
assert(i != end());
return std::make_tuple(i, is_new);
@@ -166,44 +190,18 @@ void file::load(const std::filesystem::path &p)
}
}
void file::load(const std::filesystem::path &p, std::string_view dict)
{
load(p, validator_factory::instance().operator[](dict));
}
void file::load(std::istream &is, std::string_view dict)
{
load(is, validator_factory::instance().operator[](dict));
}
void file::load(const std::filesystem::path &p, const validator &v)
{
gzio::ifstream in(p);
if (not in.is_open())
throw std::runtime_error("Could not open file '" + p.string() + '\'');
try
{
load(in, v);
}
catch (const std::exception &)
{
throw_with_nested(std::runtime_error("Error reading file '" + p.string() + '\''));
}
}
void file::load(std::istream &is, const validator &v)
{
parser p(is, *this);
p.parse_file();
for (auto &db : *this)
db.set_validator(&v);
}
void file::load(std::istream &is)
{
auto saved = m_validator;
set_validator(nullptr);
parser p(is, *this);
p.parse_file();
if (saved != nullptr)
set_validator(saved);
else
load_dictionary();
}
void file::save(const std::filesystem::path &p) const

View File

@@ -313,7 +313,7 @@ residue::residue(structure &structure, const std::vector<atom> &atoms)
{
if (atoms.empty())
throw std::runtime_error("Empty list of atoms");
auto &a = atoms.front();
m_compound_id = a.get_label_comp_id();
@@ -352,42 +352,9 @@ EntityType residue::entity_type() const
void residue::add_atom(atom &atom)
{
// update atom since it is now part of this residue
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> result;
@@ -488,12 +455,6 @@ bool residue::has_alternate_atoms() const
{ 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> ids;
@@ -712,26 +673,25 @@ float monomer::omega() const
}
const std::map<std::string, std::vector<std::string>> kChiAtomsMap = {
{ "ASP", { "CG", "OD1" } },
{ "ASN", { "CG", "OD1" } },
{ "ARG", { "CG", "CD", "NE", "CZ" } },
{ "HIS", { "CG", "ND1" } },
{ "GLN", { "CG", "CD", "OE1" } },
{ "GLU", { "CG", "CD", "OE1" } },
{ "SER", { "OG" } },
{ "THR", { "OG1" } },
{ "LYS", { "CG", "CD", "CE", "NZ" } },
{ "TYR", { "CG", "CD1" } },
{ "PHE", { "CG", "CD1" } },
{ "LEU", { "CG", "CD1" } },
{ "TRP", { "CG", "CD1" } },
{ "CYS", { "SG" } },
{ "ILE", { "CG1", "CD1" } },
{ "MET", { "CG", "SD", "CE" } },
{ "MSE", { "CG", "SE", "CE" } },
{ "PRO", { "CG", "CD" } },
{ "VAL", { "CG1" } }
};
{"ASP", {"CG", "OD1"}},
{"ASN", {"CG", "OD1"}},
{"ARG", {"CG", "CD", "NE", "CZ"}},
{"HIS", {"CG", "ND1"}},
{"GLN", {"CG", "CD", "OE1"}},
{"GLU", {"CG", "CD", "OE1"}},
{"SER", {"OG"}},
{"THR", {"OG1"}},
{"LYS", {"CG", "CD", "CE", "NZ"}},
{"TYR", {"CG", "CD1"}},
{"PHE", {"CG", "CD1"}},
{"LEU", {"CG", "CD1"}},
{"TRP", {"CG", "CD1"}},
{"CYS", {"SG"}},
{"ILE", {"CG1", "CD1"}},
{"MET", {"CG", "SD", "CE"}},
{"MSE", {"CG", "SE", "CE"}},
{"PRO", {"CG", "CD"}},
{"VAL", {"CG1"}}};
std::size_t monomer::nr_of_chis() const
{
@@ -753,7 +713,7 @@ float monomer::chi(std::size_t nr) const
auto i = kChiAtomsMap.find(m_compound_id);
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());
@@ -879,8 +839,7 @@ 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("C").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]);
double omega = dihedral_angle(atoms[0], atoms[1], atoms[2], atoms[3]);
@@ -921,15 +880,6 @@ bool monomer::is_cis(const monomer &a, const monomer &b)
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
@@ -966,7 +916,7 @@ polymer::polymer(structure &s, const std::string &entityID, const std::string &a
}
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';
}
}
@@ -1034,6 +984,7 @@ sugar::sugar(sugar &&rhs)
: residue(std::forward<residue>(rhs))
, m_branch(rhs.m_branch)
{
}
sugar &sugar::operator=(sugar &&rhs)
@@ -1097,19 +1048,19 @@ cif::mm::atom sugar::add_atom(row_initializer atom_info)
auto atom_id = atom_site.get_unique_id("");
atom_info.set_value({ "group_PDB", "HETATM" });
atom_info.set_value({ "id", atom_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_comp_id", m_compound_id });
atom_info.set_value({ "label_seq_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_comp_id", m_compound_id });
atom_info.set_value({ "auth_seq_id", m_auth_seq_id });
atom_info.set_value({ "occupancy", 1.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({"group_PDB", "HETATM"});
atom_info.set_value({"id", atom_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_comp_id", m_compound_id});
atom_info.set_value({"label_seq_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_comp_id", m_compound_id});
atom_info.set_value({"auth_seq_id", m_auth_seq_id});
atom_info.set_value({"occupancy", 1.0, 2});
atom_info.set_value({"B_iso_or_equiv", 30.0, 2});
atom_info.set_value({"pdbx_PDB_model_num", 1});
auto row = atom_site.emplace(std::move(atom_info));
auto result = m_structure->emplace_atom(db, row);
@@ -1167,7 +1118,7 @@ void branch::link_atoms()
auto entity_id = front().get_entity_id();
for (const auto &[num1, num2, atom1, atom2] : branch_link.find<std::size_t, std::size_t, std::string, std::string>(
"entity_id"_key == entity_id, "entity_branch_list_num_1", "entity_branch_list_num_2", "atom_id_1", "atom_id_2"))
"entity_id"_key == entity_id, "entity_branch_list_num_1", "entity_branch_list_num_2", "atom_id_1", "atom_id_2"))
{
// if (not iequals(atom1, "c1"))
// throw std::runtime_error("invalid pdbx_entity_branch_link");
@@ -1182,11 +1133,10 @@ void branch::link_atoms()
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())
throw std::out_of_range("Sugar with num " + std::to_string(nr) + " not found in branch " + m_asym_id);
return *i;
}
@@ -1207,29 +1157,32 @@ sugar &branch::construct_sugar(const std::string &compound_id)
auto r = chemComp.find(key("id") == compound_id);
if (r.empty())
{
chemComp.emplace({ { "id", compound_id },
{ "name", compound->name() },
{ "formula", compound->formula() },
{ "formula_weight", compound->formula_weight() },
{ "type", compound->type() } });
chemComp.emplace({
{"id", compound_id},
{"name", compound->name()},
{"formula", compound->formula()},
{"formula_weight", compound->formula_weight()},
{"type", compound->type()}});
}
sugar &result = emplace_back(*this, compound_id, m_asym_id, static_cast<int>(size() + 1));
db["pdbx_branch_scheme"].emplace({ { "asym_id", result.get_asym_id() },
{ "entity_id", result.get_entity_id() },
{ "num", result.num() },
{ "mon_id", result.get_compound_id() },
db["pdbx_branch_scheme"].emplace({
{"asym_id", result.get_asym_id()},
{"entity_id", result.get_entity_id()},
{"num", result.num()},
{"mon_id", result.get_compound_id()},
{ "pdb_asym_id", result.get_asym_id() },
{ "pdb_seq_num", result.num() },
{ "pdb_mon_id", result.get_compound_id() },
{"pdb_asym_id", result.get_asym_id()},
{"pdb_seq_num", result.num()},
{"pdb_mon_id", result.get_compound_id()},
{ "auth_asym_id", result.get_auth_asym_id() },
{ "auth_mon_id", result.get_compound_id() },
{ "auth_seq_num", result.get_auth_seq_id() },
{"auth_asym_id", result.get_auth_asym_id()},
{"auth_mon_id", result.get_compound_id()},
{"auth_seq_num", result.get_auth_seq_id()},
{ "hetero", "n" } });
{"hetero", "n"}
});
return result;
}
@@ -1247,17 +1200,19 @@ sugar &branch::construct_sugar(const std::string &compound_id, const std::string
auto &pdbx_entity_branch_link = db["pdbx_entity_branch_link"];
auto linkID = pdbx_entity_branch_link.get_unique_id("");
db["pdbx_entity_branch_link"].emplace({ { "link_id", linkID },
db["pdbx_entity_branch_link"].emplace({
{ "link_id", linkID },
{ "entity_id", get_entity_id() },
{ "entity_branch_list_num_1", result.num() },
{ "comp_id_1", compound_id },
{ "entity_branch_list_num_1", result.num() },
{ "comp_id_1", compound_id },
{ "atom_id_1", atom_id },
{ "leaving_atom_id_1", "O1" }, /// TODO: Need to fix this!
{ "entity_branch_list_num_2", linked.num() },
{ "comp_id_2", linked.get_compound_id() },
{ "atom_id_2", linked_atom_id },
{ "leaving_atom_id_2", "." },
{ "value_order", "sing" } });
{ "leaving_atom_id_1", "O1" }, /// TODO: Need to fix this!
{ "entity_branch_list_num_2", linked.num() },
{ "comp_id_2", linked.get_compound_id() },
{ "atom_id_2", linked_atom_id },
{ "leaving_atom_id_2", "." },
{ "value_order", "sing" }
});
return result;
}
@@ -1366,7 +1321,7 @@ void structure::load_data()
{
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)
m_polymers.emplace_back(*this, entityID, asym_id, auth_asym_id);
@@ -1374,7 +1329,7 @@ void structure::load_data()
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)
m_branches.emplace_back(*this, asym_id, entity_id);
@@ -1382,8 +1337,8 @@ void structure::load_data()
auto &nonPolyScheme = m_db["pdbx_nonpoly_scheme"];
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"))
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"))
m_non_polymers.emplace_back(*this, monID, asym_id, 0, pdbStrandID, pdbSeqNum, pdbInsCode);
// place atoms in residues
@@ -1394,18 +1349,18 @@ void structure::load_data()
for (auto &poly : m_polymers)
{
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)
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;
for (auto &branch : m_branches)
{
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());
}
}
@@ -1437,9 +1392,7 @@ void structure::load_data()
}
// 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)
branch.link_atoms();
@@ -1685,7 +1638,7 @@ residue &structure::get_residue(const std::string &asym_id, int seqID, const std
if (seqID != 0)
desc += "/" + std::to_string(seqID);
if (not authSeqID.empty())
desc += "-" + authSeqID;
@@ -1731,7 +1684,7 @@ residue &structure::get_residue(const std::string &asym_id, const std::string &c
if (seqID != 0)
desc += "/" + std::to_string(seqID);
if (not authSeqID.empty())
desc += "-" + authSeqID;
@@ -1761,11 +1714,12 @@ std::string structure::insert_compound(const std::string &compoundID, bool is_en
auto r = chemComp.find(key("id") == compoundID);
if (r.empty())
{
chemComp.emplace({ { "id", compoundID },
{ "name", compound->name() },
{ "formula", compound->formula() },
{ "formula_weight", compound->formula_weight() },
{ "type", compound->type() } });
chemComp.emplace({
{"id", compoundID},
{"name", compound->name()},
{"formula", compound->formula()},
{"formula_weight", compound->formula_weight()},
{"type", compound->type()}});
}
std::string entity_id;
@@ -1781,14 +1735,16 @@ std::string structure::insert_compound(const std::string &compoundID, bool is_en
auto &entity = m_db["entity"];
entity_id = entity.get_unique_id("");
entity.emplace({ { "id", entity_id },
{ "type", "non-polymer" },
{ "pdbx_description", compound->name() },
{ "formula_weight", compound->formula_weight() } });
entity.emplace({
{"id", entity_id},
{"type", "non-polymer"},
{"pdbx_description", compound->name()},
{"formula_weight", compound->formula_weight()}});
pdbxEntityNonpoly.emplace({ { "entity_id", entity_id },
{ "name", compound->name() },
{ "comp_id", compoundID } });
pdbxEntityNonpoly.emplace({
{"entity_id", entity_id},
{"name", compound->name()},
{"comp_id", compoundID}});
}
}
@@ -1817,7 +1773,7 @@ atom &structure::emplace_atom(atom &&atom)
R = i - 1;
}
if (R == -1) // msvc...
if (R == -1) // msvc...
m_atom_index.insert(m_atom_index.begin(), m_atoms.size());
else
m_atom_index.insert(m_atom_index.begin() + R + 1, m_atoms.size());
@@ -1873,15 +1829,19 @@ void structure::remove_atom(atom &a, bool removeFromResidue)
for (std::string prefix : { "ptnr1_", "ptnr2_", "pdbx_ptnr3_" })
{
if (a.get_label_seq_id() == 0)
cond = std::move(cond) or (cif::key(prefix + "label_asym_id") == a.get_label_asym_id() and
cif::key(prefix + "label_seq_id") == null and
cif::key(prefix + "auth_seq_id") == a.get_auth_seq_id() and
cif::key(prefix + "label_atom_id") == a.get_label_atom_id());
cond = std::move(cond) or (
cif::key(prefix + "label_asym_id") == a.get_label_asym_id() and
cif::key(prefix + "label_seq_id") == null and
cif::key(prefix + "auth_seq_id") == a.get_auth_seq_id() and
cif::key(prefix + "label_atom_id") == a.get_label_atom_id()
);
else
cond = std::move(cond) or (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 + "auth_seq_id") == a.get_auth_seq_id() and
cif::key(prefix + "label_atom_id") == a.get_label_atom_id());
cond = std::move(cond) or (
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 + "auth_seq_id") == a.get_auth_seq_id() and
cif::key(prefix + "label_atom_id") == a.get_label_atom_id()
);
}
if (cond)
@@ -1987,15 +1947,15 @@ void structure::change_residue(residue &res, const std::string &newCompound,
if (entityID.empty())
{
entityID = entity.get_unique_id("");
entity.emplace({ { "id", entityID },
{ "type", "non-polymer" },
{ "pdbx_description", compound->name() },
{ "formula_weight", compound->formula_weight() } });
entity.emplace({{"id", entityID},
{"type", "non-polymer"},
{"pdbx_description", compound->name()},
{"formula_weight", compound->formula_weight()}});
auto &pdbxEntityNonpoly = m_db["pdbx_entity_nonpoly"];
pdbxEntityNonpoly.emplace({ { "entity_id", entityID },
{ "name", compound->name() },
{ "comp_id", newCompound } });
pdbxEntityNonpoly.emplace({{"entity_id", entityID},
{"name", compound->name()},
{"comp_id", newCompound}});
}
auto &pdbxNonPolyScheme = m_db["pdbx_nonpoly_scheme"];
@@ -2011,11 +1971,11 @@ void structure::change_residue(residue &res, const std::string &newCompound,
auto &chemComp = m_db["chem_comp"];
if (not chemComp.contains(key("id") == newCompound))
{
chemComp.emplace({ { "id", newCompound },
{ "name", compound->name() },
{ "formula", compound->formula() },
{ "formula_weight", compound->formula_weight() },
{ "type", compound->type() } });
chemComp.emplace({{"id", newCompound},
{"name", compound->name()},
{"formula", compound->formula()},
{"formula_weight", compound->formula_weight()},
{"type", compound->type()}});
}
// update the struct_asym for the new entity
@@ -2145,7 +2105,7 @@ void structure::remove_residue(residue &res)
case EntityType::Branched:
{
auto &s = dynamic_cast<sugar &>(res);
auto &s = dynamic_cast<sugar&>(res);
remove_sugar(s);
@@ -2173,7 +2133,7 @@ void structure::remove_sugar(sugar &s)
throw std::runtime_error("sugar not part of branch");
std::size_t six = si - branch.begin();
if (six == 0) // first sugar, means the death of this branch
if (six == 0) // first sugar, means the death of this branch
remove_branch(branch);
else
{
@@ -2188,7 +2148,7 @@ void structure::remove_sugar(sugar &s)
if (dix.count(tix))
continue;
dix.insert(tix);
for (auto &s2 : branch)
@@ -2201,9 +2161,7 @@ void structure::remove_sugar(sugar &s)
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);
@@ -2223,21 +2181,23 @@ void structure::remove_sugar(sugar &s)
for (auto &sugar : branch)
{
pdbx_branch_scheme.emplace({ { "asym_id", asym_id },
{ "entity_id", entity_id },
{ "num", sugar.num() },
{ "mon_id", sugar.get_compound_id() },
pdbx_branch_scheme.emplace({
{"asym_id", asym_id},
{"entity_id", entity_id},
{"num", sugar.num()},
{"mon_id", sugar.get_compound_id()},
{ "pdb_asym_id", asym_id },
{ "pdb_seq_num", sugar.num() },
{ "pdb_mon_id", sugar.get_compound_id() },
{"pdb_asym_id", asym_id},
{"pdb_seq_num", sugar.num()},
{"pdb_mon_id", sugar.get_compound_id()},
// TODO: need fix, collect from nag_atoms?
{ "auth_asym_id", asym_id },
{ "auth_mon_id", sugar.get_compound_id() },
{ "auth_seq_num", sugar.get_auth_seq_id() },
{"auth_asym_id", asym_id},
{"auth_mon_id", sugar.get_compound_id()},
{"auth_seq_num", sugar.get_auth_seq_id()},
{ "hetero", "n" } });
{"hetero", "n"}
});
}
}
}
@@ -2272,11 +2232,13 @@ std::string structure::create_non_poly(const std::string &entity_id, const std::
auto &struct_asym = m_db["struct_asym"];
std::string asym_id = struct_asym.get_unique_id();
struct_asym.emplace({ { "id", asym_id },
{ "pdbx_blank_PDB_chainid_flag", "N" },
{ "pdbx_modified", "N" },
{ "entity_id", entity_id },
{ "details", "?" } });
struct_asym.emplace({
{"id", asym_id},
{"pdbx_blank_PDB_chainid_flag", "N"},
{"pdbx_modified", "N"},
{"entity_id", entity_id},
{"details", "?"}
});
std::string comp_id = m_db["pdbx_entity_nonpoly"].find1<std::string>("entity_id"_key == entity_id, "comp_id");
@@ -2288,27 +2250,29 @@ std::string structure::create_non_poly(const std::string &entity_id, const std::
{
auto atom_id = atom_site.get_unique_id("");
auto row = atom_site.emplace({ { "group_PDB", atom.get_property("group_PDB") },
{ "id", atom_id },
{ "type_symbol", atom.get_property("type_symbol") },
{ "label_atom_id", atom.get_property("label_atom_id") },
{ "label_alt_id", atom.get_property("label_alt_id") },
{ "label_comp_id", comp_id },
{ "label_asym_id", asym_id },
{ "label_entity_id", entity_id },
{ "label_seq_id", "." },
{ "pdbx_PDB_ins_code", "" },
{ "Cartn_x", atom.get_property("Cartn_x") },
{ "Cartn_y", atom.get_property("Cartn_y") },
{ "Cartn_z", atom.get_property("Cartn_z") },
{ "occupancy", atom.get_property("occupancy") },
{ "B_iso_or_equiv", atom.get_property("B_iso_or_equiv") },
{ "pdbx_formal_charge", atom.get_property("pdbx_formal_charge") },
{ "auth_seq_id", 1 },
{ "auth_comp_id", comp_id },
{ "auth_asym_id", asym_id },
{ "auth_atom_id", atom.get_property("label_atom_id") },
{ "pdbx_PDB_model_num", 1 } });
auto row = atom_site.emplace({
{"group_PDB", atom.get_property("group_PDB")},
{"id", atom_id},
{"type_symbol", atom.get_property("type_symbol")},
{"label_atom_id", atom.get_property("label_atom_id")},
{"label_alt_id", atom.get_property("label_alt_id")},
{"label_comp_id", comp_id},
{"label_asym_id", asym_id},
{"label_entity_id", entity_id},
{"label_seq_id", "."},
{"pdbx_PDB_ins_code", ""},
{"Cartn_x", atom.get_property("Cartn_x")},
{"Cartn_y", atom.get_property("Cartn_y")},
{"Cartn_z", atom.get_property("Cartn_z")},
{"occupancy", atom.get_property("occupancy")},
{"B_iso_or_equiv", atom.get_property("B_iso_or_equiv")},
{"pdbx_formal_charge", atom.get_property("pdbx_formal_charge")},
{"auth_seq_id", 1},
{"auth_comp_id", comp_id},
{"auth_asym_id", asym_id},
{"auth_atom_id", atom.get_property("label_atom_id")},
{"pdbx_PDB_model_num", 1}
});
auto &newAtom = emplace_atom(std::make_shared<atom::atom_impl>(m_db, atom_id));
res.add_atom(newAtom);
@@ -2317,16 +2281,16 @@ std::string structure::create_non_poly(const std::string &entity_id, const std::
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;
pdbx_nonpoly_scheme.emplace({
{ "asym_id", asym_id },
{ "entity_id", entity_id },
{ "mon_id", comp_id },
{ "ndb_seq_num", ndb_nr },
{ "pdb_seq_num", res.get_auth_seq_id() },
{ "auth_seq_num", res.get_auth_seq_id() },
{ "pdb_mon_id", comp_id },
{ "auth_mon_id", comp_id },
{ "pdb_strand_id", asym_id },
{ "pdb_ins_code", "." },
{"asym_id", asym_id},
{"entity_id", entity_id},
{"mon_id", comp_id},
{"ndb_seq_num", ndb_nr},
{"pdb_seq_num", res.get_auth_seq_id()},
{"auth_seq_num", res.get_auth_seq_id()},
{"pdb_mon_id", comp_id},
{"auth_mon_id", comp_id},
{"pdb_strand_id", asym_id},
{"pdb_ins_code", "."},
});
return asym_id;
@@ -2339,11 +2303,13 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector
auto &struct_asym = m_db["struct_asym"];
std::string asym_id = struct_asym.get_unique_id();
struct_asym.emplace({ { "id", asym_id },
{ "pdbx_blank_PDB_chainid_flag", "N" },
{ "pdbx_modified", "N" },
{ "entity_id", entity_id },
{ "details", "?" } });
struct_asym.emplace({
{"id", asym_id},
{"pdbx_blank_PDB_chainid_flag", "N"},
{"pdbx_modified", "N"},
{"entity_id", entity_id},
{"details", "?"}
});
std::string comp_id = m_db["pdbx_entity_nonpoly"].find1<std::string>("entity_id"_key == entity_id, "comp_id");
@@ -2360,14 +2326,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("label_entity_id", entity_id);
atom.set_value_if_empty({ "group_PDB", "HETATM" });
atom.set_value_if_empty({ "label_comp_id", comp_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_seq_id", 1 });
atom.set_value_if_empty({ "pdbx_PDB_model_num", 1 });
atom.set_value_if_empty({ "label_alt_id", "" });
atom.set_value_if_empty({ "occupancy", 1.0, 2 });
atom.set_value_if_empty({"group_PDB", "HETATM"});
atom.set_value_if_empty({"label_comp_id", comp_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_seq_id", 1});
atom.set_value_if_empty({"pdbx_PDB_model_num", 1});
atom.set_value_if_empty({"label_alt_id", ""});
atom.set_value_if_empty({"occupancy", 1.0, 2});
auto row = atom_site.emplace(atom.begin(), atom.end());
@@ -2378,16 +2344,16 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector
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;
pdbx_nonpoly_scheme.emplace({
{ "asym_id", asym_id },
{ "entity_id", entity_id },
{ "mon_id", comp_id },
{ "ndb_seq_num", ndb_nr },
{ "pdb_seq_num", res.get_auth_seq_id() },
{ "auth_seq_num", res.get_auth_seq_id() },
{ "pdb_mon_id", comp_id },
{ "auth_mon_id", comp_id },
{ "pdb_strand_id", asym_id },
{ "pdb_ins_code", "." },
{"asym_id", asym_id},
{"entity_id", entity_id},
{"mon_id", comp_id},
{"ndb_seq_num", ndb_nr},
{"pdb_seq_num", res.get_auth_seq_id()},
{"auth_seq_num", res.get_auth_seq_id()},
{"pdb_mon_id", comp_id},
{"auth_mon_id", comp_id},
{"pdb_strand_id", asym_id},
{"pdb_ins_code", "."},
});
return asym_id;
@@ -2409,13 +2375,15 @@ void structure::create_water(row_initializer atom)
{
asym_id = struct_asym.get_unique_id();
struct_asym.emplace({ { "id", asym_id },
{ "pdbx_blank_PDB_chainid_flag", "N" },
{ "pdbx_modified", "N" },
{ "entity_id", entity_id },
{ "details", "?" } });
struct_asym.emplace({
{"id", asym_id},
{"pdbx_blank_PDB_chainid_flag", "N"},
{"pdbx_modified", "N"},
{"entity_id", entity_id},
{"details", "?"}
});
}
auto &atom_site = m_db["atom_site"];
auto auth_seq_id = atom_site.find_max<int>("auth_seq_id", "label_entity_id"_key == entity_id) + 1;
if (auth_seq_id < 0)
@@ -2429,13 +2397,13 @@ void structure::create_water(row_initializer atom)
atom.set_value("label_entity_id", entity_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({ "label_comp_id", "HOH" });
atom.set_value_if_empty({ "label_seq_id", "." });
atom.set_value_if_empty({ "auth_comp_id", "HOH" });
atom.set_value_if_empty({ "pdbx_PDB_model_num", 1 });
atom.set_value_if_empty({ "label_alt_id", "" });
atom.set_value_if_empty({ "occupancy", 1.0, 2 });
atom.set_value_if_empty({"group_PDB", "HETATM"});
atom.set_value_if_empty({"label_comp_id", "HOH"});
atom.set_value_if_empty({"label_seq_id", "."});
atom.set_value_if_empty({"auth_comp_id", "HOH"});
atom.set_value_if_empty({"pdbx_PDB_model_num", 1});
atom.set_value_if_empty({"label_alt_id", ""});
atom.set_value_if_empty({"occupancy", 1.0, 2});
auto row = atom_site.emplace(atom.begin(), atom.end());
@@ -2444,16 +2412,16 @@ void structure::create_water(row_initializer atom)
auto &pdbx_nonpoly_scheme = m_db["pdbx_nonpoly_scheme"];
int ndb_nr = pdbx_nonpoly_scheme.find_max<int>("ndb_seq_num") + 1;
pdbx_nonpoly_scheme.emplace({
{ "asym_id", asym_id },
{ "entity_id", entity_id },
{ "mon_id", "HOH" },
{ "ndb_seq_num", ndb_nr },
{ "pdb_seq_num", auth_seq_id },
{ "auth_seq_num", auth_seq_id },
{ "pdb_mon_id", "HOH" },
{ "auth_mon_id", "HOH" },
{ "pdb_strand_id", asym_id },
{ "pdb_ins_code", "." },
{"asym_id", asym_id},
{"entity_id", entity_id},
{"mon_id", "HOH"},
{"ndb_seq_num", ndb_nr},
{"pdb_seq_num", auth_seq_id},
{"auth_seq_num", auth_seq_id},
{"pdb_mon_id", "HOH"},
{"auth_mon_id", "HOH"},
{"pdb_strand_id", asym_id},
{"pdb_ins_code", "."},
});
}
@@ -2465,14 +2433,18 @@ branch &structure::create_branch()
auto entity_id = entity.get_unique_id("");
auto asym_id = struct_asym.get_unique_id();
entity.emplace({ { "id", entity_id },
{ "type", "branched" } });
entity.emplace({
{"id", entity_id},
{"type", "branched"}
});
struct_asym.emplace({ { "id", asym_id },
{ "pdbx_blank_PDB_chainid_flag", "N" },
{ "pdbx_modified", "N" },
{ "entity_id", entity_id },
{ "details", "?" } });
struct_asym.emplace({
{"id", asym_id},
{"pdbx_blank_PDB_chainid_flag", "N"},
{"pdbx_modified", "N"},
{"entity_id", entity_id},
{"details", "?"}
});
return m_branches.emplace_back(*this, asym_id, entity_id);
}
@@ -2669,19 +2641,22 @@ std::string structure::create_entity_for_branch(branch &branch)
if (VERBOSE)
std::cout << "Creating new entity " << entityID << " for branched sugar " << entityName << '\n';
entity.emplace({ { "id", entityID },
{ "type", "branched" },
{ "src_method", "man" },
{ "pdbx_description", entityName },
{ "formula_weight", branch.weight() } });
entity.emplace({
{"id", entityID},
{"type", "branched"},
{"src_method", "man"},
{"pdbx_description", entityName},
{"formula_weight", branch.weight()}});
auto &pdbx_entity_branch_list = m_db["pdbx_entity_branch_list"];
for (auto &sugar : branch)
{
pdbx_entity_branch_list.emplace({ { "entity_id", entityID },
{ "comp_id", sugar.get_compound_id() },
{ "num", sugar.num() },
{ "hetero", "n" } });
pdbx_entity_branch_list.emplace({
{"entity_id", entityID},
{"comp_id", sugar.get_compound_id()},
{"num", sugar.num()},
{"hetero", "n"}
});
}
auto &pdbx_entity_branch_link = m_db["pdbx_entity_branch_link"];
@@ -2695,17 +2670,19 @@ std::string structure::create_entity_for_branch(branch &branch)
auto &s2 = branch.at(stoi(l2.get_auth_seq_id()) - 1);
auto l1 = s2.get_atom_by_atom_id("C1");
pdbx_entity_branch_link.emplace({ { "link_id", pdbx_entity_branch_link.get_unique_id("") },
{ "entity_id", entityID },
{ "entity_branch_list_num_1", s1.get_auth_seq_id() },
{ "comp_id_1", s1.get_compound_id() },
{ "atom_id_1", l1.get_label_atom_id() },
{ "leaving_atom_id_1", "O1" },
{ "entity_branch_list_num_2", s2.get_auth_seq_id() },
{ "comp_id_2", s2.get_compound_id() },
{ "atom_id_2", l2.get_label_atom_id() },
{ "leaving_atom_id_2", "H" + l2.get_label_atom_id() },
{ "value_order", "sing" } });
pdbx_entity_branch_link.emplace({
{"link_id", pdbx_entity_branch_link.get_unique_id("")},
{"entity_id", entityID},
{"entity_branch_list_num_1", s1.get_auth_seq_id()},
{"comp_id_1", s1.get_compound_id()},
{"atom_id_1", l1.get_label_atom_id()},
{"leaving_atom_id_1", "O1"},
{"entity_branch_list_num_2", s2.get_auth_seq_id()},
{"comp_id_2", s2.get_compound_id()},
{"atom_id_2", l2.get_label_atom_id()},
{"leaving_atom_id_2", "H" + l2.get_label_atom_id()},
{"value_order", "sing"}
});
}
}
@@ -2748,26 +2725,12 @@ void structure::cleanup_empty_categories()
obsoleteEntities.push_back(entity);
}
auto validator = m_db.get_validator();
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);
}
// the rest?
for (const char *cat : { "pdbx_entity_nonpoly" })
for (const char *cat : {"pdbx_entity_nonpoly"})
{
auto &category = m_db[cat];
@@ -2873,89 +2836,4 @@ void structure::validate_atoms() const
assert(atoms.empty());
}
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
} // namespace pdbx

View File

@@ -837,9 +837,6 @@ void parser::produce_datablock(std::string_view name)
const auto &[iter, ignore] = m_file.emplace(name);
m_datablock = &(*iter);
if (m_validator)
m_datablock->set_validator(m_validator);
}
void parser::produce_category(std::string_view name)

View File

@@ -5909,8 +5909,7 @@ void PDBFileParser::Parse(std::istream &is, cif::file &result)
{
try
{
if (mDatablock.get_validator() == nullptr)
mDatablock.load_dictionary();
mDatablock.set_validator(result.get_validator());
PreParseInput(is);
@@ -6374,10 +6373,9 @@ void read_pdb_file(std::istream &pdbFile, cif::file &cifFile)
{
PDBFileParser p;
p.Parse(pdbFile, cifFile);
cifFile.load_dictionary("mmcif_pdbx.dic");
if (not cifFile.empty() and cifFile.front().get_validator() == nullptr)
cifFile.front().load_dictionary("mmcif_pdbx.dic");
p.Parse(pdbFile, cifFile);
if (not cifFile.is_valid() and cif::VERBOSE >= 0)
std::cerr << "Resulting mmCIF file is not valid!\n";
@@ -6423,8 +6421,8 @@ file read(std::istream &is)
}
// Must be a PDB like file, right?
if (not result.empty() and result.front().get_validator() == nullptr)
result.front().load_dictionary("mmcif_pdbx.dic");
if (result.get_validator() == nullptr)
result.load_dictionary("mmcif_pdbx.dic");
return result;
}

View File

@@ -1480,9 +1480,6 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
for (auto &cat1 : best.parser->mDb)
{
if (cat1.empty())
continue;
auto &cat2 = db[cat1.name()];
// copy only the values in the first row for the following categories

View File

@@ -100,35 +100,10 @@ void checkEntities(datablock &db)
for (auto entity : db["entity"].find("formula_weight"_key == null or "formula_weight"_key == 0))
{
auto &&[entity_id, type] = entity.get<std::string, std::string>("id", "type");
const auto &[entity_id, type] = entity.get<std::string, std::string>("id", "type");
float formula_weight = 0;
if (type.empty()) // yes, that happens
{
const auto comp_id = db["atom_site"].find_first<std::string>("label_entity_id"_key == entity_id, "label_comp_id");
auto compound = cf.create(comp_id);
if (compound != nullptr)
{
if (compound->is_base() or compound->is_peptide())
type = "polymer";
else if (compound->is_water())
type = "water";
else
{
if (db["pdbx_entity_branch_link"].contains("entity_id"_key == entity_id))
type = "branched";
else
type = "non-polymer";
}
}
if (type.empty())
throw std::runtime_error("Entity without type and cannot determine what it should be");
entity["type"] = type;
}
if (type == "polymer")
{
int n = 0;
@@ -169,11 +144,9 @@ void checkEntities(datablock &db)
if (comp_id.has_value())
{
auto compound = cf.create(*comp_id);
assert(compound);
if (not compound)
{
std::cerr << "missing information for compound " << *comp_id << "\n";
continue;
}
throw std::runtime_error("missing information for compound " + *comp_id);
formula_weight = compound->formula_weight();
}
}
@@ -443,8 +416,6 @@ void checkAtomRecords(datablock &db)
for (int id : db["entity"].find<int>("type"_key == "polymer", "id"))
polymer_entities.insert(id);
std::set<std::string> missingCompounds;
for (auto row : atom_site)
{
residue_key_type k = row.get<std::optional<std::string>,
@@ -475,18 +446,11 @@ void checkAtomRecords(datablock &db)
std::string asym_id = get_asym_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>());
auto compound = cf.create(comp_id);
if (not compound)
{
missingCompounds.insert(comp_id);
std::cerr << "Missing compound information for " << comp_id << "\n";
continue;
}
throw std::runtime_error("Missing compound information for " + comp_id);
auto chem_comp_entry = chem_comp.find_first("id"_key == comp_id);
@@ -626,18 +590,18 @@ void checkAtomAnisotropRecords(datablock &db)
row["type_symbol"] = parent["type_symbol"].text();
}
if (row["pdbx_auth_alt_id"].empty() and not parent["pdbx_auth_alt_id"].empty())
if (row["pdbx_auth_alt_id"].empty())
row["pdbx_auth_alt_id"] = parent["pdbx_auth_alt_id"].text();
if (row["pdbx_label_seq_id"].empty() and not parent["pdbx_label_seq_id"].empty())
if (row["pdbx_label_seq_id"].empty())
row["pdbx_label_seq_id"] = parent["label_seq_id"].text();
if (row["pdbx_label_asym_id"].empty() and not parent["pdbx_label_asym_id"].empty())
if (row["pdbx_label_asym_id"].empty())
row["pdbx_label_asym_id"] = parent["label_asym_id"].text();
if (row["pdbx_label_atom_id"].empty() and not parent["pdbx_label_atom_id"].empty())
if (row["pdbx_label_atom_id"].empty())
row["pdbx_label_atom_id"] = parent["label_atom_id"].text();
if (row["pdbx_label_comp_id"].empty() and not parent["pdbx_label_comp_id"].empty())
if (row["pdbx_label_comp_id"].empty())
row["pdbx_label_comp_id"] = parent["label_comp_id"].text();
// 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();
if (row["pdbx_PDB_model_num"].empty())
row["pdbx_PDB_model_num"] = parent["pdbx_PDB_model_num"].text();
}
if (not to_be_deleted.empty())
@@ -847,18 +811,6 @@ void createEntityPoly(datablock &db)
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())
type = c_type;
@@ -925,7 +877,7 @@ void createEntityPoly(datablock &db)
void createEntityPolySeq(datablock &db)
{
if (auto cat = db.get("entity_poly"); cat == nullptr or cat->empty())
if (db.get("entity_poly") == nullptr)
createEntityPoly(db);
using namespace literals;
@@ -976,10 +928,7 @@ void createEntityPolySeq(datablock &db)
void createPdbxPolySeqScheme(datablock &db)
{
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())
if (db.get("entity_poly_seq") == nullptr)
createEntityPolySeq(db);
using namespace literals;
@@ -1037,10 +986,6 @@ void comparePolySeqSchemes(datablock &db)
auto &ndb_poly_seq_scheme = db["ndb_poly_seq_scheme"];
auto &pdbx_poly_seq_scheme = db["pdbx_poly_seq_scheme"];
// Don't bother if ndb_poly_seq_scheme is empty
if (ndb_poly_seq_scheme.empty())
return;
// Since often ndb_poly_seq_scheme only contains an id and mon_id item
// we assume that it should match the accompanying pdbx_poly_seq
@@ -1109,87 +1054,6 @@ void comparePolySeqSchemes(datablock &db)
}
}
void createPdbxEntityNonpoly(datablock &db)
{
using namespace literals;
auto &atom_site = db["atom_site"];
auto &entity = db["entity"];
auto &pdbx_entity_nonpoly = db["pdbx_entity_nonpoly"];
for (const auto &[entity_id, type] : entity.find<std::string, std::string>("type"_key == "water" or "type"_key == "non-polymer", "id", "type"))
{
for (auto comp_id : atom_site.find<std::string>("label_entity_id"_key == entity_id, "label_comp_id"))
{
if (auto test_comp_id = pdbx_entity_nonpoly.find_first<std::optional<std::string>>("entity_id"_key == entity_id, "comp_id"); test_comp_id.has_value())
{
if (*test_comp_id != comp_id)
throw std::runtime_error("Inconsistent pdbx_entity_nonpoly record for entity " + entity_id + ", expected comp_id " + comp_id);
continue;
}
if (type == "water")
pdbx_entity_nonpoly.emplace({ //
{ "entity_id", entity_id },
{ "name", "water" },
{ "comp_id", comp_id }
});
else
{
auto c = cif::compound_factory::instance().create(comp_id);
std::string name = c ? c->name() : ".";
pdbx_entity_nonpoly.emplace({ //
{ "entity_id", entity_id },
{ "name", name },
{ "comp_id", comp_id }
});
}
}
}
}
void createPdbxNonpolyScheme(datablock &db)
{
using namespace literals;
createPdbxEntityNonpoly(db);
auto &pdbx_entity_nonpoly = db["pdbx_entity_nonpoly"];
auto &pdbx_nonpoly_scheme = db["pdbx_nonpoly_scheme"];
auto &atom_site = db["atom_site"];
for (const auto &[entity_id, comp_id] : pdbx_entity_nonpoly.rows<std::string,std::string>("entity_id", "comp_id"))
{
for (int ndb_nr = 1; auto row : atom_site.find("label_entity_id"_key == entity_id and "label_comp_id"_key == comp_id))
{
// Skip existing records
auto linked = atom_site.get_linked(row, pdbx_nonpoly_scheme);
if (not linked.empty())
continue;
int num = row.get<int>("auth_seq_id");
pdbx_nonpoly_scheme.emplace({//
{ "asym_id", row.get<std::string>("label_asym_id") },
{ "entity_id", entity_id },
{ "mon_id", comp_id },
{ "ndb_seq_num", ndb_nr++ },
{ "pdb_seq_num", num },
{ "auth_seq_num", num },
{ "pdb_mon_id", row.get<std::string>("auth_comp_id") },
{ "auth_mon_id", row.get<std::string>("auth_comp_id") },
{ "pdb_strand_id", row.get<std::string>("auth_asym_id") },
{ "pdb_ins_code", row.get<std::string>("pdbx_PDB_ins_code") }
});
}
}
}
bool reconstruct_pdbx(file &file, std::string_view dictionary)
{
if (file.empty())
@@ -1201,7 +1065,7 @@ bool reconstruct_pdbx(file &file, std::string_view dictionary)
// ... and any additional datablock will contain compound information
cif::compound_source cs(file);
if (auto cat = db.get("atom_site"); cat == nullptr or cat->empty())
if (db.get("atom_site") == nullptr)
throw std::runtime_error("Cannot reconstruct PDBx file, atom data missing");
auto &validator = validator_factory::instance()[dictionary];
@@ -1209,7 +1073,7 @@ bool reconstruct_pdbx(file &file, std::string_view dictionary)
std::string entry_id;
// Phenix files do not have an entry record
if (auto cat = db.get("entry"); cat == nullptr or cat->empty())
if (db.get("entry") == nullptr)
{
entry_id = db.name();
category entry("entry");
@@ -1456,29 +1320,27 @@ bool reconstruct_pdbx(file &file, std::string_view dictionary)
db["chem_comp"].reorder_by_index();
db.load_dictionary(dictionary);
file.load_dictionary(dictionary);
if (db.get("atom_site_anisotrop"))
checkAtomAnisotropRecords(db);
// Now create any missing categories
// Next make sure we have struct_asym records
if (auto cat = db.get("struct_asym"); cat == nullptr or cat->empty())
if (db.get("struct_asym") == nullptr)
createStructAsym(db);
if (auto cat = db.get("entity"); cat == nullptr or cat->empty())
if (db.get("entity") == nullptr)
createEntity(db);
// fill in missing formula_weight, e.g.
checkEntities(db);
if (auto cat = db.get("pdbx_poly_seq_scheme"); cat == nullptr or cat->empty())
if (db.get("pdbx_poly_seq_scheme") == nullptr)
createPdbxPolySeqScheme(db);
if (auto cat = db.get("ndb_poly_seq_scheme"); cat == nullptr or cat->empty())
if (db.get("ndb_poly_seq_scheme") != nullptr)
comparePolySeqSchemes(db);
createPdbxNonpolyScheme(db);
// skip unknown categories for now
bool valid = true;

View File

@@ -189,7 +189,6 @@ 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"))
{
if (pdbx_poly_seq_scheme.count(
"entity_id"_key == entity_id and
"asym_id"_key == asym_id and
"mon_id"_key == mon_id and
"seq_id"_key == num and
@@ -203,7 +202,6 @@ 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"))
{
if (entity_poly_seq.count(
"entity_id"_key == entity_id and
"mon_id"_key == mon_id and
"num"_key == seq_id and
"hetero"_key == hetero) != 1)
@@ -306,8 +304,8 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary, std::erro
if (not seq_can.has_value())
{
if (cif::VERBOSE > 1)
std::clog << "Warning: entity_poly has no canonical sequence for entity_id " << entity_id << '\n';
if (cif::VERBOSE > 0)
std::clog << "Warning: entity_poly has no sequence for entity_id " << entity_id << '\n';
}
else
{

View File

@@ -32,7 +32,7 @@
#include "symop_table_data.hpp"
#include <Eigen/Eigen>
#include <Eigen/Eigenvalues>
namespace cif
{
@@ -103,9 +103,9 @@ sym_op::sym_op(std::string_view s)
auto b = s.data();
auto e = b + s.length();
int rnri = 256; // default to unexisting number
int rnri = 256; // default to unexisting number
auto r = std::from_chars(b, e, rnri);
m_nr = static_cast<uint8_t>(rnri);
m_ta = r.ptr[1] - '0';
m_tb = r.ptr[2] - '0';
@@ -121,7 +121,7 @@ std::string sym_op::string() const
auto r = std::to_chars(b, b + sizeof(b), m_nr);
if ((bool)r.ec or r.ptr > b + 4)
throw std::runtime_error("Could not write out symmetry operation to string");
*r.ptr++ = '_';
*r.ptr++ = '0' + m_ta;
*r.ptr++ = '0' + m_tb;
@@ -163,16 +163,41 @@ transformation::transformation(const matrix3x3<float> &r, const cif::point &t)
void transformation::try_create_quaternion()
{
Eigen::Matrix3f rot;
float Qxx = m_rotation(0, 0);
float Qxy = m_rotation(0, 1);
float Qxz = m_rotation(0, 2);
float Qyx = m_rotation(1, 0);
float Qyy = m_rotation(1, 1);
float Qyz = m_rotation(1, 2);
float Qzx = m_rotation(2, 0);
float Qzy = m_rotation(2, 1);
float Qzz = m_rotation(2, 2);
rot << m_rotation(0, 0), m_rotation(0, 1), m_rotation(0, 2),
m_rotation(1, 0), m_rotation(1, 1), m_rotation(1, 2),
m_rotation(2, 0), m_rotation(2, 1), m_rotation(2, 2);
Eigen::Matrix4f em;
if (rot * rot.transpose() == Eigen::Matrix3f::Identity() and rot.determinant() == 1)
em << Qxx - Qyy - Qzz, Qyx + Qxy, Qzx + Qxz, Qzy - Qyz,
Qyx + Qxy, Qyy - Qxx - Qzz, Qzy + Qyz, Qxz - Qzx,
Qzx + Qxz, Qzy + Qyz, Qzz - Qxx - Qyy, Qyx - Qxy,
Qzy - Qyz, Qxz - Qzx, Qyx - Qxy, Qxx + Qyy + Qzz;
Eigen::EigenSolver<Eigen::Matrix4f> es(em / 3);
auto ev = es.eigenvalues();
for (std::size_t j = 0; j < 4; ++j)
{
Eigen::Quaternionf qe(rot);
m_q = normalize(cif::quaternion{ qe.w(), qe.x(), qe.y(), qe.z() });
if (std::abs(ev[j].real() - 1) > 0.01)
continue;
auto col = es.eigenvectors().col(j);
m_q = normalize(cif::quaternion{
static_cast<float>(col(3).real()),
static_cast<float>(col(0).real()),
static_cast<float>(col(1).real()),
static_cast<float>(col(2).real()) });
break;
}
}
@@ -272,7 +297,7 @@ point spacegroup::operator()(const point &pt, const cell &c, sym_op symop) const
{
if (symop.m_nr < 1 or symop.m_nr > size())
throw std::out_of_range("symmetry operator number out of range");
transformation t = at(symop.m_nr - 1);
t.m_translation.m_x += symop.m_ta - 5;
@@ -291,7 +316,7 @@ point spacegroup::inverse(const point &pt, const cell &c, sym_op symop) const
{
if (symop.m_nr < 1 or symop.m_nr > size())
throw std::out_of_range("symmetry operator number out of range");
transformation t = at(symop.m_nr - 1);
t.m_translation.m_x += symop.m_ta - 5;
@@ -425,13 +450,13 @@ int get_space_group_number(const datablock &db)
if (_symmetry.size() != 1)
throw std::runtime_error("Could not find a unique symmetry in this mmCIF file");
return _symmetry.front().get<int>("Int_Tables_number");
}
// --------------------------------------------------------------------
std::tuple<float, point, sym_op> crystal::closest_symmetry_copy(point a, point b) const
std::tuple<float,point,sym_op> crystal::closest_symmetry_copy(point a, point b) const
{
if (m_cell.get_a() == 0 or m_cell.get_b() == 0 or m_cell.get_c() == 0)
throw std::runtime_error("Invalid cell, contains a dimension that is zero");
@@ -466,7 +491,7 @@ std::tuple<float, point, sym_op> crystal::closest_symmetry_copy(point a, point b
while (fsb.m_x + 0.5f < fa.m_x)
{
fsb.m_x += 1;
s.m_ta += 1;
s.m_ta += 1;
}
while (fsb.m_y - 0.5f > fa.m_y)
@@ -478,7 +503,7 @@ std::tuple<float, point, sym_op> crystal::closest_symmetry_copy(point a, point b
while (fsb.m_y + 0.5f < fa.m_y)
{
fsb.m_y += 1;
s.m_tb += 1;
s.m_tb += 1;
}
while (fsb.m_z - 0.5f > fa.m_z)
@@ -490,7 +515,7 @@ std::tuple<float, point, sym_op> crystal::closest_symmetry_copy(point a, point b
while (fsb.m_z + 0.5f < fa.m_z)
{
fsb.m_z += 1;
s.m_tc += 1;
s.m_tc += 1;
}
auto p = orthogonal(fsb, m_cell);

View File

@@ -69,7 +69,7 @@ bool iequals(const char *a, const char *b)
{
bool result = true;
for (; result and *a and *b; ++a, ++b)
result = kCharToLowerMap[uint8_t(*a)] == kCharToLowerMap[uint8_t(*b)];
result = tolower(*a) == tolower(*b);
return result and *a == *b;
}
@@ -80,7 +80,7 @@ int icompare(std::string_view a, std::string_view b)
auto ai = a.begin(), bi = b.begin();
for (; d == 0 and ai != a.end() and bi != b.end(); ++ai, ++bi)
d = (int)kCharToLowerMap[uint8_t(*ai)] - (int)kCharToLowerMap[uint8_t(*bi)];
d = tolower(*ai) - tolower(*bi);
if (d == 0)
{
@@ -98,7 +98,7 @@ int icompare(const char *a, const char *b)
int d = 0;
for (; d == 0 and *a != 0 and *b != 0; ++a, ++b)
d = (int)kCharToLowerMap[uint8_t(*a)] - (int)kCharToLowerMap[uint8_t(*b)];
d = tolower(*a) - tolower(*b);
if (d == 0)
{

View File

@@ -63,9 +63,9 @@ std::string get_version_nr()
// --------------------------------------------------------------------
#if defined(_WIN32) or defined(__MINGW32__)
#ifdef _WIN32
}
#include <windows.h>
#include <Windows.h>
#include <libloaderapi.h>
#include <wincon.h>
@@ -235,7 +235,7 @@ void progress_bar_impl::print_progress()
float progress = static_cast<float>(m_consumed) / m_max_value;
if (width < kMinBarWidth)
std::cout << (100 * progress) << "%\n";
std::cout << (100 * progress) << '%' << std::endl;
else
{
uint32_t bar_width = 7 * width / 10;
@@ -329,7 +329,7 @@ void progress_bar_impl::print_done()
if (msg.length() < width)
msg += std::string(width - msg.length(), ' ');
std::cout << '\r' << msg << '\n';
std::cout << '\r' << msg << std::endl;
}
progress_bar::progress_bar(int64_t inMax, const std::string &inAction)

View File

@@ -138,7 +138,7 @@ int type_validator::compare(std::string_view a, std::string_view b) const
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)
if (not (bool)ra.ec and not (bool)rb.ec)
{
auto d = da - db;
if (std::abs(d) > std::numeric_limits<double>::epsilon())
@@ -232,7 +232,7 @@ bool item_validator::validate_value(std::string_view value, std::error_code &ec)
ec = make_error_code(validation_error::value_is_not_in_enumeration_list);
}
return not(bool) ec;
return not (bool)ec;
}
// --------------------------------------------------------------------
@@ -401,18 +401,20 @@ void validator::report_error(std::error_code ec, bool fatal) const
{
if (m_strict or fatal)
throw validation_exception(ec);
else if (VERBOSE > 0)
else
std::cerr << ec.message() << '\n';
}
void validator::report_error(std::error_code ec, std::string_view category,
std::string_view item, bool fatal) const
{
auto ex = item.empty() ? validation_exception(ec, category) : validation_exception(ec, category, item);
auto ex = item.empty() ?
validation_exception(ec, category) :
validation_exception(ec, category, item);
if (m_strict or fatal)
throw ex;
else if (VERBOSE > 0)
else
std::cerr << ex.what() << '\n';
}
@@ -453,71 +455,59 @@ const validator &validator_factory::operator[](std::string_view dictionary_name)
}
// not found, add it
auto data = load_resource(dictionary_name);
validator v(dictionary_name);
if (not data and dictionary.extension().string() != ".dic")
data = load_resource(dictionary.parent_path() / (dictionary.filename().string() + ".dic"));
for (bool first = true; auto part_name : cif::split(dictionary_name, ";", true))
if (data)
construct_validator(dictionary_name, *data);
else
{
auto data = load_resource(part_name);
dictionary.assign(part_name.begin(), part_name.end());
std::error_code ec;
if (not data and dictionary.extension().string() != ".dic")
data = load_resource(dictionary.parent_path() / (dictionary.filename().string() + ".dic"));
if (not data)
{
std::error_code ec;
// might be a compressed dictionary on disk
std::filesystem::path p = dictionary;
if (p.extension() == ".dic")
p = p.parent_path() / (p.filename().string() + ".gz");
else
p = p.parent_path() / (p.filename().string() + ".dic.gz");
// might be a compressed dictionary on disk
std::filesystem::path p = dictionary;
if (p.extension() == ".dic")
p = p.parent_path() / (p.filename().string() + ".gz");
else
p = p.parent_path() / (p.filename().string() + ".dic.gz");
#if defined(CACHE_DIR) or defined(DATA_DIR)
if (not std::filesystem::exists(p, ec) or ec)
{
for (const char *dir : {
if (not std::filesystem::exists(p, ec) or ec)
{
for (const char *dir : {
# if defined(CACHE_DIR)
CACHE_DIR,
CACHE_DIR,
# endif
# if defined(DATA_DIR)
DATA_DIR
# endif
})
})
{
auto p2 = std::filesystem::path(dir) / p;
if (std::filesystem::exists(p2, ec) and not ec)
{
auto p2 = std::filesystem::path(dir) / p;
if (std::filesystem::exists(p2, ec) and not ec)
{
swap(p, p2);
break;
}
swap(p, p2);
break;
}
}
}
#endif
if (std::filesystem::exists(p, ec) and not ec)
{
auto in = std::make_unique<gzio::ifstream>(p);
if (std::filesystem::exists(p, ec) and not ec)
{
gzio::ifstream in(p);
if (not in->is_open())
throw std::runtime_error("Could not open dictionary (" + p.string() + ")");
if (not in.is_open())
throw std::runtime_error("Could not open dictionary (" + p.string() + ")");
data.reset(in.release());
}
else
throw std::runtime_error("Dictionary not found or defined (" + dictionary.string() + ")");
construct_validator(dictionary_name, in);
}
if (std::exchange(first, false))
v = parse_dictionary(part_name, *data);
else
extend_dictionary(v, *data);
throw std::runtime_error("Dictionary not found or defined (" + dictionary.string() + ")");
}
m_validators.emplace_back(std::move(v));
return m_validators.back();
}
catch (const std::exception &ex)

View File

@@ -1,20 +1,16 @@
# We're using the older version 2 of Catch2
if(NOT(Catch2_FOUND OR TARGET Catch2))
find_package(Catch2 QUIET)
find_package(Catch2 QUIET)
if(NOT Catch2_FOUND)
include(FetchContent)
if(NOT Catch2_FOUND)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v2.13.9)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v2.13.9)
FetchContent_MakeAvailable(Catch2)
FetchContent_MakeAvailable(Catch2)
set(Catch2_VERSION "2.13.9")
endif()
set(Catch2_VERSION "2.13.9")
endif()
list(
@@ -34,10 +30,10 @@ add_library(test-main OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/test-main.cpp")
target_link_libraries(test-main cifpp::cifpp Catch2::Catch2)
if("${Catch2_VERSION}" VERSION_LESS 3.0.0)
target_compile_definitions(test-main PUBLIC CATCH22=1)
else()
if(${Catch2_VERSION} VERSION_GREATER_EQUAL 3.0.0)
target_compile_definitions(test-main PUBLIC CATCH22=0)
else()
target_compile_definitions(test-main PUBLIC CATCH22=1)
endif()
foreach(CIFPP_TEST IN LISTS CIFPP_tests)
@@ -53,7 +49,8 @@ foreach(CIFPP_TEST IN LISTS CIFPP_tests)
target_compile_definitions(${CIFPP_TEST} PUBLIC CATCH22=1)
endif()
target_link_libraries(${CIFPP_TEST} PRIVATE cifpp::cifpp Catch2::Catch2)
target_link_libraries(${CIFPP_TEST} PRIVATE Threads::Threads cifpp::cifpp
Catch2::Catch2)
target_include_directories(${CIFPP_TEST} PRIVATE "${EIGEN_INCLUDE_DIR}")
if(MSVC)

View File

@@ -53,8 +53,8 @@ TEST_CASE("create_nonpoly_1")
cif::VERBOSE = 1;
cif::file file;
auto &&[dbi, ignore] = file.emplace("TEST"); // create a datablock
dbi->load_dictionary("mmcif_pdbx.dic");
file.load_dictionary("mmcif_pdbx.dic");
file.emplace("TEST"); // create a datablock
cif::mm::structure structure(file);
@@ -82,7 +82,7 @@ _atom_site.pdbx_formal_charge
# that's enough to test with
)"_cf;
atoms.front().load_dictionary("mmcif_pdbx.dic");
atoms.load_dictionary("mmcif_pdbx.dic");
auto &hem_data = atoms["HEM"];
auto &atom_site = hem_data["atom_site"];
@@ -159,7 +159,7 @@ _struct_asym.details ?
_atom_type.symbol C
)"_cf;
expected.front().load_dictionary("mmcif_pdbx.dic");
expected.load_dictionary("mmcif_pdbx.dic");
if (not(expected.front() == structure.get_datablock()))
{
@@ -177,8 +177,8 @@ TEST_CASE("create_nonpoly_2")
cif::VERBOSE = 1;
cif::file file;
auto &&[dbi, ignore] = file.emplace("TEST"); // create a datablock
dbi->load_dictionary("mmcif_pdbx.dic");
file.load_dictionary("mmcif_pdbx.dic");
file.emplace("TEST"); // create a datablock
cif::mm::structure structure(file);
@@ -270,7 +270,7 @@ _struct_asym.details ?
_atom_type.symbol C
)"_cf;
expected.front().load_dictionary("mmcif_pdbx.dic");
expected.load_dictionary("mmcif_pdbx.dic");
REQUIRE(expected.front() == structure.get_datablock());
@@ -354,7 +354,7 @@ _struct_asym.details ?
#
)"_cf;
data.front().load_dictionary("mmcif_pdbx.dic");
data.load_dictionary("mmcif_pdbx.dic");
cif::mm::structure s(data);

View File

@@ -148,15 +148,16 @@ TEST_CASE("dh_q_0")
cif::point axis(1, 0, 0);
cif::point p(1, 1, 0);
cif::point t[3] = {
cif::point t[3] =
{
{ 0, 1, 0 },
{ 0, 0, 0 },
{ 1, 0, 0 }
};
auto a = cif::dihedral_angle(t[0], t[1], t[2], p);
REQUIRE_THAT(a, Catch::Matchers::WithinRel(0.f, 0.01f));
REQUIRE_THAT(a, Catch::Matchers::WithinRel(0, 0.01f));
auto q = cif::construct_from_angle_axis(90, axis);
@@ -179,6 +180,7 @@ TEST_CASE("dh_q_0")
a = cif::dihedral_angle(t[0], t[1], t[2], p);
REQUIRE(std::abs(a - 0.f) < 0.01f);
}
TEST_CASE("dh_q_1")
@@ -226,103 +228,62 @@ TEST_CASE("dh_q_1")
// --------------------------------------------------------------------
// TEST_CASE("m2q_0")
// {
// for (std::size_t i = 0; i < cif::kSymopNrTableSize; ++i)
// {
// auto d = cif::kSymopNrTable[i].symop().data();
// cif::matrix3x3<float> rot;
// float Qxx = rot(0, 0) = d[0];
// float Qxy = rot(0, 1) = d[1];
// float Qxz = rot(0, 2) = d[2];
// float Qyx = rot(1, 0) = d[3];
// float Qyy = rot(1, 1) = d[4];
// float Qyz = rot(1, 2) = d[5];
// float Qzx = rot(2, 0) = d[6];
// float Qzy = rot(2, 1) = d[7];
// float Qzz = rot(2, 2) = d[8];
// Eigen::Matrix4f em;
// em << Qxx - Qyy - Qzz, Qyx + Qxy, Qzx + Qxz, Qzy - Qyz,
// Qyx + Qxy, Qyy - Qxx - Qzz, Qzy + Qyz, Qxz - Qzx,
// Qzx + Qxz, Qzy + Qyz, Qzz - Qxx - Qyy, Qyx - Qxy,
// Qzy - Qyz, Qxz - Qzx, Qyx - Qxy, Qxx + Qyy + Qzz;
// Eigen::EigenSolver<Eigen::Matrix4f> es(em / 3);
// auto ev = es.eigenvalues();
// std::size_t bestJ = 0;
// float bestEV = -1;
// for (std::size_t j = 0; j < 4; ++j)
// {
// if (bestEV < ev[j].real())
// {
// bestEV = ev[j].real();
// bestJ = j;
// }
// }
// if (std::abs(bestEV - 1) > 0.01)
// continue; // not a rotation matrix
// auto col = es.eigenvectors().col(bestJ);
// auto q = normalize(cif::quaternion{
// static_cast<float>(col(3).real()),
// static_cast<float>(col(0).real()),
// static_cast<float>(col(1).real()),
// static_cast<float>(col(2).real()) });
// cif::point p1{ 1, 1, 1 };
// cif::point p2 = p1;
// p2.rotate(q);
// cif::point p3 = rot * p1;
// REQUIRE_THAT(p2.m_x, Catch::Matchers::WithinRel(p3.m_x, 0.01f));
// REQUIRE_THAT(p2.m_y, Catch::Matchers::WithinRel(p3.m_y, 0.01f));
// REQUIRE_THAT(p2.m_z, Catch::Matchers::WithinRel(p3.m_z, 0.01f));
// }
// }
TEST_CASE("m2q_0a")
TEST_CASE("m2q_0, *utf::tolerance(0.001f)")
{
for (std::size_t i = 0; i < cif::kSymopNrTableSize; ++i)
{
auto d = cif::kSymopNrTable[i].symop().data();
Eigen::Matrix3f rot;
rot << d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8];
cif::matrix3x3<float> rot;
float Qxx = rot(0, 0) = d[0];
float Qxy = rot(0, 1) = d[1];
float Qxz = rot(0, 2) = d[2];
float Qyx = rot(1, 0) = d[3];
float Qyy = rot(1, 1) = d[4];
float Qyz = rot(1, 2) = d[5];
float Qzx = rot(2, 0) = d[6];
float Qzy = rot(2, 1) = d[7];
float Qzz = rot(2, 2) = d[8];
// check to see if this matrix contains a true rotation
if (rot * rot.transpose() != Eigen::Matrix3f::Identity() or rot.determinant() != 1)
continue;
Eigen::Matrix4f em;
Eigen::Quaternionf qe(rot);
em << Qxx - Qyy - Qzz, Qyx + Qxy, Qzx + Qxz, Qzy - Qyz,
Qyx + Qxy, Qyy - Qxx - Qzz, Qzy + Qyz, Qxz - Qzx,
Qzx + Qxz, Qzy + Qyz, Qzz - Qxx - Qyy, Qyx - Qxy,
Qzy - Qyz, Qxz - Qzx, Qyx - Qxy, Qxx + Qyy + Qzz;
auto q = normalize(cif::quaternion{ qe.w(), qe.x(), qe.y(), qe.z() });
Eigen::EigenSolver<Eigen::Matrix4f> es(em / 3);
auto ev = es.eigenvalues();
std::size_t bestJ = 0;
float bestEV = -1;
for (std::size_t j = 0; j < 4; ++j)
{
if (bestEV < ev[j].real())
{
bestEV = ev[j].real();
bestJ = j;
}
}
if (std::abs(bestEV - 1) > 0.01)
continue; // not a rotation matrix
auto col = es.eigenvectors().col(bestJ);
auto q = normalize(cif::quaternion{
static_cast<float>(col(3).real()),
static_cast<float>(col(0).real()),
static_cast<float>(col(1).real()),
static_cast<float>(col(2).real()) });
cif::point p1{ 1, 1, 1 };
cif::point p2 = p1;
p2.rotate(q);
cif::matrix3x3<float> rot_c({
rot_c(0, 0) = d[0],
rot_c(0, 1) = d[1],
rot_c(0, 2) = d[2],
rot_c(1, 0) = d[3],
rot_c(1, 1) = d[4],
rot_c(1, 2) = d[5],
rot_c(2, 0) = d[6],
rot_c(2, 1) = d[7],
rot_c(2, 2) = d[8]
});
cif::point p3 = rot_c * p1;
cif::point p3 = rot * p1;
REQUIRE_THAT(p2.m_x, Catch::Matchers::WithinRel(p3.m_x, 0.01f));
REQUIRE_THAT(p2.m_y, Catch::Matchers::WithinRel(p3.m_y, 0.01f));
@@ -330,7 +291,7 @@ TEST_CASE("m2q_0a")
}
}
// "TEST_CASE(m2q_1")
// "TEST_CASE(m2q_1, *utf::tolerance(0.001f)")
// {
// for (std::size_t i = 0; i < cif::kSymopNrTableSize; ++i)
// {
@@ -376,7 +337,7 @@ TEST_CASE("m2q_0a")
// static_cast<float>(em(bestJ, 0)),
// static_cast<float>(em(bestJ, 1)),
// static_cast<float>(em(bestJ, 2)) });
// cif::point p1{ 1, 1, 1 };
// cif::point p2 = p1;
// p2.rotate(q);
@@ -429,17 +390,17 @@ TEST_CASE("symm_3")
REQUIRE(sg.get_name() == "P 21 21 2");
}
TEST_CASE("symm_4")
TEST_CASE("symm_4, *utf::tolerance(0.1f)")
{
using namespace cif::literals;
// based on 2b8h
auto sg = cif::spacegroup(154); // p 32 2 1
auto c = cif::cell(107.516, 107.516, 338.487, 90.00, 90.00, 120.00);
cif::point a{ -8.688, 79.351, 10.439 }; // O6 NAG A 500
cif::point b{ -35.356, 33.693, -3.236 }; // CG2 THR D 400
cif::point sb(-6.916, 79.34, 3.236); // 4_565 copy of b
cif::point a{ -8.688, 79.351, 10.439 }; // O6 NAG A 500
cif::point b{ -35.356, 33.693, -3.236 }; // CG2 THR D 400
cif::point sb( -6.916, 79.34, 3.236); // 4_565 copy of b
REQUIRE_THAT(distance(a, sg(a, c, "1_455"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_a()), 0.01f));
REQUIRE_THAT(distance(a, sg(a, c, "1_545"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_b()), 0.01f));
@@ -450,12 +411,12 @@ TEST_CASE("symm_4")
REQUIRE_THAT(sb.m_y, Catch::Matchers::WithinRel(sb2.m_y, 0.01f));
REQUIRE_THAT(sb.m_z, Catch::Matchers::WithinRel(sb2.m_z, 0.01f));
REQUIRE_THAT(distance(a, sb2), Catch::Matchers::WithinRel(7.42f, 0.01f));
REQUIRE_THAT(distance(a, sb2), Catch::Matchers::WithinRel(7.42f, 0.01f));
}
// --------------------------------------------------------------------
TEST_CASE("symm_4wvp_1")
TEST_CASE("symm_4wvp_1, *utf::tolerance(0.1f)")
{
using namespace cif::literals;
@@ -466,7 +427,7 @@ TEST_CASE("symm_4wvp_1")
cif::crystal c(db);
cif::point p{ -78.722, 98.528, 11.994 };
cif::point p{ -78.722, 98.528, 11.994 };
auto a = s.get_residue("A", 10, "").get_atom_by_atom_id("O");
auto sp1 = c.symmetry_copy(a.get_location(), "2_565"_symop);
@@ -481,9 +442,10 @@ TEST_CASE("symm_4wvp_1")
REQUIRE_THAT(sp2.m_x, Catch::Matchers::WithinAbs(p.m_x, 0.5f));
REQUIRE_THAT(sp2.m_y, Catch::Matchers::WithinAbs(p.m_y, 0.5f));
REQUIRE_THAT(sp2.m_z, Catch::Matchers::WithinAbs(p.m_z, 0.5f));
}
TEST_CASE("symm_2bi3_1")
TEST_CASE("symm_2bi3_1, *utf::tolerance(0.1f)")
{
cif::file f(gTestDir / "2bi3.cif.gz");
@@ -493,15 +455,18 @@ TEST_CASE("symm_2bi3_1")
cif::crystal c(db);
auto struct_conn = db["struct_conn"];
for (const auto &[asym1, seqid1, authseqid1, atomid1, symm1,
asym2, seqid2, authseqid2, atomid2, symm2,
dist] : struct_conn.find<std::string, int, std::string, std::string, std::string,
std::string, int, std::string, std::string, std::string,
float>(
cif::key("ptnr1_symmetry") != "1_555" or cif::key("ptnr2_symmetry") != "1_555",
"ptnr1_label_asym_id", "ptnr1_label_seq_id", "ptnr1_auth_seq_id", "ptnr1_label_atom_id", "ptnr1_symmetry",
"ptnr2_label_asym_id", "ptnr2_label_seq_id", "ptnr2_auth_seq_id", "ptnr2_label_atom_id", "ptnr2_symmetry",
"pdbx_dist_value"))
for (const auto &[
asym1, seqid1, authseqid1, atomid1, symm1,
asym2, seqid2, authseqid2, atomid2, symm2,
dist] : struct_conn.find<
std::string,int,std::string,std::string,std::string,
std::string,int,std::string,std::string,std::string,
float>(
cif::key("ptnr1_symmetry") != "1_555" or cif::key("ptnr2_symmetry") != "1_555",
"ptnr1_label_asym_id", "ptnr1_label_seq_id", "ptnr1_auth_seq_id", "ptnr1_label_atom_id", "ptnr1_symmetry",
"ptnr2_label_asym_id", "ptnr2_label_seq_id", "ptnr2_auth_seq_id", "ptnr2_label_atom_id", "ptnr2_symmetry",
"pdbx_dist_value"
))
{
auto &r1 = s.get_residue(asym1, seqid1, authseqid1);
auto &r2 = s.get_residue(asym2, seqid2, authseqid2);
@@ -527,7 +492,7 @@ TEST_CASE("symm_2bi3_1")
}
}
TEST_CASE("symm_2bi3_1a")
TEST_CASE("symm_2bi3_1a, *utf::tolerance(0.1f)")
{
using namespace cif::literals;
@@ -539,20 +504,23 @@ TEST_CASE("symm_2bi3_1a")
auto struct_conn = db["struct_conn"];
auto atom_site = db["atom_site"];
for (const auto &[asym1, seqid1, authseqid1, atomid1, symm1,
asym2, seqid2, authseqid2, atomid2, symm2,
dist] : struct_conn.find<std::string, std::optional<int>, std::string, std::string, std::string,
std::string, std::optional<int>, std::string, std::string, std::string,
float>(
cif::key("ptnr1_symmetry") != "1_555" or cif::key("ptnr2_symmetry") != "1_555",
"ptnr1_label_asym_id", "ptnr1_label_seq_id", "ptnr1_auth_seq_id", "ptnr1_label_atom_id", "ptnr1_symmetry",
"ptnr2_label_asym_id", "ptnr2_label_seq_id", "ptnr2_auth_seq_id", "ptnr2_label_atom_id", "ptnr2_symmetry",
"pdbx_dist_value"))
for (const auto &[
asym1, seqid1, authseqid1, atomid1, symm1,
asym2, seqid2, authseqid2, atomid2, symm2,
dist] : struct_conn.find<
std::string,std::optional<int>,std::string,std::string,std::string,
std::string,std::optional<int>,std::string,std::string,std::string,
float>(
cif::key("ptnr1_symmetry") != "1_555" or cif::key("ptnr2_symmetry") != "1_555",
"ptnr1_label_asym_id", "ptnr1_label_seq_id", "ptnr1_auth_seq_id", "ptnr1_label_atom_id", "ptnr1_symmetry",
"ptnr2_label_asym_id", "ptnr2_label_seq_id", "ptnr2_auth_seq_id", "ptnr2_label_atom_id", "ptnr2_symmetry",
"pdbx_dist_value"
))
{
cif::point p1 = atom_site.find1<float, float, float>(
cif::point p1 = atom_site.find1<float,float,float>(
"label_asym_id"_key == asym1 and "label_seq_id"_key == seqid1 and "auth_seq_id"_key == authseqid1 and "label_atom_id"_key == atomid1,
"cartn_x", "cartn_y", "cartn_z");
cif::point p2 = atom_site.find1<float, float, float>(
cif::point p2 = atom_site.find1<float,float,float>(
"label_asym_id"_key == asym2 and "label_seq_id"_key == seqid2 and "auth_seq_id"_key == authseqid2 and "label_atom_id"_key == atomid2,
"cartn_x", "cartn_y", "cartn_z");
@@ -572,7 +540,7 @@ TEST_CASE("symm_2bi3_1a")
}
}
TEST_CASE("symm_3bwh_1")
TEST_CASE("symm_3bwh_1, *utf::tolerance(0.1f)")
{
cif::file f(gTestDir / "3bwh.cif.gz");
@@ -587,15 +555,15 @@ TEST_CASE("symm_3bwh_1")
{
if (a1 == a2)
continue;
const auto &[d, p, so] = c.closest_symmetry_copy(a1.get_location(), a2.get_location());
const auto&[ d, p, so ] = c.closest_symmetry_copy(a1.get_location(), a2.get_location());
REQUIRE_THAT(d, Catch::Matchers::WithinAbs(distance(a1.get_location(), p), 0.5f));
}
}
}
TEST_CASE("volume_3bwh_1")
TEST_CASE("volume_3bwh_1, *utf::tolerance(0.1f)")
{
cif::file f(gTestDir / "1juh.cif.gz");
@@ -605,3 +573,4 @@ TEST_CASE("volume_3bwh_1")
REQUIRE_THAT(c.get_cell().get_volume(), Catch::Matchers::WithinRel(741009.625f, 0.01f));
}

View File

@@ -50,23 +50,6 @@ cif::file operator""_cf(const char *text, std::size_t length)
// --------------------------------------------------------------------
TEST_CASE("text_1")
{
CHECK(cif::iequals("TEST", "test"));
CHECK(cif::iequals(std::string_view{"TEST"}, std::string_view{"test"}));
CHECK(cif::icompare("TEST", "test") == 0);
CHECK(cif::icompare(std::string_view{"TEST"}, std::string_view{"test"}) == 0);
CHECK(cif::icompare("TEST1", "test") > 0);
CHECK(cif::icompare(std::string_view{"TEST1"}, std::string_view{"test"}) > 0);
CHECK(cif::icompare("aap", "noot") < 0);
CHECK(cif::icompare(std::string_view{"aap"}, std::string_view{"noot"}) < 0);
}
// --------------------------------------------------------------------
TEST_CASE("from_chars_1")
{
auto f = R"(data_TEST
@@ -87,6 +70,8 @@ _test.v
auto r2 = c.back();
REQUIRE(r2.get<double>("v") == 616.487);
REQUIRE(r2["v"].compare(616.487) == 0);
}
// --------------------------------------------------------------------
@@ -774,6 +759,7 @@ save__cat_2.desc
auto validator = cif::parse_dictionary("test", is_dict);
cif::file f;
f.set_validator(&validator);
// --------------------------------------------------------------------
@@ -804,7 +790,7 @@ _cat_2.desc
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data, validator);
f.load(is_data);
SECTION("one")
{
@@ -926,6 +912,7 @@ save__cat_1.c
auto validator = cif::parse_dictionary("test", is_dict);
cif::file f;
f.set_validator(&validator);
// --------------------------------------------------------------------
@@ -948,7 +935,7 @@ mies Mies
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data, validator);
f.load(is_data);
auto &cat1 = f.front()["cat_1"];
@@ -1087,6 +1074,7 @@ save__cat_2.desc
auto validator = cif::parse_dictionary("test", is_dict);
cif::file f;
f.set_validator(&validator);
// --------------------------------------------------------------------
@@ -1120,7 +1108,7 @@ _cat_2.desc
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data, validator);
f.load(is_data);
auto &cat1 = f.front()["cat_1"];
auto &cat2 = f.front()["cat_2"];
@@ -1289,6 +1277,7 @@ save__cat_2.parent_id3
auto validator = cif::parse_dictionary("test", is_dict);
cif::file f;
f.set_validator(&validator);
// --------------------------------------------------------------------
@@ -1332,7 +1321,7 @@ _cat_2.parent_id3
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data, validator);
f.load(is_data);
auto &cat1 = f.front()["cat_1"];
auto &cat2 = f.front()["cat_2"];
@@ -1509,6 +1498,7 @@ cat_2 3 cat_2:cat_1:3
auto validator = cif::parse_dictionary("test", is_dict);
cif::file f;
f.set_validator(&validator);
// --------------------------------------------------------------------
@@ -1545,7 +1535,7 @@ _cat_2.parent_id3
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data, validator);
f.load(is_data);
auto &cat1 = f.front()["cat_1"];
auto &cat2 = f.front()["cat_2"];
@@ -1748,6 +1738,7 @@ cat_2 1 cat_2:cat_1:1
auto validator = cif::parse_dictionary("test", is_dict);
cif::file f;
f.set_validator(&validator);
// --------------------------------------------------------------------
@@ -1784,7 +1775,7 @@ _cat_2.parent_id_2
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data, validator);
f.load(is_data);
// auto &cat1 = f.front()["cat_1"];
auto &cat2 = f.front()["cat_2"];
@@ -2143,6 +2134,7 @@ cat_2 1 '_cat_2.num' '_cat_3.num' cat_3
auto validator = cif::parse_dictionary("test", is_dict);
cif::file f;
f.set_validator(&validator);
// --------------------------------------------------------------------
@@ -2184,7 +2176,7 @@ _cat_3.num
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data, validator);
f.load(is_data);
auto &cat1 = f.front()["cat_1"];
auto &cat2 = f.front()["cat_2"];
@@ -2427,6 +2419,7 @@ cat_2 1 '_cat_2.num' '_cat_3.num' cat_3
auto validator = cif::parse_dictionary("test", is_dict);
cif::file f;
f.set_validator(&validator);
// --------------------------------------------------------------------
@@ -2468,7 +2461,7 @@ _cat_3.num
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data, validator);
f.load(is_data);
auto &cat1 = f.front()["cat_1"];
auto &cat2 = f.front()["cat_2"];
@@ -3023,6 +3016,7 @@ save__cat_1.name
auto validator = cif::parse_dictionary("test", is_dict);
cif::file f;
f.set_validator(&validator);
// --------------------------------------------------------------------
@@ -3045,7 +3039,7 @@ _cat_1.name
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data, validator);
f.load(is_data);
REQUIRE(f.is_valid());
@@ -3217,6 +3211,7 @@ save__cat_1.name
auto &validator = cif::validator_factory::instance().construct_validator("test_dict.dic", is_dict);
cif::file f;
f.set_validator(&validator);
// --------------------------------------------------------------------
@@ -3243,7 +3238,7 @@ _cat_1.name
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data, validator);
f.load(is_data);
REQUIRE(f.is_valid());
@@ -3323,6 +3318,7 @@ save__cat_1.id_2
auto validator = cif::parse_dictionary("test", is_dict);
cif::file f;
f.set_validator(&validator);
// --------------------------------------------------------------------
@@ -3347,7 +3343,7 @@ _cat_1.id_2
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data, validator);
f.load(is_data);
auto &cat1 = f.front()["cat_1"];
@@ -3486,7 +3482,7 @@ TEST_CASE("compound_test_1")
cif::compound_factory::instance().push_dictionary(gTestDir / "REA_v2.cif");
auto compound = cif::compound_factory::instance().create("REA_v2");
REQUIRE(compound != nullptr);
REQUIRE(cif::iequals(compound->id(), "REA_v2"));
REQUIRE(compound->id() == "REA_v2");
}
// --------------------------------------------------------------------