Compare commits

..

1 Commits

Author SHA1 Message Date
Maarten L. Hekkelman
be0f885fa5 Merge branch 'trunk' into dict-for-data 2025-03-10 14:00:19 +01:00
58 changed files with 5532 additions and 12987 deletions

View File

@@ -1,22 +0,0 @@
BasedOnStyle: LLVM
UseTab: AlignWithSpaces
IndentWidth: 4
TabWidth: 4
BreakBeforeBraces: Allman
ColumnLimit: 0
NamespaceIndentation: Inner
FixNamespaceComments: true
AccessModifierOffset: -2
AllowShortCaseLabelsOnASingleLine: true
IndentCaseLabels: true
BreakConstructorInitializers: BeforeComma
BraceWrapping:
BeforeLambdaBody: false
AlignAfterOpenBracket: DontAlign
Cpp11BracedListStyle: false
IncludeBlocks: Regroup
LambdaBodyIndentation: Signature
AllowShortLambdasOnASingleLine: Inline
EmptyLineBeforeAccessModifier: LogicalBlock
IndentPPDirectives: AfterHash
PPIndentWidth: 1

View File

@@ -19,7 +19,7 @@ jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v1
- name: Set reusable strings
# Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file.
@@ -47,7 +47,7 @@ jobs:
ls -l ${{ steps.strings.outputs.build-output-dir }}/docs/sphinx
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@v2
with:
path: ${{ steps.strings.outputs.build-output-dir }}/docs/sphinx
@@ -62,4 +62,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v2

View File

@@ -33,18 +33,13 @@ jobs:
- name: Install dependencies Ubuntu
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update && sudo apt-get install mrc catch2
run: sudo apt-get update && sudo apt-get install mrc
- name: Install dependencies Window
if: matrix.os == 'windows-latest'
run: ./tools/depends.cmd
shell: cmd
- name: Install Catch2 macOS
if: matrix.os == 'macos-latest'
run: >
brew install catch2
- name: Configure CMake
run: >
cmake -B ${{ steps.strings.outputs.build-output-dir }}

3
.gitignore vendored
View File

@@ -13,6 +13,3 @@ docs/api
docs/conf.py
build_ci/
data/components.cif
perf.data*
.cache/

View File

@@ -24,25 +24,29 @@
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 9.0.4
LANGUAGES CXX C)
VERSION 8.0.0
LANGUAGES CXX)
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(ExternalProject)
include(FetchContent)
include(ExternalProject)
# FindBoost, take care of it now.
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.30)
cmake_policy(SET CMP0167 NEW)
endif()
# When building with ninja-multiconfig, build both debug and release by default
if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
@@ -59,23 +63,25 @@ elseif(MSVC)
endif()
# Build documentation?
set(BUILD_DOCUMENTATION OFF CACHE BOOL "Build the documentation")
option(BUILD_DOCUMENTATION "Build the documentation" OFF)
# Optionally build a version to be installed inside CCP4
set(BUILD_FOR_CCP4 OFF CACHE BOOL "Build a version to be installed in CCP4")
option(BUILD_FOR_CCP4 "Build a version to be installed in CCP4")
# Building shared libraries?
if(NOT(BUILD_FOR_CCP4 AND WIN32))
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build a shared library instead of a static one")
option(BUILD_SHARED_LIBS "Build a shared library instead of a static one" OFF)
endif()
if(PROJECT_IS_TOP_LEVEL AND NOT BUILD_FOR_CCP4)
# Lots of code depend on the availability of the components.cif file
set(CIFPP_DOWNLOAD_CCD ON CACHE BOOL "Download the CCD file components.cif during installation")
option(CIFPP_DOWNLOAD_CCD
"Download the CCD file components.cif during installation" ON)
# An optional cron script can be installed to keep the data files up-to-date
if(UNIX AND NOT APPLE)
set(CIFPP_INSTALL_UPDATE_SCRIPT ON CACHE BOOL "Install the script to update CCD and dictionary files")
option(CIFPP_INSTALL_UPDATE_SCRIPT
"Install the script to update CCD and dictionary files" ON)
endif()
else()
unset(CIFPP_DOWNLOAD_CCD)
@@ -85,13 +91,14 @@ endif()
# When CCP4 is sourced in the environment, we can recreate the symmetry
# operations table
if(EXISTS "$ENV{CCP4}/lib/data/syminfo.lib")
set(CIFPP_RECREATE_SYMOP_DATA ON CACHE BOOL "Recreate SymOp data table in case it is out of date")
option(CIFPP_RECREATE_SYMOP_DATA
"Recreate SymOp data table in case it is out of date" ON)
endif()
# CCP4 build
if(BUILD_FOR_CCP4)
if("$ENV{CCP4}" STREQUAL "" OR NOT EXISTS $ENV{CCP4})
message(FATAL_ERROR "cifpp: A CCP4 built was requested but CCP4 was not sourced")
message(FATAL_ERROR "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}")
@@ -121,6 +128,9 @@ 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()
@@ -140,8 +150,51 @@ 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)
@@ -168,43 +221,11 @@ if(MSVC)
endforeach()
endif()
# 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 "cifpp: <format> not found and neither was libfmt, compiler too old, you're out of luck")
endif()
endif()
# Using fast_float for float parsing, but only if needed
# try_compile(STD_CHARCONV_COMPILING
# SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/test-charconv.cpp)
if(NOT STD_CHARCONV_COMPILING)
message(NOTICE "libcifpp: Using fast_float for std::from_chars")
FetchContent_Declare(fastfloat
GIT_REPOSITORY "https://github.com/fastfloat/fast_float"
GIT_TAG v8.0.2)
FetchContent_MakeAvailable(fastfloat)
endif()
find_package(Threads)
find_package(ZLIB QUIET)
find_package(Threads)
if(NOT ZLIB_FOUND)
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()
include(FindPkgConfig)
if(PKG_CONFIG_FOUND)
pkg_check_modules(PCRE2 IMPORTED_TARGET libpcre2-8)
endif()
if(NOT PCRE2_FOUND)
add_subdirectory(pcre2-simple)
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)")
endif()
# Using Eigen3 is a bit of a thing. We don't want to build it completely since
@@ -218,16 +239,16 @@ if(Eigen3_FOUND AND TARGET Eigen3::Eigen)
else()
# Use ExternalProject since FetchContent always tries to install the result...
ExternalProject_Add(my-eigen3
URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.zip
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.0
INSTALL_COMMAND "")
ExternalProject_Get_Property(my-eigen3 SOURCE_DIR)
set(EIGEN_INCLUDE_DIR ${SOURCE_DIR})
endif()
message(STATUS "Eigen include dir is ${EIGEN_INCLUDE_DIR}")
# Create a revision file, containing the current git version info
include(VersionString)
write_version_header(${CMAKE_CURRENT_SOURCE_DIR}/src/ LIB_NAME "LibCIFPP")
@@ -330,9 +351,19 @@ 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()
@@ -343,25 +374,9 @@ target_include_directories(
cifpp
PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
PRIVATE "${EIGEN_INCLUDE_DIR}")
PRIVATE "${BOOST_REGEX_INCLUDE_DIR}" "${EIGEN_INCLUDE_DIR}")
target_link_libraries(cifpp
PUBLIC Threads::Threads ZLIB::ZLIB $<$<TARGET_EXISTS:std::atomic>:std::atomic>)
if(PCRE2_FOUND)
target_include_directories(cifpp PRIVATE ${PCRE2_INCLUDE_DIRS})
target_link_libraries(cifpp PRIVATE ${PCRE2_LINK_LIBRARIES})
else()
target_link_libraries(cifpp PRIVATE $<BUILD_INTERFACE:pcre2s>)
endif()
if(NOT STD_CHARCONV_COMPILING)
target_link_libraries(cifpp PUBLIC FastFloat::fast_float)
endif()
if(fmt_FOUND)
target_link_libraries(cifpp PUBLIC fmt)
endif()
target_link_libraries(cifpp PUBLIC Threads::Threads ZLIB::ZLIB $<$<TARGET_EXISTS:std::atomic>:std::atomic>)
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
@@ -375,7 +390,7 @@ if(CIFPP_DOWNLOAD_CCD)
file(SIZE ${COMPONENTS_CIF} CCD_FILE_SIZE)
if(CCD_FILE_SIZE EQUAL 0)
message(STATUS "cifpp: Removing empty ${COMPONENTS_CIF} file")
message(STATUS "Removing empty ${COMPONENTS_CIF} file")
file(REMOVE "${COMPONENTS_CIF}")
endif()
endif()
@@ -414,7 +429,7 @@ if(CIFPP_DOWNLOAD_CCD)
if(CCD_FETCH_STATUS_CODE)
message(
FATAL_ERROR "cifpp: Error trying to download CCD file: ${CCD_FETCH_STATUS}")
FATAL_ERROR "Error trying to download CCD file: ${CCD_FETCH_STATUS}")
endif()
endif()
endif()
@@ -478,7 +493,7 @@ file(GLOB OLD_CONFIG_FILES
if(OLD_CONFIG_FILES)
message(
STATUS "cifpp: Installation will remove old config files: ${OLD_CONFIG_FILES}")
STATUS "Installation will remove old config files: ${OLD_CONFIG_FILES}")
install(CODE "file(REMOVE ${OLD_CONFIG_FILES})")
endif()
@@ -499,7 +514,6 @@ 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
@@ -545,7 +559,7 @@ if(CIFPP_INSTALL_UPDATE_SCRIPT)
PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE
WORLD_READ)
else()
message(FATAL_ERROR "cifpp: Don't know where to install the update script")
message(FATAL_ERROR "Don't know where to install the update script")
endif()
# a config file, to make it complete
@@ -559,7 +573,7 @@ if(CIFPP_INSTALL_UPDATE_SCRIPT)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR})
install(
CODE "message(\"cifpp: A configuration file has been written to ${CIFPP_ETC_DIR}/libcifpp.conf, please edit this file to enable automatic updates\")"
CODE "message(\"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)

View File

@@ -117,8 +117,12 @@ 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`.
- [pcre2](https://www.pcre.org/), the Perl Compatible Regular Expression
library. On Debian/Ubuntu this is the package `libpcre2-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.
### Building

View File

@@ -1,33 +1,3 @@
Version 9.0.4
- Fix various stopping and reconstruction errors
Version 9.0.3
- Reconstruction fixed when some entity ids are missing
Version 9.0.2
- Fix code that reconstructs sequences, could throw a map::at
- Many optimisations in validation and reconstruction code.
Version 9.0.1
- Use pcre2 from pkg-config if available, if not
build a version from the original code.
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
- Reconstruct some branch records in bare pdbx files
- Fix parsing PDB files (bug due to missing validator in dest. cat.)
- Do not fail conversion of PDB files when compound info is missing
Version 8.0.0
- A dictionary is for a datablock and a file can have
datablocks with differing dictionaries.

View File

@@ -1,12 +0,0 @@
# The problem is, find_package(PCRE2) does not work
# and using pkg-config results in linking to a shared library
# causing all kinds of trouble later on
find_path(PCRE2_INCLUDEDIR NAMES pcre2.h HINTS "C:/Program Files (x86)/PCRE2/include" REQUIRED)
find_library(PCRE2_LIBRARY NAMES pcre2-8-static libpcre2-8.a 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)
set_target_properties(pcre2-8 PROPERTIES IMPORTED_LOCATION ${PCRE2_LIBRARY})
set_target_properties(pcre2-8 PROPERTIES IMPORTED_IMPLIB ${PCRE2_LIBRARY})

View File

@@ -8,8 +8,5 @@ include(CMakeFindDependencyMacro)
find_dependency(Threads)
find_dependency(ZLIB REQUIRED)
if(@REQUIRE_FMT@)
find_dependency(fmt REQUIRED)
endif()
check_required_components(cifpp)

View File

@@ -1,17 +0,0 @@
#include <charconv>
#include <cassert>
#include <cstring>
int main()
{
float v;
char s[] = "1.0";
auto r = std::from_chars(s, s + strlen(s), v);
assert(r.ec == std::errc{});
assert(r.ptr = s + strlen(s));
assert(v == 1.0f);
return 0;
}

18
cmake/test-rx.cpp Normal file
View File

@@ -0,0 +1,18 @@
// 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;
}

View File

@@ -217,7 +217,7 @@ A simple case:
#include <cif++.hpp>
cif::file f("1cbs.cif.gz");
f.load_dictionary("mmcif_pdbx.dic");
f.load_dictionary("mmcif_pdbx");
if (not f.is_valid())
std::cout << "This file is not valid\n";

View File

@@ -32,6 +32,7 @@
#include "cif++/iterator.hpp"
#include "cif++/row.hpp"
#include "cif++/text.hpp"
#include "cif++/validate.hpp"
#include <array>
@@ -50,11 +51,6 @@
namespace cif
{
class validator;
struct category_validator;
struct item_validator;
struct link_validator;
// --------------------------------------------------------------------
// special exceptions
@@ -147,17 +143,7 @@ class category
category() = default; ///< Default constructor
category(std::string_view name); ///< Constructor taking a \a name
/// @brief Constructor creating a category named @a name and filled with @a rows
/// @param name Name for the new category
/// @param rows The data stored in the category
category(std::string_view name, row_initializer &&rows)
: category(name)
{
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,11 +209,6 @@ 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
@@ -332,16 +313,8 @@ 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 = std::vector<key_element_type>;
using key_type = row_initializer;
/// @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
@@ -974,12 +947,6 @@ class category
return insert_impl(cend(), r);
}
void emplace(const_iterator b, const_iterator e)
{
while (b != e)
emplace(*b++);
}
/// @brief Completely erase all rows contained in this category
void clear();
@@ -1112,7 +1079,25 @@ class category
// --------------------------------------------------------------------
/// \brief Return the index number for \a item_name
uint16_t get_item_ix(std::string_view item_name) const;
uint16_t get_item_ix(std::string_view item_name) const
{
uint16_t result;
for (result = 0; result < m_items.size(); ++result)
{
if (iequals(item_name, m_items[result].m_name))
break;
}
if (VERBOSE > 0 and result == m_items.size() and m_cat_validator != nullptr) // validate the name, if it is known at all (since it was not found)
{
auto iv = m_cat_validator->get_validator_for_item(item_name);
if (iv == nullptr)
std::cerr << "Invalid name used '" << item_name << "' is not a known item in " + m_name << '\n';
}
return result;
}
/// @brief Return the name for item with index @a ix
/// @param ix The index number
@@ -1128,7 +1113,28 @@ class category
/// @brief Make sure a item with name @a item_name is known and return its index number
/// @param item_name The name of the item
/// @return The index number of the item
uint16_t add_item(std::string_view item_name);
uint16_t add_item(std::string_view item_name)
{
using namespace std::literals;
uint16_t result = get_item_ix(item_name);
if (result == m_items.size())
{
const item_validator *item_validator = nullptr;
if (m_cat_validator != nullptr)
{
item_validator = m_cat_validator->get_validator_for_item(item_name);
if (item_validator == nullptr)
m_validator->report_error(validation_error::item_not_allowed_in_category, m_name, item_name, false);
}
m_items.emplace_back(item_name, item_validator);
}
return result;
}
/** @brief Remove item name @a colum_name
* @param item_name The item to be removed
@@ -1257,7 +1263,7 @@ class category
{
}
// TODO: NEED TO FIX THIS!
// TODO: NEED TO FIX THIS!
category *linked;
const link_validator *v;
};

View File

@@ -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,13 +290,6 @@ 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();
@@ -308,7 +301,6 @@ class compound_factory
static bool s_use_thread_local_instance;
std::shared_ptr<compound_factory_impl> m_impl;
bool m_report_missing = true;
};
// --------------------------------------------------------------------

View File

@@ -27,7 +27,6 @@
#pragma once
#include "cif++/row.hpp"
#include "cif++/format.hpp"
#include <cassert>
#include <concepts>
@@ -50,49 +49,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
@@ -107,7 +106,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
*/
@@ -116,7 +115,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
*/
@@ -124,7 +123,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
@@ -133,7 +132,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*
@@ -176,13 +175,14 @@ 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,9 +752,28 @@ namespace detail
delete sub;
}
condition_impl *prepare(const category &c) override;
condition_impl *prepare(const category &c) override
{
for (auto &sub : m_sub)
sub = sub->prepare(c);
return this;
}
bool test(row_handle r) const 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;
}
void str(std::ostream &os) const override
{
@@ -801,7 +820,6 @@ 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
@@ -959,9 +977,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);
@@ -979,9 +997,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);
@@ -1001,7 +1019,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
@@ -1013,7 +1031,7 @@ struct empty_type
/**
* @brief A helper to make it possible to have conditions like
*
*
* @code{.cpp}
* "id"_key == cif::null;
* @endcode
@@ -1023,14 +1041,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)
@@ -1039,8 +1057,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)
@@ -1049,8 +1067,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)
@@ -1072,8 +1090,7 @@ 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)
{
// 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)));
return condition(new detail::key_equals_number_condition_impl(key.m_item_name, v));
}
/**
@@ -1120,10 +1137,13 @@ 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; },
cif::format(" > {}", v)));
s.str()));
}
/**
@@ -1132,10 +1152,13 @@ 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; },
cif::format(" >= {}", v)));
s.str()));
}
/**
@@ -1144,10 +1167,13 @@ 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; },
cif::format(" < {}", v)));
s.str()));
}
/**
@@ -1156,10 +1182,13 @@ 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; },
cif::format(" <= {}", v)));
s.str()));
}
/**
@@ -1167,10 +1196,13 @@ 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; },
cif::format(" > {}", v)));
s.str()));
}
/**
@@ -1178,10 +1210,13 @@ 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; },
cif::format(" >= {}", v)));
s.str()));
}
/**
@@ -1189,10 +1224,13 @@ 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; },
cif::format(" < {}", v)));
s.str()));
}
/**
@@ -1200,10 +1238,13 @@ 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; },
cif::format(" <= {}", v)));
s.str()));
}
/**
@@ -1304,7 +1345,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

View File

@@ -29,8 +29,6 @@
#include "cif++/category.hpp"
#include "cif++/forward_decl.hpp"
#include <list>
/** \file datablock.hpp
* Each valid mmCIF file contains at least one @ref cif::datablock.
* A datablock has a name and can contain one or more @ref cif::category "categories"
@@ -106,6 +104,13 @@ class datablock : public std::list<category>
*/
void load_dictionary();
/**
* @brief Load the dictionary named @a dict_name
*
* @param dict_name
*/
void load_dictionary(std::string_view dict_name);
/**
* @brief Set the validator object to @a v
*
@@ -128,6 +133,15 @@ 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
@@ -137,14 +151,6 @@ 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();
// --------------------------------------------------------------------
/**
@@ -183,15 +189,6 @@ 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
@@ -250,4 +247,4 @@ class datablock : public std::list<category>
const validator *m_validator = nullptr;
};
} // namespace cif
} // namespace cif

View File

@@ -38,8 +38,13 @@ namespace cif
{
/**
* @brief Parse the contents of @a is and place content in validator @a v
* @brief Parse the contents of @a is and create a new validator object with name @a name
*/
void parse_dictionary(validator &v, std::istream &is);
validator parse_dictionary(std::string_view name, std::istream &is);
/**
* @brief Extend the definitions in validator @a v with the contents of stream @a is
*/
void extend_dictionary(validator &v, std::istream &is);
} // namespace cif

View File

@@ -210,6 +210,12 @@ class file : public std::list<datablock>
/** Load the data from @a is using validator @a v */
void load(std::istream &is, const validator &v);
/** Load the data from the file specified by @a p using a validator constructed from dictionary @a dict */
void load(const std::filesystem::path &p, std::string_view dict);
/** Load the data from @a is using a validator constructed from dictionary @a dict */
void load(std::istream &is, std::string_view dict);
/** Save the data to the file specified by @a p */
void save(const std::filesystem::path &p) const;
@@ -226,4 +232,4 @@ class file : public std::list<datablock>
}
};
} // namespace cif
} // namespace cif

View File

@@ -26,28 +26,138 @@
#pragma once
#if __has_include(<format>)
#include <format>
#define USE_STD_FORMAT 1
#else
#include <fmt/format.h>
#endif
#include <string>
/** \file format.hpp
*
* Now using cif::format instead of a home grown rip off
* 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.
*/
namespace cif
{
#if USE_STD_FORMAT
using std::format;
#else
using fmt::format;
#endif
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)...);
}
// --------------------------------------------------------------------
/// A streambuf that fills out lines with spaces up until a specified width

View File

@@ -1,33 +1,7 @@
/*-
* 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.
*/
// 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)
#pragma once

View File

@@ -53,12 +53,12 @@ namespace cif
// --------------------------------------------------------------------
/** @brief item is a transient class that is used to pass data into rows
* but it also takes care of formatting data.
*
*
*
*
*
*
* The class cif::item is often used implicitly when creating a row in a category
* using the emplace function.
*
*
* @code{.cpp}
* cif::category cat("my-cat");
* cat.emplace({
@@ -68,12 +68,12 @@ namespace cif
* { "item-4", std::make_optional<int>(42) }, // <- stores an item with value 42
* { "item-5" } // <- stores an item with value .
* });
*
*
* std::cout << cat << '\n';
* @endcode
*
*
* Will result in:
*
*
* @code{.txt}
* _my-cat.item-1 1
* _my-cat.item-2 1.00
@@ -176,7 +176,7 @@ class item
/// \brief constructor for an item with name \a name and as
/// content value \a value
template <typename T, std::enable_if_t<std::is_same_v<T, std::string>, int> = 0>
template<typename T, std::enable_if_t<std::is_same_v<T, std::string>, int> = 0>
item(const std::string_view name, T &&value)
: m_name(name)
, m_value(std::move(value))
@@ -221,8 +221,8 @@ class item
item &operator=(item &&rhs) noexcept = default;
/** @endcond */
std::string_view name() const { return m_name; } ///< Return the name of the item
std::string_view value() const & { return m_value; } ///< Return the value of the item
std::string_view name() const { return m_name; } ///< Return the name of the item
std::string_view value() const & { return m_value; } ///< Return the value of the item
std::string value() const && { return std::move(m_value); } ///< Return the value of the item
/// \brief replace the content of the stored value with \a v
@@ -250,8 +250,6 @@ class item
return value();
}
auto operator<=>(const item &rhs) const = default;
private:
std::string_view m_name;
std::string m_value;
@@ -560,9 +558,7 @@ struct item_handle::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<T> an
auto b = txt.data();
auto e = txt.data() + txt.size();
std::from_chars_result r = (b + 1 < e and *b == '+' and std::isdigit(b[1])) //
? from_chars(b + 1, e, result)
: from_chars(b, e, result);
std::from_chars_result r = (b + 1 < e and *b == '+' and std::isdigit(b[1])) ? selected_charconv<value_type>::from_chars(b + 1, e, result) : selected_charconv<value_type>::from_chars(b, e, result);
if ((bool)r.ec or r.ptr != e)
{
@@ -597,9 +593,7 @@ struct item_handle::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<T> an
auto b = txt.data();
auto e = txt.data() + txt.size();
std::from_chars_result r = (b + 1 < e and *b == '+' and std::isdigit(b[1]))
? from_chars(b + 1, e, v)
: from_chars(b, e, v);
std::from_chars_result r = (b + 1 < e and *b == '+' and std::isdigit(b[1])) ? selected_charconv<value_type>::from_chars(b + 1, e, v) : selected_charconv<value_type>::from_chars(b, e, v);
if ((bool)r.ec or r.ptr != e)
{

View File

@@ -34,7 +34,7 @@
#include <numeric>
#if __cpp_lib_format
# include <format>
#include <format>
#endif
/** @file model.hpp
@@ -106,6 +106,8 @@ class atom
atom_impl(const atom_impl &i) = default;
void prefetch();
int compare(const atom_impl &b) const;
// bool getAnisoU(float anisou[6]) const;
@@ -343,7 +345,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("pdbx_auth_alt_id"); } ///< Return the auth_alt_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_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
@@ -479,8 +481,8 @@ class residue
, m_compound_id(compoundID)
, m_asym_id(asymID)
, m_seq_id(seqID)
, m_pdb_strand_id(authAsymID)
, m_pdb_seq_num(authSeqID)
, m_auth_asym_id(authAsymID)
, m_auth_seq_id(authSeqID)
, m_pdb_ins_code(pdbInsCode)
{
}
@@ -507,9 +509,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_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_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_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
@@ -538,9 +540,6 @@ class residue
/// \brief Return the atom with atom_id @a atomID
atom get_atom_by_atom_id(const std::string &atomID) const;
/// \brief Return the atom with atom_id @a atomID and alternate_id @a altID
atom get_atom_by_atom_id(const std::string &atomID, const std::string &altID) const;
/// \brief Return the list of atoms having ID \a atomID
///
/// This includes all alternate atoms with this ID
@@ -578,7 +577,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_pdb_seq_num == rhs.m_pdb_seq_num);
m_auth_seq_id == rhs.m_auth_seq_id);
}
/// @brief Create a new atom and add it to the list
@@ -592,7 +591,7 @@ class residue
structure *m_structure = nullptr;
std::string m_compound_id, m_asym_id;
int m_seq_id = 0;
std::string m_pdb_strand_id, m_pdb_seq_num, m_pdb_ins_code;
std::string m_auth_asym_id, m_auth_seq_id, m_pdb_ins_code;
std::vector<atom> m_atoms;
/** @endcond */
};
@@ -623,9 +622,6 @@ class monomer : public residue
bool is_first_in_chain() const; ///< Return if this residue is the first residue in the chain
bool is_last_in_chain() const; ///< Return if this residue is the last residue in the chain
const monomer &prev() const; // Return previous monomer in polymer
const monomer &next() const; // Return next monomer in polymer
// convenience
bool has_alpha() const; ///< Return if a alpha value can be calculated (depends on location in chain)
bool has_kappa() const; ///< Return if a kappa value can be calculated (depends on location in chain)
@@ -712,15 +708,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_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
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
private:
structure *m_structure;
std::string m_entity_id;
std::string m_asym_id;
std::string m_pdb_strand_id;
std::string m_auth_asym_id;
};
// --------------------------------------------------------------------
@@ -758,7 +754,7 @@ class sugar : public residue
int num() const
{
int result;
auto r = std::from_chars(m_pdb_seq_num.data(), m_pdb_seq_num.data() + m_pdb_seq_num.length(), result);
auto r = std::from_chars(m_auth_seq_id.data(), m_auth_seq_id.data() + m_auth_seq_id.length(), result);
if ((bool)r.ec)
throw std::runtime_error("The auth_seq_id should be a number for a sugar");
return result;
@@ -857,38 +853,19 @@ class branch : public std::vector<sugar>
std::string m_asym_id, m_entity_id;
};
/** @brief Enumeration for controlling atom selection based on occupancy. */
enum class occupancy_policy
// --------------------------------------------------------------------
/// \brief A still very limited set of options for reading structures
enum class StructureOpenOptions
{
/** @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
SkipHydrogen = 1 << 0 ///< Do not include hydrogen atoms in the structure object
};
struct structure_open_options
/// \brief A way to combine two options. Not very useful as there is only one...
constexpr inline bool operator&(StructureOpenOptions a, StructureOpenOptions 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
};
return static_cast<int>(a) bitand static_cast<int>(b);
}
// --------------------------------------------------------------------
@@ -902,10 +879,10 @@ class structure
{
public:
/// \brief Read the structure from cif::file @a p
structure(file &p, std::size_t modelNr = 1, structure_open_options options = {});
structure(file &p, std::size_t modelNr = 1, StructureOpenOptions options = {});
/// \brief Load the structure from already parsed mmCIF data in @a db
structure(datablock &db, std::size_t modelNr = 1, structure_open_options options = {});
structure(datablock &db, std::size_t modelNr = 1, StructureOpenOptions options = {});
/** @cond */
structure(structure &&s) = default;
@@ -1015,18 +992,18 @@ class structure
/**
* @brief Change residue @a res to a new compound ID optionally
* remapping atoms.
*
*
* A new chem_comp entry as well as an entity is created if needed and
* if the list of @a remappedAtoms is not empty it is used to remap.
*
*
* The array in @a remappedAtoms contains tuples of strings, both
* strings contain an atom_id. The first is the one in the current
* residue and the second is the atom_id that should be used instead.
* If the second string is empty, the atom is removed from the residue.
*
* @param res
* @param newcompound
* @param remappedAtoms
*
* @param res
* @param newcompound
* @param remappedAtoms
*/
void change_residue(residue &res, const std::string &newcompound,
const std::vector<std::tuple<std::string, std::string>> &remappedAtoms);
@@ -1065,16 +1042,6 @@ class structure
/// \param atom The set of item data containing the data for the atoms.
void create_water(row_initializer atom);
/// \brief Create a link, a struct_conn record for two atoms.
///
/// \param a1 Atom 1
/// \param a2 Atom 2
/// \param link_type The struct_conn_type ID for the link
/// \param role The pdbx_role field value
/// \return The ID of the struct_conn record created
std::string create_link(atom a1, atom a2, const std::string &link_type, const std::string &role);
/// \brief Create a new and empty (sugar) branch
branch &create_branch();
@@ -1145,7 +1112,7 @@ class structure
friend polymer;
friend residue;
void load_atoms_for_model(structure_open_options options);
void load_atoms_for_model(StructureOpenOptions options);
std::string insert_compound(const std::string &compoundID, bool is_entity);

View File

@@ -27,7 +27,6 @@
#pragma once
#include "cif++/file.hpp"
#include "cif++/validate.hpp"
#include <system_error>
@@ -104,50 +103,16 @@ 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.
*
* This version uses the audit_conform information and falls back to
* using mmcif_pdbx.dic if not specified.
*
* \param pdbx_file The cif::file that hopefully contains some valid data
* \param file The cif::file that hopefully contains some valid data
* \param dictionary The mmcif dictionary to use
* \result Returns true if the resulting file is valid
*/
bool reconstruct_pdbx(file &pdbx_file);
/** \brief Reconstruct all missing categories for an assumed PDBx file.
*
* Some people believe that simply dumping some atom records is enough.
*
* \param pdbx_file The cif::file that hopefully contains some valid data
* \param v The validator to use
* \result Returns true if the resulting file is valid
*/
bool reconstruct_pdbx(file &pdbx_file, const validator &v);
bool reconstruct_pdbx(file &pdbx_file, std::string_view dictionary = "mmcif_pdbx");
/** \brief This is an extension to cif::validator, use the logic in common
* PDBx files to see if the file is internally consistent.
@@ -160,13 +125,12 @@ bool reconstruct_pdbx(file &pdbx_file, const validator &v);
*
* This function throws a std::system_error in case of an error
*
* \param pdbx_file The input file
* \param v The validator to use
* \param file The input file
* \param dictionary The mmcif dictionary to use
* \result Returns true if the file was valid and consistent
*/
bool is_valid_pdbx_file(const file &pdbx_file,
const validator &v = validator_factory::instance().get("mmcif_pdbx.dic"));
bool is_valid_pdbx_file(const file &pdbx_file, std::string_view dictionary = "mmcif_pdbx");
/** \brief This is an extension to cif::validator, use the logic in common
* PDBx files to see if the file is internally consistent.
@@ -181,6 +145,7 @@ bool is_valid_pdbx_file(const file &pdbx_file,
* default mmcif_pdbx.dic dictionary.
*
* \param file The input file
* \param ec The error_code in case something was wrong
* \result Returns true if the file was valid and consistent
*/
@@ -196,12 +161,12 @@ bool is_valid_pdbx_file(const file &pdbx_file, std::error_code &ec);
* Use the common \ref cif::VERBOSE flag to turn on diagnostic messages.
*
* \param file The input file
* \param v The validator to use
* \param dictionary The dictionary to use
* \param ec The error_code in case something was wrong
* \result Returns true if the file was valid and consistent
*/
bool is_valid_pdbx_file(const file &pdbx_file, const validator &v,
bool is_valid_pdbx_file(const file &pdbx_file, std::string_view dictionary,
std::error_code &ec);
// --------------------------------------------------------------------

View File

@@ -355,35 +355,279 @@ std::string cif_id_for_number(int number);
std::vector<std::string> word_wrap(const std::string &text, std::size_t width);
// --------------------------------------------------------------------
/// \brief std::from_chars for floating point types.
///
/// These are optional, there's a selected_charconv class below that selects
/// the best option to use based on support by the stl library.
///
/// I.e. that in case of GNU < 12 (or something) the cif implementation will
/// be used, all other cases will use the stl version.
template <typename FloatType, std::enable_if_t<std::is_floating_point_v<FloatType>, int> = 0>
std::from_chars_result from_chars(const char *first, const char *last, FloatType &value)
{
std::from_chars_result result{ first, {} };
enum State
{
IntegerSign,
Integer,
Fraction,
ExponentSign,
Exponent
} state = IntegerSign;
int sign = 1;
unsigned long long vi = 0;
int fl = 0, tz = 0;
int exponent_sign = 1;
int exponent = 0;
bool done = false;
while (not done and not (bool)result.ec)
{
char ch = result.ptr != last ? *result.ptr : 0;
++result.ptr;
switch (state)
{
case IntegerSign:
if (ch == '-')
{
sign = -1;
state = Integer;
}
else if (ch == '+')
state = Integer;
else if (ch >= '0' and ch <= '9')
{
vi = ch - '0';
state = Integer;
}
else if (ch == '.')
state = Fraction;
else
result.ec = std::errc::invalid_argument;
break;
case Integer:
if (ch >= '0' and ch <= '9')
vi = 10 * vi + (ch - '0');
else if (ch == 'e' or ch == 'E')
state = ExponentSign;
else if (ch == '.')
state = Fraction;
else
{
done = true;
--result.ptr;
}
break;
case Fraction:
if (ch >= '0' and ch <= '9')
{
vi = 10 * vi + (ch - '0');
if (ch == '0')
tz += 1;
else
{
fl += tz + 1;
tz = 0;
}
}
else if (ch == 'e' or ch == 'E')
state = ExponentSign;
else
{
done = true;
--result.ptr;
}
break;
case ExponentSign:
if (ch == '-')
{
exponent_sign = -1;
state = Exponent;
}
else if (ch == '+')
state = Exponent;
else if (ch >= '0' and ch <= '9')
{
exponent = ch - '0';
state = Exponent;
}
else
result.ec = std::errc::invalid_argument;
break;
case Exponent:
if (ch >= '0' and ch <= '9')
exponent = 10 * exponent + (ch - '0');
else
{
done = true;
--result.ptr;
}
break;
}
}
if (not (bool)result.ec)
{
while (tz-- > 0)
vi /= 10;
long double v = std::pow(10, -fl) * vi * sign;
if (exponent != 0)
v *= std::pow(10, exponent * exponent_sign);
if (std::isnan(v))
result.ec = std::errc::invalid_argument;
else if (std::abs(v) > std::numeric_limits<FloatType>::max())
result.ec = std::errc::result_out_of_range;
value = static_cast<FloatType>(v);
}
return result;
}
/// \brief duplication of std::chars_format for deficient STL implementations
enum class chars_format
{
scientific = 1,
fixed = 2,
// hex,
general = fixed | scientific
};
/// \brief a simplistic implementation of std::to_chars for old STL implementations
template <typename FloatType, std::enable_if_t<std::is_floating_point_v<FloatType>, int> = 0>
std::to_chars_result to_chars(char *first, char *last, FloatType &value, chars_format fmt)
{
int size = static_cast<int>(last - first);
int r = 0;
switch (fmt)
{
case chars_format::scientific:
if constexpr (std::is_same_v<FloatType, long double>)
r = snprintf(first, last - first, "%le", value);
else
r = snprintf(first, last - first, "%e", value);
break;
case chars_format::fixed:
if constexpr (std::is_same_v<FloatType, long double>)
r = snprintf(first, last - first, "%lf", value);
else
r = snprintf(first, last - first, "%f", value);
break;
case chars_format::general:
if constexpr (std::is_same_v<FloatType, long double>)
r = snprintf(first, last - first, "%lg", value);
else
r = snprintf(first, last - first, "%g", value);
break;
}
std::to_chars_result result;
if (r < 0 or r >= size)
result = { first, std::errc::value_too_large };
else
result = { first + r, std::errc() };
return result;
}
/// \brief a simplistic implementation of std::to_chars for old STL implementations
template <typename FloatType, std::enable_if_t<std::is_floating_point_v<FloatType>, int> = 0>
std::to_chars_result to_chars(char *first, char *last, FloatType &value, chars_format fmt, int precision)
{
int size = static_cast<int>(last - first);
int r = 0;
switch (fmt)
{
case chars_format::scientific:
if constexpr (std::is_same_v<FloatType, long double>)
r = snprintf(first, last - first, "%.*le", precision, value);
else
r = snprintf(first, last - first, "%.*e", precision, value);
break;
case chars_format::fixed:
if constexpr (std::is_same_v<FloatType, long double>)
r = snprintf(first, last - first, "%.*lf", precision, value);
else
r = snprintf(first, last - first, "%.*f", precision, value);
break;
case chars_format::general:
if constexpr (std::is_same_v<FloatType, long double>)
r = snprintf(first, last - first, "%.*lg", precision, value);
else
r = snprintf(first, last - first, "%.*g", precision, value);
break;
}
std::to_chars_result result;
if (r < 0 or r >= size)
result = { first, std::errc::value_too_large };
else
result = { first + r, std::errc() };
return result;
}
/// \brief class that uses our implementation of std::from_chars and std::to_chars
template <typename T>
using from_chars_function = decltype(std::from_chars(std::declval<const char *>(), std::declval<const char *>(), std::declval<T &>()));
struct my_charconv
{
/// @brief Simply call our version of std::from_chars
static std::from_chars_result from_chars(const char *a, const char *b, T &d)
{
return cif::from_chars(a, b, d);
}
/// @brief Simply call our version of std::to_chars
static std::to_chars_result to_chars(char *first, char *last, T &value, chars_format fmt)
{
return cif::to_chars(first, last, value, fmt);
}
};
/// \brief class that uses the STL implementation of std::from_chars and std::to_chars
template <typename T>
struct std_charconv
{
/// @brief Simply call std::from_chars
static std::from_chars_result from_chars(const char *a, const char *b, T &d)
{
return std::from_chars(a, b, d);
}
/// @brief Simply call std::to_chars
static std::to_chars_result to_chars(char *first, char *last, T &value, chars_format fmt)
{
return std::to_chars(first, last, value, fmt);
}
};
template <typename T, typename = void>
struct ff_charconv;
/// \brief helper to find a from_chars function
template <typename T>
struct ff_charconv<T, typename std::enable_if_t<std::is_floating_point_v<T>>>
{
static std::from_chars_result from_chars(const char *a, const char *b, T &v);
};
using from_chars_function = decltype(std::from_chars(std::declval<const char *>(), std::declval<const char *>(), std::declval<T &>()));
/**
* @brief Helper to select the best implementation of charconv based on availability of the
* function in the std:: namespace
*
* @tparam T The type for which we want to find a from_chars/to_chars function
*/
template <typename T>
using charconv = typename std::conditional_t<std_experimental::is_detected_v<from_chars_function, T>, std_charconv<T>, ff_charconv<T>>;
template <typename T>
constexpr auto from_chars(const char *s, const char *e, T &v)
{
return charconv<T>::from_chars(s, e, v);
}
using selected_charconv = typename std::conditional_t<std_experimental::is_detected_v<from_chars_function, T>, std_charconv<T>, my_charconv<T>>;
} // namespace cif

View File

@@ -53,7 +53,6 @@
#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
@@ -296,11 +295,6 @@ class progress_bar
*/
void message(const std::string &inMessage);
/**
* @brief Flush the progress bar to the output stream
*/
void flush();
private:
progress_bar(const progress_bar &) = delete;
progress_bar &operator=(const progress_bar &) = delete;

View File

@@ -26,14 +26,12 @@
#pragma once
#include "cif++/category.hpp"
#include "cif++/text.hpp"
#include <cassert>
#include <filesystem>
#include <list>
#include <mutex>
#include <optional>
#include <system_error>
#include <utility>
@@ -50,7 +48,6 @@
namespace cif
{
class category;
struct category_validator;
// --------------------------------------------------------------------
@@ -230,39 +227,38 @@ struct type_validator
{
std::string m_name; ///< The name of the type
DDL_PrimitiveType m_primitive_type; ///< The primitive_type of the type
std::shared_ptr<regex_impl> m_rx; ///< The regular expression for the type
regex_impl *m_rx; ///< The regular expression for the type
type_validator() = delete;
/// @brief Constructor
type_validator(std::string_view name, DDL_PrimitiveType type, std::string_view rx);
/// @brief Copy constructor
type_validator(const type_validator &tv);
type_validator(const type_validator &) = delete;
/// @brief Move constructor
/// @brief Copy constructor
type_validator(type_validator &&rhs)
: m_name(std::move(rhs.m_name))
, m_primitive_type(rhs.m_primitive_type)
{
swap(*this, rhs);
m_rx = std::exchange(rhs.m_rx, nullptr);
}
type_validator &operator=(const type_validator &) = delete;
/// @brief Move constructor
type_validator &operator=(type_validator rhs)
type_validator &operator=(type_validator &&rhs)
{
swap(*this, rhs);
m_name = std::move(rhs.m_name);
m_primitive_type = rhs.m_primitive_type;
m_rx = std::exchange(rhs.m_rx, nullptr);
return *this;
}
/// @brief Destructor
~type_validator();
friend void swap(type_validator &a, type_validator &b)
{
std::swap(a.m_name, b.m_name);
std::swap(a.m_primitive_type, b.m_primitive_type);
std::swap(a.m_rx, b.m_rx);
}
/// @brief Return the sorting order
bool operator<(const type_validator &rhs) const
{
@@ -307,13 +303,13 @@ struct item_alias
*/
struct item_validator
{
std::string m_item_name; ///< The item name
bool m_mandatory; ///< Flag indicating this item is mandatory
const type_validator *m_type; ///< The type for this item
cif::iset m_enums; ///< If filled, the set of allowed values
std::string m_default; ///< If filled, a default value for this item
std::string m_category; ///< The category this item_validator belongs to
std::vector<item_alias> m_aliases; ///< The aliases for this item
std::string m_item_name; ///< The item name
bool m_mandatory; ///< Flag indicating this item is mandatory
const type_validator *m_type; ///< The type for this item
cif::iset m_enums; ///< If filled, the set of allowed values
std::string m_default; ///< If filled, a default value for this item
category_validator *m_category = nullptr; ///< The category_validator this item_validator belongs to
std::vector<item_alias> m_aliases; ///< The aliases for this item
/// @brief Compare based on the name
bool operator<(const item_validator &rhs) const
@@ -400,48 +396,24 @@ class validator
*
* @param name The name of the underlying dictionary
*/
validator()
: m_audit_conform("audit_conform")
validator(std::string_view name)
: m_name(name)
{
}
/**
* @brief Construct a new validator object
*
* @param name The name of the underlying dictionary
* @param is The data to parse
*/
validator(std::istream &is)
: m_audit_conform("audit_conform")
{
parse(is);
}
/// @brief destructor
~validator() = default;
validator(const validator &rhs);
validator(const validator &rhs) = delete;
validator &operator=(const validator &rhs) = delete;
/// @brief move constructor
validator(validator &&rhs)
{
swap(*this, rhs);
}
validator(validator &&rhs) = default;
validator &operator=(validator rhs)
{
swap(*this, rhs);
return *this;
}
friend void swap(validator &a, validator &b) noexcept;
/// @brief move assignment operator
validator &operator=(validator &&rhs) = default;
friend class dictionary_parser;
friend class validator_factory;
/// @brief Parse dictionary in @a is and put content in this validator, optionally extending it
/// @param is The stream containing a valid cif dictionary
void parse(std::istream &is);
/// @brief Add type_validator @a v to the list of type validators
void add_type_validator(type_validator &&v);
@@ -484,22 +456,18 @@ class validator
void report_error(std::error_code ec, std::string_view category,
std::string_view item, bool fatal = true) const;
/// @brief Write out the audit_conform data for this validator
/// @param audit_conform
void fill_audit_conform(category &audit_conform) const;
const std::string &name() const { return m_name; } ///< Get the name of this validator
void set_name(const std::string &name) { m_name = name; } ///< Set the name of this validator
/// @brief Return true if this validator matches @a audit_conform
bool matches_audit_conform(const category &audit_conform) const;
/// @brief Add info
void append_audit_conform(const std::string &name, const std::optional<std::string> &version);
const std::string &version() const { return m_version; } ///< Get the version of this validator
void set_version(const std::string &version) { m_version = version; } ///< Set the version of this validator
private:
// name is fully qualified here:
item_validator *get_validator_for_item(std::string_view name) const;
category m_audit_conform;
std::string m_name;
std::string m_version;
bool m_strict = false;
std::set<type_validator> m_type_validators;
std::set<category_validator> m_category_validators;
@@ -519,27 +487,16 @@ class validator_factory
/// @brief Return the singleton instance
static validator_factory &instance();
/// @brief Return validator with info recorded in @a audit_conform
const validator &get(const category &audit_conform);
/// @brief Return the validator with name @a dictionary_name
const validator &operator[](std::string_view dictionary_name);
/// @brief Return the single-file validator with name @a dictionary_name
const validator &get(std::string_view dictionary_name);
/// @brief Return true if the version @a found is equal or higher than @a expected for dictionary @a name
static bool check_version(std::string_view name, std::string_view expected, std::string_view found);
/// @brief Add validator @a v to the list of known validators
const validator &add(validator &&v)
{
std::unique_lock lock(m_mutex);
return m_validators.emplace_back(std::move(v));
}
/// @brief Construct a new validator with name @a name from the data in @a is
const validator &construct_validator(std::string_view name, std::istream &is);
private:
validator_factory() = default;
// --------------------------------------------------------------------
/// @brief Construct a new validator with name @a name from the data in @a is with at least version @a version if specified
validator construct_validator(std::string_view name, std::optional<std::string> version);
validator_factory() = default;
std::mutex m_mutex;
std::list<validator> m_validators;

View File

@@ -1,316 +0,0 @@
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2025 Maarten L. Hekkelman
#
# 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.
# A simplified wrapper CMakeLists.txt file for PCRE2
#
# This will generate an OBJECT library so it can be linked into another library
cmake_minimum_required(VERSION 3.25)
include(FetchContent)
project(pcre2s VERSION 1.0.0 LANGUAGES C CXX)
# The original code:
file(DOWNLOAD https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.46/pcre2-10.46.tar.gz
${CMAKE_CURRENT_BINARY_DIR}/pcre2-code.tgz
EXPECTED_HASH SHA256=8d28d7f2c3b970c3a4bf3776bcbb5adfc923183ce74bc8df1ebaad8c1985bd07)
file(ARCHIVE_EXTRACT INPUT ${CMAKE_CURRENT_BINARY_DIR}/pcre2-code.tgz
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
set(PCRE2_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/pcre2-10.46)
set(PCRE2_MAJOR 10)
set(PCRE2_MINOR 46)
set(PCRE2_VERSION "${PCRE2_MAJOR}.${PCRE2_MINOR}")
set(PCRE2_DATE "2024-06-09")
# Some needed configuration options
# option(PCRE2_BUILD_PCRE2_8 "Build 8 bit PCRE2 library" ON)
# option(PCRE2_BUILD_PCRE2_16 "Build 16 bit PCRE2 library" OFF)
# option(PCRE2_BUILD_PCRE2_32 "Build 32 bit PCRE2 library" OFF)
option(PCRE2_STATIC_PIC "Build the static library with the option position independent code enabled." OFF)
set(PCRE2_NEWLINE "LF" CACHE STRING "What to recognize as a newline (one of CR, LF, CRLF, ANY, ANYCRLF, NUL)." FORCE)
set_property(CACHE PCRE2_NEWLINE PROPERTY STRINGS "CR" "LF" "CRLF" "ANY" "ANYCRLF" "NUL")
set(PCRE2_LINK_SIZE "2" CACHE STRING "Internal link size (2, 3 or 4 allowed). See LINK_SIZE in config.h.in for details.")
set_property(CACHE PCRE2_LINK_SIZE PROPERTY STRINGS "2" "3" "4")
set(PCRE2_PARENS_NEST_LIMIT "250" CACHE STRING "Default nested parentheses limit. See PARENS_NEST_LIMIT in config.h.in for details.")
set(PCRE2_HEAP_LIMIT "20000000" CACHE STRING "Default limit on heap memory (kibibytes). See HEAP_LIMIT in config.h.in for details.")
set(PCRE2_MAX_VARLOOKBEHIND "255" CACHE STRING "Default limit on variable lookbehinds.")
set(PCRE2_MATCH_LIMIT "10000000" CACHE STRING "Default limit on internal looping. See MATCH_LIMIT in config.h.in for details.")
set(PCRE2_MATCH_LIMIT_DEPTH "MATCH_LIMIT" CACHE STRING "Default limit on internal depth of search. See MATCH_LIMIT_DEPTH in config.h.in for details.")
set(PCRE2GREP_BUFSIZE "20480" CACHE STRING "Buffer starting size parameter for pcre2grep. See PCRE2GREP_BUFSIZE in config.h.in for details.")
set(PCRE2GREP_MAX_BUFSIZE "1048576" CACHE STRING "Buffer maximum size parameter for pcre2grep. See PCRE2GREP_MAX_BUFSIZE in config.h.in for details.")
set(PCRE2_SUPPORT_JIT OFF CACHE BOOL "Enable support for Just-in-time compiling.")
if(${CMAKE_SYSTEM_NAME} MATCHES Linux|NetBSD)
set(PCRE2_SUPPORT_JIT_SEALLOC OFF CACHE BOOL "Enable SELinux compatible execmem allocator in JIT (experimental).")
else()
set(PCRE2_SUPPORT_JIT_SEALLOC IGNORE)
endif()
set(PCRE2GREP_SUPPORT_JIT ON CACHE BOOL "Enable use of Just-in-time compiling in pcre2grep.")
set(PCRE2GREP_SUPPORT_CALLOUT ON CACHE BOOL "Enable callout string support in pcre2grep.")
set(PCRE2GREP_SUPPORT_CALLOUT_FORK ON CACHE BOOL "Enable callout string fork support in pcre2grep.")
set(PCRE2_SUPPORT_UNICODE ON CACHE BOOL "Enable support for Unicode and UTF-8/UTF-16/UTF-32 encoding.")
set(PCRE2_SUPPORT_BSR_ANYCRLF OFF CACHE BOOL "ON=Backslash-R matches only LF CR and CRLF, OFF=Backslash-R matches all Unicode Linebreaks")
set(PCRE2_NEVER_BACKSLASH_C OFF CACHE BOOL "If ON, backslash-C (upper case C) is locked out.")
set(PCRE2_SUPPORT_VALGRIND OFF CACHE BOOL "Enable Valgrind support.")
if(MINGW)
option(NON_STANDARD_LIB_PREFIX "ON=Shared libraries built in mingw will be named pcre2.dll, etc., instead of libpcre2.dll, etc." OFF)
option(NON_STANDARD_LIB_SUFFIX "ON=Shared libraries built in mingw will be named libpcre2-0.dll, etc., instead of libpcre2.dll, etc." OFF)
endif()
#
set(NEWLINE_DEFAULT "")
if(PCRE2_NEWLINE STREQUAL "CR")
set(NEWLINE_DEFAULT "1")
elseif(PCRE2_NEWLINE STREQUAL "LF")
set(NEWLINE_DEFAULT "2")
elseif(PCRE2_NEWLINE STREQUAL "CRLF")
set(NEWLINE_DEFAULT "3")
elseif(PCRE2_NEWLINE STREQUAL "ANY")
set(NEWLINE_DEFAULT "4")
elseif(PCRE2_NEWLINE STREQUAL "ANYCRLF")
set(NEWLINE_DEFAULT "5")
elseif(PCRE2_NEWLINE STREQUAL "NUL")
set(NEWLINE_DEFAULT "6")
else()
message(FATAL_ERROR "The PCRE2_NEWLINE variable must be set to one of the following values: \"LF\", \"CR\", \"CRLF\", \"ANY\", \"ANYCRLF\".")
endif()
# Some tests
include(CheckCSourceCompiles)
include(CheckFunctionExists)
include(CheckSymbolExists)
include(CheckIncludeFile)
check_include_file(assert.h HAVE_ASSERT_H)
check_include_file(dirent.h HAVE_DIRENT_H)
check_include_file(sys/stat.h HAVE_SYS_STAT_H)
check_include_file(sys/types.h HAVE_SYS_TYPES_H)
check_include_file(unistd.h HAVE_UNISTD_H)
check_include_file(windows.h HAVE_WINDOWS_H)
check_symbol_exists(bcopy "strings.h" HAVE_BCOPY)
check_symbol_exists(memfd_create "sys/mman.h" HAVE_MEMFD_CREATE)
check_symbol_exists(memmove "string.h" HAVE_MEMMOVE)
check_symbol_exists(secure_getenv "stdlib.h" HAVE_SECURE_GETENV)
check_symbol_exists(strerror "string.h" HAVE_STRERROR)
check_c_source_compiles(
"int main(void) { char buf[128] __attribute__((uninitialized)); (void)buf; return 0; }"
HAVE_ATTRIBUTE_UNINITIALIZED
)
check_c_source_compiles(
[=[
extern __attribute__ ((visibility ("default"))) int f(void);
int main(void) { return f(); }
int f(void) { return 42; }
]=]
HAVE_VISIBILITY
)
if(HAVE_VISIBILITY)
set(PCRE2_EXPORT [=[__attribute__ ((visibility ("default")))]=])
else()
set(PCRE2_EXPORT)
endif()
check_c_source_compiles("int main(void) { __assume(1); return 0; }" HAVE_BUILTIN_ASSUME)
check_c_source_compiles(
[=[
#include <stddef.h>
int main(void) { int a,b; size_t m; __builtin_mul_overflow(a,b,&m); return 0; }
]=]
HAVE_BUILTIN_MUL_OVERFLOW
)
check_c_source_compiles(
"int main(int c, char *v[]) { if (c) __builtin_unreachable(); return (int)(*v[0]); }"
HAVE_BUILTIN_UNREACHABLE
)
# # Check whether Intel CET is enabled, and if so, adjust compiler flags. This
# # code was written by PH, trying to imitate the logic from the autotools
# # configuration.
# check_c_source_compiles(
# [=[
# #ifndef __CET__
# #error CET is not enabled
# #endif
# int main() { return 0; }
# ]=]
# INTEL_CET_ENABLED
# )
# if(INTEL_CET_ENABLED)
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mshstk")
# endif()
# Set up some dependencies first
configure_file(
${PCRE2_SOURCE_DIR}/src/pcre2_chartables.c.dist
${CMAKE_CURRENT_BINARY_DIR}/pcre2_chartables.c
COPYONLY
)
configure_file(
${PCRE2_SOURCE_DIR}/config-cmake.h.in
${CMAKE_CURRENT_BINARY_DIR}/interface/config.h
@ONLY
)
configure_file(
${PCRE2_SOURCE_DIR}/src/pcre2.h.in
${CMAKE_CURRENT_BINARY_DIR}/interface/pcre2.h
@ONLY
)
# Define our library
list(APPEND PCRE2_HEADERS
${CMAKE_CURRENT_BINARY_DIR}/interface/pcre2.h)
list(APPEND PCRE2_SOURCES
${PCRE2_SOURCE_DIR}/src/pcre2_auto_possess.c
${CMAKE_CURRENT_BINARY_DIR}/pcre2_chartables.c
${PCRE2_SOURCE_DIR}/src/pcre2_chkdint.c
${PCRE2_SOURCE_DIR}/src/pcre2_compile.c
${PCRE2_SOURCE_DIR}/src/pcre2_compile_class.c
${PCRE2_SOURCE_DIR}/src/pcre2_config.c
${PCRE2_SOURCE_DIR}/src/pcre2_context.c
${PCRE2_SOURCE_DIR}/src/pcre2_convert.c
${PCRE2_SOURCE_DIR}/src/pcre2_dfa_match.c
${PCRE2_SOURCE_DIR}/src/pcre2_error.c
${PCRE2_SOURCE_DIR}/src/pcre2_extuni.c
${PCRE2_SOURCE_DIR}/src/pcre2_find_bracket.c
${PCRE2_SOURCE_DIR}/src/pcre2_jit_compile.c
${PCRE2_SOURCE_DIR}/src/pcre2_maketables.c
${PCRE2_SOURCE_DIR}/src/pcre2_match.c
${PCRE2_SOURCE_DIR}/src/pcre2_match_data.c
${PCRE2_SOURCE_DIR}/src/pcre2_newline.c
${PCRE2_SOURCE_DIR}/src/pcre2_ord2utf.c
${PCRE2_SOURCE_DIR}/src/pcre2_pattern_info.c
${PCRE2_SOURCE_DIR}/src/pcre2_script_run.c
${PCRE2_SOURCE_DIR}/src/pcre2_serialize.c
${PCRE2_SOURCE_DIR}/src/pcre2_string_utils.c
${PCRE2_SOURCE_DIR}/src/pcre2_study.c
${PCRE2_SOURCE_DIR}/src/pcre2_substitute.c
${PCRE2_SOURCE_DIR}/src/pcre2_substring.c
${PCRE2_SOURCE_DIR}/src/pcre2_tables.c
${PCRE2_SOURCE_DIR}/src/pcre2_ucd.c
${PCRE2_SOURCE_DIR}/src/pcre2_valid_utf.c
${PCRE2_SOURCE_DIR}/src/pcre2_xclass.c
)
add_library(pcre2s OBJECT)
target_sources(pcre2s
PRIVATE ${PCRE2_SOURCES}
PUBLIC
FILE_SET pcre2_headers TYPE HEADERS
BASE_DIRS ${PCRE2_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/interface
FILES ${PCRE2_HEADERS}
)
target_compile_definitions(pcre2s PUBLIC PCRE2_CODE_UNIT_WIDTH=8 HAVE_CONFIG_H)
if(NOT BUILD_SHARED_LIBS)
target_compile_definitions(pcre2s PUBLIC PCRE2_STATIC)
endif()
target_include_directories(pcre2s PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/interface ${PCRE2_SOURCE_DIR}/src)
if(PCRE2_STATIC_PIC)
set_target_properties(pcre2s PROPERTIES POSITION_INDEPENDENT_CODE 1)
endif()
# # Installation and config files
# include(CMakePackageConfigHelpers)
# include(GenerateExportHeader)
# # Install rules
# install(TARGETS pcre2s
# EXPORT pcre2s
# FILE_SET pcre2_headers DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
# if(MSVC AND BUILD_SHARED_LIBS)
# install(
# FILES $<TARGET_PDB_FILE:pcre2s>
# DESTINATION ${CMAKE_INSTALL_LIBDIR}
# OPTIONAL)
# endif()
# install(EXPORT pcre2s
# NAMESPACE pcre2s::
# FILE "pcre2s-targets.cmake"
# DESTINATION lib/cmake/pcre2s)
# configure_package_config_file(
# ${CMAKE_CURRENT_SOURCE_DIR}/pcre2s-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/pcre2s/pcre2s-config.cmake
# INSTALL_DESTINATION lib/cmake/pcre2s)
# install(
# FILES "${CMAKE_CURRENT_BINARY_DIR}/pcre2s/pcre2s-config.cmake"
# "${CMAKE_CURRENT_BINARY_DIR}/pcre2s/pcre2s-config-version.cmake"
# DESTINATION lib/cmake/pcre2s)
# set_target_properties(
# pcre2s
# PROPERTIES VERSION ${PCRE2_VERSION}
# SOVERSION ${PCRE2_VERSION}
# INTERFACE_pcre2s_MAJOR_VERSION ${PCRE2_MAJOR})
# set_property(
# TARGET pcre2s
# APPEND
# PROPERTY COMPATIBLE_INTERFACE_STRING pcre2s_MAJOR_VERSION)
# write_basic_package_version_file(
# "${CMAKE_CURRENT_BINARY_DIR}/pcre2s/pcre2s-config-version.cmake"
# VERSION "${PCRE2_VERSION}"
# COMPATIBILITY AnyNewerVersion)
# # Testing
# if(PROJECT_IS_TOP_LEVEL)
# include(CTest)
# if(BUILD_TESTING)
# add_subdirectory(test)
# endif()
# endif()

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,6 @@
#include "cif++/datablock.hpp"
#include "cif++/parser.hpp"
#include "cif++/utilities.hpp"
#include "cif++/validate.hpp"
#include <numeric>
#include <stack>
@@ -92,7 +91,7 @@ class row_comparator
return d;
}
int operator()(const category &cat, const category::key_type &a, const row *b) const
int operator()(const category &cat, const row_initializer &a, const row *b) const
{
assert(b);
@@ -105,11 +104,10 @@ 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();
if (not (ai->may_be_null and rhb[k].empty()))
d = f(ka, kb);
d = f(ka, kb);
if (d != 0)
break;
@@ -143,7 +141,7 @@ class category_index
}
row *find(const category &cat, row *k) const;
row *find_by_value(const category &cat, const category::key_type &k) const;
row *find_by_value(const category &cat, row_initializer k) const;
void insert(category &cat, row *r);
void erase(category &cat, row *r);
@@ -353,19 +351,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, const category::key_type &k) const
row *category_index::find_by_value(const category &cat, row_initializer k) const
{
// sort the values in k first
category::key_type k2;
row_initializer 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(std::string{ fld }, "");
k2.emplace_back(fld, "");
else
k2.emplace_back(*ki);
}
@@ -544,49 +542,6 @@ category::~category()
// --------------------------------------------------------------------
uint16_t category::get_item_ix(std::string_view item_name) const
{
uint16_t result;
for (result = 0; result < m_items.size(); ++result)
{
if (iequals(item_name, m_items[result].m_name))
break;
}
if (VERBOSE > 0 and result == m_items.size() and m_cat_validator != nullptr) // validate the name, if it is known at all (since it was not found)
{
auto iv = m_cat_validator->get_validator_for_item(item_name);
if (iv == nullptr)
std::cerr << "Invalid name used '" << item_name << "' is not a known item in " + m_name << '\n';
}
return result;
}
uint16_t category::add_item(std::string_view item_name)
{
using namespace std::literals;
uint16_t result = get_item_ix(item_name);
if (result == m_items.size())
{
const item_validator *item_validator = nullptr;
if (m_cat_validator != nullptr)
{
item_validator = m_cat_validator->get_validator_for_item(item_name);
if (item_validator == nullptr)
m_validator->report_error(validation_error::item_not_allowed_in_category, m_name, item_name, false);
}
m_items.emplace_back(item_name, item_validator);
}
return result;
}
void category::remove_item(std::string_view item_name)
{
for (std::size_t ix = 0; ix < m_items.size(); ++ix)
@@ -915,24 +870,6 @@ 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)
@@ -1339,7 +1276,8 @@ std::string category::get_unique_value(std::string_view item_name)
// brain-dead implementation
for (std::size_t ix = 0; ix < size(); ++ix)
{
result = cif_id_for_number(static_cast<int>(ix));
// result = m_name + "-" + std::to_string(ix);
result = cif_id_for_number(ix);
if (not contains(key(item_name) == result))
break;
}
@@ -2235,4 +2173,4 @@ bool category::operator==(const category &rhs) const
return true;
}
} // namespace cif
} // namespace cif

View File

@@ -299,14 +299,37 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
std::shared_lock lock(mMutex);
compound *result = nullptr;
// walk the list, see if any of the implementations has the compound already
for (auto impl = shared_from_this(); impl; impl = impl->m_next)
{
result = impl->create(id);
if (result != nullptr)
for (auto cmp : impl->m_compounds)
{
if (iequals(cmp->id(), id))
{
result = cmp;
break;
}
}
if (result)
break;
}
if (result == nullptr and
find_if(m_missing.begin(), m_missing.end(), [&id](const std::string &m_id) { return cif::iequals(id, m_id); }) == m_missing.end())
{
for (auto impl = shared_from_this(); impl; impl = impl->m_next)
{
result = impl->create(id);
if (result != nullptr)
break;
}
if (result == nullptr)
m_missing.emplace_back(id);
}
return result;
}
@@ -337,7 +360,7 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
cif::parser::datablock_index m_index;
std::vector<compound *> m_compounds;
cif::iset m_missing;
std::vector<std::string> m_missing;
std::shared_ptr<compound_factory_impl> m_next;
};
@@ -358,14 +381,6 @@ compound_factory_impl::compound_factory_impl(const fs::path &file, std::shared_p
compound *compound_factory_impl::create(const std::string &id)
{
// shortcut
if (m_missing.contains(id))
return nullptr;
if (auto i = find_if(m_compounds.begin(), m_compounds.end(), [id](compound *c) { return c->id() == id; }); i != m_compounds.end())
return *i;
compound *result = nullptr;
std::unique_ptr<std::istream> ccd;
@@ -434,9 +449,6 @@ compound *compound_factory_impl::create(const std::string &id)
}
}
if (result == nullptr)
m_missing.insert(id);
return result;
}
@@ -449,6 +461,20 @@ class local_compound_factory_impl : public compound_factory_impl
: compound_factory_impl(next)
, m_local_file(file)
{
// const std::regex peptideRx("(?:[lmp]-)?peptide", std::regex::icase);
// for (const auto &[id, name, threeLetterCode, group] :
// file["comp_list"]["chem_comp"].rows<std::string, std::string, std::string, std::string>("id", "name", "three_letter_code", "group"))
// {
// auto &rdb = m_local_file["comp_" + id];
// if (rdb.empty())
// {
// // std::cerr << "Missing data in restraint file for id " + id + '\n';
// continue;
// }
// construct_compound(rdb, id, name, threeLetterCode, group);
// }
}
compound *create(const std::string &id) override;
@@ -462,12 +488,6 @@ class local_compound_factory_impl : public compound_factory_impl
compound *local_compound_factory_impl::create(const std::string &id)
{
if (m_missing.contains(id))
return nullptr;
if (auto i = find_if(m_compounds.begin(), m_compounds.end(), [id](compound *c) { return c->id() == id; }); i != m_compounds.end())
return *i;
compound *result = nullptr;
for (auto &db : m_local_file)
@@ -480,13 +500,10 @@ compound *local_compound_factory_impl::create(const std::string &id)
try
{
const auto &[id2, name, threeLetterCode, group] =
const auto &[id, name, threeLetterCode, group] =
chem_comp->front().get<std::string, std::string, std::string, std::string>("id", "name", "three_letter_code", "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);
result = construct_compound(db, id, name, threeLetterCode, group);
}
catch (const std::exception &ex)
{
@@ -497,9 +514,6 @@ compound *local_compound_factory_impl::create(const std::string &id)
}
}
if (result == nullptr)
m_missing.insert(id);
return result;
}
@@ -512,13 +526,9 @@ compound *local_compound_factory_impl::construct_compound(const datablock &rdb,
int formal_charge = 0;
std::map<std::string,std::size_t> formula_data;
for (std::size_t ord = 1; const auto &[atom_id, type_symbol, type, charge, x, y, z, xi, yi, zi] :
rdb["chem_comp_atom"].rows<std::string, std::string, std::string, int,
std::optional<float>, std::optional<float>, std::optional<float>,
std::optional<float>, std::optional<float>, std::optional<float>>(
"atom_id", "type_symbol", "type", "charge",
"model_Cartn_x", "model_Cartn_y", "model_Cartn_z",
"pdbx_model_Cartn_x_ideal", "pdbx_model_Cartn_y_ideal", "pdbx_model_Cartn_z_ideal"))
for (std::size_t ord = 1; const auto &[atom_id, type_symbol, type, charge, x, y, z] :
rdb["chem_comp_atom"].rows<std::string, std::string, std::string, int, float, float, float>(
"atom_id", "type_symbol", "type", "charge", "x", "y", "z"))
{
auto atom = cif::atom_type_traits(type_symbol);
formula_weight += atom.weight();
@@ -530,9 +540,9 @@ compound *local_compound_factory_impl::construct_compound(const datablock &rdb,
{ "atom_id", atom_id },
{ "type_symbol", type_symbol },
{ "charge", charge },
{ "model_Cartn_x", x.has_value() ? x : xi, 3 },
{ "model_Cartn_y", y.has_value() ? y : yi, 3 },
{ "model_Cartn_z", z.has_value() ? z : zi, 3 },
{ "model_Cartn_x", x, 3 },
{ "model_Cartn_y", y, 3 },
{ "model_Cartn_z", z, 3 },
{ "pdbx_ordinal", ord++ }
});
@@ -760,7 +770,8 @@ bool compound_factory::is_monomer(std::string_view res_name) const
void compound_factory::report_missing_compound(std::string_view compound_id)
{
if (std::exchange(m_report_missing, false))
static bool s_reported = false;
if (std::exchange(s_reported, true) == false)
{
using namespace cif::colour;
@@ -784,7 +795,7 @@ void compound_factory::report_missing_compound(std::string_view compound_id)
<< "in /var/cache/libcifpp using the following commands:\n\n"
<< "curl -o " << CACHE_DIR << "/components.cif https://files.wwpdb.org/pub/pdb/data/monomers/components.cif\n"
<< "curl -o " << CACHE_DIR << "/mmcif_pdbx.dic https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic\n"
<< "curl -o " << CACHE_DIR << "/mmcif_ma.dic https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_ma.dic\n\n";
<< "curl -o " << CACHE_DIR << "/mmcif_ma.dic https://github.com/ihmwg/ModelCIF/raw/master/dist/mmcif_ma.dic\n\n";
#endif
if (m_impl)

View File

@@ -24,9 +24,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++/condition.hpp"
#include "cif++/category.hpp"
#include "cif++/validate.hpp"
#include "cif++/condition.hpp"
namespace cif
{
@@ -61,52 +60,6 @@ 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)
{
@@ -131,8 +84,7 @@ namespace detail
c.key_item_indices().contains(m_item_ix) and
c.key_item_indices().size() == 1)
{
item v(m_item_name, m_value);
m_single_hit = c[{ { m_item_name, std::string{ v.value() }, false } }];
m_single_hit = c[{ { m_item_name, m_value } }];
}
return this;
@@ -146,8 +98,7 @@ 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;
@@ -167,8 +118,7 @@ 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;
}
@@ -186,12 +136,11 @@ 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;
@@ -208,99 +157,6 @@ 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;
@@ -324,7 +180,7 @@ void condition::prepare(const category &c)
{
if (m_impl)
m_impl = m_impl->prepare(c);
m_prepared = true;
}

View File

@@ -26,10 +26,6 @@
#include "cif++/datablock.hpp"
#include "cif++/validate.hpp"
#include <exception>
namespace cif
{
@@ -46,17 +42,31 @@ void datablock::load_dictionary()
{
if (auto *audit_conform = get("audit_conform"); audit_conform and not audit_conform->empty())
{
try
std::string name = audit_conform->front().get<std::string>("dict_name");
if (name == "mmcif_pdbx_v50")
name = "mmcif_pdbx.dic"; // we had a bug here in libcifpp...
if (not name.empty())
{
set_validator(&validator_factory::instance().get(*audit_conform));
}
catch (const std::exception &ex)
{
std::clog << ex.what() << '\n';
try
{
load_dictionary(name);
}
catch (const std::exception &ex)
{
if (VERBOSE)
std::cerr << "Failed to load dictionary " << std::quoted(name) << ": " << ex.what() << '\n';
}
}
}
}
void datablock::load_dictionary(std::string_view name)
{
set_validator(&validator_factory::instance()[name]);
}
void datablock::set_validator(const validator *v)
{
m_validator = v;
@@ -90,6 +100,40 @@ bool datablock::is_valid() const
return result;
}
bool datablock::is_valid()
{
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;
// Add or remove the audit_conform block here.
if (result)
{
// If the dictionary declares an audit_conform category, put it in,
// but only if it does not exist already!
if (m_validator->get_validator_for_category("audit_conform") != nullptr)
{
auto &audit_conform = operator[]("audit_conform");
audit_conform.clear();
audit_conform.emplace({
// clang-format off
{ "dict_name", m_validator->name() },
{ "dict_version", m_validator->version() }
// clang-format on
});
}
}
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;
@@ -103,46 +147,6 @@ bool datablock::validate_links() const
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 (is_valid())
{
// If the dictionary declares an audit_conform category, put it in,
// but only if it does not exist already!
if (auto audit_conform = get("audit_conform");
audit_conform != nullptr and m_validator->get_validator_for_category("audit_conform") != nullptr)
{
audit_conform->clear();
m_validator->fill_audit_conform(*audit_conform);
}
}
else
result = false;
return result;
}
// --------------------------------------------------------------------
category &datablock::operator[](std::string_view name)
@@ -199,7 +203,7 @@ std::tuple<datablock::iterator, bool> datablock::emplace(std::string_view name)
if (is_new)
{
i = insert(end(), { name });
i = insert(end(), {name});
i->set_validator(m_validator, *this);
}
@@ -468,4 +472,4 @@ bool datablock::operator==(const datablock &rhs) const
return true;
}
} // namespace cif
} // namespace cif

View File

@@ -28,9 +28,6 @@
#include "cif++/dictionary_parser.hpp"
#include "cif++/file.hpp"
#include "cif++/parser.hpp"
#include <exception>
#include <iomanip>
#include <stdexcept>
namespace cif
{
@@ -49,7 +46,7 @@ class dictionary_parser : public parser
void load_dictionary()
{
std::unique_ptr<datablock> dict;
auto savedDatablock = std::exchange(m_datablock, nullptr);
auto savedDatablock = m_datablock;
try
{
@@ -78,9 +75,6 @@ class dictionary_parser : public parser
error(ex.what());
}
if (m_datablock == nullptr)
throw std::runtime_error("Dictionary file is empty?");
// store all validators
for (auto &ic : mCategoryValidators)
m_validator.add_category_validator(std::move(ic));
@@ -102,10 +96,14 @@ class dictionary_parser : public parser
link_items();
// store meta information
if (auto dictionary = m_datablock->get("dictionary"); dictionary != nullptr and not dictionary->empty())
datablock::iterator info;
bool is_new;
std::tie(info, is_new) = m_datablock->emplace("dictionary");
if (not is_new and not info->empty())
{
const auto &[name, version] = dictionary->front().get<std::string,std::optional<std::string>>("title", "version");
m_validator.append_audit_conform(name, version);
auto r = info->front();
m_validator.set_name(r["title"].as<std::string>());
m_validator.set_version(r["version"].as<std::string>());
}
m_datablock = savedDatablock;
@@ -254,7 +252,7 @@ class dictionary_parser : public parser
auto vi = find(ivs.begin(), ivs.end(), item_validator{ item_name });
if (vi == ivs.end())
ivs.push_back(item_validator{ item_name, iequals(mandatory, "yes"), tv, ess, defaultValue, cat_name, std::move(aliases) });
ivs.push_back(item_validator{ item_name, iequals(mandatory, "yes"), tv, ess, defaultValue, nullptr, std::move(aliases) });
else
{
// need to update the itemValidator?
@@ -352,7 +350,7 @@ class dictionary_parser : public parser
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
key_type key{ piv->m_category, civ->m_category, link_group_id };
key_type key{ piv->m_category->m_name, civ->m_category->m_name, link_group_id };
if (not linkIndex.count(key))
{
linkIndex[key] = linkKeys.size();
@@ -380,7 +378,7 @@ class dictionary_parser : public parser
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
key_type key{ piv->m_category, civ->m_category, 0 };
key_type key{ piv->m_category->m_name, civ->m_category->m_name, 0 };
if (not linkIndex.count(key))
{
linkIndex[key] = linkKeys.size();
@@ -479,7 +477,18 @@ class dictionary_parser : public parser
// --------------------------------------------------------------------
void parse_dictionary(validator &v, std::istream &is)
validator parse_dictionary(std::string_view name, std::istream &is)
{
validator result(name);
file f;
dictionary_parser p(result, is, f);
p.load_dictionary();
return result;
}
void extend_dictionary(validator &v, std::istream &is)
{
file f;
dictionary_parser p(v, is, f);

View File

@@ -25,7 +25,6 @@
*/
#include "cif++/file.hpp"
#include "cif++/condition.hpp"
#include "cif++/gzio.hpp"
namespace cif
@@ -47,16 +46,8 @@ bool file::is_valid()
{
bool result = not empty();
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;
}
for (auto &d : *this)
result = d.is_valid() and result;
if (result)
result = validate_links();
@@ -74,6 +65,42 @@ bool file::validate_links() const
return result;
}
// void file::load_dictionary()
// {
// if (not empty())
// {
// auto *audit_conform = front().get("audit_conform");
// if (audit_conform and not audit_conform->empty())
// {
// std::string name = audit_conform->front().get<std::string>("dict_name");
// if (name == "mmcif_pdbx_v50")
// name = "mmcif_pdbx.dic"; // we had a bug here in libcifpp...
// if (not name.empty())
// {
// try
// {
// load_dictionary(name);
// }
// catch (const std::exception &ex)
// {
// if (VERBOSE)
// std::cerr << "Failed to load dictionary " << std::quoted(name) << ": " << ex.what() << '\n';
// }
// }
// }
// }
// // if (not m_validator)
// // load_dictionary("mmcif_pdbx.dic"); // TODO: maybe incorrect? Perhaps improve?
// }
// void file::load_dictionary(std::string_view name)
// {
// set_validator(&validator_factory::instance()[name]);
// }
bool file::contains(std::string_view name) const
{
return std::find_if(begin(), end(), [name](const datablock &db)
@@ -139,6 +166,16 @@ void file::load(const std::filesystem::path &p)
}
}
void file::load(const std::filesystem::path &p, std::string_view dict)
{
load(p, validator_factory::instance().operator[](dict));
}
void file::load(std::istream &is, std::string_view dict)
{
load(is, validator_factory::instance().operator[](dict));
}
void file::load(const std::filesystem::path &p, const validator &v)
{
gzio::ifstream in(p);
@@ -184,4 +221,4 @@ void file::save(std::ostream &os) const
db.write(os);
}
} // namespace cif
} // namespace cif

View File

@@ -28,7 +28,6 @@
#include <filesystem>
#include <fstream>
#include <initializer_list>
#include <iomanip>
#include <numeric>
#include <stack>
@@ -48,10 +47,15 @@ void atom::atom_impl::moveTo(const point &p)
auto r = row();
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);
#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
m_location = p;
}
@@ -98,18 +102,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 = 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"));
// 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);
return d;
}
// return d;
// }
// bool atom::atom_impl::getAnisoU(float anisou[6]) const
// {
@@ -145,6 +149,145 @@ 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())
@@ -176,8 +319,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_pdb_strand_id = a.get_auth_asym_id();
m_pdb_seq_num = a.get_auth_seq_id();
m_auth_asym_id = a.get_auth_asym_id();
m_auth_seq_id = a.get_auth_seq_id();
m_pdb_ins_code = a.get_pdb_ins_code();
for (auto atom : atoms)
@@ -228,12 +371,11 @@ 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_pdb_strand_id },
{ "auth_asym_id", m_auth_asym_id },
{ "auth_atom_id", inAtomID },
{ "auth_comp_id", m_compound_id },
{ "auth_seq_id", m_pdb_seq_num },
{ "auth_seq_id", m_auth_seq_id },
{ "occupancy", 1.0f, 2 },
{ "B_iso_or_equiv", 20.0f },
{ "pdbx_PDB_model_num", m_structure->get_model_nr() },
});
@@ -308,28 +450,6 @@ atom residue::get_atom_by_atom_id(const std::string &atom_id) const
return result;
}
atom residue::get_atom_by_atom_id(const std::string &atomID, const std::string &altID) const
{
if (altID.empty())
return get_atom_by_atom_id(atomID);
atom result;
for (auto &a : m_atoms)
{
if (auto a_alt_id = a.get_label_alt_id(); a.get_label_atom_id() == atomID and (a_alt_id.empty() or a_alt_id == altID))
{
result = a;
break;
}
}
if (not result and VERBOSE > 1)
std::cerr << "atom with atom_id " << atomID << " and alt_id " << altID << " not found in residue " << m_asym_id << ':' << m_seq_id << '\n';
return result;
}
// residue is a single entity if the atoms for the asym with m_asym_id is equal
// to the number of atoms in this residue... hope this is correct....
bool residue::is_entity() const
@@ -398,8 +518,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_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() << ']';
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() << ']';
return os;
}
@@ -408,7 +528,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_pdb_strand_id(), authSeqID, pdbInsCode)
: residue(*polymer.get_structure(), compoundID, polymer.get_asym_id(), seqID, polymer.get_auth_asym_id(), authSeqID, pdbInsCode)
, m_polymer(&polymer)
, m_index(index)
{
@@ -442,16 +562,6 @@ bool monomer::is_last_in_chain() const
return m_index + 1 == m_polymer->size();
}
const monomer &monomer::prev() const
{
return m_polymer->at(m_index - 1);
}
const monomer &monomer::next() const
{
return m_polymer->at(m_index + 1);
}
bool monomer::has_alpha() const
{
return m_index >= 1 and m_index + 2 < m_polymer->size();
@@ -468,7 +578,7 @@ float monomer::phi() const
if (m_index > 0)
{
auto &prev = m_polymer->at(m_index - 1);
auto &prev = m_polymer->operator[](m_index - 1);
if (prev.m_seq_id + 1 == m_seq_id)
{
auto a1 = prev.C();
@@ -490,7 +600,7 @@ float monomer::psi() const
if (m_index + 1 < m_polymer->size())
{
auto &next = m_polymer->at(m_index + 1);
auto &next = m_polymer->operator[](m_index + 1);
if (m_seq_id + 1 == next.m_seq_id)
{
auto a1 = N();
@@ -514,9 +624,9 @@ float monomer::alpha() const
{
if (m_index >= 1 and m_index + 2 < m_polymer->size())
{
auto &prev = m_polymer->at(m_index - 1);
auto &next = m_polymer->at(m_index + 1);
auto &nextNext = m_polymer->at(m_index + 2);
auto &prev = m_polymer->operator[](m_index - 1);
auto &next = m_polymer->operator[](m_index + 1);
auto &nextNext = m_polymer->operator[](m_index + 2);
result = static_cast<float>(dihedral_angle(prev.CAlpha().get_location(), CAlpha().get_location(), next.CAlpha().get_location(), nextNext.CAlpha().get_location()));
}
@@ -538,8 +648,8 @@ float monomer::kappa() const
{
if (m_index >= 2 and m_index + 2 < m_polymer->size())
{
auto &prevPrev = m_polymer->at(m_index - 2);
auto &nextNext = m_polymer->at(m_index + 2);
auto &prevPrev = m_polymer->operator[](m_index - 2);
auto &nextNext = m_polymer->operator[](m_index + 2);
if (prevPrev.m_seq_id + 4 == nextNext.m_seq_id)
{
@@ -567,7 +677,7 @@ float monomer::tco() const
{
if (m_index > 0)
{
auto &prev = m_polymer->at(m_index - 1);
auto &prev = m_polymer->operator[](m_index - 1);
if (prev.m_seq_id + 1 == m_seq_id)
result = static_cast<float>(cosinus_angle(C().get_location(), O().get_location(), prev.C().get_location(), prev.O().get_location()));
}
@@ -589,7 +699,7 @@ float monomer::omega() const
try
{
if (not is_last_in_chain())
result = omega(*this, m_polymer->at(m_index + 1));
result = omega(*this, m_polymer->operator[](m_index + 1));
}
catch (const std::exception &ex)
{
@@ -685,7 +795,7 @@ bool monomer::is_cis() const
if (m_index + 1 < m_polymer->size())
{
auto &next = m_polymer->at(m_index + 1);
auto &next = m_polymer->operator[](m_index + 1);
result = monomer::is_cis(*this, next);
}
@@ -827,7 +937,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_pdb_strand_id(auth_asym_id)
, m_auth_asym_id(auth_asym_id)
{
using namespace cif::literals;
@@ -839,8 +949,12 @@ 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::string compoundID, pdbSeqNum, pdbInsCode;
cif::tie(seqID, pdbSeqNum, compoundID, pdbInsCode) = r.get("seq_id", "pdb_seq_num", "mon_id", "pdb_ins_code");
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::size_t index = size();
@@ -848,11 +962,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, pdbSeqNum, pdbInsCode, compoundID);
emplace_back(*this, index, seqID, authSeqID, pdbInsCode, compoundID);
}
else if (VERBOSE > 0)
{
monomer m{ *this, index, seqID, pdbSeqNum, pdbInsCode, compoundID };
monomer m{ *this, index, seqID, authSeqID, pdbInsCode, compoundID };
std::cerr << "Dropping alternate residue " << m << '\n';
}
}
@@ -992,7 +1106,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_pdb_seq_num });
atom_info.set_value({ "auth_seq_id", m_auth_seq_id });
atom_info.set_value({ "occupancy", 1.0, 2 });
atom_info.set_value({ "B_iso_or_equiv", 30.0, 2 });
atom_info.set_value({ "pdbx_PDB_model_num", 1 });
@@ -1108,12 +1222,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.get_pdb_seq_num() },
{ "pdb_seq_num", result.num() },
{ "pdb_mon_id", result.get_compound_id() },
{ "auth_asym_id", result.get_pdb_strand_id() },
{ "auth_asym_id", result.get_auth_asym_id() },
{ "auth_mon_id", result.get_compound_id() },
{ "auth_seq_num", result.get_pdb_seq_num() },
{ "auth_seq_num", result.get_auth_seq_id() },
{ "hetero", "n" } });
@@ -1156,7 +1270,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_pdb_seq_num())
if (not sn.get_link() or sn.get_link().get_auth_seq_id() != s.get_auth_seq_id())
continue;
auto n = name(sn) + "-(1-" + sn.get_link().get_label_atom_id().substr(1) + ')';
@@ -1183,19 +1297,16 @@ float branch::weight() const
// --------------------------------------------------------------------
// structure
structure::structure(file &p, std::size_t modelNr, structure_open_options options)
structure::structure(file &p, std::size_t modelNr, StructureOpenOptions options)
: structure(p.front(), modelNr, options)
{
}
structure::structure(datablock &db, std::size_t modelNr, structure_open_options options)
structure::structure(datablock &db, std::size_t modelNr, StructureOpenOptions options)
: m_db(db)
, m_model_nr(modelNr)
{
if (db.get_validator() == nullptr)
db.load_dictionary();
auto &atom_site = db["atom_site"];
auto &atomCat = db["atom_site"];
load_atoms_for_model(options);
@@ -1203,7 +1314,7 @@ structure::structure(datablock &db, std::size_t modelNr, structure_open_options
if (m_atoms.empty() and m_model_nr == 1)
{
std::optional<std::size_t> model_nr;
cif::tie(model_nr) = atom_site.front().get("pdbx_PDB_model_num");
cif::tie(model_nr) = atomCat.front().get("pdbx_PDB_model_num");
if (model_nr and *model_nr != m_model_nr)
{
if (VERBOSE > 0)
@@ -1222,131 +1333,42 @@ structure::structure(datablock &db, std::size_t modelNr, structure_open_options
load_data();
}
void structure::load_atoms_for_model(structure_open_options options)
void structure::load_atoms_for_model(StructureOpenOptions options)
{
using namespace literals;
auto &atom_site = m_db["atom_site"];
auto &atomCat = 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");
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);
}
}
for (auto id : atomCat.find<std::string>(std::move(c), "id"))
emplace_atom(std::make_shared<atom::atom_impl>(m_db, id));
}
// 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)
if (m_polymers.empty() or m_polymers.back().get_asym_id() != asym_id or m_polymers.back().get_entity_id() != entityID)
m_polymers.emplace_back(*this, entityID, asym_id, auth_asym_id);
}
@@ -1372,18 +1394,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_pdb_seq_num() }] = &res;
resMap[{ res.get_asym_id(), res.get_seq_id(), res.get_auth_seq_id() }] = &res;
}
for (auto &res : m_non_polymers)
resMap[{ res.get_asym_id(), res.get_seq_id(), res.get_pdb_seq_num() }] = &res;
resMap[{ res.get_asym_id(), res.get_seq_id(), res.get_auth_seq_id() }] = &res;
std::set<std::string> sugars;
for (auto &branch : m_branches)
{
for (auto &sugar : branch)
{
resMap[{ sugar.get_asym_id(), sugar.get_seq_id(), sugar.get_pdb_seq_num() }] = &sugar;
resMap[{ sugar.get_asym_id(), sugar.get_seq_id(), sugar.get_auth_seq_id() }] = &sugar;
sugars.insert(sugar.get_compound_id());
}
}
@@ -1458,6 +1480,30 @@ 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());
@@ -1606,7 +1652,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_pdb_seq_num() == authSeqID))
if (res.get_asym_id() == asym_id and (authSeqID.empty() or res.get_auth_seq_id() == authSeqID))
return res;
}
}
@@ -1630,7 +1676,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_pdb_seq_num() == authSeqID)
if (sugar.get_asym_id() == asym_id and sugar.get_auth_seq_id() == authSeqID)
return sugar;
}
}
@@ -1652,7 +1698,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_pdb_seq_num() == authSeqID and res.get_compound_id() == compID)
if (res.get_asym_id() == asym_id and res.get_auth_seq_id() == authSeqID and res.get_compound_id() == compID)
return res;
}
}
@@ -1676,7 +1722,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_pdb_seq_num() == authSeqID and sugar.get_compound_id() == compID)
if (sugar.get_asym_id() == asym_id and sugar.get_auth_seq_id() == authSeqID and sugar.get_compound_id() == compID)
return sugar;
}
}
@@ -1897,12 +1943,13 @@ void structure::swap_atoms(atom a1, atom a2)
auto r1 = atomSites.find1(key("id") == a1.id());
auto r2 = atomSites.find1(key("id") == a2.id());
for (std::string fld : std::initializer_list<std::string>{ "label_atom_id", "auth_atom_id", "type_symbol" })
{
auto l1 = r1[fld];
auto l2 = r2[fld];
l1.swap(l2);
}
auto l1 = r1["label_atom_id"];
auto l2 = r2["label_atom_id"];
l1.swap(l2);
auto l3 = r1["auth_atom_id"];
auto l4 = r2["auth_atom_id"];
l3.swap(l4);
}
catch (const std::exception &ex)
{
@@ -2025,7 +2072,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_pdb_seq_num() == auth_seq_id))
if (res.get_asym_id() == asym_id and (auth_seq_id.empty() or res.get_auth_seq_id() == auth_seq_id))
{
remove_residue(res);
return;
@@ -2055,7 +2102,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_pdb_seq_num() == auth_seq_id)
if (sugar.get_asym_id() == asym_id and sugar.get_auth_seq_id() == auth_seq_id)
{
remove_residue(sugar);
return;
@@ -2188,7 +2235,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_pdb_seq_num() },
{ "auth_seq_num", sugar.get_auth_seq_id() },
{ "hetero", "n" } });
}
@@ -2274,8 +2321,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_pdb_seq_num() },
{ "auth_seq_num", res.get_pdb_seq_num() },
{ "pdb_seq_num", res.get_auth_seq_id() },
{ "auth_seq_num", res.get_auth_seq_id() },
{ "pdb_mon_id", comp_id },
{ "auth_mon_id", comp_id },
{ "pdb_strand_id", asym_id },
@@ -2335,8 +2382,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_pdb_seq_num() },
{ "auth_seq_num", res.get_pdb_seq_num() },
{ "pdb_seq_num", res.get_auth_seq_id() },
{ "auth_seq_num", res.get_auth_seq_id() },
{ "pdb_mon_id", comp_id },
{ "auth_mon_id", comp_id },
{ "pdb_strand_id", asym_id },
@@ -2410,61 +2457,6 @@ void structure::create_water(row_initializer atom)
});
}
std::string structure::create_link(atom a1, atom a2, const std::string &link_type, const std::string &role)
{
using namespace literals;
auto &struct_conn = m_db["struct_conn"];
auto &struct_conn_type = m_db["struct_conn_type"];
// This will validate link_type :-)
if (not struct_conn_type.contains("id"_key == link_type))
struct_conn_type.emplace({ { "id", link_type } });
std::string link_id = struct_conn.get_unique_id(link_type + '_');
item label_seq_id_1("ptnr1_label_seq_id");
if (int nr = a1.get_label_seq_id(); nr != 0)
label_seq_id_1.value(std::to_string(nr));
item label_seq_id_2("ptnr2_label_seq_id");
if (int nr = a2.get_label_seq_id(); nr != 0)
label_seq_id_2.value(std::to_string(nr));
struct_conn.emplace(
{ //
{ "id", link_id },
{ "conn_type_id", link_type },
{ "pdbx_leaving_atom_flag", "one" },
{ "ptnr1_label_asym_id", a1.get_label_asym_id() },
{ "ptnr1_label_comp_id", a1.get_label_comp_id() },
label_seq_id_1,
{ "ptnr1_label_atom_id", a1.get_label_atom_id() },
{ "pdbx_ptnr1_label_alt_id", a1.get_label_alt_id() },
{ "pdbx_ptnr1_PDB_ins_code", a1.get_pdb_ins_code() },
{ "ptnr1_auth_asym_id", a1.get_auth_asym_id() },
{ "ptnr1_auth_comp_id", a1.get_auth_comp_id() },
{ "ptnr1_auth_seq_id", a1.get_auth_seq_id() },
{ "ptnr1_symmetry", a1.symmetry() },
{ "ptnr2_label_asym_id", a2.get_label_asym_id() },
{ "ptnr2_label_comp_id", a2.get_label_comp_id() },
label_seq_id_2,
{ "ptnr2_label_atom_id", a2.get_label_atom_id() },
{ "pdbx_ptnr2_label_alt_id", a2.get_label_alt_id() },
{ "pdbx_ptnr2_PDB_ins_code", a2.get_pdb_ins_code() },
{ "ptnr2_auth_asym_id", a2.get_auth_asym_id() },
{ "ptnr2_auth_comp_id", a2.get_auth_comp_id() },
{ "ptnr2_auth_seq_id", a2.get_auth_seq_id() },
{ "ptnr2_symmetry", a2.symmetry() },
{ "pdbx_dist_value", distance(a1.get_location(), a2.get_location()), 3 },
{ "pdbx_role", role } });
return link_id;
}
branch &structure::create_branch()
{
auto &entity = m_db["entity"];
@@ -2705,11 +2697,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_pdb_seq_num() },
{ "entity_branch_list_num_1", s1.get_auth_seq_id() },
{ "comp_id_1", s1.get_compound_id() },
{ "atom_id_1", l1.get_label_atom_id() },
{ "leaving_atom_id_1", "O1" },
{ "entity_branch_list_num_2", s2.get_pdb_seq_num() },
{ "entity_branch_list_num_2", s2.get_auth_seq_id() },
{ "comp_id_2", s2.get_compound_id() },
{ "atom_id_2", l2.get_label_atom_id() },
{ "leaving_atom_id_2", "H" + l2.get_label_atom_id() },
@@ -2722,12 +2714,9 @@ std::string structure::create_entity_for_branch(branch &branch)
void structure::cleanup_empty_categories()
{
using namespace literals;
auto &atomSite = m_db["atom_site"];
auto &pdbxPolySeqScheme = m_db["pdbx_poly_seq_scheme"];
auto &entityPolySeq = m_db["entity_poly_seq"];
// Remove chem_comp's for which there are no atoms at all
auto &chem_comp = m_db["chem_comp"];
@@ -2736,12 +2725,8 @@ void structure::cleanup_empty_categories()
for (auto chemComp : chem_comp)
{
std::string compID = chemComp["id"].as<std::string>();
if (atomSite.contains("label_comp_id"_key == compID or "auth_comp_id"_key == compID) or
pdbxPolySeqScheme.contains("mon_id"_key == compID or "auth_mon_id"_key == compID or "pdb_mon_id"_key == compID) or
entityPolySeq.contains("mon_id"_key == compID))
{
if (atomSite.contains("label_comp_id"_key == compID or "auth_comp_id"_key == compID))
continue;
}
obsoleteChemComps.push_back(chemComp);
}
@@ -2898,8 +2883,8 @@ static int compare_numbers(std::string_view a, std::string_view b)
std::from_chars_result ra, rb;
ra = from_chars(a.data(), a.data() + a.length(), da);
rb = from_chars(b.data(), b.data() + b.length(), db);
ra = selected_charconv<double>::from_chars(a.data(), a.data() + a.length(), da);
rb = selected_charconv<double>::from_chars(b.data(), b.data() + b.length(), db);
if (not(bool) ra.ec and not(bool) rb.ec)
{
@@ -2920,14 +2905,6 @@ static int compare_numbers(std::string_view a, std::string_view b)
return result;
}
int compare_cif_id(const std::string &a, const std::string &b)
{
int d = static_cast<int>(a.length() - b.length());
if (d == 0)
d = a.compare(b);
return d;
}
void structure::reorder_atoms()
{
auto &atom_site = m_db["atom_site"];
@@ -2939,7 +2916,7 @@ void structure::reorder_atoms()
// First by model number
d = a.get<int>("pdbx_PDB_model_num") - b.get<int>("pdbx_PDB_model_num");
if (d == 0)
d = compare_cif_id(a.get<std::string>("label_asym_id"), b.get<std::string>("label_asym_id"));
d = a.get<std::string>("label_asym_id").compare(b.get<std::string>("label_asym_id"));
if (d == 0)
{
auto na = a.get<std::optional<int>>("label_seq_id");

View File

@@ -33,6 +33,7 @@
#include <regex>
#include <set>
namespace cif::pdb
{
@@ -57,9 +58,9 @@ std::string cif2pdbDate(const std::string &d)
int month = std::stoi(m[2].str());
if (m[3].matched)
result = cif::format("{:02}-{:3.3}-{:02}", stoi(m[3].str()), kMonths[month - 1], (year % 100));
result = cif::format("%02.2d-%3.3s-%02.2d", stoi(m[3].str()), kMonths[month - 1], (year % 100)).str();
else
result = cif::format("{:3.3}-{:02}", kMonths[month - 1], (year % 100));
result = cif::format("%3.3s-%02.2d", kMonths[month - 1], (year % 100)).str();
}
return result;
@@ -257,14 +258,16 @@ std::size_t WriteCitation(std::ostream &pdbFile, const datablock &db, row_handle
{
to_upper(pubname);
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)
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)
<< '\n';
++result;
}
if (not issn.empty())
{
pdbFile << s1 << cif::format("REFN ISSN {:<25.25s}", issn) << '\n';
const std::string kRefHeader = s1 + "REFN ISSN %-25.25s";
pdbFile << cif::format(kRefHeader, issn) << '\n';
++result;
}
@@ -273,25 +276,27 @@ 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)
// % issn).str()
// << '\n';
// }
if (not pmid.empty())
{
pdbFile << s1 << cif::format("PMID {:<60.60s} ", pmid) << '\n';
const std::string kPMID = s1 + "PMID %-60.60s ";
pdbFile << cif::format(kPMID, pmid) << '\n';
++result;
}
if (not doi.empty())
{
pdbFile << s1 << cif::format("DOI {:<60.60s} ", doi) << '\n';
const std::string kDOI = s1 + "DOI %-60.60s ";
pdbFile << cif::format(kDOI, doi) << '\n';
++result;
}
@@ -302,10 +307,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
@@ -340,12 +345,7 @@ void write_header_lines(std::ostream &pdbFile, const datablock &db)
}
}
pdbFile << cif::format(/* kHeader */
"HEADER {:<40.40s}"
"{:<9.9s}"
" {:<4.4s}"
, keywords, date, db.name()) << '\n';
pdbFile << cif::format(kHeader, keywords, date, db.name()) << '\n';
// TODO: implement
// OBSLTE (skip for now)
@@ -535,6 +535,7 @@ 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
@@ -558,9 +559,9 @@ void WriteTitle(std::ostream &pdbFile, const datablock &db)
{
std::string cs = ++continuation > 1 ? std::to_string(continuation) : std::string();
pdbFile << cif::format("REVDAT {:3}{:2.2s} {:9.9s} {:4.4s} {:1} ", revNum, cs, date, db.name(), modType);
pdbFile << cif::format(kRevDatFmt, 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)
@@ -613,7 +614,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 */
@@ -760,7 +761,10 @@ class Fs : public FBase
else
{
os << '\n';
WriteOneContinuedLine(os, cif::format("REMARK {:3} ", mNr), 0, s);
std::stringstream ss;
ss << "REMARK " << std::setw(3) << std::right << mNr << ' ';
WriteOneContinuedLine(os, ss.str(), 0, s);
}
}
@@ -1613,7 +1617,7 @@ void WriteRemark3Phenix(std::ostream &pdbFile, const datablock &db)
percent_reflns_obs /= 100;
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(" ") << 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("") << '\n'
@@ -2581,7 +2585,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} {:5}{:1.1s}", modelNr, resName, chainID, seqNr, iCode) << '\n';
pdbFile << cif::format("REMARK 465 %3.3s %3.3s %1.1s %5d%1.1s", modelNr, resName, chainID, seqNr, iCode) << '\n';
}
}
@@ -2628,7 +2632,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}{:4}{:1.1s} ", modelNr, resName, chainID, seqNr, iCode) << " ";
pdbFile << cif::format("REMARK 470 %3.3s %3.3s %1.1s%4d%1.1s ", modelNr, resName, chainID, seqNr, iCode) << " ";
for (std::size_t i = 0; i < 6 and not a.second.empty(); ++i)
{
@@ -2726,16 +2730,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';
}
@@ -2754,8 +2758,9 @@ 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';
}
@@ -2783,7 +2788,7 @@ int WritePrimaryStructure(std::ostream &pdbFile, const datablock &db)
t = 13;
pdbFile << cif::format(
"SEQRES {:3} {:1.1s} {:4} {:<51.51s} ",
"SEQRES %3d %1.1s %4d %-51.51s ",
n++, std::string{ chainID }, seqresl[chainID], join(seq.begin(), seq.begin() + t, " "))
<< '\n';
@@ -2803,8 +2808,9 @@ 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';
}
@@ -2919,7 +2925,7 @@ int WriteHeterogen(std::ostream &pdbFile, const datablock &db)
{
if (h.water)
continue;
pdbFile << cif::format("HET {:3.3s} {:1c}{:4}{:1c} {:5}", h.hetID, h.chainID, h.seqNum, h.iCode, h.numHetAtoms) << '\n';
pdbFile << cif::format("HET %3.3s %c%4d%c %5d", h.hetID, h.chainID, h.seqNum, h.iCode, h.numHetAtoms) << '\n';
++numHet;
}
@@ -2934,7 +2940,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)
@@ -3026,7 +3032,7 @@ int WriteHeterogen(std::ostream &pdbFile, const datablock &db)
{
std::stringstream fs;
fs << cif::format("FORMUL {:2} {:3.3s} {:2.2s}{:1c}", componentNr, hetID, (c > 1 ? std::to_string(c) : std::string()), (hetID == water_comp_id ? '*' : ' '));
fs << cif::format("FORMUL %2d %3.3s %2.2s%c", componentNr, hetID, (c > 1 ? std::to_string(c) : std::string()), (hetID == water_comp_id ? '*' : ' '));
++c;
if (formula.length() > 51)
@@ -3093,7 +3099,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 {:3} {:>3.3s} {:3.3s} {:1.1s} {:4}{:1.1s} {:3.3s} {:1.1s} {:4}{:1.1s}{:2}{:<30.30s} {:5}",
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",
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';
}
@@ -3130,7 +3136,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}{: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';
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';
first = false;
}
@@ -3149,7 +3155,7 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
if (h.empty())
{
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';
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';
}
else
{
@@ -3162,8 +3168,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}{: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}",
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",
rangeID2, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, sense, curAtom, curResName, curChainID, curResSeq, curICode, prevAtom, prevResName, prevChainID, prevResSeq, prevICode)
<< '\n';
}
@@ -3201,7 +3207,7 @@ void WriteConnectivity(std::ostream &pdbFile, const datablock &db)
sym1 = cif2pdbSymmetry(sym1);
sym2 = cif2pdbSymmetry(sym2);
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';
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';
++nr;
}
@@ -3228,10 +3234,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}{: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);
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);
if (not Length.empty())
pdbFile << cif::format(" {:5.2f}", stod(Length));
pdbFile << cif::format(" %5.2f", stod(Length));
pdbFile << '\n';
}
@@ -3249,7 +3255,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} {:4}{:1.1s} {:3.3s} {:1.1s} {:4}{:1.1s} {:3.3s} {:6.2f}",
pdbFile << cif::format("CISPEP %3.3s %3.3s %1.1s %4d%1.1s %3.3s %1.1s %4d%1.1s %3.3s %6.2f",
serNum, pep1, chainID1, seqNum1, icode1, pep2, chainID2, seqNum2, icode2, modNum, measure) << '\n';
}
}
@@ -3270,7 +3276,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}{:4}{:1.1s} ", resName, chainID, seq, iCode));
sites[siteID].push_back(cif::format("%3.3s %1.1s%4d%1.1s ", resName, chainID, seq, iCode).str());
}
for (auto s : sites)
@@ -3283,7 +3289,7 @@ int WriteMiscellaneousFeatures(std::ostream &pdbFile, const datablock &db)
int nr = 1;
while (res.empty() == false)
{
pdbFile << cif::format("SITE {:3} {:3.3s} {:2} ", nr, siteID, numRes);
pdbFile << cif::format("SITE %3d %3.3s %2d ", nr, siteID, numRes);
for (int i = 0; i < 4; ++i)
{
@@ -3312,7 +3318,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}{: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';
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';
}
int WriteCoordinateTransformation(std::ostream &pdbFile, const datablock &db)
@@ -3321,18 +3327,18 @@ int WriteCoordinateTransformation(std::ostream &pdbFile, const datablock &db)
for (auto r : db["database_PDB_matrix"])
{
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';
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';
result += 3;
break;
}
for (auto r : db["atom_sites"])
{
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';
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';
result += 3;
break;
}
@@ -3342,9 +3348,9 @@ int WriteCoordinateTransformation(std::ostream &pdbFile, const datablock &db)
{
std::string given = r["code"] == "given" ? "1" : "";
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';
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';
++nr;
result += 3;
@@ -3363,6 +3369,10 @@ 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();
@@ -3407,7 +3417,7 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
if (terminate)
{
pdbFile << cif::format("TER {:5} {:3.3s} {:1.1s}{:4}{:1.1s}", serial, resName, chainID, resSeq, iCode) << '\n';
pdbFile << cif::format("TER %5d %3.3s %1.1s%4d%1.1s", serial, resName, chainID, resSeq, iCode) << '\n';
++serial;
terminatedChains.insert(chainID);
@@ -3436,6 +3446,26 @@ 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");
@@ -3446,8 +3476,7 @@ 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}{: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';
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';
++numCoord;
@@ -3462,7 +3491,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{: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';
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';
}
++serial;
@@ -3514,7 +3543,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 {:4}", model_nr) << '\n';
pdbFile << cif::format("MODEL %4d", model_nr) << '\n';
std::set<std::string> TERminatedChains;
auto n = WriteCoordinatesForModel(pdbFile, db, last_resseq_for_chain_map, TERminatedChains, model_nr);
@@ -3586,7 +3615,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()), truncate_at);
return FixStringLength(cif::format("HEADER %-40.40s%-9.9s %-4.4s", keywords, date, db.name()).str(), truncate_at);
}
std::string get_COMPND_line(const datablock &db, std::string::size_type truncate_at)
@@ -3759,7 +3788,7 @@ void write(std::ostream &os, const datablock &db)
numXform = WriteCoordinateTransformation(os, db);
std::tie(numCoord, numTer) = WriteCoordinate(os, db);
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'
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'
<< "END\n";
}

