Commit f1ca916d by Maarten L. Hekkelman

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

parents 4782a4e0 6aae012a
...@@ -6,26 +6,29 @@ ...@@ -6,26 +6,29 @@
# modification, are permitted provided that the following conditions are met: # modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice, this # 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer # list of conditions and the following disclaimer
# 2. Redistributions in binary form must reproduce the above copyright notice, # 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation # this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution. # and/or other materials provided with the distribution.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
# set the project name # set the project name
project(libcifpp VERSION 6.0.0 LANGUAGES CXX) project(
libcifpp
VERSION 6.0.1
LANGUAGES CXX)
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
...@@ -45,14 +48,16 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) ...@@ -45,14 +48,16 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
# When building with ninja-multiconfig, build both debug and release by default # When building with ninja-multiconfig, build both debug and release by default
if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config") if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
set(CMAKE_CROSS_CONFIGS "Debug;Release") set(CMAKE_CROSS_CONFIGS "Debug;Release")
set(CMAKE_DEFAULT_CONFIGS "Debug;Release") set(CMAKE_DEFAULT_CONFIGS "Debug;Release")
endif() endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers") set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers"
)
elseif(MSVC) elseif(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif() endif()
# Build documentation? # Build documentation?
...@@ -62,116 +67,126 @@ option(BUILD_DOCUMENTATION "Build the documentation" OFF) ...@@ -62,116 +67,126 @@ option(BUILD_DOCUMENTATION "Build the documentation" OFF)
option(BUILD_FOR_CCP4 "Build a version to be installed in CCP4") option(BUILD_FOR_CCP4 "Build a version to be installed in CCP4")
# Building shared libraries? # Building shared libraries?
if(NOT(BUILD_FOR_CCP4 AND WIN32)) if(NOT (BUILD_FOR_CCP4 AND WIN32))
option(BUILD_SHARED_LIBS "Build a shared library instead of a static one" OFF) option(BUILD_SHARED_LIBS "Build a shared library instead of a static one" OFF)
endif() endif()
if(BUILD_FOR_CCP4) if(BUILD_FOR_CCP4)
unset(CIFPP_DOWNLOAD_CCD) unset(CIFPP_DOWNLOAD_CCD)
unset(CIFPP_INSTALL_UPDATE_SCRIPT) unset(CIFPP_INSTALL_UPDATE_SCRIPT)
else() else()
# Lots of code depend on the availability of the components.cif file # Lots of code depend on the availability of the components.cif file
option(CIFPP_DOWNLOAD_CCD "Download the CCD file components.cif during installation" ON) option(CIFPP_DOWNLOAD_CCD
"Download the CCD file components.cif during installation" ON)
# An optional cron script can be installed to keep the data files up-to-date
if(UNIX AND NOT APPLE) # An optional cron script can be installed to keep the data files up-to-date
option(CIFPP_INSTALL_UPDATE_SCRIPT "Install the script to update CCD and dictionary files" ON) if(UNIX AND NOT APPLE)
endif() option(CIFPP_INSTALL_UPDATE_SCRIPT
"Install the script to update CCD and dictionary files" ON)
endif()
endif() endif()
# When CCP4 is sourced in the environment, we can recreate the symmetry operations table # When CCP4 is sourced in the environment, we can recreate the symmetry
# operations table
if(EXISTS "$ENV{CCP4}/lib/data/syminfo.lib") if(EXISTS "$ENV{CCP4}/lib/data/syminfo.lib")
option(CIFPP_RECREATE_SYMOP_DATA "Recreate SymOp data table in case it is out of date" ON) option(CIFPP_RECREATE_SYMOP_DATA
"Recreate SymOp data table in case it is out of date" ON)
endif() endif()
# CCP4 build # CCP4 build
if(BUILD_FOR_CCP4) if(BUILD_FOR_CCP4)
if("$ENV{CCP4}" STREQUAL "" OR NOT EXISTS $ENV{CCP4}) if("$ENV{CCP4}" STREQUAL "" OR NOT EXISTS $ENV{CCP4})
message(FATAL_ERROR "A CCP4 built was requested but CCP4 was not sourced") message(FATAL_ERROR "A CCP4 built was requested but CCP4 was not sourced")
else() else()
list(PREPEND CMAKE_MODULE_PATH "$ENV{CCP4}") list(PREPEND CMAKE_MODULE_PATH "$ENV{CCP4}")
list(PREPEND CMAKE_PREFIX_PATH "$ENV{CCP4}") list(PREPEND CMAKE_PREFIX_PATH "$ENV{CCP4}")
set(CMAKE_INSTALL_PREFIX "$ENV{CCP4}") set(CMAKE_INSTALL_PREFIX "$ENV{CCP4}")
if(WIN32) if(WIN32)
set(BUILD_SHARED_LIBS ON) set(BUILD_SHARED_LIBS ON)
endif() endif()
endif() endif()
endif() endif()
# Now include the GNUInstallDirs module # Now include the GNUInstallDirs module
include(GNUInstallDirs) include(GNUInstallDirs)
if(WIN32) if(WIN32)
if(${CMAKE_SYSTEM_VERSION} GREATER_EQUAL 10) # Windows 10 if(${CMAKE_SYSTEM_VERSION} GREATER_EQUAL 10) # Windows 10
add_definitions(-D _WIN32_WINNT=0x0A00) add_definitions(-D _WIN32_WINNT=0x0A00)
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.3) # Windows 8.1 elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.3) # Windows 8.1
add_definitions(-D _WIN32_WINNT=0x0603) add_definitions(-D _WIN32_WINNT=0x0603)
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.2) # Windows 8 elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.2) # Windows 8
add_definitions(-D _WIN32_WINNT=0x0602) add_definitions(-D _WIN32_WINNT=0x0602)
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.1) # Windows 7 elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.1) # Windows 7
add_definitions(-D _WIN32_WINNT=0x0601) add_definitions(-D _WIN32_WINNT=0x0601)
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.0) # Windows Vista elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.0) # Windows Vista
add_definitions(-D _WIN32_WINNT=0x0600) add_definitions(-D _WIN32_WINNT=0x0600)
else() # Windows XP (5.1) else() # Windows XP (5.1)
add_definitions(-D _WIN32_WINNT=0x0501) add_definitions(-D _WIN32_WINNT=0x0501)
endif() endif()
add_definitions(-DNOMINMAX) add_definitions(-DNOMINMAX)
# We do not want to write an export file for all our symbols... # We do not want to write an export file for all our symbols...
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif() endif()
if(MSVC) if(MSVC)
# make msvc standards compliant... # make msvc standards compliant...
add_compile_options(/permissive- /bigobj) add_compile_options(/permissive- /bigobj)
add_link_options(/NODEFAULTLIB:library) add_link_options(/NODEFAULTLIB:library)
if(BUILD_SHARED_LIBS) if(BUILD_SHARED_LIBS)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL") set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
else() else()
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif() endif()
endif() endif()
# Libraries # Libraries
# Start by finding out if std:regex is usable. Note that the current # Start by finding out if std:regex is usable. Note that the current
# implementation in GCC is not acceptable, it crashes on long lines. # implementation in GCC is not acceptable, it crashes on long lines. The
# The implementation in libc++ (clang) and MSVC seem to be OK. # implementation in libc++ (clang) and MSVC seem to be OK.
check_cxx_source_compiles(" check_cxx_source_compiles(
"
#include <iostream> #include <iostream>
#ifndef __GLIBCXX__ #ifndef __GLIBCXX__
#error #error
#endif #endif
int main(int argc, char *argv[]) { return 0; }" GXX_LIBSTDCPP) int main(int argc, char *argv[]) { return 0; }"
GXX_LIBSTDCPP)
if(GXX_LIBSTDCPP) if(GXX_LIBSTDCPP)
message(STATUS "Testing for known regex bug, since you're using GNU libstdc++") message(
STATUS "Testing for known regex bug, since you're using GNU libstdc++")
try_run(STD_REGEX_RUNNING STD_REGEX_COMPILING try_run(STD_REGEX_RUNNING STD_REGEX_COMPILING
${CMAKE_CURRENT_BINARY_DIR}/test ${PROJECT_SOURCE_DIR}/cmake/test-rx.cpp) ${CMAKE_CURRENT_BINARY_DIR}/test
${PROJECT_SOURCE_DIR}/cmake/test-rx.cpp)
if(STD_REGEX_RUNNING STREQUAL FAILED_TO_RUN) if(STD_REGEX_RUNNING STREQUAL FAILED_TO_RUN)
message(STATUS "You are probably trying to compile using the g++ standard library which contains a crashing std::regex implementation. Will use boost::regex instead") message(
STATUS
"You are probably trying to compile using the g++ standard library which contains a crashing std::regex implementation. Will use boost::regex instead"
)
find_package(Boost 1.80 QUIET COMPONENTS regex) find_package(Boost 1.80 QUIET COMPONENTS regex)
if(NOT Boost_FOUND) if(NOT Boost_FOUND)
set(BOOST_REGEX_STANDALONE ON) set(BOOST_REGEX_STANDALONE ON)
FetchContent_Declare( FetchContent_Declare(
boost-rx boost-rx
GIT_REPOSITORY https://github.com/boostorg/regex GIT_REPOSITORY https://github.com/boostorg/regex
GIT_TAG boost-1.83.0 GIT_TAG boost-1.83.0)
)
FetchContent_MakeAvailable(boost-rx) FetchContent_MakeAvailable(boost-rx)
endif() endif()
set(BOOST_REGEX ON) set(BOOST_REGEX ON)
endif() endif()
endif() endif()
set(CMAKE_THREAD_PREFER_PTHREAD) set(CMAKE_THREAD_PREFER_PTHREAD)
...@@ -179,55 +194,57 @@ set(THREADS_PREFER_PTHREAD_FLAG) ...@@ -179,55 +194,57 @@ set(THREADS_PREFER_PTHREAD_FLAG)
find_package(Threads) find_package(Threads)
if(MSVC) if(MSVC)
# Avoid linking the shared library of zlib # Avoid linking the shared library of zlib Search ZLIB_ROOT first if it is
# Search ZLIB_ROOT first if it is set. # set.
if(ZLIB_ROOT) if(ZLIB_ROOT)
set(_ZLIB_SEARCH_ROOT PATHS ${ZLIB_ROOT} NO_DEFAULT_PATH) set(_ZLIB_SEARCH_ROOT PATHS ${ZLIB_ROOT} NO_DEFAULT_PATH)
list(APPEND _ZLIB_SEARCHES _ZLIB_SEARCH_ROOT) list(APPEND _ZLIB_SEARCHES _ZLIB_SEARCH_ROOT)
endif() endif()
# Normal search. # Normal search.
set(_ZLIB_x86 "(x86)") set(_ZLIB_x86 "(x86)")
set(_ZLIB_SEARCH_NORMAL set(_ZLIB_SEARCH_NORMAL
PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\Zlib;InstallPath]" PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\Zlib;InstallPath]"
"$ENV{ProgramFiles}/zlib" "$ENV{ProgramFiles}/zlib" "$ENV{ProgramFiles${_ZLIB_x86}}/zlib")
"$ENV{ProgramFiles${_ZLIB_x86}}/zlib") unset(_ZLIB_x86)
unset(_ZLIB_x86) list(APPEND _ZLIB_SEARCHES _ZLIB_SEARCH_NORMAL)
list(APPEND _ZLIB_SEARCHES _ZLIB_SEARCH_NORMAL)
if(BUILD_FOR_CCP4)
if(BUILD_FOR_CCP4) list(PREPEND _ZLIB_SEARCHES "$ENV{CCP4}/lib")
list(PREPEND _ZLIB_SEARCHES "$ENV{CCP4}/lib") endif()
endif()
foreach(search ${_ZLIB_SEARCHES})
foreach(search ${_ZLIB_SEARCHES}) find_library(
find_library(ZLIB_LIBRARY NAMES zlibstatic NAMES_PER_DIR ${${search}} PATH_SUFFIXES lib) ZLIB_LIBRARY
endforeach() NAMES zlibstatic NAMES_PER_DIR ${${search}}
PATH_SUFFIXES lib)
endforeach()
endif() endif()
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
# Using Eigen3 is a bit of a thing. We don't want to build it completely since we # Using Eigen3 is a bit of a thing. We don't want to build it completely since
# only need a couple of header files. Nothing special. But often, eigen3 is already # we only need a couple of header files. Nothing special. But often, eigen3 is
# installed and then we prefer that. # already installed and then we prefer that.
find_package(Eigen3 3.4 QUIET) find_package(Eigen3 3.4 QUIET)
if(Eigen3_FOUND AND TARGET Eigen3::Eigen) if(Eigen3_FOUND AND TARGET Eigen3::Eigen)
get_target_property(EIGEN_INCLUDE_DIR Eigen3::Eigen INTERFACE_INCLUDE_DIRECTORIES) get_target_property(EIGEN_INCLUDE_DIR Eigen3::Eigen
INTERFACE_INCLUDE_DIRECTORIES)
else() else()
# Create a private copy of eigen3 and populate it only, no need to build # Create a private copy of eigen3 and populate it only, no need to build
FetchContent_Declare( FetchContent_Declare(
my-eigen3 my-eigen3
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.0 GIT_TAG 3.4.0)
)
FetchContent_GetProperties(my-eigen3) FetchContent_GetProperties(my-eigen3)
if(NOT my-eigen3_POPULATED) if(NOT my-eigen3_POPULATED)
FetchContent_Populate(my-eigen3) FetchContent_Populate(my-eigen3)
endif() endif()
set(EIGEN_INCLUDE_DIR ${my-eigen3_SOURCE_DIR}) set(EIGEN_INCLUDE_DIR ${my-eigen3_SOURCE_DIR})
endif() endif()
include(FindFilesystem) include(FindFilesystem)
...@@ -242,356 +259,387 @@ write_version_header(${PROJECT_SOURCE_DIR}/src/ LIB_NAME "LibCIFPP") ...@@ -242,356 +259,387 @@ write_version_header(${PROJECT_SOURCE_DIR}/src/ LIB_NAME "LibCIFPP")
# SymOp data table # SymOp data table
if(CIFPP_RECREATE_SYMOP_DATA) if(CIFPP_RECREATE_SYMOP_DATA)
# The tool to create the table # The tool to create the table
add_executable(symop-map-generator "${PROJECT_SOURCE_DIR}/src/symop-map-generator.cpp") add_executable(symop-map-generator
"${PROJECT_SOURCE_DIR}/src/symop-map-generator.cpp")
add_custom_command(
OUTPUT ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp add_custom_command(
COMMAND $<TARGET_FILE:symop-map-generator> $ENV{CLIBD}/syminfo.lib $ENV{CLIBD}/symop.lib ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp OUTPUT ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp
) COMMAND
$<TARGET_FILE:symop-map-generator> $ENV{CLIBD}/syminfo.lib
add_custom_target( $ENV{CLIBD}/symop.lib ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp)
OUTPUT ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp
DEPENDS symop-map-generator "$ENV{CLIBD}/syminfo.lib" "$ENV{CLIBD}/symop.lib" add_custom_target(
) OUTPUT
${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp
DEPENDS symop-map-generator "$ENV{CLIBD}/syminfo.lib"
"$ENV{CLIBD}/symop.lib")
endif() endif()
# Sources # Sources
set(project_sources set(project_sources
${PROJECT_SOURCE_DIR}/src/category.cpp ${PROJECT_SOURCE_DIR}/src/category.cpp
${PROJECT_SOURCE_DIR}/src/condition.cpp ${PROJECT_SOURCE_DIR}/src/condition.cpp
${PROJECT_SOURCE_DIR}/src/datablock.cpp ${PROJECT_SOURCE_DIR}/src/datablock.cpp
${PROJECT_SOURCE_DIR}/src/dictionary_parser.cpp ${PROJECT_SOURCE_DIR}/src/dictionary_parser.cpp
${PROJECT_SOURCE_DIR}/src/file.cpp ${PROJECT_SOURCE_DIR}/src/file.cpp
${PROJECT_SOURCE_DIR}/src/item.cpp ${PROJECT_SOURCE_DIR}/src/item.cpp
${PROJECT_SOURCE_DIR}/src/parser.cpp ${PROJECT_SOURCE_DIR}/src/parser.cpp
${PROJECT_SOURCE_DIR}/src/row.cpp ${PROJECT_SOURCE_DIR}/src/row.cpp
${PROJECT_SOURCE_DIR}/src/validate.cpp ${PROJECT_SOURCE_DIR}/src/validate.cpp
${PROJECT_SOURCE_DIR}/src/text.cpp ${PROJECT_SOURCE_DIR}/src/text.cpp
${PROJECT_SOURCE_DIR}/src/utilities.cpp ${PROJECT_SOURCE_DIR}/src/utilities.cpp
${PROJECT_SOURCE_DIR}/src/atom_type.cpp
${PROJECT_SOURCE_DIR}/src/atom_type.cpp ${PROJECT_SOURCE_DIR}/src/compound.cpp
${PROJECT_SOURCE_DIR}/src/compound.cpp ${PROJECT_SOURCE_DIR}/src/point.cpp
${PROJECT_SOURCE_DIR}/src/point.cpp ${PROJECT_SOURCE_DIR}/src/symmetry.cpp
${PROJECT_SOURCE_DIR}/src/symmetry.cpp ${PROJECT_SOURCE_DIR}/src/model.cpp
${PROJECT_SOURCE_DIR}/src/pdb/cif2pdb.cpp
${PROJECT_SOURCE_DIR}/src/model.cpp ${PROJECT_SOURCE_DIR}/src/pdb/pdb2cif.cpp
${PROJECT_SOURCE_DIR}/src/pdb/pdb_record.hpp
${PROJECT_SOURCE_DIR}/src/pdb/cif2pdb.cpp ${PROJECT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.hpp
${PROJECT_SOURCE_DIR}/src/pdb/pdb2cif.cpp ${PROJECT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.cpp)
${PROJECT_SOURCE_DIR}/src/pdb/pdb_record.hpp
${PROJECT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.hpp
${PROJECT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.cpp
)
set(project_headers set(project_headers
${PROJECT_SOURCE_DIR}/include/cif++.hpp ${PROJECT_SOURCE_DIR}/include/cif++.hpp
${PROJECT_SOURCE_DIR}/include/cif++/utilities.hpp ${PROJECT_SOURCE_DIR}/include/cif++/utilities.hpp
${PROJECT_SOURCE_DIR}/include/cif++/item.hpp ${PROJECT_SOURCE_DIR}/include/cif++/item.hpp
${PROJECT_SOURCE_DIR}/include/cif++/datablock.hpp ${PROJECT_SOURCE_DIR}/include/cif++/datablock.hpp
${PROJECT_SOURCE_DIR}/include/cif++/file.hpp ${PROJECT_SOURCE_DIR}/include/cif++/file.hpp
${PROJECT_SOURCE_DIR}/include/cif++/validate.hpp ${PROJECT_SOURCE_DIR}/include/cif++/validate.hpp
${PROJECT_SOURCE_DIR}/include/cif++/iterator.hpp ${PROJECT_SOURCE_DIR}/include/cif++/iterator.hpp
${PROJECT_SOURCE_DIR}/include/cif++/parser.hpp ${PROJECT_SOURCE_DIR}/include/cif++/parser.hpp
${PROJECT_SOURCE_DIR}/include/cif++/forward_decl.hpp ${PROJECT_SOURCE_DIR}/include/cif++/forward_decl.hpp
${PROJECT_SOURCE_DIR}/include/cif++/dictionary_parser.hpp ${PROJECT_SOURCE_DIR}/include/cif++/dictionary_parser.hpp
${PROJECT_SOURCE_DIR}/include/cif++/condition.hpp ${PROJECT_SOURCE_DIR}/include/cif++/condition.hpp
${PROJECT_SOURCE_DIR}/include/cif++/category.hpp ${PROJECT_SOURCE_DIR}/include/cif++/category.hpp
${PROJECT_SOURCE_DIR}/include/cif++/row.hpp ${PROJECT_SOURCE_DIR}/include/cif++/row.hpp
${PROJECT_SOURCE_DIR}/include/cif++/atom_type.hpp
${PROJECT_SOURCE_DIR}/include/cif++/atom_type.hpp ${PROJECT_SOURCE_DIR}/include/cif++/compound.hpp
${PROJECT_SOURCE_DIR}/include/cif++/compound.hpp ${PROJECT_SOURCE_DIR}/include/cif++/point.hpp
${PROJECT_SOURCE_DIR}/include/cif++/point.hpp ${PROJECT_SOURCE_DIR}/include/cif++/symmetry.hpp
${PROJECT_SOURCE_DIR}/include/cif++/symmetry.hpp ${PROJECT_SOURCE_DIR}/include/cif++/model.hpp
${PROJECT_SOURCE_DIR}/include/cif++/pdb.hpp
${PROJECT_SOURCE_DIR}/include/cif++/model.hpp ${PROJECT_SOURCE_DIR}/include/cif++/pdb/cif2pdb.hpp
${PROJECT_SOURCE_DIR}/include/cif++/pdb/io.hpp
${PROJECT_SOURCE_DIR}/include/cif++/pdb.hpp ${PROJECT_SOURCE_DIR}/include/cif++/pdb/pdb2cif.hpp
${PROJECT_SOURCE_DIR}/include/cif++/pdb/tls.hpp)
${PROJECT_SOURCE_DIR}/include/cif++/pdb/cif2pdb.hpp
${PROJECT_SOURCE_DIR}/include/cif++/pdb/io.hpp add_library(cifpp ${project_sources} ${project_headers}
${PROJECT_SOURCE_DIR}/include/cif++/pdb/pdb2cif.hpp ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp)
${PROJECT_SOURCE_DIR}/include/cif++/pdb/tls.hpp
)
add_library(cifpp ${project_sources} ${project_headers} ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp)
add_library(cifpp::cifpp ALIAS cifpp) add_library(cifpp::cifpp ALIAS cifpp)
set(CMAKE_DEBUG_POSTFIX d) set(CMAKE_DEBUG_POSTFIX d)
set_target_properties(cifpp PROPERTIES DEBUG_POSTFIX "d") set_target_properties(cifpp PROPERTIES DEBUG_POSTFIX "d")
generate_export_header(cifpp EXPORT_FILE_NAME ${PROJECT_SOURCE_DIR}/include/cif++/exports.hpp) generate_export_header(cifpp EXPORT_FILE_NAME
${PROJECT_SOURCE_DIR}/include/cif++/exports.hpp)
if(BOOST_REGEX) if(BOOST_REGEX)
target_compile_definitions(cifpp PRIVATE USE_BOOST_REGEX=1 BOOST_REGEX_STANDALONE=1) target_compile_definitions(cifpp PRIVATE USE_BOOST_REGEX=1
get_target_property(BOOST_REGEX_INCLUDE_DIR Boost::regex INTERFACE_INCLUDE_DIRECTORIES) BOOST_REGEX_STANDALONE=1)
get_target_property(BOOST_REGEX_INCLUDE_DIR Boost::regex
INTERFACE_INCLUDE_DIRECTORIES)
endif() endif()
if(MSVC) if(MSVC)
target_compile_definitions(cifpp PUBLIC NOMINMAX=1) target_compile_definitions(cifpp PUBLIC NOMINMAX=1)
endif() endif()
set_target_properties(cifpp PROPERTIES POSITION_INDEPENDENT_CODE ON) set_target_properties(cifpp PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(cifpp target_include_directories(
PUBLIC cifpp
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>" PUBLIC "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
PRIVATE PRIVATE "${BOOST_REGEX_INCLUDE_DIR}" "${EIGEN_INCLUDE_DIR}")
"${BOOST_REGEX_INCLUDE_DIR}"
"${EIGEN_INCLUDE_DIR}"
)
target_link_libraries(cifpp target_link_libraries(cifpp PUBLIC Threads::Threads ZLIB::ZLIB
PUBLIC Threads::Threads ZLIB::ZLIB ${CIFPP_REQUIRED_LIBRARIES}) ${CIFPP_REQUIRED_LIBRARIES})
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
target_link_options(cifpp PRIVATE -undefined dynamic_lookup) target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
endif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") endif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
if(CIFPP_DOWNLOAD_CCD) if(CIFPP_DOWNLOAD_CCD)
# download the components.cif file from CCD # download the components.cif file from CCD
set(COMPONENTS_CIF ${PROJECT_SOURCE_DIR}/rsrc/components.cif) set(COMPONENTS_CIF ${PROJECT_SOURCE_DIR}/rsrc/components.cif)
if(EXISTS ${COMPONENTS_CIF}) if(EXISTS ${COMPONENTS_CIF})
file(SIZE ${COMPONENTS_CIF} CCD_FILE_SIZE) file(SIZE ${COMPONENTS_CIF} CCD_FILE_SIZE)
if(CCD_FILE_SIZE EQUAL 0) if(CCD_FILE_SIZE EQUAL 0)
message(STATUS "Removing empty ${COMPONENTS_CIF} file") message(STATUS "Removing empty ${COMPONENTS_CIF} file")
file(REMOVE "${COMPONENTS_CIF}") file(REMOVE "${COMPONENTS_CIF}")
endif() endif()
endif() endif()
if(NOT EXISTS ${COMPONENTS_CIF}) if(NOT EXISTS ${COMPONENTS_CIF})
# Since the file(DOWNLOAD) command in cmake does not use # Since the file(DOWNLOAD) command in cmake does not use compression, we try
# compression, we try to download the gzipped version and # to download the gzipped version and decompress it ourselves.
# decompress it ourselves. find_program(GUNZIP gunzip)
find_program(GUNZIP gunzip)
if(WIN32 OR GUNZIP STREQUAL "GUNZIP-NOTFOUND")
if(WIN32 OR GUNZIP STREQUAL "GUNZIP-NOTFOUND") file(
file(DOWNLOAD https://files.wwpdb.org/pub/pdb/data/monomers/components.cif ${COMPONENTS_CIF} DOWNLOAD https://files.wwpdb.org/pub/pdb/data/monomers/components.cif
SHOW_PROGRESS STATUS CCD_FETCH_STATUS) ${COMPONENTS_CIF}
else() SHOW_PROGRESS
if(NOT EXISTS "${COMPONENTS_CIF}.gz") STATUS CCD_FETCH_STATUS)
file(DOWNLOAD https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz ${COMPONENTS_CIF}.gz else()
SHOW_PROGRESS STATUS CCD_FETCH_STATUS) if(NOT EXISTS "${COMPONENTS_CIF}.gz")
endif() file(
DOWNLOAD
add_custom_command(OUTPUT ${COMPONENTS_CIF} https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz
COMMAND "${GUNZIP}" ${COMPONENTS_CIF}.gz ${COMPONENTS_CIF}.gz
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/rsrc/) SHOW_PROGRESS
STATUS CCD_FETCH_STATUS)
add_custom_target(COMPONENTS ALL DEPENDS ${COMPONENTS_CIF}) endif()
endif()
add_custom_command(
# Do not continue if downloading went wrong OUTPUT ${COMPONENTS_CIF}
list(POP_FRONT CCD_FETCH_STATUS CCD_FETCH_STATUS_CODE) COMMAND "${GUNZIP}" ${COMPONENTS_CIF}.gz
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/rsrc/)
if(CCD_FETCH_STATUS_CODE)
message(FATAL_ERROR "Error trying to download CCD file: ${CCD_FETCH_STATUS}") add_custom_target(COMPONENTS ALL DEPENDS ${COMPONENTS_CIF})
endif() endif()
endif()
# Do not continue if downloading went wrong
list(POP_FRONT CCD_FETCH_STATUS CCD_FETCH_STATUS_CODE)
if(CCD_FETCH_STATUS_CODE)
message(
FATAL_ERROR "Error trying to download CCD file: ${CCD_FETCH_STATUS}")
endif()
endif()
endif() endif()
# Installation directories # Installation directories
if(BUILD_FOR_CCP4) if(BUILD_FOR_CCP4)
set(CIFPP_DATA_DIR "$ENV{CCP4}/share/libcifpp" CACHE PATH "Directory where dictionary and other static data is stored") set(CIFPP_DATA_DIR
"$ENV{CCP4}/share/libcifpp"
CACHE PATH "Directory where dictionary and other static data is stored")
else() else()
set(CIFPP_DATA_DIR "${CMAKE_INSTALL_FULL_DATADIR}/libcifpp" CACHE PATH "Directory where dictionary and other static data is stored") set(CIFPP_DATA_DIR
"${CMAKE_INSTALL_FULL_DATADIR}/libcifpp"
CACHE PATH "Directory where dictionary and other static data is stored")
endif() endif()
target_compile_definitions(cifpp PUBLIC DATA_DIR="${CIFPP_DATA_DIR}") if(CIFPP_DATA_DIR)
target_compile_definitions(cifpp PUBLIC DATA_DIR="${CIFPP_DATA_DIR}")
endif()
if(UNIX AND NOT BUILD_FOR_CCP4) if(UNIX AND NOT BUILD_FOR_CCP4)
if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local") if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local")
set(CIFPP_CACHE_DIR "/var/cache/libcifpp" CACHE PATH "The directory where downloaded data files are stored") set(CIFPP_CACHE_DIR
else() "/var/cache/libcifpp"
set(CIFPP_CACHE_DIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/cache/libcifpp" CACHE PATH "The directory where downloaded data files are stored") CACHE PATH "The directory where downloaded data files are stored")
endif() else()
set(CIFPP_CACHE_DIR
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}") "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/cache/libcifpp"
CACHE PATH "The directory where downloaded data files are stored")
set(CIFPP_ETC_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}" CACHE PATH "The directory where the update configuration file is stored") endif()
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
set(CIFPP_ETC_DIR
"${CMAKE_INSTALL_FULL_SYSCONFDIR}"
CACHE PATH "The directory where the update configuration file is stored")
else() else()
unset(CIFPP_CACHE_DIR) unset(CIFPP_CACHE_DIR)
endif() endif()
# Install rules # Install rules
install(TARGETS cifpp install(
EXPORT cifpp-targets TARGETS cifpp
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT cifpp-targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
if(MSVC AND BUILD_SHARED_LIBS) if(MSVC AND BUILD_SHARED_LIBS)
install( install(
FILES $<TARGET_PDB_FILE:cifpp> FILES $<TARGET_PDB_FILE:cifpp>
DESTINATION ${CMAKE_INSTALL_LIBDIR} DESTINATION ${CMAKE_INSTALL_LIBDIR}
OPTIONAL) OPTIONAL)
endif() endif()
# Clean up old config files (with old names) # Clean up old config files (with old names)
file(GLOB OLD_CONFIG_FILES file(GLOB OLD_CONFIG_FILES
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppConfig*.cmake ${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppConfig*.cmake
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppTargets*.cmake) ${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppTargets*.cmake)
if(OLD_CONFIG_FILES) if(OLD_CONFIG_FILES)
message(STATUS "Installation will remove old config files: ${OLD_CONFIG_FILES}") message(
install(CODE "file(REMOVE ${OLD_CONFIG_FILES})") STATUS "Installation will remove old config files: ${OLD_CONFIG_FILES}")
install(CODE "file(REMOVE ${OLD_CONFIG_FILES})")
endif() endif()
install(EXPORT cifpp-targets install(
FILE "cifpp-targets.cmake" EXPORT cifpp-targets
NAMESPACE cifpp:: FILE "cifpp-targets.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cifpp NAMESPACE cifpp::
) DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cifpp)
install( install(
DIRECTORY include/cif++ DIRECTORY include/cif++
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT Devel COMPONENT Devel)
)
install( install(
FILES include/cif++.hpp FILES include/cif++.hpp
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT Devel COMPONENT Devel)
)
if(CIFPP_DATA_DIR)
install(FILES install(
${PROJECT_SOURCE_DIR}/rsrc/mmcif_ddl.dic FILES ${PROJECT_SOURCE_DIR}/rsrc/mmcif_ddl.dic
${PROJECT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic ${PROJECT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic
${PROJECT_SOURCE_DIR}/rsrc/mmcif_ma.dic ${PROJECT_SOURCE_DIR}/rsrc/mmcif_ma.dic ${COMPONENTS_CIF}
${COMPONENTS_CIF} DESTINATION ${CIFPP_DATA_DIR})
DESTINATION ${CIFPP_DATA_DIR} endif()
)
if(CIFPP_CACHE_DIR) if(CIFPP_CACHE_DIR)
install(FILES install(
${PROJECT_SOURCE_DIR}/rsrc/mmcif_ddl.dic FILES ${PROJECT_SOURCE_DIR}/rsrc/mmcif_ddl.dic
${PROJECT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic ${PROJECT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic
${PROJECT_SOURCE_DIR}/rsrc/mmcif_ma.dic ${PROJECT_SOURCE_DIR}/rsrc/mmcif_ma.dic ${COMPONENTS_CIF}
${COMPONENTS_CIF} DESTINATION ${CIFPP_CACHE_DIR})
DESTINATION ${CIFPP_CACHE_DIR}
)
endif() endif()
set(CONFIG_TEMPLATE_FILE ${PROJECT_SOURCE_DIR}/cmake/cifpp-config.cmake.in) set(CONFIG_TEMPLATE_FILE ${PROJECT_SOURCE_DIR}/cmake/cifpp-config.cmake.in)
configure_package_config_file( configure_package_config_file(
${CONFIG_TEMPLATE_FILE} ${CONFIG_TEMPLATE_FILE} ${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cifpp
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cifpp PATH_VARS CIFPP_DATA_DIR)
PATH_VARS CIFPP_DATA_DIR
) install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake"
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cifpp
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake" COMPONENT Devel)
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cifpp
COMPONENT Devel set_target_properties(
) cifpp
PROPERTIES VERSION ${PROJECT_VERSION}
set_target_properties(cifpp PROPERTIES SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
VERSION ${PROJECT_VERSION} INTERFACE_cifpp_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
INTERFACE_cifpp_MAJOR_VERSION ${PROJECT_VERSION_MAJOR}) set_property(
TARGET cifpp
set_property(TARGET cifpp APPEND PROPERTY APPEND
COMPATIBLE_INTERFACE_STRING cifpp_MAJOR_VERSION PROPERTY COMPATIBLE_INTERFACE_STRING cifpp_MAJOR_VERSION)
)
write_basic_package_version_file( write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake" "${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake"
VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion COMPATIBILITY AnyNewerVersion)
)
if(BUILD_TESTING) if(BUILD_TESTING)
# We're using the older version 2 of Catch2 # We're using the older version 2 of Catch2
FetchContent_Declare( FetchContent_Declare(
Catch2 Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v2.13.9 GIT_TAG v2.13.9)
)
FetchContent_MakeAvailable(Catch2)
FetchContent_MakeAvailable(Catch2)
list(
list(APPEND CIFPP_tests APPEND
unit-v2 CIFPP_tests
unit-3d unit-v2
format unit-3d
model format
rename-compound model
sugar rename-compound
spinner sugar
) spinner)
foreach(CIFPP_TEST IN LISTS CIFPP_tests) foreach(CIFPP_TEST IN LISTS CIFPP_tests)
set(CIFPP_TEST "${CIFPP_TEST}-test") set(CIFPP_TEST "${CIFPP_TEST}-test")
set(CIFPP_TEST_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/test/${CIFPP_TEST}.cpp") set(CIFPP_TEST_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/test/${CIFPP_TEST}.cpp")
add_executable(${CIFPP_TEST} ${CIFPP_TEST_SOURCE} "${CMAKE_CURRENT_SOURCE_DIR}/test/test-main.cpp") add_executable(
${CIFPP_TEST} ${CIFPP_TEST_SOURCE}
target_link_libraries(${CIFPP_TEST} PRIVATE Threads::Threads cifpp::cifpp Catch2::Catch2) "${CMAKE_CURRENT_SOURCE_DIR}/test/test-main.cpp")
target_include_directories(${CIFPP_TEST} PRIVATE "${EIGEN_INCLUDE_DIR}")
target_link_libraries(${CIFPP_TEST} PRIVATE Threads::Threads cifpp::cifpp
if(MSVC) Catch2::Catch2)
# Specify unwind semantics so that MSVC knowns how to handle exceptions target_include_directories(${CIFPP_TEST} PRIVATE "${EIGEN_INCLUDE_DIR}")
target_compile_options(${CIFPP_TEST} PRIVATE /EHsc)
endif() if(MSVC)
# Specify unwind semantics so that MSVC knowns how to handle exceptions
add_custom_target("run-${CIFPP_TEST}" DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Run${CIFPP_TEST}.touch ${CIFPP_TEST}) target_compile_options(${CIFPP_TEST} PRIVATE /EHsc)
endif()
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Run${CIFPP_TEST}.touch add_custom_target(
COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir ${CMAKE_CURRENT_SOURCE_DIR}/test) "run-${CIFPP_TEST}"
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Run${CIFPP_TEST}.touch ${CIFPP_TEST})
add_test(NAME ${CIFPP_TEST}
COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir ${CMAKE_CURRENT_SOURCE_DIR}/test) add_custom_command(
endforeach() OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Run${CIFPP_TEST}.touch
COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir
${CMAKE_CURRENT_SOURCE_DIR}/test)
add_test(NAME ${CIFPP_TEST} COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir
${CMAKE_CURRENT_SOURCE_DIR}/test)
endforeach()
endif() endif()
# Optionally install the update scripts for CCD and dictionary files # Optionally install the update scripts for CCD and dictionary files
if(CIFPP_INSTALL_UPDATE_SCRIPT) if(CIFPP_INSTALL_UPDATE_SCRIPT)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_SYSTEM_NAME} STREQUAL "GNU") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_SYSTEM_NAME} STREQUAL
if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local") "GNU")
set(CIFPP_CRON_DIR "/etc/cron.weekly" CACHE PATH "The cron directory, for the update script") if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local")
else() set(CIFPP_CRON_DIR
set(CIFPP_CRON_DIR "${CIFPP_ETC_DIR}/cron.weekly" CACHE PATH "The cron directory, for the update script") "/etc/cron.weekly"
endif() CACHE PATH "The cron directory, for the update script")
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") else()
set(CIFPP_CRON_DIR "${CIFPP_ETC_DIR}/periodic/weekly" CACHE PATH "The cron directory, for the update script") set(CIFPP_CRON_DIR
else() "${CIFPP_ETC_DIR}/cron.weekly"
message(FATAL_ERROR "Don't know where to install the update script") CACHE PATH "The cron directory, for the update script")
endif() endif()
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
configure_file(${PROJECT_SOURCE_DIR}/tools/update-libcifpp-data.in update-libcifpp-data @ONLY) set(CIFPP_CRON_DIR
install( "${CIFPP_ETC_DIR}/periodic/weekly"
FILES ${CMAKE_CURRENT_BINARY_DIR}/update-libcifpp-data CACHE PATH "The cron directory, for the update script")
DESTINATION ${CIFPP_CRON_DIR} else()
PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ message(FATAL_ERROR "Don't know where to install the update script")
) endif()
install(DIRECTORY DESTINATION ${CIFPP_CACHE_DIR}) configure_file(${PROJECT_SOURCE_DIR}/tools/update-libcifpp-data.in
update-libcifpp-data @ONLY)
# a config file, to make it complete install(
if(NOT EXISTS "${CIFPP_ETC_DIR}/libcifpp.conf") FILES ${CMAKE_CURRENT_BINARY_DIR}/update-libcifpp-data
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf [[# Uncomment the next line to enable automatic updates DESTINATION ${CIFPP_CRON_DIR}
PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE
WORLD_READ)
install(DIRECTORY DESTINATION ${CIFPP_CACHE_DIR})
# a config file, to make it complete
if(NOT EXISTS "${CIFPP_ETC_DIR}/libcifpp.conf")
file(
WRITE ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf
[[# Uncomment the next line to enable automatic updates
# update=true # update=true
]]) ]])
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf DESTINATION "${CIFPP_ETC_DIR}") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf
install(CODE "message(\"A configuration file has been written to ${CIFPP_ETC_DIR}/libcifpp.conf, please edit this file to enable automatic updates\")") DESTINATION "${CIFPP_ETC_DIR}")
install(
CODE "message(\"A configuration file has been written to ${CIFPP_ETC_DIR}/libcifpp.conf, please edit this file to enable automatic updates\")"
)
install(DIRECTORY DESTINATION "${CIFPP_ETC_DIR}/libcifpp/cache-update.d") install(DIRECTORY DESTINATION "${CIFPP_ETC_DIR}/libcifpp/cache-update.d")
endif() endif()
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}") target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
endif() endif()
if(BUILD_DOCUMENTATION) if(BUILD_DOCUMENTATION)
add_subdirectory(docs) add_subdirectory(docs)
endif() endif()
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
......
Version 6.0.1
- Add formula weight to entity in pdb2cif
- Change order of categories inside a datablock to match order in file
- Change default order to write out categories in a file based on
parent/child relationship
Version 6.0.0 Version 6.0.0
- Drop the use of CCP4's monomer library for compound information - Drop the use of CCP4's monomer library for compound information
......
...@@ -18,7 +18,7 @@ Loading Resources ...@@ -18,7 +18,7 @@ Loading Resources
No matter where the resource is located, you should always use the single libcifpp API call :cpp:func:`cif::load_resource` to load them. This function returns a *std::istream* wrapped inside a *std::unique_ptr*. No matter where the resource is located, you should always use the single libcifpp API call :cpp:func:`cif::load_resource` to load them. This function returns a *std::istream* wrapped inside a *std::unique_ptr*.
The order in which resources are search for is: The order in which resources are searched for is:
* Use the resource that was defined by calling :cpp:func:`cif::add_file_resource` * Use the resource that was defined by calling :cpp:func:`cif::add_file_resource`
for this name. for this name.
......
...@@ -168,14 +168,18 @@ class compound ...@@ -168,14 +168,18 @@ class compound
private: private:
friend class compound_factory_impl; friend class compound_factory_impl;
friend class local_compound_factory_impl;
compound(cif::datablock &db); compound(cif::datablock &db);
compound(cif::datablock &db, const std::string &id, const std::string &name, const std::string &type, const std::string &group); compound(cif::datablock &db, int);
std::string m_id; std::string m_id;
std::string m_name; std::string m_name;
std::string m_type; std::string m_type;
std::string m_group; /// @cond
// m_group is no longer used
std::string __m_group;
/// @endcond
std::string m_formula; std::string m_formula;
float m_formula_weight = 0; float m_formula_weight = 0;
int m_formal_charge = 0; int m_formal_charge = 0;
...@@ -214,6 +218,20 @@ class compound_factory ...@@ -214,6 +218,20 @@ class compound_factory
/// Override any previously loaded dictionary with @a inDictFile /// Override any previously loaded dictionary with @a inDictFile
void push_dictionary(const std::filesystem::path &inDictFile); void push_dictionary(const std::filesystem::path &inDictFile);
/** @brief Override any previously loaded dictionary with the data in @a file
*
* @note experimental feature
*
* Load the file @a file as a source for compound information. This may
* be e.g. a regular mmCIF file with extra files containing compound
* information.
*
* Be carefull to remove the block again, best use @ref cif::compound_source
* as a stack based object.
*/
void push_dictionary(const file &file);
/// Remove the last pushed dictionary /// Remove the last pushed dictionary
void pop_dictionary(); void pop_dictionary();
...@@ -251,4 +269,35 @@ class compound_factory ...@@ -251,4 +269,35 @@ class compound_factory
std::shared_ptr<compound_factory_impl> m_impl; std::shared_ptr<compound_factory_impl> m_impl;
}; };
// --------------------------------------------------------------------
/**
* @brief Stack based source for compound info.
*
* Use this class to temporarily add a compound source to the
* compound_factory.
*
* @code{.cpp}
* cif::file f("1cbs-with-custom-rea.cif");
* cif::compound_source cs(f);
*
* auto &cf = cif::compound_factory::instance();
* auto rea_compound = cf.create("REA");
* @endcode
*/
class compound_source
{
public:
compound_source(const cif::file &file)
{
cif::compound_factory::instance().push_dictionary(file);
}
~compound_source()
{
cif::compound_factory::instance().pop_dictionary();
}
};
} // namespace cif } // namespace cif
...@@ -290,6 +290,13 @@ class row_handle ...@@ -290,6 +290,13 @@ class row_handle
return operator[](get_column_ix(column)).template as<T>(); return operator[](get_column_ix(column)).template as<T>();
} }
/// \brief Get the value of column @a column cast to type @a T
template <typename T>
T get(std::string_view column) const
{
return operator[](get_column_ix(column)).template as<T>();
}
/// \brief assign each of the columns named in @a values to their respective value /// \brief assign each of the columns named in @a values to their respective value
void assign(const std::vector<item> &values) void assign(const std::vector<item> &values)
{ {
......
...@@ -142,8 +142,6 @@ compound::compound(cif::datablock &db) ...@@ -142,8 +142,6 @@ compound::compound(cif::datablock &db)
// The name should not contain newline characters since that triggers validation errors later on // The name should not contain newline characters since that triggers validation errors later on
cif::replace_all(m_name, "\n", ""); cif::replace_all(m_name, "\n", "");
m_group = "non-polymer";
auto &chemCompAtom = db["chem_comp_atom"]; auto &chemCompAtom = db["chem_comp_atom"];
for (auto row : chemCompAtom) for (auto row : chemCompAtom)
{ {
...@@ -153,6 +151,9 @@ compound::compound(cif::datablock &db) ...@@ -153,6 +151,9 @@ compound::compound(cif::datablock &db)
row.get("atom_id", "type_symbol", "charge", "pdbx_aromatic_flag", "pdbx_leaving_atom_flag", "pdbx_stereo_config", row.get("atom_id", "type_symbol", "charge", "pdbx_aromatic_flag", "pdbx_leaving_atom_flag", "pdbx_stereo_config",
"model_Cartn_x", "model_Cartn_y", "model_Cartn_z"); "model_Cartn_x", "model_Cartn_y", "model_Cartn_z");
atom.type_symbol = atom_type_traits(type_symbol).type(); atom.type_symbol = atom_type_traits(type_symbol).type();
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)); m_atoms.push_back(std::move(atom));
} }
...@@ -163,17 +164,28 @@ compound::compound(cif::datablock &db) ...@@ -163,17 +164,28 @@ compound::compound(cif::datablock &db)
compound_bond bond; compound_bond bond;
std::string valueOrder; std::string valueOrder;
cif::tie(bond.atom_id[0], bond.atom_id[1], valueOrder, bond.aromatic, bond.stereo_config) = row.get("atom_id_1", "atom_id_2", "value_order", "pdbx_aromatic_flag", "pdbx_stereo_config"); cif::tie(bond.atom_id[0], bond.atom_id[1], valueOrder, bond.aromatic, bond.stereo_config) = row.get("atom_id_1", "atom_id_2", "value_order", "pdbx_aromatic_flag", "pdbx_stereo_config");
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)); m_bonds.push_back(std::move(bond));
} }
} }
compound::compound(cif::datablock &db, const std::string &id, const std::string &name, const std::string &type, const std::string &group) compound::compound(cif::datablock &db, int)
: m_id(id)
, m_name(name)
, m_type(type)
, m_group(group)
{ {
auto &chemComp = db["chem_comp"];
if (chemComp.size() != 1)
throw std::runtime_error("Invalid compound file, chem_comp should contain a single row");
cif::tie(m_id, m_name) =
chemComp.front().get("id", "name");
cif::trim(m_name);
m_type = "NON-POLYMER";
auto &chemCompAtom = db["chem_comp_atom"]; auto &chemCompAtom = db["chem_comp_atom"];
for (auto row : chemCompAtom) for (auto row : chemCompAtom)
{ {
...@@ -184,7 +196,6 @@ compound::compound(cif::datablock &db, const std::string &id, const std::string ...@@ -184,7 +196,6 @@ compound::compound(cif::datablock &db, const std::string &id, const std::string
atom.type_symbol = atom_type_traits(type_symbol).type(); atom.type_symbol = atom_type_traits(type_symbol).type();
m_formal_charge += atom.charge; m_formal_charge += atom.charge;
m_formula_weight += atom_type_traits(atom.type_symbol).weight();
m_atoms.push_back(std::move(atom)); m_atoms.push_back(std::move(atom));
} }
...@@ -209,11 +220,39 @@ compound::compound(cif::datablock &db, const std::string &id, const std::string ...@@ -209,11 +220,39 @@ compound::compound(cif::datablock &db, const std::string &id, const std::string
else else
{ {
if (cif::VERBOSE > 0) if (cif::VERBOSE > 0)
std::cerr << "Unimplemented chem_comp_bond.type " << btype << " in " << id << '\n'; std::cerr << "Unimplemented chem_comp_bond.type " << btype << " in " << db.name() << '\n';
bond.type = bond_type::sing; bond.type = bond_type::sing;
} }
m_bonds.push_back(std::move(bond)); m_bonds.push_back(std::move(bond));
} }
// reconstruct a formula and weight
m_formula_weight = 0;
std::map<atom_type, int> f;
for (auto &atom : m_atoms)
f[atom.type_symbol] += 1;
if (f.count(atom_type::C))
{
atom_type_traits att(atom_type::C);
m_formula += att.symbol() + std::to_string(f[atom_type::C]) + ' ';
m_formula_weight += att.weight() * f[atom_type::C];
}
for (const auto &[type, count] : f)
{
if (type == atom_type::C)
continue;
atom_type_traits att(type);
m_formula += att.symbol() + std::to_string(count) + ' ';
m_formula_weight += att.weight() * count;
}
if (not m_formula.empty())
m_formula.pop_back();
} }
compound_atom compound::get_atom_by_atom_id(const std::string &atom_id) const compound_atom compound::get_atom_by_atom_id(const std::string &atom_id) const
...@@ -260,13 +299,12 @@ float compound::bond_length(const std::string &atomId_1, const std::string &atom ...@@ -260,13 +299,12 @@ float compound::bond_length(const std::string &atomId_1, const std::string &atom
auto a = get_atom_by_atom_id(atomId_1); auto a = get_atom_by_atom_id(atomId_1);
auto b = get_atom_by_atom_id(atomId_2); auto b = get_atom_by_atom_id(atomId_2);
result = distance(point{a.x, a.y, a.z}, point{b.x, b.y, b.z}); result = distance(point{ a.x, a.y, a.z }, point{ b.x, b.y, b.z });
} }
return result; return result;
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// known amino acids and bases // known amino acids and bases
...@@ -316,7 +354,7 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto ...@@ -316,7 +354,7 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
compound_factory_impl(); compound_factory_impl();
compound_factory_impl(const fs::path &file, std::shared_ptr<compound_factory_impl> next); compound_factory_impl(const fs::path &file, std::shared_ptr<compound_factory_impl> next);
~compound_factory_impl() virtual ~compound_factory_impl()
{ {
for (auto c : m_compounds) for (auto c : m_compounds)
delete c; delete c;
...@@ -373,13 +411,15 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto ...@@ -373,13 +411,15 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
os << "CCD components.cif resource\n"; os << "CCD components.cif resource\n";
else else
os << "CCD components file: " << std::quoted(m_file.string()) << '\n'; os << "CCD components file: " << std::quoted(m_file.string()) << '\n';
if (m_next) if (m_next)
m_next->describe(os); m_next->describe(os);
} }
private: protected:
compound *create(const std::string &id); compound_factory_impl(std::shared_ptr<compound_factory_impl> next);
virtual compound *create(const std::string &id);
std::shared_timed_mutex mMutex; std::shared_timed_mutex mMutex;
...@@ -395,10 +435,15 @@ compound_factory_impl::compound_factory_impl() ...@@ -395,10 +435,15 @@ compound_factory_impl::compound_factory_impl()
{ {
} }
compound_factory_impl::compound_factory_impl(std::shared_ptr<compound_factory_impl> next)
: m_next(next)
{
}
compound_factory_impl::compound_factory_impl(const fs::path &file, std::shared_ptr<compound_factory_impl> next) compound_factory_impl::compound_factory_impl(const fs::path &file, std::shared_ptr<compound_factory_impl> next)
: m_file(file) : compound_factory_impl(next)
, m_next(next)
{ {
m_file = file;
} }
compound *compound_factory_impl::create(const std::string &id) compound *compound_factory_impl::create(const std::string &id)
...@@ -476,6 +521,45 @@ compound *compound_factory_impl::create(const std::string &id) ...@@ -476,6 +521,45 @@ compound *compound_factory_impl::create(const std::string &id)
// -------------------------------------------------------------------- // --------------------------------------------------------------------
class local_compound_factory_impl : public compound_factory_impl
{
public:
local_compound_factory_impl(const cif::file &file, std::shared_ptr<compound_factory_impl> next)
: compound_factory_impl(next)
, m_local_file(file)
{
}
compound *create(const std::string &id) override;
private:
const cif::file &m_local_file;
};
compound *local_compound_factory_impl::create(const std::string &id)
{
compound *result = nullptr;
for (auto &db : m_local_file)
{
if (db.name() == "comp_" + id)
{
cif::datablock db_copy(db);
result = new compound(db_copy, 1);
std::shared_lock lock(mMutex);
m_compounds.push_back(result);
break;
}
}
return result;
}
// --------------------------------------------------------------------
std::unique_ptr<compound_factory> compound_factory::s_instance; std::unique_ptr<compound_factory> compound_factory::s_instance;
thread_local std::unique_ptr<compound_factory> compound_factory::tl_instance; thread_local std::unique_ptr<compound_factory> compound_factory::tl_instance;
bool compound_factory::s_use_thread_local_instance; bool compound_factory::s_use_thread_local_instance;
...@@ -553,6 +637,18 @@ void compound_factory::push_dictionary(const fs::path &inDictFile) ...@@ -553,6 +637,18 @@ void compound_factory::push_dictionary(const fs::path &inDictFile)
} }
} }
void compound_factory::push_dictionary(const cif::file &inDictFile)
{
try
{
m_impl.reset(new local_compound_factory_impl(inDictFile, m_impl));
}
catch (const std::exception &)
{
std::throw_with_nested(std::runtime_error("Error loading dictionary from local mmCIF file"));
}
}
void compound_factory::pop_dictionary() void compound_factory::pop_dictionary()
{ {
if (m_impl) if (m_impl)
...@@ -584,25 +680,26 @@ void compound_factory::report_missing_compound(const std::string &compound_id) ...@@ -584,25 +680,26 @@ void compound_factory::report_missing_compound(const std::string &compound_id)
{ {
using namespace cif::colour; using namespace cif::colour;
std::clog << "\n" << cif::coloured("Configuration error:", white, red) << "\n\n" std::clog << "\n"
<< cif::coloured("Configuration error:", white, red) << "\n\n"
<< "The attempt to retrieve compound information for " << std::quoted(compound_id) << " failed.\n\n" << "The attempt to retrieve compound information for " << std::quoted(compound_id) << " failed.\n\n"
<< "This information is searched for in a CCD file called components.cif or\n" << "This information is searched for in a CCD file called components.cif or\n"
<< "components.cif.gz which should be located in one of the following directories:\n\n"; << "components.cif.gz which should be located in one of the following directories:\n\n";
cif::list_data_directories(std::clog); cif::list_data_directories(std::clog);
std::clog << "\n(Note that you can add a directory to the search paths by setting the \n" std::clog << "\n(Note that you can add a directory to the search paths by setting the \n"
<< "LIBCIFPP_DATA_DIR environmental variable)\n\n"; << "LIBCIFPP_DATA_DIR environmental variable)\n\n";
#if defined(CACHE_DIR) #if defined(CACHE_DIR)
std::clog << "On Linux an optional cron script might have been installed that automatically updates\n" std::clog << "On Linux an optional cron script might have been installed that automatically updates\n"
<< "components.cif and mmCIF dictionary files. This script only works when the file\n" << "components.cif and mmCIF dictionary files. This script only works when the file\n"
<< "libcifpp.conf contains an uncommented line with the text:\n\n" << "libcifpp.conf contains an uncommented line with the text:\n\n"
<< "update=true\n\n" << "update=true\n\n"
<< "If you do not have a working cron script, you can manually update the files\n" << "If you do not have a working cron script, you can manually update the files\n"
<< "in /var/cache/libcifpp using the following commands:\n\n" << "in /var/cache/libcifpp using the following commands:\n\n"
<< "curl -o " << CACHE_DIR << "/components.cif https://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif.gz\n" << "curl -o " << CACHE_DIR << "/components.cif https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz\n"
<< "curl -o " << CACHE_DIR << "/mmcif_pdbx.dic https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic.gz\n" << "curl -o " << CACHE_DIR << "/mmcif_pdbx.dic https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic.gz\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://github.com/ihmwg/ModelCIF/raw/master/dist/mmcif_ma.dic\n\n";
#endif #endif
...@@ -613,9 +710,9 @@ void compound_factory::report_missing_compound(const std::string &compound_id) ...@@ -613,9 +710,9 @@ void compound_factory::report_missing_compound(const std::string &compound_id)
} }
else else
std::clog << "No compound factory objects are created since none of the data sources is found.\n"; std::clog << "No compound factory objects are created since none of the data sources is found.\n";
cif::list_file_resources(std::clog); cif::list_file_resources(std::clog);
std::clog.flush(); std::clog.flush();
} }
} }
......
...@@ -91,7 +91,7 @@ bool datablock::validate_links() const ...@@ -91,7 +91,7 @@ bool datablock::validate_links() const
for (auto &cat : *this) for (auto &cat : *this)
result = cat.validate_links() and result; result = cat.validate_links() and result;
return result; return result;
} }
...@@ -158,11 +158,12 @@ std::tuple<datablock::iterator, bool> datablock::emplace(std::string_view name) ...@@ -158,11 +158,12 @@ std::tuple<datablock::iterator, bool> datablock::emplace(std::string_view name)
if (is_new) if (is_new)
{ {
auto &c = emplace_front(name); auto &c = emplace_back(name);
c.set_validator(m_validator, *this); c.set_validator(m_validator, *this);
} }
return std::make_tuple(begin(), is_new); assert(end() != begin());
return std::make_tuple(std::prev(end()), is_new);
} }
std::vector<std::string> datablock::get_tag_order() const std::vector<std::string> datablock::get_tag_order() const
...@@ -171,14 +172,16 @@ std::vector<std::string> datablock::get_tag_order() const ...@@ -171,14 +172,16 @@ std::vector<std::string> datablock::get_tag_order() const
// for entry and audit_conform on top // for entry and audit_conform on top
auto ci = find_if(begin(), end(), [](const category &cat) { return cat.name() == "entry"; }); auto ci = find_if(begin(), end(), [](const category &cat)
{ return cat.name() == "entry"; });
if (ci != end()) if (ci != end())
{ {
auto cto = ci->get_tag_order(); auto cto = ci->get_tag_order();
result.insert(result.end(), cto.begin(), cto.end()); result.insert(result.end(), cto.begin(), cto.end());
} }
ci = find_if(begin(), end(), [](const category &cat) { return cat.name() == "audit_conform"; }); ci = find_if(begin(), end(), [](const category &cat)
{ return cat.name() == "audit_conform"; });
if (ci != end()) if (ci != end())
{ {
auto cto = ci->get_tag_order(); auto cto = ci->get_tag_order();
...@@ -196,42 +199,131 @@ std::vector<std::string> datablock::get_tag_order() const ...@@ -196,42 +199,131 @@ std::vector<std::string> datablock::get_tag_order() const
return result; return result;
} }
void datablock::write(std::ostream &os) const namespace
{ {
os << "data_" << m_name << '\n' using elem_t = std::tuple<std::string, int, bool>;
<< "# \n"; using cat_order_t = std::vector<elem_t>;
using iter_t = cat_order_t::iterator;
// mmcif support, sort of. First write the 'entry' Category constexpr inline int get_count(iter_t i)
// and if it exists, _AND_ we have a Validator, write out the {
// audit_conform record. return std::get<1>(*i);
}
for (auto &cat : *this) constexpr inline bool is_on_stack(iter_t i)
{ {
if (cat.name() != "entry") return std::get<2>(*i);
continue; }
cat.write(os); void calculate_cat_order(cat_order_t &cat_order, iter_t i, const validator &validator)
{
if (i == cat_order.end() or get_count(i) >= 0)
return;
auto &&[cat, count, on_stack] = *i;
on_stack = true;
int parent_count = 0;
break; for (auto link : validator.get_links_for_child(cat))
{
auto ei = std::find_if(cat_order.begin(), cat_order.end(), [parent = link->m_parent_category](elem_t &a)
{ return std::get<0>(a) == parent; });
if (ei == cat_order.end())
continue;
if (not is_on_stack(ei))
calculate_cat_order(cat_order, ei, validator);
parent_count += get_count(ei);
}
count = parent_count + 1;
} }
}
// If the dictionary declares an audit_conform category, put it in, void datablock::write(std::ostream &os) const
// but only if it does not exist already! {
if (get("audit_conform")) os << "data_" << m_name << '\n'
get("audit_conform")->write(os); << "# \n";
else if (m_validator != nullptr and m_validator->get_validator_for_category("audit_conform") != nullptr)
if (m_validator and size() > 0)
{ {
category auditConform("audit_conform"); // If the dictionary declares an audit_conform category, put it in,
auditConform.emplace({ // but only if it does not exist already!
{"dict_name", m_validator->name()}, if (get("audit_conform") == nullptr and m_validator->get_validator_for_category("audit_conform") != nullptr)
{"dict_version", m_validator->version()}}); {
auditConform.write(os); category auditConform("audit_conform");
} auditConform.emplace({ { "dict_name", m_validator->name() },
{ "dict_version", m_validator->version() } });
auditConform.write(os);
}
for (auto &cat : *this) // base order on parent child relationships, parents first
cat_order_t cat_order;
for (auto &cat : *this)
cat_order.emplace_back(cat.name(), -1, false);
for (auto i = cat_order.begin(); i != cat_order.end(); ++i)
calculate_cat_order(cat_order, i, *m_validator);
std::sort(cat_order.begin(), cat_order.end(), [](const elem_t &a, const elem_t &b)
{
const auto &[cat_a, count_a, on_stack_a] = a;
const auto &[cat_b, count_b, on_stack_b] = b;
int d = 0;
if (cat_a == "audit_conform")
d = -1;
else if (cat_b == "audit_conform")
d = 1;
else if (cat_a == "entry")
d = -1;
else if (cat_b == "entry")
d = 1;
else
{
d = std::get<1>(a) - std::get<1>(b);
if (d == 0)
d = cat_b.compare(cat_a);
}
return d < 0; });
for (auto &&[cat, count, on_stack] : cat_order)
get(cat)->write(os);
}
else
{ {
if (cat.name() != "entry" and cat.name() != "audit_conform") // mmcif support, sort of. First write the 'entry' Category
// and if it exists, _AND_ we have a Validator, write out the
// audit_conform record.
for (auto &cat : *this)
{
if (cat.name() != "entry")
continue;
cat.write(os); cat.write(os);
break;
}
// If the dictionary declares an audit_conform category, put it in,
// but only if it does not exist already!
if (get("audit_conform"))
get("audit_conform")->write(os);
for (auto &cat : *this)
{
if (cat.name() != "entry" and cat.name() != "audit_conform")
cat.write(os);
}
} }
} }
...@@ -337,7 +429,7 @@ bool datablock::operator==(const datablock &rhs) const ...@@ -337,7 +429,7 @@ bool datablock::operator==(const datablock &rhs) const
++catA_i; ++catA_i;
else else
{ {
if (not (*dbA.get(*catA_i) == *dbB.get(*catB_i))) if (not(*dbA.get(*catA_i) == *dbB.get(*catB_i)))
return false; return false;
++catA_i; ++catA_i;
++catB_i; ++catB_i;
...@@ -347,4 +439,4 @@ bool datablock::operator==(const datablock &rhs) const ...@@ -347,4 +439,4 @@ bool datablock::operator==(const datablock &rhs) const
return true; return true;
} }
} // namespace cif::cif } // namespace cif
\ No newline at end of file \ No newline at end of file
...@@ -173,11 +173,12 @@ std::tuple<file::iterator, bool> file::emplace(std::string_view name) ...@@ -173,11 +173,12 @@ std::tuple<file::iterator, bool> file::emplace(std::string_view name)
if (is_new) if (is_new)
{ {
auto &db = emplace_front(name); auto &db = emplace_back(name);
db.set_validator(m_validator); db.set_validator(m_validator);
} }
return std::make_tuple(begin(), is_new); assert(begin() != end());
return std::make_tuple(std::prev(end()), is_new);
} }
void file::load(const std::filesystem::path &p) void file::load(const std::filesystem::path &p)
......
...@@ -4511,6 +4511,47 @@ void PDBFileParser::ConstructEntities() ...@@ -4511,6 +4511,47 @@ void PDBFileParser::ConstructEntities()
} }
} }
} }
// Finish by calculating the formula_weight for each entity
for (auto entity : *getCategory("entity"))
{
auto entity_id = entity["id"].as<std::string>();
float formula_weight = 0;
if (entity["type"] == "polymer")
{
int n = 0;
for (std::string comp_id : getCategory("pdbx_poly_seq_scheme")->find<std::string>(cif::key("entity_id") == entity_id, "mon_id"))
{
auto compound = cif::compound_factory::instance().create(comp_id);
assert(compound);
if (not compound)
throw std::runtime_error("missing information for compound " + comp_id);
formula_weight += compound->formula_weight();
++n;
}
formula_weight -= (n - 1) * 18.015;
}
else if (entity["type"] == "water")
formula_weight = 18.015;
else
{
auto comp_id = getCategory("pdbx_nonpoly_scheme")->find_first<std::optional<std::string>>(cif::key("entity_id") == entity_id, "mon_id");
if (comp_id.has_value())
{
auto compound = cif::compound_factory::instance().create(*comp_id);
assert(compound);
if (not compound)
throw std::runtime_error("missing information for compound " + *comp_id);
formula_weight = compound->formula_weight();
}
}
if (formula_weight > 0)
entity["formula_weight"] = formula_weight;
}
} }
void PDBFileParser::ConstructSugarTrees(int &asymNr) void PDBFileParser::ConstructSugarTrees(int &asymNr)
......
...@@ -3468,3 +3468,22 @@ TEST_CASE("compound_not_found_test_1") ...@@ -3468,3 +3468,22 @@ TEST_CASE("compound_not_found_test_1")
auto cmp = cif::compound_factory::instance().create("&&&"); auto cmp = cif::compound_factory::instance().create("&&&");
REQUIRE(cmp == nullptr); REQUIRE(cmp == nullptr);
} }
// --------------------------------------------------------------------
// PDB2CIF tests
TEST_CASE("pdb2cif_formula_weight")
{
cif::compound_factory::instance().push_dictionary(gTestDir / "REA.cif");
cif::file a = cif::pdb::read(gTestDir / "pdb1cbs.ent.gz");
auto fw = a.front()["entity"].find1<float>(cif::key("id") == 1, "formula_weight");
CHECK(std::abs(fw - 15581.802f) < 0.1f);
fw = a.front()["entity"].find1<float>(cif::key("id") == 2, "formula_weight");
CHECK(fw == 300.435f);
fw = a.front()["entity"].find1<float>(cif::key("id") == 3, "formula_weight");
CHECK(fw == 18.015f);
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment