mirror of
https://github.com/PDB-REDO/libcifpp.git
synced 2026-06-04 22:14:24 +08:00
Compare commits
106 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00b0473438 | ||
|
|
f15a76e29b | ||
|
|
915a147449 | ||
|
|
edf24ca9ff | ||
|
|
46a9318aa5 | ||
|
|
4a7f48eed8 | ||
|
|
42e66afd92 | ||
|
|
b550e9b027 | ||
|
|
452bb83ce7 | ||
|
|
6eda9aaf36 | ||
|
|
251fb55d6a | ||
|
|
f94e9aece9 | ||
|
|
c565bb96be | ||
|
|
e51f31dc4c | ||
|
|
4e128885d6 | ||
|
|
b37054228d | ||
|
|
815b33fee0 | ||
|
|
97f55c1639 | ||
|
|
89de73eb6f | ||
|
|
75f2ec3792 | ||
|
|
f4d29e8da9 | ||
|
|
b97b2638b8 | ||
|
|
ea8dea8cbd | ||
|
|
bc0222dc0e | ||
|
|
10a6b5649b | ||
|
|
ff2a233156 | ||
|
|
743e2800f8 | ||
|
|
32ac884127 | ||
|
|
bec69f7d07 | ||
|
|
a99215ad6a | ||
|
|
e3d2cbd044 | ||
|
|
5fc965789d | ||
|
|
b4596902aa | ||
|
|
cbf8b52f62 | ||
|
|
4e0fa1c916 | ||
|
|
95b007d38f | ||
|
|
b66f7a30ce | ||
|
|
ec7287c503 | ||
|
|
a41c591f0c | ||
|
|
3a6527cdd5 | ||
|
|
5f21a094c0 | ||
|
|
2203a1855d | ||
|
|
7edd2ecc18 | ||
|
|
1d2953c850 | ||
|
|
dbf59ce622 | ||
|
|
1596db8499 | ||
|
|
bd1fb5c5cd | ||
|
|
da500025c3 | ||
|
|
60eeea9a93 | ||
|
|
1220f01f1e | ||
|
|
ad0a34fe98 | ||
|
|
a7425ff1a0 | ||
|
|
1ce25f86ae | ||
|
|
cd93f72b96 | ||
|
|
23500bd303 | ||
|
|
14b4753b4f | ||
|
|
4c37d5db5f | ||
|
|
fc2c4b4172 | ||
|
|
3ac64de16b | ||
|
|
45eecd72b0 | ||
|
|
d1dd558cda | ||
|
|
d19e2c2196 | ||
|
|
72c7aca074 | ||
|
|
683a1087d0 | ||
|
|
35bc139deb | ||
|
|
45ece2fa0d | ||
|
|
11c98f553f | ||
|
|
28aa9b1036 | ||
|
|
d7b5c0a748 | ||
|
|
065e7f5f18 | ||
|
|
4b1623cfdc | ||
|
|
1de973ddcb | ||
|
|
eecc801203 | ||
|
|
5c50154ea4 | ||
|
|
0fa3d6aa94 | ||
|
|
01f5242bfb | ||
|
|
af6d8d4f71 | ||
|
|
fa8285fc0f | ||
|
|
2e7f6b8337 | ||
|
|
a6a55020eb | ||
|
|
0e84ea454d | ||
|
|
f3bf211d45 | ||
|
|
f5ef44836c | ||
|
|
070124b6e1 | ||
|
|
c8a46fcdd9 | ||
|
|
5306b59fd8 | ||
|
|
90c5df832a | ||
|
|
2aa439d51f | ||
|
|
ac2b68517c | ||
|
|
e56b568c42 | ||
|
|
63c49b2e04 | ||
|
|
559fd18a20 | ||
|
|
beb7585261 | ||
|
|
8b0f92aa9a | ||
|
|
0d8beeae5b | ||
|
|
e3da654e67 | ||
|
|
dc9e151d89 | ||
|
|
7cfaf051ba | ||
|
|
7920491309 | ||
|
|
0ee493a3fb | ||
|
|
7e23bc0c0b | ||
|
|
579f859562 | ||
|
|
752938ca44 | ||
|
|
fce58c02fe | ||
|
|
924f7c1505 | ||
|
|
8944906fd2 |
22
.clang-format
Normal file
22
.clang-format
Normal file
@@ -0,0 +1,22 @@
|
||||
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
|
||||
7
.github/workflows/cmake-multi-platform.yml
vendored
7
.github/workflows/cmake-multi-platform.yml
vendored
@@ -33,13 +33,18 @@ jobs:
|
||||
|
||||
- name: Install dependencies Ubuntu
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: sudo apt-get update && sudo apt-get install mrc
|
||||
run: sudo apt-get update && sudo apt-get install mrc catch2
|
||||
|
||||
- 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 }}
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -14,3 +14,5 @@ docs/conf.py
|
||||
build_ci/
|
||||
data/components.cif
|
||||
perf.data*
|
||||
.cache/
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ endif()
|
||||
# set the project name
|
||||
project(
|
||||
libcifpp
|
||||
VERSION 9.0.0
|
||||
LANGUAGES CXX)
|
||||
VERSION 9.0.5
|
||||
LANGUAGES CXX C)
|
||||
|
||||
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
@@ -43,7 +43,7 @@ include(GenerateExportHeader)
|
||||
include(CTest)
|
||||
include(ExternalProject)
|
||||
include(FetchContent)
|
||||
include(CPM)
|
||||
include(VersionString)
|
||||
|
||||
# When building with ninja-multiconfig, build both debug and release by default
|
||||
if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
|
||||
@@ -169,17 +169,36 @@ if(MSVC)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# find_package(pcre2) is unfortunately broken
|
||||
# Using fast_float for float parsing, but only if needed
|
||||
try_compile(STD_CHARCONV_COMPILING
|
||||
SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/test-charconv.cpp)
|
||||
|
||||
include(FindPCRE2x)
|
||||
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
|
||||
EXCLUDE_FROM_ALL)
|
||||
FetchContent_MakeAvailable(fastfloat)
|
||||
endif()
|
||||
|
||||
find_package(ZLIB QUIET)
|
||||
find_package(Threads)
|
||||
find_package(ZLIB QUIET)
|
||||
|
||||
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)
|
||||
endif()
|
||||
|
||||
# Using Eigen3 is a bit of a thing. We don't want to build it completely since
|
||||
# we only need a couple of header files. Nothing special. But often, eigen3 is
|
||||
# already installed and then we prefer that.
|
||||
@@ -191,20 +210,16 @@ if(Eigen3_FOUND AND TARGET Eigen3::Eigen)
|
||||
else()
|
||||
# Use ExternalProject since FetchContent always tries to install the result...
|
||||
ExternalProject_Add(my-eigen3
|
||||
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
|
||||
GIT_TAG 3.4.0
|
||||
URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.zip
|
||||
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
|
||||
ExternalProject_Get_Property(my-eigen3 SOURCE_DIR)
|
||||
set(EIGEN_INCLUDE_DIR ${SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
message(STATUS "cifpp: 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")
|
||||
|
||||
# SymOp data table
|
||||
if(CIFPP_RECREATE_SYMOP_DATA)
|
||||
# The tool to create the table
|
||||
@@ -226,6 +241,9 @@ if(CIFPP_RECREATE_SYMOP_DATA)
|
||||
"$ENV{CLIBD}/symop.lib")
|
||||
endif()
|
||||
|
||||
# Create a revision file, containing the current git version info
|
||||
write_version_header("${CMAKE_CURRENT_SOURCE_DIR}/src/" LIB_NAME "LibCIFPP")
|
||||
|
||||
# Sources
|
||||
set(project_sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/category.cpp
|
||||
@@ -244,6 +262,7 @@ set(project_sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/point.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/symmetry.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/model.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/cql/transaction.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/cif2pdb.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb_record.hpp
|
||||
@@ -280,6 +299,7 @@ set(project_headers
|
||||
include/cif++/row.hpp
|
||||
include/cif++/symmetry.hpp
|
||||
include/cif++/text.hpp
|
||||
include/cif++/cql/transaction.hpp
|
||||
include/cif++/utilities.hpp
|
||||
include/cif++/validate.hpp
|
||||
)
|
||||
@@ -319,8 +339,18 @@ target_include_directories(
|
||||
PRIVATE "${EIGEN_INCLUDE_DIR}")
|
||||
|
||||
target_link_libraries(cifpp
|
||||
PUBLIC Threads::Threads ZLIB::ZLIB $<$<TARGET_EXISTS:std::atomic>:std::atomic>
|
||||
PRIVATE $<BUILD_INTERFACE:pcre2-8>)
|
||||
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(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
|
||||
|
||||
20
changelog
20
changelog
@@ -1,3 +1,23 @@
|
||||
Version 9.0.5
|
||||
- Added exists to compound_factory
|
||||
- Added sub_matrix, fix and extend determinant calculation
|
||||
- Added yet another structure::create_non_poly
|
||||
- Remove revision.hpp file in make clean (new VersionString.cmake)
|
||||
|
||||
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
|
||||
|
||||
1291
cmake/CPM.cmake
1291
cmake/CPM.cmake
File diff suppressed because it is too large
Load Diff
12
cmake/FindPCRE2.cmake
Normal file
12
cmake/FindPCRE2.cmake
Normal file
@@ -0,0 +1,12 @@
|
||||
# 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})
|
||||
@@ -1,33 +0,0 @@
|
||||
set(PCRE2_USE_STATIC_LIBS ON)
|
||||
|
||||
find_package(pcre2 CONFIG)
|
||||
|
||||
if(PCRE2_FOUND)
|
||||
message(STATUS "Using pcre2 found using find_package")
|
||||
else()
|
||||
include(FindPkgConfig)
|
||||
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PCRE2 IMPORTED_TARGET libpcre2-8)
|
||||
|
||||
if(PCRE2_FOUND)
|
||||
message(STATUS "Using pcre2 found using pkg-config")
|
||||
|
||||
add_library(pcre2-8 ALIAS PkgConfig::PCRE2)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT PCRE2_FOUND)
|
||||
message(STATUS "Using pcre2 using FetchContent")
|
||||
|
||||
set(PCRE2_BUILD_TESTS OFF)
|
||||
FetchContent_Declare(
|
||||
pcre2
|
||||
GIT_REPOSITORY https://github.com/PCRE2Project/pcre2
|
||||
GIT_TAG pcre2-10.45
|
||||
EXCLUDE_FROM_ALL)
|
||||
FetchContent_MakeAvailable(pcre2)
|
||||
|
||||
# add_subdirectory(${pcre2_SOURCE_DIR} EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
@@ -238,7 +238,7 @@ function(write_version_header dir)
|
||||
if(res EQUAL 0)
|
||||
set(REVISION_STRING "${out}")
|
||||
else()
|
||||
message(STATUS "Git hash not found, does this project has a 'build' tag?")
|
||||
message(STATUS "Git hash not found, does this project have a 'build' tag?")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Git hash not found")
|
||||
|
||||
17
cmake/test-charconv.cpp
Normal file
17
cmake/test-charconv.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#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;
|
||||
}
|
||||
@@ -179,7 +179,7 @@ class compound
|
||||
friend class compound_factory_impl;
|
||||
friend class local_compound_factory_impl;
|
||||
|
||||
compound(cif::datablock &db);
|
||||
compound(datablock &db);
|
||||
|
||||
std::string m_id;
|
||||
std::string m_name;
|
||||
@@ -270,11 +270,15 @@ class compound_factory
|
||||
return is_std_base(res_name) or is_std_peptide(res_name);
|
||||
}
|
||||
|
||||
/// Return whether @a res_name is water
|
||||
bool is_water(std::string_view res_name) const
|
||||
{
|
||||
return res_name == "HOH" or res_name == "H2O" or res_name == "WAT";
|
||||
}
|
||||
|
||||
/// Return whether @a res_name already exists, without creating it.
|
||||
bool exists(std::string_view res_name) const;
|
||||
|
||||
/// \brief Create the compound object for \a id
|
||||
///
|
||||
/// This will create the compound instance for \a id if it doesn't exist already.
|
||||
@@ -331,14 +335,14 @@ class compound_factory
|
||||
class compound_source
|
||||
{
|
||||
public:
|
||||
compound_source(const cif::file &file)
|
||||
compound_source(const file &file)
|
||||
{
|
||||
cif::compound_factory::instance().push_dictionary(file);
|
||||
compound_factory::instance().push_dictionary(file);
|
||||
}
|
||||
|
||||
~compound_source()
|
||||
{
|
||||
cif::compound_factory::instance().pop_dictionary();
|
||||
compound_factory::instance().pop_dictionary();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1072,7 +1072,8 @@ concept Numeric = ((std::is_floating_point_v<T> or std::is_integral_v<T>) and no
|
||||
template <Numeric T>
|
||||
condition operator==(const key &key, const T &v)
|
||||
{
|
||||
return condition(new detail::key_equals_number_condition_impl(key.m_item_name, v));
|
||||
// TODO: change key_equals_etc... to use std::variant<double,int64_t> or something
|
||||
return condition(new detail::key_equals_number_condition_impl(key.m_item_name, static_cast<double>(v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1122,7 +1123,7 @@ condition operator>(const key &key, const T &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; },
|
||||
std::format(" > {}", v)));
|
||||
cif::format(" > {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1134,7 +1135,7 @@ condition operator>=(const key &key, const T &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; },
|
||||
std::format(" >= {}", v)));
|
||||
cif::format(" >= {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1146,7 +1147,7 @@ condition operator<(const key &key, const T &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; },
|
||||
std::format(" < {}", v)));
|
||||
cif::format(" < {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1158,7 +1159,7 @@ condition operator<=(const key &key, const T &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; },
|
||||
std::format(" <= {}", v)));
|
||||
cif::format(" <= {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1169,7 +1170,7 @@ inline condition operator>(const key &key, std::string_view 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; },
|
||||
std::format(" > {}", v)));
|
||||
cif::format(" > {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1180,7 +1181,7 @@ inline condition operator>=(const key &key, std::string_view 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; },
|
||||
std::format(" >= {}", v)));
|
||||
cif::format(" >= {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1191,7 +1192,7 @@ inline condition operator<(const key &key, std::string_view 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; },
|
||||
std::format(" < {}", v)));
|
||||
cif::format(" < {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1202,7 +1203,7 @@ inline condition operator<=(const key &key, std::string_view 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; },
|
||||
std::format(" <= {}", v)));
|
||||
cif::format(" <= {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
451
include/cif++/cql/transaction.hpp
Normal file
451
include/cif++/cql/transaction.hpp
Normal file
@@ -0,0 +1,451 @@
|
||||
/*-
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cif++/category.hpp"
|
||||
#include "cif++/condition.hpp"
|
||||
#include "cif++/datablock.hpp"
|
||||
#include "cif++/item.hpp"
|
||||
#include "cif++/row.hpp"
|
||||
#include "cif++/validate.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
namespace cif::cql
|
||||
{
|
||||
|
||||
class result;
|
||||
class row;
|
||||
class transaction;
|
||||
class view;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
struct column
|
||||
{
|
||||
std::string name;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
using column_list = std::vector<column>;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class field_ref
|
||||
{
|
||||
public:
|
||||
std::string_view name() const &
|
||||
{
|
||||
return m_col->name;
|
||||
}
|
||||
|
||||
constexpr size_t num() const noexcept
|
||||
{
|
||||
return m_col->index;
|
||||
}
|
||||
|
||||
std::string_view text() const &
|
||||
{
|
||||
return m_row[m_col->index].text();
|
||||
}
|
||||
|
||||
/** Return the contents of this item as type @tparam T */
|
||||
template <typename T = std::string>
|
||||
auto as() const -> T
|
||||
{
|
||||
return m_row[m_col->index].as<T>();
|
||||
}
|
||||
|
||||
/** Return the contents of this item as type @tparam T or, if not
|
||||
* set, use @a dv as the default value.
|
||||
*/
|
||||
template <typename T>
|
||||
auto value_or(const T &dv) const
|
||||
{
|
||||
return m_row[m_col->index].value_or(dv);
|
||||
}
|
||||
|
||||
field_ref(row_handle rh, column_list::const_iterator col)
|
||||
: m_row(rh)
|
||||
, m_col(col)
|
||||
{
|
||||
}
|
||||
|
||||
field_ref(const field_ref &) = default;
|
||||
field_ref(field_ref &&) = default;
|
||||
|
||||
field_ref &operator=(const field_ref &) = default;
|
||||
field_ref &operator=(field_ref &&) = default;
|
||||
|
||||
private:
|
||||
row_handle m_row;
|
||||
column_list::const_iterator m_col;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class row_ref final
|
||||
{
|
||||
public:
|
||||
class const_field_iterator
|
||||
{
|
||||
public:
|
||||
friend class result;
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = const field_ref;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
|
||||
const_field_iterator(const const_field_iterator &) = default;
|
||||
const_field_iterator(const_field_iterator &&) = default;
|
||||
|
||||
const_field_iterator &operator=(const const_field_iterator &) = default;
|
||||
const_field_iterator &operator=(const_field_iterator &&) = default;
|
||||
|
||||
reference operator*()
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
pointer operator->()
|
||||
{
|
||||
return &m_current;
|
||||
}
|
||||
|
||||
const_field_iterator &operator++()
|
||||
{
|
||||
if (m_row)
|
||||
{
|
||||
++m_col;
|
||||
m_current = field_ref(m_row, m_col);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_field_iterator operator++(int)
|
||||
{
|
||||
const_field_iterator result(*this);
|
||||
this->operator++();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator==(const const_field_iterator &rhs) const
|
||||
{
|
||||
return m_row == rhs.m_row and m_col == rhs.m_col;
|
||||
}
|
||||
|
||||
bool operator!=(const const_field_iterator &rhs) const
|
||||
{
|
||||
return m_row != rhs.m_row or m_col != rhs.m_col;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class row_ref;
|
||||
|
||||
const_field_iterator(const row_handle &row, column_list::const_iterator col)
|
||||
: m_row(row)
|
||||
, m_col(col)
|
||||
, m_current(m_row, m_col)
|
||||
{
|
||||
}
|
||||
|
||||
row_handle m_row;
|
||||
column_list::const_iterator m_col;
|
||||
field_ref m_current;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
row_ref() = default;
|
||||
|
||||
row_ref(row_handle rh, const column_list &cols)
|
||||
: m_row(rh)
|
||||
, m_cols(&cols)
|
||||
{
|
||||
}
|
||||
|
||||
row_ref(row_ref r, const column_list &cols)
|
||||
: m_row(r.m_row)
|
||||
, m_cols(&cols)
|
||||
{
|
||||
}
|
||||
|
||||
row_ref(const row_ref &) = default;
|
||||
row_ref &operator=(const row_ref &) = default;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
const_field_iterator cbegin() const noexcept { return const_field_iterator(m_row, m_cols->cbegin()); }
|
||||
const_field_iterator begin() const noexcept { return const_field_iterator(m_row, m_cols->cbegin()); }
|
||||
const_field_iterator cend() const noexcept { return const_field_iterator(m_row, m_cols->cend()); }
|
||||
const_field_iterator end() const noexcept { return const_field_iterator(m_row, m_cols->cend()); }
|
||||
|
||||
field_ref front() const noexcept { return field_ref(m_row, m_cols->cbegin()); }
|
||||
field_ref back() const noexcept { return field_ref(m_row, m_cols->cend()); }
|
||||
|
||||
size_t size() const noexcept { return m_cols->size(); }
|
||||
bool empty() const noexcept { return m_cols->empty(); }
|
||||
|
||||
field_ref operator[](size_t index) const noexcept;
|
||||
field_ref operator[](std::string_view name) const noexcept;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
bool operator==(const row_ref &rhs) const { return m_row == rhs.m_row and m_cols == rhs.m_cols; }
|
||||
bool operator!=(const row_ref &rhs) const { return m_row != rhs.m_row or m_cols != rhs.m_cols; }
|
||||
|
||||
private:
|
||||
row_handle m_row;
|
||||
const column_list *m_cols = nullptr;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class view : public std::enable_shared_from_this<view>
|
||||
{
|
||||
public:
|
||||
virtual ~view() = default;
|
||||
|
||||
class const_row_iterator
|
||||
{
|
||||
public:
|
||||
friend class view;
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = const row_ref;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
|
||||
// const_row_iterator() = default;
|
||||
|
||||
const_row_iterator(const const_row_iterator &) = default;
|
||||
const_row_iterator(const_row_iterator &&) = default;
|
||||
|
||||
// const_row_iterator &operator=(const const_row_iterator &) = default;
|
||||
// const_row_iterator &operator=(const_row_iterator &&) = default;
|
||||
|
||||
reference operator*()
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
pointer operator->()
|
||||
{
|
||||
return &m_current;
|
||||
}
|
||||
|
||||
const_row_iterator &operator++()
|
||||
{
|
||||
++m_index;
|
||||
if (m_index < m_data.size())
|
||||
m_current = m_data.at(m_index);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_row_iterator operator++(int)
|
||||
{
|
||||
const_row_iterator result(*this);
|
||||
this->operator++();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator==(const const_row_iterator &rhs) const
|
||||
{
|
||||
return &m_data == &rhs.m_data and m_index == rhs.m_index;
|
||||
}
|
||||
|
||||
bool operator!=(const const_row_iterator &rhs) const
|
||||
{
|
||||
return &m_data != &rhs.m_data or m_index != rhs.m_index;
|
||||
}
|
||||
|
||||
private:
|
||||
const_row_iterator(const view &result, size_t index, row_ref current)
|
||||
: m_data(result)
|
||||
, m_index(index)
|
||||
, m_current(current)
|
||||
{
|
||||
}
|
||||
|
||||
const view &m_data;
|
||||
size_t m_index = 0;
|
||||
row_ref m_current;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
const_row_iterator begin() const noexcept { return const_row_iterator(*this, 0, at(0)); }
|
||||
const_row_iterator cbegin() const noexcept { return const_row_iterator(*this, 0, at(0)); }
|
||||
|
||||
const_row_iterator end() const noexcept { return const_row_iterator(*this, size(), row_ref{}); }
|
||||
const_row_iterator cend() const noexcept { return const_row_iterator(*this, size(), row_ref{}); }
|
||||
|
||||
virtual row_ref front() const noexcept = 0;
|
||||
virtual row_ref back() const noexcept = 0;
|
||||
|
||||
virtual size_t size() const noexcept = 0;
|
||||
bool empty() const noexcept { return size() == 0; }
|
||||
|
||||
virtual row_ref at(size_t index) const = 0;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
std::vector<std::string> columns() const
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
for (const auto &[name, ignore] : m_columns)
|
||||
result.emplace_back(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class const_row_iterator;
|
||||
|
||||
view(const column_list &cols)
|
||||
: m_columns(cols)
|
||||
{
|
||||
}
|
||||
|
||||
view(column_list &&cols)
|
||||
: m_columns(std::forward<column_list>(cols))
|
||||
{
|
||||
}
|
||||
|
||||
column_list m_columns;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class simple_view : public view
|
||||
{
|
||||
public:
|
||||
simple_view(const category &cat)
|
||||
: view(get_column_list_for_category(cat))
|
||||
, m_cat(cat)
|
||||
{
|
||||
}
|
||||
|
||||
simple_view(const simple_view &) = default;
|
||||
simple_view(simple_view &&) = default;
|
||||
|
||||
virtual size_t size() const noexcept override { return m_cat.size(); }
|
||||
|
||||
virtual row_ref front() const noexcept override;
|
||||
virtual row_ref back() const noexcept override;
|
||||
|
||||
virtual row_ref at(size_t index) const override;
|
||||
|
||||
protected:
|
||||
|
||||
static column_list get_column_list_for_category(const category &cat);
|
||||
|
||||
const category &m_cat;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class result
|
||||
{
|
||||
public:
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
using const_row_iterator = view::const_row_iterator;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
result();
|
||||
result(result const &rhs) noexcept = default;
|
||||
result(result &&rhs) noexcept = default;
|
||||
result &operator=(result const &rhs) noexcept = default;
|
||||
result &operator=(result &&rhs) noexcept = default;
|
||||
|
||||
result(view &vw, const std::string &query = "");
|
||||
|
||||
row_ref one_row() const;
|
||||
field_ref one_field() const;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
const_row_iterator begin() const noexcept;
|
||||
const_row_iterator cbegin() const noexcept;
|
||||
|
||||
const_row_iterator end() const noexcept;
|
||||
const_row_iterator cend() const noexcept;
|
||||
|
||||
row_ref front() const noexcept;
|
||||
row_ref back() const noexcept;
|
||||
|
||||
size_t size() const noexcept;
|
||||
bool empty() const noexcept;
|
||||
|
||||
size_t column_count() const;
|
||||
|
||||
private:
|
||||
friend class transaction;
|
||||
friend class SelectStatement;
|
||||
|
||||
result expect_columns(size_t cols) const
|
||||
{
|
||||
if (auto actual = column_count(); cols != actual)
|
||||
throw std::runtime_error("Unexpected number of columns");
|
||||
return *this;
|
||||
}
|
||||
|
||||
row_ref at(size_t index) const;
|
||||
|
||||
std::string m_query;
|
||||
std::shared_ptr<view> m_view;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class transaction
|
||||
{
|
||||
public:
|
||||
transaction(const datablock &db)
|
||||
: m_db(const_cast<datablock &>(db))
|
||||
{
|
||||
}
|
||||
|
||||
result exec(std::string_view query);
|
||||
|
||||
private:
|
||||
datablock &m_db;
|
||||
};
|
||||
|
||||
} // namespace cif::cql
|
||||
@@ -43,7 +43,7 @@ namespace cif
|
||||
|
||||
/**
|
||||
* @brief A datablock is a list of category objects with some additional features
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
class datablock : public std::list<category>
|
||||
@@ -53,7 +53,7 @@ class datablock : public std::list<category>
|
||||
|
||||
/**
|
||||
* @brief Construct a new datablock object with name @a name
|
||||
*
|
||||
*
|
||||
* @param name The name for the new datablock
|
||||
*/
|
||||
datablock(std::string_view name)
|
||||
@@ -80,7 +80,7 @@ class datablock : public std::list<category>
|
||||
{
|
||||
std::swap(a.m_name, b.m_name);
|
||||
std::swap(a.m_validator, b.m_validator);
|
||||
std::swap(static_cast<std::list<category>&>(a), static_cast<std::list<category>&>(b));
|
||||
std::swap(static_cast<std::list<category> &>(a), static_cast<std::list<category> &>(b));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -92,7 +92,7 @@ class datablock : public std::list<category>
|
||||
|
||||
/**
|
||||
* @brief Set the name of this datablock to @a name
|
||||
*
|
||||
*
|
||||
* @param name The new name
|
||||
*/
|
||||
void set_name(std::string_view name)
|
||||
@@ -102,27 +102,27 @@ class datablock : public std::list<category>
|
||||
|
||||
/**
|
||||
* @brief Attempt to load the dictionary specified in audit_conform category
|
||||
*
|
||||
*
|
||||
*/
|
||||
void load_dictionary();
|
||||
|
||||
/**
|
||||
* @brief Set the validator object to @a v
|
||||
*
|
||||
*
|
||||
* @param v The new validator object, may be null
|
||||
*/
|
||||
void set_validator(const validator *v);
|
||||
|
||||
/**
|
||||
* @brief Get the validator object
|
||||
*
|
||||
*
|
||||
* @return const validator* The validator or nullptr if there is none
|
||||
*/
|
||||
const validator *get_validator() const;
|
||||
|
||||
/**
|
||||
* @brief Validates the content of this datablock and all its content
|
||||
*
|
||||
*
|
||||
* @return true If the content is valid
|
||||
* @return false If the content is not valid
|
||||
*/
|
||||
@@ -131,7 +131,7 @@ class datablock : public std::list<category>
|
||||
/**
|
||||
* @brief Validates all contained data for valid links between parents and children
|
||||
* as defined in the validator
|
||||
*
|
||||
*
|
||||
* @return true If all links are valid
|
||||
* @return false If all links are not valid
|
||||
*/
|
||||
@@ -140,7 +140,7 @@ class datablock : public std::list<category>
|
||||
/**
|
||||
* @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();
|
||||
@@ -150,7 +150,7 @@ class datablock : public std::list<category>
|
||||
/**
|
||||
* @brief Return the category named @a name, will create a new and empty
|
||||
* category named @a name if it does not exist.
|
||||
*
|
||||
*
|
||||
* @param name The name of the category to return
|
||||
* @return category& Reference to the named category
|
||||
*/
|
||||
@@ -159,7 +159,7 @@ class datablock : public std::list<category>
|
||||
/**
|
||||
* @brief Return the const category named @a name, will return a reference
|
||||
* to a static empty category if it was not found.
|
||||
*
|
||||
*
|
||||
* @param name The name of the category to return
|
||||
* @return category& Reference to the named category
|
||||
*/
|
||||
@@ -168,7 +168,7 @@ class datablock : public std::list<category>
|
||||
/**
|
||||
* @brief Return a pointer to the category named @a name or nullptr if
|
||||
* it does not exist.
|
||||
*
|
||||
*
|
||||
* @param name The name of the category
|
||||
* @return category* Pointer to the category found or nullptr
|
||||
*/
|
||||
@@ -177,13 +177,12 @@ class datablock : public std::list<category>
|
||||
/**
|
||||
* @brief Return a pointer to the category named @a name or nullptr if
|
||||
* it does not exist.
|
||||
*
|
||||
*
|
||||
* @param name The name of the category
|
||||
* @return category* Pointer to the category found or nullptr
|
||||
*/
|
||||
const category *get(std::string_view name) const;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return true if this datablock contains a non-empty category
|
||||
*/
|
||||
@@ -197,7 +196,7 @@ class datablock : public std::list<category>
|
||||
* new one if it is not found. The result is a tuple of an iterator
|
||||
* pointing to the category and a boolean indicating whether the category
|
||||
* was created or not.
|
||||
*
|
||||
*
|
||||
* @param name The name for the category
|
||||
* @return std::tuple<iterator, bool> A tuple containing an iterator pointing
|
||||
* at the category and a boolean indicating whether the category was newly
|
||||
|
||||
@@ -26,17 +26,29 @@
|
||||
|
||||
#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 std::format instead of a home grown rip off
|
||||
* Now using cif::format instead of a home grown rip off
|
||||
*/
|
||||
|
||||
namespace cif
|
||||
{
|
||||
|
||||
#if USE_STD_FORMAT
|
||||
using std::format;
|
||||
#else
|
||||
using fmt::format;
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/// A streambuf that fills out lines with spaces up until a specified width
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -560,7 +560,9 @@ 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])) ? selected_charconv<value_type>::from_chars(b + 1, e, result) : selected_charconv<value_type>::from_chars(b, e, result);
|
||||
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);
|
||||
|
||||
if ((bool)r.ec or r.ptr != e)
|
||||
{
|
||||
@@ -595,7 +597,9 @@ 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])) ? selected_charconv<value_type>::from_chars(b + 1, e, v) : selected_charconv<value_type>::from_chars(b, e, v);
|
||||
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);
|
||||
|
||||
if ((bool)r.ec or r.ptr != e)
|
||||
{
|
||||
|
||||
@@ -124,6 +124,23 @@ class matrix_expression
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename M2>
|
||||
constexpr bool operator==(const matrix_expression<M2> &m) const
|
||||
{
|
||||
bool same = false;
|
||||
if (dim_m() == m.dim_m() and dim_n() == m.dim_n())
|
||||
{
|
||||
same = true;
|
||||
for (std::size_t i = 0; same and i < m.dim_m(); ++i)
|
||||
{
|
||||
for (std::size_t j = 0; same and j < m.dim_n(); ++j)
|
||||
same = operator()(i, j) == m(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
return same;
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -594,6 +611,35 @@ auto operator*(const matrix_expression<M1> &m1, const matrix_expression<M2> &m2)
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
template <typename M2>
|
||||
class sub_matrix : public matrix_expression<sub_matrix<M2>>
|
||||
{
|
||||
public:
|
||||
sub_matrix(const M2 &m, int i, int j)
|
||||
: m_m(m)
|
||||
, m_i(i)
|
||||
, m_j(j)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr std::size_t dim_m() const { return m_m.dim_m() - 1; } ///< Return dimension m
|
||||
constexpr std::size_t dim_n() const { return m_m.dim_n() - 1; } ///< Return dimension n
|
||||
|
||||
/** Access to the value of element [ @a i, @a j ] */
|
||||
constexpr auto operator()(std::size_t i, std::size_t j) const
|
||||
{
|
||||
return m_m(
|
||||
i >= m_i ? i + 1 : i,
|
||||
j >= m_j ? j + 1 : j);
|
||||
}
|
||||
|
||||
private:
|
||||
const M2 &m_m;
|
||||
std::size_t m_i, m_j;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/** Generic routine to calculate the determinant of a matrix
|
||||
*
|
||||
* @note This is currently only implemented for fixed matrices of size 3x3
|
||||
@@ -605,11 +651,23 @@ auto determinant(const M &m);
|
||||
template <typename F = float>
|
||||
auto determinant(const matrix3x3<F> &m)
|
||||
{
|
||||
return (m(0, 0) * (m(1, 1) * m(2, 2) - m(1, 2) * m(2, 1)) +
|
||||
m(0, 1) * (m(1, 2) * m(2, 0) - m(1, 0) * m(2, 2)) +
|
||||
m(0, 2) * (m(1, 0) * m(2, 1) - m(1, 1) * m(2, 0)));
|
||||
return (m(0, 0) * ((m(1, 1) * m(2, 2) - m(1, 2) * m(2, 1))) +
|
||||
m(0, 1) * ((m(1, 2) * m(2, 0) - m(1, 0) * m(2, 2))) +
|
||||
m(0, 2) * ((m(1, 0) * m(2, 1) - m(1, 1) * m(2, 0))));
|
||||
}
|
||||
|
||||
/** Implementation of the determinant function for fixed size matrices of size 4x4 */
|
||||
template <typename F = float>
|
||||
F determinant(const matrix4x4<F> &m)
|
||||
{
|
||||
return m(0, 0) * determinant(matrix3x3<F>(sub_matrix<decltype(m)>(m, 0, 0))) -
|
||||
m(0, 1) * determinant(matrix3x3<F>(sub_matrix<decltype(m)>(m, 0, 1))) +
|
||||
m(0, 2) * determinant(matrix3x3<F>(sub_matrix<decltype(m)>(m, 0, 2))) -
|
||||
m(0, 3) * determinant(matrix3x3<F>(sub_matrix<decltype(m)>(m, 0, 3)));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/** Generic routine to calculate the inverse of a matrix
|
||||
*
|
||||
* @note This is currently only implemented for fixed matrices of size 3x3
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "cif++/atom_type.hpp"
|
||||
#include "cif++/datablock.hpp"
|
||||
#include "cif++/point.hpp"
|
||||
#include "cif++/row.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
@@ -134,14 +135,20 @@ class atom
|
||||
|
||||
row_handle row_aniso()
|
||||
{
|
||||
row_handle result{};
|
||||
auto cat = m_db.get("atom_site_anisotrop");
|
||||
return cat ? cat->operator[]({ { "id", m_id } }) : row_handle{};
|
||||
if (cat)
|
||||
result = cat->operator[]({ { "id", m_id } });
|
||||
return result;
|
||||
}
|
||||
|
||||
const row_handle row_aniso() const
|
||||
{
|
||||
row_handle result{};
|
||||
auto cat = m_db.get("atom_site_anisotrop");
|
||||
return cat ? cat->operator[]({ { "id", m_id } }) : row_handle{};
|
||||
if (cat)
|
||||
result = cat->operator[]({ { "id", m_id } });
|
||||
return result;
|
||||
}
|
||||
|
||||
const datablock &m_db;
|
||||
@@ -1059,12 +1066,30 @@ class structure
|
||||
/// \return The newly create asym ID
|
||||
std::string create_non_poly(const std::string &entity_id, std::vector<row_initializer> atoms);
|
||||
|
||||
/// \brief Create a new NonPolymer struct_asym for a compound of type \a compound_id, returns asym_id.
|
||||
/// This method creates new atom records filled with info from the CCD compound info.
|
||||
///
|
||||
/// \param compound_id The compound ID of the new nonpoly
|
||||
/// \param skip_hydrogen Do not create hydrogen atoms when true
|
||||
/// \return The newly create asym ID
|
||||
std::string create_non_poly(const std::string &compound_id, bool skip_hydrogen);
|
||||
|
||||
/// \brief Create a new water with atom constructed from info in \a atom_info
|
||||
/// This method creates a new atom record filled with info from the info.
|
||||
///
|
||||
/// \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();
|
||||
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <valarray>
|
||||
|
||||
#if __has_include(<clipper/core/coords.h>)
|
||||
@@ -365,11 +367,18 @@ class quaternion_type
|
||||
}
|
||||
|
||||
/// \brief test for all zero values
|
||||
constexpr operator bool() const
|
||||
constexpr explicit operator bool() const
|
||||
{
|
||||
return a != 0 or b != 0 or c != 0 or d != 0;
|
||||
}
|
||||
|
||||
/// \brief for debugging e.g.
|
||||
friend std::ostream &operator<<(std::ostream &os, const quaternion_type &rhs)
|
||||
{
|
||||
os << std::format("{{ a: {}, b: {}, c: {}, d: {} }}", rhs.a, rhs.b, rhs.c, rhs.d);
|
||||
return os;
|
||||
}
|
||||
|
||||
private:
|
||||
value_type a, b, c, d;
|
||||
};
|
||||
@@ -743,6 +752,55 @@ inline constexpr auto cross_product(const point_type<F1> &a, const point_type<F2
|
||||
a.m_x * b.m_y - b.m_x * a.m_y);
|
||||
}
|
||||
|
||||
/// \brief return the squared norm of point @a p
|
||||
template <typename F>
|
||||
constexpr F norm_squared(const point_type<F> &p)
|
||||
{
|
||||
return p.m_x * p.m_x + p.m_y * p.m_y + p.m_z * p.m_z;
|
||||
}
|
||||
|
||||
/// \brief return the norm of point @a p
|
||||
template <typename F>
|
||||
constexpr point_type<F> norm(const point_type<F> &p)
|
||||
{
|
||||
return std::sqrt(norm_squared(p));
|
||||
}
|
||||
|
||||
/// \brief return the point where two lines intersect, or an empty value if they don't intersect at all
|
||||
template <typename F>
|
||||
std::optional<cif::point> line_line_intersection(const point_type<F> &p1,
|
||||
const point_type<F> &p2, const point_type<F> &p3, const point_type<F> &p4)
|
||||
{
|
||||
auto p13 = p1 - p3;
|
||||
auto p43 = p4 - p3;
|
||||
if (std::abs(p43.m_x) < std::numeric_limits<F>::epsilon() and std::abs(p43.m_y) < std::numeric_limits<F>::epsilon() and std::abs(p43.m_z) < std::numeric_limits<F>::epsilon())
|
||||
return {};
|
||||
|
||||
auto p21 = p2 - p1;
|
||||
if (std::abs(p21.m_x) < std::numeric_limits<F>::epsilon() and std::abs(p21.m_y) < std::numeric_limits<F>::epsilon() and std::abs(p21.m_z) < std::numeric_limits<F>::epsilon())
|
||||
return {};
|
||||
|
||||
auto d1343 = cif::dot_product(p43, p13);
|
||||
auto d4321 = cif::dot_product(p43, p21);
|
||||
auto d1321 = cif::dot_product(p13, p21);
|
||||
auto d4343 = cif::dot_product(p43, p43);
|
||||
auto d2121 = cif::dot_product(p21, p21);
|
||||
|
||||
auto denom = d2121 * d4343 - d4321 * d4321;
|
||||
if (std::abs(denom) < std::numeric_limits<F>::epsilon())
|
||||
return {};
|
||||
|
||||
auto numer = d1343 * d4321 - d1321 * d4343;
|
||||
|
||||
auto mua = numer / denom;
|
||||
auto mub = (d1343 + d4321 * mua) / d4343;
|
||||
|
||||
auto pa = p1 + mua * p21;
|
||||
auto pb = p3 + mub * p43;
|
||||
|
||||
return { (pa + pb) / 2 };
|
||||
}
|
||||
|
||||
/// \brief return the angle in degrees between the vectors from point @a p2 to @a p1 and @a p2 to @a p3
|
||||
template <typename F>
|
||||
constexpr auto angle(const point_type<F> &p1, const point_type<F> &p2, const point_type<F> &p3)
|
||||
@@ -806,6 +864,9 @@ constexpr auto distance_point_to_line(const point_type<F> &l1, const point_type<
|
||||
return cross.length() / line.length();
|
||||
}
|
||||
|
||||
/// \brief return the smallest sphere around the points in @a pts
|
||||
std::tuple<point, float> smallest_sphere_around_points(std::vector<point> pts);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/**
|
||||
* @brief For e.g. simulated annealing, returns a new point that is moved in
|
||||
|
||||
@@ -355,279 +355,35 @@ 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>
|
||||
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);
|
||||
}
|
||||
using from_chars_function = decltype(std::from_chars(std::declval<const char *>(), std::declval<const char *>(), std::declval<T &>()));
|
||||
|
||||
/// @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);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief helper to find a from_chars function
|
||||
template <typename T>
|
||||
using from_chars_function = decltype(std::from_chars(std::declval<const char *>(), std::declval<const char *>(), std::declval<T &>()));
|
||||
template <typename T, typename = void>
|
||||
struct ff_charconv;
|
||||
|
||||
/**
|
||||
* @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 selected_charconv = typename std::conditional_t<std_experimental::is_detected_v<from_chars_function, T>, std_charconv<T>, my_charconv<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);
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace cif
|
||||
@@ -53,6 +53,7 @@
|
||||
#pragma warning(disable : 4068) // unknown pragma
|
||||
#pragma warning(disable : 4100) // unreferenced formal parameter
|
||||
#pragma warning(disable : 4101) // unreferenced local variable
|
||||
#pragma warning(disable : 4702) // unreachable code (too bad, this one. Happens in for loops)
|
||||
#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 1
|
||||
#endif
|
||||
|
||||
@@ -295,6 +296,11 @@ 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;
|
||||
|
||||
316
pcre2-simple/CMakeLists.txt
Normal file
316
pcre2-simple/CMakeLists.txt
Normal file
@@ -0,0 +1,316 @@
|
||||
# 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()
|
||||
@@ -1339,8 +1339,7 @@ std::string category::get_unique_value(std::string_view item_name)
|
||||
// brain-dead implementation
|
||||
for (std::size_t ix = 0; ix < size(); ++ix)
|
||||
{
|
||||
// result = m_name + "-" + std::to_string(ix);
|
||||
result = cif_id_for_number(ix);
|
||||
result = cif_id_for_number(static_cast<int>(ix));
|
||||
if (not contains(key(item_name) == result))
|
||||
break;
|
||||
}
|
||||
|
||||
128
src/compound.cpp
128
src/compound.cpp
@@ -24,14 +24,38 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cif++.hpp"
|
||||
#include "cif++/compound.hpp" // for compound_atom, compound_bond, compoun...
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <numeric>
|
||||
#include <shared_mutex>
|
||||
#include "cif++/atom_type.hpp" // for atom_type_traits
|
||||
#include "cif++/category.hpp" // for category
|
||||
#include "cif++/datablock.hpp" // for datablock
|
||||
#include "cif++/file.hpp" // for file
|
||||
#include "cif++/item.hpp" // for item
|
||||
#include "cif++/iterator.hpp" // for iterator_proxy
|
||||
#include "cif++/parser.hpp" // for parser
|
||||
#include "cif++/point.hpp" // for distance, point
|
||||
#include "cif++/row.hpp" // for tie, row_initializer, tie_wrap
|
||||
#include "cif++/text.hpp" // for iequals, replace_all, iset
|
||||
#include "cif++/utilities.hpp" // for load_resource, VERBOSE, colour_type
|
||||
|
||||
#include <algorithm> // for find_if
|
||||
#include <cstddef> // for size_t
|
||||
#include <exception> // for exception, throw_with_nested
|
||||
#include <filesystem> // for path, exists
|
||||
#include <fstream> // for char_traits, basic_ostream, operator<<
|
||||
#include <iomanip> // for operator<<, quoted
|
||||
#include <iostream> // for clog, cout, cerr
|
||||
#include <limits> // for numeric_limits
|
||||
#include <list> // for _List_iterator
|
||||
#include <map> // for allocator, map, _Rb_tree_iterator
|
||||
#include <memory> // for shared_ptr, unique_ptr, __shared_ptr_...
|
||||
#include <optional> // for optional
|
||||
#include <shared_mutex> // for shared_lock, shared_timed_mutex
|
||||
#include <stdexcept> // for runtime_error, invalid_argument, out_...
|
||||
#include <string> // for basic_string, string, operator==, ope...
|
||||
#include <string_view> // for string_view, basic_string_view
|
||||
#include <utility> // for pair, exchange, move
|
||||
#include <vector> // for vector
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@@ -140,7 +164,7 @@ compound::compound(cif::datablock &db)
|
||||
|
||||
cif::tie(m_id, m_name, m_type, m_formula, m_formula_weight, m_formal_charge, one_letter_code, m_parent_id) =
|
||||
chemComp.front().get("id", "name", "type", "formula", "formula_weight", "pdbx_formal_charge", "one_letter_code", "mon_nstd_parent_comp_id");
|
||||
|
||||
|
||||
if (one_letter_code.length() == 1)
|
||||
m_one_letter_code = one_letter_code.front();
|
||||
|
||||
@@ -159,7 +183,7 @@ compound::compound(cif::datablock &db)
|
||||
if (stereo_config.empty())
|
||||
atom.stereo_config = stereo_config_type::N;
|
||||
else
|
||||
atom.stereo_config = parse_stereo_config_from_string(stereo_config);
|
||||
atom.stereo_config = parse_stereo_config_from_string(stereo_config);
|
||||
m_atoms.push_back(std::move(atom));
|
||||
}
|
||||
|
||||
@@ -172,7 +196,7 @@ compound::compound(cif::datablock &db)
|
||||
if (valueOrder.empty())
|
||||
bond.type = bond_type::sing;
|
||||
else
|
||||
bond.type = parse_bond_type_from_string(valueOrder);
|
||||
bond.type = parse_bond_type_from_string(valueOrder);
|
||||
m_bonds.push_back(std::move(bond));
|
||||
}
|
||||
}
|
||||
@@ -231,12 +255,12 @@ float compound::bond_length(const std::string &atomId_1, const std::string &atom
|
||||
|
||||
bool compound::is_peptide() const
|
||||
{
|
||||
return iequals(m_type, "l-peptide linking") or iequals(m_type, "peptide linking");
|
||||
return iequals(m_type, "l-peptide linking") or iequals(m_type, "peptide linking");
|
||||
}
|
||||
|
||||
bool compound::is_base() const
|
||||
{
|
||||
return iequals(m_type, "dna linking") or iequals(m_type, "rna linking");
|
||||
return iequals(m_type, "dna linking") or iequals(m_type, "rna linking");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -294,12 +318,31 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
|
||||
delete c;
|
||||
}
|
||||
|
||||
virtual bool exists_self(const std::string &id) const
|
||||
{
|
||||
if (m_missing.contains(id))
|
||||
return false;
|
||||
|
||||
if (std::find_if(m_compounds.begin(), m_compounds.end(), [id](compound *c)
|
||||
{ return c->id() == id; }) != m_compounds.end())
|
||||
return true;
|
||||
|
||||
return m_next and m_next->exists_self(id);
|
||||
}
|
||||
|
||||
bool exists(std::string_view id)
|
||||
{
|
||||
std::shared_lock lock(mMutex);
|
||||
|
||||
return exists_self(std::string{ id });
|
||||
}
|
||||
|
||||
compound *get(std::string id)
|
||||
{
|
||||
std::shared_lock lock(mMutex);
|
||||
|
||||
compound *result = nullptr;
|
||||
|
||||
|
||||
for (auto impl = shared_from_this(); impl; impl = impl->m_next)
|
||||
{
|
||||
result = impl->create(id);
|
||||
@@ -363,7 +406,9 @@ compound *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())
|
||||
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;
|
||||
@@ -454,7 +499,6 @@ class local_compound_factory_impl : public compound_factory_impl
|
||||
compound *create(const std::string &id) override;
|
||||
|
||||
private:
|
||||
|
||||
compound *construct_compound(const datablock &db, const std::string &id, const std::string &name, const std::string &three_letter_code, const std::string &group);
|
||||
|
||||
cif::file m_local_file;
|
||||
@@ -465,7 +509,9 @@ 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())
|
||||
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;
|
||||
@@ -480,10 +526,13 @@ compound *local_compound_factory_impl::create(const std::string &id)
|
||||
|
||||
try
|
||||
{
|
||||
const auto &[id, name, threeLetterCode, group] =
|
||||
const auto &[id2, name, threeLetterCode, group] =
|
||||
chem_comp->front().get<std::string, std::string, std::string, std::string>("id", "name", "three_letter_code", "group");
|
||||
|
||||
result = construct_compound(db, id, name, threeLetterCode, group);
|
||||
if (id == id2)
|
||||
result = construct_compound(db, id, name, threeLetterCode, group);
|
||||
else
|
||||
throw std::runtime_error("Compound ID's don't match: id 1=" + id + ", id 2=" + id2);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
@@ -507,12 +556,10 @@ compound *local_compound_factory_impl::construct_compound(const datablock &rdb,
|
||||
|
||||
float formula_weight = 0;
|
||||
int formal_charge = 0;
|
||||
std::map<std::string,std::size_t> formula_data;
|
||||
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>>(
|
||||
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"))
|
||||
@@ -522,16 +569,14 @@ compound *local_compound_factory_impl::construct_compound(const datablock &rdb,
|
||||
|
||||
formula_data[type_symbol] += 1;
|
||||
|
||||
db["chem_comp_atom"].emplace({
|
||||
{ "comp_id", id },
|
||||
db["chem_comp_atom"].emplace({ { "comp_id", id },
|
||||
{ "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 },
|
||||
{ "pdbx_ordinal", ord++ }
|
||||
});
|
||||
{ "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 },
|
||||
{ "pdbx_ordinal", ord++ } });
|
||||
|
||||
formal_charge += charge;
|
||||
}
|
||||
@@ -548,21 +593,19 @@ compound *local_compound_factory_impl::construct_compound(const datablock &rdb,
|
||||
else if (cif::iequals(type, "triple") or cif::iequals(type, "trip"))
|
||||
value_order = "TRIP";
|
||||
|
||||
db["chem_comp_bond"].emplace({
|
||||
{ "comp_id", id },
|
||||
db["chem_comp_bond"].emplace({ { "comp_id", id },
|
||||
{ "atom_id_1", atom_id_1 },
|
||||
{ "atom_id_2", atom_id_2 },
|
||||
{ "value_order", value_order },
|
||||
{ "pdbx_aromatic_flag", aromatic },
|
||||
// TODO: fetch stereo_config info from chem_comp_chir
|
||||
{ "pdbx_ordinal", ord++ }
|
||||
});
|
||||
{ "pdbx_ordinal", ord++ } });
|
||||
}
|
||||
|
||||
db.emplace_back(rdb["pdbx_chem_comp_descriptor"]);
|
||||
|
||||
std::string formula;
|
||||
for (bool first = true; const auto &[symbol, count]: formula_data)
|
||||
for (bool first = true; const auto &[symbol, count] : formula_data)
|
||||
{
|
||||
if (std::exchange(first, false))
|
||||
formula += ' ';
|
||||
@@ -581,15 +624,13 @@ compound *local_compound_factory_impl::construct_compound(const datablock &rdb,
|
||||
else
|
||||
type = "NON-POLYMER";
|
||||
|
||||
db["chem_comp"].emplace({
|
||||
{ "id", id },
|
||||
db["chem_comp"].emplace({ { "id", id },
|
||||
{ "name", name },
|
||||
{ "type", type },
|
||||
{ "formula", formula },
|
||||
{ "pdbx_formal_charge", formal_charge },
|
||||
{ "formula_weight", formula_weight },
|
||||
{ "three_letter_code", three_letter_code }
|
||||
});
|
||||
{ "three_letter_code", three_letter_code } });
|
||||
|
||||
std::shared_lock lock(mMutex);
|
||||
|
||||
@@ -695,6 +736,11 @@ void compound_factory::pop_dictionary()
|
||||
m_impl = m_impl->next();
|
||||
}
|
||||
|
||||
bool compound_factory::exists(std::string_view id) const
|
||||
{
|
||||
return m_impl and m_impl->exists(id);
|
||||
}
|
||||
|
||||
const compound *compound_factory::create(std::string_view id)
|
||||
{
|
||||
auto result = m_impl ? m_impl->get(std::string{ id }) : nullptr;
|
||||
@@ -719,7 +765,7 @@ bool compound_factory::is_peptide(std::string_view res_name) const
|
||||
bool result = is_std_peptide(res_name);
|
||||
if (not result and m_impl)
|
||||
{
|
||||
auto compound = const_cast<compound_factory&>(*this).create(res_name);
|
||||
auto compound = const_cast<compound_factory &>(*this).create(res_name);
|
||||
result = compound != nullptr and compound->is_peptide();
|
||||
}
|
||||
return result;
|
||||
@@ -731,7 +777,7 @@ bool compound_factory::is_base(std::string_view res_name) const
|
||||
bool result = is_std_base(res_name);
|
||||
if (not result and m_impl)
|
||||
{
|
||||
auto compound = const_cast<compound_factory&>(*this).create(res_name);
|
||||
auto compound = const_cast<compound_factory &>(*this).create(res_name);
|
||||
result = compound != nullptr and compound->is_base();
|
||||
}
|
||||
return result;
|
||||
@@ -781,7 +827,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://github.com/ihmwg/ModelCIF/raw/master/dist/mmcif_ma.dic\n\n";
|
||||
<< "curl -o " << CACHE_DIR << "/mmcif_ma.dic https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_ma.dic\n\n";
|
||||
#endif
|
||||
|
||||
if (m_impl)
|
||||
|
||||
1371
src/cql/transaction.cpp
Normal file
1371
src/cql/transaction.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -25,8 +25,11 @@
|
||||
*/
|
||||
|
||||
#include "cif++/datablock.hpp"
|
||||
|
||||
#include "cif++/validate.hpp"
|
||||
|
||||
#include <exception>
|
||||
|
||||
namespace cif
|
||||
{
|
||||
|
||||
@@ -42,7 +45,16 @@ datablock::datablock(const datablock &db)
|
||||
void datablock::load_dictionary()
|
||||
{
|
||||
if (auto *audit_conform = get("audit_conform"); audit_conform and not audit_conform->empty())
|
||||
set_validator(&validator_factory::instance().get(*audit_conform));
|
||||
{
|
||||
try
|
||||
{
|
||||
set_validator(&validator_factory::instance().get(*audit_conform));
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
std::clog << ex.what() << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void datablock::set_validator(const validator *v)
|
||||
@@ -96,7 +108,8 @@ bool datablock::strip()
|
||||
bool result = true;
|
||||
|
||||
// remove all categories that have no validator
|
||||
erase(std::remove_if(begin(), end(), [](category &c) {
|
||||
erase(std::remove_if(begin(), end(), [](category &c)
|
||||
{
|
||||
bool result = false;
|
||||
if (c.get_cat_validator() == nullptr)
|
||||
{
|
||||
@@ -104,8 +117,8 @@ bool datablock::strip()
|
||||
std::clog << "Dropping category " << c.name() << '\n';
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}), end());
|
||||
return result; }),
|
||||
end());
|
||||
|
||||
// then strip the remaining categories
|
||||
for (auto &cat : *this)
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
#include "cif++/dictionary_parser.hpp"
|
||||
#include "cif++/file.hpp"
|
||||
#include "cif++/parser.hpp"
|
||||
#include <exception>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace cif
|
||||
{
|
||||
@@ -46,7 +49,7 @@ class dictionary_parser : public parser
|
||||
void load_dictionary()
|
||||
{
|
||||
std::unique_ptr<datablock> dict;
|
||||
auto savedDatablock = m_datablock;
|
||||
auto savedDatablock = std::exchange(m_datablock, nullptr);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -75,6 +78,9 @@ 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));
|
||||
|
||||
13
src/file.cpp
13
src/file.cpp
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
#include "cif++/file.hpp"
|
||||
#include "cif++/condition.hpp"
|
||||
#include "cif++/gzio.hpp"
|
||||
|
||||
namespace cif
|
||||
@@ -46,8 +47,16 @@ bool file::is_valid()
|
||||
{
|
||||
bool result = not empty();
|
||||
|
||||
for (auto &d : *this)
|
||||
result = d.is_valid() and result;
|
||||
for (bool first = true; auto &d : *this)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
result = d.is_valid() and result;
|
||||
first = false;
|
||||
}
|
||||
else if (d.get_validator() != nullptr)
|
||||
result = d.is_valid() and result;
|
||||
}
|
||||
|
||||
if (result)
|
||||
result = validate_links();
|
||||
|
||||
146
src/model.cpp
146
src/model.cpp
@@ -24,13 +24,17 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cif++/model.hpp"
|
||||
#include "cif++.hpp"
|
||||
#include "cif++/point.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <initializer_list>
|
||||
#include <iomanip>
|
||||
#include <numeric>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@@ -47,9 +51,9 @@ void atom::atom_impl::moveTo(const point &p)
|
||||
|
||||
auto r = row();
|
||||
|
||||
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);
|
||||
r.assign("Cartn_x", cif::format("{:.3f}", p.m_x), false, false);
|
||||
r.assign("Cartn_y", cif::format("{:.3f}", p.m_y), false, false);
|
||||
r.assign("Cartn_z", cif::format("{:.3f}", p.m_z), false, false);
|
||||
|
||||
m_location = p;
|
||||
}
|
||||
@@ -348,17 +352,7 @@ std::tuple<point, float> residue::center_and_radius() const
|
||||
for (auto &a : m_atoms)
|
||||
pts.push_back(a.get_location());
|
||||
|
||||
auto center = centroid(pts);
|
||||
float radius = 0;
|
||||
|
||||
for (auto &pt : pts)
|
||||
{
|
||||
float d = static_cast<float>(distance(pt, center));
|
||||
if (radius < d)
|
||||
radius = d;
|
||||
}
|
||||
|
||||
return std::make_tuple(center, radius);
|
||||
return smallest_sphere_around_points(pts);
|
||||
}
|
||||
|
||||
bool residue::has_alternate_atoms() const
|
||||
@@ -1275,28 +1269,28 @@ void structure::load_atoms_for_model(structure_open_options options)
|
||||
else
|
||||
{
|
||||
std::vector<cif::mm::atom> atoms;
|
||||
std::map<std::tuple<std::string,int>, std::map<std::string, float>> alts;
|
||||
|
||||
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;
|
||||
|
||||
// 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();
|
||||
@@ -1319,11 +1313,11 @@ void structure::load_atoms_for_model(structure_open_options options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
value.clear();
|
||||
value.emplace(alt_id, occupancy);
|
||||
}
|
||||
|
||||
|
||||
for (auto a : atoms)
|
||||
{
|
||||
if (a.is_alternate())
|
||||
@@ -1335,10 +1329,8 @@ void structure::load_atoms_for_model(structure_open_options options)
|
||||
}
|
||||
else
|
||||
emplace_atom(a);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void structure::load_data()
|
||||
@@ -1898,13 +1890,12 @@ void structure::swap_atoms(atom a1, atom a2)
|
||||
auto r1 = atomSites.find1(key("id") == a1.id());
|
||||
auto r2 = atomSites.find1(key("id") == a2.id());
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
@@ -2348,6 +2339,36 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector
|
||||
return asym_id;
|
||||
}
|
||||
|
||||
std::string structure::create_non_poly(const std::string &compound_id, bool skip_hydrogen)
|
||||
{
|
||||
auto compound = cif::compound_factory::instance().create(compound_id);
|
||||
if (compound == nullptr)
|
||||
throw std::runtime_error(std::format("{} is not a known compound", compound_id));
|
||||
|
||||
std::vector<cif::row_initializer> atoms;
|
||||
for (auto a : compound->atoms())
|
||||
{
|
||||
// We skip H-atoms, as fitting without H-atoms works better and we avoid conflicts in protonation states between CCD and MONLIB
|
||||
if (skip_hydrogen and cif::atom_type_traits(a.type_symbol).symbol() == "H")
|
||||
continue;
|
||||
|
||||
auto ax = a.get_location().get_x();
|
||||
auto ay = a.get_location().get_y();
|
||||
auto az = a.get_location().get_z();
|
||||
|
||||
atoms.emplace_back(cif::row_initializer{
|
||||
{ "type_symbol", cif::atom_type_traits(a.type_symbol).symbol() },
|
||||
{ "label_atom_id", a.id },
|
||||
{ "auth_atom_id", a.id },
|
||||
{ "Cartn_x", ax },
|
||||
{ "Cartn_y", ay },
|
||||
{ "Cartn_z", az },
|
||||
{ "B_iso_or_equiv", 30.00 } });
|
||||
}
|
||||
|
||||
return create_non_poly(create_non_poly_entity(compound_id), atoms);
|
||||
}
|
||||
|
||||
void structure::create_water(row_initializer atom)
|
||||
{
|
||||
using namespace literals;
|
||||
@@ -2412,6 +2433,61 @@ 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"];
|
||||
@@ -2845,8 +2921,8 @@ static int compare_numbers(std::string_view a, std::string_view b)
|
||||
|
||||
std::from_chars_result ra, rb;
|
||||
|
||||
ra = selected_charconv<double>::from_chars(a.data(), a.data() + a.length(), da);
|
||||
rb = selected_charconv<double>::from_chars(b.data(), b.data() + b.length(), db);
|
||||
ra = from_chars(a.data(), a.data() + a.length(), da);
|
||||
rb = from_chars(b.data(), b.data() + b.length(), db);
|
||||
|
||||
if (not(bool) ra.ec and not(bool) rb.ec)
|
||||
{
|
||||
@@ -2869,7 +2945,7 @@ static int compare_numbers(std::string_view a, std::string_view b)
|
||||
|
||||
int compare_cif_id(const std::string &a, const std::string &b)
|
||||
{
|
||||
int d = a.length() - b.length();
|
||||
int d = static_cast<int>(a.length() - b.length());
|
||||
if (d == 0)
|
||||
d = a.compare(b);
|
||||
return d;
|
||||
|
||||
@@ -57,9 +57,9 @@ std::string cif2pdbDate(const std::string &d)
|
||||
int month = std::stoi(m[2].str());
|
||||
|
||||
if (m[3].matched)
|
||||
result = std::format("%02.2d-%3.3s-%02.2d", stoi(m[3].str()), kMonths[month - 1], (year % 100));
|
||||
result = cif::format("{:02}-{:3.3}-{:02}", stoi(m[3].str()), kMonths[month - 1], (year % 100));
|
||||
else
|
||||
result = std::format("%3.3s-%02.2d", kMonths[month - 1], (year % 100));
|
||||
result = cif::format("{:3.3}-{:02}", kMonths[month - 1], (year % 100));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -257,14 +257,14 @@ std::size_t WriteCitation(std::ostream &pdbFile, const datablock &db, row_handle
|
||||
{
|
||||
to_upper(pubname);
|
||||
|
||||
pdbFile << s1 + std::format("REF %2.2s %-28.28s %2.2s%4.4s %5.5s %4.4s", "" /* continuation */, pubname, (volume.empty() ? "" : "V."), volume, pageFirst, year)
|
||||
pdbFile << s1 << cif::format("REF {:2.2s} {:<28.28s} {:2.2s}{:>4.4s} {:>5.5s} {:4.4s}", "" /* continuation */, pubname, (volume.empty() ? "" : "V."), volume, pageFirst, year)
|
||||
<< '\n';
|
||||
++result;
|
||||
}
|
||||
|
||||
if (not issn.empty())
|
||||
{
|
||||
pdbFile << s1 + std::format("REFN ISSN %-25.25s", issn) << '\n';
|
||||
pdbFile << s1 << cif::format("REFN ISSN {:<25.25s}", issn) << '\n';
|
||||
++result;
|
||||
}
|
||||
|
||||
@@ -273,9 +273,9 @@ 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::std::format(kRefHeader)
|
||||
// pdbFile << (boost::cif::format(kRefHeader)
|
||||
// % (astm.empty() ? "" : "ASTN")
|
||||
// % astm
|
||||
// % country
|
||||
@@ -285,13 +285,13 @@ std::size_t WriteCitation(std::ostream &pdbFile, const datablock &db, row_handle
|
||||
|
||||
if (not pmid.empty())
|
||||
{
|
||||
pdbFile << s1 + std::format("PMID %-60.60s ", pmid) << '\n';
|
||||
pdbFile << s1 << cif::format("PMID {:<60.60s} ", pmid) << '\n';
|
||||
++result;
|
||||
}
|
||||
|
||||
if (not doi.empty())
|
||||
{
|
||||
pdbFile << s1 + std::format("DOI %-60.60s ", doi) << '\n';
|
||||
pdbFile << s1 << cif::format("DOI {:<60.60s} ", doi) << '\n';
|
||||
++result;
|
||||
}
|
||||
|
||||
@@ -303,9 +303,9 @@ 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";
|
||||
// "HEADER {:<40.40s}"
|
||||
// "{:<9.9s}"
|
||||
// " {:<4.4s}";
|
||||
|
||||
// HEADER
|
||||
|
||||
@@ -340,10 +340,10 @@ void write_header_lines(std::ostream &pdbFile, const datablock &db)
|
||||
}
|
||||
}
|
||||
|
||||
pdbFile << std::format(/* kHeader */
|
||||
"HEADER %-40.40s"
|
||||
"%-9.9s"
|
||||
" %-4.4s"
|
||||
pdbFile << cif::format(/* kHeader */
|
||||
"HEADER {:<40.40s}"
|
||||
"{:<9.9s}"
|
||||
" {:<4.4s}"
|
||||
|
||||
, keywords, date, db.name()) << '\n';
|
||||
|
||||
@@ -558,9 +558,9 @@ void WriteTitle(std::ostream &pdbFile, const datablock &db)
|
||||
{
|
||||
std::string cs = ++continuation > 1 ? std::to_string(continuation) : std::string();
|
||||
|
||||
pdbFile << std::format("REVDAT %3d%2.2s %9.9s %4.4s %1d ", revNum, cs, date, db.name(), modType);
|
||||
pdbFile << cif::format("REVDAT {:3}{:2.2s} {:9.9s} {:4.4s} {:1} ", revNum, cs, date, db.name(), modType);
|
||||
for (std::size_t i = 0; i < 4; ++i)
|
||||
pdbFile << std::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 +613,7 @@ void WriteRemark2(std::ostream &pdbFile, const datablock &db)
|
||||
{
|
||||
float resHigh = refine.front()["ls_d_res_high"].as<float>();
|
||||
pdbFile << "REMARK 2\n"
|
||||
<< std::format("REMARK 2 RESOLUTION. %7.2f ANGSTROMS.", resHigh) << '\n';
|
||||
<< cif::format("REMARK 2 RESOLUTION. {:7.2f} ANGSTROMS.", resHigh) << '\n';
|
||||
}
|
||||
catch (...)
|
||||
{ /* skip it */
|
||||
@@ -760,7 +760,7 @@ class Fs : public FBase
|
||||
else
|
||||
{
|
||||
os << '\n';
|
||||
WriteOneContinuedLine(os, std::format("REMARK {:3} ", mNr), 0, s);
|
||||
WriteOneContinuedLine(os, cif::format("REMARK {:3} ", mNr), 0, s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1613,7 +1613,7 @@ void WriteRemark3Phenix(std::ostream &pdbFile, const datablock &db)
|
||||
|
||||
percent_reflns_obs /= 100;
|
||||
|
||||
pdbFile << RM3(" ") << std::format("%3d %7.4f - %7.4f %4.2f %8d %5d %6.4f %6.4f", bin++, d_res_low, d_res_high, percent_reflns_obs, number_reflns_R_work, number_reflns_R_free, R_factor_R_work, R_factor_R_free) << '\n';
|
||||
pdbFile << RM3(" ") << cif::format("{:3} {:7.4f} - {:7.4f} {:4.2f} {:8} {:5} {:6.4f} {:6.4f}", bin++, d_res_low, d_res_high, percent_reflns_obs, number_reflns_R_work, number_reflns_R_free, R_factor_R_work, R_factor_R_free) << '\n';
|
||||
}
|
||||
|
||||
pdbFile << RM3("") << '\n'
|
||||
@@ -2581,7 +2581,7 @@ void WriteRemark465(std::ostream &pdbFile, const datablock &db)
|
||||
cif::tie(modelNr, resName, chainID, iCode, seqNr) =
|
||||
r.get("PDB_model_num", "auth_comp_id", "auth_asym_id", "PDB_ins_code", "auth_seq_id");
|
||||
|
||||
pdbFile << std::format("REMARK 465 %3.3s %3.3s %1.1s %5d%1.1s", modelNr, resName, chainID, seqNr, iCode) << '\n';
|
||||
pdbFile << cif::format("REMARK 465 {:3.3s} {:3.3s} {:1.1s} {:5}{:1.1s}", modelNr, resName, chainID, seqNr, iCode) << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2628,7 +2628,7 @@ void WriteRemark470(std::ostream &pdbFile, const datablock &db)
|
||||
|
||||
while (not a.second.empty())
|
||||
{
|
||||
pdbFile << std::format("REMARK 470 %3.3s %3.3s %1.1s%4d%1.1s ", modelNr, resName, chainID, seqNr, iCode) << " ";
|
||||
pdbFile << cif::format("REMARK 470 {:>3.3s} {:3.3s} {:1.1s}{:4}{:1.1s} ", modelNr, resName, chainID, seqNr, iCode) << " ";
|
||||
|
||||
for (std::size_t i = 0; i < 6 and not a.second.empty(); ++i)
|
||||
{
|
||||
@@ -2725,17 +2725,17 @@ int WritePrimaryStructure(std::ostream &pdbFile, const datablock &db)
|
||||
"pdbx_seq_align_end_ins_code", "pdbx_db_accession", "db_align_beg", "pdbx_db_align_beg_ins_code", "db_align_end", "pdbx_db_align_end_ins_code");
|
||||
|
||||
if (dbAccession.length() > 8 or db_code.length() > 12 or atoi(dbseqEnd.c_str()) >= 100000)
|
||||
pdbFile << std::format(
|
||||
"DBREF1 %4.4s %1.1s %4.4s%1.1s %4.4s%1.1s %-6.6s %-20.20s",
|
||||
pdbFile << cif::format(
|
||||
"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'
|
||||
<< std::format(
|
||||
"DBREF2 %4.4s %1.1s %-22.22s %10.10s %10.10s",
|
||||
<< cif::format(
|
||||
"DBREF2 {:>4.4s} {:1.1s} {:<22.22s} {:10.10s} {:10.10s}",
|
||||
idCode, chainID, dbAccession, dbseqBegin, dbseqEnd)
|
||||
<< '\n';
|
||||
else
|
||||
pdbFile << std::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",
|
||||
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}",
|
||||
idCode, chainID, seqBegin, insertBegin, seqEnd, insertEnd, db_name, dbAccession, db_code, dbseqBegin, dbinsBeg, dbseqEnd, dbinsEnd)
|
||||
<< '\n';
|
||||
}
|
||||
@@ -2753,8 +2753,8 @@ int WritePrimaryStructure(std::ostream &pdbFile, const datablock &db)
|
||||
|
||||
to_upper(conflict);
|
||||
|
||||
pdbFile << std::format(
|
||||
"SEQADV %4.4s %3.3s %1.1s %4.4s%1.1s %-4.4s %-9.9s %3.3s %5.5s %-21.21s",
|
||||
pdbFile << cif::format(
|
||||
"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)
|
||||
<< '\n';
|
||||
}
|
||||
@@ -2782,8 +2782,8 @@ int WritePrimaryStructure(std::ostream &pdbFile, const datablock &db)
|
||||
if (t > 13)
|
||||
t = 13;
|
||||
|
||||
pdbFile << std::format(
|
||||
"SEQRES %3d %1.1s %4d %-51.51s ",
|
||||
pdbFile << cif::format(
|
||||
"SEQRES {:3} {:1.1s} {:4} {:<51.51s} ",
|
||||
n++, std::string{ chainID }, seqresl[chainID], join(seq.begin(), seq.begin() + t, " "))
|
||||
<< '\n';
|
||||
|
||||
@@ -2802,8 +2802,8 @@ int WritePrimaryStructure(std::ostream &pdbFile, const datablock &db)
|
||||
cif::tie(chainID, seqNum, resName, iCode, stdRes, comment) =
|
||||
r.get("auth_asym_id", "auth_seq_id", "auth_comp_id", "PDB_ins_code", "parent_comp_id", "details");
|
||||
|
||||
pdbFile << std::format(
|
||||
"MODRES %4.4s %3.3s %1.1s %4.4s%1.1s %3.3s %-41.41s",
|
||||
pdbFile << cif::format(
|
||||
"MODRES {:4.4s} {:3.3s} {:1.1s} {:4.4s}{:1.1s} {:3.3s} {:<41.41s}",
|
||||
db.name(), resName, chainID, seqNum, iCode, stdRes, comment)
|
||||
<< '\n';
|
||||
}
|
||||
@@ -2919,7 +2919,7 @@ int WriteHeterogen(std::ostream &pdbFile, const datablock &db)
|
||||
{
|
||||
if (h.water)
|
||||
continue;
|
||||
pdbFile << std::format("HET %3.3s %c%4d%c %5d", h.hetID, h.chainID, h.seqNum, h.iCode, h.numHetAtoms) << '\n';
|
||||
pdbFile << cif::format("HET {:3.3s} {:1c}{:4}{:1c} {:5}", h.hetID, h.chainID, h.seqNum, h.iCode, h.numHetAtoms) << '\n';
|
||||
++numHet;
|
||||
}
|
||||
|
||||
@@ -2934,7 +2934,7 @@ int WriteHeterogen(std::ostream &pdbFile, const datablock &db)
|
||||
|
||||
for (;;)
|
||||
{
|
||||
pdbFile << std::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 +3026,7 @@ int WriteHeterogen(std::ostream &pdbFile, const datablock &db)
|
||||
{
|
||||
std::stringstream fs;
|
||||
|
||||
fs << std::format("FORMUL %2d %3.3s %2.2s%c", componentNr, hetID, (c > 1 ? std::to_string(c) : std::string()), (hetID == water_comp_id ? '*' : ' '));
|
||||
fs << cif::format("FORMUL {:2} {:3.3s} {:2.2s}{:1c}", componentNr, hetID, (c > 1 ? std::to_string(c) : std::string()), (hetID == water_comp_id ? '*' : ' '));
|
||||
++c;
|
||||
|
||||
if (formula.length() > 51)
|
||||
@@ -3093,7 +3093,7 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
|
||||
"pdbx_PDB_helix_class", "pdbx_PDB_helix_length", "beg_auth_seq_id", "end_auth_seq_id");
|
||||
|
||||
++numHelix;
|
||||
pdbFile << std::format("HELIX %3d %3.3s %3.3s %1.1s %4d%1.1s %3.3s %1.1s %4d%1.1s%2d%-30.30s %5d",
|
||||
pdbFile << cif::format("HELIX {:3} {:>3.3s} {:3.3s} {:1.1s} {:4}{:1.1s} {:3.3s} {:1.1s} {:4}{:1.1s}{:2}{:<30.30s} {:5}",
|
||||
numHelix, pdbx_PDB_helix_id, beg_label_comp_id, beg_auth_asym_id, beg_auth_seq_id, pdbx_beg_PDB_ins_code, end_label_comp_id, end_auth_asym_id, end_auth_seq_id, pdbx_end_PDB_ins_code, pdbx_PDB_helix_class, details, pdbx_PDB_helix_length)
|
||||
<< '\n';
|
||||
}
|
||||
@@ -3130,7 +3130,7 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
|
||||
"pdbx_end_PDB_ins_code", "beg_auth_comp_id", "beg_auth_asym_id", "beg_auth_seq_id",
|
||||
"end_auth_comp_id", "end_auth_asym_id", "end_auth_seq_id");
|
||||
|
||||
pdbFile << std::format("SHEET %3.3s %3.3s%2d %3.3s %1.1s%4d%1.1s %3.3s %1.1s%4d%1.1s%2d", rangeID1, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, 0) << '\n';
|
||||
pdbFile << cif::format("SHEET {:>3.3s} {:>3.3s}{:2} {:3.3s} {:1.1s}{:4}{:1.1s} {:3.3s} {:1.1s}{:4}{:1.1s}{:2}", rangeID1, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, 0) << '\n';
|
||||
|
||||
first = false;
|
||||
}
|
||||
@@ -3149,7 +3149,7 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
|
||||
|
||||
if (h.empty())
|
||||
{
|
||||
pdbFile << std::format("SHEET %3.3s %3.3s%2d %3.3s %1.1s%4d%1.1s %3.3s %1.1s%4d%1.1s%2d", rangeID2, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, sense) << '\n';
|
||||
pdbFile << cif::format("SHEET {:>3.3s} {:>3.3s}{:2} {:3.3s} {:1.1s}{:4}{:1.1s} {:3.3s} {:1.1s}{:4}{:1.1s}{:2}", rangeID2, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, sense) << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3162,8 +3162,8 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
|
||||
curAtom = cif2pdbAtomName(curAtom, compID[0], db);
|
||||
prevAtom = cif2pdbAtomName(prevAtom, compID[1], db);
|
||||
|
||||
pdbFile << std::format("SHEET %3.3s %3.3s%2d %3.3s %1.1s%4d%1.1s %3.3s %1.1s%4d%1.1s%2d "
|
||||
"%-4.4s%3.3s %1.1s%4d%1.1s %-4.4s%3.3s %1.1s%4d%1.1s",
|
||||
pdbFile << cif::format("SHEET {:>3.3s} {:>3.3s}{:2} {:3.3s} {:1.1s}{:4}{:1.1s} {:3.3s} {:1.1s}{:4}{:1.1s}{:2} "
|
||||
"{:<4.4s}{:3.3s} {:1.1s}{:4}{:1.1s} {:<4.4s}{:3.3s} {:1.1s}{:4}{:1.1s}",
|
||||
rangeID2, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, sense, curAtom, curResName, curChainID, curResSeq, curICode, prevAtom, prevResName, prevChainID, prevResSeq, prevICode)
|
||||
<< '\n';
|
||||
}
|
||||
@@ -3201,7 +3201,7 @@ void WriteConnectivity(std::ostream &pdbFile, const datablock &db)
|
||||
sym1 = cif2pdbSymmetry(sym1);
|
||||
sym2 = cif2pdbSymmetry(sym2);
|
||||
|
||||
pdbFile << std::format("SSBOND %3d CYS %1.1s %4d%1.1s CYS %1.1s %4d%1.1s %6.6s %6.6s %5.2f", nr, chainID1, seqNum1, icode1, chainID2, seqNum2, icode2, sym1, sym2, Length) << '\n';
|
||||
pdbFile << cif::format("SSBOND {:3} CYS {:1.1s} {:4}{:1.1s} CYS {:1.1s} {:4}{:1.1s} {:6.6s} {:6.6s} {:5.2f}", nr, chainID1, seqNum1, icode1, chainID2, seqNum2, icode2, sym1, sym2, Length) << '\n';
|
||||
|
||||
++nr;
|
||||
}
|
||||
@@ -3228,10 +3228,10 @@ void WriteConnectivity(std::ostream &pdbFile, const datablock &db)
|
||||
sym1 = cif2pdbSymmetry(sym1);
|
||||
sym2 = cif2pdbSymmetry(sym2);
|
||||
|
||||
pdbFile << std::format("LINK %-4.4s%1.1s%3.3s %1.1s%4d%1.1s %-4.4s%1.1s%3.3s %1.1s%4d%1.1s %6.6s %6.6s", name1, altLoc1, resName1, chainID1, resSeq1, iCode1, name2, altLoc2, resName2, chainID2, resSeq2, iCode2, sym1, sym2);
|
||||
pdbFile << cif::format("LINK {:<4.4s}{:1.1s}{:3.3s} {:1.1s}{:4}{:1.1s} {:<4.4s}{:1.1s}{:3.3s} {:1.1s}{:4}{:1.1s} {:>6.6s} {:>6.6s}", name1, altLoc1, resName1, chainID1, resSeq1, iCode1, name2, altLoc2, resName2, chainID2, resSeq2, iCode2, sym1, sym2);
|
||||
|
||||
if (not Length.empty())
|
||||
pdbFile << std::format(" %5.2f", stod(Length));
|
||||
pdbFile << cif::format(" {:5.2f}", stod(Length));
|
||||
|
||||
pdbFile << '\n';
|
||||
}
|
||||
@@ -3249,7 +3249,7 @@ void WriteConnectivity(std::ostream &pdbFile, const datablock &db)
|
||||
"pdbx_label_comp_id_2", "pdbx_auth_asym_id_2", "pdbx_auth_seq_id_2", "pdbx_PDB_ins_code_2",
|
||||
"pdbx_PDB_model_num", "pdbx_omega_angle");
|
||||
|
||||
pdbFile << std::format("CISPEP %3.3s %3.3s %1.1s %4d%1.1s %3.3s %1.1s %4d%1.1s %3.3s %6.2f",
|
||||
pdbFile << cif::format("CISPEP {:3.3s} {:3.3s} {:1.1s} {:4}{:1.1s} {:3.3s} {:1.1s} {:4}{:1.1s} {:3.3s} {:6.2f}",
|
||||
serNum, pep1, chainID1, seqNum1, icode1, pep2, chainID2, seqNum2, icode2, modNum, measure) << '\n';
|
||||
}
|
||||
}
|
||||
@@ -3270,7 +3270,7 @@ int WriteMiscellaneousFeatures(std::ostream &pdbFile, const datablock &db)
|
||||
cif::tie(siteID, resName, chainID, seq, iCode) =
|
||||
r.get("site_id", "auth_comp_id", "auth_asym_id", "auth_seq_id", "pdbx_auth_ins_code");
|
||||
|
||||
sites[siteID].push_back(std::format("%3.3s %1.1s%4d%1.1s ", resName, chainID, seq, iCode));
|
||||
sites[siteID].push_back(cif::format("{:3.3s} {:1.1s}{:4}{:1.1s} ", resName, chainID, seq, iCode));
|
||||
}
|
||||
|
||||
for (auto s : sites)
|
||||
@@ -3283,7 +3283,7 @@ int WriteMiscellaneousFeatures(std::ostream &pdbFile, const datablock &db)
|
||||
int nr = 1;
|
||||
while (res.empty() == false)
|
||||
{
|
||||
pdbFile << std::format("SITE %3d %3.3s %2d ", nr, siteID, numRes);
|
||||
pdbFile << cif::format("SITE {:3} {:3.3s} {:2} ", nr, siteID, numRes);
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
@@ -3312,7 +3312,7 @@ void WriteCrystallographic(std::ostream &pdbFile, const datablock &db)
|
||||
|
||||
r = db["cell"].find_first(key("entry_id") == db.name());
|
||||
|
||||
pdbFile << std::format("CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11.11s%4d", r["length_a"].as<double>(), r["length_b"].as<double>(), r["length_c"].as<double>(), r["angle_alpha"].as<double>(), r["angle_beta"].as<double>(), r["angle_gamma"].as<double>(), symmetry, r["Z_PDB"].as<int>()) << '\n';
|
||||
pdbFile << cif::format("CRYST1{:9.3f}{:9.3f}{:9.3f}{:7.2f}{:7.2f}{:7.2f} {:<11.11s}{:4}", r["length_a"].as<double>(), r["length_b"].as<double>(), r["length_c"].as<double>(), r["angle_alpha"].as<double>(), r["angle_beta"].as<double>(), r["angle_gamma"].as<double>(), symmetry, r["Z_PDB"].as<int>()) << '\n';
|
||||
}
|
||||
|
||||
int WriteCoordinateTransformation(std::ostream &pdbFile, const datablock &db)
|
||||
@@ -3321,18 +3321,18 @@ int WriteCoordinateTransformation(std::ostream &pdbFile, const datablock &db)
|
||||
|
||||
for (auto r : db["database_PDB_matrix"])
|
||||
{
|
||||
pdbFile << std::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 << std::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 << std::format("ORIGX%1d %10.6f%10.6f%10.6f %10.5f", 3, r["origx[3][1]"].as<float>(), r["origx[3][2]"].as<float>(), r["origx[3][3]"].as<float>(), r["origx_vector[3]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("ORIGX{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 1, r["origx[1][1]"].as<float>(), r["origx[1][2]"].as<float>(), r["origx[1][3]"].as<float>(), r["origx_vector[1]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("ORIGX{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 2, r["origx[2][1]"].as<float>(), r["origx[2][2]"].as<float>(), r["origx[2][3]"].as<float>(), r["origx_vector[2]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("ORIGX{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 3, r["origx[3][1]"].as<float>(), r["origx[3][2]"].as<float>(), r["origx[3][3]"].as<float>(), r["origx_vector[3]"].as<float>()) << '\n';
|
||||
result += 3;
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto r : db["atom_sites"])
|
||||
{
|
||||
pdbFile << std::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 << std::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 << std::format("SCALE%1d %10.6f%10.6f%10.6f %10.5f", 3, r["fract_transf_matrix[3][1]"].as<float>(), r["fract_transf_matrix[3][2]"].as<float>(), r["fract_transf_matrix[3][3]"].as<float>(), r["fract_transf_vector[3]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("SCALE{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 1, r["fract_transf_matrix[1][1]"].as<float>(), r["fract_transf_matrix[1][2]"].as<float>(), r["fract_transf_matrix[1][3]"].as<float>(), r["fract_transf_vector[1]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("SCALE{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 2, r["fract_transf_matrix[2][1]"].as<float>(), r["fract_transf_matrix[2][2]"].as<float>(), r["fract_transf_matrix[2][3]"].as<float>(), r["fract_transf_vector[2]"].as<float>()) << '\n';
|
||||
pdbFile << cif::format("SCALE{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 3, r["fract_transf_matrix[3][1]"].as<float>(), r["fract_transf_matrix[3][2]"].as<float>(), r["fract_transf_matrix[3][3]"].as<float>(), r["fract_transf_vector[3]"].as<float>()) << '\n';
|
||||
result += 3;
|
||||
break;
|
||||
}
|
||||
@@ -3342,9 +3342,9 @@ int WriteCoordinateTransformation(std::ostream &pdbFile, const datablock &db)
|
||||
{
|
||||
std::string given = r["code"] == "given" ? "1" : "";
|
||||
|
||||
pdbFile << std::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 << std::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 << std::format("MTRIX%1d %3d%10.6f%10.6f%10.6f %10.5f %1.1s", 3, nr, r["matrix[3][1]"].as<float>(), r["matrix[3][2]"].as<float>(), r["matrix[3][3]"].as<float>(), r["vector[3]"].as<float>(), given) << '\n';
|
||||
pdbFile << cif::format("MTRIX{:1} {:3}{:10.6f}{:10.6f}{:10.6f} {:10.5f} {:1.1s}", 1, nr, r["matrix[1][1]"].as<float>(), r["matrix[1][2]"].as<float>(), r["matrix[1][3]"].as<float>(), r["vector[1]"].as<float>(), given) << '\n';
|
||||
pdbFile << cif::format("MTRIX{:1} {:3}{:10.6f}{:10.6f}{:10.6f} {:10.5f} {:1.1s}", 2, nr, r["matrix[2][1]"].as<float>(), r["matrix[2][2]"].as<float>(), r["matrix[2][3]"].as<float>(), r["vector[2]"].as<float>(), given) << '\n';
|
||||
pdbFile << cif::format("MTRIX{:1} {:3}{:10.6f}{:10.6f}{:10.6f} {:10.5f} {:1.1s}", 3, nr, r["matrix[3][1]"].as<float>(), r["matrix[3][2]"].as<float>(), r["matrix[3][3]"].as<float>(), r["vector[3]"].as<float>(), given) << '\n';
|
||||
|
||||
++nr;
|
||||
result += 3;
|
||||
@@ -3363,10 +3363,6 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
|
||||
|
||||
auto &atom_site = db["atom_site"];
|
||||
auto &atom_site_anisotrop = db["atom_site_anisotrop"];
|
||||
auto &entity = db["entity"];
|
||||
// auto &pdbx_poly_seq_scheme = db["pdbx_poly_seq_scheme"];
|
||||
// auto &pdbx_nonpoly_scheme = db["pdbx_nonpoly_scheme"];
|
||||
auto &pdbx_branch_scheme = db["pdbx_branch_scheme"];
|
||||
|
||||
int serial = 1;
|
||||
auto ri = atom_site.begin();
|
||||
@@ -3411,7 +3407,7 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
|
||||
|
||||
if (terminate)
|
||||
{
|
||||
pdbFile << std::format("TER %5d %3.3s %1.1s%4d%1.1s", serial, resName, chainID, resSeq, iCode) << '\n';
|
||||
pdbFile << cif::format("TER {:5} {:3.3s} {:1.1s}{:4}{:1.1s}", serial, resName, chainID, resSeq, iCode) << '\n';
|
||||
|
||||
++serial;
|
||||
terminatedChains.insert(chainID);
|
||||
@@ -3440,26 +3436,6 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
|
||||
r.get("id", "group_PDB", "label_atom_id", "label_alt_id", "auth_comp_id", "auth_asym_id", "auth_seq_id",
|
||||
"pdbx_PDB_ins_code", "Cartn_x", "Cartn_y", "Cartn_z", "occupancy", "B_iso_or_equiv", "type_symbol", "pdbx_formal_charge");
|
||||
|
||||
if (resName != "HOH")
|
||||
{
|
||||
int entity_id = r.get<int>("label_entity_id");
|
||||
try
|
||||
{
|
||||
auto type = entity.find1<std::string>("id"_key == entity_id, "type");
|
||||
|
||||
if (type == "branched") // find the real auth_seq_num, since sugars have their auth_seq_num reused as sugar number... sigh.
|
||||
resSeq = pdbx_branch_scheme.find1<int>("asym_id"_key == r.get<std::string>("label_asym_id") and "pdb_seq_num"_key == resSeq, "auth_seq_num");
|
||||
// else if (type == "non-polymer") // same for non-polymers
|
||||
// resSeq = pdbx_nonpoly_scheme.find1<int>("asym_id"_key == r.get<std::string>("label_asym_id") and "pdb_seq_num"_key == resSeq, "auth_seq_num");
|
||||
// else if (type == "polymer")
|
||||
// resSeq = pdbx_poly_seq_scheme.find1<int>("asym_id"_key == r.get<std::string>("label_asym_id") and "pdb_seq_num"_key == resSeq, "auth_seq_num");
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
std::cerr << "Oops, there was not exactly one entity with id " << entity_id << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
if (chainID.length() > 1)
|
||||
throw std::runtime_error("Chain ID " + chainID + " won't fit into a PDB file");
|
||||
|
||||
@@ -3470,7 +3446,8 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
|
||||
if (charge != 0)
|
||||
sCharge = std::to_string(charge) + (charge > 0 ? '+' : '-');
|
||||
|
||||
pdbFile << std::format("%-6.6s%5d %-4.4s%1.1s%3.3s %1.1s%4d%1.1s %8.3f%8.3f%8.3f%6.2f%6.2f %2.2s%2.2s", group, serial, name, altLoc, resName, chainID, resSeq, iCode, x, y, z, occupancy, tempFactor, element, sCharge) << '\n';
|
||||
pdbFile << cif::format("{:<6.6s}{:5} {:<4.4s}{:1.1s}{:3.3s} {:1.1s}{:4}{:1.1s} {:8.3f}{:8.3f}{:8.3f}{:6.2f}{:6.2f} {:>2.2s}{:2.2s}",
|
||||
group, serial, name, altLoc, resName, chainID, resSeq, iCode, x, y, z, occupancy, tempFactor, element, sCharge) << '\n';
|
||||
|
||||
++numCoord;
|
||||
|
||||
@@ -3485,7 +3462,7 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
|
||||
tie(u11, u22, u33, u12, u13, u23) =
|
||||
ai.get("U[1][1]", "U[2][2]", "U[3][3]", "U[1][2]", "U[1][3]", "U[2][3]");
|
||||
|
||||
pdbFile << std::format("ANISOU%5d %-4.4s%1.1s%3.3s %1.1s%4d%1.1s %7d%7d%7d%7d%7d%7d %2.2s%2.2s", serial, name, altLoc, resName, chainID, resSeq, iCode, std::lrintf(u11 * 10000), std::lrintf(u22 * 10000), std::lrintf(u33 * 10000), std::lrintf(u12 * 10000), std::lrintf(u13 * 10000), std::lrintf(u23 * 10000), element, sCharge) << '\n';
|
||||
pdbFile << cif::format("ANISOU{:5} {:<4.4s}{:1.1s}{:3.3s} {:1.1s}{:4}{:1.1s} {:7}{:7}{:7}{:7}{:7}{:7} {:2.2s}{:2.2s}", serial, name, altLoc, resName, chainID, resSeq, iCode, std::lrintf(u11 * 10000), std::lrintf(u22 * 10000), std::lrintf(u33 * 10000), std::lrintf(u12 * 10000), std::lrintf(u13 * 10000), std::lrintf(u23 * 10000), element, sCharge) << '\n';
|
||||
}
|
||||
|
||||
++serial;
|
||||
@@ -3537,7 +3514,7 @@ std::tuple<int, int> WriteCoordinate(std::ostream &pdbFile, const datablock &db)
|
||||
for (int model_nr : models)
|
||||
{
|
||||
if (models.size() > 1)
|
||||
pdbFile << std::format("MODEL %4d", model_nr) << '\n';
|
||||
pdbFile << cif::format("MODEL {:4}", model_nr) << '\n';
|
||||
|
||||
std::set<std::string> TERminatedChains;
|
||||
auto n = WriteCoordinatesForModel(pdbFile, db, last_resseq_for_chain_map, TERminatedChains, model_nr);
|
||||
@@ -3609,7 +3586,7 @@ std::string get_HEADER_line(const datablock &db, std::string::size_type truncate
|
||||
}
|
||||
}
|
||||
|
||||
return FixStringLength(std::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()), truncate_at);
|
||||
}
|
||||
|
||||
std::string get_COMPND_line(const datablock &db, std::string::size_type truncate_at)
|
||||
@@ -3782,7 +3759,7 @@ void write(std::ostream &os, const datablock &db)
|
||||
numXform = WriteCoordinateTransformation(os, db);
|
||||
std::tie(numCoord, numTer) = WriteCoordinate(os, db);
|
||||
|
||||
os << std::format("MASTER %5d 0%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d", numRemark, numHet, numHelix, numSheet, numTurn, numSite, numXform, numCoord, numTer, numConect, numSeq) << '\n'
|
||||
os << cif::format("MASTER {:5} 0{:5}{:5}{:5}{:5}{:5}{:5}{:5}{:5}{:5}{:5}", numRemark, numHet, numHelix, numSheet, numTurn, numSite, numXform, numCoord, numTer, numConect, numSeq) << '\n'
|
||||
<< "END\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
|
||||
using cif::category;
|
||||
using cif::datablock;
|
||||
@@ -895,7 +896,7 @@ class PDBFileParser
|
||||
if (year < 1950)
|
||||
year += 100;
|
||||
|
||||
s = std::format("{:04}-{:02}-{:02}", year, month, day);
|
||||
s = cif::format("{:04}-{:02}-{:02}", year, month, day);
|
||||
}
|
||||
else if (regex_match(s, m, rx2))
|
||||
{
|
||||
@@ -907,7 +908,7 @@ class PDBFileParser
|
||||
if (year < 1950)
|
||||
year += 100;
|
||||
|
||||
s = std::format("{:04}-{:02}", year, month);
|
||||
s = cif::format("{:04}-{:02}", year, month);
|
||||
}
|
||||
else
|
||||
ec = error::make_error_code(error::pdbErrors::invalidDate);
|
||||
@@ -3141,7 +3142,6 @@ void PDBFileParser::ParseRemark350()
|
||||
std::map<std::string, std::string> values;
|
||||
std::vector<std::string> asymIdList;
|
||||
std::smatch m;
|
||||
cif::row_handle genR;
|
||||
|
||||
std::vector<double> mat, vec;
|
||||
|
||||
@@ -3336,18 +3336,18 @@ void PDBFileParser::ParseRemark350()
|
||||
{ "type", type },
|
||||
// { "name", "" },
|
||||
// { "symmetryOperation", "" },
|
||||
{ "matrix[1][1]", std::format("%12.10f", mat[0]) },
|
||||
{ "matrix[1][2]", std::format("%12.10f", mat[1]) },
|
||||
{ "matrix[1][3]", std::format("%12.10f", mat[2]) },
|
||||
{ "vector[1]", std::format("%12.10f", vec[0]) },
|
||||
{ "matrix[2][1]", std::format("%12.10f", mat[3]) },
|
||||
{ "matrix[2][2]", std::format("%12.10f", mat[4]) },
|
||||
{ "matrix[2][3]", std::format("%12.10f", mat[5]) },
|
||||
{ "vector[2]", std::format("%12.10f", vec[1]) },
|
||||
{ "matrix[3][1]", std::format("%12.10f", mat[6]) },
|
||||
{ "matrix[3][2]", std::format("%12.10f", mat[7]) },
|
||||
{ "matrix[3][3]", std::format("%12.10f", mat[8]) },
|
||||
{ "vector[3]", std::format("%12.10f", vec[2]) }
|
||||
{ "matrix[1][1]", cif::format("{:12.10f}", mat[0]) },
|
||||
{ "matrix[1][2]", cif::format("{:12.10f}", mat[1]) },
|
||||
{ "matrix[1][3]", cif::format("{:12.10f}", mat[2]) },
|
||||
{ "vector[1]", cif::format("{:12.10f}", vec[0]) },
|
||||
{ "matrix[2][1]", cif::format("{:12.10f}", mat[3]) },
|
||||
{ "matrix[2][2]", cif::format("{:12.10f}", mat[4]) },
|
||||
{ "matrix[2][3]", cif::format("{:12.10f}", mat[5]) },
|
||||
{ "vector[2]", cif::format("{:12.10f}", vec[1]) },
|
||||
{ "matrix[3][1]", cif::format("{:12.10f}", mat[6]) },
|
||||
{ "matrix[3][2]", cif::format("{:12.10f}", mat[7]) },
|
||||
{ "matrix[3][3]", cif::format("{:12.10f}", mat[8]) },
|
||||
{ "vector[3]", cif::format("{:12.10f}", vec[2]) }
|
||||
});
|
||||
// clang-format on
|
||||
|
||||
@@ -4313,7 +4313,7 @@ void PDBFileParser::ConstructEntities()
|
||||
}
|
||||
|
||||
// build sugar trees first
|
||||
ConstructSugarTrees(asymNr);
|
||||
// ConstructSugarTrees(asymNr);
|
||||
|
||||
// done with the sugar, resume operation as before
|
||||
|
||||
@@ -5767,6 +5767,9 @@ void PDBFileParser::ParseCoordinate(int modelNr)
|
||||
std::string element = vS(77, 78); // 77 - 78 LString(2) element Element symbol, right-justified.
|
||||
std::string charge = vS(79, 80); // 79 - 80 LString(2) charge Charge on the atom.
|
||||
|
||||
if (element.empty())
|
||||
throw std::runtime_error("Empty element column in PDB file at line " + std::to_string(mRec->mLineNr));
|
||||
|
||||
std::string entityID = mAsymID2EntityID[asymID];
|
||||
|
||||
charge = pdb2cifCharge(charge);
|
||||
@@ -5845,7 +5848,7 @@ void PDBFileParser::ParseCoordinate(int modelNr)
|
||||
|
||||
auto f = [](float f) -> std::string
|
||||
{
|
||||
return std::format("%6.4f", f);
|
||||
return cif::format("{:6.4f}", f);
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
@@ -6437,8 +6440,14 @@ file read(std::istream &is)
|
||||
}
|
||||
|
||||
// Must be a PDB like file, right?
|
||||
if (not result.empty() and result.front().get_validator() == nullptr)
|
||||
result.front().set_validator(&validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
if (not result.empty())
|
||||
{
|
||||
auto &db = result.front();
|
||||
if (db.get_validator() == nullptr)
|
||||
db.set_validator(&validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
if (db.is_valid())
|
||||
db.get_validator()->fill_audit_conform(db["audit_conform"]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
*/
|
||||
|
||||
#include "cif++.hpp"
|
||||
#include "cif++/compound.hpp"
|
||||
#include "cif++/row.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
@@ -143,10 +148,10 @@ void checkEntities(datablock &db)
|
||||
++n;
|
||||
}
|
||||
|
||||
formula_weight -= (n - 1) * 18.015;
|
||||
formula_weight -= (n - 1) * 18.015f;
|
||||
}
|
||||
else if (type == "water")
|
||||
formula_weight = 18.015;
|
||||
formula_weight = 18.015f;
|
||||
else if (type == "branched")
|
||||
{
|
||||
int n = 0;
|
||||
@@ -161,7 +166,7 @@ void checkEntities(datablock &db)
|
||||
++n;
|
||||
}
|
||||
|
||||
formula_weight -= (n - 1) * 18.015;
|
||||
formula_weight -= (n - 1) * 18.015f;
|
||||
}
|
||||
else if (type == "non-polymer")
|
||||
{
|
||||
@@ -185,6 +190,8 @@ void checkEntities(datablock &db)
|
||||
|
||||
void createEntityIDs(datablock &db)
|
||||
{
|
||||
using namespace literals;
|
||||
|
||||
// Suppose the file does not have entity ID's. We have to make up some
|
||||
|
||||
// walk the atoms. For each auth_asym_id we have a new struct_asym.
|
||||
@@ -196,28 +203,44 @@ void createEntityIDs(datablock &db)
|
||||
// that should cover it
|
||||
|
||||
auto &atom_site = db["atom_site"];
|
||||
auto &entity = db["entity"];
|
||||
auto &cf = compound_factory::instance();
|
||||
|
||||
std::vector<std::vector<residue_key_type>> entities;
|
||||
std::vector<std::vector<row_handle>> entities;
|
||||
|
||||
std::string lastAsymID;
|
||||
int lastSeqID = -1;
|
||||
std::vector<residue_key_type> waters;
|
||||
std::vector<row_handle> waters;
|
||||
|
||||
for (residue_key_type k : atom_site.rows<std::optional<std::string>,
|
||||
std::optional<int>,
|
||||
std::optional<std::string>,
|
||||
std::optional<std::string>,
|
||||
std::optional<int>,
|
||||
std::optional<std::string>>(
|
||||
"auth_asym_id", "auth_seq_id", "auth_comp_id",
|
||||
"label_asym_id", "label_seq_id", "label_comp_id"))
|
||||
int nextEntityID;
|
||||
try
|
||||
{
|
||||
if (entity.empty())
|
||||
nextEntityID = 1;
|
||||
else
|
||||
nextEntityID = entity.find_max<int>("id") + 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
nextEntityID = 1;
|
||||
}
|
||||
|
||||
for (auto rh : atom_site.find("label_entity_id"_key == cif::null))
|
||||
{
|
||||
residue_key_type k = rh.get<std::optional<std::string>,
|
||||
std::optional<int>,
|
||||
std::optional<std::string>,
|
||||
std::optional<std::string>,
|
||||
std::optional<int>,
|
||||
std::optional<std::string>>(
|
||||
"auth_asym_id", "auth_seq_id", "auth_comp_id",
|
||||
"label_asym_id", "label_seq_id", "label_comp_id");
|
||||
|
||||
std::string comp_id = get_comp_id(k);
|
||||
|
||||
if (cf.is_water(comp_id))
|
||||
{
|
||||
waters.emplace_back(k);
|
||||
waters.emplace_back(rh);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -226,19 +249,20 @@ void createEntityIDs(datablock &db)
|
||||
|
||||
bool is_monomer = cf.is_monomer(comp_id);
|
||||
|
||||
if (lastAsymID == asym_id and lastSeqID == seq_id and not is_monomer)
|
||||
continue;
|
||||
// if (lastAsymID == asym_id and lastSeqID == seq_id and not is_monomer)
|
||||
// continue;
|
||||
|
||||
if (asym_id != lastAsymID or (not is_monomer and lastSeqID != seq_id))
|
||||
entities.push_back({});
|
||||
|
||||
entities.back().emplace_back(k);
|
||||
entities.back().emplace_back(rh);
|
||||
|
||||
lastAsymID = asym_id;
|
||||
lastSeqID = seq_id;
|
||||
}
|
||||
|
||||
std::map<std::size_t, std::string> entity_ids;
|
||||
std::map<std::string, std::string> newEntitiesForCompound;
|
||||
|
||||
atom_site.add_item("label_entity_id");
|
||||
|
||||
@@ -247,7 +271,39 @@ void createEntityIDs(datablock &db)
|
||||
if (entity_ids.contains(i))
|
||||
continue;
|
||||
|
||||
auto entity_id = std::to_string(i + 1);
|
||||
residue_key_type k = entities[i].front().get<std::optional<std::string>, std::optional<int>, std::optional<std::string>, std::optional<std::string>, std::optional<int>, std::optional<std::string>>(
|
||||
"auth_asym_id", "auth_seq_id", "auth_comp_id",
|
||||
"label_asym_id", "label_seq_id", "label_comp_id");
|
||||
|
||||
std::string comp_id = get_comp_id(k);
|
||||
|
||||
std::string entity_id;
|
||||
if (auto v = db["pdbx_entity_nonpoly"].find_first("comp_id"_key == comp_id); v)
|
||||
entity_id = v.get<std::string>("entity_id");
|
||||
else if (auto i2 = newEntitiesForCompound.find(comp_id); i2 != newEntitiesForCompound.end())
|
||||
entity_id = i2->second;
|
||||
else
|
||||
{
|
||||
entity_id = std::to_string(nextEntityID++);
|
||||
|
||||
if (cf.is_monomer(comp_id))
|
||||
entity.emplace({ //
|
||||
{ "id", entity_id },
|
||||
{ "type", "polymer" } });
|
||||
else if (cf.is_water(comp_id))
|
||||
entity.emplace({ //
|
||||
{ "id", entity_id },
|
||||
{ "type", "water" } });
|
||||
else
|
||||
{
|
||||
entity.emplace({ //
|
||||
{ "id", entity_id },
|
||||
{ "type", "non-polymer" } });
|
||||
|
||||
newEntitiesForCompound[comp_id] = entity_id;
|
||||
}
|
||||
}
|
||||
|
||||
entity_ids[i] = entity_id;
|
||||
|
||||
for (std::size_t j = i + 1; j < entities.size(); ++j)
|
||||
@@ -259,20 +315,17 @@ void createEntityIDs(datablock &db)
|
||||
|
||||
for (std::size_t ix = 0; auto &e : entities)
|
||||
{
|
||||
auto k = e.front();
|
||||
const auto &entity_id = entity_ids[ix++];
|
||||
|
||||
std::string comp_id = get_comp_id(k);
|
||||
|
||||
for (auto &k : e)
|
||||
atom_site.update_value(get_condition(k), "label_entity_id", entity_id);
|
||||
for (auto rh : e)
|
||||
rh["label_entity_id"] = entity_id;
|
||||
}
|
||||
|
||||
if (not waters.empty())
|
||||
{
|
||||
std::string waterEntityID = std::to_string(entities.size() + 1);
|
||||
for (auto &k : waters)
|
||||
atom_site.update_value(get_condition(k), "label_entity_id", waterEntityID);
|
||||
for (auto rh : waters)
|
||||
rh["label_entity_id"] = waterEntityID;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +372,7 @@ void fillLabelAsymID(category &atom_site)
|
||||
{
|
||||
if (not mapAuthAsymIDAndEntityToLabelAsymID.contains(key))
|
||||
{
|
||||
std::string asym_id = cif_id_for_number(mapAuthAsymIDAndEntityToLabelAsymID.size());
|
||||
std::string asym_id = cif_id_for_number(static_cast<int>(mapAuthAsymIDAndEntityToLabelAsymID.size()));
|
||||
mapAuthAsymIDAndEntityToLabelAsymID[key] = asym_id;
|
||||
}
|
||||
}
|
||||
@@ -439,9 +492,38 @@ void checkAtomRecords(datablock &db)
|
||||
if (atom_site.contains(key("label_seq_id") < 0))
|
||||
fixNegativeSeqID(atom_site);
|
||||
|
||||
std::set<int> polymer_entities;
|
||||
for (int id : db["entity"].find<int>("type"_key == "polymer", "id"))
|
||||
polymer_entities.insert(id);
|
||||
std::set<std::string> polymer_entities;
|
||||
if (db["entity"].empty())
|
||||
{
|
||||
// No entity, so we have to guess the types based on the content of atom_site
|
||||
|
||||
std::string last_entity_id;
|
||||
std::optional<int> last_label_seq_id, last_auth_seq_id;
|
||||
|
||||
std::set<std::string> entityIDs;
|
||||
for (auto &[entity_id, label_comp_id, label_seq_id, auth_comp_id, auth_seq_id] :
|
||||
atom_site.rows<std::string, std::string, std::optional<int>, std::string, std::optional<int>>(
|
||||
"label_entity_id", "label_comp_id", "label_seq_id", "auth_comp_id", "auth_seq_id"))
|
||||
{
|
||||
if (cf.is_water(label_comp_id) or cf.is_water(auth_comp_id))
|
||||
continue;
|
||||
|
||||
if (polymer_entities.contains(entity_id))
|
||||
continue;
|
||||
|
||||
if (last_entity_id == entity_id and (last_label_seq_id != label_seq_id or last_auth_seq_id != auth_seq_id))
|
||||
polymer_entities.emplace(entity_id);
|
||||
|
||||
last_entity_id = entity_id;
|
||||
last_label_seq_id = label_seq_id;
|
||||
last_auth_seq_id = auth_seq_id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (std::string id : db["entity"].find<std::string>("type"_key == "polymer", "id"))
|
||||
polymer_entities.insert(id);
|
||||
}
|
||||
|
||||
std::set<std::string> missingCompounds;
|
||||
|
||||
@@ -478,7 +560,7 @@ void checkAtomRecords(datablock &db)
|
||||
if (missingCompounds.contains(comp_id))
|
||||
continue;
|
||||
|
||||
bool is_polymer = polymer_entities.contains(row["label_entity_id"].as<int>());
|
||||
bool is_polymer = polymer_entities.contains(row["label_entity_id"].as<std::string>());
|
||||
auto compound = cf.create(comp_id);
|
||||
|
||||
if (not compound)
|
||||
@@ -532,18 +614,24 @@ void checkAtomRecords(datablock &db)
|
||||
if (is_polymer and row["label_seq_id"].empty() and cf.is_monomer(comp_id))
|
||||
row["label_seq_id"] = std::to_string(seq_id);
|
||||
|
||||
if (row["label_atom_id"].empty())
|
||||
row["label_atom_id"] = row["auth_atom_id"].text();
|
||||
if (row["label_asym_id"].empty())
|
||||
row["label_asym_id"] = row["auth_asym_id"].text();
|
||||
else if (row["auth_asym_id"].empty())
|
||||
row["auth_asym_id"] = row["label_asym_id"].text();
|
||||
|
||||
if (row["label_comp_id"].empty())
|
||||
row["label_comp_id"] = row["auth_comp_id"].text();
|
||||
else if (row["auth_comp_id"].empty())
|
||||
row["auth_comp_id"] = row["label_comp_id"].text();
|
||||
|
||||
if (row["label_atom_id"].empty())
|
||||
row["label_atom_id"] = row["auth_atom_id"].text();
|
||||
else if (row["auth_atom_id"].empty())
|
||||
row["auth_atom_id"] = row["label_atom_id"].text();
|
||||
|
||||
// Rewrite the coordinates and other items that look better in a fixed format
|
||||
// Be careful not to nuke invalidly formatted data here
|
||||
for (auto [item_name, prec] : std::vector<std::tuple<std::string_view, std::string::size_type>>{
|
||||
for (auto [item_name, prec] : std::vector<std::tuple<std::string_view, int>>{
|
||||
{ "cartn_x", 3 },
|
||||
{ "cartn_y", 3 },
|
||||
{ "cartn_z", 3 },
|
||||
@@ -558,11 +646,11 @@ void checkAtomRecords(datablock &db)
|
||||
if (auto [ptr, ec] = cif::from_chars(s.data(), s.data() + s.length(), v); (bool)ec)
|
||||
continue;
|
||||
|
||||
if (s.length() < prec + 1 or s[s.length() - prec - 1] != '.')
|
||||
if (s.length() < prec + 1UL or s[s.length() - prec - 1] != '.')
|
||||
{
|
||||
char b[12];
|
||||
|
||||
if (auto [ptr, ec] = cif::to_chars(b, b + sizeof(b), v, cif::chars_format::fixed, prec); (bool)ec)
|
||||
if (auto [ptr, ec] = std::to_chars(b, b + sizeof(b), v, std::chars_format::fixed, prec); ec == std::errc{})
|
||||
row.assign(item_name, { b, static_cast<std::string::size_type>(ptr - b) }, false, false);
|
||||
}
|
||||
}
|
||||
@@ -604,19 +692,24 @@ void checkAtomAnisotropRecords(datablock &db)
|
||||
|
||||
std::vector<row_handle> to_be_deleted;
|
||||
|
||||
std::map<int, row_handle> atoms;
|
||||
for (auto rh : atom_site)
|
||||
atoms[rh.get<int>("id")] = rh;
|
||||
|
||||
bool warnReplaceTypeSymbol = true;
|
||||
for (auto row : atom_site_anisotrop)
|
||||
{
|
||||
auto parents = atom_site_anisotrop.get_parents(row, atom_site);
|
||||
if (parents.size() != 1)
|
||||
auto ai = atoms.find(row.get<int>("id"));
|
||||
|
||||
if (ai == atoms.end())
|
||||
{
|
||||
to_be_deleted.emplace_back(row);
|
||||
continue;
|
||||
}
|
||||
|
||||
// this happens sometimes (Phenix):
|
||||
auto parent = ai->second;
|
||||
|
||||
auto parent = parents.front();
|
||||
// this happens sometimes (Phenix):
|
||||
|
||||
if (row["type_symbol"].empty())
|
||||
row["type_symbol"] = parent["type_symbol"].text();
|
||||
@@ -637,8 +730,6 @@ void checkAtomAnisotropRecords(datablock &db)
|
||||
row["pdbx_label_atom_id"] = parent["label_atom_id"].text();
|
||||
if (row["pdbx_label_comp_id"].empty() and not parent["label_comp_id"].empty())
|
||||
row["pdbx_label_comp_id"] = parent["label_comp_id"].text();
|
||||
// if (row["pdbx_PDB_model_num"].empty() and not parent["pdbx_PDB_model_num"].empty())
|
||||
// row["pdbx_PDB_model_num"] = parent["pdbx_PDB_model_num"].text();
|
||||
}
|
||||
|
||||
if (not to_be_deleted.empty())
|
||||
@@ -651,23 +742,53 @@ void checkAtomAnisotropRecords(datablock &db)
|
||||
}
|
||||
}
|
||||
|
||||
void createStructAsym(datablock &db)
|
||||
void checkStructAsym(datablock &db)
|
||||
{
|
||||
auto &atom_site = db["atom_site"];
|
||||
auto &struct_asym = db["struct_asym"];
|
||||
|
||||
for (const auto &[label_asym_id, entity_id] : atom_site.rows<std::string, std::string>("label_asym_id", "label_entity_id"))
|
||||
if (struct_asym.empty())
|
||||
{
|
||||
if (label_asym_id.empty())
|
||||
throw std::runtime_error("File contains atom_site records without a label_asym_id");
|
||||
if (struct_asym.count(key("id") == label_asym_id) == 0)
|
||||
for (const auto &[label_asym_id, entity_id] : atom_site.rows<std::string, std::string>("label_asym_id", "label_entity_id"))
|
||||
{
|
||||
struct_asym.emplace({
|
||||
// clang-format off
|
||||
{ "id", label_asym_id },
|
||||
{ "entity_id", entity_id }
|
||||
//clang-format on
|
||||
});
|
||||
if (label_asym_id.empty())
|
||||
throw std::runtime_error("File contains atom_site records without a label_asym_id");
|
||||
if (struct_asym.count(key("id") == label_asym_id) == 0)
|
||||
{
|
||||
struct_asym.emplace({
|
||||
// clang-format off
|
||||
{ "id", label_asym_id },
|
||||
{ "entity_id", entity_id }
|
||||
//clang-format on
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto &[label_asym_id, entity_id] :
|
||||
atom_site.rows<std::string, std::string>("label_asym_id", "label_entity_id"))
|
||||
{
|
||||
if (label_asym_id.empty())
|
||||
throw std::runtime_error("File contains atom_site records without a label_asym_id");
|
||||
|
||||
auto sa = struct_asym.find_first(key("id") == label_asym_id);
|
||||
if (sa)
|
||||
{
|
||||
if (sa["entity_id"].empty())
|
||||
sa.assign("entity_id", entity_id, false, true);
|
||||
else if (sa.get<std::string>("entity_id") != entity_id)
|
||||
throw std::runtime_error("Inconsistent entity ID's in struct_asym");
|
||||
}
|
||||
else
|
||||
{
|
||||
struct_asym.emplace({
|
||||
// clang-format off
|
||||
{ "id", label_asym_id },
|
||||
{ "entity_id", entity_id }
|
||||
//clang-format on
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -723,7 +844,7 @@ void createEntity(datablock &db)
|
||||
|
||||
std::string type, desc;
|
||||
float weight = 0;
|
||||
int count = 0;
|
||||
size_t count = 0;
|
||||
|
||||
auto first_comp_id = std::get<0>(content.front());
|
||||
|
||||
@@ -738,8 +859,11 @@ void createEntity(datablock &db)
|
||||
auto c = cf.create(first_comp_id);
|
||||
|
||||
type = "non-polymer";
|
||||
desc = c->name();
|
||||
weight = c->formula_weight();
|
||||
if (c)
|
||||
{
|
||||
desc = c->name();
|
||||
weight = c->formula_weight();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -810,28 +934,28 @@ void createEntityPoly(datablock &db)
|
||||
if (type != "other")
|
||||
{
|
||||
std::string c_type;
|
||||
if (cf.is_base(comp_id))
|
||||
if (auto i = compound_factory::kBaseMap.find(comp_id); i != compound_factory::kBaseMap.end())
|
||||
{
|
||||
c_type = "polydeoxyribonucleotide";
|
||||
letter_can = compound_factory::kBaseMap.at(comp_id);
|
||||
|
||||
letter_can = i->second;
|
||||
|
||||
if (comp_id.length() == 1)
|
||||
letter = letter_can;
|
||||
else
|
||||
letter = '(' + letter_can + ')';
|
||||
letter = '(' + comp_id + ')';
|
||||
}
|
||||
else if (cf.is_peptide(comp_id))
|
||||
else if (auto i2 = compound_factory::kAAMap.find(comp_id); i2 != compound_factory::kAAMap.end())
|
||||
{
|
||||
c_type = "polypeptide(L)";
|
||||
letter = letter_can = compound_factory::kAAMap.at(comp_id);
|
||||
|
||||
letter = letter_can = i2->second;
|
||||
}
|
||||
else if (iequals(c->type(), "D-PEPTIDE LINKING"))
|
||||
{
|
||||
c_type = "polypeptide(D)";
|
||||
|
||||
letter_can = c->one_letter_code();
|
||||
if (letter_can == 0)
|
||||
letter_can = 'X';
|
||||
|
||||
letter = '(' + comp_id + ')';
|
||||
|
||||
non_std_linkage = true;
|
||||
@@ -842,9 +966,6 @@ void createEntityPoly(datablock &db)
|
||||
c_type = "polypeptide(L)";
|
||||
|
||||
letter_can = c->one_letter_code();
|
||||
if (letter_can == 0)
|
||||
letter_can = 'X';
|
||||
|
||||
letter = '(' + comp_id + ')';
|
||||
|
||||
non_std_monomer = true;
|
||||
@@ -854,9 +975,6 @@ void createEntityPoly(datablock &db)
|
||||
// c_type = "other";
|
||||
|
||||
letter_can = c->one_letter_code();
|
||||
if (letter_can == 0)
|
||||
letter_can = 'X';
|
||||
|
||||
letter = '(' + comp_id + ')';
|
||||
|
||||
non_std_monomer = true;
|
||||
@@ -869,7 +987,7 @@ void createEntityPoly(datablock &db)
|
||||
}
|
||||
|
||||
seq[auth_asym_id] += letter;
|
||||
seq_can[auth_asym_id] += letter_can;
|
||||
seq_can[auth_asym_id] += letter_can ? letter_can : 'X';
|
||||
|
||||
if (find(pdb_strand_ids.begin(), pdb_strand_ids.end(), auth_asym_id) == pdb_strand_ids.end())
|
||||
pdb_strand_ids.emplace_back(auth_asym_id);
|
||||
@@ -1389,7 +1507,7 @@ bool reconstruct_pdbx(file &file, const validator &validator)
|
||||
checkChemCompRecords(db);
|
||||
|
||||
// If the data is really horrible, it might not contain entities
|
||||
if (not db["atom_site"].find_first(key("label_entity_id") != null))
|
||||
if (db["atom_site"].find_first(key("label_entity_id") == null))
|
||||
createEntityIDs(db);
|
||||
|
||||
// Now see if atom records make sense at all
|
||||
@@ -1433,7 +1551,7 @@ bool reconstruct_pdbx(file &file, const validator &validator)
|
||||
iv->m_type != nullptr and
|
||||
iv->m_type->m_primitive_type == cif::DDL_PrimitiveType::Numb;
|
||||
|
||||
for (std::size_t ix = 0; auto row : cat)
|
||||
for (int ix = 0; auto row : cat)
|
||||
{
|
||||
if (number)
|
||||
row.assign(key, std::to_string(++ix), false, false);
|
||||
@@ -1533,9 +1651,8 @@ bool reconstruct_pdbx(file &file, const validator &validator)
|
||||
checkAtomAnisotropRecords(db);
|
||||
|
||||
// Now create any missing categories
|
||||
// Next make sure we have struct_asym records
|
||||
if (auto cat = db.get("struct_asym"); cat == nullptr or cat->empty())
|
||||
createStructAsym(db);
|
||||
// Next make sure we have good struct_asym records
|
||||
checkStructAsym(db);
|
||||
|
||||
if (auto cat = db.get("entity"); cat == nullptr or cat->empty())
|
||||
createEntity(db);
|
||||
@@ -1644,9 +1761,8 @@ void fixup_pdbx(file &file, const validator &validator)
|
||||
db.set_validator(&validator);
|
||||
|
||||
// Now create any missing categories
|
||||
// Next make sure we have struct_asym records
|
||||
if (auto cat = db.get("struct_asym"); cat == nullptr or cat->empty())
|
||||
createStructAsym(db);
|
||||
// Next make sure we have good struct_asym records
|
||||
checkStructAsym(db);
|
||||
|
||||
if (auto cat = db.get("entity"); cat == nullptr or cat->empty())
|
||||
createEntity(db);
|
||||
|
||||
@@ -69,7 +69,7 @@ bool is_valid_pdbx_file(const file &file, const validator &v)
|
||||
{
|
||||
std::error_code ec;
|
||||
bool result = is_valid_pdbx_file(file, v, ec);
|
||||
return result and not (bool)ec;
|
||||
return result and not(bool) ec;
|
||||
}
|
||||
|
||||
bool is_valid_pdbx_file(const file &file, std::error_code &ec)
|
||||
@@ -82,7 +82,7 @@ bool is_valid_pdbx_file(const file &file, std::error_code &ec)
|
||||
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);
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -167,7 +167,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,28 +202,37 @@ 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");
|
||||
}
|
||||
|
||||
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;
|
||||
// This code proved to take too much time ...
|
||||
|
||||
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");
|
||||
}
|
||||
// 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"))
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -260,8 +269,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();
|
||||
@@ -285,7 +294,9 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
}
|
||||
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);
|
||||
@@ -298,7 +309,9 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
}
|
||||
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);
|
||||
@@ -315,11 +328,10 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
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
|
||||
|
||||
|
||||
266
src/point.cpp
266
src/point.cpp
@@ -25,17 +25,19 @@
|
||||
*/
|
||||
|
||||
#include "cif++/point.hpp"
|
||||
#include "cif++/matrix.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <random>
|
||||
#include "cif++/matrix.hpp" // for matrix_subtraction, matrix_cofactors
|
||||
|
||||
#include <initializer_list>
|
||||
#include <random> // for uniform_real_distribution, normal_distri...
|
||||
#include <stdexcept>
|
||||
|
||||
namespace cif
|
||||
{
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
quaternion_type<T> normalize(quaternion_type<T> q)
|
||||
{
|
||||
std::valarray<double> t(4);
|
||||
@@ -126,10 +128,9 @@ quaternion construct_for_dihedral_angle(point p1, point p2, point p3, point p4,
|
||||
p4 -= p3;
|
||||
p3 -= p3;
|
||||
|
||||
quaternion q;
|
||||
auto axis = -p2;
|
||||
|
||||
float dh = dihedral_angle(p1, p2, p3, p4);
|
||||
|
||||
return construct_from_angle_axis(angle - dh, axis);
|
||||
}
|
||||
|
||||
@@ -293,9 +294,9 @@ quaternion align_points(const std::vector<point> &pa, const std::vector<point> &
|
||||
}
|
||||
|
||||
quaternion q(
|
||||
static_cast<float>(cf(maxR, 0)),
|
||||
static_cast<float>(cf(maxR, 1)),
|
||||
static_cast<float>(cf(maxR, 2)),
|
||||
static_cast<float>(cf(maxR, 0)),
|
||||
static_cast<float>(cf(maxR, 1)),
|
||||
static_cast<float>(cf(maxR, 2)),
|
||||
static_cast<float>(cf(maxR, 3)));
|
||||
q = normalize(q);
|
||||
|
||||
@@ -327,4 +328,251 @@ point nudge(point p, float offset)
|
||||
return p + r;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
std::tuple<point, float> smallest_sphere_around_2_points(std::array<cif::point, 2> pts)
|
||||
{
|
||||
return { (pts[0] + pts[1]) / 2, distance(pts[0], pts[1]) / 2 };
|
||||
}
|
||||
|
||||
std::tuple<point, float> smallest_sphere_around_3_points(std::array<cif::point, 3> pts)
|
||||
{
|
||||
// Find two bisectors
|
||||
auto vz = cross_product(pts[1] - pts[0], pts[2] - pts[0]);
|
||||
|
||||
auto bs1 = cross_product(vz, pts[1] - pts[0]);
|
||||
bs1.normalize();
|
||||
|
||||
auto v1 = (pts[1] - pts[0]);
|
||||
v1.normalize();
|
||||
|
||||
auto s1 = pts[0] + (distance(pts[1], pts[0]) / 2) * v1;
|
||||
|
||||
auto bs2 = cross_product(vz, pts[2] - pts[0]);
|
||||
bs2.normalize();
|
||||
|
||||
auto v2 = (pts[2] - pts[0]);
|
||||
v2.normalize();
|
||||
|
||||
auto s2 = pts[0] + (distance(pts[2], pts[0]) / 2) * v2;
|
||||
|
||||
auto c = line_line_intersection(s1, s1 + bs1, s2, s2 + bs2);
|
||||
if (c)
|
||||
return { *c, distance(*c, pts[0]) };
|
||||
|
||||
// Colinear points I guess, try something else
|
||||
auto l1 = distance_squared(pts[0], pts[1]);
|
||||
auto l2 = distance_squared(pts[0], pts[2]);
|
||||
auto l3 = distance_squared(pts[1], pts[2]);
|
||||
|
||||
if (l1 > l2 and l1 > l3)
|
||||
return smallest_sphere_around_2_points({ pts[0], pts[1] });
|
||||
else if (l2 > l1 and l2 > l3)
|
||||
return smallest_sphere_around_2_points({ pts[0], pts[2] });
|
||||
else
|
||||
return smallest_sphere_around_2_points({ pts[1], pts[2] });
|
||||
}
|
||||
|
||||
std::tuple<point, float> smallest_sphere_around_4_points(std::array<cif::point, 4> pts)
|
||||
{
|
||||
auto t0 = -norm_squared(pts[0]);
|
||||
auto t1 = -norm_squared(pts[1]);
|
||||
auto t2 = -norm_squared(pts[2]);
|
||||
auto t3 = -norm_squared(pts[3]);
|
||||
|
||||
// clang-format off
|
||||
matrix4x4<float> Tm({
|
||||
pts[0].m_x, pts[0].m_y, pts[0].m_z, 1,
|
||||
pts[1].m_x, pts[1].m_y, pts[1].m_z, 1,
|
||||
pts[2].m_x, pts[2].m_y, pts[2].m_z, 1,
|
||||
pts[3].m_x, pts[3].m_y, pts[3].m_z, 1
|
||||
});
|
||||
auto T = determinant(Tm);
|
||||
|
||||
if (T != 0)
|
||||
{
|
||||
matrix4x4<float> Dm({
|
||||
t0, pts[0].m_y, pts[0].m_z, 1,
|
||||
t1, pts[1].m_y, pts[1].m_z, 1,
|
||||
t2, pts[2].m_y, pts[2].m_z, 1,
|
||||
t3, pts[3].m_y, pts[3].m_z, 1
|
||||
});
|
||||
auto D = determinant(Dm) / T;
|
||||
|
||||
matrix4x4<float> Em({
|
||||
pts[0].m_x, t0, pts[0].m_z, 1,
|
||||
pts[1].m_x, t1, pts[1].m_z, 1,
|
||||
pts[2].m_x, t2, pts[2].m_z, 1,
|
||||
pts[3].m_x, t3, pts[3].m_z, 1
|
||||
});
|
||||
auto E = determinant(Em) / T;
|
||||
|
||||
matrix4x4<float> Fm({
|
||||
pts[0].m_x, pts[0].m_y, t0, 1,
|
||||
pts[1].m_x, pts[1].m_y, t1, 1,
|
||||
pts[2].m_x, pts[2].m_y, t2, 1,
|
||||
pts[3].m_x, pts[3].m_y, t3, 1
|
||||
});
|
||||
|
||||
auto F = determinant(Fm) / T;
|
||||
|
||||
matrix4x4<float> Gm({
|
||||
pts[0].m_x, pts[0].m_y, pts[0].m_z, t0,
|
||||
pts[1].m_x, pts[1].m_y, pts[1].m_z, t1,
|
||||
pts[2].m_x, pts[2].m_y, pts[2].m_z, t2,
|
||||
pts[3].m_x, pts[3].m_y, pts[3].m_z, t3
|
||||
});
|
||||
auto G = determinant(Gm) / T;
|
||||
|
||||
point center{ -D / 2, -E / 2, -F / 2 };
|
||||
float radius = std::sqrt(D * D + E * E + F * F - 4 * G) / 2;
|
||||
|
||||
// clang-format on
|
||||
|
||||
return { center, radius };
|
||||
}
|
||||
|
||||
// Perhaps some colinear points, try something else:
|
||||
|
||||
for (auto ix : std::initializer_list<std::array<size_t, 4>>{
|
||||
{ 1, 2, 3, 0 },
|
||||
{ 0, 2, 3, 1 },
|
||||
{ 0, 1, 3, 2 },
|
||||
{ 0, 1, 2, 3 },
|
||||
})
|
||||
{
|
||||
auto [center, radius] =
|
||||
smallest_sphere_around_3_points({ pts[ix[0]], pts[ix[1]], pts[ix[2]] });
|
||||
|
||||
if (distance(pts[ix[3]], center) <= radius)
|
||||
return { center, radius };
|
||||
}
|
||||
|
||||
assert(false);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::tuple<point, float> smallest_sphere_around_all_points(std::vector<point> P, std::vector<point> R)
|
||||
{
|
||||
if (P.empty() or R.size() == 4)
|
||||
{
|
||||
switch (R.size())
|
||||
{
|
||||
case 1:
|
||||
return { R[0], 0 };
|
||||
|
||||
case 2:
|
||||
return smallest_sphere_around_2_points({ R[0], R[1] });
|
||||
|
||||
case 3:
|
||||
return smallest_sphere_around_3_points({ R[0], R[1], R[2] });
|
||||
|
||||
case 4:
|
||||
return smallest_sphere_around_4_points({ R[0], R[1], R[2], R[3] });
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
auto p = P.back();
|
||||
P.pop_back();
|
||||
|
||||
auto [c, r] = smallest_sphere_around_all_points(P, R);
|
||||
assert(not std::isnan(r));
|
||||
if (distance(c, p) <= r)
|
||||
return { c, r };
|
||||
|
||||
R.emplace_back(p);
|
||||
return smallest_sphere_around_all_points(P, R);
|
||||
}
|
||||
|
||||
bool point_in_circle(point p, std::vector<point> c)
|
||||
{
|
||||
switch (c.size())
|
||||
{
|
||||
case 0:
|
||||
return false;
|
||||
|
||||
case 1:
|
||||
return p == c.front();
|
||||
|
||||
case 2:
|
||||
{
|
||||
auto [center, radius] = smallest_sphere_around_2_points({ c[0], c[1] });
|
||||
return cif::distance_squared(p, center) <= radius * radius;
|
||||
}
|
||||
|
||||
case 3:
|
||||
{
|
||||
auto [center, radius] = smallest_sphere_around_3_points({ c[0], c[1], c[2] });
|
||||
return cif::distance_squared(p, center) <= radius * radius;
|
||||
}
|
||||
|
||||
case 4:
|
||||
{
|
||||
auto [center, radius] = smallest_sphere_around_4_points({ c[0], c[1], c[2], c[3] });
|
||||
return cif::distance_squared(p, center) <= radius * radius;
|
||||
}
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
throw std::runtime_error("Error finding smallest sphere");
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<point, float> smallest_sphere_around_points(std::vector<point> pts)
|
||||
{
|
||||
std::random_device rd;
|
||||
std::mt19937 g(rd());
|
||||
|
||||
std::shuffle(pts.begin(), pts.end(), g);
|
||||
|
||||
std::vector<size_t> cix;
|
||||
|
||||
auto cirle_points = [&]()
|
||||
{
|
||||
std::vector<point> result;
|
||||
for (auto ix : cix)
|
||||
result.emplace_back(pts[ix]);
|
||||
return result;
|
||||
};
|
||||
|
||||
size_t i = 0;
|
||||
while (i < pts.size())
|
||||
{
|
||||
if (std::find(cix.begin(), cix.end(), i) != cix.end() or
|
||||
point_in_circle(pts[i], cirle_points()))
|
||||
{
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
cix.erase(std::remove_if(cix.begin(), cix.end(), [i](size_t j)
|
||||
{ return j < i; }),
|
||||
cix.end());
|
||||
cix.push_back(i);
|
||||
if (cix.size() < 4)
|
||||
i = 0;
|
||||
else
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
switch (cix.size())
|
||||
{
|
||||
case 1:
|
||||
return { pts[cix[0]], 0 };
|
||||
case 2:
|
||||
return smallest_sphere_around_2_points({ pts[cix[0]], pts[cix[1]] });
|
||||
case 3:
|
||||
return smallest_sphere_around_3_points({ pts[cix[0]], pts[cix[1]], pts[cix[2]] });
|
||||
case 4:
|
||||
return smallest_sphere_around_4_points({ pts[cix[0]], pts[cix[1]], pts[cix[2]], pts[cix[3]] });
|
||||
default:
|
||||
assert(false);
|
||||
throw std::runtime_error("Error finding smallest sphere");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cif
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
|
||||
#include "symop_table_data.hpp"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning (disable : 5054) // warning C5054: operator '&': deprecated between enumerations of different types
|
||||
#pragma warning (disable : 4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
#include <Eigen/Eigen>
|
||||
|
||||
namespace cif
|
||||
@@ -90,10 +95,10 @@ float cell::get_volume() const
|
||||
auto cos_beta = std::cos(beta);
|
||||
auto cos_gamma = std::cos(gamma);
|
||||
|
||||
auto vol = m_a * m_b * m_c;
|
||||
double vol = m_a * m_b * m_c;
|
||||
vol *= std::sqrt(1.0f - cos_alpha * cos_alpha - cos_beta * cos_beta - cos_gamma * cos_gamma + 2.0f * cos_alpha * cos_beta * cos_gamma);
|
||||
|
||||
return vol;
|
||||
return static_cast<float>(vol);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
33
src/text.cpp
33
src/text.cpp
@@ -28,6 +28,11 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <charconv>
|
||||
|
||||
#if __has_include("fast_float/fast_float.h")
|
||||
#include "fast_float/fast_float.h"
|
||||
#endif
|
||||
|
||||
namespace cif
|
||||
{
|
||||
@@ -512,4 +517,32 @@ 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 <typename T>
|
||||
std::from_chars_result ff_charconv<T, typename std::enable_if_t<std::is_floating_point_v<T>>>::from_chars(const char *a, const char *b, T &v)
|
||||
{
|
||||
auto r = fast_float::from_chars(a, b, v);
|
||||
return { r.ptr, r.ec };
|
||||
}
|
||||
|
||||
template struct ff_charconv<float>;
|
||||
template struct ff_charconv<double>;
|
||||
// template struct ff_charconv<long double>;
|
||||
|
||||
#ifdef __STDCPP_FLOAT64_T__
|
||||
template struct ff_charconv<std::float64_t>;
|
||||
#endif
|
||||
#ifdef __STDCPP_FLOAT32_T__
|
||||
template struct ff_charconv<std::float32_t>;
|
||||
#endif
|
||||
#ifdef __STDCPP_FLOAT16_T__
|
||||
template struct ff_charconv<std::float16_t>;
|
||||
#endif
|
||||
#ifdef __STDCPP_BFLOAT16_T__
|
||||
template struct ff_charconv<std::bfloat16_t>;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace cif
|
||||
@@ -34,14 +34,20 @@
|
||||
#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;
|
||||
|
||||
@@ -65,27 +71,50 @@ std::string get_version_nr()
|
||||
|
||||
#if defined(_WIN32) or defined(__MINGW32__)
|
||||
}
|
||||
#include <windows.h>
|
||||
#include <libloaderapi.h>
|
||||
#include <wincon.h>
|
||||
|
||||
#include <codecvt>
|
||||
// clang-format off
|
||||
# include <windows.h>
|
||||
# include <libloaderapi.h>
|
||||
# include <wincon.h>
|
||||
// clang-format on
|
||||
|
||||
namespace cif
|
||||
{
|
||||
|
||||
uint32_t get_terminal_width()
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
::GetConsoleScreenBufferInfo(::GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
|
||||
return csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include <limits.h>
|
||||
# include <limits.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <termios.h>
|
||||
|
||||
uint32_t get_terminal_width()
|
||||
{
|
||||
@@ -100,59 +129,220 @@ 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(int64_t inMax, const std::string &inAction)
|
||||
: m_max_value(inMax)
|
||||
progress_bar_impl(uint64_t max_value, const std::string &message)
|
||||
: m_max_value(max_value)
|
||||
, m_consumed(0)
|
||||
, m_action(inAction)
|
||||
, m_message(inAction)
|
||||
, m_thread(std::bind(&progress_bar_impl::run, this))
|
||||
, m_action(message)
|
||||
, m_message(message)
|
||||
{
|
||||
}
|
||||
|
||||
progress_bar_impl(const progress_bar_impl&) = delete;
|
||||
progress_bar_impl &operator=(const progress_bar_impl &) = delete;
|
||||
virtual ~progress_bar_impl() {}
|
||||
|
||||
~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();
|
||||
virtual void consumed(uint64_t n);
|
||||
virtual void progress(uint64_t p);
|
||||
virtual void message(const std::string &msg);
|
||||
virtual void print_done();
|
||||
|
||||
using time_point = std::chrono::time_point<std::chrono::system_clock>;
|
||||
|
||||
int64_t m_max_value;
|
||||
std::atomic<int64_t> m_consumed;
|
||||
int64_t m_last_consumed = 0;
|
||||
int m_spinner_index = 0;
|
||||
uint64_t m_max_value;
|
||||
std::atomic<uint64_t> m_consumed;
|
||||
std::string m_action, m_message;
|
||||
std::mutex m_mutex;
|
||||
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;
|
||||
};
|
||||
|
||||
progress_bar_impl::~progress_bar_impl()
|
||||
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;
|
||||
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()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
void progress_bar_impl::run()
|
||||
#if __cpp_lib_jthread >= 201911L
|
||||
void fancy_progress_bar_impl::run(std::stop_token stoken)
|
||||
#else
|
||||
void fancy_progress_bar_impl::run()
|
||||
#endif
|
||||
{
|
||||
using namespace std::literals;
|
||||
|
||||
@@ -160,25 +350,44 @@ void progress_bar_impl::run()
|
||||
|
||||
try
|
||||
{
|
||||
while (not m_stop)
|
||||
for (;;)
|
||||
{
|
||||
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 (now - m_start < 2s or now - m_last < 100ms)
|
||||
{
|
||||
std::this_thread::sleep_for(10ms);
|
||||
if (m_consumed == m_last_consumed or now - m_start < 1s)
|
||||
continue;
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_mutex);
|
||||
m_last_consumed = m_consumed;
|
||||
|
||||
if (not printedAny and isatty(STDOUT_FILENO))
|
||||
std::cout << "\x1b[?25l";
|
||||
// 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");
|
||||
|
||||
print_progress();
|
||||
|
||||
printedAny = true;
|
||||
m_last = std::chrono::system_clock::now();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@@ -187,161 +396,94 @@ void progress_bar_impl::run()
|
||||
|
||||
if (printedAny)
|
||||
{
|
||||
write_to_console("\r\x1b[?25h");
|
||||
print_done();
|
||||
if (isatty(STDOUT_FILENO))
|
||||
std::cout << "\x1b[?25h";
|
||||
}
|
||||
}
|
||||
|
||||
void progress_bar_impl::consumed(int64_t n)
|
||||
void fancy_progress_bar_impl::consumed(uint64_t n)
|
||||
{
|
||||
m_consumed += n;
|
||||
progress_bar_impl::consumed(n);
|
||||
// m_cv.notify_one();
|
||||
}
|
||||
|
||||
void progress_bar_impl::progress(int64_t p)
|
||||
void fancy_progress_bar_impl::progress(uint64_t p)
|
||||
{
|
||||
m_consumed = p;
|
||||
progress_bar_impl::progress(p);
|
||||
// m_cv.notify_one();
|
||||
}
|
||||
|
||||
void progress_bar_impl::message(const std::string &msg)
|
||||
void fancy_progress_bar_impl::message(const std::string &msg)
|
||||
{
|
||||
std::unique_lock lock(m_mutex);
|
||||
m_message = msg;
|
||||
progress_bar_impl::message(msg);
|
||||
// m_cv.notify_one();
|
||||
}
|
||||
|
||||
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 progress_bar_impl::print_progress()
|
||||
void fancy_progress_bar_impl::print_progress()
|
||||
{
|
||||
const char *kBlocks[] = {
|
||||
// "▯", // 0
|
||||
// "▮", // 1
|
||||
"=",
|
||||
"-"
|
||||
};
|
||||
const uint32_t pct_width = 5;
|
||||
uint32_t msg_width = m_width - m_bar_width - pct_width - 1;
|
||||
|
||||
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
|
||||
if (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;
|
||||
m_bar_width += kMinMsgWidth - msg_width;
|
||||
msg_width = kMinMsgWidth;
|
||||
}
|
||||
|
||||
if (msg_width < kMinMsgWidth)
|
||||
{
|
||||
bar_width += kMinMsgWidth - msg_width;
|
||||
msg_width = kMinMsgWidth;
|
||||
}
|
||||
std::string bar;
|
||||
bar.reserve(m_bar_width * 4);
|
||||
|
||||
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(), ' ');
|
||||
}
|
||||
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];
|
||||
else
|
||||
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();
|
||||
bar += kBlocks[1 + m_steps % kBlockCount];
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const std::chrono::duration<double> &t)
|
||||
// make the bar more colorfull
|
||||
struct color_type
|
||||
{
|
||||
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;
|
||||
}
|
||||
uint8_t r, g, b;
|
||||
} fg{ 0, 3, 5 }, bg{ 0, 1, 2 };
|
||||
|
||||
if (s > 60 * 60)
|
||||
{
|
||||
auto hours = s / (60 * 60);
|
||||
os << hours << "h ";
|
||||
s %= 60 * 60;
|
||||
}
|
||||
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");
|
||||
|
||||
if (s > 60)
|
||||
{
|
||||
auto minutes = s / 60;
|
||||
os << minutes << "m ";
|
||||
s %= 60;
|
||||
}
|
||||
bar = esc_1 + bar + esc_2;
|
||||
|
||||
double ss = s + 1e-6 * (t.count() - s);
|
||||
std::string msg = m_message.length() <= msg_width
|
||||
? m_message
|
||||
: m_message.substr(0, msg_width - 3) + "...";
|
||||
|
||||
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';
|
||||
write_to_console(std::format("{:{}} {} {:3d}%\r", msg, msg_width, bar,
|
||||
static_cast<int>(std::ceil(m_progress * 100))));
|
||||
}
|
||||
|
||||
progress_bar::progress_bar(int64_t inMax, const std::string &inAction)
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
progress_bar::progress_bar(int64_t max_value, const std::string &message)
|
||||
: m_impl(nullptr)
|
||||
{
|
||||
if (isatty(STDOUT_FILENO) and VERBOSE >= 0)
|
||||
m_impl = new progress_bar_impl(inMax, inAction);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
progress_bar::~progress_bar()
|
||||
{
|
||||
delete m_impl;
|
||||
flush();
|
||||
}
|
||||
|
||||
void progress_bar::consumed(int64_t inConsumed)
|
||||
@@ -350,16 +492,25 @@ void progress_bar::consumed(int64_t inConsumed)
|
||||
m_impl->consumed(inConsumed);
|
||||
}
|
||||
|
||||
void progress_bar::progress(int64_t inProgress)
|
||||
void progress_bar::progress(int64_t value)
|
||||
{
|
||||
if (m_impl != nullptr)
|
||||
m_impl->progress(inProgress);
|
||||
m_impl->progress(value);
|
||||
}
|
||||
|
||||
void progress_bar::message(const std::string &inMessage)
|
||||
void progress_bar::message(const std::string &message)
|
||||
{
|
||||
if (m_impl != nullptr)
|
||||
m_impl->message(inMessage);
|
||||
m_impl->message(message);
|
||||
}
|
||||
|
||||
void progress_bar::flush()
|
||||
{
|
||||
if (m_impl)
|
||||
{
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cif
|
||||
@@ -387,13 +538,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] = {};
|
||||
@@ -403,11 +554,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[];
|
||||
|
||||
@@ -62,14 +62,6 @@ validation_exception::validation_exception(std::error_code ec, std::string_view
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// struct regex_impl : public regex
|
||||
// {
|
||||
// regex_impl(std::string_view rx)
|
||||
// : regex(rx.begin(), rx.end(), regex::extended | regex::optimize)
|
||||
// {
|
||||
// }
|
||||
// };
|
||||
|
||||
struct regex_impl
|
||||
{
|
||||
regex_impl(std::string_view rx);
|
||||
@@ -81,7 +73,6 @@ struct regex_impl
|
||||
bool match(std::string_view v) const;
|
||||
|
||||
private:
|
||||
|
||||
pcre2_code *m_rx = nullptr;
|
||||
pcre2_match_data *m_data = nullptr;
|
||||
};
|
||||
@@ -190,8 +181,8 @@ int type_validator::compare(std::string_view a, std::string_view b) const
|
||||
|
||||
std::from_chars_result ra, rb;
|
||||
|
||||
ra = selected_charconv<double>::from_chars(a.data(), a.data() + a.length(), da);
|
||||
rb = selected_charconv<double>::from_chars(b.data(), b.data() + b.length(), db);
|
||||
ra = from_chars(a.data(), a.data() + a.length(), da);
|
||||
rb = from_chars(b.data(), b.data() + b.length(), db);
|
||||
|
||||
if (not(bool) ra.ec and not(bool) rb.ec)
|
||||
{
|
||||
@@ -570,8 +561,10 @@ const validator &validator_factory::get(const category &audit_conform)
|
||||
// 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");
|
||||
return m_validators.emplace_back(construct_validator(name, version));
|
||||
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
|
||||
@@ -579,6 +572,9 @@ const validator &validator_factory::get(const category &audit_conform)
|
||||
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
|
||||
@@ -613,7 +609,7 @@ validator validator_factory::construct_validator(std::string_view name, std::opt
|
||||
not v.matches_audit_conform(category{ "audit_conform", //
|
||||
{ { "dict_name", name }, { "dict_version", version } } }))
|
||||
{
|
||||
std::clog << "Invalid dictionary?\n";
|
||||
std::clog << "Loaded dictionary does not match name=" << name << " and version=" << version.value_or("''") << "\n";
|
||||
}
|
||||
|
||||
return v;
|
||||
|
||||
@@ -1,19 +1,39 @@
|
||||
# We're using the older version 2 of Catch2
|
||||
# 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.
|
||||
|
||||
if(NOT(Catch2_FOUND OR TARGET Catch2))
|
||||
find_package(Catch2 QUIET)
|
||||
if(NOT (Catch2_FOUND OR TARGET Catch2))
|
||||
find_package(Catch2 3 QUIET)
|
||||
|
||||
if(NOT Catch2_FOUND)
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
Catch2
|
||||
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
||||
GIT_TAG v2.13.9)
|
||||
GIT_TAG v3.4.0)
|
||||
|
||||
FetchContent_MakeAvailable(Catch2)
|
||||
|
||||
set(Catch2_VERSION "2.13.9")
|
||||
target_compile_features(Catch2 PRIVATE cxx_std_20)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -27,20 +47,16 @@ list(
|
||||
rename-compound
|
||||
sugar
|
||||
spinner
|
||||
# reconstruction
|
||||
reconstruction
|
||||
validate-pdbx
|
||||
)
|
||||
cql
|
||||
matrix
|
||||
)
|
||||
|
||||
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")
|
||||
@@ -48,12 +64,6 @@ 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}")
|
||||
|
||||
@@ -62,15 +72,8 @@ foreach(CIFPP_TEST IN LISTS CIFPP_tests)
|
||||
target_compile_options(${CIFPP_TEST} PRIVATE /EHsc)
|
||||
endif()
|
||||
|
||||
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()
|
||||
if(NOT (CIFPP_TEST STREQUAL "spinner-test"))
|
||||
add_test(NAME ${CIFPP_TEST}
|
||||
COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
157
test/cql-test.cpp
Normal file
157
test/cql-test.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
/*-
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "test-main.hpp"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <cif++.hpp>
|
||||
#include <cif++/cql/transaction.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
cif::file operator""_cf(const char *text, std::size_t length)
|
||||
{
|
||||
struct membuf : public std::streambuf
|
||||
{
|
||||
membuf(char *text, std::size_t length)
|
||||
{
|
||||
this->setg(text, text, text + length);
|
||||
}
|
||||
} buffer(const_cast<char *>(text), length);
|
||||
|
||||
std::istream is(&buffer);
|
||||
return cif::file(is);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("cql-1")
|
||||
{
|
||||
cif::file f(gTestDir / ".." / "examples" / "1cbs.cif.gz");
|
||||
auto &db = f.front();
|
||||
|
||||
cif::cql::transaction tx(db);
|
||||
|
||||
// CHECK(tx.exec("SELECT COUNT(*) FROM entry").one_field().as<int>() == 1);
|
||||
// CHECK(tx.exec("SELECT COUNT(*) FROM entry WHERE id = '1CBS'").one_field().as<int>() == 1);
|
||||
// CHECK(tx.exec("SELECT COUNT(*) FROM entry WHERE id = 'XXXX'").one_field().as<int>() == 0);
|
||||
|
||||
// CHECK(tx.exec("SELECT COUNT(*) FROM citation").one_field().as<int>() == 4);
|
||||
// CHECK(tx.exec("SELECT COUNT(page_last) FROM citation").one_field().as<int>() == 1);
|
||||
|
||||
const char *kPrimaryAuthors[] = {
|
||||
"Kleywegt, G.J.",
|
||||
"Bergfors, T.",
|
||||
"Senn, H.",
|
||||
"Le Motte, P.",
|
||||
"Gsell, B.",
|
||||
"Shudo, K.",
|
||||
"Jones, T.A."
|
||||
};
|
||||
|
||||
auto r = tx.exec("SELECT name, ordinal FROM citation_author WHERE citation_id = 'primary';");
|
||||
CHECK(r.size() == 7);
|
||||
|
||||
for (size_t ix = 0; auto row : r)
|
||||
{
|
||||
REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char *)));
|
||||
|
||||
CHECK(row[0].as<std::string>() == kPrimaryAuthors[ix++]);
|
||||
CHECK(row[1].as<size_t>() == ix);
|
||||
|
||||
// CHECK(row["name"].as<std::string>() == kPrimaryAuthors[ix++]);
|
||||
// CHECK(row["ordinal"].as<int>() == ix);
|
||||
}
|
||||
|
||||
r = tx.exec("SELECT ordinal, name FROM citation_author WHERE citation_id = 'primary';");
|
||||
CHECK(r.size() == 7);
|
||||
|
||||
for (size_t ix = 0; auto row : r)
|
||||
{
|
||||
REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char *)));
|
||||
|
||||
CHECK(row[1].as<std::string>() == kPrimaryAuthors[ix++]);
|
||||
CHECK(row[0].as<size_t>() == ix);
|
||||
|
||||
// CHECK(row["name"].as<std::string>() == kPrimaryAuthors[ix++]);
|
||||
// CHECK(row["ordinal"].as<int>() == ix);
|
||||
}
|
||||
|
||||
r = tx.exec("SELECT * FROM citation_author WHERE citation_id = 'primary';");
|
||||
CHECK(r.size() == 7);
|
||||
|
||||
for (size_t ix = 0; auto row : r)
|
||||
{
|
||||
REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char *)));
|
||||
|
||||
for (auto fld : row)
|
||||
{
|
||||
switch (fld.num())
|
||||
{
|
||||
case 0:
|
||||
CHECK(fld.name() == "citation_id");
|
||||
CHECK(fld.as<std::string>() == "primary");
|
||||
break;
|
||||
case 1:
|
||||
CHECK(fld.name() == "name");
|
||||
CHECK(fld.as<std::string>() == kPrimaryAuthors[ix]);
|
||||
break;
|
||||
case 2:
|
||||
CHECK(fld.name() == "ordinal");
|
||||
CHECK(fld.as<int>() == ix);
|
||||
break;
|
||||
default:
|
||||
REQUIRE(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++ix;
|
||||
|
||||
// CHECK(row[0].as<std::string>() == kPrimaryAuthors[ix++]);
|
||||
// CHECK(row[1].as<int>() == ix);
|
||||
|
||||
CHECK(row["name"].as<std::string>() == kPrimaryAuthors[ix++]);
|
||||
CHECK(row["ordinal"].as<size_t>() == ix);
|
||||
}
|
||||
|
||||
// CHECK(tx.query_value<int>("SELECT COUNT(*) FROM citation_author WHERE citation_id = 'primary';") == 7);
|
||||
|
||||
// for (size_t ix = 0; auto row : r)
|
||||
// {
|
||||
// REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char*)));
|
||||
// // CHECK(row["name"].as<std::string>() == kPrimaryAuthors[ix++]);
|
||||
// // CHECK(row["ordinal"].as<int>() == ix);
|
||||
// }
|
||||
|
||||
// for (size_t ix = 0; const auto &[name, ordinal] : tx.stream<std::string, int>("SELECT name FROM citation_author WHERE citation_id = 'primary'"))
|
||||
// {
|
||||
// REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char*)));
|
||||
// CHECK(name == kPrimaryAuthors[ix++]);
|
||||
// CHECK(ordinal == ix);
|
||||
// }
|
||||
}
|
||||
103
test/matrix-test.cpp
Normal file
103
test/matrix-test.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/*-
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "cif++/matrix.hpp"
|
||||
#include "test-main.hpp"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <cif++.hpp>
|
||||
|
||||
TEST_CASE("m1")
|
||||
{
|
||||
cif::matrix3x3<int> m = cif::identity_matrix<int>(3);
|
||||
|
||||
CHECK(cif::determinant(m) == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("m2")
|
||||
{
|
||||
cif::matrix4x4<int> m = cif::identity_matrix<int>(4);
|
||||
|
||||
cif::sub_matrix<cif::matrix4x4<int>> ms(m, 1, 1);
|
||||
CHECK(ms == cif::identity_matrix<int>(3));
|
||||
}
|
||||
|
||||
TEST_CASE("m3")
|
||||
{
|
||||
cif::matrix4x4<int> m{
|
||||
{ 1, 2, 3, 4, //
|
||||
5, 6, 7, 8, //
|
||||
9, 10, 11, 12, //
|
||||
13, 14, 15, 16 }
|
||||
};
|
||||
cif::sub_matrix<cif::matrix4x4<int>> ms(m, 1, 1);
|
||||
|
||||
cif::matrix3x3<int> t{
|
||||
{ 1, 3, 4, 9, 11, 12, 13, 15, 16 }
|
||||
};
|
||||
|
||||
CHECK(ms == t);
|
||||
}
|
||||
|
||||
TEST_CASE("m4")
|
||||
{
|
||||
cif::matrix4x4<int> m{
|
||||
{
|
||||
-2,
|
||||
3,
|
||||
1,
|
||||
0,
|
||||
4,
|
||||
1,
|
||||
-3,
|
||||
2,
|
||||
0,
|
||||
-1,
|
||||
2,
|
||||
5,
|
||||
3,
|
||||
2,
|
||||
0,
|
||||
-4,
|
||||
}
|
||||
};
|
||||
|
||||
// std::cout << m << "\n\n";
|
||||
|
||||
// std::cout << cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 0)) << "\n\n";
|
||||
// std::cout << cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 1)) << "\n\n";
|
||||
// std::cout << cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 2)) << "\n\n";
|
||||
// std::cout << cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 3)) << "\n\n";
|
||||
|
||||
// std::cout << cif::determinant(cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 0))) << "\n\n";
|
||||
// std::cout << cif::determinant(cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 1))) << "\n\n";
|
||||
// std::cout << cif::determinant(cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 2))) << "\n\n";
|
||||
// std::cout << cif::determinant(cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 3))) << "\n\n";
|
||||
|
||||
CHECK(cif::determinant(m) == 332);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,4 @@ TEST_CASE("q-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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include <cif++.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
@@ -11,12 +11,7 @@ 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
|
||||
|
||||
@@ -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,11 +26,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if CATCH22
|
||||
#include <catch2/catch.hpp>
|
||||
#else
|
||||
#include <catch2/catch_all.hpp>
|
||||
#endif
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
|
||||
@@ -24,11 +24,18 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cif++/point.hpp"
|
||||
#include "test-main.hpp"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include <cif++.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <cif++.hpp>
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable : 5054) // warning C5054: operator '&': deprecated between enumerations of different types
|
||||
# pragma warning(disable : 4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
#include <Eigen/Eigenvalues>
|
||||
|
||||
@@ -296,7 +303,7 @@ TEST_CASE("m2q_0a")
|
||||
auto d = cif::kSymopNrTable[i].symop().data();
|
||||
|
||||
Eigen::Matrix3f rot;
|
||||
rot << d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8];
|
||||
rot << static_cast<float>(d[0]), static_cast<float>(d[1]), static_cast<float>(d[2]), static_cast<float>(d[3]), static_cast<float>(d[4]), static_cast<float>(d[5]), static_cast<float>(d[6]), static_cast<float>(d[7]), static_cast<float>(d[8]);
|
||||
|
||||
// check to see if this matrix contains a true rotation
|
||||
if (rot * rot.transpose() != Eigen::Matrix3f::Identity() or rot.determinant() != 1)
|
||||
@@ -310,8 +317,7 @@ TEST_CASE("m2q_0a")
|
||||
cif::point p2 = p1;
|
||||
p2.rotate(q);
|
||||
|
||||
cif::matrix3x3<float> rot_c({
|
||||
static_cast<float>(d[0]),
|
||||
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]),
|
||||
@@ -319,8 +325,7 @@ TEST_CASE("m2q_0a")
|
||||
static_cast<float>(d[5]),
|
||||
static_cast<float>(d[6]),
|
||||
static_cast<float>(d[7]),
|
||||
static_cast<float>(d[8])
|
||||
});
|
||||
static_cast<float>(d[8]) });
|
||||
|
||||
cif::point p3 = rot_c * p1;
|
||||
|
||||
@@ -435,11 +440,11 @@ TEST_CASE("symm_4")
|
||||
|
||||
// based on 2b8h
|
||||
auto sg = cif::spacegroup(154); // p 32 2 1
|
||||
auto c = cif::cell(107.516, 107.516, 338.487, 90.00, 90.00, 120.00);
|
||||
auto c = cif::cell(107.516f, 107.516f, 338.487f, 90.00f, 90.00f, 120.00f);
|
||||
|
||||
cif::point a{ -8.688, 79.351, 10.439 }; // O6 NAG A 500
|
||||
cif::point b{ -35.356, 33.693, -3.236 }; // CG2 THR D 400
|
||||
cif::point sb(-6.916, 79.34, 3.236); // 4_565 copy of b
|
||||
cif::point a{ -8.688f, 79.351f, 10.439f }; // O6 NAG A 500
|
||||
cif::point b{ -35.356f, 33.693f, -3.236f }; // CG2 THR D 400
|
||||
cif::point sb(-6.916f, 79.34f, 3.236f); // 4_565 copy of b
|
||||
|
||||
CHECK_THAT(distance(a, sg(a, c, "1_455"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_a()), 0.01f));
|
||||
CHECK_THAT(distance(a, sg(a, c, "1_545"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_b()), 0.01f));
|
||||
@@ -466,7 +471,7 @@ TEST_CASE("symm_4wvp_1")
|
||||
|
||||
cif::crystal c(db);
|
||||
|
||||
cif::point p{ -78.722, 98.528, 11.994 };
|
||||
cif::point p{ -78.722f, 98.528f, 11.994f };
|
||||
auto a = s.get_residue("A", 10, "").get_atom_by_atom_id("O");
|
||||
|
||||
auto sp1 = c.symmetry_copy(a.get_location(), "2_565"_symop);
|
||||
@@ -605,3 +610,40 @@ TEST_CASE("volume_3bwh_1")
|
||||
|
||||
CHECK_THAT(c.get_cell().get_volume(), Catch::Matchers::WithinRel(741009.625f, 0.01f));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("smallest_sphere-1")
|
||||
{
|
||||
std::vector<cif::point> pts{
|
||||
{ 0.9295, 4.9006, 46.9706 },
|
||||
{ -0.1215, 5.5936, 46.0726 },
|
||||
{ -0.7975, 4.7046, 45.0796 },
|
||||
{ -1.4875, 3.5486, 45.7196 },
|
||||
{ -0.6535, 2.8816, 46.8186 },
|
||||
{ 0.3825, 3.5156, 47.4496 },
|
||||
{ 1.1995, 2.9206, 48.5286 },
|
||||
{ 0.8255, 2.0466, 49.4716 },
|
||||
{ 1.6625, 1.5036, 50.5176 },
|
||||
{ 1.1165, 0.6056, 51.3626 },
|
||||
{ 1.8325, -0.0064, 52.4656 },
|
||||
{ 1.1945, -0.9044, 53.2216 },
|
||||
{ 1.8135, -1.5534, 54.3566 },
|
||||
{ 1.0925, -2.4574, 55.0656 },
|
||||
{ 1.5205, -3.2204, 56.2476 },
|
||||
{ 1.1955, 5.8066, 48.1796 },
|
||||
{ 2.2495, 4.6896, 46.1796 },
|
||||
{ -1.2515, 1.5186, 47.1786 },
|
||||
{ 3.1385, 1.9106, 50.6166 },
|
||||
{ 3.2605, -1.1834, 54.7206 },
|
||||
{ 2.5975, -3.8554, 56.2096 },
|
||||
{ 0.7975, -3.2184, 57.2686 }
|
||||
};
|
||||
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
auto [c, r] = cif::smallest_sphere_around_points(pts);
|
||||
CHECK_THAT(cif::distance(c, cif::point{ 0, 0.743099928, 51.1741028 }), Catch::Matchers::WithinAbs(0.f, 0.01f));
|
||||
CHECK_THAT(r, Catch::Matchers::WithinAbs(7.31248331f, 0.01f));
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,8 @@ IF NOT EXIST build_ci\libs (
|
||||
MKDIR build_ci\libs
|
||||
)
|
||||
CD build_ci\libs
|
||||
|
||||
@REM Install ZLib
|
||||
IF NOT EXIST zlib-%ZLIB_VERSION%.zip (
|
||||
ECHO Downloading https://github.com/libarchive/zlib/archive/v%ZLIB_VERSION%.zip
|
||||
curl -L -o zlib-%ZLIB_VERSION%.zip https://github.com/libarchive/zlib/archive/v%ZLIB_VERSION%.zip || EXIT /b 1
|
||||
@@ -14,9 +16,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 -G "Visual Studio 17 2022" . || EXIT /b 1
|
||||
cmake --build . --target ALL_BUILD --config Release || EXIT /b 1
|
||||
cmake --build . --target RUN_TESTS --config Release || EXIT /b 1
|
||||
cmake --build . --target INSTALL --config Release || EXIT /b 1
|
||||
cmake -B build || EXIT /b 1
|
||||
cmake --build build --target ALL_BUILD --config Release || EXIT /b 1
|
||||
cmake --build build --target RUN_TESTS --config Release || EXIT /b 1
|
||||
cmake --build build --target INSTALL --config Release || EXIT /b 1
|
||||
|
||||
@EXIT /b 0
|
||||
|
||||
@@ -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://github.com/ihmwg/ModelCIF/raw/master/dist/mmcif_ma.dic"
|
||||
update_dictionary "@CIFPP_CACHE_DIR@/mmcif_ma.dic" "https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_ma.dic"
|
||||
|
||||
# notify subscribers, using find instead of run-parts to make it work on FreeBSD as well
|
||||
|
||||
|
||||
Reference in New Issue
Block a user