View File

@@ -32,7 +32,6 @@
#include <map>
#include <set>
#include <stack>
#include <stdexcept>
using cif::category;
using cif::datablock;
@@ -896,7 +895,12 @@ class PDBFileParser
if (year < 1950)
year += 100;
s = cif::format("{:04}-{:02}-{:02}", year, month, day);
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();
}
else if (regex_match(s, m, rx2))
{
@@ -908,7 +912,7 @@ class PDBFileParser
if (year < 1950)
year += 100;
s = cif::format("{:04}-{:02}", year, month);
s = cif::format("%04d-%02d", year, month).str();
}
else
ec = error::make_error_code(error::pdbErrors::invalidDate);
@@ -1958,7 +1962,7 @@ void PDBFileParser::ParseRemarks()
switch (remarkNr)
{
case 1:
while (mRec->is("REMARK 1") or mRec->is("REMARK 001"))
while (mRec->is("REMARK 1"))
{
if (mRec->mVlen > 15 and vS(12, 20) == "REFERENCE")
{
@@ -3330,27 +3334,38 @@ void PDBFileParser::ParseRemark350()
std::string type = mat == std::vector<double>{ 1, 0, 0, 0, 1, 0, 0, 0, 1 } and vec == std::vector<double>{ 0, 0, 0 } ? "identity operation" : "crystal symmetry operation";
auto pdbx_struct_oper_list = getCategory("pdbx_struct_oper_list");
if (not pdbx_struct_oper_list->contains(cif::key("id") == operID))
getCategory("pdbx_struct_oper_list")->emplace({ // clang-format off
// if (type == "identity operation")
// {
// }
// else
try
{
// clang-format off
getCategory("pdbx_struct_oper_list")->emplace({
{ "id", operID },
{ "type", type },
// { "name", "" },
// { "symmetryOperation", "" },
{ "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]) }
{ "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() }
});
// clang-format on
// clang-format on
}
catch (duplicate_key_error &ex)
{
// so what?
}
mat.clear();
vec.clear();
@@ -4285,8 +4300,6 @@ void PDBFileParser::ConstructEntities()
type = "polypeptide(L)";
else if (mightBeDNA and not mightBePolyPeptide)
type = "polyribonucleotide";
else
type = "other";
// clang-format off
getCategory("entity_poly")->emplace({
@@ -4314,7 +4327,7 @@ void PDBFileParser::ConstructEntities()
}
// build sugar trees first
// ConstructSugarTrees(asymNr);
ConstructSugarTrees(asymNr);
// done with the sugar, resume operation as before
@@ -4492,7 +4505,7 @@ void PDBFileParser::ConstructEntities()
int modResID = 1;
std::set<std::string> modResSet;
for (auto rec = FindRecord("MODRES"); rec != nullptr and rec->is("MODRES");
rec = rec->mNext) // 1 - 6 Record name "MODRES"
rec = rec->mNext) // 1 - 6 Record name "MODRES"
{ // 8 - 11 IDcode idCode ID code of this datablock.
std::string resName = rec->vS(13, 15); // 13 - 15 Residue name resName Residue name used in this datablock.
char chainID = rec->vC(17); // 17 Character chainID Chain identifier.
@@ -5614,7 +5627,7 @@ void PDBFileParser::ParseCoordinateTransformation()
igiven = vC(60) == '1'; // 60 Integer iGiven 1 if coordinates for the representations
// which are approximately related by the
GetNextRecord(); // transformations of the molecule are
} // contained in the datablock. Otherwise, blank.
} // contained in the datablock. Otherwise, blank.
// clang-format off
getCategory("struct_ncs_oper")->emplace({
@@ -5768,9 +5781,6 @@ 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);
@@ -5849,7 +5859,7 @@ void PDBFileParser::ParseCoordinate(int modelNr)
auto f = [](float f) -> std::string
{
return cif::format("{:6.4f}", f);
return cif::format("%6.4f", f).str();
};
// clang-format off
@@ -6366,20 +6376,11 @@ void read_pdb_file(std::istream &pdbFile, cif::file &cifFile)
p.Parse(pdbFile, cifFile);
if (cifFile.empty())
{
if (VERBOSE >= 0)
std::cerr << "PDB is empty!\n";
}
else
{
cifFile.front().load_dictionary();
if (cifFile.front().get_validator() == nullptr)
cifFile.front().set_validator(&validator_factory::instance().get("mmcif_pdbx.dic"));
if (not cifFile.empty() and cifFile.front().get_validator() == nullptr)
cifFile.front().load_dictionary("mmcif_pdbx.dic");
if (not cifFile.is_valid() and cif::VERBOSE >= 0)
std::cerr << "Resulting mmCIF file is not valid!\n";
}
if (not cifFile.is_valid() and cif::VERBOSE >= 0)
std::cerr << "Resulting mmCIF file is not valid!\n";
}
// --------------------------------------------------------------------
@@ -6403,10 +6404,7 @@ file read(std::istream &is)
// apart from the letter 'd', the test has changed into the following:
if (std::isalpha(ch) and std::toupper(ch) != 'D')
{
read_pdb_file(is, result);
fixup_pdbx(result);
}
else
{
try
@@ -6417,38 +6415,16 @@ 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."));
}
if (not(result.empty() or result.front().empty()))
{
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);
}
}
}
}
// Since we're using the cif::pdb way of reading the file, the data may need
// reconstruction
reconstruct_pdbx(result);
}
// Must be a PDB like file, right?
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"]);
}
if (not result.empty() and result.front().get_validator() == nullptr)
result.front().load_dictionary("mmcif_pdbx.dic");
return result;
}

View File

@@ -1478,8 +1478,6 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
best.parser->fixup();
auto &validator = cif::validator_factory::instance().get("mmcif_pdbx.dic");
for (auto &cat1 : best.parser->mDb)
{
if (cat1.empty())
@@ -1498,15 +1496,8 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
auto r1 = cat1.front();
auto r2 = cat2.front();
auto cv = cat1.get_cat_validator();
if (cv == nullptr)
cv = validator.get_validator_for_category(cat1.name());
if (cv == nullptr)
continue;
for (auto &iv : cv->m_item_validators)
r2[iv.m_item_name] = r1[iv.m_item_name].text();
for (auto item : cat1.key_items())
r2[item] = r1[item].text();
}
}
else

File diff suppressed because it is too large Load Diff

View File

@@ -61,15 +61,17 @@ 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;
}
bool is_valid_pdbx_file(const file &file, const validator &v)
bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
{
std::error_code ec;
bool result = is_valid_pdbx_file(file, v, ec);
return result and not(bool) ec;
bool result = is_valid_pdbx_file(file, dictionary, ec);
return result and not (bool)ec;
}
bool is_valid_pdbx_file(const file &file, std::error_code &ec)
@@ -78,23 +80,42 @@ bool is_valid_pdbx_file(const file &file, std::error_code &ec)
if (file.empty())
ec = make_error_code(validation_error::empty_file);
else if (auto ac = file.front().get("audit_conform"); ac != nullptr)
result = is_valid_pdbx_file(file, validator_factory::instance().get(*ac), ec);
else
result = is_valid_pdbx_file(file, validator_factory::instance().get("mmcif_pdbx.dic"), ec);
{
std::string dictionary = "mmcif_pdbx";
for (auto &db : file)
{
auto audit_conform = db.get("audit_conform");
if (audit_conform == nullptr)
continue;
if (not audit_conform->empty())
{
auto specified_dict = audit_conform->front()["dict_name"];
if (not specified_dict.empty())
dictionary = specified_dict.as<std::string>();
}
break;
}
result = is_valid_pdbx_file(file, dictionary, ec);
}
return result;
}
bool is_valid_pdbx_file(const file &file, const validator &validator, std::error_code &ec)
bool is_valid_pdbx_file(const file &file, std::string_view dictionary, std::error_code &ec)
{
using namespace cif::literals;
bool result = true, warned_missing_parents = false;
bool result = true;
try
{
auto &cf = cif::compound_factory::instance();
auto &validator = cif::validator_factory::instance().operator[](dictionary);
if (file.empty())
throw std::runtime_error("Empty file");
@@ -127,18 +148,10 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
if (not cf.is_monomer(comp_id))
continue;
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));
auto p = pdbx_poly_seq_scheme.find(get_parents_condition(validator, r, pdbx_poly_seq_scheme));
if (p.size() != 1)
{
if (VERBOSE > 0)
if (cif::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");
}
@@ -167,7 +180,7 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
const auto entity_poly_type = entity_poly.find1<std::string>("entity_id"_key == entity_id, "type");
std::map<int, std::set<std::string>> mon_per_seq_id;
std::map<int,std::set<std::string>> mon_per_seq_id;
for (const auto &[num, mon_id, hetero] : entity_poly_seq.find<int, std::string, bool>("entity_id"_key == entity_id, "num", "mon_id", "hetero"))
{
@@ -202,37 +215,28 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
throw std::runtime_error("Mismatch between the hetero flag in the poly seq schemes and the number residues per seq_id");
}
// This code proved to take too much time ...
// for (const auto &[seq_id, mon_ids] : mon_per_seq_id)
// {
// for (auto asym_id : struct_asym.find<std::string>("entity_id"_key == entity_id, "id"))
// {
// condition cond;
// for (auto mon_id : mon_ids)
// cond = std::move(cond) or "label_comp_id"_key == mon_id;
// cond = "label_entity_id"_key == entity_id and
// "label_asym_id"_key == asym_id and
// "label_seq_id"_key == seq_id and not std::move(cond);
// if (atom_site.contains(std::move(cond)))
// throw std::runtime_error("An atom_site record exists that has no parent in the poly seq scheme categories");
// }
// }
// ... so we're using this instead, should be almost the same...
for (const auto &[comp_id, seq_id] :
atom_site.find<std::string, int>("label_entity_id"_key == entity_id, "label_comp_id", "label_seq_id"))
for (const auto &[seq_id, mon_ids] : mon_per_seq_id)
{
if (not mon_per_seq_id[seq_id].contains(comp_id))
throw std::runtime_error("An atom_site record exists that has no parent in the poly seq scheme categories");
for (auto asym_id : struct_asym.find<std::string>("entity_id"_key == entity_id, "id"))
{
condition cond;
for (auto mon_id : mon_ids)
cond = std::move(cond) or "label_comp_id"_key == mon_id;
cond = "label_entity_id"_key == entity_id and
"label_asym_id"_key == asym_id and
"label_seq_id"_key == seq_id and not std::move(cond);
if (atom_site.contains(std::move(cond)))
throw std::runtime_error("An atom_site record exists that has no parent in the poly seq scheme categories");
}
}
auto &&[seq, seq_can] = entity_poly.find1<std::optional<std::string>, std::optional<std::string>>("entity_id"_key == entity_id,
"pdbx_seq_one_letter_code", "pdbx_seq_one_letter_code_can");
std::string::const_iterator si, sci, se, sce;
auto seq_match = [&](bool can, std::string::const_iterator si, std::string::const_iterator se)
{
@@ -269,8 +273,8 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
else
letter = '(' + comp_id + ')';
}
if (iequals(std::string{ si, si + letter.length() }, letter))
if (iequals(std::string{si, si + letter.length()}, letter))
{
match = true;
si += letter.length();
@@ -289,14 +293,12 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
if (not seq.has_value())
{
if (VERBOSE > 0)
if (cif::VERBOSE > 0)
std::clog << "Warning: entity_poly has no sequence for entity_id " << entity_id << '\n';
}
else
{
seq->erase(std::remove_if(seq->begin(), seq->end(), [](char ch)
{ return std::isspace(ch); }),
seq->end());
seq->erase(std::remove_if(seq->begin(), seq->end(), [](char ch) { return std::isspace(ch); }), seq->end());
if (not seq_match(false, seq->begin(), seq->end()))
throw std::runtime_error("Sequences do not match for entity " + entity_id);
@@ -304,14 +306,12 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
if (not seq_can.has_value())
{
if (VERBOSE > 1)
if (cif::VERBOSE > 1)
std::clog << "Warning: entity_poly has no canonical sequence for entity_id " << entity_id << '\n';
}
else
{
seq_can->erase(std::remove_if(seq_can->begin(), seq_can->end(), [](char ch)
{ return std::isspace(ch); }),
seq_can->end());
seq_can->erase(std::remove_if(seq_can->begin(), seq_can->end(), [](char ch) { return std::isspace(ch); }), seq_can->end());
if (not seq_match(true, seq_can->begin(), seq_can->end()))
throw std::runtime_error("Canonical sequences do not match for entity " + entity_id);
@@ -323,15 +323,16 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
catch (const std::exception &ex)
{
result = false;
if (VERBOSE > 0)
if (cif::VERBOSE > 0)
std::clog << ex.what() << '\n';
ec = make_error_code(validation_error::not_valid_pdbx);
}
if (not result and (bool) ec)
if (not result and (bool)ec)
ec = make_error_code(validation_error::not_valid_pdbx);
return result;
}
} // namespace cif::pdb

View File

@@ -32,11 +32,6 @@
#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
@@ -95,10 +90,10 @@ float cell::get_volume() const
auto cos_beta = std::cos(beta);
auto cos_gamma = std::cos(gamma);
double vol = m_a * m_b * m_c;
auto 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 static_cast<float>(vol);
return vol;
}
// --------------------------------------------------------------------

View File

@@ -29,11 +29,6 @@
#include <algorithm>
#include <cassert>
#if __has_include("fast_float/fast_float.h")
#include "fast_float/fast_float.h"
#endif
namespace cif
{
@@ -517,22 +512,4 @@ std::vector<std::string> word_wrap(const std::string &text, std::size_t width)
return result;
}
#if __has_include("fast_float/fast_float.h")
template<>
std::from_chars_result ff_charconv<float>::from_chars(const char *a, const char *b, float &v)
{
auto r = fast_float::from_chars(a, b, v);
return { r.ptr, r.ec };
}
template<>
std::from_chars_result ff_charconv<double>::from_chars(const char *a, const char *b, double &v)
{
auto r = fast_float::from_chars(a, b, v);
return { r.ptr, r.ec };
}
#endif
} // namespace cif

View File

@@ -34,20 +34,14 @@
#include <condition_variable>
#include <cstring>
#include <deque>
#include <format>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#include <utility>
#if __cpp_lib_jthread >= 201911L
#include <stop_token>
#endif
namespace fs = std::filesystem;
@@ -71,50 +65,27 @@ std::string get_version_nr()
#if defined(_WIN32) or defined(__MINGW32__)
}
// clang-format off
# include <windows.h>
# include <libloaderapi.h>
# include <wincon.h>
// clang-format on
#include <windows.h>
#include <libloaderapi.h>
#include <wincon.h>
#include <codecvt>
namespace cif
{
uint32_t get_terminal_width()
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
return ::GetConsoleScreenBufferInfo(::GetStdHandle(STD_OUTPUT_HANDLE), &csbi)
? csbi.srWindow.Right - csbi.srWindow.Left + 1
: 80;
}
void write_to_console(const std::string &s)
{
auto h = ::GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (auto l = ::MultiByteToWideChar(CP_UTF8, 0, s.data(), s.length(), nullptr, 0);
l > 0 and ::GetConsoleScreenBufferInfo(::GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
{
std::u16string ws(l, 0);
::MultiByteToWideChar(CP_UTF8, 0, s.data(), s.length(), (LPWSTR)ws.data(), l);
DWORD w;
::WriteConsoleW(h, ws.data(), ws.length(), &w, nullptr);
}
else
{
std::cout.write(s.data(), s.length());
std::cout.flush();
}
CONSOLE_SCREEN_BUFFER_INFO csbi;
::GetConsoleScreenBufferInfo(::GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
return csbi.srWindow.Right - csbi.srWindow.Left + 1;
}
#else
# include <limits.h>
# include <sys/ioctl.h>
# include <termios.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <limits.h>
uint32_t get_terminal_width()
{
@@ -129,220 +100,59 @@ uint32_t get_terminal_width()
return result;
}
inline void write_to_console(const std::string &s)
{
std::cout << s << std::flush;
}
#endif
// --------------------------------------------------------------------
struct progress_bar_impl
{
progress_bar_impl(uint64_t max_value, const std::string &message)
: m_max_value(max_value)
progress_bar_impl(int64_t inMax, const std::string &inAction)
: m_max_value(inMax)
, m_consumed(0)
, m_action(message)
, m_message(message)
, m_action(inAction)
, m_message(inAction)
, m_thread(std::bind(&progress_bar_impl::run, this))
{
}
virtual ~progress_bar_impl() {}
progress_bar_impl(const progress_bar_impl&) = delete;
progress_bar_impl &operator=(const progress_bar_impl &) = delete;
virtual void consumed(uint64_t n);
virtual void progress(uint64_t p);
virtual void message(const std::string &msg);
virtual void print_done();
~progress_bar_impl();
void run();
void consumed(int64_t n);
void progress(int64_t p);
void message(const std::string &msg);
void print_progress();
void print_done();
using time_point = std::chrono::time_point<std::chrono::system_clock>;
uint64_t m_max_value;
std::atomic<uint64_t> m_consumed;
int64_t m_max_value;
std::atomic<int64_t> m_consumed;
int64_t m_last_consumed = 0;
int m_spinner_index = 0;
std::string m_action, m_message;
time_point m_start = std::chrono::system_clock::now();
};
void progress_bar_impl::consumed(uint64_t n)
{
m_consumed += n;
}
void progress_bar_impl::progress(uint64_t p)
{
m_consumed = p;
}
void progress_bar_impl::message(const std::string &msg)
{
m_message = msg;
}
void progress_bar_impl::print_done()
{
std::chrono::duration<double> elapsed = std::chrono::system_clock::now() - m_start;
std::string days, hours, minutes, seconds;
uint64_t s = static_cast<uint64_t>(std::trunc(elapsed.count()));
if (s > 24 * 60 * 60)
{
days = std::format("{:d}d ", s / (24 * 60 * 60));
s %= 24 * 60 * 60;
}
if (s > 60 * 60)
{
hours = std::format("{:2d}h ", s / (60 * 60));
s %= 60 * 60;
}
if (s > 60)
{
minutes = std::format("{:2d}m ", s / 60);
s %= 60;
}
std::string msg = std::format("{} done in {}{}{}{:.1f}s", m_action, days, hours, minutes, s + 1e-6 * (elapsed.count() - s));
uint32_t width = get_terminal_width();
if (msg.length() < width)
msg += std::string(width - msg.length(), ' ');
write_to_console(msg += '\n');
}
// --------------------------------------------------------------------
struct simple_progress_bar_impl : public progress_bar_impl
{
simple_progress_bar_impl(uint64_t max_value, const std::string &message)
: progress_bar_impl(max_value, message)
{
}
~simple_progress_bar_impl()
{
if (m_printed_any)
print_done();
}
void consumed(uint64_t n) override
{
using namespace std::literals;
progress_bar_impl::consumed(n);
// print at most 10 steps, but only if it took long enough
int percentile = static_cast<int>(std::floor(10.f * m_consumed / m_max_value));
if (percentile > m_last_percentile and (m_printed_any or std::chrono::system_clock::now() - m_start >= 1s))
{
if (not std::exchange(m_printed_any, true))
write_to_console(m_action + ": ");
write_to_console(std::format("...{:d}0%", percentile));
m_last_percentile = percentile;
}
}
void progress(uint64_t p) override
{
consumed(p - m_consumed);
}
void print_done() override
{
if (m_printed_any)
{
write_to_console("\n");
progress_bar_impl::print_done();
}
}
bool m_printed_any = false;
int m_last_percentile = 0;
};
// --------------------------------------------------------------------
struct fancy_progress_bar_impl : public progress_bar_impl
{
fancy_progress_bar_impl(uint64_t max_value, const std::string &message)
: progress_bar_impl(max_value, message)
, m_thread(
#if __cpp_lib_jthread >= 201911L
[this](std::stop_token stoken)
{ this->run(stoken); }
#else
[this]()
{ this->run(); }
#endif
)
{
}
~fancy_progress_bar_impl();
#if __cpp_lib_jthread >= 201911L
void run(std::stop_token stoken);
#else
void run();
#endif
void consumed(uint64_t n) override;
void progress(uint64_t p) override;
void message(const std::string &msg) override;
void print_progress();
std::mutex m_mutex;
std::condition_variable m_cv;
float m_progress;
uint32_t m_width, m_bar_width;
uint32_t m_steps, m_last_steps = 0;
uint64_t m_last_consumed = 0;
#if __cpp_lib_jthread >= 201911L
std::jthread m_thread;
#else
std::thread m_thread;
time_point m_start = std::chrono::system_clock::now();
time_point m_last = std::chrono::system_clock::now();
bool m_stop = false;
#endif
};
const char *kBlocks[] = {
" ",
"",
"",
"",
"",
"",
"",
"",
"",
};
const size_t kBlockCount = sizeof(kBlocks) / sizeof(void *) - 1;
fancy_progress_bar_impl::~fancy_progress_bar_impl()
progress_bar_impl::~progress_bar_impl()
{
using namespace std::literals;
assert(m_thread.joinable());
#if __cpp_lib_jthread >= 201911L
m_thread.request_stop();
#else
m_stop = true;
#endif
m_thread.join();
}
#if __cpp_lib_jthread >= 201911L
void fancy_progress_bar_impl::run(std::stop_token stoken)
#else
void fancy_progress_bar_impl::run()
#endif
void progress_bar_impl::run()
{
using namespace std::literals;
@@ -350,44 +160,25 @@ void fancy_progress_bar_impl::run()
try
{
for (;;)
while (not m_stop)
{
std::unique_lock lock(m_mutex);
m_cv.wait_for(lock, 25ms);
#if __cpp_lib_jthread >= 201911L
if (stoken.stop_requested())
break;
#else
if (m_stop)
break;
#endif
auto now = std::chrono::system_clock::now();
if (m_consumed == m_last_consumed or now - m_start < 1s)
if (now - m_start < 2s or now - m_last < 100ms)
{
std::this_thread::sleep_for(10ms);
continue;
}
m_last_consumed = m_consumed;
std::lock_guard lock(m_mutex);
// See if we need to do work
m_width = get_terminal_width();
m_progress = static_cast<float>(m_consumed) / m_max_value;
m_bar_width = 7 * m_width / 10; // 70% of the width of the terminal
m_steps = static_cast<uint32_t>(std::ceil(m_progress * m_bar_width * kBlockCount));
if (m_steps == m_last_steps)
continue;
m_last_steps = m_steps;
if (not printedAny)
write_to_console("\x1b[?25l");
if (not printedAny and isatty(STDOUT_FILENO))
std::cout << "\x1b[?25l";
print_progress();
printedAny = true;
m_last = std::chrono::system_clock::now();
}
}
catch (...)
@@ -396,94 +187,161 @@ void fancy_progress_bar_impl::run()
if (printedAny)
{
write_to_console("\r\x1b[?25h");
print_done();
if (isatty(STDOUT_FILENO))
std::cout << "\x1b[?25h";
}
}
void fancy_progress_bar_impl::consumed(uint64_t n)
void progress_bar_impl::consumed(int64_t n)
{
progress_bar_impl::consumed(n);
// m_cv.notify_one();
m_consumed += n;
}
void fancy_progress_bar_impl::progress(uint64_t p)
void progress_bar_impl::progress(int64_t p)
{
progress_bar_impl::progress(p);
// m_cv.notify_one();
m_consumed = p;
}
void fancy_progress_bar_impl::message(const std::string &msg)
void progress_bar_impl::message(const std::string &msg)
{
std::unique_lock lock(m_mutex);
progress_bar_impl::message(msg);
// m_cv.notify_one();
m_message = msg;
}
const char* kSpinner[] = {
// ".", "o", "O", "0", "O", "o", ".", " "
// "⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠"
".", "o", "O", "0", "@", "*", " "
};
const std::size_t kSpinnerCount = sizeof(kSpinner) / sizeof(char*);
const int kSpinnerTimeInterval = 100;
const uint32_t kMinBarWidth = 40, kMinMsgWidth = 12;
void fancy_progress_bar_impl::print_progress()
void progress_bar_impl::print_progress()
{
const uint32_t pct_width = 5;
uint32_t msg_width = m_width - m_bar_width - pct_width - 1;
const char *kBlocks[] = {
// "▯", // 0
// "▮", // 1
"=",
"-"
};
if (msg_width < kMinMsgWidth)
uint32_t width = get_terminal_width();
float progress = static_cast<float>(m_consumed) / m_max_value;
if (width < kMinBarWidth)
std::cout << (100 * progress) << "%\n";
else
{
m_bar_width += kMinMsgWidth - msg_width;
msg_width = kMinMsgWidth;
}
uint32_t bar_width = 7 * width / 10;
uint32_t pct_width = 7;
uint32_t msg_width = width - bar_width - pct_width - 1;
std::string bar;
bar.reserve(m_bar_width * 4);
if (msg_width < kMinMsgWidth)
{
bar_width += kMinMsgWidth - msg_width;
msg_width = kMinMsgWidth;
}
for (uint32_t i = 0; i < m_bar_width; ++i)
{
if (i * kBlockCount <= m_steps)
bar += kBlocks[kBlockCount];
else if (i * kBlockCount > m_steps + kBlockCount)
bar += kBlocks[0];
std::ostringstream msg;
if (m_message.length() <= msg_width)
{
msg << m_message;
if (m_message.length() < msg_width)
msg << std::string(msg_width - m_message.length(), ' ');
}
else
bar += kBlocks[1 + m_steps % kBlockCount];
msg << m_message.substr(0, msg_width - 3) << "...";
msg << ' ';
uint32_t pi = static_cast<uint32_t>(std::ceil(progress * bar_width));
for (uint32_t i = 0; i < bar_width; ++i)
msg << kBlocks[i > pi ? 1 : 0];
msg << ' ';
msg << std::setw(3) << static_cast<int>(std::ceil(progress * 100)) << "% ";
auto now = std::chrono::system_clock::now();
m_spinner_index = (std::chrono::duration_cast<std::chrono::milliseconds>(now - m_start).count() / kSpinnerTimeInterval) % kSpinnerCount;
msg << kSpinner[m_spinner_index];
std::cout << '\r' << msg.str();
std::cout.flush();
}
// make the bar more colorfull
struct color_type
{
uint8_t r, g, b;
} fg{ 0, 3, 5 }, bg{ 0, 1, 2 };
auto esc_1 = std::format("\x1b[38;5;{}m\x1b[48;5;{}m",
16 + (fg.r * 36) + (fg.g * 6) + fg.b,
16 + (bg.r * 36) + (bg.g * 6) + bg.b);
std::string esc_2("\x1b[0m");
bar = esc_1 + bar + esc_2;
std::string msg = m_message.length() <= msg_width
? m_message
: m_message.substr(0, msg_width - 3) + "...";
write_to_console(std::format("{:{}} {} {:3d}%\r", msg, msg_width, bar,
static_cast<int>(std::ceil(m_progress * 100))));
}
// --------------------------------------------------------------------
namespace
{
progress_bar::progress_bar(int64_t max_value, const std::string &message)
std::ostream &operator<<(std::ostream &os, const std::chrono::duration<double> &t)
{
uint64_t s = static_cast<uint64_t>(std::trunc(t.count()));
if (s > 24 * 60 * 60)
{
auto days = s / (24 * 60 * 60);
os << days << "d ";
s %= 24 * 60 * 60;
}
if (s > 60 * 60)
{
auto hours = s / (60 * 60);
os << hours << "h ";
s %= 60 * 60;
}
if (s > 60)
{
auto minutes = s / 60;
os << minutes << "m ";
s %= 60;
}
double ss = s + 1e-6 * (t.count() - s);
os << std::fixed << std::setprecision(1) << ss << 's';
return os;
}
} // namespace
void progress_bar_impl::print_done()
{
std::chrono::duration<double> elapsed = std::chrono::system_clock::now() - m_start;
std::ostringstream msgstr;
msgstr << m_action << " done in " << elapsed << " seconds";
auto msg = msgstr.str();
uint32_t width = get_terminal_width();
if (msg.length() < width)
msg += std::string(width - msg.length(), ' ');
std::cout << '\r' << msg << '\n';
}
progress_bar::progress_bar(int64_t inMax, const std::string &inAction)
: m_impl(nullptr)
{
if (VERBOSE >= 0)
{
if (isatty(STDOUT_FILENO) and get_terminal_width() > kMinBarWidth)
m_impl = new fancy_progress_bar_impl(max_value, message);
else
m_impl = new simple_progress_bar_impl(max_value, message);
}
if (isatty(STDOUT_FILENO) and VERBOSE >= 0)
m_impl = new progress_bar_impl(inMax, inAction);
}
progress_bar::~progress_bar()
{
flush();
delete m_impl;
}
void progress_bar::consumed(int64_t inConsumed)
@@ -492,25 +350,16 @@ void progress_bar::consumed(int64_t inConsumed)
m_impl->consumed(inConsumed);
}
void progress_bar::progress(int64_t value)
void progress_bar::progress(int64_t inProgress)
{
if (m_impl != nullptr)
m_impl->progress(value);
m_impl->progress(inProgress);
}
void progress_bar::message(const std::string &message)
void progress_bar::message(const std::string &inMessage)
{
if (m_impl != nullptr)
m_impl->message(message);
}
void progress_bar::flush()
{
if (m_impl)
{
delete m_impl;
m_impl = nullptr;
}
m_impl->message(inMessage);
}
} // namespace cif
@@ -538,13 +387,13 @@ struct rsrc_imp
#if _WIN32
# if __MINGW32__
#if __MINGW32__
extern "C" __attribute__((weak, alias("gResourceIndexDefault"))) const mrsrc::rsrc_imp gResourceIndex[];
extern "C" __attribute__((weak, alias("gResourceDataDefault"))) const char gResourceData[];
extern "C" __attribute__((weak, alias("gResourceNameDefault"))) const char gResourceName[];
# else
#else
extern "C" const mrsrc::rsrc_imp *gResourceIndexDefault[1] = {};
extern "C" const char *gResourceDataDefault[1] = {};
@@ -554,11 +403,11 @@ extern "C" const mrsrc::rsrc_imp gResourceIndex[];
extern "C" const char gResourceData[];
extern "C" const char gResourceName[];
# pragma comment(linker, "/alternatename:gResourceIndex=gResourceIndexDefault")
# pragma comment(linker, "/alternatename:gResourceData=gResourceDataDefault")
# pragma comment(linker, "/alternatename:gResourceName=gResourceNameDefault")
#pragma comment(linker, "/alternatename:gResourceIndex=gResourceIndexDefault")
#pragma comment(linker, "/alternatename:gResourceData=gResourceDataDefault")
#pragma comment(linker, "/alternatename:gResourceName=gResourceNameDefault")
# endif
#endif
#else
extern const __attribute__((weak)) mrsrc::rsrc_imp gResourceIndex[];

View File

@@ -25,7 +25,6 @@
*/
#include "cif++/validate.hpp"
#include "cif++/category.hpp"
#include "cif++/dictionary_parser.hpp"
#include "cif++/gzio.hpp"
#include "cif++/utilities.hpp"
@@ -36,11 +35,16 @@
// The validator depends on regular expressions. Unfortunately,
// the implementation of std::regex in g++ is buggy and crashes
// on reading the pdbx dictionary. We used to use boost regex
// instead but using pcre2 is even easier and faster.
// on reading the pdbx dictionary. Therefore, in case g++ is used
// the code will use boost::regex instead.
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#if USE_BOOST_REGEX
# include <boost/regex.hpp>
using boost::regex;
#else
# include <regex>
using std::regex;
#endif
namespace cif
{
@@ -62,57 +66,13 @@ validation_exception::validation_exception(std::error_code ec, std::string_view
// --------------------------------------------------------------------
struct regex_impl
struct regex_impl : public regex
{
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)
regex_impl(std::string_view rx)
: regex(rx.begin(), rx.end(), regex::extended | regex::optimize)
{
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;
}
};
// --------------------------------------------------------------------
@@ -149,15 +109,9 @@ type_validator::type_validator(std::string_view name, DDL_PrimitiveType type, st
{
}
type_validator::type_validator(const type_validator &tv)
: m_name(tv.m_name)
, m_primitive_type(tv.m_primitive_type)
, m_rx(tv.m_rx)
{
}
type_validator::~type_validator()
{
delete m_rx;
}
int type_validator::compare(std::string_view a, std::string_view b) const
@@ -181,8 +135,8 @@ int type_validator::compare(std::string_view a, std::string_view b) const
std::from_chars_result ra, rb;
ra = from_chars(a.data(), a.data() + a.length(), da);
rb = from_chars(b.data(), b.data() + b.length(), db);
ra = selected_charconv<double>::from_chars(a.data(), a.data() + a.length(), da);
rb = selected_charconv<double>::from_chars(b.data(), b.data() + b.length(), db);
if (not(bool) ra.ec and not(bool) rb.ec)
{
@@ -272,7 +226,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 m_type->m_rx->match(value))
if (m_type != nullptr and not regex_match(value.begin(), value.end(), *m_type->m_rx))
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);
@@ -288,7 +242,7 @@ void category_validator::add_item_validator(item_validator &&v)
if (v.m_mandatory)
m_mandatory_items.insert(v.m_item_name);
v.m_category = m_name;
v.m_category = this;
auto r = m_item_validators.insert(std::move(v));
if (not r.second and VERBOSE >= 4)
@@ -330,29 +284,6 @@ const item_validator *category_validator::get_validator_for_aliased_item(std::st
// --------------------------------------------------------------------
validator::validator(const validator &rhs)
: m_audit_conform(rhs.m_audit_conform)
, m_strict(rhs.m_strict)
, m_type_validators(rhs.m_type_validators)
, m_category_validators(rhs.m_category_validators)
, m_link_validators(rhs.m_link_validators)
{
}
void swap(validator &a, validator &b) noexcept
{
std::swap(a.m_audit_conform, b.m_audit_conform);
std::swap(a.m_strict, b.m_strict);
std::swap(a.m_type_validators, b.m_type_validators);
std::swap(a.m_category_validators, b.m_category_validators);
std::swap(a.m_link_validators, b.m_link_validators);
}
void validator::parse(std::istream &is)
{
parse_dictionary(*this, is);
}
void validator::add_type_validator(type_validator &&v)
{
auto r = m_type_validators.insert(std::move(v));
@@ -485,48 +416,6 @@ void validator::report_error(std::error_code ec, std::string_view category,
std::cerr << ex.what() << '\n';
}
void validator::fill_audit_conform(category &audit_conform) const
{
audit_conform.clear();
audit_conform.emplace(m_audit_conform.begin(), m_audit_conform.end());
}
bool validator::matches_audit_conform(const category &audit_conform) const
{
if (audit_conform.empty())
return false;
auto ai = m_audit_conform.begin();
auto bi = audit_conform.begin();
while (ai != m_audit_conform.end() and bi != audit_conform.end())
{
const auto &[name_a, version_a] = ai->get<std::string, std::optional<std::string>>("dict_name", "dict_version");
const auto &[name_b, version_b] = bi->get<std::string, std::optional<std::string>>("dict_name", "dict_version");
++ai;
++bi;
if (name_a != name_b)
return false;
if (not version_b.has_value() or not version_a.has_value())
continue;
if (validator_factory::check_version(name_a, *version_b, *version_a) == false)
return false;
}
return ai == m_audit_conform.end() and bi == audit_conform.end();
}
void validator::append_audit_conform(const std::string &name, const std::optional<std::string> &version)
{
m_audit_conform.emplace({ //
{ "dict_name", name },
{ "dict_version", version } });
}
// --------------------------------------------------------------------
validator_factory &validator_factory::instance()
@@ -535,127 +424,113 @@ validator_factory &validator_factory::instance()
return s_instance;
}
const validator &validator_factory::get(std::string_view dictionary_name)
const validator &validator_factory::operator[](std::string_view dictionary_name)
{
category audit_conform("audit_conform");
for (auto part : cif::split(dictionary_name, ";", true))
audit_conform.emplace({ { "dict_name", part } });
return get(audit_conform);
}
const validator &validator_factory::get(const category &audit_conform)
{
if (audit_conform.empty())
throw std::runtime_error("Empty audit_conform category, cannot create a validator");
std::lock_guard lock(m_mutex);
// Check existing first
for (auto &v : m_validators)
try
{
if (v.matches_audit_conform(audit_conform))
return v;
}
std::lock_guard lock(m_mutex);
// If the audit conform contains only one record, this is easy
if (audit_conform.size() == 1)
{
const auto &[name, version] =
audit_conform.front().get<std::string, std::optional<std::string>>("dict_name", "dict_version");
if (not name.empty())
return m_validators.emplace_back(construct_validator(name, version));
}
// A new, merged dictionary
std::optional<validator> v;
for (const auto &[name, version] : audit_conform.rows<std::string, std::optional<std::string>>("dict_name", "dict_version"))
{
if (name.empty())
continue;
if (not v)
v = construct_validator(name, version);
else
for (auto &validator : m_validators)
{
auto data = load_resource(name);
if (iequals(validator.name(), dictionary_name))
return validator;
}
// not found, try to see if it helps if we tweak the name a little
// too bad clang version 10 did not have a constructor for std::filesystem::path that accepts a std::string_view
std::filesystem::path dictionary(dictionary_name.data(), dictionary_name.data() + dictionary_name.length());
if (dictionary.extension() != ".dic")
{
auto dict_name = dictionary.filename().string() + ".dic";
for (auto &validator : m_validators)
{
if (iequals(validator.name(), dict_name))
return validator;
}
}
// not found, add it
validator v(dictionary_name);
for (bool first = true; auto part_name : cif::split(dictionary_name, ";", true))
{
auto data = load_resource(part_name);
dictionary.assign(part_name.begin(), part_name.end());
if (not data and dictionary.extension().string() != ".dic")
data = load_resource(dictionary.parent_path() / (dictionary.filename().string() + ".dic"));
if (not data)
throw std::runtime_error("Could not load dictionary " + std::string{ name });
{
std::error_code ec;
v->parse(*data);
// might be a compressed dictionary on disk
std::filesystem::path p = dictionary;
if (p.extension() == ".dic")
p = p.parent_path() / (p.filename().string() + ".gz");
else
p = p.parent_path() / (p.filename().string() + ".dic.gz");
#if defined(CACHE_DIR) or defined(DATA_DIR)
if (not std::filesystem::exists(p, ec) or ec)
{
for (const char *dir : {
# if defined(CACHE_DIR)
CACHE_DIR,
# endif
# if defined(DATA_DIR)
DATA_DIR
# endif
})
{
auto p2 = std::filesystem::path(dir) / p;
if (std::filesystem::exists(p2, ec) and not ec)
{
swap(p, p2);
break;
}
}
}
#endif
if (std::filesystem::exists(p, ec) and not ec)
{
auto in = std::make_unique<gzio::ifstream>(p);
if (not in->is_open())
throw std::runtime_error("Could not open dictionary (" + p.string() + ")");
data.reset(in.release());
}
else
throw std::runtime_error("Dictionary not found or defined (" + dictionary.string() + ")");
}
if (std::exchange(first, false))
v = parse_dictionary(part_name, *data);
else
extend_dictionary(v, *data);
}
m_validators.emplace_back(std::move(v));
return m_validators.back();
}
catch (const std::exception &ex)
{
std::string msg = "Error while loading dictionary ";
msg += dictionary_name;
std::throw_with_nested(std::runtime_error(msg));
}
if (not v)
throw std::runtime_error("Missing dictionary information?");
return m_validators.emplace_back(std::move(*v));
}
validator validator_factory::construct_validator(std::string_view name, std::optional<std::string> version)
const validator &validator_factory::construct_validator(std::string_view name, std::istream &is)
{
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 });
validator v;
v.parse(*data);
if (version.has_value() and VERBOSE >= 0 and
not v.matches_audit_conform(category{ "audit_conform", //
{ { "dict_name", name }, { "dict_version", version } } }))
{
std::clog << "Loaded dictionary does not match name=" << name << " and version=" << version.value_or("''") << "\n";
}
return v;
}
bool validator_factory::check_version(std::string_view name, std::string_view expected, std::string_view found)
{
bool result = true;
auto el = cif::split(expected, ".");
auto fl = cif::split(found, ".");
auto eli = el.begin();
auto fli = fl.begin();
while (eli != el.end() and fli != fl.end())
{
int e_int, f_int;
if (auto [ptr, ec] = std::from_chars(eli->data(), eli->data() + eli->length(), e_int); ec != std::errc{})
{
std::clog << "Could not parse requested version string for dictionary " << std::quoted(expected) << "\n";
result = false;
break;
}
if (auto [ptr, ec] = std::from_chars(fli->data(), fli->data() + fli->length(), f_int); ec != std::errc{})
{
std::clog << "Could not parse version string in dictionary " << name << " " << std::quoted(found) << "\n";
result = false;
break;
}
if (f_int > e_int) // newer version, assume this is ok
break;
if (f_int < e_int)
{
std::clog << "The version in dictionary " << name << " is lower than requested, this may cause validation errors\n";
result = false;
break;
}
++eli;
++fli;
}
return result;
return m_validators.emplace_back(parse_dictionary(name, is));
}
} // namespace cif

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +1,19 @@
# 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.
# We're using the older version 2 of Catch2
if(NOT (Catch2_FOUND OR TARGET Catch2))
find_package(Catch2 3 QUIET)
if(NOT(Catch2_FOUND OR TARGET Catch2))
find_package(Catch2 QUIET)
if(NOT Catch2_FOUND)
include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.4.0)
GIT_TAG v2.13.9)
FetchContent_MakeAvailable(Catch2)
target_compile_features(Catch2 PRIVATE cxx_std_20)
set(Catch2_VERSION "2.13.9")
endif()
endif()
@@ -42,19 +22,24 @@ list(
CIFPP_tests
unit-v2
unit-3d
format
model
query
rename-compound
sugar
spinner
reconstruction
validate-pdbx
)
# reconstruction
validate-pdbx)
add_library(test-main OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/test-main.cpp")
target_link_libraries(test-main cifpp::cifpp Catch2::Catch2)
if("${Catch2_VERSION}" VERSION_LESS 3.0.0)
target_compile_definitions(test-main PUBLIC CATCH22=1)
else()
target_compile_definitions(test-main PUBLIC CATCH22=0)
endif()
foreach(CIFPP_TEST IN LISTS CIFPP_tests)
set(CIFPP_TEST "${CIFPP_TEST}-test")
set(CIFPP_TEST_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${CIFPP_TEST}.cpp")
@@ -62,6 +47,12 @@ foreach(CIFPP_TEST IN LISTS CIFPP_tests)
add_executable(
${CIFPP_TEST} ${CIFPP_TEST_SOURCE} $<TARGET_OBJECTS:test-main>)
if(${Catch2_VERSION} VERSION_GREATER_EQUAL 3.0.0)
target_compile_definitions(${CIFPP_TEST} PUBLIC CATCH22=0)
else()
target_compile_definitions(${CIFPP_TEST} PUBLIC CATCH22=1)
endif()
target_link_libraries(${CIFPP_TEST} PRIVATE cifpp::cifpp Catch2::Catch2)
target_include_directories(${CIFPP_TEST} PRIVATE "${EIGEN_INCLUDE_DIR}")
@@ -70,6 +61,15 @@ foreach(CIFPP_TEST IN LISTS CIFPP_tests)
target_compile_options(${CIFPP_TEST} PRIVATE /EHsc)
endif()
add_test(NAME ${CIFPP_TEST}
COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir ${CMAKE_CURRENT_SOURCE_DIR})
endforeach()
add_custom_target(
"run-${CIFPP_TEST}"
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Run${CIFPP_TEST}.touch ${CIFPP_TEST})
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Run${CIFPP_TEST}.touch
COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir
${CMAKE_CURRENT_SOURCE_DIR})
add_test(NAME ${CIFPP_TEST} COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir
${CMAKE_CURRENT_SOURCE_DIR})
endforeach()

View File

@@ -1,822 +0,0 @@
REMARK 001 design 0 N 3 RMSD 1.605
MODEL 1
ATOM 1 N MET A 1 -24.216 -7.571 -1.155 1.00 31.87 N
ATOM 2 CA MET A 1 -22.971 -7.756 -0.416 1.00 31.87 C
ATOM 3 C MET A 1 -21.814 -7.051 -1.114 1.00 31.87 C
ATOM 4 CB MET A 1 -22.656 -9.245 -0.256 1.00 31.87 C
ATOM 5 O MET A 1 -21.516 -7.338 -2.275 1.00 31.87 O
ATOM 6 CG MET A 1 -22.779 -9.747 1.174 1.00 31.87 C
ATOM 7 SD MET A 1 -22.473 -11.551 1.314 1.00 31.87 S
ATOM 8 CE MET A 1 -22.597 -11.758 3.112 1.00 31.87 C
ATOM 9 N LYS A 2 -21.652 -5.596 -0.909 1.00 33.06 N
ATOM 10 CA LYS A 2 -21.035 -4.320 -0.554 1.00 33.06 C
ATOM 11 C LYS A 2 -19.531 -4.475 -0.351 1.00 33.06 C
ATOM 12 CB LYS A 2 -21.678 -3.747 0.710 1.00 33.06 C
ATOM 13 O LYS A 2 -19.087 -5.326 0.423 1.00 33.06 O
ATOM 14 CG LYS A 2 -22.957 -2.963 0.453 1.00 33.06 C
ATOM 15 CD LYS A 2 -23.454 -2.275 1.717 1.00 33.06 C
ATOM 16 CE LYS A 2 -24.810 -1.615 1.500 1.00 33.06 C
ATOM 17 NZ LYS A 2 -25.284 -0.906 2.725 1.00 33.06 N
ATOM 18 N ILE A 3 -18.889 -4.065 -1.586 1.00 34.22 N
ATOM 19 CA ILE A 3 -17.539 -3.550 -1.381 1.00 34.22 C
ATOM 20 C ILE A 3 -17.331 -2.297 -2.229 1.00 34.22 C
ATOM 21 CB ILE A 3 -16.471 -4.612 -1.721 1.00 34.22 C
ATOM 22 O ILE A 3 -17.508 -2.330 -3.449 1.00 34.22 O
ATOM 23 CG1 ILE A 3 -16.524 -5.766 -0.713 1.00 34.22 C
ATOM 24 CG2 ILE A 3 -15.075 -3.982 -1.759 1.00 34.22 C
ATOM 25 CD1 ILE A 3 -15.743 -7.000 -1.142 1.00 34.22 C
ATOM 26 N ILE A 4 -17.776 -0.985 -1.831 1.00 40.18 N
ATOM 27 CA ILE A 4 -17.255 0.212 -1.181 1.00 40.18 C
ATOM 28 C ILE A 4 -16.631 1.136 -2.224 1.00 40.18 C
ATOM 29 CB ILE A 4 -16.218 -0.142 -0.091 1.00 40.18 C
ATOM 30 O ILE A 4 -15.717 0.736 -2.949 1.00 40.18 O
ATOM 31 CG1 ILE A 4 -16.844 -1.063 0.963 1.00 40.18 C
ATOM 32 CG2 ILE A 4 -15.658 1.128 0.555 1.00 40.18 C
ATOM 33 CD1 ILE A 4 -15.839 -1.662 1.937 1.00 40.18 C
ATOM 34 N GLU A 5 -17.325 2.198 -2.769 1.00 38.95 N
ATOM 35 CA GLU A 5 -17.243 3.640 -2.553 1.00 38.95 C
ATOM 36 C GLU A 5 -15.944 4.019 -1.848 1.00 38.95 C
ATOM 37 CB GLU A 5 -18.445 4.131 -1.741 1.00 38.95 C
ATOM 38 O GLU A 5 -15.602 3.445 -0.812 1.00 38.95 O
ATOM 39 CG GLU A 5 -19.296 5.162 -2.466 1.00 38.95 C
ATOM 40 CD GLU A 5 -20.407 5.738 -1.602 1.00 38.95 C
ATOM 41 OE1 GLU A 5 -20.194 6.791 -0.960 1.00 38.95 O
ATOM 42 OE2 GLU A 5 -21.500 5.129 -1.566 1.00 38.95 O
ATOM 43 N LYS A 6 -14.697 4.048 -2.514 1.00 40.24 N
ATOM 44 CA LYS A 6 -13.793 5.173 -2.294 1.00 40.24 C
ATOM 45 C LYS A 6 -12.348 4.784 -2.593 1.00 40.24 C
ATOM 46 CB LYS A 6 -13.911 5.684 -0.857 1.00 40.24 C
ATOM 47 O LYS A 6 -11.769 3.946 -1.899 1.00 40.24 O
ATOM 48 CG LYS A 6 -14.799 6.911 -0.707 1.00 40.24 C
ATOM 49 CD LYS A 6 -14.696 7.508 0.690 1.00 40.24 C
ATOM 50 CE LYS A 6 -15.689 8.645 0.887 1.00 40.24 C
ATOM 51 NZ LYS A 6 -15.523 9.301 2.219 1.00 40.24 N
ATOM 52 N TYR A 7 -12.096 4.360 -3.877 1.00 55.19 N
ATOM 53 CA TYR A 7 -10.695 4.330 -4.282 1.00 55.19 C
ATOM 54 C TYR A 7 -9.919 5.478 -3.647 1.00 55.19 C
ATOM 55 CB TYR A 7 -10.574 4.398 -5.807 1.00 55.19 C
ATOM 56 O TYR A 7 -10.324 6.639 -3.746 1.00 55.19 O
ATOM 57 CG TYR A 7 -11.069 3.159 -6.511 1.00 55.19 C
ATOM 58 CD1 TYR A 7 -10.245 2.047 -6.669 1.00 55.19 C
ATOM 59 CD2 TYR A 7 -12.361 3.097 -7.021 1.00 55.19 C
ATOM 60 CE1 TYR A 7 -10.696 0.903 -7.319 1.00 55.19 C
ATOM 61 CE2 TYR A 7 -12.823 1.958 -7.673 1.00 55.19 C
ATOM 62 OH TYR A 7 -12.437 -0.262 -8.461 1.00 55.19 O
ATOM 63 CZ TYR A 7 -11.985 0.868 -7.817 1.00 55.19 C
ATOM 64 N VAL A 8 -9.308 5.253 -2.484 1.00 55.50 N
ATOM 65 CA VAL A 8 -8.469 6.226 -1.792 1.00 55.50 C
ATOM 66 C VAL A 8 -7.196 6.476 -2.597 1.00 55.50 C
ATOM 67 CB VAL A 8 -8.112 5.754 -0.364 1.00 55.50 C
ATOM 68 O VAL A 8 -6.488 5.534 -2.960 1.00 55.50 O
ATOM 69 CG1 VAL A 8 -7.215 6.774 0.333 1.00 55.50 C
ATOM 70 CG2 VAL A 8 -9.381 5.509 0.450 1.00 55.50 C
ATOM 71 N PHE A 9 -7.225 7.625 -3.250 1.00 66.91 N
ATOM 72 CA PHE A 9 -6.004 8.094 -3.895 1.00 66.91 C
ATOM 73 C PHE A 9 -4.934 8.414 -2.858 1.00 66.91 C
ATOM 74 CB PHE A 9 -6.289 9.330 -4.754 1.00 66.91 C
ATOM 75 O PHE A 9 -5.089 9.345 -2.065 1.00 66.91 O
ATOM 76 CG PHE A 9 -6.868 9.009 -6.105 1.00 66.91 C
ATOM 77 CD1 PHE A 9 -6.039 8.797 -7.200 1.00 66.91 C
ATOM 78 CD2 PHE A 9 -8.243 8.920 -6.281 1.00 66.91 C
ATOM 79 CE1 PHE A 9 -6.572 8.499 -8.452 1.00 66.91 C
ATOM 80 CE2 PHE A 9 -8.783 8.623 -7.530 1.00 66.91 C
ATOM 81 CZ PHE A 9 -7.946 8.414 -8.614 1.00 66.91 C
ATOM 82 N LEU A 10 -4.065 7.388 -2.607 1.00 72.14 N
ATOM 83 CA LEU A 10 -2.967 7.542 -1.658 1.00 72.14 C
ATOM 84 C LEU A 10 -2.342 8.929 -1.771 1.00 72.14 C
ATOM 85 CB LEU A 10 -1.902 6.469 -1.893 1.00 72.14 C
ATOM 86 O LEU A 10 -1.903 9.499 -0.770 1.00 72.14 O
ATOM 87 CG LEU A 10 -2.222 5.069 -1.365 1.00 72.14 C
ATOM 88 CD1 LEU A 10 -1.258 4.046 -1.957 1.00 72.14 C
ATOM 89 CD2 LEU A 10 -2.165 5.045 0.158 1.00 72.14 C
ATOM 90 N ALA A 11 -2.498 9.659 -2.947 1.00 69.94 N
ATOM 91 CA ALA A 11 -1.847 10.939 -3.211 1.00 69.94 C
ATOM 92 C ALA A 11 -2.571 12.080 -2.501 1.00 69.94 C
ATOM 93 CB ALA A 11 -1.786 11.206 -4.713 1.00 69.94 C
ATOM 94 O ALA A 11 -1.971 13.117 -2.207 1.00 69.94 O
ATOM 95 N GLU A 12 -3.713 11.866 -1.974 1.00 76.23 N
ATOM 96 CA GLU A 12 -4.520 12.921 -1.368 1.00 76.23 C
ATOM 97 C GLU A 12 -4.620 12.741 0.144 1.00 76.23 C
ATOM 98 CB GLU A 12 -5.920 12.951 -1.987 1.00 76.23 C
ATOM 99 O GLU A 12 -5.178 13.590 0.841 1.00 76.23 O
ATOM 100 CG GLU A 12 -5.939 13.391 -3.444 1.00 76.23 C
ATOM 101 CD GLU A 12 -7.343 13.548 -4.005 1.00 76.23 C
ATOM 102 OE1 GLU A 12 -7.904 14.665 -3.935 1.00 76.23 O
ATOM 103 OE2 GLU A 12 -7.888 12.544 -4.518 1.00 76.23 O
ATOM 104 N LEU A 13 -3.986 11.682 0.620 1.00 82.70 N
ATOM 105 CA LEU A 13 -4.106 11.386 2.043 1.00 82.70 C
ATOM 106 C LEU A 13 -2.951 12.001 2.826 1.00 82.70 C
ATOM 107 CB LEU A 13 -4.146 9.873 2.274 1.00 82.70 C
ATOM 108 O LEU A 13 -1.826 12.073 2.327 1.00 82.70 O
ATOM 109 CG LEU A 13 -5.347 9.129 1.688 1.00 82.70 C
ATOM 110 CD1 LEU A 13 -5.171 7.623 1.857 1.00 82.70 C
ATOM 111 CD2 LEU A 13 -6.640 9.598 2.346 1.00 82.70 C
ATOM 112 N SER A 14 -3.339 12.631 3.947 1.00 86.03 N
ATOM 113 CA SER A 14 -2.328 13.121 4.878 1.00 86.03 C
ATOM 114 C SER A 14 -1.680 11.974 5.645 1.00 86.03 C
ATOM 115 CB SER A 14 -2.942 14.120 5.859 1.00 86.03 C
ATOM 116 O SER A 14 -2.161 10.839 5.599 1.00 86.03 O
ATOM 117 OG SER A 14 -3.526 15.211 5.168 1.00 86.03 O
ATOM 118 N ASP A 15 -0.488 12.215 6.359 1.00 86.85 N
ATOM 119 CA ASP A 15 0.221 11.246 7.189 1.00 86.85 C
ATOM 120 C ASP A 15 -0.734 10.542 8.149 1.00 86.85 C
ATOM 121 CB ASP A 15 1.345 11.930 7.971 1.00 86.85 C
ATOM 122 O ASP A 15 -0.647 9.327 8.340 1.00 86.85 O
ATOM 123 CG ASP A 15 2.418 12.521 7.074 1.00 86.85 C
ATOM 124 OD1 ASP A 15 3.279 11.766 6.575 1.00 86.85 O
ATOM 125 OD2 ASP A 15 2.403 13.754 6.867 1.00 86.85 O
ATOM 126 N GLU A 16 -1.606 11.353 8.835 1.00 86.30 N
ATOM 127 CA GLU A 16 -2.590 10.797 9.760 1.00 86.30 C
ATOM 128 C GLU A 16 -3.555 9.859 9.041 1.00 86.30 C
ATOM 129 CB GLU A 16 -3.366 11.918 10.456 1.00 86.30 C
ATOM 130 O GLU A 16 -3.871 8.780 9.545 1.00 86.30 O
ATOM 131 CG GLU A 16 -2.547 12.688 11.481 1.00 86.30 C
ATOM 132 CD GLU A 16 -3.342 13.770 12.194 1.00 86.30 C
ATOM 133 OE1 GLU A 16 -3.925 13.489 13.266 1.00 86.30 O
ATOM 134 OE2 GLU A 16 -3.384 14.908 11.676 1.00 86.30 O
ATOM 135 N GLU A 17 -4.022 10.266 7.880 1.00 87.05 N
ATOM 136 CA GLU A 17 -4.956 9.466 7.093 1.00 87.05 C
ATOM 137 C GLU A 17 -4.305 8.172 6.613 1.00 87.05 C
ATOM 138 CB GLU A 17 -5.477 10.268 5.898 1.00 87.05 C
ATOM 139 O GLU A 17 -4.937 7.114 6.618 1.00 87.05 O
ATOM 140 CG GLU A 17 -6.410 11.407 6.281 1.00 87.05 C
ATOM 141 CD GLU A 17 -7.322 11.844 5.146 1.00 87.05 C
ATOM 142 OE1 GLU A 17 -8.505 11.433 5.125 1.00 87.05 O
ATOM 143 OE2 GLU A 17 -6.850 12.602 4.270 1.00 87.05 O
ATOM 144 N LEU A 18 -3.057 8.282 6.290 1.00 88.16 N
ATOM 145 CA LEU A 18 -2.291 7.120 5.855 1.00 88.16 C
ATOM 146 C LEU A 18 -2.194 6.084 6.970 1.00 88.16 C
ATOM 147 CB LEU A 18 -0.889 7.539 5.407 1.00 88.16 C
ATOM 148 O LEU A 18 -2.374 4.888 6.730 1.00 88.16 O
ATOM 149 CG LEU A 18 -0.785 8.201 4.032 1.00 88.16 C
ATOM 150 CD1 LEU A 18 0.627 8.730 3.803 1.00 88.16 C
ATOM 151 CD2 LEU A 18 -1.179 7.219 2.934 1.00 88.16 C
ATOM 152 N LYS A 19 -1.979 6.558 8.141 1.00 87.30 N
ATOM 153 CA LYS A 19 -1.908 5.682 9.308 1.00 87.30 C
ATOM 154 C LYS A 19 -3.243 4.984 9.553 1.00 87.30 C
ATOM 155 CB LYS A 19 -1.495 6.474 10.549 1.00 87.30 C
ATOM 156 O LYS A 19 -3.280 3.787 9.843 1.00 87.30 O
ATOM 157 CG LYS A 19 -0.007 6.781 10.620 1.00 87.30 C
ATOM 158 CD LYS A 19 0.352 7.515 11.906 1.00 87.30 C
ATOM 159 CE LYS A 19 1.818 7.927 11.926 1.00 87.30 C
ATOM 160 NZ LYS A 19 2.176 8.639 13.189 1.00 87.30 N
ATOM 161 N LYS A 20 -4.246 5.781 9.525 1.00 88.15 N
ATOM 162 CA LYS A 20 -5.586 5.234 9.715 1.00 88.15 C
ATOM 163 C LYS A 20 -5.907 4.185 8.654 1.00 88.15 C
ATOM 164 CB LYS A 20 -6.631 6.350 9.682 1.00 88.15 C
ATOM 165 O LYS A 20 -6.511 3.153 8.956 1.00 88.15 O
ATOM 166 CG LYS A 20 -6.661 7.208 10.937 1.00 88.15 C
ATOM 167 CD LYS A 20 -7.772 8.249 10.877 1.00 88.15 C
ATOM 168 CE LYS A 20 -7.774 9.138 12.113 1.00 88.15 C
ATOM 169 NZ LYS A 20 -8.875 10.146 12.069 1.00 88.15 N
ATOM 170 N LEU A 21 -5.444 4.518 7.462 1.00 86.41 N
ATOM 171 CA LEU A 21 -5.662 3.611 6.341 1.00 86.41 C
ATOM 172 C LEU A 21 -4.974 2.271 6.583 1.00 86.41 C
ATOM 173 CB LEU A 21 -5.148 4.234 5.040 1.00 86.41 C
ATOM 174 O LEU A 21 -5.577 1.214 6.385 1.00 86.41 O
ATOM 175 CG LEU A 21 -5.364 3.417 3.765 1.00 86.41 C
ATOM 176 CD1 LEU A 21 -6.842 3.400 3.391 1.00 86.41 C
ATOM 177 CD2 LEU A 21 -4.524 3.976 2.622 1.00 86.41 C
ATOM 178 N VAL A 22 -3.737 2.304 6.914 1.00 87.19 N
ATOM 179 CA VAL A 22 -2.953 1.109 7.205 1.00 87.19 C
ATOM 180 C VAL A 22 -3.627 0.307 8.316 1.00 87.19 C
ATOM 181 CB VAL A 22 -1.504 1.464 7.609 1.00 87.19 C
ATOM 182 O VAL A 22 -3.719 -0.921 8.236 1.00 87.19 O
ATOM 183 CG1 VAL A 22 -0.752 0.218 8.073 1.00 87.19 C
ATOM 184 CG2 VAL A 22 -0.774 2.130 6.444 1.00 87.19 C
ATOM 185 N GLU A 23 -4.070 0.928 9.305 1.00 88.34 N
ATOM 186 CA GLU A 23 -4.790 0.285 10.400 1.00 88.34 C
ATOM 187 C GLU A 23 -6.050 -0.414 9.897 1.00 88.34 C
ATOM 188 CB GLU A 23 -5.151 1.308 11.480 1.00 88.34 C
ATOM 189 O GLU A 23 -6.364 -1.525 10.328 1.00 88.34 O
ATOM 190 CG GLU A 23 -3.959 1.792 12.292 1.00 88.34 C
ATOM 191 CD GLU A 23 -4.356 2.595 13.521 1.00 88.34 C
ATOM 192 OE1 GLU A 23 -4.486 2.004 14.616 1.00 88.34 O
ATOM 193 OE2 GLU A 23 -4.539 3.826 13.386 1.00 88.34 O
ATOM 194 N GLU A 24 -6.671 0.256 9.129 1.00 87.82 N
ATOM 195 CA GLU A 24 -7.883 -0.315 8.549 1.00 87.82 C
ATOM 196 C GLU A 24 -7.569 -1.568 7.737 1.00 87.82 C
ATOM 197 CB GLU A 24 -8.595 0.717 7.670 1.00 87.82 C
ATOM 198 O GLU A 24 -8.334 -2.535 7.758 1.00 87.82 O
ATOM 199 CG GLU A 24 -9.372 1.763 8.456 1.00 87.82 C
ATOM 200 CD GLU A 24 -10.598 1.200 9.157 1.00 87.82 C
ATOM 201 OE1 GLU A 24 -10.544 0.974 10.387 1.00 87.82 O
ATOM 202 OE2 GLU A 24 -11.621 0.982 8.469 1.00 87.82 O
ATOM 203 N TRP A 25 -6.452 -1.421 6.925 1.00 87.98 N
ATOM 204 CA TRP A 25 -6.008 -2.576 6.152 1.00 87.98 C
ATOM 205 C TRP A 25 -5.804 -3.789 7.054 1.00 87.98 C
ATOM 206 CB TRP A 25 -4.712 -2.256 5.403 1.00 87.98 C
ATOM 207 O TRP A 25 -6.237 -4.895 6.724 1.00 87.98 O
ATOM 208 CG TRP A 25 -4.867 -1.219 4.332 1.00 87.98 C
ATOM 209 CD1 TRP A 25 -6.030 -0.819 3.734 1.00 87.98 C
ATOM 210 CD2 TRP A 25 -3.820 -0.453 3.727 1.00 87.98 C
ATOM 211 CE2 TRP A 25 -4.422 0.394 2.770 1.00 87.98 C
ATOM 212 CE3 TRP A 25 -2.431 -0.399 3.902 1.00 87.98 C
ATOM 213 NE1 TRP A 25 -5.769 0.151 2.794 1.00 87.98 N
ATOM 214 CH2 TRP A 25 -2.324 1.317 2.182 1.00 87.98 C
ATOM 215 CZ2 TRP A 25 -3.681 1.285 1.990 1.00 87.98 C
ATOM 216 CZ3 TRP A 25 -1.695 0.488 3.125 1.00 87.98 C
ATOM 217 N ILE A 26 -5.164 -3.521 8.121 1.00 86.80 N
ATOM 218 CA ILE A 26 -4.842 -4.578 9.074 1.00 86.80 C
ATOM 219 C ILE A 26 -6.123 -5.095 9.724 1.00 86.80 C
ATOM 220 CB ILE A 26 -3.855 -4.082 10.155 1.00 86.80 C
ATOM 221 O ILE A 26 -6.287 -6.302 9.913 1.00 86.80 O
ATOM 222 CG1 ILE A 26 -2.511 -3.706 9.519 1.00 86.80 C
ATOM 223 CG2 ILE A 26 -3.667 -5.144 11.243 1.00 86.80 C
ATOM 224 CD1 ILE A 26 -1.549 -3.010 10.471 1.00 86.80 C
ATOM 225 N LYS A 27 -7.085 -4.130 10.074 1.00 88.23 N
ATOM 226 CA LYS A 27 -8.358 -4.482 10.697 1.00 88.23 C
ATOM 227 C LYS A 27 -9.231 -5.292 9.743 1.00 88.23 C
ATOM 228 CB LYS A 27 -9.101 -3.224 11.147 1.00 88.23 C
ATOM 229 O LYS A 27 -9.857 -6.273 10.148 1.00 88.23 O
ATOM 230 CG LYS A 27 -10.280 -3.498 12.070 1.00 88.23 C
ATOM 231 CD LYS A 27 -10.890 -2.206 12.597 1.00 88.23 C
ATOM 232 CE LYS A 27 -12.096 -2.477 13.486 1.00 88.23 C
ATOM 233 NZ LYS A 27 -12.726 -1.212 13.967 1.00 88.23 N
ATOM 234 N SER A 28 -9.257 -4.866 8.486 1.00 88.60 N
ATOM 235 CA SER A 28 -10.097 -5.462 7.453 1.00 88.60 C
ATOM 236 C SER A 28 -9.499 -6.767 6.939 1.00 88.60 C
ATOM 237 CB SER A 28 -10.292 -4.488 6.290 1.00 88.60 C
ATOM 238 O SER A 28 -10.208 -7.598 6.366 1.00 88.60 O
ATOM 239 OG SER A 28 -11.021 -3.347 6.707 1.00 88.60 O
ATOM 240 N LYS A 29 -8.274 -7.060 7.266 1.00 87.22 N
ATOM 241 CA LYS A 29 -7.552 -8.279 6.911 1.00 87.22 C
ATOM 242 C LYS A 29 -7.546 -8.493 5.400 1.00 87.22 C
ATOM 243 CB LYS A 29 -8.169 -9.491 7.609 1.00 87.22 C
ATOM 244 O LYS A 29 -7.336 -9.612 4.927 1.00 87.22 O
ATOM 245 CG LYS A 29 -7.958 -9.511 9.116 1.00 87.22 C
ATOM 246 CD LYS A 29 -8.508 -10.787 9.741 1.00 87.22 C
ATOM 247 CE LYS A 29 -8.322 -10.795 11.252 1.00 87.22 C
ATOM 248 NZ LYS A 29 -8.836 -12.056 11.866 1.00 87.22 N
ATOM 249 N GLU A 30 -7.995 -7.561 4.711 1.00 90.12 N
ATOM 250 CA GLU A 30 -7.955 -7.620 3.253 1.00 90.12 C
ATOM 251 C GLU A 30 -7.715 -6.239 2.650 1.00 90.12 C
ATOM 252 CB GLU A 30 -9.254 -8.215 2.703 1.00 90.12 C
ATOM 253 O GLU A 30 -8.286 -5.248 3.110 1.00 90.12 O
ATOM 254 CG GLU A 30 -9.273 -8.357 1.188 1.00 90.12 C
ATOM 255 CD GLU A 30 -10.542 -9.008 0.662 1.00 90.12 C
ATOM 256 OE1 GLU A 30 -11.585 -8.321 0.575 1.00 90.12 O
ATOM 257 OE2 GLU A 30 -10.494 -10.215 0.335 1.00 90.12 O
ATOM 258 N VAL A 31 -6.835 -6.090 1.610 1.00 86.60 N
ATOM 259 CA VAL A 31 -6.583 -4.828 0.922 1.00 86.60 C
ATOM 260 C VAL A 31 -6.313 -5.090 -0.558 1.00 86.60 C
ATOM 261 CB VAL A 31 -5.397 -4.064 1.553 1.00 86.60 C
ATOM 262 O VAL A 31 -5.684 -6.090 -0.912 1.00 86.60 O
ATOM 263 CG1 VAL A 31 -4.102 -4.859 1.404 1.00 86.60 C
ATOM 264 CG2 VAL A 31 -5.255 -2.682 0.919 1.00 86.60 C
ATOM 265 N THR A 32 -6.886 -4.281 -1.455 1.00 86.60 N
ATOM 266 CA THR A 32 -6.648 -4.386 -2.890 1.00 86.60 C
ATOM 267 C THR A 32 -5.875 -3.173 -3.400 1.00 86.60 C
ATOM 268 CB THR A 32 -7.971 -4.518 -3.667 1.00 86.60 C
ATOM 269 O THR A 32 -6.311 -2.033 -3.224 1.00 86.60 O
ATOM 270 CG2 THR A 32 -7.715 -4.710 -5.159 1.00 86.60 C
ATOM 271 OG1 THR A 32 -8.700 -5.647 -3.170 1.00 86.60 O
ATOM 272 N PHE A 33 -4.627 -3.379 -3.960 1.00 85.17 N
ATOM 273 CA PHE A 33 -3.825 -2.315 -4.552 1.00 85.17 C
ATOM 274 C PHE A 33 -4.096 -2.201 -6.048 1.00 85.17 C
ATOM 275 CB PHE A 33 -2.333 -2.565 -4.307 1.00 85.17 C
ATOM 276 O PHE A 33 -4.048 -3.198 -6.771 1.00 85.17 O
ATOM 277 CG PHE A 33 -1.918 -2.397 -2.870 1.00 85.17 C
ATOM 278 CD1 PHE A 33 -1.696 -1.132 -2.340 1.00 85.17 C
ATOM 279 CD2 PHE A 33 -1.750 -3.504 -2.050 1.00 85.17 C
ATOM 280 CE1 PHE A 33 -1.312 -0.973 -1.011 1.00 85.17 C
ATOM 281 CE2 PHE A 33 -1.366 -3.354 -0.720 1.00 85.17 C
ATOM 282 CZ PHE A 33 -1.146 -2.087 -0.203 1.00 85.17 C
ATOM 283 N VAL A 34 -4.488 -1.033 -6.377 1.00 79.71 N
ATOM 284 CA VAL A 34 -4.738 -0.771 -7.791 1.00 79.71 C
ATOM 285 C VAL A 34 -3.582 0.033 -8.380 1.00 79.71 C
ATOM 286 CB VAL A 34 -6.072 -0.020 -8.002 1.00 79.71 C
ATOM 287 O VAL A 34 -3.285 1.137 -7.917 1.00 79.71 O
ATOM 288 CG1 VAL A 34 -6.363 0.156 -9.491 1.00 79.71 C
ATOM 289 CG2 VAL A 34 -7.216 -0.762 -7.314 1.00 79.71 C
ATOM 290 N ILE A 35 -2.823 -0.596 -9.337 1.00 77.12 N
ATOM 291 CA ILE A 35 -1.721 0.099 -9.993 1.00 77.12 C
ATOM 292 C ILE A 35 -2.004 0.218 -11.489 1.00 77.12 C
ATOM 293 CB ILE A 35 -0.376 -0.624 -9.757 1.00 77.12 C
ATOM 294 O ILE A 35 -2.768 -0.572 -12.047 1.00 77.12 O
ATOM 295 CG1 ILE A 35 -0.441 -2.059 -10.291 1.00 77.12 C
ATOM 296 CG2 ILE A 35 -0.007 -0.608 -8.271 1.00 77.12 C
ATOM 297 CD1 ILE A 35 0.916 -2.740 -10.402 1.00 77.12 C
ATOM 298 N SER A 36 -1.599 1.325 -11.947 1.00 72.93 N
ATOM 299 CA SER A 36 -1.731 1.517 -13.387 1.00 72.93 C
ATOM 300 C SER A 36 -0.484 1.040 -14.125 1.00 72.93 C
ATOM 301 CB SER A 36 -1.994 2.988 -13.711 1.00 72.93 C
ATOM 302 O SER A 36 0.613 1.040 -13.564 1.00 72.93 O
ATOM 303 OG SER A 36 -1.967 3.208 -15.110 1.00 72.93 O
ATOM 304 N SER A 37 -0.670 0.403 -15.236 1.00 69.39 N
ATOM 305 CA SER A 37 0.426 -0.073 -16.073 1.00 69.39 C
ATOM 306 C SER A 37 1.372 1.064 -16.443 1.00 69.39 C
ATOM 307 CB SER A 37 -0.115 -0.731 -17.343 1.00 69.39 C
ATOM 308 O SER A 37 2.505 0.823 -16.867 1.00 69.39 O
ATOM 309 OG SER A 37 -1.045 0.119 -17.992 1.00 69.39 O
ATOM 310 N ALA A 38 0.862 2.297 -16.248 1.00 67.39 N
ATOM 311 CA ALA A 38 1.656 3.492 -16.525 1.00 67.39 C
ATOM 312 C ALA A 38 2.561 3.834 -15.344 1.00 67.39 C
ATOM 313 CB ALA A 38 0.745 4.672 -16.853 1.00 67.39 C
ATOM 314 O ALA A 38 3.386 4.746 -15.431 1.00 67.39 O
ATOM 315 N ASP A 39 2.279 3.071 -14.251 1.00 72.75 N
ATOM 316 CA ASP A 39 3.057 3.298 -13.037 1.00 72.75 C
ATOM 317 C ASP A 39 4.501 2.833 -13.215 1.00 72.75 C
ATOM 318 CB ASP A 39 2.417 2.580 -11.847 1.00 72.75 C
ATOM 319 O ASP A 39 4.780 1.959 -14.039 1.00 72.75 O
ATOM 320 CG ASP A 39 1.183 3.292 -11.320 1.00 72.75 C
ATOM 321 OD1 ASP A 39 1.293 4.454 -10.874 1.00 72.75 O
ATOM 322 OD2 ASP A 39 0.091 2.684 -11.348 1.00 72.75 O
ATOM 323 N SER A 40 5.475 3.562 -12.768 1.00 72.73 N
ATOM 324 CA SER A 40 6.908 3.286 -12.803 1.00 72.73 C
ATOM 325 C SER A 40 7.256 2.065 -11.958 1.00 72.73 C
ATOM 326 CB SER A 40 7.700 4.499 -12.314 1.00 72.73 C
ATOM 327 O SER A 40 6.491 1.677 -11.073 1.00 72.73 O
ATOM 328 OG SER A 40 7.410 5.640 -13.101 1.00 72.73 O
ATOM 329 N GLU A 41 8.265 1.200 -12.472 1.00 83.34 N
ATOM 330 CA GLU A 41 8.802 0.051 -11.749 1.00 83.34 C
ATOM 331 C GLU A 41 8.950 0.356 -10.261 1.00 83.34 C
ATOM 332 CB GLU A 41 10.152 -0.371 -12.336 1.00 83.34 C
ATOM 333 O GLU A 41 8.722 -0.515 -9.418 1.00 83.34 O
ATOM 334 CG GLU A 41 10.046 -1.058 -13.689 1.00 83.34 C
ATOM 335 CD GLU A 41 11.377 -1.586 -14.200 1.00 83.34 C
ATOM 336 OE1 GLU A 41 11.690 -2.775 -13.962 1.00 83.34 O
ATOM 337 OE2 GLU A 41 12.113 -0.805 -14.842 1.00 83.34 O
ATOM 338 N GLU A 42 9.164 1.541 -9.996 1.00 85.29 N
ATOM 339 CA GLU A 42 9.324 1.965 -8.608 1.00 85.29 C
ATOM 340 C GLU A 42 8.026 1.793 -7.825 1.00 85.29 C
ATOM 341 CB GLU A 42 9.789 3.422 -8.541 1.00 85.29 C
ATOM 342 O GLU A 42 8.031 1.257 -6.715 1.00 85.29 O
ATOM 343 CG GLU A 42 10.241 3.861 -7.156 1.00 85.29 C
ATOM 344 CD GLU A 42 11.177 5.059 -7.182 1.00 85.29 C
ATOM 345 OE1 GLU A 42 10.689 6.208 -7.276 1.00 85.29 O
ATOM 346 OE2 GLU A 42 12.408 4.847 -7.108 1.00 85.29 O
ATOM 347 N ILE A 43 6.916 2.343 -8.302 1.00 82.81 N
ATOM 348 CA ILE A 43 5.610 2.254 -7.658 1.00 82.81 C
ATOM 349 C ILE A 43 5.202 0.789 -7.519 1.00 82.81 C
ATOM 350 CB ILE A 43 4.537 3.036 -8.447 1.00 82.81 C
ATOM 351 O ILE A 43 4.717 0.370 -6.465 1.00 82.81 O
ATOM 352 CG1 ILE A 43 4.822 4.541 -8.387 1.00 82.81 C
ATOM 353 CG2 ILE A 43 3.137 2.722 -7.911 1.00 82.81 C
ATOM 354 CD1 ILE A 43 3.876 5.384 -9.232 1.00 82.81 C
ATOM 355 N LYS A 44 5.549 -0.013 -8.535 1.00 83.23 N
ATOM 356 CA LYS A 44 5.294 -1.450 -8.502 1.00 83.23 C
ATOM 357 C LYS A 44 6.064 -2.118 -7.366 1.00 83.23 C
ATOM 358 CB LYS A 44 5.667 -2.093 -9.838 1.00 83.23 C
ATOM 359 O LYS A 44 5.511 -2.944 -6.636 1.00 83.23 O
ATOM 360 CG LYS A 44 4.624 -1.902 -10.929 1.00 83.23 C
ATOM 361 CD LYS A 44 4.985 -2.679 -12.189 1.00 83.23 C
ATOM 362 CE LYS A 44 4.004 -2.398 -13.320 1.00 83.23 C
ATOM 363 NZ LYS A 44 4.334 -3.183 -14.547 1.00 83.23 N
ATOM 364 N LYS A 45 7.296 -1.791 -7.304 1.00 88.48 N
ATOM 365 CA LYS A 45 8.146 -2.345 -6.255 1.00 88.48 C
ATOM 366 C LYS A 45 7.628 -1.967 -4.870 1.00 88.48 C
ATOM 367 CB LYS A 45 9.589 -1.867 -6.424 1.00 88.48 C
ATOM 368 O LYS A 45 7.601 -2.800 -3.962 1.00 88.48 O
ATOM 369 CG LYS A 45 10.363 -2.605 -7.506 1.00 88.48 C
ATOM 370 CD LYS A 45 11.819 -2.159 -7.554 1.00 88.48 C
ATOM 371 CE LYS A 45 12.582 -2.860 -8.670 1.00 88.48 C
ATOM 372 NZ LYS A 45 14.014 -2.437 -8.712 1.00 88.48 N
ATOM 373 N LEU A 46 7.267 -0.760 -4.707 1.00 90.20 N
ATOM 374 CA LEU A 46 6.764 -0.277 -3.426 1.00 90.20 C
ATOM 375 C LEU A 46 5.478 -1.001 -3.040 1.00 90.20 C
ATOM 376 CB LEU A 46 6.516 1.233 -3.481 1.00 90.20 C
ATOM 377 O LEU A 46 5.308 -1.401 -1.886 1.00 90.20 O
ATOM 378 CG LEU A 46 7.756 2.126 -3.413 1.00 90.20 C
ATOM 379 CD1 LEU A 46 7.391 3.567 -3.755 1.00 90.20 C
ATOM 380 CD2 LEU A 46 8.398 2.045 -2.033 1.00 90.20 C
ATOM 381 N VAL A 47 4.634 -1.055 -3.956 1.00 87.72 N
ATOM 382 CA VAL A 47 3.355 -1.724 -3.743 1.00 87.72 C
ATOM 383 C VAL A 47 3.592 -3.184 -3.363 1.00 87.72 C
ATOM 384 CB VAL A 47 2.454 -1.638 -4.995 1.00 87.72 C
ATOM 385 O VAL A 47 2.953 -3.705 -2.446 1.00 87.72 O
ATOM 386 CG1 VAL A 47 1.201 -2.494 -4.819 1.00 87.72 C
ATOM 387 CG2 VAL A 47 2.077 -0.186 -5.282 1.00 87.72 C
ATOM 388 N GLU A 48 4.453 -3.832 -4.059 1.00 89.12 N
ATOM 389 CA GLU A 48 4.817 -5.214 -3.761 1.00 89.12 C
ATOM 390 C GLU A 48 5.366 -5.347 -2.344 1.00 89.12 C
ATOM 391 CB GLU A 48 5.843 -5.729 -4.774 1.00 89.12 C
ATOM 392 O GLU A 48 5.015 -6.283 -1.622 1.00 89.12 O
ATOM 393 CG GLU A 48 5.260 -6.014 -6.150 1.00 89.12 C
ATOM 394 CD GLU A 48 6.211 -6.777 -7.058 1.00 89.12 C
ATOM 395 OE1 GLU A 48 6.128 -8.025 -7.112 1.00 89.12 O
ATOM 396 OE2 GLU A 48 7.046 -6.122 -7.721 1.00 89.12 O
ATOM 397 N GLU A 49 6.263 -4.525 -2.078 1.00 92.03 N
ATOM 398 CA GLU A 49 6.825 -4.532 -0.731 1.00 92.03 C
ATOM 399 C GLU A 49 5.738 -4.339 0.322 1.00 92.03 C
ATOM 400 CB GLU A 49 7.894 -3.445 -0.589 1.00 92.03 C
ATOM 401 O GLU A 49 5.730 -5.026 1.346 1.00 92.03 O
ATOM 402 CG GLU A 49 9.203 -3.773 -1.292 1.00 92.03 C
ATOM 403 CD GLU A 49 10.340 -2.840 -0.909 1.00 92.03 C
ATOM 404 OE1 GLU A 49 10.970 -3.054 0.152 1.00 92.03 O
ATOM 405 OE2 GLU A 49 10.603 -1.886 -1.675 1.00 92.03 O
ATOM 406 N ASN A 50 4.950 -3.304 0.106 1.00 90.05 N
ATOM 407 CA ASN A 50 3.830 -3.068 1.010 1.00 90.05 C
ATOM 408 C ASN A 50 2.960 -4.313 1.161 1.00 90.05 C
ATOM 409 CB ASN A 50 2.987 -1.887 0.524 1.00 90.05 C
ATOM 410 O ASN A 50 2.539 -4.651 2.269 1.00 90.05 O
ATOM 411 CG ASN A 50 3.539 -0.550 0.978 1.00 90.05 C
ATOM 412 ND2 ASN A 50 4.277 0.116 0.098 1.00 90.05 N
ATOM 413 OD1 ASN A 50 3.305 -0.120 2.110 1.00 90.05 O
ATOM 414 N ALA A 51 2.704 -4.904 0.020 1.00 89.43 N
ATOM 415 CA ALA A 51 1.897 -6.121 -0.004 1.00 89.43 C
ATOM 416 C ALA A 51 2.538 -7.220 0.839 1.00 89.43 C
ATOM 417 CB ALA A 51 1.701 -6.602 -1.439 1.00 89.43 C
ATOM 418 O ALA A 51 1.849 -7.920 1.585 1.00 89.43 O
ATOM 419 N GLU A 52 3.815 -7.414 0.696 1.00 91.12 N
ATOM 420 CA GLU A 52 4.547 -8.437 1.437 1.00 91.12 C
ATOM 421 C GLU A 52 4.436 -8.213 2.942 1.00 91.12 C
ATOM 422 CB GLU A 52 6.018 -8.457 1.015 1.00 91.12 C
ATOM 423 O GLU A 52 4.209 -9.158 3.700 1.00 91.12 O
ATOM 424 CG GLU A 52 6.248 -9.005 -0.387 1.00 91.12 C
ATOM 425 CD GLU A 52 7.720 -9.166 -0.733 1.00 91.12 C
ATOM 426 OE1 GLU A 52 8.313 -10.215 -0.392 1.00 91.12 O
ATOM 427 OE2 GLU A 52 8.285 -8.235 -1.350 1.00 91.12 O
ATOM 428 N ILE A 53 4.609 -7.008 3.402 1.00 91.89 N
ATOM 429 CA ILE A 53 4.531 -6.669 4.819 1.00 91.89 C
ATOM 430 C ILE A 53 3.112 -6.910 5.330 1.00 91.89 C
ATOM 431 CB ILE A 53 4.950 -5.204 5.074 1.00 91.89 C
ATOM 432 O ILE A 53 2.921 -7.486 6.403 1.00 91.89 O
ATOM 433 CG1 ILE A 53 6.419 -4.994 4.686 1.00 91.89 C
ATOM 434 CG2 ILE A 53 4.710 -4.819 6.536 1.00 91.89 C
ATOM 435 CD1 ILE A 53 6.878 -3.544 4.762 1.00 91.89 C
ATOM 436 N LEU A 54 2.220 -6.422 4.598 1.00 90.60 N
ATOM 437 CA LEU A 54 0.817 -6.592 4.960 1.00 90.60 C
ATOM 438 C LEU A 54 0.454 -8.071 5.048 1.00 90.60 C
ATOM 439 CB LEU A 54 -0.087 -5.892 3.942 1.00 90.60 C
ATOM 440 O LEU A 54 -0.274 -8.482 5.955 1.00 90.60 O
ATOM 441 CG LEU A 54 -0.161 -4.367 4.036 1.00 90.60 C
ATOM 442 CD1 LEU A 54 -0.837 -3.792 2.797 1.00 90.60 C
ATOM 443 CD2 LEU A 54 -0.900 -3.944 5.301 1.00 90.60 C
ATOM 444 N GLU A 55 0.909 -8.858 4.086 1.00 90.94 N
ATOM 445 CA GLU A 55 0.715 -10.305 4.103 1.00 90.94 C
ATOM 446 C GLU A 55 1.303 -10.926 5.367 1.00 90.94 C
ATOM 447 CB GLU A 55 1.342 -10.946 2.862 1.00 90.94 C
ATOM 448 O GLU A 55 0.712 -11.837 5.950 1.00 90.94 O
ATOM 449 CG GLU A 55 0.549 -10.716 1.584 1.00 90.94 C
ATOM 450 CD GLU A 55 0.996 -11.603 0.432 1.00 90.94 C
ATOM 451 OE1 GLU A 55 0.436 -12.710 0.266 1.00 90.94 O
ATOM 452 OE2 GLU A 55 1.915 -11.187 -0.308 1.00 90.94 O
ATOM 453 N LYS A 56 2.477 -10.439 5.689 1.00 90.58 N
ATOM 454 CA LYS A 56 3.128 -10.914 6.907 1.00 90.58 C
ATOM 455 C LYS A 56 2.303 -10.564 8.143 1.00 90.58 C
ATOM 456 CB LYS A 56 4.533 -10.324 7.031 1.00 90.58 C
ATOM 457 O LYS A 56 2.287 -11.317 9.119 1.00 90.58 O
ATOM 458 CG LYS A 56 5.551 -10.945 6.085 1.00 90.58 C
ATOM 459 CD LYS A 56 6.949 -10.393 6.327 1.00 90.58 C
ATOM 460 CE LYS A 56 7.967 -11.008 5.376 1.00 90.58 C
ATOM 461 NZ LYS A 56 9.350 -10.516 5.651 1.00 90.58 N
ATOM 462 N LEU A 57 1.666 -9.472 8.081 1.00 89.28 N
ATOM 463 CA LEU A 57 0.843 -8.979 9.180 1.00 89.28 C
ATOM 464 C LEU A 57 -0.522 -9.661 9.185 1.00 89.28 C
ATOM 465 CB LEU A 57 0.668 -7.462 9.080 1.00 89.28 C
ATOM 466 O LEU A 57 -1.332 -9.430 10.085 1.00 89.28 O
ATOM 467 CG LEU A 57 1.906 -6.615 9.381 1.00 89.28 C
ATOM 468 CD1 LEU A 57 1.651 -5.157 9.016 1.00 89.28 C
ATOM 469 CD2 LEU A 57 2.298 -6.744 10.849 1.00 89.28 C
ATOM 470 N GLY A 58 -0.801 -10.562 8.254 1.00 89.24 N
ATOM 471 CA GLY A 58 -2.043 -11.316 8.202 1.00 89.24 C
ATOM 472 C GLY A 58 -3.109 -10.651 7.352 1.00 89.24 C
ATOM 473 O GLY A 58 -4.304 -10.868 7.563 1.00 89.24 O
ATOM 474 N VAL A 59 -2.742 -9.745 6.559 1.00 90.59 N
ATOM 475 CA VAL A 59 -3.658 -9.015 5.689 1.00 90.59 C
ATOM 476 C VAL A 59 -3.626 -9.615 4.285 1.00 90.59 C
ATOM 477 CB VAL A 59 -3.312 -7.511 5.634 1.00 90.59 C
ATOM 478 O VAL A 59 -2.551 -9.854 3.730 1.00 90.59 O
ATOM 479 CG1 VAL A 59 -4.305 -6.760 4.750 1.00 90.59 C
ATOM 480 CG2 VAL A 59 -3.290 -6.917 7.042 1.00 90.59 C
ATOM 481 N LYS A 60 -4.796 -10.038 3.669 1.00 91.31 N
ATOM 482 CA LYS A 60 -4.890 -10.525 2.295 1.00 91.31 C
ATOM 483 C LYS A 60 -4.751 -9.380 1.296 1.00 91.31 C
ATOM 484 CB LYS A 60 -6.215 -11.256 2.074 1.00 91.31 C
ATOM 485 O LYS A 60 -5.554 -8.444 1.300 1.00 91.31 O
ATOM 486 CG LYS A 60 -6.280 -12.039 0.771 1.00 91.31 C
ATOM 487 CD LYS A 60 -7.562 -12.856 0.673 1.00 91.31 C
ATOM 488 CE LYS A 60 -7.618 -13.657 -0.621 1.00 91.31 C
ATOM 489 NZ LYS A 60 -8.859 -14.483 -0.707 1.00 91.31 N
ATOM 490 N VAL A 61 -3.640 -9.388 0.542 1.00 90.50 N
ATOM 491 CA VAL A 61 -3.325 -8.295 -0.373 1.00 90.50 C
ATOM 492 C VAL A 61 -3.627 -8.719 -1.808 1.00 90.50 C
ATOM 493 CB VAL A 61 -1.849 -7.856 -0.246 1.00 90.50 C
ATOM 494 O VAL A 61 -3.177 -9.776 -2.258 1.00 90.50 O
ATOM 495 CG1 VAL A 61 -1.548 -6.696 -1.192 1.00 90.50 C
ATOM 496 CG2 VAL A 61 -1.531 -7.470 1.197 1.00 90.50 C
ATOM 497 N LYS A 62 -4.464 -7.942 -2.568 1.00 89.89 N
ATOM 498 CA LYS A 62 -4.755 -8.135 -3.986 1.00 89.89 C
ATOM 499 C LYS A 62 -4.271 -6.948 -4.813 1.00 89.89 C
ATOM 500 CB LYS A 62 -6.254 -8.348 -4.202 1.00 89.89 C
ATOM 501 O LYS A 62 -4.598 -5.799 -4.510 1.00 89.89 O
ATOM 502 CG LYS A 62 -6.615 -8.835 -5.598 1.00 89.89 C
ATOM 503 CD LYS A 62 -8.100 -9.156 -5.711 1.00 89.89 C
ATOM 504 CE LYS A 62 -8.454 -9.686 -7.094 1.00 89.89 C
ATOM 505 NZ LYS A 62 -9.906 -10.018 -7.205 1.00 89.89 N
ATOM 506 N VAL A 63 -3.369 -7.174 -5.795 1.00 85.66 N
ATOM 507 CA VAL A 63 -2.847 -6.112 -6.649 1.00 85.66 C
ATOM 508 C VAL A 63 -3.490 -6.198 -8.032 1.00 85.66 C
ATOM 509 CB VAL A 63 -1.309 -6.185 -6.772 1.00 85.66 C
ATOM 510 O VAL A 63 -3.359 -7.211 -8.723 1.00 85.66 O
ATOM 511 CG1 VAL A 63 -0.779 -5.034 -7.625 1.00 85.66 C
ATOM 512 CG2 VAL A 63 -0.663 -6.171 -5.388 1.00 85.66 C
ATOM 513 N VAL A 64 -4.304 -5.230 -8.309 1.00 83.17 N
ATOM 514 CA VAL A 64 -5.001 -5.174 -9.589 1.00 83.17 C
ATOM 515 C VAL A 64 -4.339 -4.137 -10.494 1.00 83.17 C
ATOM 516 CB VAL A 64 -6.499 -4.843 -9.406 1.00 83.17 C
ATOM 517 O VAL A 64 -4.094 -3.004 -10.073 1.00 83.17 O
ATOM 518 CG1 VAL A 64 -7.221 -4.843 -10.753 1.00 83.17 C
ATOM 519 CG2 VAL A 64 -7.152 -5.836 -8.447 1.00 83.17 C
ATOM 520 N GLU A 65 -3.914 -4.576 -11.655 1.00 82.38 N
ATOM 521 CA GLU A 65 -3.300 -3.697 -12.646 1.00 82.38 C
ATOM 522 C GLU A 65 -4.341 -3.154 -13.621 1.00 82.38 C
ATOM 523 CB GLU A 65 -2.198 -4.434 -13.410 1.00 82.38 C
ATOM 524 O GLU A 65 -5.007 -3.922 -14.318 1.00 82.38 O
ATOM 525 CG GLU A 65 -1.357 -3.531 -14.300 1.00 82.38 C
ATOM 526 CD GLU A 65 -0.228 -4.266 -15.005 1.00 82.38 C
ATOM 527 OE1 GLU A 65 -0.445 -4.784 -16.124 1.00 82.38 O
ATOM 528 OE2 GLU A 65 0.884 -4.325 -14.433 1.00 82.38 O
ATOM 529 N LEU A 66 -4.731 -1.848 -13.511 1.00 73.61 N
ATOM 530 CA LEU A 66 -5.704 -1.232 -14.407 1.00 73.61 C
ATOM 531 C LEU A 66 -5.031 -0.737 -15.682 1.00 73.61 C
ATOM 532 CB LEU A 66 -6.417 -0.072 -13.708 1.00 73.61 C
ATOM 533 O LEU A 66 -3.927 -0.189 -15.634 1.00 73.61 O
ATOM 534 CG LEU A 66 -7.425 -0.450 -12.622 1.00 73.61 C
ATOM 535 CD1 LEU A 66 -7.847 0.787 -11.836 1.00 73.61 C
ATOM 536 CD2 LEU A 66 -8.638 -1.141 -13.234 1.00 73.61 C
ATOM 537 N GLY A 67 -5.215 -1.540 -16.831 1.00 61.81 N
ATOM 538 CA GLY A 67 -4.761 -1.145 -18.155 1.00 61.81 C
ATOM 539 C GLY A 67 -5.505 0.056 -18.708 1.00 61.81 C
ATOM 540 O GLY A 67 -6.738 0.065 -18.746 1.00 61.81 O
ATOM 541 N GLY A 68 -5.090 1.390 -18.491 1.00 60.96 N
ATOM 542 CA GLY A 68 -5.697 2.625 -18.962 1.00 60.96 C
ATOM 543 C GLY A 68 -4.768 3.820 -18.865 1.00 60.96 C
ATOM 544 O GLY A 68 -3.738 3.760 -18.191 1.00 60.96 O
ATOM 545 N GLU A 69 -4.531 4.607 -19.986 1.00 63.19 N
ATOM 546 CA GLU A 69 -3.769 5.840 -20.158 1.00 63.19 C
ATOM 547 C GLU A 69 -3.863 6.725 -18.918 1.00 63.19 C
ATOM 548 CB GLU A 69 -4.257 6.607 -21.389 1.00 63.19 C
ATOM 549 O GLU A 69 -4.938 6.870 -18.332 1.00 63.19 O
ATOM 550 CG GLU A 69 -3.142 7.015 -22.341 1.00 63.19 C
ATOM 551 CD GLU A 69 -3.653 7.632 -23.634 1.00 63.19 C
ATOM 552 OE1 GLU A 69 -3.936 8.852 -23.654 1.00 63.19 O
ATOM 553 OE2 GLU A 69 -3.771 6.890 -24.634 1.00 63.19 O
ATOM 554 N LEU A 70 -2.763 6.805 -18.127 1.00 62.85 N
ATOM 555 CA LEU A 70 -2.656 7.713 -16.990 1.00 62.85 C
ATOM 556 C LEU A 70 -3.013 9.139 -17.398 1.00 62.85 C
ATOM 557 CB LEU A 70 -1.242 7.676 -16.406 1.00 62.85 C
ATOM 558 O LEU A 70 -2.495 9.653 -18.391 1.00 62.85 O
ATOM 559 CG LEU A 70 -0.896 6.460 -15.544 1.00 62.85 C
ATOM 560 CD1 LEU A 70 0.611 6.230 -15.534 1.00 62.85 C
ATOM 561 CD2 LEU A 70 -1.425 6.642 -14.126 1.00 62.85 C
ATOM 562 N ASP A 71 -4.382 9.504 -17.115 1.00 67.78 N
ATOM 563 CA ASP A 71 -4.779 10.889 -17.347 1.00 67.78 C
ATOM 564 C ASP A 71 -3.796 11.859 -16.695 1.00 67.78 C
ATOM 565 CB ASP A 71 -6.193 11.139 -16.819 1.00 67.78 C
ATOM 566 O ASP A 71 -3.070 11.487 -15.770 1.00 67.78 O
ATOM 567 CG ASP A 71 -6.996 12.083 -17.696 1.00 67.78 C
ATOM 568 OD1 ASP A 71 -7.054 13.295 -17.394 1.00 67.78 O
ATOM 569 OD2 ASP A 71 -7.578 11.611 -18.697 1.00 67.78 O
ATOM 570 N GLU A 72 -3.069 12.841 -17.280 1.00 66.31 N
ATOM 571 CA GLU A 72 -2.192 13.936 -16.876 1.00 66.31 C
ATOM 572 C GLU A 72 -2.299 14.205 -15.378 1.00 66.31 C
ATOM 573 CB GLU A 72 -2.521 15.207 -17.663 1.00 66.31 C
ATOM 574 O GLU A 72 -1.296 14.487 -14.719 1.00 66.31 O
ATOM 575 CG GLU A 72 -1.767 15.327 -18.980 1.00 66.31 C
ATOM 576 CD GLU A 72 -2.109 16.590 -19.754 1.00 66.31 C
ATOM 577 OE1 GLU A 72 -1.474 17.642 -19.511 1.00 66.31 O
ATOM 578 OE2 GLU A 72 -3.020 16.529 -20.609 1.00 66.31 O
ATOM 579 N LEU A 73 -3.365 13.942 -14.773 1.00 66.33 N
ATOM 580 CA LEU A 73 -3.564 14.142 -13.342 1.00 66.33 C
ATOM 581 C LEU A 73 -2.893 13.032 -12.539 1.00 66.33 C
ATOM 582 CB LEU A 73 -5.058 14.196 -13.010 1.00 66.33 C
ATOM 583 O LEU A 73 -2.291 13.292 -11.495 1.00 66.33 O
ATOM 584 CG LEU A 73 -5.727 15.567 -13.115 1.00 66.33 C
ATOM 585 CD1 LEU A 73 -7.205 15.411 -13.456 1.00 66.33 C
ATOM 586 CD2 LEU A 73 -5.551 16.349 -11.818 1.00 66.33 C
ATOM 587 N THR A 74 -2.894 11.771 -13.049 1.00 65.78 N
ATOM 588 CA THR A 74 -2.225 10.632 -12.430 1.00 65.78 C
ATOM 589 C THR A 74 -0.710 10.750 -12.575 1.00 65.78 C
ATOM 590 CB THR A 74 -2.699 9.303 -13.046 1.00 65.78 C
ATOM 591 O THR A 74 0.035 10.429 -11.646 1.00 65.78 O
ATOM 592 CG2 THR A 74 -2.057 8.111 -12.344 1.00 65.78 C
ATOM 593 OG1 THR A 74 -4.124 9.206 -12.920 1.00 65.78 O
ATOM 594 N LEU A 75 -0.311 11.344 -13.746 1.00 68.90 N
ATOM 595 CA LEU A 75 1.107 11.593 -13.982 1.00 68.90 C
ATOM 596 C LEU A 75 1.625 12.698 -13.068 1.00 68.90 C
ATOM 597 CB LEU A 75 1.348 11.970 -15.446 1.00 68.90 C
ATOM 598 O LEU A 75 2.740 12.610 -12.550 1.00 68.90 O
ATOM 599 CG LEU A 75 1.401 10.812 -16.445 1.00 68.90 C
ATOM 600 CD1 LEU A 75 1.017 11.297 -17.839 1.00 68.90 C
ATOM 601 CD2 LEU A 75 2.788 10.179 -16.456 1.00 68.90 C
ATOM 602 N LYS A 76 0.708 13.698 -12.805 1.00 71.32 N
ATOM 603 CA LYS A 76 1.071 14.813 -11.936 1.00 71.32 C
ATOM 604 C LYS A 76 1.146 14.372 -10.477 1.00 71.32 C
ATOM 605 CB LYS A 76 0.068 15.959 -12.086 1.00 71.32 C
ATOM 606 O LYS A 76 1.991 14.854 -9.720 1.00 71.32 O
ATOM 607 CG LYS A 76 0.514 17.260 -11.436 1.00 71.32 C
ATOM 608 CD LYS A 76 -0.488 18.381 -11.684 1.00 71.32 C
ATOM 609 CE LYS A 76 -0.066 19.672 -10.996 1.00 71.32 C
ATOM 610 NZ LYS A 76 -1.069 20.760 -11.197 1.00 71.32 N
ATOM 611 N HIS A 77 0.420 13.365 -10.189 1.00 75.38 N
ATOM 612 CA HIS A 77 0.315 12.913 -8.807 1.00 75.38 C
ATOM 613 C HIS A 77 1.167 11.672 -8.567 1.00 75.38 C
ATOM 614 CB HIS A 77 -1.144 12.626 -8.446 1.00 75.38 C
ATOM 615 O HIS A 77 1.146 11.101 -7.474 1.00 75.38 O
ATOM 616 CG HIS A 77 -1.983 13.857 -8.316 1.00 75.38 C
ATOM 617 CD2 HIS A 77 -1.961 14.855 -7.401 1.00 75.38 C
ATOM 618 ND1 HIS A 77 -2.994 14.166 -9.199 1.00 75.38 N
ATOM 619 CE1 HIS A 77 -3.559 15.304 -8.833 1.00 75.38 C
ATOM 620 NE2 HIS A 77 -2.951 15.743 -7.744 1.00 75.38 N
ATOM 621 N LYS A 78 1.936 11.316 -9.653 1.00 73.17 N
ATOM 622 CA LYS A 78 2.824 10.158 -9.610 1.00 73.17 C
ATOM 623 C LYS A 78 3.862 10.302 -8.500 1.00 73.17 C
ATOM 624 CB LYS A 78 3.521 9.965 -10.957 1.00 73.17 C
ATOM 625 O LYS A 78 4.117 9.353 -7.755 1.00 73.17 O
ATOM 626 CG LYS A 78 4.245 8.633 -11.095 1.00 73.17 C
ATOM 627 CD LYS A 78 4.838 8.461 -12.487 1.00 73.17 C
ATOM 628 CE LYS A 78 5.641 7.172 -12.598 1.00 73.17 C
ATOM 629 NZ LYS A 78 6.222 6.996 -13.963 1.00 73.17 N
ATOM 630 N GLU A 79 4.467 11.425 -8.459 1.00 82.01 N
ATOM 631 CA GLU A 79 5.451 11.676 -7.410 1.00 82.01 C
ATOM 632 C GLU A 79 4.810 11.619 -6.027 1.00 82.01 C
ATOM 633 CB GLU A 79 6.127 13.034 -7.621 1.00 82.01 C
ATOM 634 O GLU A 79 5.388 11.062 -5.091 1.00 82.01 O
ATOM 635 CG GLU A 79 7.125 13.052 -8.769 1.00 82.01 C
ATOM 636 CD GLU A 79 7.967 14.317 -8.812 1.00 82.01 C
ATOM 637 OE1 GLU A 79 9.088 14.320 -8.254 1.00 82.01 O
ATOM 638 OE2 GLU A 79 7.502 15.314 -9.408 1.00 82.01 O
ATOM 639 N LYS A 80 3.748 12.178 -5.930 1.00 81.73 N
ATOM 640 CA LYS A 80 3.031 12.190 -4.659 1.00 81.73 C
ATOM 641 C LYS A 80 2.604 10.781 -4.255 1.00 81.73 C
ATOM 642 CB LYS A 80 1.808 13.105 -4.740 1.00 81.73 C
ATOM 643 O LYS A 80 2.693 10.412 -3.082 1.00 81.73 O
ATOM 644 CG LYS A 80 2.129 14.583 -4.570 1.00 81.73 C
ATOM 645 CD LYS A 80 0.863 15.427 -4.516 1.00 81.73 C
ATOM 646 CE LYS A 80 1.184 16.914 -4.444 1.00 81.73 C
ATOM 647 NZ LYS A 80 -0.055 17.744 -4.351 1.00 81.73 N
ATOM 648 N ILE A 81 2.180 10.044 -5.166 1.00 80.10 N
ATOM 649 CA ILE A 81 1.790 8.655 -4.957 1.00 80.10 C
ATOM 650 C ILE A 81 2.993 7.848 -4.473 1.00 80.10 C
ATOM 651 CB ILE A 81 1.208 8.030 -6.245 1.00 80.10 C
ATOM 652 O ILE A 81 2.875 7.039 -3.550 1.00 80.10 O
ATOM 653 CG1 ILE A 81 -0.130 8.688 -6.600 1.00 80.10 C
ATOM 654 CG2 ILE A 81 1.050 6.515 -6.087 1.00 80.10 C
ATOM 655 CD1 ILE A 81 -0.679 8.279 -7.960 1.00 80.10 C
ATOM 656 N LEU A 82 4.081 8.122 -5.134 1.00 83.52 N
ATOM 657 CA LEU A 82 5.328 7.478 -4.739 1.00 83.52 C
ATOM 658 C LEU A 82 5.679 7.813 -3.294 1.00 83.52 C
ATOM 659 CB LEU A 82 6.470 7.905 -5.666 1.00 83.52 C
ATOM 660 O LEU A 82 5.984 6.919 -2.501 1.00 83.52 O
ATOM 661 CG LEU A 82 7.813 7.204 -5.456 1.00 83.52 C
ATOM 662 CD1 LEU A 82 7.738 5.760 -5.939 1.00 83.52 C
ATOM 663 CD2 LEU A 82 8.927 7.957 -6.174 1.00 83.52 C
ATOM 664 N GLU A 83 5.694 9.021 -2.968 1.00 87.96 N
ATOM 665 CA GLU A 83 6.011 9.477 -1.618 1.00 87.96 C
ATOM 666 C GLU A 83 5.056 8.873 -0.593 1.00 87.96 C
ATOM 667 CB GLU A 83 5.967 11.005 -1.542 1.00 87.96 C
ATOM 668 O GLU A 83 5.488 8.386 0.454 1.00 87.96 O
ATOM 669 CG GLU A 83 6.496 11.571 -0.233 1.00 87.96 C
ATOM 670 CD GLU A 83 6.453 13.090 -0.175 1.00 87.96 C
ATOM 671 OE1 GLU A 83 5.424 13.652 0.264 1.00 87.96 O
ATOM 672 OE2 GLU A 83 7.456 13.723 -0.573 1.00 87.96 O
ATOM 673 N LYS A 84 3.752 8.925 -0.931 1.00 87.17 N
ATOM 674 CA LYS A 84 2.749 8.416 0.000 1.00 87.17 C
ATOM 675 C LYS A 84 2.865 6.903 0.160 1.00 87.17 C
ATOM 676 CB LYS A 84 1.342 8.787 -0.470 1.00 87.17 C
ATOM 677 O LYS A 84 2.708 6.376 1.264 1.00 87.17 O
ATOM 678 CG LYS A 84 1.031 10.274 -0.383 1.00 87.17 C
ATOM 679 CD LYS A 84 0.932 10.739 1.064 1.00 87.17 C
ATOM 680 CE LYS A 84 0.563 12.214 1.154 1.00 87.17 C
ATOM 681 NZ LYS A 84 0.580 12.705 2.564 1.00 87.17 N
ATOM 682 N THR A 85 3.075 6.194 -0.878 1.00 86.48 N
ATOM 683 CA THR A 85 3.277 4.749 -0.859 1.00 86.48 C
ATOM 684 C THR A 85 4.478 4.381 0.007 1.00 86.48 C
ATOM 685 CB THR A 85 3.478 4.195 -2.282 1.00 86.48 C
ATOM 686 O THR A 85 4.438 3.396 0.747 1.00 86.48 O
ATOM 687 CG2 THR A 85 3.571 2.673 -2.271 1.00 86.48 C
ATOM 688 OG1 THR A 85 2.371 4.589 -3.103 1.00 86.48 O
ATOM 689 N LYS A 86 5.560 5.179 -0.120 1.00 89.61 N
ATOM 690 CA LYS A 86 6.738 4.988 0.722 1.00 89.61 C
ATOM 691 C LYS A 86 6.390 5.146 2.199 1.00 89.61 C
ATOM 692 CB LYS A 86 7.840 5.975 0.335 1.00 89.61 C
ATOM 693 O LYS A 86 6.864 4.379 3.040 1.00 89.61 O
ATOM 694 CG LYS A 86 8.596 5.594 -0.930 1.00 89.61 C
ATOM 695 CD LYS A 86 9.745 6.555 -1.205 1.00 89.61 C
ATOM 696 CE LYS A 86 10.475 6.201 -2.494 1.00 89.61 C
ATOM 697 NZ LYS A 86 11.592 7.151 -2.777 1.00 89.61 N
ATOM 698 N LEU A 87 5.725 6.133 2.442 1.00 90.63 N
ATOM 699 CA LEU A 87 5.303 6.398 3.814 1.00 90.63 C
ATOM 700 C LEU A 87 4.520 5.217 4.379 1.00 90.63 C
ATOM 701 CB LEU A 87 4.449 7.667 3.877 1.00 90.63 C
ATOM 702 O LEU A 87 4.736 4.815 5.524 1.00 90.63 O
ATOM 703 CG LEU A 87 4.127 8.198 5.274 1.00 90.63 C
ATOM 704 CD1 LEU A 87 5.400 8.666 5.971 1.00 90.63 C
ATOM 705 CD2 LEU A 87 3.108 9.330 5.195 1.00 90.63 C
ATOM 706 N VAL A 88 3.611 4.770 3.617 1.00 89.50 N
ATOM 707 CA VAL A 88 2.818 3.610 4.012 1.00 89.50 C
ATOM 708 C VAL A 88 3.741 2.436 4.330 1.00 89.50 C
ATOM 709 CB VAL A 88 1.808 3.212 2.913 1.00 89.50 C
ATOM 710 O VAL A 88 3.547 1.739 5.328 1.00 89.50 O
ATOM 711 CG1 VAL A 88 1.118 1.895 3.265 1.00 89.50 C
ATOM 712 CG2 VAL A 88 0.777 4.321 2.708 1.00 89.50 C
ATOM 713 N LEU A 89 4.633 2.181 3.505 1.00 90.95 N
ATOM 714 CA LEU A 89 5.629 1.136 3.718 1.00 90.95 C
ATOM 715 C LEU A 89 6.348 1.334 5.048 1.00 90.95 C
ATOM 716 CB LEU A 89 6.644 1.123 2.572 1.00 90.95 C
ATOM 717 O LEU A 89 6.528 0.380 5.809 1.00 90.95 O
ATOM 718 CG LEU A 89 7.716 0.033 2.629 1.00 90.95 C
ATOM 719 CD1 LEU A 89 7.069 -1.348 2.620 1.00 90.95 C
ATOM 720 CD2 LEU A 89 8.691 0.180 1.466 1.00 90.95 C
ATOM 721 N GLU A 90 6.858 2.505 5.268 1.00 91.77 N
ATOM 722 CA GLU A 90 7.540 2.813 6.521 1.00 91.77 C
ATOM 723 C GLU A 90 6.648 2.516 7.723 1.00 91.77 C
ATOM 724 CB GLU A 90 7.985 4.278 6.545 1.00 91.77 C
ATOM 725 O GLU A 90 7.113 1.982 8.731 1.00 91.77 O
ATOM 726 CG GLU A 90 9.127 4.590 5.589 1.00 91.77 C
ATOM 727 CD GLU A 90 10.319 5.241 6.271 1.00 91.77 C
ATOM 728 OE1 GLU A 90 11.320 4.540 6.544 1.00 91.77 O
ATOM 729 OE2 GLU A 90 10.252 6.462 6.535 1.00 91.77 O
ATOM 730 N ILE A 91 5.411 2.885 7.569 1.00 90.84 N
ATOM 731 CA ILE A 91 4.447 2.638 8.635 1.00 90.84 C
ATOM 732 C ILE A 91 4.287 1.135 8.849 1.00 90.84 C
ATOM 733 CB ILE A 91 3.078 3.283 8.321 1.00 90.84 C
ATOM 734 O ILE A 91 4.286 0.660 9.987 1.00 90.84 O
ATOM 735 CG1 ILE A 91 3.208 4.810 8.263 1.00 90.84 C
ATOM 736 CG2 ILE A 91 2.031 2.861 9.356 1.00 90.84 C
ATOM 737 CD1 ILE A 91 1.989 5.514 7.684 1.00 90.84 C
ATOM 738 N LEU A 92 4.083 0.438 7.825 1.00 90.22 N
ATOM 739 CA LEU A 92 3.929 -1.012 7.890 1.00 90.22 C
ATOM 740 C LEU A 92 5.163 -1.662 8.506 1.00 90.22 C
ATOM 741 CB LEU A 92 3.677 -1.587 6.494 1.00 90.22 C
ATOM 742 O LEU A 92 5.045 -2.579 9.322 1.00 90.22 O
ATOM 743 CG LEU A 92 2.300 -1.314 5.887 1.00 90.22 C
ATOM 744 CD1 LEU A 92 2.262 -1.772 4.432 1.00 90.22 C
ATOM 745 CD2 LEU A 92 1.211 -2.006 6.699 1.00 90.22 C
ATOM 746 N LYS A 93 6.355 -1.270 8.049 1.00 89.96 N
ATOM 747 CA LYS A 93 7.611 -1.791 8.582 1.00 89.96 C
ATOM 748 C LYS A 93 7.703 -1.568 10.089 1.00 89.96 C
ATOM 749 CB LYS A 93 8.803 -1.137 7.881 1.00 89.96 C
ATOM 750 O LYS A 93 8.149 -2.450 10.826 1.00 89.96 O
ATOM 751 CG LYS A 93 9.068 -1.672 6.481 1.00 89.96 C
ATOM 752 CD LYS A 93 10.333 -1.070 5.884 1.00 89.96 C
ATOM 753 CE LYS A 93 10.606 -1.615 4.488 1.00 89.96 C
ATOM 754 NZ LYS A 93 11.842 -1.022 3.894 1.00 89.96 N
ATOM 755 N GLU A 94 7.401 -0.356 10.430 1.00 91.25 N
ATOM 756 CA GLU A 94 7.387 -0.032 11.853 1.00 91.25 C
ATOM 757 C GLU A 94 6.457 -0.965 12.623 1.00 91.25 C
ATOM 758 CB GLU A 94 6.968 1.425 12.069 1.00 91.25 C
ATOM 759 O GLU A 94 6.797 -1.429 13.713 1.00 91.25 O
ATOM 760 CG GLU A 94 8.067 2.432 11.765 1.00 91.25 C
ATOM 761 CD GLU A 94 7.783 3.817 12.324 1.00 91.25 C
ATOM 762 OE1 GLU A 94 8.221 4.116 13.459 1.00 91.25 O
ATOM 763 OE2 GLU A 94 7.116 4.610 11.623 1.00 91.25 O
ATOM 764 N ARG A 95 5.355 -1.225 12.075 1.00 85.31 N
ATOM 765 CA ARG A 95 4.398 -2.127 12.709 1.00 85.31 C
ATOM 766 C ARG A 95 4.938 -3.553 12.756 1.00 85.31 C
ATOM 767 CB ARG A 95 3.059 -2.098 11.969 1.00 85.31 C
ATOM 768 O ARG A 95 4.712 -4.276 13.728 1.00 85.31 O
ATOM 769 CG ARG A 95 2.304 -0.786 12.115 1.00 85.31 C
ATOM 770 CD ARG A 95 1.648 -0.662 13.483 1.00 85.31 C
ATOM 771 NE ARG A 95 0.780 0.509 13.560 1.00 85.31 N
ATOM 772 NH1 ARG A 95 -1.032 -0.634 14.424 1.00 85.31 N
ATOM 773 NH2 ARG A 95 -1.174 1.620 14.033 1.00 85.31 N
ATOM 774 CZ ARG A 95 -0.474 0.496 14.006 1.00 85.31 C
ATOM 775 N LEU A 96 5.431 -3.928 11.737 1.00 87.53 N
ATOM 776 CA LEU A 96 6.039 -5.251 11.646 1.00 87.53 C
ATOM 777 C LEU A 96 7.142 -5.415 12.687 1.00 87.53 C
ATOM 778 CB LEU A 96 6.605 -5.486 10.244 1.00 87.53 C
ATOM 779 O LEU A 96 7.260 -6.471 13.311 1.00 87.53 O
ATOM 780 CG LEU A 96 7.106 -6.899 9.943 1.00 87.53 C
ATOM 781 CD1 LEU A 96 5.938 -7.879 9.903 1.00 87.53 C
ATOM 782 CD2 LEU A 96 7.877 -6.925 8.627 1.00 87.53 C
ATOM 783 N LYS A 97 8.035 -4.458 12.855 1.00 87.68 N
ATOM 784 CA LYS A 97 9.111 -4.470 13.842 1.00 87.68 C
ATOM 785 C LYS A 97 8.555 -4.573 15.260 1.00 87.68 C
ATOM 786 CB LYS A 97 9.976 -3.216 13.709 1.00 87.68 C
ATOM 787 O LYS A 97 9.131 -5.256 16.109 1.00 87.68 O
ATOM 788 CG LYS A 97 10.961 -3.264 12.550 1.00 87.68 C
ATOM 789 CD LYS A 97 11.875 -2.045 12.542 1.00 87.68 C
ATOM 790 CE LYS A 97 12.840 -2.076 11.365 1.00 87.68 C
ATOM 791 NZ LYS A 97 13.742 -0.886 11.356 1.00 87.68 N
ATOM 792 N GLU A 98 7.395 -3.949 15.535 1.00 85.16 N
ATOM 793 CA GLU A 98 6.766 -3.985 16.852 1.00 85.16 C
ATOM 794 C GLU A 98 6.185 -5.364 17.149 1.00 85.16 C
ATOM 795 CB GLU A 98 5.671 -2.920 16.954 1.00 85.16 C
ATOM 796 O GLU A 98 6.174 -5.804 18.300 1.00 85.16 O
ATOM 797 CG GLU A 98 6.201 -1.494 16.993 1.00 85.16 C
ATOM 798 CD GLU A 98 5.199 -0.496 17.550 1.00 85.16 C
ATOM 799 OE1 GLU A 98 5.207 -0.247 18.777 1.00 85.16 O
ATOM 800 OE2 GLU A 98 4.397 0.041 16.753 1.00 85.16 O
ATOM 801 N LYS A 99 5.862 -6.201 16.145 1.00 71.59 N
ATOM 802 CA LYS A 99 5.236 -7.516 16.249 1.00 71.59 C
ATOM 803 C LYS A 99 6.285 -8.620 16.338 1.00 71.59 C
ATOM 804 CB LYS A 99 4.311 -7.766 15.057 1.00 71.59 C
ATOM 805 O LYS A 99 6.031 -9.682 16.910 1.00 71.59 O
ATOM 806 CG LYS A 99 2.886 -7.276 15.266 1.00 71.59 C
ATOM 807 CD LYS A 99 1.932 -7.876 14.240 1.00 71.59 C
ATOM 808 CE LYS A 99 0.499 -7.420 14.474 1.00 71.59 C
ATOM 809 NZ LYS A 99 -0.480 -8.264 13.726 1.00 71.59 N
ATOM 810 N GLU A 100 7.534 -8.377 16.016 1.00 69.93 N
ATOM 811 CA GLU A 100 8.637 -9.325 16.142 1.00 69.93 C
ATOM 812 C GLU A 100 9.233 -9.296 17.546 1.00 69.93 C
ATOM 813 CB GLU A 100 9.723 -9.028 15.104 1.00 69.93 C
ATOM 814 O GLU A 100 9.551 -10.343 18.113 1.00 69.93 O
ATOM 815 CG GLU A 100 9.683 -9.948 13.892 1.00 69.93 C
ATOM 816 CD GLU A 100 10.905 -9.814 12.998 1.00 69.93 C
ATOM 817 OE1 GLU A 100 11.866 -10.601 13.160 1.00 69.93 O
ATOM 818 OE2 GLU A 100 10.903 -8.915 12.128 1.00 69.93 O
ENDMDL
END

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,17 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 NKI/AVL, Netherlands Cancer Institute
*
*
* Copyright (c) 2020 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,31 +24,38 @@
* 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("q-1")
TEST_CASE("fmt_1")
{
using namespace cif::literals;
std::ostringstream os;
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);
}
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");
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';
}

View File

@@ -54,7 +54,7 @@ TEST_CASE("create_nonpoly_1")
cif::file file;
auto &&[dbi, ignore] = file.emplace("TEST"); // create a datablock
dbi->set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
dbi->load_dictionary("mmcif_pdbx.dic");
cif::mm::structure structure(file);
@@ -82,7 +82,7 @@ _atom_site.pdbx_formal_charge
# that's enough to test with
)"_cf;
atoms.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
atoms.front().load_dictionary("mmcif_pdbx.dic");
auto &hem_data = atoms["HEM"];
auto &atom_site = hem_data["atom_site"];
@@ -159,7 +159,7 @@ _struct_asym.details ?
_atom_type.symbol C
)"_cf;
expected.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
expected.front().load_dictionary("mmcif_pdbx.dic");
if (not(expected.front() == structure.get_datablock()))
{
@@ -178,7 +178,7 @@ TEST_CASE("create_nonpoly_2")
cif::file file;
auto &&[dbi, ignore] = file.emplace("TEST"); // create a datablock
dbi->set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
dbi->load_dictionary("mmcif_pdbx.dic");
cif::mm::structure structure(file);
@@ -270,7 +270,7 @@ _struct_asym.details ?
_atom_type.symbol C
)"_cf;
expected.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
expected.front().load_dictionary("mmcif_pdbx.dic");
REQUIRE(expected.front() == structure.get_datablock());
@@ -354,7 +354,7 @@ _struct_asym.details ?
#
)"_cf;
data.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
data.front().load_dictionary("mmcif_pdbx.dic");
cif::mm::structure s(data);
@@ -431,169 +431,3 @@ 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());
}
}

