mirror of
https://github.com/PDB-REDO/libcifpp.git
synced 2026-06-04 22:14:24 +08:00
Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1de973ddcb | ||
|
|
eecc801203 | ||
|
|
5c50154ea4 | ||
|
|
0fa3d6aa94 | ||
|
|
01f5242bfb | ||
|
|
af6d8d4f71 | ||
|
|
fa8285fc0f | ||
|
|
2e7f6b8337 | ||
|
|
a6a55020eb | ||
|
|
0e84ea454d | ||
|
|
f3bf211d45 | ||
|
|
f5ef44836c | ||
|
|
070124b6e1 | ||
|
|
c8a46fcdd9 | ||
|
|
5306b59fd8 | ||
|
|
90c5df832a | ||
|
|
2aa439d51f | ||
|
|
ac2b68517c | ||
|
|
e56b568c42 | ||
|
|
63c49b2e04 | ||
|
|
559fd18a20 | ||
|
|
beb7585261 | ||
|
|
8b0f92aa9a | ||
|
|
0d8beeae5b | ||
|
|
e3da654e67 | ||
|
|
dc9e151d89 | ||
|
|
7cfaf051ba | ||
|
|
7920491309 | ||
|
|
0ee493a3fb | ||
|
|
7e23bc0c0b | ||
|
|
579f859562 | ||
|
|
752938ca44 | ||
|
|
fce58c02fe | ||
|
|
924f7c1505 | ||
|
|
8944906fd2 | ||
|
|
cb02969604 | ||
|
|
31090c6ec5 | ||
|
|
9e30d2bc1a | ||
|
|
93d703f7a1 | ||
|
|
3c241048a5 | ||
|
|
2788536799 | ||
|
|
314d435a18 | ||
|
|
37edcd8666 | ||
|
|
10e290fbdf | ||
|
|
58cda1241e | ||
|
|
3659aaabff | ||
|
|
727a39cc54 | ||
|
|
fd9ccdfff9 | ||
|
|
aabee270b3 | ||
|
|
647c58f8ec | ||
|
|
0b8024d19c | ||
|
|
d59b0bf27f | ||
|
|
398c16eac2 | ||
|
|
fa869bdc7d | ||
|
|
c20d0d2a30 | ||
|
|
000f2736c2 | ||
|
|
cfcc81bb62 | ||
|
|
82eae05868 | ||
|
|
e8fb53c49b | ||
|
|
604c97afe1 | ||
|
|
7e60cdf272 | ||
|
|
9ea7cfcc80 | ||
|
|
a7a4a16f79 | ||
|
|
6717059934 | ||
|
|
714747c280 | ||
|
|
81cd305c80 | ||
|
|
5de872bbb3 | ||
|
|
ce6a75a920 | ||
|
|
874a5cb2f2 | ||
|
|
6e2202d4f1 | ||
|
|
bcf33df701 | ||
|
|
3bdcf21c69 | ||
|
|
4b36bdc58c | ||
|
|
6d9008ee8c | ||
|
|
ee93692707 | ||
|
|
2bcc368bce | ||
|
|
6cc4467d53 | ||
|
|
41c0521480 | ||
|
|
7d33d56c0e | ||
|
|
f86f34e5e1 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -13,3 +13,6 @@ docs/api
|
||||
docs/conf.py
|
||||
build_ci/
|
||||
data/components.cif
|
||||
perf.data*
|
||||
.cache/
|
||||
|
||||
|
||||
140
CMakeLists.txt
140
CMakeLists.txt
@@ -24,29 +24,25 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.23)
|
||||
|
||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
# set the project name
|
||||
project(
|
||||
libcifpp
|
||||
VERSION 8.0.1
|
||||
LANGUAGES CXX)
|
||||
VERSION 9.0.0
|
||||
LANGUAGES CXX C)
|
||||
|
||||
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
include(FindAtomic)
|
||||
include(CheckFunctionExists)
|
||||
include(CheckIncludeFiles)
|
||||
include(CheckLibraryExists)
|
||||
include(CMakePackageConfigHelpers)
|
||||
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()
|
||||
include(FetchContent)
|
||||
|
||||
# When building with ninja-multiconfig, build both debug and release by default
|
||||
if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
|
||||
@@ -63,25 +59,23 @@ elseif(MSVC)
|
||||
endif()
|
||||
|
||||
# Build documentation?
|
||||
option(BUILD_DOCUMENTATION "Build the documentation" OFF)
|
||||
set(BUILD_DOCUMENTATION OFF CACHE BOOL "Build the documentation")
|
||||
|
||||
# Optionally build a version to be installed inside CCP4
|
||||
option(BUILD_FOR_CCP4 "Build a version to be installed in CCP4")
|
||||
set(BUILD_FOR_CCP4 OFF CACHE BOOL "Build a version to be installed in CCP4")
|
||||
|
||||
# Building shared libraries?
|
||||
if(NOT(BUILD_FOR_CCP4 AND WIN32))
|
||||
option(BUILD_SHARED_LIBS "Build a shared library instead of a static one" OFF)
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build a shared library instead of a static one")
|
||||
endif()
|
||||
|
||||
if(PROJECT_IS_TOP_LEVEL AND NOT BUILD_FOR_CCP4)
|
||||
# Lots of code depend on the availability of the components.cif file
|
||||
option(CIFPP_DOWNLOAD_CCD
|
||||
"Download the CCD file components.cif during installation" ON)
|
||||
set(CIFPP_DOWNLOAD_CCD ON CACHE BOOL "Download the CCD file components.cif during installation")
|
||||
|
||||
# An optional cron script can be installed to keep the data files up-to-date
|
||||
if(UNIX AND NOT APPLE)
|
||||
option(CIFPP_INSTALL_UPDATE_SCRIPT
|
||||
"Install the script to update CCD and dictionary files" ON)
|
||||
set(CIFPP_INSTALL_UPDATE_SCRIPT ON CACHE BOOL "Install the script to update CCD and dictionary files")
|
||||
endif()
|
||||
else()
|
||||
unset(CIFPP_DOWNLOAD_CCD)
|
||||
@@ -91,14 +85,13 @@ endif()
|
||||
# When CCP4 is sourced in the environment, we can recreate the symmetry
|
||||
# operations table
|
||||
if(EXISTS "$ENV{CCP4}/lib/data/syminfo.lib")
|
||||
option(CIFPP_RECREATE_SYMOP_DATA
|
||||
"Recreate SymOp data table in case it is out of date" ON)
|
||||
set(CIFPP_RECREATE_SYMOP_DATA ON CACHE BOOL "Recreate SymOp data table in case it is out of date")
|
||||
endif()
|
||||
|
||||
# CCP4 build
|
||||
if(BUILD_FOR_CCP4)
|
||||
if("$ENV{CCP4}" STREQUAL "" OR NOT EXISTS $ENV{CCP4})
|
||||
message(FATAL_ERROR "A CCP4 built was requested but CCP4 was not sourced")
|
||||
message(FATAL_ERROR "cifpp: A CCP4 built was requested but CCP4 was not sourced")
|
||||
else()
|
||||
list(PREPEND CMAKE_MODULE_PATH "$ENV{CCP4}")
|
||||
list(PREPEND CMAKE_PREFIX_PATH "$ENV{CCP4}")
|
||||
@@ -128,9 +121,6 @@ if(WIN32)
|
||||
add_definitions(-D _WIN32_WINNT=0x0501)
|
||||
endif()
|
||||
|
||||
# Man, this is 2024 we're living in...
|
||||
add_definitions(-DNOMINMAX)
|
||||
|
||||
# We do not want to write an export file for all our symbols...
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
endif()
|
||||
@@ -150,51 +140,8 @@ endif()
|
||||
|
||||
# Libraries
|
||||
|
||||
# Start by finding out if std:regex is usable. Note that the current
|
||||
# implementation in GCC is not acceptable, it crashes on long lines. The
|
||||
# implementation in libc++ (clang) and MSVC seem to be OK.
|
||||
check_cxx_source_compiles(
|
||||
"
|
||||
#include <iostream>
|
||||
#ifndef __GLIBCXX__
|
||||
#error
|
||||
#endif
|
||||
int main(int argc, char *argv[]) { return 0; }"
|
||||
GXX_LIBSTDCPP)
|
||||
|
||||
if(GXX_LIBSTDCPP)
|
||||
message(
|
||||
STATUS "Testing for known regex bug, since you're using GNU libstdc++")
|
||||
|
||||
try_run(STD_REGEX_RUNNING STD_REGEX_COMPILING
|
||||
${CMAKE_CURRENT_BINARY_DIR}/test
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/test-rx.cpp)
|
||||
|
||||
if(STD_REGEX_RUNNING STREQUAL FAILED_TO_RUN)
|
||||
message(
|
||||
STATUS
|
||||
"You are probably trying to compile using the g++ standard library which contains a crashing std::regex implementation. Will use boost::regex instead"
|
||||
)
|
||||
|
||||
find_package(Boost 1.80 QUIET COMPONENTS regex)
|
||||
|
||||
if(NOT Boost_FOUND)
|
||||
set(BOOST_REGEX_STANDALONE ON)
|
||||
|
||||
FetchContent_Declare(
|
||||
boost-rx
|
||||
GIT_REPOSITORY https://github.com/boostorg/regex
|
||||
GIT_TAG boost-1.83.0)
|
||||
|
||||
FetchContent_MakeAvailable(boost-rx)
|
||||
endif()
|
||||
|
||||
set(BOOST_REGEX ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
# Avoid linking the shared library of zlib Search ZLIB_ROOT first if it is
|
||||
# Avoid linking the shared library of zlib. Search ZLIB_ROOT first if it is
|
||||
# set.
|
||||
if(ZLIB_ROOT)
|
||||
set(_ZLIB_SEARCH_ROOT PATHS ${ZLIB_ROOT} NO_DEFAULT_PATH)
|
||||
@@ -221,11 +168,22 @@ if(MSVC)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
find_package(ZLIB QUIET)
|
||||
# First check if <format> is available
|
||||
find_file(FMT NAME format)
|
||||
|
||||
if(FMT EQUAL "FMT-NOTFOUND")
|
||||
if(NOT (fmt_FOUND OR TARGET fmt))
|
||||
find_package(fmt REQUIRED)
|
||||
message(FATAL_ERROR "fmt not found, compiler too old, you're out of luck")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Threads)
|
||||
include(FindPCRE2)
|
||||
find_package(ZLIB QUIET)
|
||||
|
||||
if(NOT ZLIB_FOUND)
|
||||
message(FATAL_ERROR "The zlib development files were not found you this system, please install them and try again (hint: on debian/ubuntu use apt-get install zlib1g-dev)")
|
||||
message(FATAL_ERROR "cifpp: 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)")
|
||||
endif()
|
||||
|
||||
# Using Eigen3 is a bit of a thing. We don't want to build it completely since
|
||||
@@ -239,15 +197,17 @@ if(Eigen3_FOUND AND TARGET Eigen3::Eigen)
|
||||
else()
|
||||
# Use ExternalProject since FetchContent always tries to install the result...
|
||||
ExternalProject_Add(my-eigen3
|
||||
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
|
||||
GIT_TAG 3.4.0
|
||||
URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.zip
|
||||
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
|
||||
|
||||
ExternalProject_Get_Property(my-eigen3 SOURCE_DIR)
|
||||
set(EIGEN_INCLUDE_DIR ${SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
message(STATUS "Eigen include dir is ${EIGEN_INCLUDE_DIR}")
|
||||
message(STATUS "cifpp: Eigen include dir is ${EIGEN_INCLUDE_DIR}")
|
||||
|
||||
# Create a revision file, containing the current git version info
|
||||
include(VersionString)
|
||||
@@ -351,19 +311,9 @@ target_sources(cifpp
|
||||
# The code now really requires C++20
|
||||
target_compile_features(cifpp PUBLIC cxx_std_20)
|
||||
|
||||
set(CMAKE_DEBUG_POSTFIX d)
|
||||
set_target_properties(cifpp PROPERTIES DEBUG_POSTFIX "d")
|
||||
|
||||
generate_export_header(cifpp EXPORT_FILE_NAME
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cif++/exports.hpp)
|
||||
|
||||
if(BOOST_REGEX)
|
||||
target_compile_definitions(cifpp PRIVATE USE_BOOST_REGEX=1
|
||||
BOOST_REGEX_STANDALONE=1)
|
||||
get_target_property(BOOST_REGEX_INCLUDE_DIR Boost::regex
|
||||
INTERFACE_INCLUDE_DIRECTORIES)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
target_compile_definitions(cifpp PUBLIC NOMINMAX=1)
|
||||
endif()
|
||||
@@ -374,9 +324,14 @@ target_include_directories(
|
||||
cifpp
|
||||
PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
PRIVATE "${BOOST_REGEX_INCLUDE_DIR}" "${EIGEN_INCLUDE_DIR}")
|
||||
PRIVATE "${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 $<$<TARGET_EXISTS:std::atomic>:std::atomic> $<BUILD_INTERFACE:pcre2-8>)
|
||||
|
||||
if(fmt_FOUND)
|
||||
target_link_libraries(cifpp PUBLIC fmt)
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
|
||||
@@ -390,7 +345,7 @@ if(CIFPP_DOWNLOAD_CCD)
|
||||
file(SIZE ${COMPONENTS_CIF} CCD_FILE_SIZE)
|
||||
|
||||
if(CCD_FILE_SIZE EQUAL 0)
|
||||
message(STATUS "Removing empty ${COMPONENTS_CIF} file")
|
||||
message(STATUS "cifpp: Removing empty ${COMPONENTS_CIF} file")
|
||||
file(REMOVE "${COMPONENTS_CIF}")
|
||||
endif()
|
||||
endif()
|
||||
@@ -429,7 +384,7 @@ if(CIFPP_DOWNLOAD_CCD)
|
||||
|
||||
if(CCD_FETCH_STATUS_CODE)
|
||||
message(
|
||||
FATAL_ERROR "Error trying to download CCD file: ${CCD_FETCH_STATUS}")
|
||||
FATAL_ERROR "cifpp: Error trying to download CCD file: ${CCD_FETCH_STATUS}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -493,7 +448,7 @@ file(GLOB OLD_CONFIG_FILES
|
||||
|
||||
if(OLD_CONFIG_FILES)
|
||||
message(
|
||||
STATUS "Installation will remove old config files: ${OLD_CONFIG_FILES}")
|
||||
STATUS "cifpp: Installation will remove old config files: ${OLD_CONFIG_FILES}")
|
||||
install(CODE "file(REMOVE ${OLD_CONFIG_FILES})")
|
||||
endif()
|
||||
|
||||
@@ -514,6 +469,7 @@ if(CIFPP_DATA_DIR AND CIFPP_DOWNLOAD_CCD)
|
||||
endif()
|
||||
|
||||
set(CONFIG_TEMPLATE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cifpp-config.cmake.in)
|
||||
set(REQUIRE_FMT ${fmt_FOUND})
|
||||
|
||||
configure_package_config_file(
|
||||
${CONFIG_TEMPLATE_FILE} ${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake
|
||||
@@ -559,7 +515,7 @@ if(CIFPP_INSTALL_UPDATE_SCRIPT)
|
||||
PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE
|
||||
WORLD_READ)
|
||||
else()
|
||||
message(FATAL_ERROR "Don't know where to install the update script")
|
||||
message(FATAL_ERROR "cifpp: Don't know where to install the update script")
|
||||
endif()
|
||||
|
||||
# a config file, to make it complete
|
||||
@@ -573,7 +529,7 @@ if(CIFPP_INSTALL_UPDATE_SCRIPT)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf
|
||||
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR})
|
||||
install(
|
||||
CODE "message(\"A configuration file has been written to ${CIFPP_ETC_DIR}/libcifpp.conf, please edit this file to enable automatic updates\")"
|
||||
CODE "message(\"cifpp: A configuration file has been written to ${CIFPP_ETC_DIR}/libcifpp.conf, please edit this file to enable automatic updates\")"
|
||||
)
|
||||
|
||||
install(DIRECTORY DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/libcifpp/cache-update.d)
|
||||
|
||||
@@ -117,12 +117,8 @@ Other libraries you might want to install beforehand are:
|
||||
`libeigen3-dev`
|
||||
- [zlib](https://github.com/madler/zlib), the development version of this
|
||||
library. On Debian/Ubuntu this is the package `zlib1g-dev`.
|
||||
- [boost](https://www.boost.org), in Debian/Ubuntu this is `libboost-dev`.
|
||||
|
||||
The Boost libraries are only needed in case you are using GCC due to a long
|
||||
standing bug in GNU's implementation of std::regex. It simply crashes
|
||||
on the regular expressions used in the mmcif_pdbx dictionary and so
|
||||
we use the boost regex implementation instead.
|
||||
- [pcre2](https://www.pcre.org/), the Perl Compatible Regular Expression
|
||||
library. On Debian/Ubuntu this is the package `libpcre2-dev`.
|
||||
|
||||
### Building
|
||||
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
Version 9.0.0
|
||||
- Rename fields of cif::mm::polymer to match the naming
|
||||
in mmcif_pdbx.dic. Also, related, fix building mm::structure
|
||||
using the correct mapping between atom_site and residues.
|
||||
- _atom_site.auth_alt_id does not exist, it should be
|
||||
_atom_site.pdbx_auth_alt_id of course.
|
||||
- Added a more lightweight fixup for mmcif_pdbx files
|
||||
that lack certain categories.
|
||||
|
||||
Version 8.0.1
|
||||
- Fix cif::mm::structure::cleanup_empty_categories, removed too much
|
||||
- Add default value for B_iso_or_equiv in residue::create_new_atom
|
||||
|
||||
27
cmake/FindPCRE2.cmake
Normal file
27
cmake/FindPCRE2.cmake
Normal file
@@ -0,0 +1,27 @@
|
||||
set(PCRE2_USE_STATIC_LIBS ON)
|
||||
|
||||
# The cmake config files for pcre2 are broken
|
||||
|
||||
include(FindPkgConfig)
|
||||
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PCRE2 IMPORTED_TARGET libpcre2-8)
|
||||
|
||||
if(PCRE2_FOUND)
|
||||
message(STATUS "Using pcre2 found using pkg-config")
|
||||
|
||||
add_library(pcre2-8 ALIAS PkgConfig::PCRE2)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT PCRE2_FOUND)
|
||||
find_path(PCRE2_INCLUDEDIR NAMES pcre2.h HINTS "C:/Program Files (x86)/PCRE2/include" REQUIRED)
|
||||
find_library(PCRE2_LIBRARY NAMES pcre2-8-static HINTS "C:/Program Files (x86)/PCRE2/lib" REQUIRED)
|
||||
|
||||
add_library(pcre2-8 IMPORTED STATIC)
|
||||
target_include_directories(pcre2-8 INTERFACE ${PCRE2_INCLUDEDIR})
|
||||
target_compile_definitions(pcre2-8 INTERFACE PCRE2_STATIC)
|
||||
# target_link_libraries(pcre2-8 INTERFACE ${PCRE2_LIBRARY})
|
||||
set_target_properties(pcre2-8 PROPERTIES IMPORTED_LOCATION ${PCRE2_LIBRARY})
|
||||
set_target_properties(pcre2-8 PROPERTIES IMPORTED_IMPLIB ${PCRE2_LIBRARY})
|
||||
endif()
|
||||
@@ -8,5 +8,8 @@ include(CMakeFindDependencyMacro)
|
||||
|
||||
find_dependency(Threads)
|
||||
find_dependency(ZLIB REQUIRED)
|
||||
if(@REQUIRE_FMT@)
|
||||
find_dependency(fmt REQUIRED)
|
||||
endif()
|
||||
|
||||
check_required_components(cifpp)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86164
|
||||
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
|
||||
int main()
|
||||
{
|
||||
std::string s(100'000, '*');
|
||||
std::smatch m;
|
||||
std::regex r("^(.*?)$");
|
||||
|
||||
std::regex_search(s, m, r);
|
||||
|
||||
std::cout << s.substr(0, 10) << '\n';
|
||||
std::cout << m.str(1).substr(0, 10) << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -157,7 +157,7 @@ class category
|
||||
emplace(std::forward<row_initializer>(rows));
|
||||
}
|
||||
|
||||
category(const category &rhs); ///< Copy constructor
|
||||
category(const category &rhs); ///< Copy constructor
|
||||
|
||||
category(category &&rhs) noexcept ///< Move constructor
|
||||
{
|
||||
@@ -223,6 +223,11 @@ class category
|
||||
/// @return Returns true is all validations pass
|
||||
bool validate_links() const;
|
||||
|
||||
/**
|
||||
* @brief Strip removes items from this category that are invalid according to the assigned validator
|
||||
*/
|
||||
void strip();
|
||||
|
||||
/// @brief Equality operator, returns true if @a rhs is equal to this
|
||||
/// @param rhs The object to compare with
|
||||
/// @return True if the data contained is equal
|
||||
@@ -327,8 +332,16 @@ class category
|
||||
// --------------------------------------------------------------------
|
||||
// A category can have a key, as defined by the validator/dictionary
|
||||
|
||||
/// @brief The type of an element of the key_type
|
||||
struct key_element_type
|
||||
{
|
||||
std::string name; ///< Name of the item
|
||||
std::string value; ///< Value to be found
|
||||
bool may_be_null = false; ///< If true, value should be same or empty
|
||||
};
|
||||
|
||||
/// @brief The key type
|
||||
using key_type = row_initializer;
|
||||
using key_type = std::vector<key_element_type>;
|
||||
|
||||
/// @brief Return a row_handle for the row specified by \a key
|
||||
/// @param key The value for the key, items specified in the dictionary should have a value
|
||||
@@ -1244,7 +1257,7 @@ class category
|
||||
{
|
||||
}
|
||||
|
||||
// TODO: NEED TO FIX THIS!
|
||||
// TODO: NEED TO FIX THIS!
|
||||
category *linked;
|
||||
const link_validator *v;
|
||||
};
|
||||
|
||||
@@ -180,7 +180,7 @@ class compound
|
||||
friend class local_compound_factory_impl;
|
||||
|
||||
compound(cif::datablock &db);
|
||||
|
||||
|
||||
std::string m_id;
|
||||
std::string m_name;
|
||||
std::string m_type;
|
||||
@@ -290,6 +290,13 @@ class compound_factory
|
||||
|
||||
void report_missing_compound(std::string_view compound_id);
|
||||
|
||||
bool get_report_missing() const { return m_report_missing; }
|
||||
|
||||
void set_report_missing(bool report)
|
||||
{
|
||||
m_report_missing = report;
|
||||
}
|
||||
|
||||
private:
|
||||
compound_factory();
|
||||
|
||||
@@ -301,6 +308,7 @@ class compound_factory
|
||||
static bool s_use_thread_local_instance;
|
||||
|
||||
std::shared_ptr<compound_factory_impl> m_impl;
|
||||
bool m_report_missing = true;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "cif++/row.hpp"
|
||||
#include "cif++/format.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
@@ -49,49 +50,49 @@
|
||||
* @code {.cpp}
|
||||
* cif::condition c = cif::key("id") == 1;
|
||||
* @endcode
|
||||
*
|
||||
*
|
||||
* That will find rows where the ID item contains the number 1. If
|
||||
* using cif::key is a bit too much typing, you can also write:
|
||||
*
|
||||
*
|
||||
* @code{.cpp}
|
||||
* using namespace cif::literals;
|
||||
*
|
||||
*
|
||||
* cif::condition c2 = "id"_key == 1;
|
||||
* @endcode
|
||||
*
|
||||
*
|
||||
* Now if you want both ID = 1 and ID = 2 in the result:
|
||||
*
|
||||
*
|
||||
* @code{.cpp}
|
||||
* auto c3 = "id"_key == 1 or "id"_key == 2;
|
||||
* @endcode
|
||||
*
|
||||
*
|
||||
* There are some special values you can use. To find rows with item that
|
||||
* do not have a value:
|
||||
*
|
||||
*
|
||||
* @code{.cpp}
|
||||
* auto c4 = "type"_key == cif::null;
|
||||
* @endcode
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
* Of if it should not be NULL:
|
||||
*
|
||||
*
|
||||
* @code{.cpp}
|
||||
* auto c5 = "type"_key != cif::null;
|
||||
* @endcode
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
* There's even a way to find all records:
|
||||
*
|
||||
*
|
||||
* @code{.cpp}
|
||||
* auto c6 = cif::all;
|
||||
* @endcode
|
||||
*
|
||||
*
|
||||
* And when you want to search for any item containing the value 'foo':
|
||||
*
|
||||
*
|
||||
* @code{.cpp}
|
||||
* auto c7 = cif::any == "foo";
|
||||
* @endcode
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
* All these conditions can be chained together again:
|
||||
*
|
||||
*
|
||||
* @code{.cpp}
|
||||
* auto c8 = std::move(c3) and std::move(c5);
|
||||
* @endcode
|
||||
@@ -106,7 +107,7 @@ namespace cif
|
||||
|
||||
/**
|
||||
* @brief Get the items that can be used as key in conditions for a category
|
||||
*
|
||||
*
|
||||
* @param cat The category whose items to return
|
||||
* @return iset The set of key item names
|
||||
*/
|
||||
@@ -115,7 +116,7 @@ iset get_category_fields(const category &cat);
|
||||
|
||||
/**
|
||||
* @brief Get the items that can be used as key in conditions for a category
|
||||
*
|
||||
*
|
||||
* @param cat The category whose items to return
|
||||
* @return iset The set of key field names
|
||||
*/
|
||||
@@ -123,7 +124,7 @@ iset get_category_items(const category &cat);
|
||||
|
||||
/**
|
||||
* @brief Get the item index for item @a col in category @a cat
|
||||
*
|
||||
*
|
||||
* @param cat The category
|
||||
* @param col The name of the item
|
||||
* @return uint16_t The index
|
||||
@@ -132,7 +133,7 @@ uint16_t get_item_ix(const category &cat, std::string_view col);
|
||||
|
||||
/**
|
||||
* @brief Return whether the item @a col in category @a cat has a primitive type of *uchar*
|
||||
*
|
||||
*
|
||||
* @param cat The category
|
||||
* @param col The item name
|
||||
* @return true If the primitive type is of type *uchar*
|
||||
@@ -175,14 +176,13 @@ namespace detail
|
||||
class condition
|
||||
{
|
||||
public:
|
||||
|
||||
/** @cond */
|
||||
using condition_impl = detail::condition_impl;
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
* @brief Construct a new, empty condition object
|
||||
*
|
||||
*
|
||||
*/
|
||||
condition()
|
||||
: m_impl(nullptr)
|
||||
@@ -191,7 +191,7 @@ class condition
|
||||
|
||||
/**
|
||||
* @brief Construct a new condition object with implementation @a impl
|
||||
*
|
||||
*
|
||||
* @param impl The implementation to use
|
||||
*/
|
||||
explicit condition(condition_impl *impl)
|
||||
@@ -230,15 +230,15 @@ class condition
|
||||
/**
|
||||
* @brief Prepare the condition to be used on category @a c. This will
|
||||
* take care of setting the correct indices for items e.g.
|
||||
*
|
||||
*
|
||||
* @param c The category this query should act upon
|
||||
*/
|
||||
void prepare(const category &c);
|
||||
|
||||
/**
|
||||
* @brief This operator returns true if the row referenced by @a r is
|
||||
* @brief This operator returns true if the row referenced by @a r is
|
||||
* a match for this condition.
|
||||
*
|
||||
*
|
||||
* @param r The reference to a row.
|
||||
* @return true If there is a match
|
||||
* @return false If there is no match
|
||||
@@ -263,7 +263,7 @@ class condition
|
||||
/**
|
||||
* @brief If the prepare step found out there is only one hit
|
||||
* this single hit can be returned by this method.
|
||||
*
|
||||
*
|
||||
* @return std::optional<row_handle> The result will contain
|
||||
* a row reference if there is a single hit, it will be empty otherwise
|
||||
*/
|
||||
@@ -292,7 +292,7 @@ class condition
|
||||
|
||||
/**
|
||||
* @brief Operator to use to write out a condition to @a os, for debugging purposes
|
||||
*
|
||||
*
|
||||
* @param os The std::ostream to write to
|
||||
* @param cond The condition to write
|
||||
* @return std::ostream& The same as @a os
|
||||
@@ -752,28 +752,9 @@ namespace detail
|
||||
delete sub;
|
||||
}
|
||||
|
||||
condition_impl *prepare(const category &c) override
|
||||
{
|
||||
for (auto &sub : m_sub)
|
||||
sub = sub->prepare(c);
|
||||
return this;
|
||||
}
|
||||
condition_impl *prepare(const category &c) override;
|
||||
|
||||
bool test(row_handle r) const override
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
for (auto sub : m_sub)
|
||||
{
|
||||
if (sub->test(r))
|
||||
continue;
|
||||
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
bool test(row_handle r) const override;
|
||||
|
||||
void str(std::ostream &os) const override
|
||||
{
|
||||
@@ -820,6 +801,7 @@ namespace detail
|
||||
static condition_impl *combine_equal(std::vector<and_condition_impl *> &subs, or_condition_impl *oc);
|
||||
|
||||
std::vector<condition_impl *> m_sub;
|
||||
std::optional<row_handle> m_single; // Potential result of index lookup
|
||||
};
|
||||
|
||||
struct or_condition_impl : public condition_impl
|
||||
@@ -977,9 +959,9 @@ inline condition operator or(condition &&a, condition &&b)
|
||||
if (ci->m_item_name == ce->m_item_name)
|
||||
return condition(new detail::key_equals_or_empty_condition_impl(ci));
|
||||
}
|
||||
|
||||
|
||||
if (typeid(*b.m_impl) == typeid(detail::key_equals_condition_impl) and
|
||||
typeid(*a.m_impl) == typeid(detail::key_is_empty_condition_impl))
|
||||
typeid(*a.m_impl) == typeid(detail::key_is_empty_condition_impl))
|
||||
{
|
||||
auto ci = static_cast<detail::key_equals_condition_impl *>(b.m_impl);
|
||||
auto ce = static_cast<detail::key_is_empty_condition_impl *>(a.m_impl);
|
||||
@@ -997,9 +979,9 @@ inline condition operator or(condition &&a, condition &&b)
|
||||
if (ci->m_item_name == ce->m_item_name)
|
||||
return condition(new detail::key_equals_number_or_empty_condition_impl(ci));
|
||||
}
|
||||
|
||||
|
||||
if (typeid(*b.m_impl) == typeid(detail::key_equals_number_condition_impl) and
|
||||
typeid(*a.m_impl) == typeid(detail::key_is_empty_condition_impl))
|
||||
typeid(*a.m_impl) == typeid(detail::key_is_empty_condition_impl))
|
||||
{
|
||||
auto ci = static_cast<detail::key_equals_number_condition_impl *>(b.m_impl);
|
||||
auto ce = static_cast<detail::key_is_empty_condition_impl *>(a.m_impl);
|
||||
@@ -1019,7 +1001,7 @@ inline condition operator or(condition &&a, condition &&b)
|
||||
|
||||
/**
|
||||
* @brief A helper class to make it possible to search for empty items (NULL)
|
||||
*
|
||||
*
|
||||
* @code{.cpp}
|
||||
* "id"_key == cif::empty_type();
|
||||
* @endcode
|
||||
@@ -1031,7 +1013,7 @@ struct empty_type
|
||||
|
||||
/**
|
||||
* @brief A helper to make it possible to have conditions like
|
||||
*
|
||||
*
|
||||
* @code{.cpp}
|
||||
* "id"_key == cif::null;
|
||||
* @endcode
|
||||
@@ -1041,14 +1023,14 @@ inline constexpr empty_type null = empty_type();
|
||||
|
||||
/**
|
||||
* @brief Class to use in creating conditions, creates a reference to a item or item
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct key
|
||||
{
|
||||
/**
|
||||
* @brief Construct a new key object using @a item_name as name
|
||||
*
|
||||
* @param item_name
|
||||
*
|
||||
* @param item_name
|
||||
*/
|
||||
explicit key(const std::string &item_name)
|
||||
: m_item_name(item_name)
|
||||
@@ -1057,8 +1039,8 @@ struct key
|
||||
|
||||
/**
|
||||
* @brief Construct a new key object using @a item_name as name
|
||||
*
|
||||
* @param item_name
|
||||
*
|
||||
* @param item_name
|
||||
*/
|
||||
explicit key(const char *item_name)
|
||||
: m_item_name(item_name)
|
||||
@@ -1067,8 +1049,8 @@ struct key
|
||||
|
||||
/**
|
||||
* @brief Construct a new key object using @a item_name as name
|
||||
*
|
||||
* @param item_name
|
||||
*
|
||||
* @param item_name
|
||||
*/
|
||||
explicit key(std::string_view item_name)
|
||||
: m_item_name(item_name)
|
||||
@@ -1090,7 +1072,8 @@ concept Numeric = ((std::is_floating_point_v<T> or std::is_integral_v<T>) and no
|
||||
template <Numeric T>
|
||||
condition operator==(const key &key, const T &v)
|
||||
{
|
||||
return condition(new detail::key_equals_number_condition_impl(key.m_item_name, v));
|
||||
// TODO: change key_equals_etc... to use std::variant<double,int64_t> or something
|
||||
return condition(new detail::key_equals_number_condition_impl(key.m_item_name, static_cast<double>(v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1137,13 +1120,10 @@ inline condition operator!=(const key &key, std::string_view value)
|
||||
template <Numeric T>
|
||||
condition operator>(const key &key, const T &v)
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << " > " << v;
|
||||
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v) > 0; },
|
||||
s.str()));
|
||||
cif::format(" > {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1152,13 +1132,10 @@ condition operator>(const key &key, const T &v)
|
||||
template <Numeric T>
|
||||
condition operator>=(const key &key, const T &v)
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << " >= " << v;
|
||||
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v) >= 0; },
|
||||
s.str()));
|
||||
cif::format(" >= {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1167,13 +1144,10 @@ condition operator>=(const key &key, const T &v)
|
||||
template <Numeric T>
|
||||
condition operator<(const key &key, const T &v)
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << " < " << v;
|
||||
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v) < 0; },
|
||||
s.str()));
|
||||
cif::format(" < {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1182,13 +1156,10 @@ condition operator<(const key &key, const T &v)
|
||||
template <Numeric T>
|
||||
condition operator<=(const key &key, const T &v)
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << " <= " << v;
|
||||
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v) <= 0; },
|
||||
s.str()));
|
||||
cif::format(" <= {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1196,13 +1167,10 @@ condition operator<=(const key &key, const T &v)
|
||||
*/
|
||||
inline condition operator>(const key &key, std::string_view v)
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << " > " << v;
|
||||
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v, icase) > 0; },
|
||||
s.str()));
|
||||
cif::format(" > {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1210,13 +1178,10 @@ inline condition operator>(const key &key, std::string_view v)
|
||||
*/
|
||||
inline condition operator>=(const key &key, std::string_view v)
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << " >= " << v;
|
||||
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v, icase) >= 0; },
|
||||
s.str()));
|
||||
cif::format(" >= {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1224,13 +1189,10 @@ inline condition operator>=(const key &key, std::string_view v)
|
||||
*/
|
||||
inline condition operator<(const key &key, std::string_view v)
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << " < " << v;
|
||||
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v, icase) < 0; },
|
||||
s.str()));
|
||||
cif::format(" < {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1238,13 +1200,10 @@ inline condition operator<(const key &key, std::string_view v)
|
||||
*/
|
||||
inline condition operator<=(const key &key, std::string_view v)
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << " <= " << v;
|
||||
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v, icase) <= 0; },
|
||||
s.str()));
|
||||
cif::format(" <= {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1345,7 +1304,7 @@ namespace literals
|
||||
{
|
||||
/**
|
||||
* @brief Return a cif::key for the item name @a text
|
||||
*
|
||||
*
|
||||
* @param text The name of the item
|
||||
* @param length The length of @a text
|
||||
* @return key The cif::key created
|
||||
|
||||
@@ -128,15 +128,6 @@ class datablock : public std::list<category>
|
||||
*/
|
||||
bool is_valid() const;
|
||||
|
||||
/**
|
||||
* @brief Validates the content of this datablock and all its content
|
||||
* and updates or removes the audit_conform category to match the result.
|
||||
*
|
||||
* @return true If the content is valid
|
||||
* @return false If the content is not valid
|
||||
*/
|
||||
bool is_valid();
|
||||
|
||||
/**
|
||||
* @brief Validates all contained data for valid links between parents and children
|
||||
* as defined in the validator
|
||||
@@ -146,6 +137,14 @@ class datablock : public std::list<category>
|
||||
*/
|
||||
bool validate_links() const;
|
||||
|
||||
/**
|
||||
* @brief Strip removes all categories and items that are invalid according
|
||||
* to the assigned validator. Will also add a valid audit_conform block.
|
||||
*
|
||||
* @return true if the remaining datablock is valid
|
||||
*/
|
||||
bool strip();
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -184,6 +183,15 @@ class datablock : public std::list<category>
|
||||
*/
|
||||
const category *get(std::string_view name) const;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return true if this datablock contains a non-empty category
|
||||
*/
|
||||
bool contains(std::string_view name) const
|
||||
{
|
||||
return get(name) != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tries to find a category with name @a name and will create a
|
||||
* new one if it is not found. The result is a tuple of an iterator
|
||||
|
||||
@@ -26,138 +26,28 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if __has_include(<format>)
|
||||
#include <format>
|
||||
#define USE_STD_FORMAT 1
|
||||
#else
|
||||
#include <fmt/format.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
/** \file format.hpp
|
||||
*
|
||||
* File containing a basic reimplementation of boost::format
|
||||
* but then a bit more simplistic. Still this allowed me to move my code
|
||||
* from using boost::format to something without external dependency easily.
|
||||
* Now using cif::format instead of a home grown rip off
|
||||
*/
|
||||
|
||||
namespace cif
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename T>
|
||||
struct to_varg
|
||||
{
|
||||
using type = T;
|
||||
|
||||
to_varg(const T &v)
|
||||
: m_value(v)
|
||||
{
|
||||
}
|
||||
|
||||
type operator*() { return m_value; }
|
||||
|
||||
T m_value;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct to_varg<const char *>
|
||||
{
|
||||
using type = const char *;
|
||||
|
||||
to_varg(const char *v)
|
||||
: m_value(v)
|
||||
{
|
||||
}
|
||||
|
||||
type operator*() { return m_value.c_str(); }
|
||||
|
||||
std::string m_value;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct to_varg<std::string>
|
||||
{
|
||||
using type = const char *;
|
||||
|
||||
to_varg(const std::string &v)
|
||||
: m_value(v)
|
||||
{
|
||||
}
|
||||
|
||||
type operator*() { return m_value.c_str(); }
|
||||
|
||||
std::string m_value;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/** @cond */
|
||||
|
||||
template <typename... Args>
|
||||
class format_plus_arg
|
||||
{
|
||||
public:
|
||||
using args_vector_type = std::tuple<detail::to_varg<Args>...>;
|
||||
using vargs_vector_type = std::tuple<typename detail::to_varg<Args>::type...>;
|
||||
|
||||
format_plus_arg(const format_plus_arg &) = delete;
|
||||
format_plus_arg &operator=(const format_plus_arg &) = delete;
|
||||
|
||||
|
||||
format_plus_arg(std::string_view fmt, Args... args)
|
||||
: m_fmt(fmt)
|
||||
, m_args(std::forward<Args>(args)...)
|
||||
{
|
||||
auto ix = std::make_index_sequence<sizeof...(Args)>();
|
||||
copy_vargs(ix);
|
||||
}
|
||||
|
||||
std::string str()
|
||||
{
|
||||
char buffer[1024];
|
||||
std::string::size_type r = std::apply(snprintf, std::tuple_cat(std::make_tuple(buffer, sizeof(buffer), m_fmt.c_str()), m_vargs));
|
||||
return { buffer, r };
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &os, const format_plus_arg &f)
|
||||
{
|
||||
char buffer[1024];
|
||||
std::string::size_type r = std::apply(snprintf, std::tuple_cat(std::make_tuple(buffer, sizeof(buffer), f.m_fmt.c_str()), f.m_vargs));
|
||||
os.write(buffer, r);
|
||||
return os;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template <std::size_t... I>
|
||||
void copy_vargs(std::index_sequence<I...>)
|
||||
{
|
||||
((std::get<I>(m_vargs) = *std::get<I>(m_args)), ...);
|
||||
}
|
||||
|
||||
std::string m_fmt;
|
||||
args_vector_type m_args;
|
||||
vargs_vector_type m_vargs;
|
||||
};
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
* @brief A simplistic reimplementation of boost::format, in fact it is
|
||||
* actually a way to call the C function snprintf to format the arguments
|
||||
* in @a args into the format string @a fmt
|
||||
*
|
||||
* The string in @a fmt should thus be a C style format string.
|
||||
*
|
||||
* TODO: Move to C++23 style of printing.
|
||||
*
|
||||
* @tparam Args The types of the arguments
|
||||
* @param fmt The format string
|
||||
* @param args The arguments
|
||||
* @return An object that can be written out to a std::ostream using operator<<
|
||||
*/
|
||||
|
||||
template <typename... Args>
|
||||
constexpr auto format(std::string_view fmt, Args... args)
|
||||
{
|
||||
return format_plus_arg(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
#if USE_STD_FORMAT
|
||||
using std::format;
|
||||
#else
|
||||
using fmt::format;
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/// A streambuf that fills out lines with spaces up until a specified width
|
||||
|
||||
@@ -1,7 +1,33 @@
|
||||
// Copyright Maarten L. Hekkelman, 2022
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2025 NKI/AVL, Netherlands Cancer Institute
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
Original code comes from libgxrio at https://github.com/mhekkel/gxrio
|
||||
This is a stripped down version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -250,6 +250,8 @@ class item
|
||||
return value();
|
||||
}
|
||||
|
||||
auto operator<=>(const item &rhs) const = default;
|
||||
|
||||
private:
|
||||
std::string_view m_name;
|
||||
std::string m_value;
|
||||
|
||||
@@ -106,8 +106,6 @@ class atom
|
||||
|
||||
atom_impl(const atom_impl &i) = default;
|
||||
|
||||
void prefetch();
|
||||
|
||||
int compare(const atom_impl &b) const;
|
||||
|
||||
// bool getAnisoU(float anisou[6]) const;
|
||||
@@ -345,7 +343,7 @@ class atom
|
||||
std::string get_auth_asym_id() const { return get_property("auth_asym_id"); } ///< Return the auth_asym_id property
|
||||
std::string get_auth_seq_id() const { return get_property("auth_seq_id"); } ///< Return the auth_seq_id property
|
||||
std::string get_auth_atom_id() const { return get_property("auth_atom_id"); } ///< Return the auth_atom_id property
|
||||
std::string get_auth_alt_id() const { return get_property("auth_alt_id"); } ///< Return the auth_alt_id property
|
||||
std::string get_auth_alt_id() const { return get_property("pdbx_auth_alt_id"); } ///< Return the auth_alt_id property
|
||||
std::string get_auth_comp_id() const { return get_property("auth_comp_id"); } ///< Return the auth_comp_id property
|
||||
std::string get_pdb_ins_code() const { return get_property("pdbx_PDB_ins_code"); } ///< Return the pdb_ins_code property
|
||||
|
||||
@@ -481,8 +479,8 @@ class residue
|
||||
, m_compound_id(compoundID)
|
||||
, m_asym_id(asymID)
|
||||
, m_seq_id(seqID)
|
||||
, m_auth_asym_id(authAsymID)
|
||||
, m_auth_seq_id(authSeqID)
|
||||
, m_pdb_strand_id(authAsymID)
|
||||
, m_pdb_seq_num(authSeqID)
|
||||
, m_pdb_ins_code(pdbInsCode)
|
||||
{
|
||||
}
|
||||
@@ -509,9 +507,9 @@ class residue
|
||||
const std::string &get_asym_id() const { return m_asym_id; } ///< Return the asym_id
|
||||
int get_seq_id() const { return m_seq_id; } ///< Return the seq_id
|
||||
|
||||
const std::string get_auth_asym_id() const { return m_auth_asym_id; } ///< Return the auth_asym_id
|
||||
const std::string get_auth_seq_id() const { return m_auth_seq_id; } ///< Return the auth_seq_id
|
||||
std::string get_pdb_ins_code() const { return m_pdb_ins_code; } ///< Return the pdb_ins_code
|
||||
const std::string get_pdb_strand_id() const { return m_pdb_strand_id; } ///< Return the pdb_strand_id
|
||||
const std::string get_pdb_seq_num() const { return m_pdb_seq_num; } ///< Return the pdb_seq_num
|
||||
std::string get_pdb_ins_code() const { return m_pdb_ins_code; } ///< Return the pdb_ins_code
|
||||
|
||||
const std::string &get_compound_id() const { return m_compound_id; } ///< Return the compound_id
|
||||
void set_compound_id(const std::string &id) { m_compound_id = id; } ///< Set the compound_id to @a id
|
||||
@@ -580,7 +578,7 @@ class residue
|
||||
m_seq_id == rhs.m_seq_id and
|
||||
m_asym_id == rhs.m_asym_id and
|
||||
m_compound_id == rhs.m_compound_id and
|
||||
m_auth_seq_id == rhs.m_auth_seq_id);
|
||||
m_pdb_seq_num == rhs.m_pdb_seq_num);
|
||||
}
|
||||
|
||||
/// @brief Create a new atom and add it to the list
|
||||
@@ -594,7 +592,7 @@ class residue
|
||||
structure *m_structure = nullptr;
|
||||
std::string m_compound_id, m_asym_id;
|
||||
int m_seq_id = 0;
|
||||
std::string m_auth_asym_id, m_auth_seq_id, m_pdb_ins_code;
|
||||
std::string m_pdb_strand_id, m_pdb_seq_num, m_pdb_ins_code;
|
||||
std::vector<atom> m_atoms;
|
||||
/** @endcond */
|
||||
};
|
||||
@@ -714,15 +712,15 @@ class polymer : public std::vector<monomer>
|
||||
|
||||
structure *get_structure() const { return m_structure; } ///< Return the structure
|
||||
|
||||
std::string get_asym_id() const { return m_asym_id; } ///< Return the asym_id
|
||||
std::string get_auth_asym_id() const { return m_auth_asym_id; } ///< Return the PDB chain ID, actually
|
||||
std::string get_entity_id() const { return m_entity_id; } ///< Return the entity_id
|
||||
std::string get_asym_id() const { return m_asym_id; } ///< Return the asym_id
|
||||
std::string get_pdb_strand_id() const { return m_pdb_strand_id; } ///< Return the PDB chain ID, actually
|
||||
std::string get_entity_id() const { return m_entity_id; } ///< Return the entity_id
|
||||
|
||||
private:
|
||||
structure *m_structure;
|
||||
std::string m_entity_id;
|
||||
std::string m_asym_id;
|
||||
std::string m_auth_asym_id;
|
||||
std::string m_pdb_strand_id;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -760,7 +758,7 @@ class sugar : public residue
|
||||
int num() const
|
||||
{
|
||||
int result;
|
||||
auto r = std::from_chars(m_auth_seq_id.data(), m_auth_seq_id.data() + m_auth_seq_id.length(), result);
|
||||
auto r = std::from_chars(m_pdb_seq_num.data(), m_pdb_seq_num.data() + m_pdb_seq_num.length(), result);
|
||||
if ((bool)r.ec)
|
||||
throw std::runtime_error("The auth_seq_id should be a number for a sugar");
|
||||
return result;
|
||||
@@ -859,19 +857,38 @@ class branch : public std::vector<sugar>
|
||||
std::string m_asym_id, m_entity_id;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/// \brief A still very limited set of options for reading structures
|
||||
enum class StructureOpenOptions
|
||||
/** @brief Enumeration for controlling atom selection based on occupancy. */
|
||||
enum class occupancy_policy
|
||||
{
|
||||
SkipHydrogen = 1 << 0 ///< Do not include hydrogen atoms in the structure object
|
||||
/** @brief Include all atoms regardless of their occupancy factor. */
|
||||
ALL = 0,
|
||||
|
||||
/** @brief Select only alternate atoms with the maximum occupancy factor.
|
||||
* If multiple atoms have the same maximum occupancy, choose the one with the minimum B-factor.
|
||||
* If multiple atoms share both the maximum occupancy and the minimum B-factor, select the first encountered atom.
|
||||
*/
|
||||
MAX = 1,
|
||||
|
||||
/** @brief Select only alternate atoms with the minimum occupancy factor.
|
||||
* Similar to MAX, if multiple atoms have the same minimum occupancy, choose the one with the minimum B-factor.
|
||||
* If multiple atoms share both the minimum occupancy and the minimum B-factor, select the first encountered atom.
|
||||
*/
|
||||
MIN = 2,
|
||||
|
||||
/** @brief Exclude all atoms with an occupancy factor greater than zero. */
|
||||
UNOCCUPIED = 3
|
||||
};
|
||||
|
||||
/// \brief A way to combine two options. Not very useful as there is only one...
|
||||
constexpr inline bool operator&(StructureOpenOptions a, StructureOpenOptions b)
|
||||
struct structure_open_options
|
||||
{
|
||||
return static_cast<int>(a) bitand static_cast<int>(b);
|
||||
}
|
||||
bool skip_hydrogen = false; ///< Do not include hydrogen atoms in the structure object
|
||||
bool skip_hetatom = false; ///< Do not include HET atoms in the structure object
|
||||
bool skip_water = false; ///< Do not include water atoms in the structure object
|
||||
occupancy_policy occupancy_mode = occupancy_policy::ALL; ///< By default, the occupancy policy is set to occupancy_policy::ALL
|
||||
std::vector<std::string> asyms; ///< The asyms to load, if empty load all
|
||||
std::optional<float> min_b_factor; ///< Only load atoms with at least this b_factor
|
||||
std::optional<float> max_b_factor; ///< Only load atoms with at most this b_factor
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
@@ -885,10 +902,10 @@ class structure
|
||||
{
|
||||
public:
|
||||
/// \brief Read the structure from cif::file @a p
|
||||
structure(file &p, std::size_t modelNr = 1, StructureOpenOptions options = {});
|
||||
structure(file &p, std::size_t modelNr = 1, structure_open_options options = {});
|
||||
|
||||
/// \brief Load the structure from already parsed mmCIF data in @a db
|
||||
structure(datablock &db, std::size_t modelNr = 1, StructureOpenOptions options = {});
|
||||
structure(datablock &db, std::size_t modelNr = 1, structure_open_options options = {});
|
||||
|
||||
/** @cond */
|
||||
structure(structure &&s) = default;
|
||||
@@ -1118,7 +1135,7 @@ class structure
|
||||
friend polymer;
|
||||
friend residue;
|
||||
|
||||
void load_atoms_for_model(StructureOpenOptions options);
|
||||
void load_atoms_for_model(structure_open_options options);
|
||||
|
||||
std::string insert_compound(const std::string &compoundID, bool is_entity);
|
||||
|
||||
|
||||
@@ -104,6 +104,27 @@ inline void write(const std::filesystem::path &p, const file &f)
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Quickly fix a PDB file that lacks some often needed categories
|
||||
*
|
||||
* This differs from reconstruct_pdbx which does a much more thorough job
|
||||
*
|
||||
* \param pdbx_file The cif::file that hopefully contains some valid data
|
||||
*/
|
||||
|
||||
void fixup_pdbx(file &pdbx_file);
|
||||
|
||||
/**
|
||||
* @brief Quickly fix a PDB file that lacks some often needed categories
|
||||
*
|
||||
* This differs from reconstruct_pdbx which does a much more thorough job
|
||||
*
|
||||
* \param pdbx_file The cif::file that hopefully contains some valid data
|
||||
* \param v The validator to use
|
||||
*/
|
||||
|
||||
void fixup_pdbx(file &pdbx_file, const validator &v);
|
||||
|
||||
/** \brief Reconstruct all missing categories for an assumed PDBx file.
|
||||
*
|
||||
* Some people believe that simply dumping some atom records is enough.
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#pragma warning(disable : 4068) // unknown pragma
|
||||
#pragma warning(disable : 4100) // unreferenced formal parameter
|
||||
#pragma warning(disable : 4101) // unreferenced local variable
|
||||
#pragma warning(disable : 4702) // unreachable code (too bad, this one. Happens in for loops)
|
||||
#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 1
|
||||
#endif
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ _datablock.description
|
||||
#
|
||||
_dictionary.title mmcif_pdbx.dic
|
||||
_dictionary.datablock_id mmcif_pdbx.dic
|
||||
_dictionary.version 5.403
|
||||
_dictionary.version 5.404
|
||||
#
|
||||
loop_
|
||||
_dictionary_history.version
|
||||
@@ -3292,6 +3292,18 @@ Changes (ep):
|
||||
+ Add 'M' to _em_software.name enumeration.
|
||||
;
|
||||
|
||||
5.404 2025-06-01
|
||||
;
|
||||
Changes (ep):
|
||||
+ Add DeepEMhancer to _em_software.name enumeration
|
||||
+ Add HexAuFoil to _em_sample_support.grid_type enumeration
|
||||
+ Add "PSI JUNGFRAU 9M" and "PSI JUNGFRAU 10M" detectors to
|
||||
_diffrn_detector.type
|
||||
+ Add "N6-benzoyllysine", "N6-isonicotinyllysine",
|
||||
and "N6-methacryllysine" to enumeration list for
|
||||
_pdbx_chem_comp_pcm.type and _pdbx_modification_feature.type
|
||||
;
|
||||
|
||||
#
|
||||
loop_
|
||||
_sub_category.id
|
||||
@@ -3838,13 +3850,13 @@ _pdbx_dictionary_component.datablock_id
|
||||
_pdbx_dictionary_component.dictionary_component_id
|
||||
_pdbx_dictionary_component.title
|
||||
_pdbx_dictionary_component.version
|
||||
mmcif_pdbx-base.dic mmcif_pdbx-base.dic "mmCIF/PDBx base dictionary" 0.39
|
||||
mmcif_pdbx-base.dic mmcif_pdbx-base.dic "mmCIF/PDBx base dictionary" 0.40
|
||||
mmcif_xfel_extensions-v3.dic mmcif_xfel_extensions-v3.dic "PDBx/mmCIF XFELDictionary License Extension" 0.0.2
|
||||
mmcif_pdbx_audit_support-extension.dic mmcif_pdbx_audit_support-extension.dic "mmCIF/PDBx Audit support extension" 0.24
|
||||
mmcif_pdbx_sifts.dic mmcif_pdbx_sifts.dic "PDBx/mmCIF Dictionary Sifts Extension" 0.0.2
|
||||
mmcif_pdbx_license.dic mmcif_pdbx_license.dic "PDBx/mmCIF Dictionary License Extension" 0.0.1
|
||||
initial-model-extension.dic initial-model-extension.dic "PDBx/mmCIF Initial model extension" 0.10
|
||||
ptm-extension.dic ptm-extension.dic "PDBx/mmCIF PTM extension" 0.10
|
||||
ptm-extension.dic ptm-extension.dic "PDBx/mmCIF PTM extension" 0.11
|
||||
#
|
||||
loop_
|
||||
_pdbx_dictionary_component_history.dictionary_component_id
|
||||
@@ -4181,6 +4193,15 @@ Changes (ep):
|
||||
+ Add 'M' to _em_software.name enumeration.
|
||||
;
|
||||
|
||||
mmcif_pdbx-base.dic 0.40 2025-06-01
|
||||
;
|
||||
Changes (ep):
|
||||
+ Add DeepEMhancer to _em_software.name enumeration
|
||||
+ Add HexAuFoil to _em_sample_support.grid_type enumeration
|
||||
+ Add "PSI JUNGFRAU 9M" and "PSI JUNGFRAU 10M" detectors to
|
||||
_diffrn_detector.type
|
||||
;
|
||||
|
||||
mmcif_xfel_extensions-v3.dic 0.0.1 2023-05-31
|
||||
;
|
||||
Changes (ep):
|
||||
@@ -4606,6 +4627,16 @@ ptm-extension.dic 0.10 2024-11-26
|
||||
_pdbx_modification_feature.type
|
||||
;
|
||||
|
||||
ptm-extension.dic 0.11 2024-06-01
|
||||
;
|
||||
Changes (dh/ep)
|
||||
+ added "N6-benzoyllysine", "N6-isonicotinyllysine",
|
||||
and "N6-methacryllysine"
|
||||
type to enumeration list for
|
||||
_pdbx_chem_comp_pcm.type
|
||||
_pdbx_modification_feature.type
|
||||
;
|
||||
|
||||
#
|
||||
loop_
|
||||
_pdbx_item_linked_group.category_id
|
||||
@@ -21911,6 +21942,8 @@ save__diffrn_detector.type
|
||||
"_diffrn_detector.type" "PSI JUNGFRAU 1M" PIXEL
|
||||
"_diffrn_detector.type" "PSI JUNGFRAU 4M" PIXEL
|
||||
"_diffrn_detector.type" "PSI JUNGFRAU 8M" PIXEL
|
||||
"_diffrn_detector.type" "PSI JUNGFRAU 9M" PIXEL
|
||||
"_diffrn_detector.type" "PSI JUNGFRAU 10M" PIXEL
|
||||
"_diffrn_detector.type" "PSI JUNGFRAU 16M" PIXEL
|
||||
"_diffrn_detector.type" "PSI PILATUS 6M" PIXEL
|
||||
"_diffrn_detector.type" "RAYONIX MX-225" CCD
|
||||
@@ -117761,6 +117794,7 @@ save__em_sample_support.grid_type
|
||||
"_em_sample_support.grid_type" "Quantifoil Active R2/1" .
|
||||
"_em_sample_support.grid_type" "Quantifoil Active R1.6/0.9" .
|
||||
"_em_sample_support.grid_type" "Quantifoil Active R1.2/0.8" .
|
||||
"_em_sample_support.grid_type" HexAuFoil .
|
||||
"_em_sample_support.grid_type" UltrAuFoil .
|
||||
"_em_sample_support.grid_type" "UltrAuFoil R0./1" .
|
||||
"_em_sample_support.grid_type" "UltrAuFoil R1.2/1.3" .
|
||||
@@ -126147,6 +126181,7 @@ save__em_software.name
|
||||
"_em_software.name" CTFPHASEFLIP .
|
||||
"_em_software.name" CTFTILT .
|
||||
"_em_software.name" DE-IM .
|
||||
"_em_software.name" DeepEMhancer .
|
||||
"_em_software.name" DIALS .
|
||||
"_em_software.name" DigitalMicrograph .
|
||||
"_em_software.name" DireX .
|
||||
@@ -168351,6 +168386,9 @@ save__pdbx_chem_comp_pcm.type
|
||||
Methylsulfanylation .
|
||||
Methylsulfation .
|
||||
Myristoylation .
|
||||
N6-benzoyllysine .
|
||||
N6-isonicotinyllysine .
|
||||
N6-methacryllysine .
|
||||
"N-pyruvic acid 2-iminylation" .
|
||||
N-methylcarbamoylation .
|
||||
Nitration .
|
||||
@@ -169337,6 +169375,9 @@ save__pdbx_modification_feature.type
|
||||
Methylsulfanylation .
|
||||
Methylsulfation .
|
||||
Myristoylation .
|
||||
N6-benzoyllysine .
|
||||
N6-isonicotinyllysine .
|
||||
N6-methacryllysine .
|
||||
"N-pyruvic acid 2-iminylation" .
|
||||
N-methylcarbamoylation .
|
||||
Nitration .
|
||||
|
||||
@@ -92,7 +92,7 @@ class row_comparator
|
||||
return d;
|
||||
}
|
||||
|
||||
int operator()(const category &cat, const row_initializer &a, const row *b) const
|
||||
int operator()(const category &cat, const category::key_type &a, const row *b) const
|
||||
{
|
||||
assert(b);
|
||||
|
||||
@@ -105,10 +105,11 @@ class row_comparator
|
||||
{
|
||||
assert(ai != a.end());
|
||||
|
||||
std::string_view ka = ai->value();
|
||||
std::string_view ka = ai->value;
|
||||
std::string_view kb = rhb[k].text();
|
||||
|
||||
d = f(ka, kb);
|
||||
if (not (ai->may_be_null and rhb[k].empty()))
|
||||
d = f(ka, kb);
|
||||
|
||||
if (d != 0)
|
||||
break;
|
||||
@@ -142,7 +143,7 @@ class category_index
|
||||
}
|
||||
|
||||
row *find(const category &cat, row *k) const;
|
||||
row *find_by_value(const category &cat, row_initializer k) const;
|
||||
row *find_by_value(const category &cat, const category::key_type &k) const;
|
||||
|
||||
void insert(category &cat, row *r);
|
||||
void erase(category &cat, row *r);
|
||||
@@ -352,19 +353,19 @@ row *category_index::find(const category &cat, row *k) const
|
||||
return r ? r->m_row : nullptr;
|
||||
}
|
||||
|
||||
row *category_index::find_by_value(const category &cat, row_initializer k) const
|
||||
row *category_index::find_by_value(const category &cat, const category::key_type &k) const
|
||||
{
|
||||
// sort the values in k first
|
||||
|
||||
row_initializer k2;
|
||||
category::key_type k2;
|
||||
for (auto &f : cat.key_item_indices())
|
||||
{
|
||||
auto fld = cat.get_item_name(f);
|
||||
|
||||
auto ki = find_if(k.begin(), k.end(), [&fld](auto &i)
|
||||
{ return i.name() == fld; });
|
||||
{ return i.name == fld; });
|
||||
if (ki == k.end())
|
||||
k2.emplace_back(fld, "");
|
||||
k2.emplace_back(std::string{ fld }, "");
|
||||
else
|
||||
k2.emplace_back(*ki);
|
||||
}
|
||||
@@ -914,6 +915,24 @@ bool category::validate_links() const
|
||||
return result;
|
||||
}
|
||||
|
||||
void category::strip()
|
||||
{
|
||||
std::vector<std::string> to_be_removed;
|
||||
|
||||
for (auto &item : m_items)
|
||||
{
|
||||
if (item.m_validator == nullptr)
|
||||
to_be_removed.push_back(item.m_name);
|
||||
}
|
||||
|
||||
for (auto item : to_be_removed)
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
std::clog << "Dropping item " << m_name << '.' << item << '\n';
|
||||
remove_item(item);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
row_handle category::operator[](const key_type &key)
|
||||
@@ -1320,8 +1339,7 @@ std::string category::get_unique_value(std::string_view item_name)
|
||||
// brain-dead implementation
|
||||
for (std::size_t ix = 0; ix < size(); ++ix)
|
||||
{
|
||||
// result = m_name + "-" + std::to_string(ix);
|
||||
result = cif_id_for_number(ix);
|
||||
result = cif_id_for_number(static_cast<int>(ix));
|
||||
if (not contains(key(item_name) == result))
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -480,10 +480,13 @@ compound *local_compound_factory_impl::create(const std::string &id)
|
||||
|
||||
try
|
||||
{
|
||||
const auto &[id, name, threeLetterCode, group] =
|
||||
const auto &[id2, name, threeLetterCode, group] =
|
||||
chem_comp->front().get<std::string, std::string, std::string, std::string>("id", "name", "three_letter_code", "group");
|
||||
|
||||
result = construct_compound(db, id, name, threeLetterCode, group);
|
||||
if (id == id2)
|
||||
result = construct_compound(db, id, name, threeLetterCode, group);
|
||||
else
|
||||
throw std::runtime_error("Compound ID's don't match: id 1=" + id + ", id 2=" + id2);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
@@ -757,8 +760,7 @@ bool compound_factory::is_monomer(std::string_view res_name) const
|
||||
|
||||
void compound_factory::report_missing_compound(std::string_view compound_id)
|
||||
{
|
||||
static bool s_reported = false;
|
||||
if (std::exchange(s_reported, true) == false)
|
||||
if (std::exchange(m_report_missing, false))
|
||||
{
|
||||
using namespace cif::colour;
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cif++/category.hpp"
|
||||
#include "cif++/condition.hpp"
|
||||
#include "cif++/category.hpp"
|
||||
#include "cif++/validate.hpp"
|
||||
|
||||
namespace cif
|
||||
@@ -61,6 +61,52 @@ bool is_item_type_uchar(const category &cat, std::string_view col)
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// // index lookup
|
||||
// struct index_lookup_condition_impl : public condition_impl
|
||||
// {
|
||||
// index_lookup_condition_impl(row_initializer &&key_values)
|
||||
// : m_key_values(std::move(key_values))
|
||||
// {
|
||||
// }
|
||||
//
|
||||
// condition_impl *prepare(const category &c) override
|
||||
// {
|
||||
// m_single_hit = c[m_key_values];
|
||||
// return this;
|
||||
// }
|
||||
//
|
||||
// bool test(row_handle r) const override
|
||||
// {
|
||||
// return m_single_hit == r;
|
||||
// }
|
||||
//
|
||||
// void str(std::ostream &os) const override
|
||||
// {
|
||||
// os << "index scan";
|
||||
// }
|
||||
//
|
||||
// virtual std::optional<row_handle> single() const override
|
||||
// {
|
||||
// return m_single_hit;
|
||||
// }
|
||||
//
|
||||
// virtual bool equals(const condition_impl *rhs) const override
|
||||
// {
|
||||
// if (typeid(*rhs) == typeid(index_lookup_condition_impl))
|
||||
// {
|
||||
// auto ri = static_cast<const index_lookup_condition_impl *>(rhs);
|
||||
// if (m_single_hit or ri->m_single_hit)
|
||||
// return m_single_hit == ri->m_single_hit;
|
||||
// else
|
||||
// // watch out, both m_item_ix might be the same while item_names might be diffent (in case they both do not exist in the category)
|
||||
// return m_key_values == ri->m_key_values;
|
||||
// }
|
||||
// return this == rhs;
|
||||
// }
|
||||
//
|
||||
// row_initializer m_key_values;
|
||||
// row_handle m_single_hit;
|
||||
// };
|
||||
|
||||
condition_impl *key_equals_condition_impl::prepare(const category &c)
|
||||
{
|
||||
@@ -85,7 +131,8 @@ namespace detail
|
||||
c.key_item_indices().contains(m_item_ix) and
|
||||
c.key_item_indices().size() == 1)
|
||||
{
|
||||
m_single_hit = c[{ { m_item_name, m_value } }];
|
||||
item v(m_item_name, m_value);
|
||||
m_single_hit = c[{ { m_item_name, std::string{ v.value() }, false } }];
|
||||
}
|
||||
|
||||
return this;
|
||||
@@ -99,7 +146,8 @@ namespace detail
|
||||
{
|
||||
auto &cs = (*s)->m_sub;
|
||||
|
||||
if (find_if(cs.begin(), cs.end(), [c](const condition_impl *i) { return i->equals(c); }) == cs.end())
|
||||
if (find_if(cs.begin(), cs.end(), [c](const condition_impl *i)
|
||||
{ return i->equals(c); }) == cs.end())
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
@@ -119,7 +167,8 @@ namespace detail
|
||||
for (size_t fc_i = 0; fc_i < fc.size();)
|
||||
{
|
||||
auto c = fc[fc_i];
|
||||
if (not found_in_range(c, subs.begin() + 1, subs.end())) {
|
||||
if (not found_in_range(c, subs.begin() + 1, subs.end()))
|
||||
{
|
||||
++fc_i;
|
||||
continue;
|
||||
}
|
||||
@@ -137,11 +186,12 @@ namespace detail
|
||||
for (size_t ssub_i = 0; ssub_i < ssub.size();)
|
||||
{
|
||||
auto sc = ssub[ssub_i];
|
||||
if (not sc->equals(c)) {
|
||||
if (not sc->equals(c))
|
||||
{
|
||||
++ssub_i;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
ssub.erase(ssub.begin() + ssub_i);
|
||||
delete sc;
|
||||
break;
|
||||
@@ -158,6 +208,99 @@ namespace detail
|
||||
return oc;
|
||||
}
|
||||
|
||||
condition_impl *and_condition_impl::prepare(const category &c)
|
||||
{
|
||||
for (auto &sub : m_sub)
|
||||
sub = sub->prepare(c);
|
||||
|
||||
if (auto cv = c.get_cat_validator(); cv != nullptr)
|
||||
{
|
||||
// See if we can collapse a search part of this and_condition into a single index lookup
|
||||
|
||||
cif::iset keys{ cv->m_keys.begin(), cv->m_keys.end() };
|
||||
category::key_type lookup;
|
||||
std::vector<condition_impl *> subs;
|
||||
std::vector<std::string> may_be_empty;
|
||||
|
||||
for (auto &sub : m_sub)
|
||||
{
|
||||
if (auto s = dynamic_cast<const key_equals_condition_impl *>(sub); s != nullptr)
|
||||
{
|
||||
if (keys.contains(s->m_item_name))
|
||||
{
|
||||
lookup.emplace_back(s->m_item_name, s->m_value);
|
||||
subs.emplace_back(sub);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto s = dynamic_cast<const key_equals_number_condition_impl *>(sub); s != nullptr)
|
||||
{
|
||||
if (keys.contains(s->m_item_name))
|
||||
{
|
||||
item v{ s->m_item_name, s->m_value };
|
||||
lookup.emplace_back(s->m_item_name, std::string{ v.value() } );
|
||||
subs.emplace_back(sub);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto s = dynamic_cast<const key_equals_or_empty_condition_impl *>(sub); s != nullptr)
|
||||
{
|
||||
if (keys.contains(s->m_item_name))
|
||||
{
|
||||
lookup.emplace_back(s->m_item_name, s->m_value, true);
|
||||
subs.emplace_back(sub);
|
||||
may_be_empty.emplace_back(s->m_item_name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto s = dynamic_cast<const key_equals_number_or_empty_condition_impl *>(sub); s != nullptr)
|
||||
{
|
||||
if (keys.contains(s->m_item_name))
|
||||
{
|
||||
item v{ s->m_item_name, s->m_value };
|
||||
lookup.emplace_back(s->m_item_name, std::string{ v.value() }, true );
|
||||
subs.emplace_back(sub);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (lookup.size() == keys.size())
|
||||
{
|
||||
m_single = c[lookup];
|
||||
|
||||
for (auto s : subs)
|
||||
m_sub.erase(std::remove(m_sub.begin(), m_sub.end(), s), m_sub.end());
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
bool and_condition_impl::test(row_handle r) const
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (m_single.has_value() and *m_single != r)
|
||||
result = false;
|
||||
else
|
||||
{
|
||||
for (auto sub : m_sub)
|
||||
{
|
||||
if (sub->test(r))
|
||||
continue;
|
||||
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
condition_impl *or_condition_impl::prepare(const category &c)
|
||||
{
|
||||
std::vector<and_condition_impl *> and_conditions;
|
||||
@@ -181,7 +324,7 @@ void condition::prepare(const category &c)
|
||||
{
|
||||
if (m_impl)
|
||||
m_impl = m_impl->prepare(c);
|
||||
|
||||
|
||||
m_prepared = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,17 +78,41 @@ bool datablock::is_valid() const
|
||||
return result;
|
||||
}
|
||||
|
||||
bool datablock::is_valid()
|
||||
bool datablock::validate_links() const
|
||||
{
|
||||
if (m_validator == nullptr)
|
||||
throw std::runtime_error("Validator not specified for datablock data_" + name());
|
||||
|
||||
bool result = true;
|
||||
|
||||
for (auto &cat : *this)
|
||||
result = cat.is_valid() and result;
|
||||
const_cast<category &>(cat).update_links(*this);
|
||||
|
||||
for (auto &cat : *this)
|
||||
result = cat.validate_links() and result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool datablock::strip()
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
// remove all categories that have no validator
|
||||
erase(std::remove_if(begin(), end(), [](category &c) {
|
||||
bool result = false;
|
||||
if (c.get_cat_validator() == nullptr)
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
std::clog << "Dropping category " << c.name() << '\n';
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}), end());
|
||||
|
||||
// then strip the remaining categories
|
||||
for (auto &cat : *this)
|
||||
cat.strip();
|
||||
|
||||
// Add or remove the audit_conform block here.
|
||||
if (result)
|
||||
if (is_valid())
|
||||
{
|
||||
// If the dictionary declares an audit_conform category, put it in,
|
||||
// but only if it does not exist already!
|
||||
@@ -101,22 +125,7 @@ bool datablock::is_valid()
|
||||
}
|
||||
}
|
||||
else
|
||||
erase(std::find_if(begin(), end(), [](category &cat)
|
||||
{ return cat.name() == "audit_conform"; }),
|
||||
end());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool datablock::validate_links() const
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
for (auto &cat : *this)
|
||||
const_cast<category &>(cat).update_links(*this);
|
||||
|
||||
for (auto &cat : *this)
|
||||
result = cat.validate_links() and result;
|
||||
result = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
13
src/file.cpp
13
src/file.cpp
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
#include "cif++/file.hpp"
|
||||
#include "cif++/condition.hpp"
|
||||
#include "cif++/gzio.hpp"
|
||||
|
||||
namespace cif
|
||||
@@ -46,8 +47,16 @@ bool file::is_valid()
|
||||
{
|
||||
bool result = not empty();
|
||||
|
||||
for (auto &d : *this)
|
||||
result = d.is_valid() and result;
|
||||
for (bool first = true; auto &d : *this)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
result = d.is_valid() and result;
|
||||
first = false;
|
||||
}
|
||||
else if (d.get_validator() != nullptr)
|
||||
result = d.is_valid() and result;
|
||||
}
|
||||
|
||||
if (result)
|
||||
result = validate_links();
|
||||
|
||||
413
src/model.cpp
413
src/model.cpp
@@ -47,15 +47,10 @@ void atom::atom_impl::moveTo(const point &p)
|
||||
|
||||
auto r = row();
|
||||
|
||||
#if __cpp_lib_format
|
||||
r.assign("Cartn_x", std::format("{:.3f}", p.m_x), false, false);
|
||||
r.assign("Cartn_y", std::format("{:.3f}", p.m_y), false, false);
|
||||
r.assign("Cartn_z", std::format("{:.3f}", p.m_z), false, false);
|
||||
#else
|
||||
r.assign("Cartn_x", cif::format("%.3f", p.m_x).str(), false, false);
|
||||
r.assign("Cartn_y", cif::format("%.3f", p.m_y).str(), false, false);
|
||||
r.assign("Cartn_z", cif::format("%.3f", p.m_z).str(), false, false);
|
||||
#endif
|
||||
r.assign("Cartn_x", cif::format("{:.3f}", p.m_x), false, false);
|
||||
r.assign("Cartn_y", cif::format("{:.3f}", p.m_y), false, false);
|
||||
r.assign("Cartn_z", cif::format("{:.3f}", p.m_z), false, false);
|
||||
|
||||
m_location = p;
|
||||
}
|
||||
|
||||
@@ -102,18 +97,18 @@ void atom::atom_impl::set_property(const std::string_view name, const std::strin
|
||||
r.assign(name, value, true, true);
|
||||
}
|
||||
|
||||
// int atom::atom_impl::compare(const atom_impl &b) const
|
||||
// {
|
||||
// int d = m_asym_id.compare(b.m_asym_id);
|
||||
// if (d == 0)
|
||||
// d = m_seq_id - b.m_seq_id;
|
||||
// if (d == 0)
|
||||
// d = m_auth_seq_id.compare(b.m_auth_seq_id);
|
||||
// if (d == 0)
|
||||
// d = mAtom_id.compare(b.mAtom_id);
|
||||
int atom::atom_impl::compare(const atom_impl &b) const
|
||||
{
|
||||
int d = get_property("label_asym_id").compare(b.get_property("label_asym_id"));
|
||||
if (d == 0)
|
||||
d = get_property_int("label_seq_id") - b.get_property_int("label_seq_id");
|
||||
if (d == 0)
|
||||
d = get_property_int("auth_seq_id") - b.get_property_int("auth_seq_id");
|
||||
if (d == 0)
|
||||
d = get_property("label_atom_id").compare(b.get_property("label_atom_id"));
|
||||
|
||||
// return d;
|
||||
// }
|
||||
return d;
|
||||
}
|
||||
|
||||
// bool atom::atom_impl::getAnisoU(float anisou[6]) const
|
||||
// {
|
||||
@@ -149,145 +144,6 @@ int atom::atom_impl::get_charge() const
|
||||
return formalCharge.value_or(0);
|
||||
}
|
||||
|
||||
// const Compound *atom::atom_impl::compound() const
|
||||
// {
|
||||
// if (mCompound == nullptr)
|
||||
// {
|
||||
// std::string compID = get_property("label_comp_id");
|
||||
|
||||
// mCompound = compound_factory::instance().create(compID);
|
||||
// }
|
||||
|
||||
// return mCompound;
|
||||
// }
|
||||
|
||||
// const std::string atom::atom_impl::get_property(const std::string_view name) const
|
||||
// {
|
||||
// for (auto &&[item_name, ref] : mCachedRefs)
|
||||
// {
|
||||
// if (item_name == name)
|
||||
// return ref.as<std::string>();
|
||||
// }
|
||||
|
||||
// mCachedRefs.emplace_back(name, const_cast<Row &>(mRow)[name]);
|
||||
// return std::get<1>(mCachedRefs.back()).as<std::string>();
|
||||
// }
|
||||
|
||||
// void atom::atom_impl::set_property(const std::string_view name, const std::string &value)
|
||||
// {
|
||||
// for (auto &&[item_name, ref] : mCachedRefs)
|
||||
// {
|
||||
// if (item_name != name)
|
||||
// continue;
|
||||
|
||||
// ref = value;
|
||||
// return;
|
||||
// }
|
||||
|
||||
// mCachedRefs.emplace_back(name, mRow[name]);
|
||||
// std::get<1>(mCachedRefs.back()) = value;
|
||||
// }
|
||||
|
||||
// const Row atom::getRowAniso() const
|
||||
// {
|
||||
// auto &db = m_impl->m_db;
|
||||
// auto cat = db.get("atom_site_anisotrop");
|
||||
// if (not cat)
|
||||
// return {};
|
||||
// else
|
||||
// return cat->find1(key("id") == m_impl->m_id);
|
||||
// }
|
||||
|
||||
// float atom::uIso() const
|
||||
// {
|
||||
// float result;
|
||||
|
||||
// if (not get_property<std::string>("U_iso_or_equiv").empty())
|
||||
// result = get_property<float>("U_iso_or_equiv");
|
||||
// else if (not get_property<std::string>("B_iso_or_equiv").empty())
|
||||
// result = get_property<float>("B_iso_or_equiv") / static_cast<float>(8 * kPI * kPI);
|
||||
// else
|
||||
// throw std::runtime_error("Missing B_iso or U_iso");
|
||||
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// const Compound &atom::compound() const
|
||||
// {
|
||||
// auto result = impl().compound();
|
||||
|
||||
// if (result == nullptr)
|
||||
// {
|
||||
// if (VERBOSE > 0)
|
||||
// std::cerr << "Compound not found: '" << get_property<std::string>("label_comp_id") << '\'' << '\n';
|
||||
|
||||
// throw std::runtime_error("no compound");
|
||||
// }
|
||||
|
||||
// return *result;
|
||||
// }
|
||||
|
||||
// std::string atom::labelEntityID() const
|
||||
// {
|
||||
// return get_property<std::string>("label_entity_id");
|
||||
// }
|
||||
|
||||
// std::string atom::authAtom_id() const
|
||||
// {
|
||||
// return get_property<std::string>("auth_atom_id");
|
||||
// }
|
||||
|
||||
// std::string atom::authCompID() const
|
||||
// {
|
||||
// return get_property<std::string>("auth_comp_id");
|
||||
// }
|
||||
|
||||
// std::string atom::get_auth_asym_id() const
|
||||
// {
|
||||
// return get_property<std::string>("auth_asym_id");
|
||||
// }
|
||||
|
||||
// std::string atom::get_pdb_ins_code() const
|
||||
// {
|
||||
// return get_property<std::string>("pdbx_PDB_ins_code");
|
||||
// }
|
||||
|
||||
// std::string atom::pdbxAuthAltID() const
|
||||
// {
|
||||
// return get_property<std::string>("pdbx_auth_alt_id");
|
||||
// }
|
||||
|
||||
// void atom::translate(point t)
|
||||
// {
|
||||
// auto loc = location();
|
||||
// loc += t;
|
||||
// location(loc);
|
||||
// }
|
||||
|
||||
// void atom::rotate(quaternion q)
|
||||
// {
|
||||
// auto loc = location();
|
||||
// loc.rotate(q);
|
||||
// location(loc);
|
||||
// }
|
||||
|
||||
// void atom::translate_and_rotate(point t, quaternion q)
|
||||
// {
|
||||
// auto loc = location();
|
||||
// loc += t;
|
||||
// loc.rotate(q);
|
||||
// location(loc);
|
||||
// }
|
||||
|
||||
// void atom::translate_rotate_and_translate(point t1, quaternion q, point t2)
|
||||
// {
|
||||
// auto loc = location();
|
||||
// loc += t1;
|
||||
// loc.rotate(q);
|
||||
// loc += t2;
|
||||
// location(loc);
|
||||
// }
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const atom &atom)
|
||||
{
|
||||
if (atom.is_water())
|
||||
@@ -319,8 +175,8 @@ residue::residue(structure &structure, const std::vector<atom> &atoms)
|
||||
m_compound_id = a.get_label_comp_id();
|
||||
m_asym_id = a.get_label_asym_id();
|
||||
m_seq_id = a.get_label_seq_id();
|
||||
m_auth_asym_id = a.get_auth_asym_id();
|
||||
m_auth_seq_id = a.get_auth_seq_id();
|
||||
m_pdb_strand_id = a.get_auth_asym_id();
|
||||
m_pdb_seq_num = a.get_auth_seq_id();
|
||||
m_pdb_ins_code = a.get_pdb_ins_code();
|
||||
|
||||
for (auto atom : atoms)
|
||||
@@ -371,10 +227,10 @@ atom residue::create_new_atom(atom_type inType, const std::string &inAtomID, poi
|
||||
{ "label_alt_id", "." },
|
||||
{ "label_comp_id", m_compound_id },
|
||||
{ "label_seq_id", m_seq_id },
|
||||
{ "auth_asym_id", m_auth_asym_id },
|
||||
{ "auth_asym_id", m_pdb_strand_id },
|
||||
{ "auth_atom_id", inAtomID },
|
||||
{ "auth_comp_id", m_compound_id },
|
||||
{ "auth_seq_id", m_auth_seq_id },
|
||||
{ "auth_seq_id", m_pdb_seq_num },
|
||||
{ "occupancy", 1.0f, 2 },
|
||||
{ "B_iso_or_equiv", 20.0f },
|
||||
{ "pdbx_PDB_model_num", m_structure->get_model_nr() },
|
||||
@@ -541,8 +397,8 @@ std::ostream &operator<<(std::ostream &os, const residue &res)
|
||||
{
|
||||
os << res.get_compound_id() << ' ' << res.get_asym_id() << ':' << res.get_seq_id();
|
||||
|
||||
if (res.get_auth_asym_id() != res.get_asym_id() or res.get_auth_seq_id() != std::to_string(res.get_seq_id()))
|
||||
os << " [" << res.get_auth_asym_id() << ':' << res.get_auth_seq_id() << ']';
|
||||
if (res.get_pdb_strand_id() != res.get_asym_id() or res.get_pdb_seq_num() != std::to_string(res.get_seq_id()))
|
||||
os << " [" << res.get_pdb_strand_id() << ':' << res.get_pdb_seq_num() << ']';
|
||||
|
||||
return os;
|
||||
}
|
||||
@@ -551,7 +407,7 @@ std::ostream &operator<<(std::ostream &os, const residue &res)
|
||||
// monomer
|
||||
|
||||
monomer::monomer(const polymer &polymer, std::size_t index, int seqID, const std::string &authSeqID, const std::string &pdbInsCode, const std::string &compoundID)
|
||||
: residue(*polymer.get_structure(), compoundID, polymer.get_asym_id(), seqID, polymer.get_auth_asym_id(), authSeqID, pdbInsCode)
|
||||
: residue(*polymer.get_structure(), compoundID, polymer.get_asym_id(), seqID, polymer.get_pdb_strand_id(), authSeqID, pdbInsCode)
|
||||
, m_polymer(&polymer)
|
||||
, m_index(index)
|
||||
{
|
||||
@@ -970,7 +826,7 @@ polymer::polymer(structure &s, const std::string &entityID, const std::string &a
|
||||
: m_structure(const_cast<structure *>(&s))
|
||||
, m_entity_id(entityID)
|
||||
, m_asym_id(asym_id)
|
||||
, m_auth_asym_id(auth_asym_id)
|
||||
, m_pdb_strand_id(auth_asym_id)
|
||||
{
|
||||
using namespace cif::literals;
|
||||
|
||||
@@ -982,12 +838,8 @@ polymer::polymer(structure &s, const std::string &entityID, const std::string &a
|
||||
for (auto r : poly_seq_scheme.find("asym_id"_key == asym_id))
|
||||
{
|
||||
int seqID;
|
||||
std::optional<int> pdbSeqNum;
|
||||
std::string compoundID, authSeqID, pdbInsCode;
|
||||
cif::tie(seqID, authSeqID, compoundID, pdbInsCode, pdbSeqNum) = r.get("seq_id", "auth_seq_num", "mon_id", "pdb_ins_code", "pdb_seq_num");
|
||||
|
||||
if (authSeqID.empty() and pdbSeqNum.has_value())
|
||||
authSeqID = std::to_string(*pdbSeqNum);
|
||||
std::string compoundID, pdbSeqNum, pdbInsCode;
|
||||
cif::tie(seqID, pdbSeqNum, compoundID, pdbInsCode) = r.get("seq_id", "pdb_seq_num", "mon_id", "pdb_ins_code");
|
||||
|
||||
std::size_t index = size();
|
||||
|
||||
@@ -995,11 +847,11 @@ polymer::polymer(structure &s, const std::string &entityID, const std::string &a
|
||||
if (not ix.count(seqID))
|
||||
{
|
||||
ix[seqID] = index;
|
||||
emplace_back(*this, index, seqID, authSeqID, pdbInsCode, compoundID);
|
||||
emplace_back(*this, index, seqID, pdbSeqNum, pdbInsCode, compoundID);
|
||||
}
|
||||
else if (VERBOSE > 0)
|
||||
{
|
||||
monomer m{ *this, index, seqID, authSeqID, pdbInsCode, compoundID };
|
||||
monomer m{ *this, index, seqID, pdbSeqNum, pdbInsCode, compoundID };
|
||||
std::cerr << "Dropping alternate residue " << m << '\n';
|
||||
}
|
||||
}
|
||||
@@ -1139,7 +991,7 @@ cif::mm::atom sugar::add_atom(row_initializer atom_info)
|
||||
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({ "auth_seq_id", m_pdb_seq_num });
|
||||
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 });
|
||||
@@ -1255,12 +1107,12 @@ sugar &branch::construct_sugar(const std::string &compound_id)
|
||||
{ "mon_id", result.get_compound_id() },
|
||||
|
||||
{ "pdb_asym_id", result.get_asym_id() },
|
||||
{ "pdb_seq_num", result.num() },
|
||||
{ "pdb_seq_num", result.get_pdb_seq_num() },
|
||||
{ "pdb_mon_id", result.get_compound_id() },
|
||||
|
||||
{ "auth_asym_id", result.get_auth_asym_id() },
|
||||
{ "auth_asym_id", result.get_pdb_strand_id() },
|
||||
{ "auth_mon_id", result.get_compound_id() },
|
||||
{ "auth_seq_num", result.get_auth_seq_id() },
|
||||
{ "auth_seq_num", result.get_pdb_seq_num() },
|
||||
|
||||
{ "hetero", "n" } });
|
||||
|
||||
@@ -1303,7 +1155,7 @@ std::string branch::name(const sugar &s) const
|
||||
|
||||
for (auto &sn : *this)
|
||||
{
|
||||
if (not sn.get_link() or sn.get_link().get_auth_seq_id() != s.get_auth_seq_id())
|
||||
if (not sn.get_link() or sn.get_link().get_auth_seq_id() != s.get_pdb_seq_num())
|
||||
continue;
|
||||
|
||||
auto n = name(sn) + "-(1-" + sn.get_link().get_label_atom_id().substr(1) + ')';
|
||||
@@ -1330,19 +1182,19 @@ float branch::weight() const
|
||||
// --------------------------------------------------------------------
|
||||
// structure
|
||||
|
||||
structure::structure(file &p, std::size_t modelNr, StructureOpenOptions options)
|
||||
structure::structure(file &p, std::size_t modelNr, structure_open_options options)
|
||||
: structure(p.front(), modelNr, options)
|
||||
{
|
||||
}
|
||||
|
||||
structure::structure(datablock &db, std::size_t modelNr, StructureOpenOptions options)
|
||||
structure::structure(datablock &db, std::size_t modelNr, structure_open_options options)
|
||||
: m_db(db)
|
||||
, m_model_nr(modelNr)
|
||||
{
|
||||
if (db.get_validator() == nullptr)
|
||||
db.load_dictionary();
|
||||
|
||||
auto &atomCat = db["atom_site"];
|
||||
auto &atom_site = db["atom_site"];
|
||||
|
||||
load_atoms_for_model(options);
|
||||
|
||||
@@ -1350,7 +1202,7 @@ structure::structure(datablock &db, std::size_t modelNr, StructureOpenOptions op
|
||||
if (m_atoms.empty() and m_model_nr == 1)
|
||||
{
|
||||
std::optional<std::size_t> model_nr;
|
||||
cif::tie(model_nr) = atomCat.front().get("pdbx_PDB_model_num");
|
||||
cif::tie(model_nr) = atom_site.front().get("pdbx_PDB_model_num");
|
||||
if (model_nr and *model_nr != m_model_nr)
|
||||
{
|
||||
if (VERBOSE > 0)
|
||||
@@ -1369,42 +1221,133 @@ structure::structure(datablock &db, std::size_t modelNr, StructureOpenOptions op
|
||||
load_data();
|
||||
}
|
||||
|
||||
void structure::load_atoms_for_model(StructureOpenOptions options)
|
||||
void structure::load_atoms_for_model(structure_open_options options)
|
||||
{
|
||||
using namespace literals;
|
||||
|
||||
auto &atomCat = m_db["atom_site"];
|
||||
auto &atom_site = m_db["atom_site"];
|
||||
|
||||
condition c = "pdbx_PDB_model_num"_key == null or "pdbx_PDB_model_num"_key == m_model_nr;
|
||||
if (options bitand StructureOpenOptions::SkipHydrogen)
|
||||
c = std::move(c) and ("type_symbol"_key != "H" and "type_symbol"_key != "D");
|
||||
|
||||
for (auto id : atomCat.find<std::string>(std::move(c), "id"))
|
||||
emplace_atom(std::make_shared<atom::atom_impl>(m_db, id));
|
||||
if (options.skip_hydrogen)
|
||||
c = std::move(c) and (cif::key("type_symbol") != "H" and cif::key("type_symbol") != "D");
|
||||
|
||||
if (options.skip_water)
|
||||
c = std::move(c) and (cif::key("auth_comp_id") != "HOH" and cif::key("auth_comp_id") != "H20" and cif::key("auth_comp_id") != "WAT");
|
||||
|
||||
if (options.skip_hetatom)
|
||||
{
|
||||
if (options.skip_water)
|
||||
c = std::move(c) and cif::key("group_PDB") != "HETATM";
|
||||
else
|
||||
c = std::move(c) and (cif::key("group_PDB") != "HETATM" or (cif::key("auth_comp_id") == "HOH" or cif::key("auth_comp_id") == "H20" or cif::key("auth_comp_id") == "WAT"));
|
||||
}
|
||||
|
||||
if (options.min_b_factor.has_value())
|
||||
c = std::move(c) and cif::key("B_iso_or_equiv") >= *options.min_b_factor;
|
||||
|
||||
if (options.max_b_factor.has_value())
|
||||
c = std::move(c) and cif::key("B_iso_or_equiv") <= *options.max_b_factor;
|
||||
|
||||
if (not options.asyms.empty())
|
||||
{
|
||||
condition tmp_c;
|
||||
for (auto asym_id : options.asyms)
|
||||
tmp_c = std::move(tmp_c) or cif::key("label_asym_id") == asym_id;
|
||||
c = std::move(c) and std::move(tmp_c);
|
||||
}
|
||||
|
||||
if (options.occupancy_mode == occupancy_policy::ALL)
|
||||
{
|
||||
for (auto id : atom_site.find<std::string>(std::move(c), "id"))
|
||||
emplace_atom(std::make_shared<atom::atom_impl>(m_db, id));
|
||||
}
|
||||
else if (options.occupancy_mode == occupancy_policy::UNOCCUPIED)
|
||||
{
|
||||
for (auto id : atom_site.find<std::string>(std::move(c), "id"))
|
||||
{
|
||||
auto a = std::make_shared<atom::atom_impl>(m_db, id);
|
||||
if (a->get_property_float("occupancy") > 0)
|
||||
continue;
|
||||
emplace_atom(a);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<cif::mm::atom> atoms;
|
||||
std::map<std::tuple<std::string,int>, std::map<std::string, float>> alts;
|
||||
|
||||
for (auto id : atom_site.find<std::string>(std::move(c), "id"))
|
||||
{
|
||||
auto a = atoms.emplace_back(std::make_shared<atom::atom_impl>(m_db, id));
|
||||
|
||||
if (a.is_alternate())
|
||||
{
|
||||
auto key = std::make_tuple(a.get_label_asym_id(), a.get_label_seq_id());
|
||||
auto alt_id = a.get_label_alt_id();
|
||||
|
||||
if (auto i = alts.find(key); i != alts.end())
|
||||
i->second[alt_id] += a.get_occupancy();
|
||||
else
|
||||
alts[key][alt_id] = a.get_occupancy();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &&[key, value] : alts)
|
||||
{
|
||||
// const auto &[asym_id, seq_id] = key;
|
||||
|
||||
// select highest occupancy for this residue's alternates
|
||||
std::string alt_id;
|
||||
float occupancy = options.occupancy_mode == occupancy_policy::MAX ? 0.f : std::numeric_limits<float>::max();
|
||||
for (const auto &[alt_key, alt_value] : value)
|
||||
{
|
||||
if (options.occupancy_mode == occupancy_policy::MAX)
|
||||
{
|
||||
if (occupancy < alt_value)
|
||||
{
|
||||
alt_id = alt_key;
|
||||
occupancy = alt_value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (occupancy > alt_value)
|
||||
{
|
||||
alt_id = alt_key;
|
||||
occupancy = alt_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value.clear();
|
||||
value.emplace(alt_id, occupancy);
|
||||
}
|
||||
|
||||
for (auto a : atoms)
|
||||
{
|
||||
if (a.is_alternate())
|
||||
{
|
||||
auto key = std::make_tuple(a.get_label_asym_id(), a.get_label_seq_id());
|
||||
|
||||
if (alts[key].contains(a.get_label_alt_id()))
|
||||
emplace_atom(a);
|
||||
}
|
||||
else
|
||||
emplace_atom(a);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// structure::structure(const structure &s)
|
||||
// : m_db(s.m_db)
|
||||
// , m_model_nr(s.m_model_nr)
|
||||
// {
|
||||
// m_atoms.reserve(s.m_atoms.size());
|
||||
// for (auto &atom : s.m_atoms)
|
||||
// emplace_atom(atom.clone());
|
||||
|
||||
// load_data();
|
||||
// }
|
||||
|
||||
// structure::~structure()
|
||||
// {
|
||||
// }
|
||||
|
||||
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"))
|
||||
{
|
||||
if (m_polymers.empty() or m_polymers.back().get_asym_id() != asym_id or m_polymers.back().get_entity_id() != entityID)
|
||||
if (m_polymers.empty() or m_polymers.back().get_asym_id() != asym_id)
|
||||
m_polymers.emplace_back(*this, entityID, asym_id, auth_asym_id);
|
||||
}
|
||||
|
||||
@@ -1430,18 +1373,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_pdb_seq_num() }] = &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_pdb_seq_num() }] = &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_pdb_seq_num() }] = &sugar;
|
||||
sugars.insert(sugar.get_compound_id());
|
||||
}
|
||||
}
|
||||
@@ -1516,30 +1459,6 @@ EntityType structure::get_entity_type_for_asym_id(const std::string asym_id) con
|
||||
return get_entity_type_for_entity_id(entityID);
|
||||
}
|
||||
|
||||
// std::vector<atom> structure::waters() const
|
||||
// {
|
||||
// using namespace literals;
|
||||
|
||||
// std::vector<atom> result;
|
||||
|
||||
// auto &db = datablock();
|
||||
|
||||
// // Get the entity id for water. Watch out, structure may not have water at all
|
||||
// auto &entityCat = db["entity"];
|
||||
// for (const auto &[waterEntityID] : entityCat.find<std::string>("type"_key == "water", "id"))
|
||||
// {
|
||||
// for (auto &a : m_atoms)
|
||||
// {
|
||||
// if (a.get_property("label_entity_id") == waterEntityID)
|
||||
// result.push_back(a);
|
||||
// }
|
||||
|
||||
// break;
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// }
|
||||
|
||||
bool structure::has_atom_id(const std::string &id) const
|
||||
{
|
||||
assert(m_atoms.size() == m_atom_index.size());
|
||||
@@ -1688,7 +1607,7 @@ residue &structure::get_residue(const std::string &asym_id, int seqID, const std
|
||||
{
|
||||
for (auto &res : m_non_polymers)
|
||||
{
|
||||
if (res.get_asym_id() == asym_id and (authSeqID.empty() or res.get_auth_seq_id() == authSeqID))
|
||||
if (res.get_asym_id() == asym_id and (authSeqID.empty() or res.get_pdb_seq_num() == authSeqID))
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -1712,7 +1631,7 @@ residue &structure::get_residue(const std::string &asym_id, int seqID, const std
|
||||
|
||||
for (auto &sugar : branch)
|
||||
{
|
||||
if (sugar.get_asym_id() == asym_id and sugar.get_auth_seq_id() == authSeqID)
|
||||
if (sugar.get_asym_id() == asym_id and sugar.get_pdb_seq_num() == authSeqID)
|
||||
return sugar;
|
||||
}
|
||||
}
|
||||
@@ -1734,7 +1653,7 @@ residue &structure::get_residue(const std::string &asym_id, const std::string &c
|
||||
{
|
||||
for (auto &res : m_non_polymers)
|
||||
{
|
||||
if (res.get_asym_id() == asym_id and res.get_auth_seq_id() == authSeqID and res.get_compound_id() == compID)
|
||||
if (res.get_asym_id() == asym_id and res.get_pdb_seq_num() == authSeqID and res.get_compound_id() == compID)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -1758,7 +1677,7 @@ residue &structure::get_residue(const std::string &asym_id, const std::string &c
|
||||
|
||||
for (auto &sugar : branch)
|
||||
{
|
||||
if (sugar.get_asym_id() == asym_id and sugar.get_auth_seq_id() == authSeqID and sugar.get_compound_id() == compID)
|
||||
if (sugar.get_asym_id() == asym_id and sugar.get_pdb_seq_num() == authSeqID and sugar.get_compound_id() == compID)
|
||||
return sugar;
|
||||
}
|
||||
}
|
||||
@@ -2108,7 +2027,7 @@ void structure::remove_residue(const std::string &asym_id, int seq_id, const std
|
||||
{
|
||||
for (auto &res : m_non_polymers)
|
||||
{
|
||||
if (res.get_asym_id() == asym_id and (auth_seq_id.empty() or res.get_auth_seq_id() == auth_seq_id))
|
||||
if (res.get_asym_id() == asym_id and (auth_seq_id.empty() or res.get_pdb_seq_num() == auth_seq_id))
|
||||
{
|
||||
remove_residue(res);
|
||||
return;
|
||||
@@ -2138,7 +2057,7 @@ void structure::remove_residue(const std::string &asym_id, int seq_id, const std
|
||||
|
||||
for (auto &sugar : branch)
|
||||
{
|
||||
if (sugar.get_asym_id() == asym_id and sugar.get_auth_seq_id() == auth_seq_id)
|
||||
if (sugar.get_asym_id() == asym_id and sugar.get_pdb_seq_num() == auth_seq_id)
|
||||
{
|
||||
remove_residue(sugar);
|
||||
return;
|
||||
@@ -2271,7 +2190,7 @@ void structure::remove_sugar(sugar &s)
|
||||
// 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_seq_num", sugar.get_pdb_seq_num() },
|
||||
|
||||
{ "hetero", "n" } });
|
||||
}
|
||||
@@ -2357,8 +2276,8 @@ std::string structure::create_non_poly(const std::string &entity_id, const std::
|
||||
{ "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_seq_num", res.get_pdb_seq_num() },
|
||||
{ "auth_seq_num", res.get_pdb_seq_num() },
|
||||
{ "pdb_mon_id", comp_id },
|
||||
{ "auth_mon_id", comp_id },
|
||||
{ "pdb_strand_id", asym_id },
|
||||
@@ -2418,8 +2337,8 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector
|
||||
{ "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_seq_num", res.get_pdb_seq_num() },
|
||||
{ "auth_seq_num", res.get_pdb_seq_num() },
|
||||
{ "pdb_mon_id", comp_id },
|
||||
{ "auth_mon_id", comp_id },
|
||||
{ "pdb_strand_id", asym_id },
|
||||
@@ -2733,11 +2652,11 @@ std::string structure::create_entity_for_branch(branch &branch)
|
||||
|
||||
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() },
|
||||
{ "entity_branch_list_num_1", s1.get_pdb_seq_num() },
|
||||
{ "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() },
|
||||
{ "entity_branch_list_num_2", s2.get_pdb_seq_num() },
|
||||
{ "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() },
|
||||
@@ -2950,7 +2869,7 @@ static int compare_numbers(std::string_view a, std::string_view b)
|
||||
|
||||
int compare_cif_id(const std::string &a, const std::string &b)
|
||||
{
|
||||
int d = a.length() - b.length();
|
||||
int d = static_cast<int>(a.length() - b.length());
|
||||
if (d == 0)
|
||||
d = a.compare(b);
|
||||
return d;
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include <regex>
|
||||
#include <set>
|
||||
|
||||
|
||||
namespace cif::pdb
|
||||
{
|
||||
|
||||
@@ -58,9 +57,9 @@ std::string cif2pdbDate(const std::string &d)
|
||||
int month = std::stoi(m[2].str());
|
||||
|
||||
if (m[3].matched)
|
||||
result = cif::format("%02.2d-%3.3s-%02.2d", stoi(m[3].str()), kMonths[month - 1], (year % 100)).str();
|
||||
result = cif::format("{:02}-{:3.3}-{:02}", stoi(m[3].str()), kMonths[month - 1], (year % 100));
|
||||
else
|
||||
result = cif::format("%3.3s-%02.2d", kMonths[month - 1], (year % 100)).str();
|
||||
result = cif::format("{:3.3}-{:02}", kMonths[month - 1], (year % 100));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -258,16 +257,14 @@ std::size_t WriteCitation(std::ostream &pdbFile, const datablock &db, row_handle
|
||||
{
|
||||
to_upper(pubname);
|
||||
|
||||
const std::string kRefHeader = s1 + "REF %2.2s %-28.28s %2.2s%4.4s %5.5s %4.4s";
|
||||
pdbFile << cif::format(kRefHeader, "" /* continuation */, pubname, (volume.empty() ? "" : "V."), volume, pageFirst, year)
|
||||
pdbFile << s1 << cif::format("REF {:2.2s} {:<28.28s} {:2.2s}{:>4.4s} {:>5.5s} {:4.4s}", "" /* continuation */, pubname, (volume.empty() ? "" : "V."), volume, pageFirst, year)
|
||||
<< '\n';
|
||||
++result;
|
||||
}
|
||||
|
||||
if (not issn.empty())
|
||||
{
|
||||
const std::string kRefHeader = s1 + "REFN ISSN %-25.25s";
|
||||
pdbFile << cif::format(kRefHeader, issn) << '\n';
|
||||
pdbFile << s1 << cif::format("REFN ISSN {:<25.25s}", issn) << '\n';
|
||||
++result;
|
||||
}
|
||||
|
||||
@@ -276,27 +273,25 @@ std::size_t WriteCitation(std::ostream &pdbFile, const datablock &db, row_handle
|
||||
//// 0 1 2 3 4 5 6 7 8
|
||||
//// HEADER xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxDDDDDDDDD IIII
|
||||
// const char kRefHeader[] =
|
||||
// "REMARK 1 REFN %4.4s %-6.6s %2.2s %-25.25s";
|
||||
// "REMARK 1 REFN {:4.4s} {:<6.6s} {:2.2s} {:<25.25s}";
|
||||
//
|
||||
// pdbFile << (boost::cif::format(kRefHeader)
|
||||
// % (astm.empty() ? "" : "ASTN")
|
||||
// % astm
|
||||
// % country
|
||||
// % issn).str()
|
||||
// % issn)
|
||||
// << '\n';
|
||||
// }
|
||||
|
||||
if (not pmid.empty())
|
||||
{
|
||||
const std::string kPMID = s1 + "PMID %-60.60s ";
|
||||
pdbFile << cif::format(kPMID, pmid) << '\n';
|
||||
pdbFile << s1 << cif::format("PMID {:<60.60s} ", pmid) << '\n';
|
||||
++result;
|
||||
}
|
||||
|
||||
if (not doi.empty())
|
||||
{
|
||||
const std::string kDOI = s1 + "DOI %-60.60s ";
|
||||
pdbFile << cif::format(kDOI, doi) << '\n';
|
||||
pdbFile << s1 << cif::format("DOI {:<60.60s} ", doi) << '\n';
|
||||
++result;
|
||||
}
|
||||
|
||||
@@ -307,10 +302,10 @@ void write_header_lines(std::ostream &pdbFile, const datablock &db)
|
||||
{
|
||||
// 0 1 2 3 4 5 6 7 8
|
||||
// HEADER xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxDDDDDDDDD IIII
|
||||
const char kHeader[] =
|
||||
"HEADER %-40.40s"
|
||||
"%-9.9s"
|
||||
" %-4.4s";
|
||||
// const char kHeader[] =
|
||||
// "HEADER {:<40.40s}"
|
||||
// "{:<9.9s}"
|
||||
// " {:<4.4s}";
|
||||
|
||||
// HEADER
|
||||
|
||||
@@ -345,7 +340,12 @@ void write_header_lines(std::ostream &pdbFile, const datablock &db)
|
||||
}
|
||||
}
|
||||
|
||||
pdbFile << cif::format(kHeader, keywords, date, db.name()) << '\n';
|
||||
pdbFile << cif::format(/* kHeader */
|
||||
"HEADER {:<40.40s}"
|
||||
"{:<9.9s}"
|
||||
" {:<4.4s}"
|
||||
|
||||
, keywords, date, db.name()) << '\n';
|
||||
|
||||
// TODO: implement
|
||||
// OBSLTE (skip for now)
|
||||
@@ -535,7 +535,6 @@ void WriteTitle(std::ostream &pdbFile, const datablock &db)
|
||||
write_header_lines(pdbFile, db);
|
||||
|
||||
// REVDAT
|
||||
const char kRevDatFmt[] = "REVDAT %3d%2.2s %9.9s %4.4s %1d ";
|
||||
auto &cat2 = db["database_PDB_rev"];
|
||||
std::vector<row_handle> rev(cat2.begin(), cat2.end());
|
||||
sort(rev.begin(), rev.end(), [](row_handle a, row_handle b) -> bool
|
||||
@@ -559,9 +558,9 @@ void WriteTitle(std::ostream &pdbFile, const datablock &db)
|
||||
{
|
||||
std::string cs = ++continuation > 1 ? std::to_string(continuation) : std::string();
|
||||
|
||||
pdbFile << cif::format(kRevDatFmt, revNum, cs, date, db.name(), modType);
|
||||
pdbFile << cif::format("REVDAT {:3}{:2.2s} {:9.9s} {:4.4s} {:1} ", revNum, cs, date, db.name(), modType);
|
||||
for (std::size_t i = 0; i < 4; ++i)
|
||||
pdbFile << cif::format(" %-6.6s", (i < types.size() ? types[i] : std::string()));
|
||||
pdbFile << cif::format(" {:<6.6s}", (i < types.size() ? types[i] : std::string()));
|
||||
pdbFile << '\n';
|
||||
|
||||
if (types.size() > 4)
|
||||
@@ -614,7 +613,7 @@ void WriteRemark2(std::ostream &pdbFile, const datablock &db)
|
||||
{
|
||||
float resHigh = refine.front()["ls_d_res_high"].as<float>();
|
||||
pdbFile << "REMARK 2\n"
|
||||
<< cif::format("REMARK 2 RESOLUTION. %7.2f ANGSTROMS.", resHigh) << '\n';
|
||||
<< cif::format("REMARK 2 RESOLUTION. {:7.2f} ANGSTROMS.", resHigh) << '\n';
|
||||
}
|
||||
catch (...)
|
||||
{ /* skip it */
|
||||
@@ -761,10 +760,7 @@ class Fs : public FBase
|
||||
else
|
||||
{
|
||||
os << '\n';
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "REMARK " << std::setw(3) << std::right << mNr << ' ';
|
||||
WriteOneContinuedLine(os, ss.str(), 0, s);
|
||||
WriteOneContinuedLine(os, cif::format("REMARK {:3} ", mNr), 0, s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1617,7 +1613,7 @@ void WriteRemark3Phenix(std::ostream &pdbFile, const datablock &db)
|
||||
|
||||
percent_reflns_obs /= 100;
|
||||
|
||||
pdbFile << RM3(" ") << cif::format("%3d %7.4f - %7.4f %4.2f %8d %5d %6.4f %6.4f", bin++, d_res_low, d_res_high, percent_reflns_obs, number_reflns_R_work, number_reflns_R_free, R_factor_R_work, R_factor_R_free) << '\n';
|
||||
pdbFile << RM3(" ") << cif::format("{:3} {:7.4f} - {:7.4f} {:4.2f} {:8} {:5} {:6.4f} {:6.4f}", bin++, d_res_low, d_res_high, percent_reflns_obs, number_reflns_R_work, number_reflns_R_free, R_factor_R_work, R_factor_R_free) << '\n';
|
||||
}
|
||||
|
||||
pdbFile << RM3("") << '\n'
|
||||
@@ -2585,7 +2581,7 @@ void WriteRemark465(std::ostream &pdbFile, const datablock &db)
|
||||
cif::tie(modelNr, resName, chainID, iCode, seqNr) =
|
||||
r.get("PDB_model_num", "auth_comp_id", "auth_asym_id", "PDB_ins_code", "auth_seq_id");
|
||||
|
||||
pdbFile << cif::format("REMARK 465 %3.3s %3.3s %1.1s %5d%1.1s", modelNr, resName, chainID, seqNr, iCode) << '\n';
|
||||
pdbFile << cif::format("REMARK 465 {:3.3s} {:3.3s} {:1.1s} {:5}{:1.1s}", modelNr, resName, chainID, seqNr, iCode) << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2632,7 +2628,7 @@ void WriteRemark470(std::ostream &pdbFile, const datablock &db)
|
||||
|
||||
while (not a.second.empty())
|
||||
{
|
||||
pdbFile << cif::format("REMARK 470 %3.3s %3.3s %1.1s%4d%1.1s ", modelNr, resName, chainID, seqNr, iCode) << " ";
|
||||
pdbFile << cif::format("REMARK 470 {:>3.3s} {:3.3s} {:1.1s}{:4}{:1.1s} ", modelNr, resName, chainID, seqNr, iCode) << " ";
|
||||
|
||||
for (std::size_t i = 0; i < 6 and not a.second.empty(); ++i)
|
||||
{
|
||||
@@ -2730,16 +2726,16 @@ int WritePrimaryStructure(std::ostream &pdbFile, const datablock &db)
|
||||
|
||||
if (dbAccession.length() > 8 or db_code.length() > 12 or atoi(dbseqEnd.c_str()) >= 100000)
|
||||
pdbFile << cif::format(
|
||||
"DBREF1 %4.4s %1.1s %4.4s%1.1s %4.4s%1.1s %-6.6s %-20.20s",
|
||||
"DBREF1 {:>4.4s} {:1.1s} {:>4.4s}{:1.1s} {:>4.4s}{:1.1s} {:<6.6s} {:<20.20s}",
|
||||
idCode, chainID, seqBegin, insertBegin, seqEnd, insertEnd, db_name, db_code)
|
||||
<< '\n'
|
||||
<< cif::format(
|
||||
"DBREF2 %4.4s %1.1s %-22.22s %10.10s %10.10s",
|
||||
"DBREF2 {:>4.4s} {:1.1s} {:<22.22s} {:10.10s} {:10.10s}",
|
||||
idCode, chainID, dbAccession, dbseqBegin, dbseqEnd)
|
||||
<< '\n';
|
||||
else
|
||||
pdbFile << cif::format(
|
||||
"DBREF %4.4s %1.1s %4.4s%1.1s %4.4s%1.1s %-6.6s %-8.8s %-12.12s %5.5s%1.1s %5.5s%1.1s",
|
||||
"DBREF {:>4.4s} {:1.1s} {:>4.4s}{:1.1s} {:>4.4s}{:1.1s} {:<6.6s} {:<8.8s} {:<12.12s} {:>5.5s}{:1.1s} {:>5.5s}{:1.1s}",
|
||||
idCode, chainID, seqBegin, insertBegin, seqEnd, insertEnd, db_name, dbAccession, db_code, dbseqBegin, dbinsBeg, dbseqEnd, dbinsEnd)
|
||||
<< '\n';
|
||||
}
|
||||
@@ -2758,9 +2754,8 @@ int WritePrimaryStructure(std::ostream &pdbFile, const datablock &db)
|
||||
to_upper(conflict);
|
||||
|
||||
pdbFile << cif::format(
|
||||
"SEQADV %4.4s %3.3s %1.1s %4.4s%1.1s %-4.4s %-9.9s %3.3s %5.5s %-21.21s",
|
||||
"SEQADV {:4.4s} {:3.3s} {:1.1s} {:>4.4s}{:1.1s} {:<4.4s} {:<9.9s} {:3.3s} {:>5.5s} {:<21.21s}",
|
||||
idCode, resName, chainID, seqNum, iCode, database, dbAccession, dbRes, dbSeq, conflict)
|
||||
.str()
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
@@ -2788,7 +2783,7 @@ int WritePrimaryStructure(std::ostream &pdbFile, const datablock &db)
|
||||
t = 13;
|
||||
|
||||
pdbFile << cif::format(
|
||||
"SEQRES %3d %1.1s %4d %-51.51s ",
|
||||
"SEQRES {:3} {:1.1s} {:4} {:<51.51s} ",
|
||||
n++, std::string{ chainID }, seqresl[chainID], join(seq.begin(), seq.begin() + t, " "))
|
||||
<< '\n';
|
||||
|
||||
@@ -2808,9 +2803,8 @@ int WritePrimaryStructure(std::ostream &pdbFile, const datablock &db)
|
||||
r.get("auth_asym_id", "auth_seq_id", "auth_comp_id", "PDB_ins_code", "parent_comp_id", "details");
|
||||
|
||||
pdbFile << cif::format(
|
||||
"MODRES %4.4s %3.3s %1.1s %4.4s%1.1s %3.3s %-41.41s",
|
||||
"MODRES {:4.4s} {:3.3s} {:1.1s} {:4.4s}{:1.1s} {:3.3s} {:<41.41s}",
|
||||
db.name(), resName, chainID, seqNum, iCode, stdRes, comment)
|
||||
.str()
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
@@ -2925,7 +2919,7 @@ int WriteHeterogen(std::ostream &pdbFile, const datablock &db)
|
||||
{
|
||||
if (h.water)
|
||||
continue;
|
||||
pdbFile << cif::format("HET %3.3s %c%4d%c %5d", h.hetID, h.chainID, h.seqNum, h.iCode, h.numHetAtoms) << '\n';
|
||||
pdbFile << cif::format("HET {:3.3s} {:1c}{:4}{:1c} {:5}", h.hetID, h.chainID, h.seqNum, h.iCode, h.numHetAtoms) << '\n';
|
||||
++numHet;
|
||||
}
|
||||
|
||||
@@ -2940,7 +2934,7 @@ int WriteHeterogen(std::ostream &pdbFile, const datablock &db)
|
||||
|
||||
for (;;)
|
||||
{
|
||||
pdbFile << cif::format("HETNAM %2.2s %3.3s ", (c > 1 ? std::to_string(c) : std::string()), id);
|
||||
pdbFile << cif::format("HETNAM {:2.2s} {:3.3s} ", (c > 1 ? std::to_string(c) : std::string()), id);
|
||||
++c;
|
||||
|
||||
if (name.length() > 55)
|
||||
@@ -3032,7 +3026,7 @@ int WriteHeterogen(std::ostream &pdbFile, const datablock &db)
|
||||
{
|
||||
std::stringstream fs;
|
||||
|
||||
fs << cif::format("FORMUL %2d %3.3s %2.2s%c", componentNr, hetID, (c > 1 ? std::to_string(c) : std::string()), (hetID == water_comp_id ? '*' : ' '));
|
||||
fs << cif::format("FORMUL {:2} {:3.3s} {:2.2s}{:1c}", componentNr, hetID, (c > 1 ? std::to_string(c) : std::string()), (hetID == water_comp_id ? '*' : ' '));
|
||||
++c;
|
||||
|
||||
if (formula.length() > 51)
|
||||
@@ -3099,7 +3093,7 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
|
||||
"pdbx_PDB_helix_class", "pdbx_PDB_helix_length", "beg_auth_seq_id", "end_auth_seq_id");
|
||||
|
||||
++numHelix;
|
||||
pdbFile << cif::format("HELIX %3d %3.3s %3.3s %1.1s %4d%1.1s %3.3s %1.1s %4d%1.1s%2d%-30.30s %5d",
|
||||
pdbFile << cif::format("HELIX {:3} {:>3.3s} {:3.3s} {:1.1s} {:4}{:1.1s} {:3.3s} {:1.1s} {:4}{:1.1s}{:2}{:<30.30s} {:5}",
|
||||
numHelix, pdbx_PDB_helix_id, beg_label_comp_id, beg_auth_asym_id, beg_auth_seq_id, pdbx_beg_PDB_ins_code, end_label_comp_id, end_auth_asym_id, end_auth_seq_id, pdbx_end_PDB_ins_code, pdbx_PDB_helix_class, details, pdbx_PDB_helix_length)
|
||||
<< '\n';
|
||||
}
|
||||
@@ -3136,7 +3130,7 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
|
||||
"pdbx_end_PDB_ins_code", "beg_auth_comp_id", "beg_auth_asym_id", "beg_auth_seq_id",
|
||||
"end_auth_comp_id", "end_auth_asym_id", "end_auth_seq_id");
|
||||
|
||||
pdbFile << cif::format("SHEET %3.3s %3.3s%2d %3.3s %1.1s%4d%1.1s %3.3s %1.1s%4d%1.1s%2d", rangeID1, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, 0) << '\n';
|
||||
pdbFile << cif::format("SHEET {:>3.3s} {:>3.3s}{:2} {:3.3s} {:1.1s}{:4}{:1.1s} {:3.3s} {:1.1s}{:4}{:1.1s}{:2}", rangeID1, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, 0) << '\n';
|
||||
|
||||
first = false;
|
||||
}
|
||||
@@ -3155,7 +3149,7 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
|
||||
|
||||
if (h.empty())
|
||||
{
|
||||
pdbFile << cif::format("SHEET %3.3s %3.3s%2d %3.3s %1.1s%4d%1.1s %3.3s %1.1s%4d%1.1s%2d", rangeID2, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, sense) << '\n';
|
||||
pdbFile << cif::format("SHEET {:>3.3s} {:>3.3s}{:2} {:3.3s} {:1.1s}{:4}{:1.1s} {:3.3s} {:1.1s}{:4}{:1.1s}{:2}", rangeID2, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, sense) << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3168,8 +3162,8 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
|
||||
curAtom = cif2pdbAtomName(curAtom, compID[0], db);
|
||||
prevAtom = cif2pdbAtomName(prevAtom, compID[1], db);
|
||||
|
||||
pdbFile << cif::format("SHEET %3.3s %3.3s%2d %3.3s %1.1s%4d%1.1s %3.3s %1.1s%4d%1.1s%2d "
|
||||
"%-4.4s%3.3s %1.1s%4d%1.1s %-4.4s%3.3s %1.1s%4d%1.1s",
|
||||
pdbFile << cif::format("SHEET {:>3.3s} {:>3.3s}{:2} {:3.3s} {:1.1s}{:4}{:1.1s} {:3.3s} {:1.1s}{:4}{:1.1s}{:2} "
|
||||
"{:<4.4s}{:3.3s} {:1.1s}{:4}{:1.1s} {:<4.4s}{:3.3s} {:1.1s}{:4}{:1.1s}",
|
||||
rangeID2, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, sense, curAtom, curResName, curChainID, curResSeq, curICode, prevAtom, prevResName, prevChainID, prevResSeq, prevICode)
|
||||
<< '\n';
|
||||
}
|
||||
@@ -3207,7 +3201,7 @@ void WriteConnectivity(std::ostream &pdbFile, const datablock &db)
|
||||
sym1 = cif2pdbSymmetry(sym1);
|
||||
sym2 = cif2pdbSymmetry(sym2);
|
||||
|
||||
pdbFile << cif::format("SSBOND %3d CYS %1.1s %4d%1.1s CYS %1.1s %4d%1.1s %6.6s %6.6s %5.2f", nr, chainID1, seqNum1, icode1, chainID2, seqNum2, icode2, sym1, sym2, Length) << '\n';
|
||||
pdbFile << cif::format("SSBOND {:3} CYS {:1.1s} {:4}{:1.1s} CYS {:1.1s} {:4}{:1.1s} {:6.6s} {:6.6s} {:5.2f}", nr, chainID1, seqNum1, icode1, chainID2, seqNum2, icode2, sym1, sym2, Length) << '\n';
|
||||
|
||||
++nr;
|
||||
}
|
||||
@@ -3234,10 +3228,10 @@ void WriteConnectivity(std::ostream &pdbFile, const datablock &db)
|
||||
sym1 = cif2pdbSymmetry(sym1);
|
||||
sym2 = cif2pdbSymmetry(sym2);
|
||||
|
||||
pdbFile << cif::format("LINK %-4.4s%1.1s%3.3s %1.1s%4d%1.1s %-4.4s%1.1s%3.3s %1.1s%4d%1.1s %6.6s %6.6s", name1, altLoc1, resName1, chainID1, resSeq1, iCode1, name2, altLoc2, resName2, chainID2, resSeq2, iCode2, sym1, sym2);
|
||||
pdbFile << cif::format("LINK {:<4.4s}{:1.1s}{:3.3s} {:1.1s}{:4}{:1.1s} {:<4.4s}{:1.1s}{:3.3s} {:1.1s}{:4}{:1.1s} {:>6.6s} {:>6.6s}", name1, altLoc1, resName1, chainID1, resSeq1, iCode1, name2, altLoc2, resName2, chainID2, resSeq2, iCode2, sym1, sym2);
|
||||
|
||||
if (not Length.empty())
|
||||
pdbFile << cif::format(" %5.2f", stod(Length));
|
||||
pdbFile << cif::format(" {:5.2f}", stod(Length));
|
||||
|
||||
pdbFile << '\n';
|
||||
}
|
||||
@@ -3255,7 +3249,7 @@ void WriteConnectivity(std::ostream &pdbFile, const datablock &db)
|
||||
"pdbx_label_comp_id_2", "pdbx_auth_asym_id_2", "pdbx_auth_seq_id_2", "pdbx_PDB_ins_code_2",
|
||||
"pdbx_PDB_model_num", "pdbx_omega_angle");
|
||||
|
||||
pdbFile << cif::format("CISPEP %3.3s %3.3s %1.1s %4d%1.1s %3.3s %1.1s %4d%1.1s %3.3s %6.2f",
|
||||
pdbFile << cif::format("CISPEP {:3.3s} {:3.3s} {:1.1s} {:4}{:1.1s} {:3.3s} {:1.1s} {:4}{:1.1s} {:3.3s} {:6.2f}",
|
||||
serNum, pep1, chainID1, seqNum1, icode1, pep2, chainID2, seqNum2, icode2, modNum, measure) << '\n';
|
||||
}
|
||||
}
|
||||
@@ -3276,7 +3270,7 @@ int WriteMiscellaneousFeatures(std::ostream &pdbFile, const datablock &db)
|
||||
cif::tie(siteID, resName, chainID, seq, iCode) =
|
||||
r.get("site_id", "auth_comp_id", "auth_asym_id", "auth_seq_id", "pdbx_auth_ins_code");
|
||||
|
||||
sites[siteID].push_back(cif::format("%3.3s %1.1s%4d%1.1s ", resName, chainID, seq, iCode).str());
|
||||
sites[siteID].push_back(cif::format("{:3.3s} {:1.1s}{:4}{:1.1s} ", resName, chainID, seq, iCode));
|
||||
}
|
||||
|
||||
for (auto s : sites)
|
||||
@@ -3289,7 +3283,7 @@ int WriteMiscellaneousFeatures(std::ostream &pdbFile, const datablock &db)
|
||||
int nr = 1;
|
||||
while (res.empty() == false)
|
||||
{
|
||||
pdbFile << cif::format("SITE %3d %3.3s %2d ", nr, siteID, numRes);
|
||||
pdbFile << cif::format("SITE {:3} {:3.3s} {:2} ", nr, siteID, numRes);
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
@@ -3318,7 +3312,7 @@ void WriteCrystallographic(std::ostream &pdbFile, const datablock &db)
|
||||
|
||||
r = db["cell"].find_first(key("entry_id") == db.name());
|
||||
|
||||
pdbFile << cif::format("CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11.11s%4d", r["length_a"].as<double>(), r["length_b"].as<double>(), r["length_c"].as<double>(), r["angle_alpha"].as<double>(), r["angle_beta"].as<double>(), r["angle_gamma"].as<double>(), symmetry, r["Z_PDB"].as<int>()) << '\n';
|
||||
pdbFile << cif::format("CRYST1{:9.3f}{:9.3f}{:9.3f}{:7.2f}{:7.2f}{:7.2f} {:<11.11s}{:4}", r["length_a"].as<double>(), r["length_b"].as<double>(), r["length_c"].as<double>(), r["angle_alpha"].as<double>(), r["angle_beta"].as<double>(), r["angle_gamma"].as<double>(), symmetry, r["Z_PDB"].as<int>()) << '\n';
|
||||
}
|
||||
|
||||
int WriteCoordinateTransformation(std::ostream &pdbFile, const datablock &db)
|
||||
@@ -3327,18 +3321,18 @@ int WriteCoordinateTransformation(std::ostream &pdbFile, const datablock &db)
|
||||
|
||||
for (auto r : db["database_PDB_matrix"])
|
||||
{
|
||||
pdbFile << cif::format("ORIGX%1d %10.6f%10.6f%10.6f %10.5f", 1, r["origx[1][1]"].as<float>(), r["origx[1][2]"].as<float>(), r["origx[1][3]"].as<float>(), r["origx_vector[1]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("ORIGX%1d %10.6f%10.6f%10.6f %10.5f", 2, r["origx[2][1]"].as<float>(), r["origx[2][2]"].as<float>(), r["origx[2][3]"].as<float>(), r["origx_vector[2]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("ORIGX%1d %10.6f%10.6f%10.6f %10.5f", 3, r["origx[3][1]"].as<float>(), r["origx[3][2]"].as<float>(), r["origx[3][3]"].as<float>(), r["origx_vector[3]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("ORIGX{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 1, r["origx[1][1]"].as<float>(), r["origx[1][2]"].as<float>(), r["origx[1][3]"].as<float>(), r["origx_vector[1]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("ORIGX{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 2, r["origx[2][1]"].as<float>(), r["origx[2][2]"].as<float>(), r["origx[2][3]"].as<float>(), r["origx_vector[2]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("ORIGX{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 3, r["origx[3][1]"].as<float>(), r["origx[3][2]"].as<float>(), r["origx[3][3]"].as<float>(), r["origx_vector[3]"].as<float>()) << '\n';
|
||||
result += 3;
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto r : db["atom_sites"])
|
||||
{
|
||||
pdbFile << cif::format("SCALE%1d %10.6f%10.6f%10.6f %10.5f", 1, r["fract_transf_matrix[1][1]"].as<float>(), r["fract_transf_matrix[1][2]"].as<float>(), r["fract_transf_matrix[1][3]"].as<float>(), r["fract_transf_vector[1]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("SCALE%1d %10.6f%10.6f%10.6f %10.5f", 2, r["fract_transf_matrix[2][1]"].as<float>(), r["fract_transf_matrix[2][2]"].as<float>(), r["fract_transf_matrix[2][3]"].as<float>(), r["fract_transf_vector[2]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("SCALE%1d %10.6f%10.6f%10.6f %10.5f", 3, r["fract_transf_matrix[3][1]"].as<float>(), r["fract_transf_matrix[3][2]"].as<float>(), r["fract_transf_matrix[3][3]"].as<float>(), r["fract_transf_vector[3]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("SCALE{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 1, r["fract_transf_matrix[1][1]"].as<float>(), r["fract_transf_matrix[1][2]"].as<float>(), r["fract_transf_matrix[1][3]"].as<float>(), r["fract_transf_vector[1]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("SCALE{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 2, r["fract_transf_matrix[2][1]"].as<float>(), r["fract_transf_matrix[2][2]"].as<float>(), r["fract_transf_matrix[2][3]"].as<float>(), r["fract_transf_vector[2]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("SCALE{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 3, r["fract_transf_matrix[3][1]"].as<float>(), r["fract_transf_matrix[3][2]"].as<float>(), r["fract_transf_matrix[3][3]"].as<float>(), r["fract_transf_vector[3]"].as<float>()) << '\n';
|
||||
result += 3;
|
||||
break;
|
||||
}
|
||||
@@ -3348,9 +3342,9 @@ int WriteCoordinateTransformation(std::ostream &pdbFile, const datablock &db)
|
||||
{
|
||||
std::string given = r["code"] == "given" ? "1" : "";
|
||||
|
||||
pdbFile << cif::format("MTRIX%1d %3d%10.6f%10.6f%10.6f %10.5f %1.1s", 1, nr, r["matrix[1][1]"].as<float>(), r["matrix[1][2]"].as<float>(), r["matrix[1][3]"].as<float>(), r["vector[1]"].as<float>(), given) << '\n';
|
||||
pdbFile << cif::format("MTRIX%1d %3d%10.6f%10.6f%10.6f %10.5f %1.1s", 2, nr, r["matrix[2][1]"].as<float>(), r["matrix[2][2]"].as<float>(), r["matrix[2][3]"].as<float>(), r["vector[2]"].as<float>(), given) << '\n';
|
||||
pdbFile << cif::format("MTRIX%1d %3d%10.6f%10.6f%10.6f %10.5f %1.1s", 3, nr, r["matrix[3][1]"].as<float>(), r["matrix[3][2]"].as<float>(), r["matrix[3][3]"].as<float>(), r["vector[3]"].as<float>(), given) << '\n';
|
||||
pdbFile << cif::format("MTRIX{:1} {:3}{:10.6f}{:10.6f}{:10.6f} {:10.5f} {:1.1s}", 1, nr, r["matrix[1][1]"].as<float>(), r["matrix[1][2]"].as<float>(), r["matrix[1][3]"].as<float>(), r["vector[1]"].as<float>(), given) << '\n';
|
||||
pdbFile << cif::format("MTRIX{:1} {:3}{:10.6f}{:10.6f}{:10.6f} {:10.5f} {:1.1s}", 2, nr, r["matrix[2][1]"].as<float>(), r["matrix[2][2]"].as<float>(), r["matrix[2][3]"].as<float>(), r["vector[2]"].as<float>(), given) << '\n';
|
||||
pdbFile << cif::format("MTRIX{:1} {:3}{:10.6f}{:10.6f}{:10.6f} {:10.5f} {:1.1s}", 3, nr, r["matrix[3][1]"].as<float>(), r["matrix[3][2]"].as<float>(), r["matrix[3][3]"].as<float>(), r["vector[3]"].as<float>(), given) << '\n';
|
||||
|
||||
++nr;
|
||||
result += 3;
|
||||
@@ -3369,10 +3363,6 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
|
||||
|
||||
auto &atom_site = db["atom_site"];
|
||||
auto &atom_site_anisotrop = db["atom_site_anisotrop"];
|
||||
auto &entity = db["entity"];
|
||||
// auto &pdbx_poly_seq_scheme = db["pdbx_poly_seq_scheme"];
|
||||
// auto &pdbx_nonpoly_scheme = db["pdbx_nonpoly_scheme"];
|
||||
auto &pdbx_branch_scheme = db["pdbx_branch_scheme"];
|
||||
|
||||
int serial = 1;
|
||||
auto ri = atom_site.begin();
|
||||
@@ -3417,7 +3407,7 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
|
||||
|
||||
if (terminate)
|
||||
{
|
||||
pdbFile << cif::format("TER %5d %3.3s %1.1s%4d%1.1s", serial, resName, chainID, resSeq, iCode) << '\n';
|
||||
pdbFile << cif::format("TER {:5} {:3.3s} {:1.1s}{:4}{:1.1s}", serial, resName, chainID, resSeq, iCode) << '\n';
|
||||
|
||||
++serial;
|
||||
terminatedChains.insert(chainID);
|
||||
@@ -3446,26 +3436,6 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
|
||||
r.get("id", "group_PDB", "label_atom_id", "label_alt_id", "auth_comp_id", "auth_asym_id", "auth_seq_id",
|
||||
"pdbx_PDB_ins_code", "Cartn_x", "Cartn_y", "Cartn_z", "occupancy", "B_iso_or_equiv", "type_symbol", "pdbx_formal_charge");
|
||||
|
||||
if (resName != "HOH")
|
||||
{
|
||||
int entity_id = r.get<int>("label_entity_id");
|
||||
try
|
||||
{
|
||||
auto type = entity.find1<std::string>("id"_key == entity_id, "type");
|
||||
|
||||
if (type == "branched") // find the real auth_seq_num, since sugars have their auth_seq_num reused as sugar number... sigh.
|
||||
resSeq = pdbx_branch_scheme.find1<int>("asym_id"_key == r.get<std::string>("label_asym_id") and "pdb_seq_num"_key == resSeq, "auth_seq_num");
|
||||
// else if (type == "non-polymer") // same for non-polymers
|
||||
// resSeq = pdbx_nonpoly_scheme.find1<int>("asym_id"_key == r.get<std::string>("label_asym_id") and "pdb_seq_num"_key == resSeq, "auth_seq_num");
|
||||
// else if (type == "polymer")
|
||||
// resSeq = pdbx_poly_seq_scheme.find1<int>("asym_id"_key == r.get<std::string>("label_asym_id") and "pdb_seq_num"_key == resSeq, "auth_seq_num");
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
std::cerr << "Oops, there was not exactly one entity with id " << entity_id << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
if (chainID.length() > 1)
|
||||
throw std::runtime_error("Chain ID " + chainID + " won't fit into a PDB file");
|
||||
|
||||
@@ -3476,7 +3446,8 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
|
||||
if (charge != 0)
|
||||
sCharge = std::to_string(charge) + (charge > 0 ? '+' : '-');
|
||||
|
||||
pdbFile << cif::format("%-6.6s%5d %-4.4s%1.1s%3.3s %1.1s%4d%1.1s %8.3f%8.3f%8.3f%6.2f%6.2f %2.2s%2.2s", group, serial, name, altLoc, resName, chainID, resSeq, iCode, x, y, z, occupancy, tempFactor, element, sCharge) << '\n';
|
||||
pdbFile << cif::format("{:<6.6s}{:5} {:<4.4s}{:1.1s}{:3.3s} {:1.1s}{:4}{:1.1s} {:8.3f}{:8.3f}{:8.3f}{:6.2f}{:6.2f} {:>2.2s}{:2.2s}",
|
||||
group, serial, name, altLoc, resName, chainID, resSeq, iCode, x, y, z, occupancy, tempFactor, element, sCharge) << '\n';
|
||||
|
||||
++numCoord;
|
||||
|
||||
@@ -3491,7 +3462,7 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
|
||||
tie(u11, u22, u33, u12, u13, u23) =
|
||||
ai.get("U[1][1]", "U[2][2]", "U[3][3]", "U[1][2]", "U[1][3]", "U[2][3]");
|
||||
|
||||
pdbFile << cif::format("ANISOU%5d %-4.4s%1.1s%3.3s %1.1s%4d%1.1s %7d%7d%7d%7d%7d%7d %2.2s%2.2s", serial, name, altLoc, resName, chainID, resSeq, iCode, std::lrintf(u11 * 10000), std::lrintf(u22 * 10000), std::lrintf(u33 * 10000), std::lrintf(u12 * 10000), std::lrintf(u13 * 10000), std::lrintf(u23 * 10000), element, sCharge) << '\n';
|
||||
pdbFile << cif::format("ANISOU{:5} {:<4.4s}{:1.1s}{:3.3s} {:1.1s}{:4}{:1.1s} {:7}{:7}{:7}{:7}{:7}{:7} {:2.2s}{:2.2s}", serial, name, altLoc, resName, chainID, resSeq, iCode, std::lrintf(u11 * 10000), std::lrintf(u22 * 10000), std::lrintf(u33 * 10000), std::lrintf(u12 * 10000), std::lrintf(u13 * 10000), std::lrintf(u23 * 10000), element, sCharge) << '\n';
|
||||
}
|
||||
|
||||
++serial;
|
||||
@@ -3543,7 +3514,7 @@ std::tuple<int, int> WriteCoordinate(std::ostream &pdbFile, const datablock &db)
|
||||
for (int model_nr : models)
|
||||
{
|
||||
if (models.size() > 1)
|
||||
pdbFile << cif::format("MODEL %4d", model_nr) << '\n';
|
||||
pdbFile << cif::format("MODEL {:4}", model_nr) << '\n';
|
||||
|
||||
std::set<std::string> TERminatedChains;
|
||||
auto n = WriteCoordinatesForModel(pdbFile, db, last_resseq_for_chain_map, TERminatedChains, model_nr);
|
||||
@@ -3615,7 +3586,7 @@ std::string get_HEADER_line(const datablock &db, std::string::size_type truncate
|
||||
}
|
||||
}
|
||||
|
||||
return FixStringLength(cif::format("HEADER %-40.40s%-9.9s %-4.4s", keywords, date, db.name()).str(), truncate_at);
|
||||
return FixStringLength(cif::format("HEADER {:<40.40s}{:<9.9s} {:<4.4s}", keywords, date, db.name()), truncate_at);
|
||||
}
|
||||
|
||||
std::string get_COMPND_line(const datablock &db, std::string::size_type truncate_at)
|
||||
@@ -3788,7 +3759,7 @@ void write(std::ostream &os, const datablock &db)
|
||||
numXform = WriteCoordinateTransformation(os, db);
|
||||
std::tie(numCoord, numTer) = WriteCoordinate(os, db);
|
||||
|
||||
os << cif::format("MASTER %5d 0%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d", numRemark, numHet, numHelix, numSheet, numTurn, numSite, numXform, numCoord, numTer, numConect, numSeq) << '\n'
|
||||
os << cif::format("MASTER {:5} 0{:5}{:5}{:5}{:5}{:5}{:5}{:5}{:5}{:5}{:5}", numRemark, numHet, numHelix, numSheet, numTurn, numSite, numXform, numCoord, numTer, numConect, numSeq) << '\n'
|
||||
<< "END\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
|
||||
using cif::category;
|
||||
using cif::datablock;
|
||||
@@ -895,12 +896,7 @@ class PDBFileParser
|
||||
if (year < 1950)
|
||||
year += 100;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::setw(4) << std::setfill('0') << year << '-'
|
||||
<< std::setw(2) << std::setfill('0') << month << '-'
|
||||
<< std::setw(2) << std::setfill('0') << day;
|
||||
|
||||
s = ss.str();
|
||||
s = cif::format("{:04}-{:02}-{:02}", year, month, day);
|
||||
}
|
||||
else if (regex_match(s, m, rx2))
|
||||
{
|
||||
@@ -912,7 +908,7 @@ class PDBFileParser
|
||||
if (year < 1950)
|
||||
year += 100;
|
||||
|
||||
s = cif::format("%04d-%02d", year, month).str();
|
||||
s = cif::format("{:04}-{:02}", year, month);
|
||||
}
|
||||
else
|
||||
ec = error::make_error_code(error::pdbErrors::invalidDate);
|
||||
@@ -3341,18 +3337,18 @@ void PDBFileParser::ParseRemark350()
|
||||
{ "type", type },
|
||||
// { "name", "" },
|
||||
// { "symmetryOperation", "" },
|
||||
{ "matrix[1][1]", cif::format("%12.10f", mat[0]).str() },
|
||||
{ "matrix[1][2]", cif::format("%12.10f", mat[1]).str() },
|
||||
{ "matrix[1][3]", cif::format("%12.10f", mat[2]).str() },
|
||||
{ "vector[1]", cif::format("%12.10f", vec[0]).str() },
|
||||
{ "matrix[2][1]", cif::format("%12.10f", mat[3]).str() },
|
||||
{ "matrix[2][2]", cif::format("%12.10f", mat[4]).str() },
|
||||
{ "matrix[2][3]", cif::format("%12.10f", mat[5]).str() },
|
||||
{ "vector[2]", cif::format("%12.10f", vec[1]).str() },
|
||||
{ "matrix[3][1]", cif::format("%12.10f", mat[6]).str() },
|
||||
{ "matrix[3][2]", cif::format("%12.10f", mat[7]).str() },
|
||||
{ "matrix[3][3]", cif::format("%12.10f", mat[8]).str() },
|
||||
{ "vector[3]", cif::format("%12.10f", vec[2]).str() }
|
||||
{ "matrix[1][1]", cif::format("{:12.10f}", mat[0]) },
|
||||
{ "matrix[1][2]", cif::format("{:12.10f}", mat[1]) },
|
||||
{ "matrix[1][3]", cif::format("{:12.10f}", mat[2]) },
|
||||
{ "vector[1]", cif::format("{:12.10f}", vec[0]) },
|
||||
{ "matrix[2][1]", cif::format("{:12.10f}", mat[3]) },
|
||||
{ "matrix[2][2]", cif::format("{:12.10f}", mat[4]) },
|
||||
{ "matrix[2][3]", cif::format("{:12.10f}", mat[5]) },
|
||||
{ "vector[2]", cif::format("{:12.10f}", vec[1]) },
|
||||
{ "matrix[3][1]", cif::format("{:12.10f}", mat[6]) },
|
||||
{ "matrix[3][2]", cif::format("{:12.10f}", mat[7]) },
|
||||
{ "matrix[3][3]", cif::format("{:12.10f}", mat[8]) },
|
||||
{ "vector[3]", cif::format("{:12.10f}", vec[2]) }
|
||||
});
|
||||
// clang-format on
|
||||
|
||||
@@ -4318,7 +4314,7 @@ void PDBFileParser::ConstructEntities()
|
||||
}
|
||||
|
||||
// build sugar trees first
|
||||
ConstructSugarTrees(asymNr);
|
||||
// ConstructSugarTrees(asymNr);
|
||||
|
||||
// done with the sugar, resume operation as before
|
||||
|
||||
@@ -5772,6 +5768,9 @@ void PDBFileParser::ParseCoordinate(int modelNr)
|
||||
std::string element = vS(77, 78); // 77 - 78 LString(2) element Element symbol, right-justified.
|
||||
std::string charge = vS(79, 80); // 79 - 80 LString(2) charge Charge on the atom.
|
||||
|
||||
if (element.empty())
|
||||
throw std::runtime_error("Empty element column in PDB file at line " + std::to_string(mRec->mLineNr));
|
||||
|
||||
std::string entityID = mAsymID2EntityID[asymID];
|
||||
|
||||
charge = pdb2cifCharge(charge);
|
||||
@@ -5850,7 +5849,7 @@ void PDBFileParser::ParseCoordinate(int modelNr)
|
||||
|
||||
auto f = [](float f) -> std::string
|
||||
{
|
||||
return cif::format("%6.4f", f).str();
|
||||
return cif::format("{:6.4f}", f);
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
@@ -6406,7 +6405,7 @@ file read(std::istream &is)
|
||||
if (std::isalpha(ch) and std::toupper(ch) != 'D')
|
||||
{
|
||||
read_pdb_file(is, result);
|
||||
reconstruct_pdbx(result);
|
||||
fixup_pdbx(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -6419,22 +6418,37 @@ file read(std::istream &is)
|
||||
std::throw_with_nested(std::runtime_error("Since the file did not start with a valid PDB HEADER line mmCIF was assumed, but that failed."));
|
||||
}
|
||||
|
||||
// Try to see if we can create an mm::structure out of this data.
|
||||
// If that fails, we need to reconstruct a PDBx file out of it.
|
||||
try
|
||||
if (not(result.empty() or result.front().empty()))
|
||||
{
|
||||
cif::mm::structure s(result);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
reconstruct_pdbx(result);
|
||||
if (auto &db = result.front(); db.get("audit_conform") == nullptr)
|
||||
reconstruct_pdbx(result);
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to see if we can create an mm::structure out of this data.
|
||||
// If that fails, we need to reconstruct a PDBx file out of it.
|
||||
|
||||
cif::mm::structure s(result);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
reconstruct_pdbx(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Must be a PDB like file, right?
|
||||
if (not result.empty() and result.front().get_validator() == nullptr)
|
||||
result.front().set_validator(&validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
if (not result.empty())
|
||||
{
|
||||
auto &db = result.front();
|
||||
if (db.get_validator() == nullptr)
|
||||
db.set_validator(&validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
if (db.is_valid())
|
||||
db.get_validator()->fill_audit_conform(db["audit_conform"]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
#include "cif++.hpp"
|
||||
#include "cif++/row.hpp"
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
@@ -138,15 +139,15 @@ void checkEntities(datablock &db)
|
||||
auto compound = cf.create(comp_id);
|
||||
if (compound)
|
||||
formula_weight += compound->formula_weight();
|
||||
else if (cif::VERBOSE > 0)
|
||||
std::clog << "missing information for compound " + comp_id << '\n';
|
||||
// else if (cif::VERBOSE > 0)
|
||||
// std::clog << "missing information for compound " + comp_id << '\n';
|
||||
++n;
|
||||
}
|
||||
|
||||
formula_weight -= (n - 1) * 18.015;
|
||||
formula_weight -= (n - 1) * 18.015f;
|
||||
}
|
||||
else if (type == "water")
|
||||
formula_weight = 18.015;
|
||||
formula_weight = 18.015f;
|
||||
else if (type == "branched")
|
||||
{
|
||||
int n = 0;
|
||||
@@ -156,12 +157,12 @@ void checkEntities(datablock &db)
|
||||
auto compound = cf.create(comp_id);
|
||||
if (compound)
|
||||
formula_weight += compound->formula_weight();
|
||||
else if (cif::VERBOSE > 0)
|
||||
std::clog << "missing information for compound " + comp_id << '\n';
|
||||
// else if (cif::VERBOSE > 0)
|
||||
// std::clog << "missing information for compound " + comp_id << '\n';
|
||||
++n;
|
||||
}
|
||||
|
||||
formula_weight -= (n - 1) * 18.015;
|
||||
formula_weight -= (n - 1) * 18.015f;
|
||||
}
|
||||
else if (type == "non-polymer")
|
||||
{
|
||||
@@ -171,7 +172,7 @@ void checkEntities(datablock &db)
|
||||
auto compound = cf.create(*comp_id);
|
||||
if (not compound)
|
||||
{
|
||||
std::cerr << "missing information for compound " << *comp_id << "\n";
|
||||
// std::cerr << "missing information for compound " << *comp_id << "\n";
|
||||
continue;
|
||||
}
|
||||
formula_weight = compound->formula_weight();
|
||||
@@ -264,8 +265,8 @@ void createEntityIDs(datablock &db)
|
||||
|
||||
std::string comp_id = get_comp_id(k);
|
||||
|
||||
for (auto &k : e)
|
||||
atom_site.update_value(get_condition(k), "label_entity_id", entity_id);
|
||||
for (auto &k2 : e)
|
||||
atom_site.update_value(get_condition(k2), "label_entity_id", entity_id);
|
||||
}
|
||||
|
||||
if (not waters.empty())
|
||||
@@ -319,7 +320,7 @@ void fillLabelAsymID(category &atom_site)
|
||||
{
|
||||
if (not mapAuthAsymIDAndEntityToLabelAsymID.contains(key))
|
||||
{
|
||||
std::string asym_id = cif_id_for_number(mapAuthAsymIDAndEntityToLabelAsymID.size());
|
||||
std::string asym_id = cif_id_for_number(static_cast<int>(mapAuthAsymIDAndEntityToLabelAsymID.size()));
|
||||
mapAuthAsymIDAndEntityToLabelAsymID[key] = asym_id;
|
||||
}
|
||||
}
|
||||
@@ -484,7 +485,8 @@ void checkAtomRecords(datablock &db)
|
||||
if (not compound)
|
||||
{
|
||||
missingCompounds.insert(comp_id);
|
||||
std::cerr << "Missing compound information for " << comp_id << "\n";
|
||||
// if (cif::VERBOSE > 0)
|
||||
// std::cerr << "Missing compound information for " << comp_id << "\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -542,7 +544,7 @@ void checkAtomRecords(datablock &db)
|
||||
|
||||
// Rewrite the coordinates and other items that look better in a fixed format
|
||||
// Be careful not to nuke invalidly formatted data here
|
||||
for (auto [item_name, prec] : std::vector<std::tuple<std::string_view, std::string::size_type>>{
|
||||
for (auto [item_name, prec] : std::vector<std::tuple<std::string_view, int>>{
|
||||
{ "cartn_x", 3 },
|
||||
{ "cartn_y", 3 },
|
||||
{ "cartn_z", 3 },
|
||||
@@ -557,11 +559,11 @@ void checkAtomRecords(datablock &db)
|
||||
if (auto [ptr, ec] = cif::from_chars(s.data(), s.data() + s.length(), v); (bool)ec)
|
||||
continue;
|
||||
|
||||
if (s.length() < prec + 1 or s[s.length() - prec - 1] != '.')
|
||||
if (s.length() < prec + 1UL or s[s.length() - prec - 1] != '.')
|
||||
{
|
||||
char b[12];
|
||||
|
||||
if (auto [ptr, ec] = cif::to_chars(b, b + sizeof(b), v, cif::chars_format::fixed, prec); (bool)ec)
|
||||
if (auto [ptr, ec] = cif::to_chars(b, b + sizeof(b), v, cif::chars_format::fixed, prec); ec == std::errc{})
|
||||
row.assign(item_name, { b, static_cast<std::string::size_type>(ptr - b) }, false, false);
|
||||
}
|
||||
}
|
||||
@@ -628,13 +630,13 @@ void checkAtomAnisotropRecords(datablock &db)
|
||||
|
||||
if (row["pdbx_auth_alt_id"].empty() and not parent["pdbx_auth_alt_id"].empty())
|
||||
row["pdbx_auth_alt_id"] = parent["pdbx_auth_alt_id"].text();
|
||||
if (row["pdbx_label_seq_id"].empty() and not parent["pdbx_label_seq_id"].empty())
|
||||
if (row["pdbx_label_seq_id"].empty() and not parent["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() and not parent["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() and not parent["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() and not parent["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();
|
||||
@@ -722,7 +724,7 @@ void createEntity(datablock &db)
|
||||
|
||||
std::string type, desc;
|
||||
float weight = 0;
|
||||
int count = 0;
|
||||
size_t count = 0;
|
||||
|
||||
auto first_comp_id = std::get<0>(content.front());
|
||||
|
||||
@@ -1167,7 +1169,7 @@ void createPdbxNonpolyScheme(datablock &db)
|
||||
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);
|
||||
auto linked = atom_site.get_children(row, pdbx_nonpoly_scheme);
|
||||
if (not linked.empty())
|
||||
continue;
|
||||
|
||||
@@ -1242,6 +1244,101 @@ void createPdbxBranchScheme(datablock &db)
|
||||
}
|
||||
}
|
||||
|
||||
void reconstruct_index_for_category(const validator &validator, category &cat, datablock &db)
|
||||
{
|
||||
auto cv = validator.get_validator_for_category(cat.name());
|
||||
|
||||
enum class State
|
||||
{
|
||||
Start,
|
||||
MissingKeys,
|
||||
DuplicateKeys
|
||||
} state = State::Start;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// See if we can build an index
|
||||
try
|
||||
{
|
||||
cat.set_validator(&validator, db);
|
||||
}
|
||||
catch (const missing_key_error &ex)
|
||||
{
|
||||
if (state == State::MissingKeys)
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
std::clog << "Repairing failed for category " << cat.name() << ", missing keys remain: " << ex.what() << '\n';
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
state = State::MissingKeys;
|
||||
|
||||
auto key = ex.get_key();
|
||||
|
||||
if (cif::VERBOSE > 1)
|
||||
std::clog << "Need to add key " << key << " to category " << cat.name() << '\n';
|
||||
|
||||
for (auto row : cat)
|
||||
{
|
||||
auto ord = row.get<std::string>(key.c_str());
|
||||
if (ord.empty())
|
||||
row.assign({ //
|
||||
{ key, cat.get_unique_value(key) } });
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
catch (const duplicate_key_error &ex)
|
||||
{
|
||||
if (state == State::DuplicateKeys)
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
std::clog << "Repairing failed for category " << cat.name() << ", duplicate keys remain: " << ex.what() << '\n';
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
state = State::DuplicateKeys;
|
||||
|
||||
if (cif::VERBOSE > 0)
|
||||
std::clog << "Attempt to fix " << cat.name() << " failed: " << ex.what() << '\n';
|
||||
|
||||
// replace items that do not define a relation to a parent
|
||||
|
||||
std::set<std::string> replaceableKeys;
|
||||
for (auto key : cv->m_keys)
|
||||
{
|
||||
bool replaceable = true;
|
||||
for (auto lv : validator.get_links_for_child(cat.name()))
|
||||
{
|
||||
if (find(lv->m_child_keys.begin(), lv->m_child_keys.end(), key) != lv->m_child_keys.end())
|
||||
{
|
||||
replaceable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (replaceable)
|
||||
replaceableKeys.insert(key);
|
||||
}
|
||||
|
||||
if (replaceableKeys.empty())
|
||||
throw std::runtime_error("Cannot repair category " + cat.name() + " since it contains duplicate keys that cannot be replaced");
|
||||
|
||||
for (auto key : replaceableKeys)
|
||||
{
|
||||
for (auto row : cat)
|
||||
row.assign(key, cat.get_unique_value(key), false, false);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool reconstruct_pdbx(file &file)
|
||||
{
|
||||
if (file.empty())
|
||||
@@ -1337,7 +1434,7 @@ bool reconstruct_pdbx(file &file, const validator &validator)
|
||||
iv->m_type != nullptr and
|
||||
iv->m_type->m_primitive_type == cif::DDL_PrimitiveType::Numb;
|
||||
|
||||
for (std::size_t ix = 0; auto row : cat)
|
||||
for (int ix = 0; auto row : cat)
|
||||
{
|
||||
if (number)
|
||||
row.assign(key, std::to_string(++ix), false, false);
|
||||
@@ -1408,95 +1505,7 @@ bool reconstruct_pdbx(file &file, const validator &validator)
|
||||
}
|
||||
}
|
||||
|
||||
enum class State
|
||||
{
|
||||
Start,
|
||||
MissingKeys,
|
||||
DuplicateKeys
|
||||
} state = State::Start;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// See if we can build an index
|
||||
try
|
||||
{
|
||||
cat.set_validator(&validator, db);
|
||||
}
|
||||
catch (const missing_key_error &ex)
|
||||
{
|
||||
if (state == State::MissingKeys)
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
std::clog << "Repairing failed for category " << cat.name() << ", missing keys remain: " << ex.what() << '\n';
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
state = State::MissingKeys;
|
||||
|
||||
auto key = ex.get_key();
|
||||
|
||||
if (cif::VERBOSE > 0)
|
||||
std::clog << "Need to add key " << key << " to category " << cat.name() << '\n';
|
||||
|
||||
for (auto row : cat)
|
||||
{
|
||||
auto ord = row.get<std::string>(key.c_str());
|
||||
if (ord.empty())
|
||||
row.assign({ //
|
||||
{ key, cat.get_unique_value(key) } });
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
catch (const duplicate_key_error &ex)
|
||||
{
|
||||
if (state == State::DuplicateKeys)
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
std::clog << "Repairing failed for category " << cat.name() << ", duplicate keys remain: " << ex.what() << '\n';
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
state = State::DuplicateKeys;
|
||||
|
||||
if (cif::VERBOSE > 0)
|
||||
std::clog << "Attempt to fix " << cat.name() << " failed: " << ex.what() << '\n';
|
||||
|
||||
// replace items that do not define a relation to a parent
|
||||
|
||||
std::set<std::string> replaceableKeys;
|
||||
for (auto key : cv->m_keys)
|
||||
{
|
||||
bool replaceable = true;
|
||||
for (auto lv : validator.get_links_for_child(cat.name()))
|
||||
{
|
||||
if (find(lv->m_child_keys.begin(), lv->m_child_keys.end(), key) != lv->m_child_keys.end())
|
||||
{
|
||||
replaceable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (replaceable)
|
||||
replaceableKeys.insert(key);
|
||||
}
|
||||
|
||||
if (replaceableKeys.empty())
|
||||
throw std::runtime_error("Cannot repair category " + cat.name() + " since it contains duplicate keys that cannot be replaced");
|
||||
|
||||
for (auto key : replaceableKeys)
|
||||
{
|
||||
for (auto row : cat)
|
||||
row.assign(key, cat.get_unique_value(key), false, false);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
reconstruct_index_for_category(validator, cat, db);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
@@ -1537,7 +1546,7 @@ bool reconstruct_pdbx(file &file, const validator &validator)
|
||||
|
||||
if (auto cat = db.get("ndb_poly_seq_scheme"); cat == nullptr or cat->empty())
|
||||
comparePolySeqSchemes(db);
|
||||
|
||||
|
||||
createPdbxNonpolyScheme(db);
|
||||
|
||||
// Create a minimal set of branch records
|
||||
@@ -1554,4 +1563,111 @@ bool reconstruct_pdbx(file &file, const validator &validator)
|
||||
return valid and is_valid_pdbx_file(file, validator);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
void fixup_pdbx(file &file)
|
||||
{
|
||||
if (file.empty())
|
||||
throw std::runtime_error("Cannot reconstruct PDBx, file seems to be empty");
|
||||
|
||||
auto &db = file.front();
|
||||
|
||||
if (auto ac = db.get("audit_conform"); ac != nullptr)
|
||||
fixup_pdbx(file, validator_factory::instance().get(*ac));
|
||||
else
|
||||
fixup_pdbx(file, validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
}
|
||||
|
||||
void fixup_pdbx(file &file, const validator &validator)
|
||||
{
|
||||
if (file.empty())
|
||||
throw std::runtime_error("Cannot reconstruct PDBx, file seems to be empty");
|
||||
|
||||
// assuming the first datablock contains the entry ...
|
||||
auto &db = file.front();
|
||||
|
||||
if (auto cat = db.get("atom_site"); cat == nullptr or cat->empty())
|
||||
throw std::runtime_error("Cannot reconstruct PDBx file, atom data missing");
|
||||
|
||||
// ... and any additional datablock will contain compound information
|
||||
cif::compound_source cs(file);
|
||||
|
||||
// Be silent about missing compound info in fixup
|
||||
auto &cf = compound_factory::instance();
|
||||
bool save_report = cf.get_report_missing();
|
||||
cf.set_report_missing(cif::VERBOSE > 1);
|
||||
|
||||
std::string entry_id;
|
||||
|
||||
// Phenix files do not have an entry record
|
||||
if (auto cat = db.get("entry"); cat == nullptr or cat->empty())
|
||||
{
|
||||
entry_id = db.name();
|
||||
category entry("entry");
|
||||
entry.emplace({ { "id", entry_id } });
|
||||
db.emplace_back(std::move(entry));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &entry = db["entry"];
|
||||
if (entry.size() != 1)
|
||||
throw std::runtime_error("Unexpected size of entry category");
|
||||
|
||||
entry_id = entry.front().get<std::string>("id");
|
||||
}
|
||||
|
||||
// Start with chem_comp, it is often missing many fields
|
||||
// that can easily be filled in.
|
||||
checkChemCompRecords(db);
|
||||
|
||||
// If the data is really horrible, it might not contain entities
|
||||
if (not db["atom_site"].find_first(key("label_entity_id") != null))
|
||||
createEntityIDs(db);
|
||||
|
||||
// Now see if atom records make sense at all, but in a silent way, this time
|
||||
checkAtomRecords(db);
|
||||
|
||||
db["chem_comp"].reorder_by_index();
|
||||
|
||||
// See if we can easily reconstruct missing data fields in order to create an index
|
||||
for (auto &cat : db)
|
||||
{
|
||||
try
|
||||
{
|
||||
cat.set_validator(&validator, db);
|
||||
}
|
||||
catch (const missing_key_error &)
|
||||
{
|
||||
reconstruct_index_for_category(validator, cat, db);
|
||||
}
|
||||
}
|
||||
|
||||
db.set_validator(&validator);
|
||||
|
||||
// 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())
|
||||
createStructAsym(db);
|
||||
|
||||
if (auto cat = db.get("entity"); cat == nullptr or cat->empty())
|
||||
createEntity(db);
|
||||
|
||||
if (auto cat = db.get("pdbx_poly_seq_scheme"); cat == nullptr or cat->empty())
|
||||
createPdbxPolySeqScheme(db);
|
||||
|
||||
if (auto cat = db.get("ndb_poly_seq_scheme"); cat == nullptr or cat->empty())
|
||||
comparePolySeqSchemes(db);
|
||||
|
||||
createPdbxNonpolyScheme(db);
|
||||
|
||||
// Create a minimal set of branch records
|
||||
createPdbxBranchScheme(db);
|
||||
|
||||
// fill in missing formula_weight, e.g.
|
||||
checkEntities(db);
|
||||
|
||||
// That's it
|
||||
cf.set_report_missing(save_report);
|
||||
}
|
||||
|
||||
} // namespace cif::pdb
|
||||
|
||||
@@ -61,8 +61,6 @@ condition get_parents_condition(const validator &validator, row_handle rh, const
|
||||
result = std::move(result) or std::move(cond);
|
||||
}
|
||||
}
|
||||
else if (cif::VERBOSE > 0)
|
||||
std::cerr << "warning: no child to parent links were found for child " << childName << " and parent " << parentName << '\n';
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -92,7 +90,7 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
{
|
||||
using namespace cif::literals;
|
||||
|
||||
bool result = true;
|
||||
bool result = true, warned_missing_parents = false;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -129,10 +127,18 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
if (not cf.is_monomer(comp_id))
|
||||
continue;
|
||||
|
||||
auto p = pdbx_poly_seq_scheme.find(get_parents_condition(validator, r, pdbx_poly_seq_scheme));
|
||||
auto cond = get_parents_condition(validator, r, pdbx_poly_seq_scheme);
|
||||
if (not cond)
|
||||
{
|
||||
if (VERBOSE > 0 and std::exchange(warned_missing_parents, true) == false)
|
||||
std::cerr << "warning: missing links for atom_site/pdbx_poly_seq_scheme\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
auto p = pdbx_poly_seq_scheme.find(std::move(cond));
|
||||
if (p.size() != 1)
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
if (VERBOSE > 0)
|
||||
std::clog << "In atom_site record: " << r["id"].text() << '\n';
|
||||
throw std::runtime_error("For each monomer in atom_site there should be exactly one pdbx_poly_seq_scheme record");
|
||||
}
|
||||
@@ -274,7 +280,7 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
|
||||
if (not seq.has_value())
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
if (VERBOSE > 0)
|
||||
std::clog << "Warning: entity_poly has no sequence for entity_id " << entity_id << '\n';
|
||||
}
|
||||
else
|
||||
@@ -287,7 +293,7 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
|
||||
if (not seq_can.has_value())
|
||||
{
|
||||
if (cif::VERBOSE > 1)
|
||||
if (VERBOSE > 1)
|
||||
std::clog << "Warning: entity_poly has no canonical sequence for entity_id " << entity_id << '\n';
|
||||
}
|
||||
else
|
||||
@@ -304,7 +310,7 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
result = false;
|
||||
if (cif::VERBOSE > 0)
|
||||
if (VERBOSE > 0)
|
||||
std::clog << ex.what() << '\n';
|
||||
ec = make_error_code(validation_error::not_valid_pdbx);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
|
||||
#include "symop_table_data.hpp"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning (disable : 5054) // warning C5054: operator '&': deprecated between enumerations of different types
|
||||
#pragma warning (disable : 4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
#include <Eigen/Eigen>
|
||||
|
||||
namespace cif
|
||||
@@ -90,10 +95,10 @@ float cell::get_volume() const
|
||||
auto cos_beta = std::cos(beta);
|
||||
auto cos_gamma = std::cos(gamma);
|
||||
|
||||
auto vol = m_a * m_b * m_c;
|
||||
double vol = m_a * m_b * m_c;
|
||||
vol *= std::sqrt(1.0f - cos_alpha * cos_alpha - cos_beta * cos_beta - cos_gamma * cos_gamma + 2.0f * cos_alpha * cos_beta * cos_gamma);
|
||||
|
||||
return vol;
|
||||
return static_cast<float>(vol);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
@@ -36,16 +36,11 @@
|
||||
|
||||
// The validator depends on regular expressions. Unfortunately,
|
||||
// the implementation of std::regex in g++ is buggy and crashes
|
||||
// on reading the pdbx dictionary. Therefore, in case g++ is used
|
||||
// the code will use boost::regex instead.
|
||||
// on reading the pdbx dictionary. We used to use boost regex
|
||||
// instead but using pcre2 is even easier and faster.
|
||||
|
||||
#if USE_BOOST_REGEX
|
||||
# include <boost/regex.hpp>
|
||||
using boost::regex;
|
||||
#else
|
||||
# include <regex>
|
||||
using std::regex;
|
||||
#endif
|
||||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||||
#include <pcre2.h>
|
||||
|
||||
namespace cif
|
||||
{
|
||||
@@ -67,14 +62,67 @@ validation_exception::validation_exception(std::error_code ec, std::string_view
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
struct regex_impl : public regex
|
||||
// struct regex_impl : public regex
|
||||
// {
|
||||
// regex_impl(std::string_view rx)
|
||||
// : regex(rx.begin(), rx.end(), regex::extended | regex::optimize)
|
||||
// {
|
||||
// }
|
||||
// };
|
||||
|
||||
struct regex_impl
|
||||
{
|
||||
regex_impl(std::string_view rx)
|
||||
: regex(rx.begin(), rx.end(), regex::extended | regex::optimize)
|
||||
{
|
||||
}
|
||||
regex_impl(std::string_view rx);
|
||||
~regex_impl();
|
||||
|
||||
regex_impl(const regex_impl &) = delete;
|
||||
regex_impl &operator=(const regex_impl &) = delete;
|
||||
|
||||
bool match(std::string_view v) const;
|
||||
|
||||
private:
|
||||
|
||||
pcre2_code *m_rx = nullptr;
|
||||
pcre2_match_data *m_data = nullptr;
|
||||
};
|
||||
|
||||
regex_impl::regex_impl(std::string_view rx)
|
||||
{
|
||||
int err_code;
|
||||
size_t err_offset;
|
||||
m_rx = pcre2_compile((PCRE2_SPTR)rx.data(), rx.length(), 0, &err_code, &err_offset, nullptr);
|
||||
if (m_rx == nullptr)
|
||||
{
|
||||
PCRE2_UCHAR buffer[256];
|
||||
int n = pcre2_get_error_message(err_code, buffer, sizeof(buffer));
|
||||
|
||||
throw std::runtime_error(std::string("PCRE2 compilation failed: ") + std::string{ (char *)buffer, (char *)buffer + n });
|
||||
}
|
||||
|
||||
m_data = pcre2_match_data_create_from_pattern(m_rx, nullptr);
|
||||
}
|
||||
|
||||
regex_impl::~regex_impl()
|
||||
{
|
||||
if (m_data)
|
||||
pcre2_match_data_free(m_data);
|
||||
|
||||
if (m_rx)
|
||||
pcre2_code_free(m_rx);
|
||||
}
|
||||
|
||||
bool regex_impl::match(std::string_view v) const
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if (int rc = pcre2_match(m_rx, (PCRE2_SPTR)v.data(), v.length(), 0, 0, m_data, nullptr); rc >= 0)
|
||||
result = true;
|
||||
else if (rc != PCRE2_ERROR_NOMATCH)
|
||||
std::cerr << "Error matching with pcre\n";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
DDL_PrimitiveType map_to_primitive_type(std::string_view s, std::error_code &ec) noexcept
|
||||
@@ -233,7 +281,7 @@ bool item_validator::validate_value(std::string_view value, std::error_code &ec)
|
||||
|
||||
if (not value.empty() and value != "?" and value != ".")
|
||||
{
|
||||
if (m_type != nullptr and not regex_match(value.begin(), value.end(), *m_type->m_rx))
|
||||
if (m_type != nullptr and not m_type->m_rx->match(value))
|
||||
ec = make_error_code(validation_error::value_does_not_match_rx);
|
||||
else if (not m_enums.empty() and m_enums.count(std::string{ value }) == 0)
|
||||
ec = make_error_code(validation_error::value_is_not_in_enumeration_list);
|
||||
@@ -552,6 +600,9 @@ const validator &validator_factory::get(const category &audit_conform)
|
||||
validator validator_factory::construct_validator(std::string_view name, std::optional<std::string> version)
|
||||
{
|
||||
auto data = load_resource(name);
|
||||
if (not data and name == "mmcif_pdbx_v50")
|
||||
data = load_resource("mmcif_pdbx.dic");
|
||||
|
||||
if (not data)
|
||||
throw std::runtime_error("Could not load dictionary " + std::string{ name });
|
||||
|
||||
|
||||
@@ -22,13 +22,14 @@ list(
|
||||
CIFPP_tests
|
||||
unit-v2
|
||||
unit-3d
|
||||
format
|
||||
model
|
||||
query
|
||||
rename-compound
|
||||
sugar
|
||||
spinner
|
||||
# reconstruction
|
||||
validate-pdbx)
|
||||
validate-pdbx
|
||||
)
|
||||
|
||||
add_library(test-main OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/test-main.cpp")
|
||||
|
||||
|
||||
@@ -431,3 +431,169 @@ TEST_CASE("remove_residue_1")
|
||||
|
||||
REQUIRE_NOTHROW(s.validate_atoms());
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Tests for structure_open_options
|
||||
|
||||
TEST_CASE("options_1")
|
||||
{
|
||||
using namespace cif::literals;
|
||||
|
||||
const std::filesystem::path example(gTestDir / ".." / "examples" / "1cbs.cif.gz");
|
||||
cif::file file(example.string());
|
||||
|
||||
auto &cf = cif::compound_factory::instance();
|
||||
|
||||
SECTION("skip_water")
|
||||
{
|
||||
cif::mm::structure s(file, 1, { .skip_water = true });
|
||||
|
||||
REQUIRE_NOTHROW(s.validate_atoms());
|
||||
|
||||
for (auto a : s.atoms())
|
||||
CHECK_FALSE(a.is_water());
|
||||
}
|
||||
|
||||
SECTION("skip_hetatom")
|
||||
{
|
||||
cif::mm::structure s(file, 1, { .skip_hetatom = true });
|
||||
|
||||
REQUIRE_NOTHROW(s.validate_atoms());
|
||||
|
||||
for (auto a : s.atoms())
|
||||
CHECK((a.is_water() or cf.is_peptide(a.get_label_comp_id()) or cf.is_base(a.get_label_comp_id())));
|
||||
}
|
||||
|
||||
SECTION("selected_asyms")
|
||||
{
|
||||
cif::mm::structure s(file, 1, { .asyms = { "A" } });
|
||||
|
||||
REQUIRE_NOTHROW(s.validate_atoms());
|
||||
|
||||
for (auto a : s.atoms())
|
||||
CHECK(a.get_label_asym_id() == "A");
|
||||
}
|
||||
|
||||
SECTION("min-b-factor")
|
||||
{
|
||||
cif::mm::structure s(file, 1, { .min_b_factor = 20.f });
|
||||
|
||||
REQUIRE_NOTHROW(s.validate_atoms());
|
||||
|
||||
for (auto a : s.atoms())
|
||||
CHECK(a.get_property_float("B_iso_or_equiv") >= 20.f);
|
||||
}
|
||||
|
||||
SECTION("max-b-factor")
|
||||
{
|
||||
cif::mm::structure s(file, 1, { .max_b_factor = 20.f });
|
||||
|
||||
REQUIRE_NOTHROW(s.validate_atoms());
|
||||
|
||||
for (auto a : s.atoms())
|
||||
CHECK(a.get_property_float("B_iso_or_equiv") <= 20.f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("options_2")
|
||||
{
|
||||
|
||||
auto data = R"(
|
||||
data_TEST
|
||||
#
|
||||
_pdbx_nonpoly_scheme.asym_id A
|
||||
_pdbx_nonpoly_scheme.ndb_seq_num 1
|
||||
_pdbx_nonpoly_scheme.entity_id 1
|
||||
_pdbx_nonpoly_scheme.mon_id HEM
|
||||
_pdbx_nonpoly_scheme.pdb_seq_num 1
|
||||
_pdbx_nonpoly_scheme.auth_seq_num 1
|
||||
_pdbx_nonpoly_scheme.pdb_mon_id HEM
|
||||
_pdbx_nonpoly_scheme.auth_mon_id HEM
|
||||
_pdbx_nonpoly_scheme.pdb_strand_id A
|
||||
_pdbx_nonpoly_scheme.pdb_ins_code .
|
||||
#
|
||||
loop_
|
||||
_atom_site.id
|
||||
_atom_site.auth_asym_id
|
||||
_atom_site.label_alt_id
|
||||
_atom_site.label_asym_id
|
||||
_atom_site.label_atom_id
|
||||
_atom_site.label_comp_id
|
||||
_atom_site.label_entity_id
|
||||
_atom_site.label_seq_id
|
||||
_atom_site.type_symbol
|
||||
_atom_site.group_PDB
|
||||
_atom_site.pdbx_PDB_ins_code
|
||||
_atom_site.Cartn_x
|
||||
_atom_site.Cartn_y
|
||||
_atom_site.Cartn_z
|
||||
_atom_site.occupancy
|
||||
_atom_site.B_iso_or_equiv
|
||||
_atom_site.pdbx_formal_charge
|
||||
_atom_site.auth_seq_id
|
||||
_atom_site.auth_comp_id
|
||||
_atom_site.auth_atom_id
|
||||
_atom_site.pdbx_PDB_model_num
|
||||
1 A A A CHA HEM 1 . C HETATM ? -5.248 39.769 -0.250 0.75 7.67 ? 1 HEM CHA 1
|
||||
3 A A A CHB HEM 1 . C HETATM ? -3.774 36.790 3.280 0.75 7.05 ? 1 HEM CHB 1
|
||||
2 A A A CHC HEM 1 . C HETATM ? -2.879 33.328 0.013 0.75 7.69 ? 1 HEM CHC 1
|
||||
4 A A A CHD HEM 1 . C HETATM ? -4.342 36.262 -3.536 0.75 8.00 ? 1 HEM CHD 1
|
||||
5 A B A CHA HEM 1 . C HETATM ? -5.248 39.769 -0.250 0.25 7.67 ? 1 HEM CHA 1
|
||||
6 A B A CHB HEM 1 . C HETATM ? -3.774 36.790 3.280 0.25 7.05 ? 1 HEM CHB 1
|
||||
7 A B A CHC HEM 1 . C HETATM ? -2.879 33.328 0.013 0.25 7.69 ? 1 HEM CHC 1
|
||||
8 A B A CHD HEM 1 . C HETATM ? -4.342 36.262 -3.536 0.25 8.00 ? 1 HEM CHD 1
|
||||
#
|
||||
_chem_comp.id HEM
|
||||
_chem_comp.type NON-POLYMER
|
||||
_chem_comp.name 'PROTOPORPHYRIN IX CONTAINING FE'
|
||||
_chem_comp.formula 'C34 H32 Fe N4 O4'
|
||||
_chem_comp.formula_weight 616.487000
|
||||
#
|
||||
_pdbx_entity_nonpoly.entity_id 1
|
||||
_pdbx_entity_nonpoly.name 'PROTOPORPHYRIN IX CONTAINING FE'
|
||||
_pdbx_entity_nonpoly.comp_id HEM
|
||||
#
|
||||
_entity.id 1
|
||||
_entity.type non-polymer
|
||||
_entity.pdbx_description 'PROTOPORPHYRIN IX CONTAINING FE'
|
||||
_entity.formula_weight 616.487000
|
||||
#
|
||||
_struct_asym.id A
|
||||
_struct_asym.entity_id 1
|
||||
_struct_asym.pdbx_blank_PDB_chainid_flag N
|
||||
_struct_asym.pdbx_modified N
|
||||
_struct_asym.details ?
|
||||
#
|
||||
)"_cf;
|
||||
|
||||
data.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
SECTION("max")
|
||||
{
|
||||
cif::mm::structure s(data, 1, {
|
||||
.occupancy_mode = cif::mm::occupancy_policy::MAX
|
||||
});
|
||||
|
||||
REQUIRE(s.atoms().size() == 4);
|
||||
CHECK(s.atoms().front().get_label_alt_id() == "A");
|
||||
}
|
||||
|
||||
SECTION("min")
|
||||
{
|
||||
cif::mm::structure s(data, 1, {
|
||||
.occupancy_mode = cif::mm::occupancy_policy::MIN
|
||||
});
|
||||
|
||||
REQUIRE(s.atoms().size() == 4);
|
||||
CHECK(s.atoms().front().get_label_alt_id() == "B");
|
||||
}
|
||||
|
||||
SECTION("unoccupied")
|
||||
{
|
||||
cif::mm::structure s(data, 1, {
|
||||
.occupancy_mode = cif::mm::occupancy_policy::UNOCCUPIED
|
||||
});
|
||||
|
||||
CHECK(s.atoms().empty());
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2025 NKI/AVL, Netherlands Cancer Institute
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
@@ -24,38 +24,31 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "test-main.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <cif++.hpp>
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
TEST_CASE("fmt_1")
|
||||
TEST_CASE("q-1")
|
||||
{
|
||||
std::ostringstream os;
|
||||
using namespace cif::literals;
|
||||
|
||||
std::string world("world");
|
||||
os << cif::format("Hello, %-10.10s, the magic number is %d and pi is %g", world, 42, cif::kPI);
|
||||
REQUIRE(os.str() == "Hello, world , the magic number is 42 and pi is 3.14159");
|
||||
cif::compound_factory::instance().push_dictionary(gTestDir / "REA.cif");
|
||||
|
||||
cif::file a = cif::pdb::read(gTestDir / "pdb1cbs.ent.gz");
|
||||
auto &pdbx_poly_seq_scheme = a.front()["pdbx_poly_seq_scheme"];
|
||||
REQUIRE_FALSE(pdbx_poly_seq_scheme.empty());
|
||||
|
||||
SECTION("s-11")
|
||||
{
|
||||
CHECK(pdbx_poly_seq_scheme.count("asym_id"_key == "A") == 137);
|
||||
|
||||
CHECK(pdbx_poly_seq_scheme.count("asym_id"_key == "A" and "entity_id"_key == 1 and "seq_id"_key == 1 and "mon_id"_key == "PRO") == 1);
|
||||
|
||||
CHECK(pdbx_poly_seq_scheme.count("asym_id"_key == "A" and "entity_id"_key == 1 and "seq_id"_key == 1 and "mon_id"_key == "PRO" and "hetero"_key == false) == 1);
|
||||
}
|
||||
|
||||
|
||||
REQUIRE(cif::format("Hello, %-10.10s, the magic number is %d and pi is %g", world, 42, cif::kPI).str() ==
|
||||
"Hello, world , the magic number is 42 and pi is 3.14159");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("clr_1")
|
||||
{
|
||||
using namespace cif::colour;
|
||||
|
||||
std::cout << "Hello, " << cif::coloured("world!", white, red, cif::colour::regular) << '\n'
|
||||
<< "Hello, " << cif::coloured("world!", white, red, bold) << '\n'
|
||||
<< "Hello, " << cif::coloured("world!", black, red) << '\n'
|
||||
<< "Hello, " << cif::coloured("world!", white, green) << '\n'
|
||||
<< "Hello, " << cif::coloured("world!", white, blue) << '\n'
|
||||
<< "Hello, " << cif::coloured("world!", blue, white) << '\n'
|
||||
<< "Hello, " << cif::coloured("world!", red, white, bold) << '\n';
|
||||
}
|
||||
@@ -30,6 +30,11 @@
|
||||
|
||||
#include <cif++.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning (disable : 5054) // warning C5054: operator '&': deprecated between enumerations of different types
|
||||
#pragma warning (disable : 4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
#include <Eigen/Eigenvalues>
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -296,7 +301,7 @@ TEST_CASE("m2q_0a")
|
||||
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];
|
||||
rot << static_cast<float>(d[0]), static_cast<float>(d[1]), static_cast<float>(d[2]), static_cast<float>(d[3]), static_cast<float>(d[4]), static_cast<float>(d[5]), static_cast<float>(d[6]), static_cast<float>(d[7]), static_cast<float>(d[8]);
|
||||
|
||||
// check to see if this matrix contains a true rotation
|
||||
if (rot * rot.transpose() != Eigen::Matrix3f::Identity() or rot.determinant() != 1)
|
||||
@@ -435,11 +440,11 @@ TEST_CASE("symm_4")
|
||||
|
||||
// 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);
|
||||
auto c = cif::cell(107.516f, 107.516f, 338.487f, 90.00f, 90.00f, 120.00f);
|
||||
|
||||
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.688f, 79.351f, 10.439f }; // O6 NAG A 500
|
||||
cif::point b{ -35.356f, 33.693f, -3.236f }; // CG2 THR D 400
|
||||
cif::point sb(-6.916f, 79.34f, 3.236f); // 4_565 copy of b
|
||||
|
||||
CHECK_THAT(distance(a, sg(a, c, "1_455"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_a()), 0.01f));
|
||||
CHECK_THAT(distance(a, sg(a, c, "1_545"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_b()), 0.01f));
|
||||
@@ -466,7 +471,7 @@ TEST_CASE("symm_4wvp_1")
|
||||
|
||||
cif::crystal c(db);
|
||||
|
||||
cif::point p{ -78.722, 98.528, 11.994 };
|
||||
cif::point p{ -78.722f, 98.528f, 11.994f };
|
||||
auto a = s.get_residue("A", 10, "").get_atom_by_atom_id("O");
|
||||
|
||||
auto sp1 = c.symmetry_copy(a.get_location(), "2_565"_symop);
|
||||
|
||||
@@ -228,13 +228,13 @@ TEST_CASE("item_2")
|
||||
cif::item i1("test1", std:: optional<float>());
|
||||
REQUIRE(i1.value() == "?");
|
||||
|
||||
cif::item i2("test1", std::make_optional<float>(1));
|
||||
cif::item i2("test1", std::make_optional<float>(1.f));
|
||||
REQUIRE(i2.value() == "1");
|
||||
|
||||
cif::item i3("test1", std::optional<float>(), 2);
|
||||
REQUIRE(i3.value() == "?");
|
||||
|
||||
cif::item i4("test1", std::make_optional<float>(1), 2);
|
||||
cif::item i4("test1", std::make_optional<float>(1.f), 2);
|
||||
REQUIRE(i4.value() == "1.00");
|
||||
}
|
||||
|
||||
@@ -3355,10 +3355,10 @@ _cat_1.id_2
|
||||
using test_tuple_type = std::tuple<key_type,bool>;
|
||||
|
||||
test_tuple_type TESTS[] = {
|
||||
{ {{"id", 1}, {"id_2", 10}}, true },
|
||||
{ {{"id_2", 10}, {"id", 1}}, true },
|
||||
{ {{"id", 1}, {"id_2", 20}}, false },
|
||||
{ {{"id", 3} }, true },
|
||||
{ {{"id", "1"}, {"id_2", "10"}}, true },
|
||||
{ {{"id_2", "10"}, {"id", "1"}}, true },
|
||||
{ {{"id", "1"}, {"id_2", "20"}}, false },
|
||||
{ {{"id", "3"} }, true },
|
||||
};
|
||||
|
||||
for (const auto &[key, test] : TESTS)
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
@ECHO OFF
|
||||
SET ZLIB_VERSION=1.3
|
||||
SET PCRE2_VERSION=10.45
|
||||
|
||||
IF NOT EXIST build_ci\libs (
|
||||
MKDIR build_ci\libs
|
||||
)
|
||||
CD build_ci\libs
|
||||
|
||||
@REM Install ZLib
|
||||
IF NOT EXIST zlib-%ZLIB_VERSION%.zip (
|
||||
ECHO Downloading https://github.com/libarchive/zlib/archive/v%ZLIB_VERSION%.zip
|
||||
curl -L -o zlib-%ZLIB_VERSION%.zip https://github.com/libarchive/zlib/archive/v%ZLIB_VERSION%.zip || EXIT /b 1
|
||||
@@ -14,9 +17,24 @@ IF NOT EXIST zlib-%ZLIB_VERSION% (
|
||||
C:\windows\system32\tar.exe -x -f zlib-%ZLIB_VERSION%.zip || EXIT /b 1
|
||||
)
|
||||
CD zlib-%ZLIB_VERSION%
|
||||
cmake -G "Visual Studio 17 2022" . || EXIT /b 1
|
||||
cmake --build . --target ALL_BUILD --config Release || EXIT /b 1
|
||||
cmake --build . --target RUN_TESTS --config Release || EXIT /b 1
|
||||
cmake --build . --target INSTALL --config Release || EXIT /b 1
|
||||
cmake -B build || EXIT /b 1
|
||||
cmake --build build --target ALL_BUILD --config Release || EXIT /b 1
|
||||
cmake --build build --target RUN_TESTS --config Release || EXIT /b 1
|
||||
cmake --build build --target INSTALL --config Release || EXIT /b 1
|
||||
|
||||
@REM Install PCRE2
|
||||
IF NOT EXIST pcre2-%PCRE2_VERSION%.zip (
|
||||
ECHO Downloading https://github.com/PCRE2Project/pcre2/releases/download/pcre2-%PCRE2_VERSION%/pcre2-%PCRE2_VERSION%.zip
|
||||
curl -L -o pcre2-%PCRE2_VERSION%.zip https://github.com/PCRE2Project/pcre2/releases/download/pcre2-%PCRE2_VERSION%/pcre2-%PCRE2_VERSION%.zip || EXIT /b 1
|
||||
)
|
||||
IF NOT EXIST pcre2-%PCRE2_VERSION% (
|
||||
ECHO Unpacking pcre2-%PCRE2_VERSION%.zip
|
||||
C:\windows\system32\tar.exe -x -f pcre2-%PCRE2_VERSION%.zip || EXIT /b 1
|
||||
)
|
||||
CD pcre2-%PCRE2_VERSION%
|
||||
cmake -B build -DPCRE2_BUILD_PCRE2GREP=OFF || EXIT /b 1
|
||||
cmake --build build --target ALL_BUILD --config Release || EXIT /b 1
|
||||
@REM cmake --build build --target RUN_TESTS --config Release || EXIT /b 1
|
||||
cmake --build build --target INSTALL --config Release || EXIT /b 1
|
||||
|
||||
@EXIT /b 0
|
||||
|
||||
Reference in New Issue
Block a user