View File

@@ -28,7 +28,6 @@
#include <cif++.hpp>
#include <filesystem>
#include <iostream>
#include <fstream>

View File

@@ -11,7 +11,12 @@ int main(int argc, char *argv[])
Catch::Session session; // There must be exactly one instance
// Build a new parser on top of Catch2's
#if CATCH22
using namespace Catch::clara;
#else
// Build a new parser on top of Catch2's
using namespace Catch::Clara;
#endif
auto cli = session.cli() // Get Catch2's command line parser
| Opt(gTestDir, "data-dir") // bind variable to a new option, with a hint string

View File

@@ -1,17 +1,17 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
*
* Copyright (c) 2024 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
@@ -26,7 +26,11 @@
#pragma once
#if CATCH22
#include <catch2/catch.hpp>
#else
#include <catch2/catch_all.hpp>
#endif
#include <filesystem>

View File

@@ -30,11 +30,6 @@
#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>
// --------------------------------------------------------------------
@@ -95,7 +90,7 @@ TEST_CASE("t1")
const auto &&[angle, axis] = cif::quaternion_to_angle_axis(q2);
CHECK_THAT(std::fmod(360 + angle, 360), Catch::Matchers::WithinRel(std::fmod(360 - angle0, 360), 0.01));
REQUIRE_THAT(std::fmod(360 + angle, 360), Catch::Matchers::WithinRel(std::fmod(360 - angle0, 360), 0.01));
for (auto &p : p1)
p.rotate(q2);
@@ -121,7 +116,7 @@ TEST_CASE("t2")
auto &&[angle, axis] = cif::quaternion_to_angle_axis(q);
CHECK_THAT(angle, Catch::Matchers::WithinRel(45.f, 0.01f));
REQUIRE_THAT(angle, Catch::Matchers::WithinRel(45.f, 0.01f));
}
TEST_CASE("t3")
@@ -145,7 +140,7 @@ TEST_CASE("t3")
double a = cif::angle(v, p[0], p[1]);
CHECK_THAT(a, Catch::Matchers::WithinRel(45.f, 0.01f));
REQUIRE_THAT(a, Catch::Matchers::WithinRel(45.f, 0.01f));
}
TEST_CASE("dh_q_0")
@@ -161,7 +156,7 @@ TEST_CASE("dh_q_0")
};
auto a = cif::dihedral_angle(t[0], t[1], t[2], p);
CHECK_THAT(a, Catch::Matchers::WithinRel(0.f, 0.01f));
REQUIRE_THAT(a, Catch::Matchers::WithinRel(0.f, 0.01f));
auto q = cif::construct_from_angle_axis(90, axis);
@@ -225,7 +220,7 @@ TEST_CASE("dh_q_1")
pts[3].rotate(q, pts[2]);
auto dh = cif::dihedral_angle(pts[0], pts[1], pts[2], pts[3]);
CHECK_THAT(dh, Catch::Matchers::WithinRel(angle, 0.1f));
REQUIRE_THAT(dh, Catch::Matchers::WithinRel(angle, 0.1f));
}
}
@@ -288,9 +283,9 @@ TEST_CASE("dh_q_1")
// cif::point p3 = rot * p1;
// CHECK_THAT(p2.m_x, Catch::Matchers::WithinRel(p3.m_x, 0.01f));
// CHECK_THAT(p2.m_y, Catch::Matchers::WithinRel(p3.m_y, 0.01f));
// CHECK_THAT(p2.m_z, Catch::Matchers::WithinRel(p3.m_z, 0.01f));
// REQUIRE_THAT(p2.m_x, Catch::Matchers::WithinRel(p3.m_x, 0.01f));
// REQUIRE_THAT(p2.m_y, Catch::Matchers::WithinRel(p3.m_y, 0.01f));
// REQUIRE_THAT(p2.m_z, Catch::Matchers::WithinRel(p3.m_z, 0.01f));
// }
// }
@@ -301,7 +296,7 @@ TEST_CASE("m2q_0a")
auto d = cif::kSymopNrTable[i].symop().data();
Eigen::Matrix3f rot;
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]);
rot << d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8];
// check to see if this matrix contains a true rotation
if (rot * rot.transpose() != Eigen::Matrix3f::Identity() or rot.determinant() != 1)
@@ -316,22 +311,22 @@ TEST_CASE("m2q_0a")
p2.rotate(q);
cif::matrix3x3<float> rot_c({
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])
rot_c(0, 0) = d[0],
rot_c(0, 1) = d[1],
rot_c(0, 2) = d[2],
rot_c(1, 0) = d[3],
rot_c(1, 1) = d[4],
rot_c(1, 2) = d[5],
rot_c(2, 0) = d[6],
rot_c(2, 1) = d[7],
rot_c(2, 2) = d[8]
});
cif::point p3 = rot_c * p1;
CHECK_THAT(p2.m_x, Catch::Matchers::WithinRel(p3.m_x, 0.01f));
CHECK_THAT(p2.m_y, Catch::Matchers::WithinRel(p3.m_y, 0.01f));
CHECK_THAT(p2.m_z, Catch::Matchers::WithinRel(p3.m_z, 0.01f));
REQUIRE_THAT(p2.m_x, Catch::Matchers::WithinRel(p3.m_x, 0.01f));
REQUIRE_THAT(p2.m_y, Catch::Matchers::WithinRel(p3.m_y, 0.01f));
REQUIRE_THAT(p2.m_z, Catch::Matchers::WithinRel(p3.m_z, 0.01f));
}
}
@@ -404,15 +399,15 @@ TEST_CASE("symm_1")
cif::point f = fractional(p, c);
CHECK_THAT(f.m_x, Catch::Matchers::WithinRel(0.1f, 0.01f));
CHECK_THAT(f.m_y, Catch::Matchers::WithinRel(0.1f, 0.01f));
CHECK_THAT(f.m_z, Catch::Matchers::WithinRel(0.1f, 0.01f));
REQUIRE_THAT(f.m_x, Catch::Matchers::WithinRel(0.1f, 0.01f));
REQUIRE_THAT(f.m_y, Catch::Matchers::WithinRel(0.1f, 0.01f));
REQUIRE_THAT(f.m_z, Catch::Matchers::WithinRel(0.1f, 0.01f));
cif::point o = orthogonal(f, c);
CHECK_THAT(o.m_x, Catch::Matchers::WithinRel(1.f, 0.01f));
CHECK_THAT(o.m_y, Catch::Matchers::WithinRel(1.f, 0.01f));
CHECK_THAT(o.m_z, Catch::Matchers::WithinRel(1.f, 0.01f));
REQUIRE_THAT(o.m_x, Catch::Matchers::WithinRel(1.f, 0.01f));
REQUIRE_THAT(o.m_y, Catch::Matchers::WithinRel(1.f, 0.01f));
REQUIRE_THAT(o.m_z, Catch::Matchers::WithinRel(1.f, 0.01f));
}
TEST_CASE("symm_2")
@@ -440,22 +435,22 @@ TEST_CASE("symm_4")
// based on 2b8h
auto sg = cif::spacegroup(154); // p 32 2 1
auto c = cif::cell(107.516f, 107.516f, 338.487f, 90.00f, 90.00f, 120.00f);
auto c = cif::cell(107.516, 107.516, 338.487, 90.00, 90.00, 120.00);
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
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
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));
CHECK_THAT(distance(a, sg(a, c, "1_554"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_c()), 0.01f));
REQUIRE_THAT(distance(a, sg(a, c, "1_455"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_a()), 0.01f));
REQUIRE_THAT(distance(a, sg(a, c, "1_545"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_b()), 0.01f));
REQUIRE_THAT(distance(a, sg(a, c, "1_554"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_c()), 0.01f));
auto sb2 = sg(b, c, "4_565"_symop);
CHECK_THAT(sb.m_x, Catch::Matchers::WithinRel(sb2.m_x, 0.01f));
CHECK_THAT(sb.m_y, Catch::Matchers::WithinRel(sb2.m_y, 0.01f));
CHECK_THAT(sb.m_z, Catch::Matchers::WithinRel(sb2.m_z, 0.01f));
REQUIRE_THAT(sb.m_x, Catch::Matchers::WithinRel(sb2.m_x, 0.01f));
REQUIRE_THAT(sb.m_y, Catch::Matchers::WithinRel(sb2.m_y, 0.01f));
REQUIRE_THAT(sb.m_z, Catch::Matchers::WithinRel(sb2.m_z, 0.01f));
CHECK_THAT(distance(a, sb2), Catch::Matchers::WithinRel(7.42f, 0.01f));
REQUIRE_THAT(distance(a, sb2), Catch::Matchers::WithinRel(7.42f, 0.01f));
}
// --------------------------------------------------------------------
@@ -471,21 +466,21 @@ TEST_CASE("symm_4wvp_1")
cif::crystal c(db);
cif::point p{ -78.722f, 98.528f, 11.994f };
cif::point p{ -78.722, 98.528, 11.994 };
auto a = s.get_residue("A", 10, "").get_atom_by_atom_id("O");
auto sp1 = c.symmetry_copy(a.get_location(), "2_565"_symop);
CHECK_THAT(sp1.m_x, Catch::Matchers::WithinAbs(p.m_x, 0.5f));
CHECK_THAT(sp1.m_y, Catch::Matchers::WithinAbs(p.m_y, 0.5f));
CHECK_THAT(sp1.m_z, Catch::Matchers::WithinAbs(p.m_z, 0.5f));
REQUIRE_THAT(sp1.m_x, Catch::Matchers::WithinAbs(p.m_x, 0.5f));
REQUIRE_THAT(sp1.m_y, Catch::Matchers::WithinAbs(p.m_y, 0.5f));
REQUIRE_THAT(sp1.m_z, Catch::Matchers::WithinAbs(p.m_z, 0.5f));
const auto &[d, sp2, so] = c.closest_symmetry_copy(p, a.get_location());
REQUIRE(d < 1);
CHECK_THAT(sp2.m_x, Catch::Matchers::WithinAbs(p.m_x, 0.5f));
CHECK_THAT(sp2.m_y, Catch::Matchers::WithinAbs(p.m_y, 0.5f));
CHECK_THAT(sp2.m_z, Catch::Matchers::WithinAbs(p.m_z, 0.5f));
REQUIRE_THAT(sp2.m_x, Catch::Matchers::WithinAbs(p.m_x, 0.5f));
REQUIRE_THAT(sp2.m_y, Catch::Matchers::WithinAbs(p.m_y, 0.5f));
REQUIRE_THAT(sp2.m_z, Catch::Matchers::WithinAbs(p.m_z, 0.5f));
}
TEST_CASE("symm_2bi3_1")
@@ -517,17 +512,17 @@ TEST_CASE("symm_2bi3_1")
auto sa1 = c.symmetry_copy(a1.get_location(), cif::sym_op(symm1));
auto sa2 = c.symmetry_copy(a2.get_location(), cif::sym_op(symm2));
CHECK_THAT(cif::distance(sa1, sa2), Catch::Matchers::WithinAbs(dist, 0.5f));
REQUIRE_THAT(cif::distance(sa1, sa2), Catch::Matchers::WithinAbs(dist, 0.5f));
auto pa1 = a1.get_location();
const auto &[d, p, so] = c.closest_symmetry_copy(pa1, a2.get_location());
CHECK_THAT(p.m_x, Catch::Matchers::WithinAbs(sa2.m_x, 0.5f));
CHECK_THAT(p.m_y, Catch::Matchers::WithinAbs(sa2.m_y, 0.5f));
CHECK_THAT(p.m_z, Catch::Matchers::WithinAbs(sa2.m_z, 0.5f));
REQUIRE_THAT(p.m_x, Catch::Matchers::WithinAbs(sa2.m_x, 0.5f));
REQUIRE_THAT(p.m_y, Catch::Matchers::WithinAbs(sa2.m_y, 0.5f));
REQUIRE_THAT(p.m_z, Catch::Matchers::WithinAbs(sa2.m_z, 0.5f));
CHECK_THAT(d, Catch::Matchers::WithinAbs(dist, 0.5f));
REQUIRE_THAT(d, Catch::Matchers::WithinAbs(dist, 0.5f));
REQUIRE(so.string() == symm2);
}
}
@@ -564,15 +559,15 @@ TEST_CASE("symm_2bi3_1a")
auto sa1 = c.symmetry_copy(p1, cif::sym_op(symm1));
auto sa2 = c.symmetry_copy(p2, cif::sym_op(symm2));
CHECK_THAT(cif::distance(sa1, sa2), Catch::Matchers::WithinAbs(dist, 0.5f));
REQUIRE_THAT(cif::distance(sa1, sa2), Catch::Matchers::WithinAbs(dist, 0.5f));
const auto &[d, p, so] = c.closest_symmetry_copy(p1, p2);
CHECK_THAT(p.m_x, Catch::Matchers::WithinAbs(sa2.m_x, 0.5f));
CHECK_THAT(p.m_y, Catch::Matchers::WithinAbs(sa2.m_y, 0.5f));
CHECK_THAT(p.m_z, Catch::Matchers::WithinAbs(sa2.m_z, 0.5f));
REQUIRE_THAT(p.m_x, Catch::Matchers::WithinAbs(sa2.m_x, 0.5f));
REQUIRE_THAT(p.m_y, Catch::Matchers::WithinAbs(sa2.m_y, 0.5f));
REQUIRE_THAT(p.m_z, Catch::Matchers::WithinAbs(sa2.m_z, 0.5f));
CHECK_THAT(d, Catch::Matchers::WithinAbs(dist, 0.5f));
REQUIRE_THAT(d, Catch::Matchers::WithinAbs(dist, 0.5f));
REQUIRE(so.string() == symm2);
}
}
@@ -595,7 +590,7 @@ TEST_CASE("symm_3bwh_1")
const auto &[d, p, so] = c.closest_symmetry_copy(a1.get_location(), a2.get_location());
CHECK_THAT(d, Catch::Matchers::WithinAbs(distance(a1.get_location(), p), 0.5f));
REQUIRE_THAT(d, Catch::Matchers::WithinAbs(distance(a1.get_location(), p), 0.5f));
}
}
}
@@ -608,5 +603,5 @@ TEST_CASE("volume_3bwh_1")
cif::crystal c(db);
CHECK_THAT(c.get_cell().get_volume(), Catch::Matchers::WithinRel(741009.625f, 0.01f));
REQUIRE_THAT(c.get_cell().get_volume(), Catch::Matchers::WithinRel(741009.625f, 0.01f));
}

File diff suppressed because it is too large Load Diff

View File

@@ -279,20 +279,6 @@ A 1 5 GLY 5 5 5 GLY GLY A . n
REQUIRE_FALSE(cif::pdb::is_valid_pdbx_file(f));
}
}
TEST_CASE("extended-dictionary-1")
{
cif::add_file_resource("dssp-extension.dic", gTestDir / "dssp-extension.dic");
cif::VERBOSE = 2;
auto f = cif::pdb::read(gTestDir / "1cbs-dssp.cif");
CHECK(f.is_valid());
}
TEST_CASE("brak")
{
auto f = cif::pdb::read(gTestDir / "brak.pdb");
CHECK(f.is_valid());
}

View File

@@ -5,8 +5,6 @@ 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
@@ -16,9 +14,9 @@ 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 -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
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
@EXIT /b 0

View File

@@ -63,7 +63,7 @@ update_dictionary() {
update_dictionary "@CIFPP_CACHE_DIR@/components.cif" "https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz"
update_dictionary "@CIFPP_CACHE_DIR@/mmcif_pdbx.dic" "https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic.gz"
update_dictionary "@CIFPP_CACHE_DIR@/mmcif_ma.dic" "https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_ma.dic"
update_dictionary "@CIFPP_CACHE_DIR@/mmcif_ma.dic" "https://github.com/ihmwg/ModelCIF/raw/master/dist/mmcif_ma.dic"
# notify subscribers, using find instead of run-parts to make it work on FreeBSD as well