Compare commits

..

158 Commits

Author SHA1 Message Date
Maarten L. Hekkelman
50df250415 Merge branch 'develop' into trunk 2023-06-08 10:12:03 +02:00
Maarten L. Hekkelman
2409fc5b7b update changelog, version bump 2023-06-08 10:10:49 +02:00
Maarten L. Hekkelman
8a1184a24c Fix cif_id_for_number 2023-06-07 19:11:20 +02:00
Maarten L. Hekkelman
d2fbc54765 New cache location 2023-06-07 14:07:27 +02:00
Maarten L. Hekkelman
1bcb26ba75 extend validator
faster unique_id
2023-06-07 13:08:36 +02:00
Maarten L. Hekkelman
32f4749d84 faster cif parser 2023-06-07 11:19:35 +02:00
Maarten L. Hekkelman
da12be879a progress_bar consuming too much time 2023-06-07 09:15:17 +02:00
Maarten L. Hekkelman
94a38ad4e8 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2023-06-06 14:31:26 +02:00
Maarten L. Hekkelman
20ef79a172 for c++17, limited version of std::string_view 2023-06-06 14:30:11 +02:00
Maarten L. Hekkelman
92bf25476e Speed improvements 2023-06-06 14:12:21 +02:00
Maarten L. Hekkelman
b55e074dd7 reserve some token buffer space 2023-06-06 09:33:31 +02:00
Maarten L. Hekkelman
7b654a837d with reserved words automaton 2023-06-06 09:22:55 +02:00
Maarten L. Hekkelman
ae9d247d22 optimised the parser a bit 2023-06-05 13:43:31 +02:00
Maarten L. Hekkelman
16b7deafe8 Better is_unquoted_string test 2023-06-02 17:09:57 +02:00
Maarten L. Hekkelman
f2cfe28458 Update README 2023-05-31 15:56:50 +02:00
Maarten L. Hekkelman
2e8a52949e Update example and README 2023-05-31 15:54:53 +02:00
Maarten L. Hekkelman
441e142767 Update readme 2023-05-31 15:42:54 +02:00
Maarten L. Hekkelman
bf9bdd2aae Merge branch 'trunk' of github.com:PDB-REDO/libcifpp into trunk 2023-05-31 15:17:00 +02:00
Maarten L. Hekkelman
ce14593f0b improved loading resources from absolute path
better error reporting when loading dictionary
2023-05-31 15:16:10 +02:00
Maarten L. Hekkelman
1c02a451e1 improved has_atom_id
added couple of comparison operators to sym_op class
2023-05-16 13:55:07 +02:00
Maarten L. Hekkelman
448855a2d3 catch error in create entity for branch 2023-05-09 11:46:37 +02:00
Maarten L. Hekkelman
8ac8e89f2b Fix progress bar by removing conditional variable 2023-05-02 13:45:02 +02:00
Maarten L. Hekkelman
2281f59401 Remove struct_conn records as well in remove_branch 2023-05-02 13:44:36 +02:00
Maarten L. Hekkelman
4cb0673370 small change to matrix 2023-04-25 10:13:30 +02:00
Maarten L. Hekkelman
76c5706f7c moving to eigen3 eigensolver, fixing include and dependencies 2023-04-22 14:14:48 +02:00
Maarten L. Hekkelman
2bf4284ff4 cleanup 2023-04-21 14:52:12 +02:00
Maarten L. Hekkelman
d9e2fc97f3 Added missing spinner test 2023-04-21 14:50:22 +02:00
Maarten L. Hekkelman
85dfdf4174 Better progress bar 2023-04-21 14:49:54 +02:00
Martin Salinas
1bede3efda Removed unused argument warning (#36)
As argument rhs is not being used in that equals (should the equals function always return false), I added that flag so the compiler skips that warning.
2023-04-21 09:18:47 +02:00
Maarten L. Hekkelman
505f0fdd31 oops 2023-04-20 16:33:24 +02:00
Maarten L. Hekkelman
eed7ec3a4a Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2023-04-20 13:38:48 +02:00
Maarten L. Hekkelman
fdb057e0e2 for now, require eigen3
add inverse_symmetry_copy to crystal
2023-04-20 13:14:52 +02:00
Maarten L. Hekkelman
3fddd1a628 Using quaternions, when possible 2023-04-20 11:37:36 +02:00
Maarten L. Hekkelman
2440706b87 backup 2023-04-19 18:51:41 +02:00
Maarten L. Hekkelman
cf628fa95c backup 2023-04-19 18:36:33 +02:00
Maarten L. Hekkelman
2b0b47d20d Fix special case 2023-04-19 16:04:59 +02:00
Maarten L. Hekkelman
a8abf2804f attempt to use quaternions 2023-04-19 16:01:52 +02:00
Maarten L. Hekkelman
22d7757949 Introduced cif::crystal 2023-04-19 10:17:38 +02:00
Maarten L. Hekkelman
0b0d170c96 a bit of documentation 2023-04-19 09:57:49 +02:00
Maarten L. Hekkelman
1e8e9adf62 Merge branch 'trunk' into develop 2023-04-19 09:22:49 +02:00
Maarten L. Hekkelman
0f03fc31e0 added required include 2023-04-19 09:22:32 +02:00
Maarten L. Hekkelman
518432e0fb test data 2023-04-17 20:54:57 +02:00
Maarten L. Hekkelman
10ef3464ef Fix symmetry issue 2023-04-17 20:52:10 +02:00
Maarten L. Hekkelman
226abbd577 Merge branch 'develop' of s4.hekkelman.net:git-repo/libcifpp into develop 2023-04-17 18:56:46 +02:00
Maarten L. Hekkelman
8d66f42ab1 more test cases 2023-04-17 18:56:02 +02:00
Maarten L. Hekkelman
0f14d06f9a Added inverse symmetry operation 2023-04-14 19:38:39 +02:00
Maarten L. Hekkelman
c53be78496 symmetry fixes 2023-04-14 19:04:16 +02:00
Maarten L. Hekkelman
a38f31ce48 fix closest_symmetry_copy 2023-04-14 17:56:59 +02:00
Maarten L. Hekkelman
1258bd5047 eigen, fixed 2023-04-14 14:08:52 +02:00
Maarten L. Hekkelman
d25cbeb14c matrix eigen value work 2023-04-14 11:47:18 +02:00
Maarten L. Hekkelman
9b60a07fb6 calculating eigen values 2023-04-13 19:55:32 +02:00
Maarten L. Hekkelman
c0dd41ce50 added inverse symmetry operation 2023-04-13 15:49:15 +02:00
Maarten L. Hekkelman
4cff92bbcc symmetry operations now working correctly 2023-04-13 11:42:59 +02:00
Maarten L. Hekkelman
9aa8a223c7 symmetry work 2023-04-12 17:00:09 +02:00
Maarten L. Hekkelman
fb59adcfdd Fix symmetry rotational numbers 2023-04-12 10:59:23 +02:00
Maarten L. Hekkelman
4acca8a3e3 Merge branch 'trunk' into develop 2023-04-07 09:31:11 +02:00
Maarten L. Hekkelman
c1030d2b08 Merge branch 'MartinSalinas98-trunk' into trunk 2023-04-07 09:26:00 +02:00
Maarten L. Hekkelman
16a185c6c0 More include changes 2023-04-07 09:16:38 +02:00
Maarten L. Hekkelman
174e818bd0 Merge branch 'trunk' of github.com:MartinSalinas98/libcifpp into MartinSalinas98-trunk 2023-04-07 08:43:44 +02:00
Maarten L. Hekkelman
7f829bf5df Merge branch 'trunk' of github.com:PDB-REDO/libcifpp into trunk 2023-04-07 08:43:01 +02:00
Maarten L. Hekkelman
71908282bb merged from trunk 2023-04-07 08:42:46 +02:00
MartinSalinas98
db3ae446af Imported local files with relative path 2023-04-07 03:39:02 +02:00
Martin Salinas
bc7d291307 Merge branch 'PDB-REDO:trunk' into trunk 2023-04-07 03:33:57 +02:00
Maarten L. Hekkelman
cfd4702279 Fix memory leak 2023-04-05 20:46:18 +02:00
Maarten L. Hekkelman
54eefb546d Fix memory leak 2023-04-05 20:44:47 +02:00
Maarten L. Hekkelman
6af0d96a4e Fix memory leak in category 2023-04-05 20:28:47 +02:00
Maarten L. Hekkelman
eb50bee4a3 atom_type_traits changes 2023-04-04 19:19:30 +02:00
Maarten L. Hekkelman
b6143f3652 optimise load atom data 2023-03-30 20:49:03 +02:00
Maarten L. Hekkelman
348aa7afb6 fix test (use gTestDir) 2023-03-30 20:48:41 +02:00
Martin Salinas
66912b68cc Commented unused variable
```
/home/msalinas/Documents/standalone-installations/cifParsers/libcifpp/src/pdb/cif2pdb.cpp: In function ‘std::tuple<int, int> cif::pdb::WriteCoordinatesForModel(std::ostream&, const cif::datablock&, const std::map<std::__cxx11::basic_string<char>, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, std::set<std::__cxx11::basic_string<char> >&, int)’:
/home/msalinas/Documents/standalone-installations/cifParsers/libcifpp/src/pdb/cif2pdb.cpp:3362:15: warning: unused variable ‘pdbx_nonpoly_scheme’ [-Wunused-variable]
 3362 |         auto &pdbx_nonpoly_scheme = db["pdbx_nonpoly_scheme"];
      |         
```
This warning appeared while compiling the library. The use of that variable below has been commented, so I think it's appropiate to do the same thing with the unused variable.
2023-03-28 13:54:45 +02:00
Maarten L. Hekkelman
84dd218758 Merge branch 'MartinSalinas98-patch-1' into trunk 2023-03-28 11:33:05 +02:00
Maarten L. Hekkelman
106ae38976 Update readme 2023-03-28 11:32:11 +02:00
Maarten L. Hekkelman
f1a52245ea Merge branch 'patch-1' of github.com:MartinSalinas98/libcifpp into MartinSalinas98-patch-1 2023-03-28 11:27:48 +02:00
Maarten L. Hekkelman
cea38e5bb2 Merge branch 'trunk' into develop 2023-03-28 10:31:24 +02:00
Maarten L. Hekkelman
ed5aac358c libcifpp really requires zlib, not only private. 2023-03-28 10:27:58 +02:00
Maarten L. Hekkelman
5eb128251e Added category::find1<std::optional> 2023-03-27 10:36:47 +02:00
Maarten L. Hekkelman
cfa46ec954 Added model::has_atom_id 2023-03-23 14:32:21 +01:00
Martin Salinas
07cc60e264 Fixed installation commands
Installation commands in the readme cause an error when running last command `cmake --install .` because of the lack of sudo privileges.
The following commands don't require sudo to run successfully and install the library.
2023-03-23 11:42:10 +01:00
Maarten L. Hekkelman
90973dc547 version bump, update changelog 2023-03-22 12:39:16 +01:00
Maarten L. Hekkelman
12e3d71b00 fix construct_from_angle_axis 2023-03-21 19:49:21 +01:00
Maarten L. Hekkelman
9addc8f873 fix remove_atom
add create_water
2023-03-21 19:49:11 +01:00
Maarten L. Hekkelman
343465cef0 Added test for create_non_poly with initializers 2023-03-08 16:00:40 +01:00
Maarten L. Hekkelman
bec5159415 residue numbering in pdb, again... 2023-02-14 08:28:40 +01:00
Maarten L. Hekkelman
f8da8360e6 write twin info in pdb format 2023-02-14 08:27:13 +01:00
Maarten L. Hekkelman
fb2ad7b75d Fix in REMARK3 parser for more strict mmcif_pdbx dictionary 2023-02-10 16:24:53 +01:00
Maarten L. Hekkelman
24aa7a70e5 Fix writing pdbx_ens_id 2023-02-07 11:38:15 +01:00
Maarten L. Hekkelman
5ade3d6cdd Fixes in update data script 2023-02-06 16:10:41 +01:00
Maarten L. Hekkelman
0d8e548ffc pdb2cif and vv 2023-02-06 14:19:56 +01:00
Maarten L. Hekkelman
b09650812f oops 2023-02-06 06:48:57 +01:00
Maarten L. Hekkelman
acc9ad5c08 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2023-02-05 11:28:03 +01:00
Maarten L. Hekkelman
67b6c4bd27 update downloaded files only when needed and clean up afterwards 2023-02-05 11:27:54 +01:00
Maarten L. Hekkelman
7a1d3dbdfa Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2023-02-03 19:21:08 +01:00
Maarten L. Hekkelman
4bf10df0c5 include <array> 2023-02-03 19:20:40 +01:00
Maarten L. Hekkelman
d84faad109 Accept X as alias for atom symbol Nn 2023-02-02 11:23:40 +01:00
Maarten L. Hekkelman
e01ace7ea4 write auth_seq_num as well as pdb_seq_num for nonpolies 2023-02-02 09:49:21 +01:00
Maarten L. Hekkelman
e004e1591e fix cron script 2023-02-02 09:35:44 +01:00
Maarten L. Hekkelman
4613084e1b find_first, find_min, find_max, count added
PDB writing changed for auth_seq_num
version bump
2023-02-01 13:46:08 +01:00
Maarten L. Hekkelman
637b795a8f Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2023-02-01 09:00:51 +01:00
Maarten L. Hekkelman
4de981a3c0 better handling of missing residues in pdb2cif 2023-01-31 20:32:57 +01:00
Maarten L. Hekkelman
15db026e27 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2023-01-31 19:10:44 +01:00
Maarten L. Hekkelman
d88d520553 Use std::experimental::is_detected from libzeep, if needed
version bump
2023-01-25 17:06:47 +01:00
Maarten L. Hekkelman
46cd98ea1d Dependencies and share location in Win 2023-01-25 17:01:39 +01:00
Maarten L. Hekkelman
d10328d891 Use the zeep implementation of std::experimental::is_detected 2023-01-25 16:31:13 +01:00
Maarten L. Hekkelman
e418a17256 Version bump 2023-01-25 15:01:28 +01:00
Maarten L. Hekkelman
627d3b9df2 export by default, fixes for MSVC 2023-01-25 11:23:08 +01:00
Maarten L. Hekkelman
ba28ade414 clean up 2023-01-25 10:45:08 +01:00
Maarten L. Hekkelman
7c11130357 explicitly export what needs to be exported 2023-01-25 10:07:54 +01:00
Maarten L. Hekkelman
151915beea Merge branch 'exports' into develop 2023-01-25 09:44:04 +01:00
Maarten L. Hekkelman
4f9aacb338 Merge branch 'trunk' into develop 2023-01-25 09:41:59 +01:00
Maarten L. Hekkelman
1f8e491ddc generate exports header 2023-01-25 09:41:43 +01:00
Maarten L. Hekkelman
05cfa92182 revert version number 2023-01-17 14:16:36 +01:00
Maarten L. Hekkelman
e8031aeb49 Fix is_cis 2023-01-17 14:12:33 +01:00
Maarten L. Hekkelman
85885406aa Fix sugar test 2023-01-17 14:10:50 +01:00
Maarten L. Hekkelman
636f17d78d Update changelog 2023-01-17 14:10:38 +01:00
Maarten L. Hekkelman
29559a5339 Fix is_cis 2023-01-17 13:51:46 +01:00
Maarten L. Hekkelman
19f2fd75c9 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2023-01-04 17:14:33 +01:00
Maarten L. Hekkelman
8a60bae335 non-throwing remove_residue
add chem_comp for sugars
2023-01-04 17:05:48 +01:00
Maarten L. Hekkelman
fa5ff60550 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2023-01-03 21:31:09 +01:00
Maarten L. Hekkelman
f6e0568964 start reconstructing 2023-01-03 21:30:17 +01:00
Maarten L. Hekkelman
fa27a11fea some quaternion additions 2023-01-03 16:45:53 +01:00
Maarten L. Hekkelman
19706559cb Merge branch 'trunk' of github.com:PDB-REDO/libcifpp into trunk 2023-01-02 14:06:16 +01:00
Maarten L. Hekkelman
0a06a0a51d Fix cron script 2023-01-02 14:06:09 +01:00
Maarten L. Hekkelman
b045177734 less nervous progress bar 2023-01-02 14:05:47 +01:00
Maarten L. Hekkelman
7ee5fa8765 some quaternion tests added 2023-01-02 14:05:31 +01:00
Maarten L. Hekkelman
3e690048a6 sugar work 2023-01-02 14:04:56 +01:00
Maarten L. Hekkelman
7ec3bfea9f 3d work 2023-01-02 14:04:41 +01:00
Maarten L. Hekkelman
098f3fd496 FIx compound factory stacking 2022-12-21 21:14:41 +01:00
Maarten L. Hekkelman
5476eef049 some extensions for sugar tree building 2022-12-21 16:33:33 +01:00
Maarten L. Hekkelman
33c1eea9a1 Fix copy construction, do not copy the links 2022-12-19 17:40:43 +01:00
Maarten L. Hekkelman
d3432ed87c merging in fixes from develop branch 2022-12-15 09:05:51 +01:00
Maarten L. Hekkelman
f05363ea93 duh 2022-12-15 08:51:05 +01:00
Maarten L. Hekkelman
77389c20a4 Fix equals in condition_impl, columns might be unknown 2022-12-15 08:49:49 +01:00
Maarten L. Hekkelman
7c5f1ba85e Merge branch 'trunk' into develop 2022-12-14 10:49:56 +01:00
Maarten L. Hekkelman
e7c34cc15c Merge changes 2022-12-14 10:47:20 +01:00
Maarten L. Hekkelman
72fd03a6b2 formatting, fix in is_unquoted test 2022-12-13 16:48:09 +01:00
Maarten L. Hekkelman
61f464ae4d Merge branch 'trunk' of github.com:PDB-REDO/libcifpp into trunk 2022-12-13 16:08:16 +01:00
Maarten L. Hekkelman
19cdf66f10 fix operator or for conditions (equals or empty) 2022-12-13 16:05:23 +01:00
Maarten L. Hekkelman
0c036df6a8 fix operator or for conditions (equals or empty) 2022-12-13 16:02:55 +01:00
Maarten L. Hekkelman
4c1b9d83d1 Fix memory leak 2022-12-13 15:49:11 +01:00
Maarten L. Hekkelman
b976b4657b Optimised erase III 2022-12-13 15:41:57 +01:00
Maarten L. Hekkelman
eba04950d5 Optimised erase II 2022-12-13 11:31:56 +01:00
Maarten L. Hekkelman
0c70df27ec Optimised erase 2022-12-13 10:38:33 +01:00
Maarten L. Hekkelman
d83f34722b Accept and fix incorrect SEQRES 2022-12-13 09:50:40 +01:00
Maarten L. Hekkelman
652b6021d3 improved parser. is_non_quoted string 2022-12-07 16:58:54 +01:00
Maarten L. Hekkelman
7fe9c87b6e Some small fixes for windows 2022-12-07 15:39:26 +01:00
Maarten L. Hekkelman
9b2ae6d7fd Fix update script, order is important 2022-11-28 08:48:13 +01:00
Maarten L. Hekkelman
8fd5b9a34b Remove PATH_MAX to enable compilation on Debian/hurd 2022-11-20 13:30:44 +01:00
Maarten L. Hekkelman
dffbf52d04 removed erronous static-assert 2022-11-19 11:17:50 +01:00
Maarten L. Hekkelman
57ac5f0112 Fix for 32bit, version bump 2022-11-19 10:21:36 +01:00
Maarten L. Hekkelman
d5a71b0b24 Fix verbose checking
faster aniso_row retrieval
report missing header line for pdb
2022-11-16 17:07:28 +01:00
Maarten L. Hekkelman
d95b7be2e4 removed remaining lzma references 2022-11-15 12:11:41 +01:00
Maarten L. Hekkelman
2f0a23f56a debian dislikes tweaks 2022-11-14 09:28:19 +01:00
Maarten L. Hekkelman
13b218f643 revert export of CIFPP_SHARE_DIR variable 2022-11-14 08:53:52 +01:00
Maarten L. Hekkelman
92a836ecdc More location fixes 2022-11-13 11:43:35 +01:00
Maarten L. Hekkelman
c88a46f155 Fix installation issues, version bump 2022-11-13 11:16:51 +01:00
Maarten L. Hekkelman
8882a34984 installation dirs cleanup 2022-11-11 20:40:42 +01:00
Maarten L. Hekkelman
2d4a1731d9 Merge branch 'develop' into trunk 2022-11-11 09:04:30 +01:00
Maarten L. Hekkelman
be1e3073f1 Fixes based on upstream changes 2022-11-10 17:06:59 +01:00
68 changed files with 7952 additions and 8777 deletions

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ CMakeSettings.json
msvc/
src/revision.hpp
test/test-create_sugar_?.cif
Testing/

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "gxrio"]
path = gxrio
url = https://github.com/mhekkel/gxrio.git

View File

@@ -25,7 +25,7 @@
cmake_minimum_required(VERSION 3.16)
# set the project name
project(cifpp VERSION 5.0.2 LANGUAGES CXX)
project(cifpp VERSION 5.1.0 LANGUAGES CXX)
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
@@ -35,11 +35,15 @@ include(CheckIncludeFiles)
include(CheckLibraryExists)
include(CMakePackageConfigHelpers)
include(CheckCXXSourceCompiles)
include(Dart)
include(GenerateExportHeader)
set(CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# set(CMAKE_CXX_VISIBILITY_PRESET hidden)
# set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers")
elseif(MSVC)
@@ -86,6 +90,7 @@ if(BUILD_FOR_CCP4)
list(APPEND CMAKE_MODULE_PATH "$ENV{CCP4}")
list(APPEND CMAKE_PREFIX_PATH "$ENV{CCP4}")
set(CMAKE_INSTALL_PREFIX "$ENV{CCP4}")
set(CMAKE_INSTALL_FULL_DATADIR "${CMAKE_INSTALL_PREFIX}/share/libcifpp")
if(WIN32)
set(BUILD_SHARED_LIBS ON)
@@ -98,11 +103,22 @@ if(MSVC)
add_compile_options(/permissive-)
macro(get_WIN32_WINNT version)
if(WIN32 AND CMAKE_SYSTEM_VERSION)
if(CMAKE_SYSTEM_VERSION)
set(ver ${CMAKE_SYSTEM_VERSION})
string(REPLACE "." "" ver ${ver})
string(REGEX REPLACE "([0-9])" "0\\1" ver ${ver})
string(REGEX MATCH "^([0-9]+).([0-9])" ver ${ver})
string(REGEX MATCH "^([0-9]+)" verMajor ${ver})
# Check for Windows 10, b/c we'll need to convert to hex 'A'.
if("${verMajor}" MATCHES "10")
set(verMajor "A")
string(REGEX REPLACE "^([0-9]+)" ${verMajor} ver ${ver})
endif()
# Remove all remaining '.' characters.
string(REPLACE "." "" ver ${ver})
# Prepend each digit with a zero.
string(REGEX REPLACE "([0-9A-Z])" "0\\1" ver ${ver})
set(${version} "0x${ver}")
endif()
endmacro()
@@ -153,23 +169,32 @@ if(MSVC)
set(_ZLIB_x86 "(x86)")
set(_ZLIB_SEARCH_NORMAL
PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\Zlib;InstallPath]"
"$ENV{ProgramFiles}/zlib"
"$ENV{ProgramFiles${_ZLIB_x86}}/zlib")
"$ENV{ProgramFiles}/zlib"
"$ENV{ProgramFiles${_ZLIB_x86}}/zlib")
unset(_ZLIB_x86)
list(APPEND _ZLIB_SEARCHES _ZLIB_SEARCH_NORMAL)
foreach(search ${_ZLIB_SEARCHES})
find_library(ZLIB_LIBRARY NAMES zlibstatic NAMES_PER_DIR ${${search}} PATH_SUFFIXES lib)
find_library(ZLIB_LIBRARY NAMES zlibstatic NAMES_PER_DIR ${${search}} PATH_SUFFIXES lib)
endforeach()
endif()
find_package(ZLIB REQUIRED)
find_package(Eigen3 REQUIRED)
include(FindFilesystem)
list(APPEND CIFPP_REQUIRED_LIBRARIES ${STDCPPFS_LIBRARY})
include(FindAtomic)
list(APPEND CIFPP_REQUIRED_LIBRARIES ${STDCPPATOMIC_LIBRARY})
if(MSVC)
# this dependency can go once MSVC supports std::experimental::is_detected
find_package(zeep 5.1.8 REQUIRED)
list(APPEND CIFPP_REQUIRED_LIBRARIES zeep::zeep)
endif()
# Create a revision file, containing the current git version info
include(VersionString)
write_version_header(${PROJECT_SOURCE_DIR}/src/ "LibCIFPP")
@@ -181,12 +206,12 @@ if(CIFPP_RECREATE_SYMOP_DATA)
add_custom_command(
OUTPUT ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp
COMMAND $<TARGET_FILE:symop-map-generator> $ENV{CLIBD}/syminfo.lib ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp
COMMAND $<TARGET_FILE:symop-map-generator> $ENV{CLIBD}/syminfo.lib $ENV{CLIBD}/symop.lib ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp
)
add_custom_target(
OUTPUT ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp
DEPENDS symop-map-generator "$ENV{CLIBD}/syminfo.lib"
DEPENDS symop-map-generator "$ENV{CLIBD}/syminfo.lib" "$ENV{CLIBD}/symop.lib"
)
endif()
@@ -224,7 +249,6 @@ set(project_headers
${PROJECT_SOURCE_DIR}/include/cif++/datablock.hpp
${PROJECT_SOURCE_DIR}/include/cif++/file.hpp
${PROJECT_SOURCE_DIR}/include/cif++/validate.hpp
${PROJECT_SOURCE_DIR}/include/cif++/list.hpp
${PROJECT_SOURCE_DIR}/include/cif++/iterator.hpp
${PROJECT_SOURCE_DIR}/include/cif++/parser.hpp
${PROJECT_SOURCE_DIR}/include/cif++/forward_decl.hpp
@@ -249,6 +273,7 @@ set(project_headers
add_library(cifpp ${project_sources} ${project_headers} ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp)
add_library(cifpp::cifpp ALIAS cifpp)
generate_export_header(cifpp EXPORT_FILE_NAME cif++/exports.hpp)
if(BOOST_REGEX)
target_compile_definitions(cifpp PRIVATE USE_BOOST_REGEX=1 BOOST_REGEX_STANDALONE=1)
@@ -263,7 +288,7 @@ set_target_properties(cifpp PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(cifpp
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include;${PROJECT_BINARY_DIR};${EIGEN3_INCLUDE_DIR}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
@@ -299,19 +324,17 @@ if(CIFPP_DOWNLOAD_CCD)
add_custom_target(COMPONENTS ALL DEPENDS ${COMPONENTS_CIF})
endif()
if(UNIX)
set(CIFPP_CACHE_DIR "/var/cache/libcifpp" CACHE STRING "The cache directory to use")
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
endif()
set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
set(LIBRARY_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR})
set(SHARE_INSTALL_DIR ${CMAKE_INSTALL_DATADIR}/libcifpp)
set(CIFPP_DATA_DIR "${CMAKE_INSTALL_PREFIX}/${SHARE_INSTALL_DIR}" CACHE STRING "The directory containing the provided data files")
# Installation directories
set(CIFPP_DATA_DIR "${CMAKE_INSTALL_FULL_DATADIR}/libcifpp")
target_compile_definitions(cifpp PUBLIC DATA_DIR="${CIFPP_DATA_DIR}")
if(UNIX)
set(CIFPP_CACHE_DIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/cache/libcifpp")
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
set(CIFPP_ETC_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}")
endif()
# Install rules
install(TARGETS cifpp
EXPORT cifppTargets
@@ -345,6 +368,12 @@ install(
COMPONENT Devel
)
install(
FILES ${PROJECT_BINARY_DIR}/cif++/exports.hpp
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cif++
COMPONENT Devel
)
install(FILES
${PROJECT_SOURCE_DIR}/rsrc/mmcif_ddl.dic
${PROJECT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic
@@ -353,13 +382,23 @@ install(FILES
DESTINATION ${CIFPP_DATA_DIR}
)
if(${CIFPP_CACHE_DIR})
install(FILES
${PROJECT_SOURCE_DIR}/rsrc/mmcif_ddl.dic
${PROJECT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic
${PROJECT_SOURCE_DIR}/rsrc/mmcif_ma.dic
${COMPONENTS_CIF}
DESTINATION ${CIFPP_CACHE_DIR}
)
endif()
set(CONFIG_TEMPLATE_FILE ${PROJECT_SOURCE_DIR}/cmake/cifppConfig.cmake.in)
configure_package_config_file(
${CONFIG_TEMPLATE_FILE}
${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifppConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cifpp
PATH_VARS INCLUDE_INSTALL_DIR LIBRARY_INSTALL_DIR SHARE_INSTALL_DIR
PATH_VARS CIFPP_DATA_DIR
)
install(FILES
@@ -402,8 +441,7 @@ if(ENABLE_TESTING)
find_package(Boost REQUIRED)
list(APPEND CIFPP_tests unit-v2 unit-3d format model rename-compound sugar
)
list(APPEND CIFPP_tests unit-v2 unit-3d format model rename-compound sugar spinner)
foreach(CIFPP_TEST IN LISTS CIFPP_tests)
set(CIFPP_TEST "${CIFPP_TEST}-test")
@@ -422,18 +460,22 @@ if(ENABLE_TESTING)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Run${CIFPP_TEST}.touch
COMMAND $<TARGET_FILE:${CIFPP_TEST}> -- ${PROJECT_SOURCE_DIR}/test)
COMMAND $<TARGET_FILE:${CIFPP_TEST}> -- ${CMAKE_CURRENT_SOURCE_DIR}/test)
add_test(NAME ${CIFPP_TEST}
COMMAND $<TARGET_FILE:${CIFPP_TEST}> -- ${PROJECT_SOURCE_DIR}/test)
COMMAND $<TARGET_FILE:${CIFPP_TEST}> -- ${CMAKE_CURRENT_SOURCE_DIR}/test)
endforeach()
endif()
message("Will install in ${CMAKE_INSTALL_PREFIX}")
# Optionally install the update scripts for CCD and dictionary files
if(CIFPP_INSTALL_UPDATE_SCRIPT)
set(CIFPP_CRON_DIR "$ENV{DESTDIR}/etc/cron.weekly")
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(CIFPP_CRON_DIR "${CIFPP_ETC_DIR}/cron.weekly")
elseif(UNIX) # assume all others are like FreeBSD...
set(CIFPP_CRON_DIR "${CIFPP_ETC_DIR}/periodic/weekly")
else()
message(FATAL_ERROR "Don't know where to install the update script")
endif()
configure_file(${PROJECT_SOURCE_DIR}/tools/update-libcifpp-data.in update-libcifpp-data @ONLY)
install(
@@ -443,15 +485,15 @@ if(CIFPP_INSTALL_UPDATE_SCRIPT)
)
install(DIRECTORY DESTINATION ${CIFPP_CACHE_DIR})
install(DIRECTORY DESTINATION "$ENV{DESTDIR}/etc/libcifpp/cache-update.d")
install(DIRECTORY DESTINATION "${CIFPP_ETC_DIR}/libcifpp/cache-update.d")
# a config to, to make it complete
if(NOT EXISTS "$ENV{DESTDIR}/etc/libcifpp.conf")
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
]])
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf DESTINATION "$ENV{DESTDIR}/etc")
install(CODE "message(\"A configuration file has been written to $ENV{DESTDIR}/etc/libcifpp.conf, please edit this file to enable automatic updates\")")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf 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\")")
endif()
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
@@ -462,7 +504,7 @@ set(CPACK_SOURCE_TGZ ON)
set(CPACK_SOURCE_TBZ2 OFF)
set(CPACK_SOURCE_TXZ OFF)
set(CPACK_SOURCE_TZ OFF)
set(CPACK_SOURCE_IGNORE_FILES "/data/components.cif;/build;/.vscode;/.git;/regex")
set(CPACK_SOURCE_IGNORE_FILES "/data/components.cif;/build;/.vscode;/.git")
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME})
include(CPack)

View File

@@ -3,18 +3,78 @@ libcifpp
This library contains code to work with mmCIF and PDB files.
Synopsis
--------
```c++
// A simple program counting residues with an OXT atom
#include <filesystem>
#include <iostream>
#include <cif++.hpp>
namespace fs = std::filesystem;
int main(int argc, char *argv[])
{
if (argc != 2)
exit(1);
// Read file, can be PDB or mmCIF and can even be compressed with gzip.
cif::file file = cif::pdb::read(argv[1]);
if (file.empty())
{
std::cerr << "Empty file" << std::endl;
exit(1);
}
auto &db = file.front();
auto &atom_site = db["atom_site"];
auto n = atom_site.find(cif::key("label_atom_id") == "OXT").size();
std::cout << "File contains " << atom_site.size() << " atoms of which "
<< n << (n == 1 ? " is" : " are") << " OXT" << std::endl
<< "residues with an OXT are:" << std::endl;
for (const auto &[asym, comp, seqnr] :
atom_site.find<std::string, std::string, int>(
cif::key("label_atom_id") == "OXT",
"label_asym_id", "label_comp_id", "label_seq_id"))
{
std::cout << asym << ' ' << comp << ' ' << seqnr << std::endl;
}
return 0;
}
```
Requirements
------------
The code for this library was written in C++17. You therefore need a
recent compiler to build it. For the development gcc 9.3 and clang 9.0
recent compiler to build it. For the development gcc 9.4 and clang 9.0
have been used as well as MSVC version 2019.
Other requirements are:
- [mrc](https://github.com/mhekkel/mrc), a resource compiler that
allows including data files into the executable making them easier to
install. Strictly this is optional, but at the expense of functionality.
install. Strictly speaking this is optional, but at the expense of
functionality.
- [libeigen](https://eigen.tuxfamily.org/index.php?title=Main_Page), a
library to do amongst others matrix calculations. This usually can be
installed using your package manager, in Debian/Ubuntu it is called
`libeigen3-dev`
- zlib, the development version of this library. On Debian/Ubuntu this
is the package `zlib1g-dev`.
- [boost](https://www.boost.org). The boost libraries are only needed if
you want to build the testing code.
When building using MS Visual Studio, you will also need [libzeep](https://github.com/mhekkel/libzeep)
since MSVC does not yet provide a C++ template required by libcifpp.
Building
--------
@@ -22,25 +82,20 @@ Building
This library uses [cmake](https://cmake.org). The usual way of building
and installing is to create a `build` directory and run cmake there.
On linux e.g. you would issue the following commands:
On linux e.g. you would issue the following commands to build and install
libcifpp in your `$HOME/.local` folder:
```bash
git clone https://github.com/PDB-REDO/libcifpp.git --recurse-submodules
cd libcifpp
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=$HOME/.local -DCMAKE_BUILD_TYPE=Release
cmake --build build
cmake --install build
```
git clone https://github.com/PDB-REDO/libcifpp.git
cd libcifpp
mkdir build
cd build
cmake ..
cmake --build . --config Release
ctest -C Release
cmake --install .
```
This checks out the source code from github, creates a new directory
where cmake stores its files. Run a configure, build the code and run
tests. And then it installs the library and auxiliary files.
The default is to install everything in `$HOME/.local` on Linux and
`%LOCALAPPDATA%` on Windows (the AppData/Local folder in your home directory).
You can change this by specifying the prefix with the
[CMAKE_INSTALL_PREFIX](https://cmake.org/cmake/help/v3.21/variable/CMAKE_INSTALL_PREFIX.html)
variable.
where cmake stores its files. Run a configure, build the code and then
it installs the library and auxiliary files.
If you want to run the tests before installing, you should add `-DENABLE_TESTING=ON`
to the first cmake command.

View File

@@ -1,3 +1,46 @@
Version 5.1
- New parser, optimised for speed
- Fix in unique ID generator
Version 5.0.10
- Fix in progress_bar, was using too much CPU
- Optimised mmCIF parser
Version 5.0.9
- Fix in dihedral angle calculations
- Added create_water to model
- Writing twin domain info in PDB files and more PDB fixes
- remove_atom improved (remove struct_conn records)
- Added a specialisation for category::find1<std::optional>
- fix memory leak in category
Version 5.0.8
- implemented find_first, find_min, find_max and count in category
- find1 now throws an exception if condition does not not exactly match one row
- Change in writing out PDB files, now looking up the original auth_seq_num
via the pdbx_xxx_scheme categories based on the atom_site.auth_seq_num ->
pdbx_xxx_scheme.pdb_seq_num relationship.
- fix memory leak in category
Version 5.0.7.1
- Use the implementation from zeep for std::experimental::is_detected
Version 5.0.7
- Reintroduce exports file. For DLL's
Version 5.0.6
- Fix file::contains, using iequals
- Fix is_cis
Version 5.0.5
- Fix code to work on 32 bit machines
Version 5.0.4
- Revert removal of CIFPP_SHARE_DIR export
Version 5.0.3
- Fix installation of libcifpp into the correct locations
Version 5.0.2
- Fix export of CISPEP records in PDB format
- Better support for exporting package_source

View File

@@ -4,12 +4,13 @@ include(CMakeFindDependencyMacro)
find_dependency(Threads)
find_dependency(ZLIB REQUIRED)
find_dependency(LibLZMA REQUIRED)
if(MSVC)
find_dependency(zeep REQUIRED)
endif()
INCLUDE("${CMAKE_CURRENT_LIST_DIR}/cifppTargets.cmake")
set_and_check(CIFPP_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
set_and_check(CIFPP_LIBRARY_DIR "@PACKAGE_LIBRARY_INSTALL_DIR@")
set_and_check(CIFPP_SHARE_DIR "@PACKAGE_SHARE_INSTALL_DIR@")
set_and_check(CIFPP_SHARE_DIR "@PACKAGE_CIFPP_DATA_DIR@")
check_required_components(cifpp)

View File

@@ -1,24 +1,32 @@
#include <iostream>
#include <filesystem>
#include <iostream>
#include <cif++.hpp>
namespace fs = std::filesystem;
int main()
int main(int argc, char *argv[])
{
cif::file file;
file.load("1cbs.cif.gz");
if (argc != 2)
exit(1);
auto& db = file.front();
cif::file file = cif::pdb::read(argv[1]);
if (file.empty())
{
std::cerr << "Empty file" << std::endl;
exit(1);
}
auto &db = file.front();
auto &atom_site = db["atom_site"];
auto n = atom_site.find(cif::key("label_atom_id") == "OXT").size();
std::cout << "File contains " << atom_site.size() << " atoms of which " << n << (n == 1 ? " is" : " are") << " OXT" << std::endl
<< "residues with an OXT are:" << std::endl;
for (const auto& [asym, comp, seqnr]: atom_site.find<std::string,std::string,int>(
cif::key("label_atom_id") == "OXT", "label_asym_id", "label_comp_id", "label_seq_id"))
<< "residues with an OXT are:" << std::endl;
for (const auto &[asym, comp, seqnr] : atom_site.find<std::string, std::string, int>(
cif::key("label_atom_id") == "OXT", "label_asym_id", "label_comp_id", "label_seq_id"))
{
std::cout << asym << ' ' << comp << ' ' << seqnr << std::endl;
}

View File

@@ -26,15 +26,16 @@
#pragma once
#include <cif++/utilities.hpp>
#include <cif++/file.hpp>
#include <cif++/parser.hpp>
#include <cif++/format.hpp>
#include "cif++/utilities.hpp"
#include "cif++/file.hpp"
#include "cif++/parser.hpp"
#include "cif++/format.hpp"
#include <cif++/compound.hpp>
#include <cif++/point.hpp>
#include <cif++/symmetry.hpp>
#include "cif++/compound.hpp"
#include "cif++/point.hpp"
#include "cif++/symmetry.hpp"
#include <cif++/model.hpp>
#include "cif++/model.hpp"
#include <cif++/pdb/io.hpp>
#include "cif++/pdb/io.hpp"
#include "cif++/gzio.hpp"

View File

@@ -204,7 +204,7 @@ struct atom_type_info
float radii[kRadiusTypeCount];
};
extern const atom_type_info kKnownAtoms[];
extern CIFPP_EXPORT const atom_type_info kKnownAtoms[];
// --------------------------------------------------------------------
// AtomTypeTraits
@@ -270,6 +270,10 @@ class atom_type_traits
const SFData &wksf(int charge = 0) const;
const SFData &elsf() const;
// Clipper doesn't like atoms with charges that do not have a scattering factor. And
// rightly so, but we need to know in advance if this is the case
bool has_sf(int charge) const;
private:
const struct atom_type_info *m_info;
};

View File

@@ -26,15 +26,15 @@
#pragma once
#include "cif++/forward_decl.hpp"
#include "cif++/condition.hpp"
#include "cif++/iterator.hpp"
#include "cif++/row.hpp"
#include "cif++/validate.hpp"
#include <array>
#include <cif++/forward_decl.hpp>
#include <cif++/condition.hpp>
#include <cif++/iterator.hpp>
#include <cif++/row.hpp>
#include <cif++/validate.hpp>
// TODO: implement all of:
// https://en.cppreference.com/w/cpp/named_req/Container
// https://en.cppreference.com/w/cpp/named_req/SequenceContainer
@@ -49,9 +49,26 @@ class duplicate_key_error : public std::runtime_error
{
public:
duplicate_key_error(const std::string &msg)
: std::runtime_error(msg) {}
: std::runtime_error(msg)
{
}
};
class multiple_results_error : public std::runtime_error
{
public:
multiple_results_error()
: std::runtime_error("query should have returned exactly one row")
{
}
};
// --------------------------------------------------------------------
// These should be moved elsewhere, one day.
template<typename _Tp> inline constexpr bool is_optional_v = false;
template<typename _Tp> inline constexpr bool is_optional_v<std::optional<_Tp>> = true;
// --------------------------------------------------------------------
class category
@@ -109,52 +126,52 @@ class category
reference front()
{
return {*this, *m_head};
return { *this, *m_head };
}
const_reference front() const
{
return {const_cast<category &>(*this), const_cast<row &>(*m_head)};
return { const_cast<category &>(*this), const_cast<row &>(*m_head) };
}
reference back()
{
return {*this, *m_tail};
return { *this, *m_tail };
}
const_reference back() const
{
return {const_cast<category &>(*this), const_cast<row &>(*m_tail)};
return { const_cast<category &>(*this), const_cast<row &>(*m_tail) };
}
iterator begin()
{
return {*this, m_head};
return { *this, m_head };
}
iterator end()
{
return {*this, nullptr};
return { *this, nullptr };
}
const_iterator begin() const
{
return {*this, m_head};
return { *this, m_head };
}
const_iterator end() const
{
return {*this, nullptr};
return { *this, nullptr };
}
const_iterator cbegin() const
{
return {*this, m_head};
return { *this, m_head };
}
const_iterator cend() const
{
return {*this, nullptr};
return { *this, nullptr };
}
size_t size() const
@@ -189,64 +206,64 @@ class category
iterator_proxy<const category, Ts...> rows(Ns... names) const
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return iterator_proxy<const category, Ts...>(*this, begin(), {names...});
return iterator_proxy<const category, Ts...>(*this, begin(), { names... });
}
template <typename... Ts, typename... Ns>
iterator_proxy<category, Ts...> rows(Ns... names)
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return iterator_proxy<category, Ts...>(*this, begin(), {names...});
return iterator_proxy<category, Ts...>(*this, begin(), { names... });
}
// --------------------------------------------------------------------
conditional_iterator_proxy<category> find(condition &&cond)
{
return find(begin(), std::forward<condition>(cond));
return find(begin(), std::move(cond));
}
conditional_iterator_proxy<category> find(iterator pos, condition &&cond)
{
return {*this, pos, std::forward<condition>(cond)};
return { *this, pos, std::move(cond) };
}
conditional_iterator_proxy<const category> find(condition &&cond) const
{
return find(cbegin(), std::forward<condition>(cond));
return find(cbegin(), std::move(cond));
}
conditional_iterator_proxy<const category> find(const_iterator pos, condition &&cond) const
{
return conditional_iterator_proxy<const category>{*this, pos, std::forward<condition>(cond)};
return conditional_iterator_proxy<const category>{ *this, pos, std::move(cond) };
}
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<category, Ts...> find(condition &&cond, Ns... names)
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return find<Ts...>(cbegin(), std::forward<condition>(cond), std::forward<Ns>(names)...);
return find<Ts...>(cbegin(), std::move(cond), std::forward<Ns>(names)...);
}
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<const category, Ts...> find(condition &&cond, Ns... names) const
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return find<Ts...>(cbegin(), std::forward<condition>(cond), std::forward<Ns>(names)...);
return find<Ts...>(cbegin(), std::move(cond), std::forward<Ns>(names)...);
}
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<category, Ts...> find(const_iterator pos, condition &&cond, Ns... names)
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return {*this, pos, std::forward<condition>(cond), std::forward<Ns>(names)...};
return { *this, pos, std::move(cond), std::forward<Ns>(names)... };
}
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<const category, Ts...> find(const_iterator pos, condition &&cond, Ns... names) const
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return {*this, pos, std::forward<condition>(cond), std::forward<Ns>(names)...};
return { *this, pos, std::move(cond), std::forward<Ns>(names)... };
}
// --------------------------------------------------------------------
@@ -254,40 +271,63 @@ class category
row_handle find1(condition &&cond)
{
return find1(begin(), std::forward<condition>(cond));
return find1(begin(), std::move(cond));
}
row_handle find1(iterator pos, condition &&cond)
{
auto h = find(pos, std::forward<condition>(cond));
auto h = find(pos, std::move(cond));
return h.size() != 1 ? row_handle{} : *h.begin();
if (h.size() != 1)
throw multiple_results_error();
return *h.begin();
}
const row_handle find1(condition &&cond) const
{
return find1(cbegin(), std::forward<condition>(cond));
return find1(cbegin(), std::move(cond));
}
const row_handle find1(const_iterator pos, condition &&cond) const
{
auto h = find(pos, std::forward<condition>(cond));
auto h = find(pos, std::move(cond));
return h.size() != 1 ? row_handle{} : *h.begin();
if (h.size() != 1)
throw multiple_results_error();
return *h.begin();
}
template <typename T>
T find1(condition &&cond, const char *column) const
{
return find1<T>(cbegin(), std::forward<condition>(cond), column);
return find1<T>(cbegin(), std::move(cond), column);
}
template <typename T>
template <typename T, std::enable_if_t<not is_optional_v<T>, int> = 0>
T find1(const_iterator pos, condition &&cond, const char *column) const
{
auto h = find<T>(pos, std::forward<condition>(cond), column);
auto h = find<T>(pos, std::move(cond), column);
return h.size() == 1 ? *h.begin() : T{};
if (h.size() != 1)
throw multiple_results_error();
return *h.begin();
}
template <typename T, std::enable_if_t<is_optional_v<T>, int> = 0>
T find1(const_iterator pos, condition &&cond, const char *column) const
{
auto h = find<typename T::value_type>(pos, std::move(cond), column);
if (h.size() > 1)
throw multiple_results_error();
if (h.empty())
return {};
return *h.begin();
}
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
@@ -295,16 +335,119 @@ class category
{
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of column titles should be equal to the number of types to return");
// static_assert(std::is_same_v<Cs, const char*>..., "The column names should be const char");
return find1<Ts...>(cbegin(), std::forward<condition>(cond), std::forward<Cs>(columns)...);
return find1<Ts...>(cbegin(), std::move(cond), std::forward<Cs>(columns)...);
}
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find1(const_iterator pos, condition &&cond, Cs... columns) const
{
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of column titles should be equal to the number of types to return");
auto h = find<Ts...>(pos, std::forward<condition>(cond), std::forward<Cs>(columns)...);
auto h = find<Ts...>(pos, std::move(cond), std::forward<Cs>(columns)...);
return h.size() == 1 ? *h.begin() : std::tuple<Ts...>{};
if (h.size() != 1)
throw multiple_results_error();
return *h.begin();
}
// --------------------------------------------------------------------
// if you want only a first hit
row_handle find_first(condition &&cond)
{
return find_first(begin(), std::move(cond));
}
row_handle find_first(iterator pos, condition &&cond)
{
auto h = find(pos, std::move(cond));
return h.empty() ? row_handle{} : *h.begin();
}
const row_handle find_first(condition &&cond) const
{
return find_first(cbegin(), std::move(cond));
}
const row_handle find_first(const_iterator pos, condition &&cond) const
{
auto h = find(pos, std::move(cond));
return h.empty() ? row_handle{} : *h.begin();
}
template <typename T>
T find_first(condition &&cond, const char *column) const
{
return find_first<T>(cbegin(), std::move(cond), column);
}
template <typename T>
T find_first(const_iterator pos, condition &&cond, const char *column) const
{
auto h = find<T>(pos, std::move(cond), column);
return h.empty() ? T{} : *h.begin();
}
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find_first(condition &&cond, Cs... columns) const
{
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of column titles should be equal to the number of types to return");
// static_assert(std::is_same_v<Cs, const char*>..., "The column names should be const char");
return find_first<Ts...>(cbegin(), std::move(cond), std::forward<Cs>(columns)...);
}
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find_first(const_iterator pos, condition &&cond, Cs... columns) const
{
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of column titles should be equal to the number of types to return");
auto h = find<Ts...>(pos, std::move(cond), std::forward<Cs>(columns)...);
return h.empty() ? std::tuple<Ts...>{} : *h.begin();
}
// --------------------------------------------------------------------
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
T find_max(const char *column, condition &&cond) const
{
T result = std::numeric_limits<T>::min();
for (auto v : find<T>(std::move(cond), column))
{
if (result < v)
result = v;
}
return result;
}
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
T find_max(const char *column) const
{
return find_max<T>(column, all());
}
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
T find_min(const char *column, condition &&cond) const
{
T result = std::numeric_limits<T>::max();
for (auto v : find<T>(std::move(cond), column))
{
if (result > v)
result = v;
}
return result;
}
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
T find_min(const char *column) const
{
return find_min<T>(column, all());
}
bool exists(condition &&cond) const
@@ -335,6 +478,31 @@ class category
return result;
}
size_t count(condition &&cond) const
{
size_t result = 0;
if (cond)
{
cond.prepare(*this);
auto sh = cond.single();
if (sh.has_value() and *sh)
result = 1;
else
{
for (auto r : *this)
{
if (cond(r))
++result;
}
}
}
return result;
}
// --------------------------------------------------------------------
bool has_children(row_handle r) const;
@@ -486,7 +654,7 @@ class category
// --------------------------------------------------------------------
void sort(std::function<int(row_handle,row_handle)> f);
void sort(std::function<int(row_handle, row_handle)> f);
void reorder_by_index();
// --------------------------------------------------------------------
@@ -583,7 +751,7 @@ class category
void swap_item(uint16_t column_ix, row_handle &a, row_handle &b);
// --------------------------------------------------------------------
std::string m_name;
std::vector<item_column> m_columns;
const validator *m_validator = nullptr;

View File

@@ -29,14 +29,14 @@
/// \file This file contains the definition for the class compound, encapsulating
/// the information found for compounds in the CCD.
#include "cif++/atom_type.hpp"
#include "cif++/point.hpp"
#include <map>
#include <set>
#include <tuple>
#include <vector>
#include <cif++.hpp>
#include <cif++/atom_type.hpp>
namespace cif
{
@@ -75,6 +75,11 @@ struct compound_atom
bool leaving_atom = false;
bool stereo_config = false;
float x, y, z;
point get_location() const
{
return { x, y, z };
}
};
/// --------------------------------------------------------------------
@@ -114,6 +119,7 @@ class compound
compound_atom get_atom_by_atom_id(const std::string &atom_id) const;
bool atoms_bonded(const std::string &atomId_1, const std::string &atomId_2) const;
float bond_length(const std::string &atomId_1, const std::string &atomId_2) const;
bool is_water() const
{
@@ -173,7 +179,7 @@ class compound_factory
~compound_factory();
static const std::map<std::string, char> kAAMap, kBaseMap;
static CIFPP_EXPORT const std::map<std::string, char> kAAMap, kBaseMap;
private:
compound_factory();
@@ -188,4 +194,4 @@ class compound_factory
std::shared_ptr<compound_factory_impl> m_impl;
};
} // namespace pdbx
} // namespace cif

View File

@@ -26,14 +26,14 @@
#pragma once
#include "cif++/row.hpp"
#include <cassert>
#include <functional>
#include <iostream>
#include <regex>
#include <utility>
#include <cif++/row.hpp>
namespace cif
{
@@ -57,6 +57,8 @@ namespace detail
virtual bool test(row_handle) const = 0;
virtual void str(std::ostream &) const = 0;
virtual std::optional<row_handle> single() const { return {}; };
virtual bool equals([[maybe_unused]] const condition_impl *rhs) const { return false; }
};
struct all_condition_impl : public condition_impl
@@ -145,7 +147,6 @@ class condition
}
private:
void optimise(condition_impl *&impl);
condition_impl *m_impl;
@@ -181,6 +182,33 @@ namespace detail
uint16_t m_item_ix = 0;
};
struct key_is_not_empty_condition_impl : public condition_impl
{
key_is_not_empty_condition_impl(const std::string &item_tag)
: m_item_tag(item_tag)
{
}
condition_impl *prepare(const category &c) override
{
m_item_ix = get_column_ix(c, m_item_tag);
return this;
}
bool test(row_handle r) const override
{
return not r[m_item_ix].empty();
}
void str(std::ostream &os) const override
{
os << m_item_tag << " IS NOT NULL";
}
std::string m_item_tag;
uint16_t m_item_ix = 0;
};
struct key_equals_condition_impl : public condition_impl
{
key_equals_condition_impl(item &&i)
@@ -193,9 +221,7 @@ namespace detail
bool test(row_handle r) const override
{
return m_single_hit.has_value() ?
*m_single_hit == r :
r[m_item_ix].compare(m_value, m_icase) == 0;
return m_single_hit.has_value() ? *m_single_hit == r : r[m_item_ix].compare(m_value, m_icase) == 0;
}
void str(std::ostream &os) const override
@@ -208,6 +234,20 @@ namespace detail
return m_single_hit;
}
virtual bool equals(const condition_impl *rhs) const override
{
if (typeid(*rhs) == typeid(key_equals_condition_impl))
{
auto ri = static_cast<const key_equals_condition_impl *>(rhs);
if (m_single_hit.has_value() or ri->m_single_hit.has_value())
return m_single_hit == ri->m_single_hit;
else
// watch out, both m_item_ix might be the same while tags might be diffent (in case they both do not exist in the category)
return m_item_ix == ri->m_item_ix and m_value == ri->m_value and m_item_tag == ri->m_item_tag;
}
return this == rhs;
}
std::string m_item_tag;
uint16_t m_item_ix = 0;
bool m_icase = false;
@@ -244,7 +284,7 @@ namespace detail
void str(std::ostream &os) const override
{
os << m_item_tag << (m_icase ? "^ " : " ") << " == " << m_value << " OR " << m_item_tag << " IS NULL";
os << '(' << m_item_tag << (m_icase ? "^ " : " ") << " == " << m_value << " OR " << m_item_tag << " IS NULL)";
}
virtual std::optional<row_handle> single() const override
@@ -252,12 +292,26 @@ namespace detail
return m_single_hit;
}
virtual bool equals(const condition_impl *rhs) const override
{
if (typeid(*rhs) == typeid(key_equals_or_empty_condition_impl))
{
auto ri = static_cast<const key_equals_or_empty_condition_impl *>(rhs);
if (m_single_hit.has_value() or ri->m_single_hit.has_value())
return m_single_hit == ri->m_single_hit;
else
// watch out, both m_item_ix might be the same while tags might be diffent (in case they both do not exist in the category)
return m_item_ix == ri->m_item_ix and m_value == ri->m_value and m_item_tag == ri->m_item_tag;
}
return this == rhs;
}
std::string m_item_tag;
uint16_t m_item_ix = 0;
std::string m_value;
bool m_icase = false;
std::optional<row_handle> m_single_hit;
};
};
struct key_compare_condition_impl : public condition_impl
{
@@ -409,29 +463,53 @@ namespace detail
// case they make up an indexed tuple.
struct and_condition_impl : public condition_impl
{
and_condition_impl() = default;
and_condition_impl(condition &&a, condition &&b)
{
mSub.emplace_back(std::exchange(a.m_impl, nullptr));
mSub.emplace_back(std::exchange(b.m_impl, nullptr));
if (typeid(*a.m_impl) == typeid(*this))
{
and_condition_impl *ai = static_cast<and_condition_impl *>(a.m_impl);
std::swap(m_sub, ai->m_sub);
m_sub.emplace_back(std::exchange(b.m_impl, nullptr));
}
else if (typeid(*b.m_impl) == typeid(*this))
{
and_condition_impl *bi = static_cast<and_condition_impl *>(b.m_impl);
std::swap(m_sub, bi->m_sub);
m_sub.emplace_back(std::exchange(a.m_impl, nullptr));
}
else
{
m_sub.emplace_back(std::exchange(a.m_impl, nullptr));
m_sub.emplace_back(std::exchange(b.m_impl, nullptr));
}
}
~and_condition_impl()
{
for (auto sub : mSub)
for (auto sub : m_sub)
delete sub;
}
condition_impl *prepare(const category &c) override;
condition_impl *prepare(const category &c) override
{
for (auto &sub : m_sub)
sub = sub->prepare(c);
return this;
}
bool test(row_handle r) const override
{
bool result = true;
for (auto sub : mSub)
for (auto sub : m_sub)
{
if (sub->test(r))
continue;
result = false;
break;
}
@@ -444,7 +522,7 @@ namespace detail
os << '(';
bool first = true;
for (auto sub : mSub)
for (auto sub : m_sub)
{
if (first)
first = false;
@@ -461,7 +539,7 @@ namespace detail
{
std::optional<row_handle> result;
for (auto sub : mSub)
for (auto sub : m_sub)
{
auto s = sub->single();
@@ -470,7 +548,7 @@ namespace detail
result = s;
continue;
}
if (s == result)
continue;
@@ -481,56 +559,100 @@ namespace detail
return result;
}
std::vector<condition_impl *> mSub;
static condition_impl *combine_equal(std::vector<and_condition_impl *> &subs, or_condition_impl *oc);
std::vector<condition_impl *> m_sub;
};
struct or_condition_impl : public condition_impl
{
or_condition_impl(condition &&a, condition &&b)
: mA(nullptr)
, mB(nullptr)
{
std::swap(mA, a.m_impl);
std::swap(mB, b.m_impl);
if (typeid(*a.m_impl) == typeid(*this))
{
or_condition_impl *ai = static_cast<or_condition_impl *>(a.m_impl);
std::swap(m_sub, ai->m_sub);
m_sub.emplace_back(std::exchange(b.m_impl, nullptr));
}
else if (typeid(*b.m_impl) == typeid(*this))
{
or_condition_impl *bi = static_cast<or_condition_impl *>(b.m_impl);
std::swap(m_sub, bi->m_sub);
m_sub.emplace_back(std::exchange(a.m_impl, nullptr));
}
else
{
m_sub.emplace_back(std::exchange(a.m_impl, nullptr));
m_sub.emplace_back(std::exchange(b.m_impl, nullptr));
}
}
~or_condition_impl()
{
delete mA;
delete mB;
for (auto sub : m_sub)
delete sub;
}
condition_impl *prepare(const category &c) override;
bool test(row_handle r) const override
{
return mA->test(r) or mB->test(r);
bool result = false;
for (auto sub : m_sub)
{
if (not sub->test(r))
continue;
result = true;
break;
}
return result;
}
void str(std::ostream &os) const override
{
bool first = true;
os << '(';
mA->str(os);
os << ") OR (";
mB->str(os);
for (auto sub : m_sub)
{
if (first)
first = false;
else
os << " OR ";
sub->str(os);
}
os << ')';
}
virtual std::optional<row_handle> single() const override
{
auto sa = mA->single();
auto sb = mB->single();
if (sa.has_value() and sb.has_value() and sa != sb)
sa.reset();
else if (not sa.has_value())
sa = sb;
std::optional<row_handle> result;
return sa;
for (auto sub : m_sub)
{
auto s = sub->single();
if (not result.has_value())
{
result = s;
continue;
}
if (s == result)
continue;
result.reset();
break;
}
return result;
}
condition_impl *mA;
condition_impl *mB;
std::vector<condition_impl *> m_sub;
};
struct not_condition_impl : public condition_impl
@@ -569,7 +691,7 @@ namespace detail
} // namespace detail
inline condition operator&&(condition &&a, condition &&b)
inline condition operator and(condition &&a, condition &&b)
{
if (a.m_impl and b.m_impl)
return condition(new detail::and_condition_impl(std::move(a), std::move(b)));
@@ -578,12 +700,35 @@ inline condition operator&&(condition &&a, condition &&b)
return condition(std::move(b));
}
inline condition operator||(condition &&a, condition &&b)
inline condition operator or(condition &&a, condition &&b)
{
if (a.m_impl and b.m_impl)
{
if (typeid(*a.m_impl) == typeid(detail::key_equals_condition_impl) and
typeid(*b.m_impl) == typeid(detail::key_is_empty_condition_impl))
{
auto ci = static_cast<detail::key_equals_condition_impl *>(a.m_impl);
auto ce = static_cast<detail::key_is_empty_condition_impl *>(b.m_impl);
if (ci->m_item_tag == ce->m_item_tag)
return condition(new detail::key_equals_or_empty_condition_impl(ci));
}
else if (typeid(*b.m_impl) == typeid(detail::key_equals_condition_impl) and
typeid(*a.m_impl) == typeid(detail::key_is_empty_condition_impl))
{
auto ci = static_cast<detail::key_equals_condition_impl *>(b.m_impl);
auto ce = static_cast<detail::key_is_empty_condition_impl *>(a.m_impl);
if (ci->m_item_tag == ce->m_item_tag)
return condition(new detail::key_equals_or_empty_condition_impl(ci));
}
return condition(new detail::or_condition_impl(std::move(a), std::move(b)));
}
if (a.m_impl)
return condition(std::move(a));
return condition(std::move(b));
}
@@ -706,7 +851,12 @@ inline condition operator==(const key &key, const empty_type &)
return condition(new detail::key_is_empty_condition_impl(key.m_item_tag));
}
inline condition operator !(condition &&rhs)
inline condition operator!=(const key &key, const empty_type &)
{
return condition(new detail::key_is_not_empty_condition_impl(key.m_item_tag));
}
inline condition operator not(condition &&rhs)
{
return condition(new detail::not_condition_impl(std::move(rhs)));
}
@@ -741,4 +891,4 @@ namespace literals
}
} // namespace literals
} // namespace cif
} // namespace cif

View File

@@ -26,9 +26,8 @@
#pragma once
#include <cif++/forward_decl.hpp>
#include <cif++/category.hpp>
#include "cif++/category.hpp"
#include "cif++/forward_decl.hpp"
namespace cif
{
@@ -45,11 +44,10 @@ class datablock : public std::list<category>
{
}
datablock(const datablock &) = default;
datablock(const datablock &);
datablock(datablock &&) = default;
datablock &operator=(const datablock &) = default;
datablock &operator=(const datablock &);
datablock &operator=(datablock &&) = default;
// --------------------------------------------------------------------
@@ -89,7 +87,7 @@ class datablock : public std::list<category>
}
// --------------------------------------------------------------------
bool operator==(const datablock &rhs) const;
private:

View File

@@ -26,11 +26,12 @@
#pragma once
#include <cif++/validate.hpp>
#include "cif++/validate.hpp"
namespace cif
{
validator parse_dictionary(std::string_view name, std::istream &is);
void extend_dictionary(validator &v, std::istream &is);
} // namespace cif

View File

@@ -28,8 +28,9 @@
#include <list>
#include <cif++/datablock.hpp>
#include <cif++/parser.hpp>
#include "cif++/exports.hpp"
#include "cif++/datablock.hpp"
#include "cif++/parser.hpp"
namespace cif
{

View File

@@ -26,6 +26,8 @@
#pragma once
#include "cif++/exports.hpp"
#include <string>
#include <vector>

View File

@@ -26,8 +26,8 @@
/// whether to use a compressions/decompression algorithm is
/// based on the extension of the \a filename argument.
// This is a stripped down version of the gzio library from
// https://github.com/mhekkel/gzio.git
// This is a stripped down version of the gxrio library from
// https://github.com/mhekkel/gxrio.git
// Most notably, the lzma support has been removed since getting
// that to work in Windows proved to be too much work.
@@ -128,15 +128,15 @@ class basic_igzip_streambuf : public basic_streambuf<CharT, Traits>
std::swap(m_zstream, rhs.m_zstream);
std::swap(m_gzheader, rhs.m_gzheader);
auto p = std::copy(rhs.gptr(), rhs.egptr(), m_out_buffer.begin());
this->setg(m_out_buffer.data(), m_out_buffer.data() + m_out_buffer.size(), p);
auto p = std::copy(rhs.gptr(), rhs.egptr(), m_out_buffer.data());
this->setg(m_out_buffer.data(), m_out_buffer.data(), p);
if (m_zstream and m_zstream->avail_in > 0)
{
auto next_in_offset = m_zstream->next_in - rhs.m_in_buffer.data();
std::copy(rhs.m_in_buffer.begin() + next_in_offset,
rhs.m_in_buffer.begin() + next_in_offset + m_zstream->avail_in,
m_in_buffer.begin());
std::copy(rhs.m_in_buffer.data() + next_in_offset,
rhs.m_in_buffer.data() + next_in_offset + m_zstream->avail_in,
m_in_buffer.data());
m_zstream->next_in = m_in_buffer.data();
}
}
@@ -151,15 +151,15 @@ class basic_igzip_streambuf : public basic_streambuf<CharT, Traits>
std::swap(m_zstream, rhs.m_zstream);
std::swap(m_gzheader, rhs.m_gzheader);
auto p = std::copy(rhs.gptr(), rhs.egptr(), m_out_buffer.begin());
this->setg(m_out_buffer.data(), m_out_buffer.data() + m_out_buffer.size(), p);
auto p = std::copy(rhs.gptr(), rhs.egptr(), m_out_buffer.data());
this->setg(m_out_buffer.data(), m_out_buffer.data(), p);
if (m_zstream and m_zstream->avail_in > 0)
{
auto next_in_offset = m_zstream->next_in - reinterpret_cast<unsigned char *>(rhs.m_in_buffer.data());
std::copy(rhs.m_in_buffer.begin() + next_in_offset,
rhs.m_in_buffer.begin() + next_in_offset + m_zstream->avail_in,
m_in_buffer.begin());
std::copy(rhs.m_in_buffer.data() + next_in_offset,
rhs.m_in_buffer.data() + next_in_offset + m_zstream->avail_in,
m_in_buffer.data());
m_zstream->next_in = reinterpret_cast<unsigned char *>(m_in_buffer.data());
}

View File

@@ -26,6 +26,12 @@
#pragma once
#include "cif++/exports.hpp"
#include "cif++/forward_decl.hpp"
#include "cif++/text.hpp"
#include "cif++/utilities.hpp"
#include <cassert>
#include <charconv>
#include <cstring>
#include <iomanip>
@@ -35,9 +41,6 @@
#include <optional>
#include <utility>
#include <cif++/forward_decl.hpp>
#include <cif++/text.hpp>
/// \file item.hpp
/// This file contains the declaration of item but also the item_value and item_handle
/// These handle the storage of and access to the data for a single data field.
@@ -45,8 +48,6 @@
namespace cif
{
extern int VERBOSE;
// --------------------------------------------------------------------
/// \brief item is a transient class that is used to pass data into rows
/// but it also takes care of formatting data.
@@ -194,6 +195,7 @@ struct item_value
/// \brief constructor
item_value(std::string_view text)
: m_length(text.length())
, m_storage(0)
{
if (m_length >= kBufferSize)
{
@@ -210,7 +212,7 @@ struct item_value
item_value(item_value &&rhs)
: m_length(std::exchange(rhs.m_length, 0))
, m_data(std::exchange(rhs.m_data, nullptr))
, m_storage(std::exchange(rhs.m_storage, 0))
{
}
@@ -219,7 +221,7 @@ struct item_value
if (this != &rhs)
{
m_length = std::exchange(rhs.m_length, m_length);
m_data = std::exchange(rhs.m_data, m_data);
m_storage = std::exchange(rhs.m_storage, m_storage);
}
return *this;
}
@@ -228,7 +230,7 @@ struct item_value
{
if (m_length >= kBufferSize)
delete[] m_data;
m_data = nullptr;
m_storage = 0;
m_length = 0;
}
@@ -245,6 +247,7 @@ struct item_value
{
char m_local_data[8];
char *m_data;
uint64_t m_storage;
};
static constexpr size_t kBufferSize = sizeof(m_local_data);
@@ -257,9 +260,6 @@ struct item_value
}
};
// static_assert(sizeof(item_value) == 24, "sizeof(item_value) should be 24 bytes");
static_assert(sizeof(item_value) == 16, "sizeof(item_value) should be 16 bytes");
// --------------------------------------------------------------------
// Transient object to access stored data
@@ -354,7 +354,7 @@ struct item_handle
{
}
static const item_handle s_null_item;
static CIFPP_EXPORT const item_handle s_null_item;
friend void swap(item_handle a, item_handle b)
{

View File

@@ -26,7 +26,9 @@
#pragma once
#include <cif++/row.hpp>
#include "cif++/row.hpp"
#include <array>
namespace cif
{

View File

@@ -1,79 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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 <memory>
namespace cif
{
// --------------------------------------------------------------------
template<typename Allocator = std::allocator<void>>
class list
{
public:
protected:
struct list_item
{
list_item *m_next = nullptr;
};
using list_item_allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<list_item>;
using list_item_allocator_traits = std::allocator_traits<item_allocator_type>;
list_item_allocator_traits::pointer get_item()
{
list_item_allocator_type ia(get_allocator());
return list_item_allocator_traits::allocate(ia, 1);
}
template<typename ...Arguments>
list_item *create_list_item(uint16_t column_ix, Arguments... args)
{
auto p = this->get_item();
list_item_allocator_type ia(get_allocator());
list_item_allocator_traits::construct(ia, p, std::forward<Arguments>(args)...);
return p;
}
void delete_list_item(list_item *iv)
{
list_item_allocator_type ia(get_allocator());
list_item_allocator_traits::destroy(ia, iv);
list_item_allocator_traits::deallocate(ia, iv, 1);
}
list_item *m_head = nullptr, *m_tail = nullptr;
};
} // namespace cif

531
include/cif++/matrix.hpp Normal file
View File

@@ -0,0 +1,531 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2023 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 <array>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <ostream>
#include <tuple>
#include <type_traits>
#include <vector>
namespace cif
{
// --------------------------------------------------------------------
// We're using expression templates here
template <typename M>
class matrix_expression
{
public:
constexpr uint32_t dim_m() const { return static_cast<const M &>(*this).dim_m(); }
constexpr uint32_t dim_n() const { return static_cast<const M &>(*this).dim_n(); }
constexpr auto &operator()(uint32_t i, uint32_t j)
{
return static_cast<M &>(*this).operator()(i, j);
}
constexpr auto operator()(uint32_t i, uint32_t j) const
{
return static_cast<const M &>(*this).operator()(i, j);
}
void swap_row(uint32_t r1, uint32_t r2)
{
for (uint32_t c = 0; c < dim_m(); ++c)
{
auto v = operator()(r1, c);
operator()(r1, c) = operator()(r2, c);
operator()(r2, c) = v;
}
}
void swap_col(uint32_t c1, uint32_t c2)
{
for (uint32_t r = 0; r < dim_n(); ++r)
{
auto &a = operator()(r, c1);
auto &b = operator()(r, c2);
std::swap(a, b);
}
}
friend std::ostream &operator<<(std::ostream &os, const matrix_expression &m)
{
os << '[';
for (size_t i = 0; i < m.dim_m(); ++i)
{
os << '[';
for (size_t j = 0; j < m.dim_n(); ++j)
{
os << m(i, j);
if (j + 1 < m.dim_n())
os << ", ";
}
if (i + 1 < m.dim_m())
os << ", ";
os << ']';
}
os << ']';
return os;
}
};
// --------------------------------------------------------------------
// matrix is m x n, addressing i,j is 0 <= i < m and 0 <= j < n
// element m i,j is mapped to [i * n + j] and thus storage is row major
template <typename F = float>
class matrix : public matrix_expression<matrix<F>>
{
public:
using value_type = F;
template <typename M2>
matrix(const matrix_expression<M2> &m)
: m_m(m.dim_m())
, m_n(m.dim_n())
, m_data(m_m * m_n)
{
for (uint32_t i = 0; i < m_m; ++i)
{
for (uint32_t j = 0; j < m_n; ++j)
operator()(i, j) = m(i, j);
}
}
matrix(size_t m, size_t n, value_type v = 0)
: m_m(m)
, m_n(n)
, m_data(m_m * m_n)
{
std::fill(m_data.begin(), m_data.end(), v);
}
matrix() = default;
matrix(matrix &&m) = default;
matrix(const matrix &m) = default;
matrix &operator=(matrix &&m) = default;
matrix &operator=(const matrix &m) = default;
constexpr size_t dim_m() const { return m_m; }
constexpr size_t dim_n() const { return m_n; }
constexpr value_type operator()(size_t i, size_t j) const
{
assert(i < m_m);
assert(j < m_n);
return m_data[i * m_n + j];
}
constexpr value_type &operator()(size_t i, size_t j)
{
assert(i < m_m);
assert(j < m_n);
return m_data[i * m_n + j];
}
private:
size_t m_m = 0, m_n = 0;
std::vector<value_type> m_data;
};
// --------------------------------------------------------------------
// special case, 3x3 matrix
template <typename F, size_t M, size_t N>
class matrix_fixed : public matrix_expression<matrix_fixed<F, M, N>>
{
public:
using value_type = F;
static constexpr size_t kSize = M * N;
template <typename M2>
matrix_fixed(const M2 &m)
{
assert(M == m.dim_m() and N == m.dim_n());
for (uint32_t i = 0; i < M; ++i)
{
for (uint32_t j = 0; j < N; ++j)
operator()(i, j) = m(i, j);
}
}
matrix_fixed(value_type v = 0)
{
m_data.fill(v);
}
matrix_fixed(const F (&v)[kSize])
{
fill(v, std::make_index_sequence<kSize>{});
}
matrix_fixed(matrix_fixed &&m) = default;
matrix_fixed(const matrix_fixed &m) = default;
matrix_fixed &operator=(matrix_fixed &&m) = default;
matrix_fixed &operator=(const matrix_fixed &m) = default;
template<size_t... Ixs>
matrix_fixed& fill(const F (&a)[kSize], std::index_sequence<Ixs...>)
{
m_data = { a[Ixs]... };
return *this;
}
constexpr size_t dim_m() const { return M; }
constexpr size_t dim_n() const { return N; }
constexpr value_type operator()(size_t i, size_t j) const
{
assert(i < M);
assert(j < N);
return m_data[i * N + j];
}
constexpr value_type &operator()(size_t i, size_t j)
{
assert(i < M);
assert(j < N);
return m_data[i * N + j];
}
private:
std::array<value_type, M * N> m_data;
};
template <typename F>
using matrix3x3 = matrix_fixed<F, 3, 3>;
template <typename F>
using matrix4x4 = matrix_fixed<F, 4, 4>;
// --------------------------------------------------------------------
template <typename F = float>
class symmetric_matrix : public matrix_expression<symmetric_matrix<F>>
{
public:
using value_type = F;
symmetric_matrix(uint32_t n, value_type v = 0)
: m_n(n)
, m_data((m_n * (m_n + 1)) / 2)
{
std::fill(m_data.begin(), m_data.end(), v);
}
symmetric_matrix() = default;
symmetric_matrix(symmetric_matrix &&m) = default;
symmetric_matrix(const symmetric_matrix &m) = default;
symmetric_matrix &operator=(symmetric_matrix &&m) = default;
symmetric_matrix &operator=(const symmetric_matrix &m) = default;
constexpr uint32_t dim_m() const { return m_n; }
constexpr uint32_t dim_n() const { return m_n; }
constexpr value_type operator()(uint32_t i, uint32_t j) const
{
return i < j
? m_data[(j * (j + 1)) / 2 + i]
: m_data[(i * (i + 1)) / 2 + j];
}
constexpr value_type &operator()(uint32_t i, uint32_t j)
{
if (i > j)
std::swap(i, j);
assert(j < m_n);
return m_data[(j * (j + 1)) / 2 + i];
}
private:
uint32_t m_n;
std::vector<value_type> m_data;
};
// --------------------------------------------------------------------
template <typename F, size_t M>
class symmetric_matrix_fixed : public matrix_expression<symmetric_matrix_fixed<F, M>>
{
public:
using value_type = F;
symmetric_matrix_fixed(value_type v = 0)
{
std::fill(m_data.begin(), m_data.end(), v);
}
symmetric_matrix_fixed(symmetric_matrix_fixed &&m) = default;
symmetric_matrix_fixed(const symmetric_matrix_fixed &m) = default;
symmetric_matrix_fixed &operator=(symmetric_matrix_fixed &&m) = default;
symmetric_matrix_fixed &operator=(const symmetric_matrix_fixed &m) = default;
constexpr uint32_t dim_m() const { return M; }
constexpr uint32_t dim_n() const { return M; }
constexpr value_type operator()(uint32_t i, uint32_t j) const
{
return i < j
? m_data[(j * (j + 1)) / 2 + i]
: m_data[(i * (i + 1)) / 2 + j];
}
constexpr value_type &operator()(uint32_t i, uint32_t j)
{
if (i > j)
std::swap(i, j);
assert(j < M);
return m_data[(j * (j + 1)) / 2 + i];
}
private:
std::array<value_type, (M * (M + 1)) / 2> m_data;
};
template <typename F>
using symmetric_matrix3x3 = symmetric_matrix_fixed<F, 3>;
template <typename F>
using symmetric_matrix4x4 = symmetric_matrix_fixed<F, 4>;
// --------------------------------------------------------------------
template <typename F = float>
class identity_matrix : public matrix_expression<identity_matrix<F>>
{
public:
using value_type = F;
identity_matrix(uint32_t n)
: m_n(n)
{
}
constexpr uint32_t dim_m() const { return m_n; }
constexpr uint32_t dim_n() const { return m_n; }
constexpr value_type operator()(uint32_t i, uint32_t j) const
{
return i == j ? 1 : 0;
}
private:
uint32_t m_n;
};
// --------------------------------------------------------------------
// matrix functions, implemented as expression templates
template <typename M1, typename M2>
class matrix_subtraction : public matrix_expression<matrix_subtraction<M1, M2>>
{
public:
matrix_subtraction(const M1 &m1, const M2 &m2)
: m_m1(m1)
, m_m2(m2)
{
assert(m_m1.dim_m() == m_m2.dim_m());
assert(m_m1.dim_n() == m_m2.dim_n());
}
constexpr uint32_t dim_m() const { return m_m1.dim_m(); }
constexpr uint32_t dim_n() const { return m_m1.dim_n(); }
constexpr auto operator()(uint32_t i, uint32_t j) const
{
return m_m1(i, j) - m_m2(i, j);
}
private:
const M1 &m_m1;
const M2 &m_m2;
};
template <typename M1, typename M2>
auto operator-(const matrix_expression<M1> &m1, const matrix_expression<M2> &m2)
{
return matrix_subtraction(m1, m2);
}
template <typename M1, typename M2>
class matrix_matrix_multiplication : public matrix_expression<matrix_matrix_multiplication<M1, M2>>
{
public:
matrix_matrix_multiplication(const M1 &m1, const M2 &m2)
: m_m1(m1)
, m_m2(m2)
{
assert(m1.dim_m() == m2.dim_n());
}
constexpr uint32_t dim_m() const { return m_m1.dim_m(); }
constexpr uint32_t dim_n() const { return m_m1.dim_n(); }
constexpr auto operator()(uint32_t i, uint32_t j) const
{
using value_type = decltype(m_m1(0, 0));
value_type result = {};
for (uint32_t k = 0; k < m_m1.dim_m(); ++k)
result += m_m1(i, k) * m_m2(k, j);
return result;
}
private:
const M1 &m_m1;
const M2 &m_m2;
};
template <typename M, typename T>
class matrix_scalar_multiplication : public matrix_expression<matrix_scalar_multiplication<M, T>>
{
public:
using value_type = T;
matrix_scalar_multiplication(const M &m, value_type v)
: m_m(m)
, m_v(v)
{
}
constexpr uint32_t dim_m() const { return m_m.dim_m(); }
constexpr uint32_t dim_n() const { return m_m.dim_n(); }
constexpr auto operator()(uint32_t i, uint32_t j) const
{
return m_m(i, j) * m_v;
}
private:
const M &m_m;
value_type m_v;
};
template <typename M1, typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
auto operator*(const matrix_expression<M1> &m, T v)
{
return matrix_scalar_multiplication(m, v);
}
template <typename M1, typename M2, std::enable_if_t<not std::is_floating_point_v<M2>, int> = 0>
auto operator*(const matrix_expression<M1> &m1, const matrix_expression<M2> &m2)
{
return matrix_matrix_multiplication(m1, m2);
}
// --------------------------------------------------------------------
template <typename M>
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)));
}
template <typename M>
M inverse(const M &m);
template <typename F = float>
matrix3x3<F> inverse(const matrix3x3<F> &m)
{
F det = determinant(m);
matrix3x3<F> result;
result(0, 0) = (m(1, 1) * m(2, 2) - m(1, 2) * m(2, 1)) / det;
result(1, 0) = (m(1, 2) * m(2, 0) - m(1, 0) * m(2, 2)) / det;
result(2, 0) = (m(1, 0) * m(2, 1) - m(1, 1) * m(2, 0)) / det;
result(0, 1) = (m(2, 1) * m(0, 2) - m(2, 2) * m(0, 1)) / det;
result(1, 1) = (m(2, 2) * m(0, 0) - m(2, 0) * m(0, 2)) / det;
result(2, 1) = (m(2, 0) * m(0, 1) - m(2, 1) * m(0, 0)) / det;
result(0, 2) = (m(0, 1) * m(1, 2) - m(0, 2) * m(1, 1)) / det;
result(1, 2) = (m(0, 2) * m(1, 0) - m(0, 0) * m(1, 2)) / det;
result(2, 2) = (m(0, 0) * m(1, 1) - m(0, 1) * m(1, 0)) / det;
return result;
}
// --------------------------------------------------------------------
template <typename M>
class matrix_cofactors : public matrix_expression<matrix_cofactors<M>>
{
public:
matrix_cofactors(const M &m)
: m_m(m)
{
}
constexpr uint32_t dim_m() const { return m_m.dim_m(); }
constexpr uint32_t dim_n() const { return m_m.dim_n(); }
constexpr auto operator()(uint32_t i, uint32_t j) const
{
const size_t ixs[4][3] = {
{ 1, 2, 3 },
{ 0, 2, 3 },
{ 0, 1, 3 },
{ 0, 1, 2 }
};
const size_t *ix = ixs[i];
const size_t *iy = ixs[j];
auto result =
m_m(ix[0], iy[0]) * m_m(ix[1], iy[1]) * m_m(ix[2], iy[2]) +
m_m(ix[0], iy[1]) * m_m(ix[1], iy[2]) * m_m(ix[2], iy[0]) +
m_m(ix[0], iy[2]) * m_m(ix[1], iy[0]) * m_m(ix[2], iy[1]) -
m_m(ix[0], iy[2]) * m_m(ix[1], iy[1]) * m_m(ix[2], iy[0]) -
m_m(ix[0], iy[1]) * m_m(ix[1], iy[0]) * m_m(ix[2], iy[2]) -
m_m(ix[0], iy[0]) * m_m(ix[1], iy[2]) * m_m(ix[2], iy[1]);
return (i + j) % 2 == 1 ? -result : result;
}
private:
const M &m_m;
};
} // namespace cif

View File

@@ -26,9 +26,9 @@
#pragma once
#include <numeric>
#include "cif++/atom_type.hpp"
#include <cif++/atom_type.hpp>
#include <numeric>
#if __cpp_lib_format
#include <format>
@@ -50,7 +50,7 @@ class atom
private:
struct atom_impl : public std::enable_shared_from_this<atom_impl>
{
atom_impl(datablock &db, std::string_view id)
atom_impl(const datablock &db, std::string_view id)
: m_db(db)
, m_cat(db["atom_site"])
, m_id(id)
@@ -101,17 +101,17 @@ class atom
row_handle row_aniso()
{
auto cat = m_db.get("atom_site_anisotrop");
return cat ? cat->find1(key("id") == m_id) : row_handle{};
return cat ? cat->operator[]({ {"id", m_id} }) : row_handle{};
}
const row_handle row_aniso() const
{
auto cat = m_db.get("atom_site_anisotrop");
return cat ? cat->find1(key("id") == m_id) : row_handle{};
return cat ? cat->operator[]({ {"id", m_id} }) : row_handle{};
}
const datablock &m_db;
category &m_cat;
const category &m_cat;
std::string m_id;
point m_location;
std::string m_symop = "1_555";
@@ -130,7 +130,7 @@ class atom
{
}
atom(datablock &db, row_handle &row)
atom(const datablock &db, const row_handle &row)
: atom(std::make_shared<atom_impl>(db, row["id"].as<std::string>()))
{
}
@@ -216,6 +216,14 @@ class atom
set_location(loc);
}
/// \brief rotate the coordinates of this atom by \a q around point \a p
void rotate(quaternion q, point p)
{
auto loc = get_location();
loc.rotate(q, p);
set_location(loc);
}
/// \brief Translate and rotate the position of this atom by \a t and \a q
void translate_and_rotate(point t, quaternion q)
{
@@ -321,13 +329,6 @@ class atom
friend std::ostream &operator<<(std::ostream &os, const atom &atom);
// /// \brief Synchronize data with underlying cif data
// void sync()
// {
// if (m_impl)
// m_impl->prefetch();
// }
private:
friend class structure;
@@ -341,25 +342,6 @@ class atom
std::shared_ptr<atom_impl> m_impl;
};
// template <>
// inline std::string atom::get_property<std::string>(const std::string_view name) const
// {
// return get_property(name);
// }
// template <>
// inline int atom::get_property<int>(const std::string_view name) const
// {
// auto v = impl().get_property(name);
// return v.empty() ? 0 : stoi(v);
// }
// template <>
// inline float atom::get_property<float>(const std::string_view name) const
// {
// return stof(impl().get_property(name));
// }
inline void swap(atom &a, atom &b)
{
a.swap(b);
@@ -394,7 +376,7 @@ class residue
friend class structure;
// constructor
residue(const structure &structure, const std::string &compoundID,
residue(structure &structure, const std::string &compoundID,
const std::string &asymID, int seqID,
const std::string &authAsymID, const std::string &authSeqID,
const std::string &pdbInsCode)
@@ -408,6 +390,8 @@ class residue
{
}
residue(structure &structure, const std::vector<atom> &atoms);
residue(const residue &rhs) = delete;
residue &operator=(const residue &rhs) = delete;
@@ -430,7 +414,7 @@ class residue
const std::string &get_compound_id() const { return m_compound_id; }
void set_compound_id(const std::string &id) { m_compound_id = id; }
const structure *get_structure() const { return m_structure; }
structure *get_structure() const { return m_structure; }
// const compound &compound() const;
@@ -487,7 +471,7 @@ class residue
protected:
residue() {}
const structure *m_structure = nullptr;
structure *m_structure = nullptr;
std::string m_compound_id, m_asym_id;
int m_seq_id = 0;
std::string m_auth_asym_id, m_auth_seq_id, m_pdb_ins_code;
@@ -571,7 +555,7 @@ class monomer : public residue
class polymer : public std::vector<monomer>
{
public:
polymer(const structure &s, const std::string &entityID, const std::string &asymID, const std::string &auth_asym_id);
polymer(structure &s, const std::string &entityID, const std::string &asymID, const std::string &auth_asym_id);
polymer(const polymer &) = delete;
polymer &operator=(const polymer &) = delete;
@@ -579,7 +563,7 @@ class polymer : public std::vector<monomer>
// monomer &getBySeqID(int seqID);
// const monomer &getBySeqID(int seqID) const;
const structure *get_structure() const { return m_structure; }
structure *get_structure() const { return m_structure; }
std::string get_asym_id() const { return m_asym_id; }
std::string get_auth_asym_id() const { return m_auth_asym_id; } // The PDB chain ID, actually
@@ -588,7 +572,7 @@ class polymer : public std::vector<monomer>
// int Distance(const monomer &a, const monomer &b) const;
private:
const structure *m_structure;
structure *m_structure;
std::string m_entity_id;
std::string m_asym_id;
std::string m_auth_asym_id;
@@ -602,7 +586,7 @@ class branch;
class sugar : public residue
{
public:
sugar(const branch &branch, const std::string &compoundID,
sugar(branch &branch, const std::string &compoundID,
const std::string &asymID, int authSeqID);
sugar(sugar &&rhs);
@@ -629,27 +613,44 @@ class sugar : public residue
return result;
}
cif::mm::atom add_atom(row_initializer atom_info);
private:
const branch *m_branch;
branch *m_branch;
atom m_link;
};
class branch : public std::vector<sugar>
{
public:
branch(structure &structure, const std::string &asymID);
branch(structure &structure, const std::string &asym_id, const std::string &entity_id);
branch(const branch &) = delete;
branch &operator=(const branch &) = delete;
branch(branch &&) = default;
branch &operator=(branch &&) = default;
void link_atoms();
std::string name() const;
float weight() const;
std::string get_asym_id() const { return m_asym_id; }
std::string get_entity_id() const { return m_entity_id; }
structure &get_structure() { return *m_structure; }
const structure &get_structure() const { return *m_structure; }
structure &get_structure() const { return *m_structure; }
sugar &getSugarByNum(int nr);
const sugar &getSugarByNum(int nr) const;
sugar &get_sugar_by_num(int nr);
const sugar &get_sugar_by_num(int nr) const
{
return const_cast<branch *>(this)->get_sugar_by_num(nr);
}
sugar &construct_sugar(const std::string &compound_id);
sugar &construct_sugar(const std::string &compound_id, const std::string &atom_id,
int linked_sugar_nr, const std::string &linked_atom_id);
private:
friend sugar;
@@ -657,40 +658,9 @@ class branch : public std::vector<sugar>
std::string name(const sugar &s) const;
structure *m_structure;
std::string m_asym_id;
std::string m_asym_id, m_entity_id;
};
// // --------------------------------------------------------------------
// // file is a reference to the data stored in e.g. the cif file.
// // This object is not copyable.
// class File : public file
// {
// public:
// File() {}
// // File(const std::filesystem::path &path)
// // {
// // load(path);
// // }
// // File(const char *data, size_t length)
// // {
// // load(data, length);
// // }
// File(const File &) = delete;
// File &operator=(const File &) = delete;
// // void load(const std::filesystem::path &p) override;
// // void save(const std::filesystem::path &p) override;
// // using file::load;
// // using file::save;
// datablock &data() { return front(); }
// };
// --------------------------------------------------------------------
enum class StructureOpenOptions
@@ -698,7 +668,7 @@ enum class StructureOpenOptions
SkipHydrogen = 1 << 0
};
inline bool operator&(StructureOpenOptions a, StructureOpenOptions b)
constexpr inline bool operator&(StructureOpenOptions a, StructureOpenOptions b)
{
return static_cast<int>(a) bitand static_cast<int>(b);
}
@@ -751,6 +721,7 @@ class structure
const std::vector<residue> &non_polymers() const { return m_non_polymers; }
bool has_atom_id(const std::string &id) const;
atom get_atom_by_id(const std::string &id) const;
// atom getAtomByLocation(point pt, float maxDistance) const;
@@ -763,6 +734,9 @@ class structure
/// \brief Return the atom closest to point \a p with atom type \a type in a residue of type \a res_type
atom get_atom_by_position_and_type(point p, std::string_view type, std::string_view res_type) const;
/// \brief Create a non-poly residue based on atoms already present in this structure.
residue &create_residue(const std::vector<atom> &atoms);
/// \brief Get a non-poly residue for an asym with id \a asymID
residue &get_residue(const std::string &asymID)
{
@@ -820,10 +794,7 @@ class structure
///
/// \param asym_id The asym ID
/// \param seq_id The sequence ID
void remove_residue(const std::string &asym_id, int seq_id, const std::string &auth_seq_id)
{
remove_residue(get_residue(asym_id, seq_id, auth_seq_id));
}
void remove_residue(const std::string &asym_id, int seq_id, const std::string &auth_seq_id);
/// \brief Create a new non-polymer entity, returns new ID
/// \param mon_id The mon_id for the new nonpoly, must be an existing and known compound from CCD
@@ -846,6 +817,15 @@ 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 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 new and empty (sugar) branch
branch &create_branch();
/// \brief Create a new (sugar) branch with one first NAG containing atoms constructed from \a atoms
branch &create_branch(std::vector<row_initializer> atoms);
@@ -893,6 +873,18 @@ class structure
void validate_atoms() const;
// TODO: make this protected?
void load_atoms_for_model(StructureOpenOptions options);
template <typename... Args>
atom &emplace_atom(Args&... args)
{
return emplace_atom(atom{ std::forward<Args>(args)... });
}
atom &emplace_atom(atom &&atom);
private:
friend polymer;
friend residue;
@@ -903,16 +895,6 @@ class structure
void load_data();
void load_atoms_for_model(StructureOpenOptions options);
template <typename... Args>
atom &emplace_atom(Args... args)
{
return emplace_atom(atom{ std::forward<Args>(args)... });
}
atom &emplace_atom(atom &&atom);
void remove_atom(atom &a, bool removeFromResidue);
void remove_sugar(sugar &sugar);
@@ -925,4 +907,11 @@ class structure
std::vector<residue> m_non_polymers;
};
// --------------------------------------------------------------------
/// \brief Reconstruct all missing categories for an assumed PDBx file.
/// Some people believe that simply dumping some atom records is enough.
/// \param db The cif::datablock that hopefully contains some valid data
void reconstruct_pdbx(datablock &db);
} // namespace cif::mm

View File

@@ -26,9 +26,9 @@
#pragma once
#include <map>
#include "cif++/row.hpp"
#include <cif++/row.hpp>
#include <map>
namespace cif
{
@@ -53,8 +53,6 @@ class sac_parser
public:
using datablock_index = std::map<std::string, std::size_t>;
sac_parser(std::istream &is, bool init = true);
virtual ~sac_parser() = default;
enum CharTraitsMask : uint8_t
@@ -65,9 +63,14 @@ class sac_parser
kAnyPrintMask = 1 << 3
};
static bool is_white(int ch)
static constexpr bool is_space(int ch)
{
return std::isspace(ch) or ch == '#';
return ch == ' ' or ch == '\t' or ch == '\r' or ch == '\n';
}
static constexpr bool is_white(int ch)
{
return is_space(ch) or ch == '#';
}
static constexpr bool is_ordinary(int ch)
@@ -91,26 +94,7 @@ class sac_parser
(ch >= 0x20 and ch <= 0x7f and (kCharTraitsTable[ch - 0x20] & kAnyPrintMask) != 0);
}
static bool is_unquoted_string(std::string_view text)
{
auto s = text.begin();
bool result = is_ordinary(*s++);
while (result and s != text.end())
{
result = is_non_blank(*s);
++s;
}
// but be careful it does not contain e.g. stop_
if (result)
{
static const std::regex reservedRx(R"((^(?:data|save)|.*(?:loop|stop|global))_.+)", std::regex_constants::icase);
result = not std::regex_match(text.begin(), text.end(), reservedRx);
}
return result;
}
static bool is_unquoted_string(std::string_view text);
protected:
static constexpr uint8_t kCharTraitsTable[128] = {
@@ -132,7 +116,8 @@ class sac_parser
DATA,
LOOP,
GLOBAL,
SAVE,
SAVE_,
SAVE_NAME,
STOP,
Tag,
Value
@@ -147,7 +132,8 @@ class sac_parser
case CIFToken::DATA: return "DATA";
case CIFToken::LOOP: return "LOOP";
case CIFToken::GLOBAL: return "GLOBAL";
case CIFToken::SAVE: return "SAVE";
case CIFToken::SAVE_: return "SAVE";
case CIFToken::SAVE_NAME: return "SAVE+name";
case CIFToken::STOP: return "STOP";
case CIFToken::Tag: return "Tag";
case CIFToken::Value: return "Value";
@@ -155,41 +141,13 @@ class sac_parser
}
}
enum class CIFValue
{
Int,
Float,
Numeric,
String,
TextField,
Inapplicable,
Unknown
};
static constexpr const char *get_value_name(CIFValue type)
{
switch (type)
{
case CIFValue::Int: return "Int";
case CIFValue::Float: return "Float";
case CIFValue::Numeric: return "Numeric";
case CIFValue::String: return "String";
case CIFValue::TextField: return "TextField";
case CIFValue::Inapplicable: return "Inapplicable";
case CIFValue::Unknown: return "Unknown";
default: return "Invalid type parameter";
}
}
// get_next_char takes a char from the buffer, or if it is empty
// from the istream. This function also does carriage/linefeed
// translation.
// get_next_char takes the next character from the istream.
// This function also does carriage/linefeed translation.
int get_next_char();
// Put the last read character back into the istream
void retract();
int restart(int start);
CIFToken get_next_token();
void match(CIFToken token);
@@ -204,6 +162,9 @@ class sac_parser
void parse_file();
protected:
sac_parser(std::istream &is, bool init = true);
void parse_global();
void parse_datablock();
@@ -226,13 +187,14 @@ class sac_parser
// production methods, these are pure virtual here
virtual void produce_datablock(const std::string &name) = 0;
virtual void produce_category(const std::string &name) = 0;
virtual void produce_datablock(std::string_view name) = 0;
virtual void produce_category(std::string_view name) = 0;
virtual void produce_row() = 0;
virtual void produce_item(const std::string &category, const std::string &item, const std::string &value) = 0;
virtual void produce_item(std::string_view category, std::string_view item, std::string_view value) = 0;
protected:
enum State
enum class State
{
Start,
White,
@@ -245,23 +207,21 @@ class sac_parser
UnquotedString,
Tag,
TextField,
Float = 100,
Int = 110,
Value = 300,
DATA,
SAVE
TextFieldNL,
Reserved,
Value
};
std::streambuf &m_source;
// Parser state
bool m_validate;
uint32_t m_line_nr;
bool m_bol;
CIFToken m_lookahead;
std::string m_token_value;
CIFValue mTokenType;
std::vector<int> m_buffer; // retract buffer, used to be a stack<char>
// token buffer
std::vector<char> m_token_buffer;
std::string_view m_token_value;
};
// --------------------------------------------------------------------
@@ -275,13 +235,13 @@ class parser : public sac_parser
{
}
void produce_datablock(const std::string &name) override;
void produce_datablock(std::string_view name) override;
void produce_category(const std::string &name) override;
void produce_category(std::string_view name) override;
void produce_row() override;
void produce_item(const std::string &category, const std::string &item, const std::string &value) override;
void produce_item(std::string_view category, std::string_view item, std::string_view value) override;
protected:
file &m_file;

View File

@@ -26,7 +26,7 @@
#pragma once
#include <cif++.hpp>
#include "cif++/datablock.hpp"
namespace cif::pdb
{

View File

@@ -26,7 +26,7 @@
#pragma once
#include <cif++.hpp>
#include "cif++/datablock.hpp"
namespace cif::pdb
{

View File

@@ -26,8 +26,7 @@
#pragma once
#include <cif++.hpp>
#include "cif++/file.hpp"
namespace cif::pdb
{

View File

@@ -26,7 +26,7 @@
#pragma once
#include <cif++/pdb/pdb2cif.hpp>
#include "pdb2cif.hpp"
// --------------------------------------------------------------------

View File

@@ -26,19 +26,15 @@
#pragma once
#include "cif++/datablock.hpp"
#include <string>
#include <tuple>
#include <vector>
#include <cif++.hpp>
namespace cif
{
extern const int
kResidueNrWildcard,
kNoSeqNum;
struct tls_selection;
struct tls_residue;

View File

@@ -26,6 +26,9 @@
#pragma once
#include "cif++/exports.hpp"
#include <array>
#include <cmath>
#include <complex>
#include <functional>
@@ -54,7 +57,7 @@ class quaternion_type
public:
using value_type = T;
constexpr explicit quaternion_type(value_type const &value_a = value_type(), value_type const &value_b = value_type(), value_type const &value_c = value_type(), value_type const &value_d = value_type())
constexpr explicit quaternion_type(value_type const &value_a = {}, value_type const &value_b = {}, value_type const &value_c = {}, value_type const &value_d = {})
: a(value_a)
, b(value_b)
, c(value_c)
@@ -305,6 +308,21 @@ class quaternion_type
constexpr value_type get_c() const { return c; }
constexpr value_type get_d() const { return d; }
constexpr bool operator==(const quaternion_type &rhs) const
{
return a == rhs.a and b == rhs.b and c == rhs.c and d == rhs.d;
}
constexpr bool operator!=(const quaternion_type &rhs) const
{
return a != rhs.a or b != rhs.b or c != rhs.c or d != rhs.d;
}
constexpr operator bool() const
{
return a != 0 or b != 0 or c != 0 or d != 0;
}
private:
value_type a, b, c, d;
};
@@ -483,6 +501,13 @@ struct point_type
m_z = p.get_d();
}
constexpr void rotate(const quaternion &q, point_type pivot)
{
operator-=(pivot);
rotate(q);
operator+=(pivot);
}
#if HAVE_LIBCLIPPER
operator clipper::Coord_orth() const
{
@@ -665,6 +690,12 @@ point nudge(point p, float offset);
quaternion construct_from_angle_axis(float angle, point axis);
std::tuple<double, point> quaternion_to_angle_axis(quaternion q);
/// @brief Given four points and an angle, return the quaternion required to rotate
/// point p4 along the p2-p3 axis and around point p3 to obtain the required within
/// an accuracy of esd
quaternion construct_for_dihedral_angle(point p1, point p2, point p3, point p4,
float angle, float esd);
point centroid(const std::vector<point> &Points);
point center_points(std::vector<point> &Points);
@@ -687,7 +718,6 @@ template <int N>
class spherical_dots
{
public:
constexpr static int P = 2 * N * 1;
using array_type = typename std::array<point, P>;

View File

@@ -26,7 +26,9 @@
#pragma once
#include <cif++/item.hpp>
#include "cif++/item.hpp"
#include <array>
namespace cif
{
@@ -110,12 +112,12 @@ class row : public std::vector<item_value>
item_value* get(uint16_t ix)
{
return ix < size() ? &at(ix) : nullptr;
return ix < size() ? &data()[ix] : nullptr;
}
const item_value* get(uint16_t ix) const
{
return ix < size() ? &at(ix) : nullptr;
return ix < size() ? &data()[ix] : nullptr;
}
private:
@@ -208,14 +210,14 @@ class row_handle
return detail::get_row_result<C...>(*this, { get_column_ix(columns)... });
}
template <typename... Ts, typename... C, std::enable_if_t<sizeof...(Ts) == sizeof...(C), int> = 0>
template <typename... Ts, typename... C, std::enable_if_t<sizeof...(Ts) == sizeof...(C) and sizeof...(C) != 1, int> = 0>
std::tuple<Ts...> get(C... columns) const
{
return detail::get_row_result<Ts...>(*this, { get_column_ix(columns)... });
}
template <typename T>
T get(const char *column)
T get(const char *column) const
{
return operator[](get_column_ix(column)).template as<T>();
}

View File

@@ -26,15 +26,34 @@
#pragma once
#include "cif++/exports.hpp"
#include "cif++/matrix.hpp"
#include "cif++/point.hpp"
#include <array>
#include <cstdint>
#include <string>
/// \file cif++/symmetry.hpp
/// This file contains code to do symmetry operations based on the
/// operations as specified in the International Tables.
namespace cif
{
// --------------------------------------------------------------------
inline point operator*(const matrix3x3<float> &m, const point &pt)
{
return {
m(0, 0) * pt.m_x + m(0, 1) * pt.m_y + m(0, 2) * pt.m_z,
m(1, 0) * pt.m_x + m(1, 1) * pt.m_y + m(1, 2) * pt.m_z,
m(2, 0) * pt.m_x + m(2, 1) * pt.m_y + m(2, 2) * pt.m_z
};
}
// --------------------------------------------------------------------
enum class space_group_name
{
full,
@@ -50,29 +69,29 @@ struct space_group
int nr;
};
extern const space_group kSpaceGroups[];
extern const std::size_t kNrOfSpaceGroups;
extern CIFPP_EXPORT const space_group kSpaceGroups[];
extern CIFPP_EXPORT const std::size_t kNrOfSpaceGroups;
// --------------------------------------------------------------------
struct symop_data
{
constexpr symop_data(const std::array<int, 15> &data)
: m_packed((data[0] & 0x03ULL) << 34 bitor
(data[1] & 0x03ULL) << 32 bitor
(data[2] & 0x03ULL) << 30 bitor
(data[3] & 0x03ULL) << 28 bitor
(data[4] & 0x03ULL) << 26 bitor
(data[5] & 0x03ULL) << 24 bitor
(data[6] & 0x03ULL) << 22 bitor
(data[7] & 0x03ULL) << 20 bitor
(data[8] & 0x03ULL) << 18 bitor
(data[9] & 0x07ULL) << 15 bitor
(data[10] & 0x07ULL) << 12 bitor
(data[11] & 0x07ULL) << 9 bitor
(data[12] & 0x07ULL) << 6 bitor
(data[13] & 0x07ULL) << 3 bitor
(data[14] & 0x07ULL) << 0)
: m_packed((data[0] bitand 0x03ULL) << 34 bitor
(data[1] bitand 0x03ULL) << 32 bitor
(data[2] bitand 0x03ULL) << 30 bitor
(data[3] bitand 0x03ULL) << 28 bitor
(data[4] bitand 0x03ULL) << 26 bitor
(data[5] bitand 0x03ULL) << 24 bitor
(data[6] bitand 0x03ULL) << 22 bitor
(data[7] bitand 0x03ULL) << 20 bitor
(data[8] bitand 0x03ULL) << 18 bitor
(data[9] bitand 0x07ULL) << 15 bitor
(data[10] bitand 0x07ULL) << 12 bitor
(data[11] bitand 0x07ULL) << 9 bitor
(data[12] bitand 0x07ULL) << 6 bitor
(data[13] bitand 0x07ULL) << 3 bitor
(data[14] bitand 0x07ULL) << 0)
{
}
@@ -86,24 +105,35 @@ struct symop_data
return m_packed < rhs.m_packed;
}
std::array<int, 15> data() const
inline constexpr int unpack3(int offset) const
{
int result = (m_packed >> offset) bitand 0x03;
return result == 3 ? -1 : result;
}
inline constexpr int unpack7(int offset) const
{
return (m_packed >> offset) bitand 0x07;
}
constexpr std::array<int, 15> data() const
{
return {
static_cast<int>(m_packed >> 34) bitand 0x03,
static_cast<int>(m_packed >> 32) bitand 0x03,
static_cast<int>(m_packed >> 30) bitand 0x03,
static_cast<int>(m_packed >> 28) bitand 0x03,
static_cast<int>(m_packed >> 26) bitand 0x03,
static_cast<int>(m_packed >> 24) bitand 0x03,
static_cast<int>(m_packed >> 22) bitand 0x03,
static_cast<int>(m_packed >> 20) bitand 0x03,
static_cast<int>(m_packed >> 18) bitand 0x03,
static_cast<int>(m_packed >> 15) bitand 0x07,
static_cast<int>(m_packed >> 12) bitand 0x07,
static_cast<int>(m_packed >> 9) bitand 0x07,
static_cast<int>(m_packed >> 6) bitand 0x07,
static_cast<int>(m_packed >> 3) bitand 0x07,
static_cast<int>(m_packed >> 0) bitand 0x07,
unpack3(34),
unpack3(32),
unpack3(30),
unpack3(28),
unpack3(26),
unpack3(24),
unpack3(22),
unpack3(20),
unpack3(18),
unpack7(15),
unpack7(12),
unpack7(9),
unpack7(6),
unpack7(3),
unpack7(0)
};
}
@@ -123,8 +153,8 @@ struct symop_data
struct symop_datablock
{
constexpr symop_datablock(int spacegroup, int rotational_number, const std::array<int, 15> &rt_data)
: m_v((spacegroup & 0xffffULL) << 48 bitor
(rotational_number & 0xffULL) << 40 bitor
: m_v((spacegroup bitand 0xffffULL) << 48 bitor
(rotational_number bitand 0xffULL) << 40 bitor
symop_data(rt_data).m_packed)
{
}
@@ -139,12 +169,250 @@ struct symop_datablock
static_assert(sizeof(symop_datablock) == sizeof(uint64_t), "Size of symop_data is wrong");
extern const symop_datablock kSymopNrTable[];
extern const std::size_t kSymopNrTableSize;
extern CIFPP_EXPORT const symop_datablock kSymopNrTable[];
extern CIFPP_EXPORT const std::size_t kSymopNrTableSize;
// --------------------------------------------------------------------
// Some more symmetry related stuff here.
class datablock;
class cell;
class spacegroup;
class rtop;
class sym_op;
/// @brief A class that encapsulates the symmetry operations as used in PDB files, i.e. a rotational number and a translation vector
/// The syntax in string format follows the syntax as used in mmCIF files, i.e. rotational number followed by underscore and the
/// three translations where 5 is no movement.
struct sym_op
{
public:
sym_op(uint8_t nr = 1, uint8_t ta = 5, uint8_t tb = 5, uint8_t tc = 5)
: m_nr(nr)
, m_ta(ta)
, m_tb(tb)
, m_tc(tc)
{
}
explicit sym_op(std::string_view s);
sym_op(const sym_op &) = default;
sym_op(sym_op &&) = default;
sym_op &operator=(const sym_op &) = default;
sym_op &operator=(sym_op &&) = default;
constexpr bool is_identity() const
{
return m_nr == 1 and m_ta == 5 and m_tb == 5 and m_tc == 5;
}
explicit constexpr operator bool() const
{
return not is_identity();
}
std::string string() const;
#if defined(__cpp_impl_three_way_comparison)
constexpr auto operator<=>(const sym_op &rhs) const = default;
#else
constexpr bool operator==(const sym_op &rhs) const
{
return m_nr == rhs.m_nr and m_ta == rhs.m_ta and m_tb == rhs.m_tb and m_tc == rhs.m_tc;
}
constexpr bool operator!=(const sym_op &rhs) const
{
return not operator==(rhs);
}
#endif
uint8_t m_nr;
uint8_t m_ta, m_tb, m_tc;
};
static_assert(sizeof(sym_op) == 4, "Sym_op should be four bytes");
namespace literals
{
inline sym_op operator""_symop(const char *text, size_t length)
{
return sym_op({ text, length });
}
} // namespace literals
// --------------------------------------------------------------------
// The transformation class
class transformation
{
public:
transformation(const symop_data &data);
transformation(const matrix3x3<float> &r, const cif::point &t);
transformation(const transformation &) = default;
transformation(transformation &&) = default;
transformation &operator=(const transformation &) = default;
transformation &operator=(transformation &&) = default;
point operator()(point pt) const
{
if (m_q)
pt.rotate(m_q);
else
pt = m_rotation * pt;
return pt + m_translation;
}
friend transformation operator*(const transformation &lhs, const transformation &rhs);
friend transformation inverse(const transformation &t);
transformation operator-() const
{
return inverse(*this);
}
friend class spacegroup;
private:
// Most rotation matrices provided by the International Tables
// are really rotation matrices, in those cases we can construct
// a quaternion. Unfortunately, that doesn't work for all of them
void try_create_quaternion();
matrix3x3<float> m_rotation;
quaternion m_q;
point m_translation;
};
// --------------------------------------------------------------------
// class cell
class cell
{
public:
cell(float a, float b, float c, float alpha = 90.f, float beta = 90.f, float gamma = 90.f);
cell(const datablock &db);
float get_a() const { return m_a; }
float get_b() const { return m_b; }
float get_c() const { return m_c; }
float get_alpha() const { return m_alpha; }
float get_beta() const { return m_beta; }
float get_gamma() const { return m_gamma; }
matrix3x3<float> get_orthogonal_matrix() const { return m_orthogonal; }
matrix3x3<float> get_fractional_matrix() const { return m_fractional; }
private:
void init();
float m_a, m_b, m_c, m_alpha, m_beta, m_gamma;
matrix3x3<float> m_orthogonal, m_fractional;
};
// --------------------------------------------------------------------
int get_space_group_number(std::string spacegroup); // alternative for clipper's parsing code, using space_group_name::full
int get_space_group_number(std::string spacegroup, space_group_name type); // alternative for clipper's parsing code
int get_space_group_number(const datablock &db);
int get_space_group_number(std::string_view spacegroup);
int get_space_group_number(std::string_view spacegroup, space_group_name type);
class spacegroup : public std::vector<transformation>
{
public:
spacegroup(const datablock &db)
: spacegroup(get_space_group_number(db))
{
}
spacegroup(std::string_view name)
: spacegroup(get_space_group_number(name))
{
}
spacegroup(std::string_view name, space_group_name type)
: spacegroup(get_space_group_number(name, type))
{
}
spacegroup(int nr);
int get_nr() const { return m_nr; }
std::string get_name() const;
point operator()(const point &pt, const cell &c, sym_op symop) const;
point inverse(const point &pt, const cell &c, sym_op symop) const;
private:
int m_nr;
size_t m_index;
};
// --------------------------------------------------------------------
// A crystal combines a cell and a spacegroup.
class crystal
{
public:
crystal(const datablock &db)
: m_cell(db)
, m_spacegroup(db)
{
}
crystal(const cell &c, const spacegroup &sg)
: m_cell(c)
, m_spacegroup(sg)
{
}
crystal(const crystal &) = default;
crystal(crystal &&) = default;
crystal &operator=(const crystal &) = default;
crystal &operator=(crystal &&) = default;
const cell &get_cell() const { return m_cell; }
const spacegroup &get_spacegroup() const { return m_spacegroup; }
point symmetry_copy(const point &pt, sym_op symop) const
{
return m_spacegroup(pt, m_cell, symop);
}
point inverse_symmetry_copy(const point &pt, sym_op symop) const
{
return m_spacegroup.inverse(pt, m_cell, symop);
}
std::tuple<float,point,sym_op> closest_symmetry_copy(point a, point b) const;
private:
cell m_cell;
spacegroup m_spacegroup;
};
// --------------------------------------------------------------------
// Symmetry operations on points
inline point orthogonal(const point &pt, const cell &c)
{
return c.get_orthogonal_matrix() * pt;
}
inline point fractional(const point &pt, const cell &c)
{
return c.get_fractional_matrix() * pt;
}
// --------------------------------------------------------------------
} // namespace cif

View File

@@ -26,78 +26,22 @@
#pragma once
#include "cif++/exports.hpp"
#include <charconv>
#include <cmath>
#include <cstdint>
#include <limits>
#include <set>
#include <sstream>
#include <tuple>
#include <vector>
#if __has_include(<experimental/type_traits>)
#include <experimental/type_traits>
#else
#include <type_traits>
#endif
#if (not defined(__cpp_lib_experimental_detect) or (__cpp_lib_experimental_detect < 201505)) and (not defined(_LIBCPP_VERSION) or _LIBCPP_VERSION < 5000)
// This code is copied from:
// https://ld2015.scusa.lsu.edu/cppreference/en/cpp/experimental/is_detected.html
namespace std
{
template< class... >
using void_t = void;
namespace experimental
{
namespace detail
{
template <class Default, class AlwaysVoid,
template<class...> class Op, class... Args>
struct detector
{
using value_t = false_type;
using type = Default;
};
template <class Default, template<class...> class Op, class... Args>
struct detector<Default, void_t<Op<Args...>>, Op, Args...> {
// Note that std::void_t is a c++17 feature
using value_t = true_type;
using type = Op<Args...>;
};
} // namespace detail
struct nonesuch
{
nonesuch() = delete;
~nonesuch() = delete;
nonesuch(nonesuch const&) = delete;
void operator=(nonesuch const&) = delete;
};
template <template<class...> class Op, class... Args>
using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;
template <template<class...> class Op, class... Args>
constexpr inline bool is_detected_v = is_detected<Op,Args...>::value;
template <template<class...> class Op, class... Args>
using detected_t = typename detail::detector<nonesuch, void, Op, Args...>::type;
template <class Default, template<class...> class Op, class... Args>
using detected_or = detail::detector<Default, void, Op, Args...>;
template <class Expected, template <class...> class Op, class... Args>
using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
template <class Expected, template<class...> class Op, class... Args>
constexpr inline bool is_detected_exact_v = is_detected_exact<Expected, Op, Args...>::value;
}
}
// sub optimal, but replicating the same code is worse
#include <zeep/type-traits.hpp>
#endif
namespace cif
@@ -249,7 +193,7 @@ typedef std::set<std::string, iless> iset;
// --------------------------------------------------------------------
// This really makes a difference, having our own tolower routines
extern const uint8_t kCharToLowerMap[256];
extern CIFPP_EXPORT const uint8_t kCharToLowerMap[256];
inline char tolower(int ch)
{
@@ -448,8 +392,8 @@ std::to_chars_result to_chars(char *first, char *last, FloatType &value, chars_f
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 = last - first;
int r;
int size = static_cast<int>(last - first);
int r = 0;
switch (fmt)
{

View File

@@ -26,6 +26,8 @@
#pragma once
#include "cif++/exports.hpp"
#include <filesystem>
#ifndef STDOUT_FILENO
@@ -50,7 +52,7 @@
namespace cif
{
extern int VERBOSE;
extern CIFPP_EXPORT int VERBOSE;
// the git 'build' number
std::string get_version_nr();
@@ -155,11 +157,11 @@ inline auto coloured(std::basic_string<CharT, Traits, Alloc> &s, StringColour fo
// --------------------------------------------------------------------
// A progress bar
class Progress
class progress_bar
{
public:
Progress(int64_t inMax, const std::string &inAction);
virtual ~Progress();
progress_bar(int64_t inMax, const std::string &inAction);
~progress_bar();
void consumed(int64_t inConsumed); // consumed is relative
void progress(int64_t inProgress); // progress is absolute
@@ -167,10 +169,10 @@ class Progress
void message(const std::string &inMessage);
private:
Progress(const Progress &) = delete;
Progress &operator=(const Progress &) = delete;
progress_bar(const progress_bar &) = delete;
progress_bar &operator=(const progress_bar &) = delete;
struct ProgressImpl *m_impl;
struct progress_bar_impl *m_impl;
};
// --------------------------------------------------------------------

View File

@@ -26,13 +26,13 @@
#pragma once
#include "cif++/text.hpp"
#include <filesystem>
#include <list>
#include <mutex>
#include <utility>
#include <cif++/text.hpp>
namespace cif
{
@@ -228,8 +228,9 @@ class validator_factory
const validator &operator[](std::string_view dictionary_name);
const validator &construct_validator(std::string_view name, std::istream &is);
private:
void construct_validator(std::string_view name, std::istream &is);
// --------------------------------------------------------------------

View File

@@ -8,6 +8,6 @@ Name: libcifpp
Description: C++ library for the manipulation of mmCIF files.
Version: @PACKAGE_VERSION@
Requires.private: zlib, liblzma
Requires: zlib
Libs: -L${libdir} -lcifpp
Cflags: -I${includedir} -pthread

View File

@@ -26,8 +26,7 @@
#include <cmath>
#include <cif++.hpp>
#include <cif++/atom_type.hpp>
#include "cif++.hpp"
namespace cif
{
@@ -182,24 +181,24 @@ const struct ionic_radii
{ F, { kNA, kNA, 119, kNA, kNA, kNA, kNA, kNA, kNA, 22, kNA } }, // Fluorine
{ Na, { kNA, kNA, kNA, 116, kNA, kNA, kNA, kNA, kNA, kNA, kNA } }, // Sodium
{ Mg, { kNA, kNA, kNA, kNA, 86, kNA, kNA, kNA, kNA, kNA, kNA } }, // Magnesium
{ Al, { kNA, kNA, kNA, kNA, kNA, 67.5, kNA, kNA, kNA, kNA, kNA } }, // Aluminium
{ Al, { kNA, kNA, kNA, kNA, kNA, 67.5f, kNA, kNA, kNA, kNA, kNA } }, // Aluminium
{ Si, { kNA, kNA, kNA, kNA, kNA, kNA, 54, kNA, kNA, kNA, kNA } }, // Silicon
{ P, { kNA, kNA, kNA, kNA, kNA, 58, kNA, 52, kNA, kNA, kNA } }, // Phosphorus
{ S, { kNA, 170, kNA, kNA, kNA, kNA, 51, kNA, 43, kNA, kNA } }, // Sulfur
{ Cl, { kNA, kNA, 181, kNA, kNA, kNA, kNA, 26, kNA, 41, kNA } }, // Chlorine
{ K, { kNA, kNA, kNA, 152, kNA, kNA, kNA, kNA, kNA, kNA, kNA } }, // Potassium
{ Ca, { kNA, kNA, kNA, kNA, 114, kNA, kNA, kNA, kNA, kNA, kNA } }, // Calcium
{ Sc, { kNA, kNA, kNA, kNA, kNA, 88.5, kNA, kNA, kNA, kNA, kNA } }, // Scandium
{ Ti, { kNA, kNA, kNA, kNA, 100, 81, 74.5, kNA, kNA, kNA, kNA } }, // Titanium
{ Sc, { kNA, kNA, kNA, kNA, kNA, 88.5f, kNA, kNA, kNA, kNA, kNA } }, // Scandium
{ Ti, { kNA, kNA, kNA, kNA, 100, 81, 74.5f, kNA, kNA, kNA, kNA } }, // Titanium
{ V, { kNA, kNA, kNA, kNA, 93, 78, 72, 68, kNA, kNA, kNA } }, // Vanadium
{ Cr, { kNA, kNA, kNA, kNA, 87, 75.5, 69, 63, 58, kNA, kNA } }, // Chromium ls
{ Cr, { kNA, kNA, kNA, kNA, 87, 75.5f, 69, 63, 58, kNA, kNA } }, // Chromium ls
// { Cr,{ kNA, kNA, kNA, kNA, 94, kNA, kNA, kNA, kNA, kNA, kNA } }, // Chromium hs
{ Mn, { kNA, kNA, kNA, kNA, 81, 72, 67, 47, 39.5, 60, kNA } }, // Manganese ls
// { Mn,{ kNA, kNA, kNA, kNA, 97, 78.5, kNA, kNA, kNA, kNA, kNA } }, // Manganese hs
{ Fe, { kNA, kNA, kNA, kNA, 75, 69, 72.5, kNA, 39, kNA, kNA } }, // Iron ls
// { Fe,{ kNA, kNA, kNA, kNA, 92, 78.5, kNA, kNA, kNA, kNA, kNA } }, // Iron hs
{ Co, { kNA, kNA, kNA, kNA, 79, 68.5, kNA, kNA, kNA, kNA, kNA } }, // Cobalt ls
// { Co,{ kNA, kNA, kNA, kNA, 88.5, 75, 67, kNA, kNA, kNA, kNA } }, // Cobalt hs
{ Mn, { kNA, kNA, kNA, kNA, 81, 72, 67, 47, 39.5f, 60, kNA } }, // Manganese ls
// { Mn,{ kNA, kNA, kNA, kNA, 97, 78.5f, kNA, kNA, kNA, kNA, kNA } }, // Manganese hs
{ Fe, { kNA, kNA, kNA, kNA, 75, 69, 72.5f, kNA, 39, kNA, kNA } }, // Iron ls
// { Fe,{ kNA, kNA, kNA, kNA, 92, 78.5f, kNA, kNA, kNA, kNA, kNA } }, // Iron hs
{ Co, { kNA, kNA, kNA, kNA, 79, 68.5f, kNA, kNA, kNA, kNA, kNA } }, // Cobalt ls
// { Co,{ kNA, kNA, kNA, kNA, 88.5f, 75, 67, kNA, kNA, kNA, kNA } }, // Cobalt hs
{ Ni, { kNA, kNA, kNA, kNA, 83, 70, 62, kNA, kNA, kNA, kNA } }, // Nickel ls
// { Ni,{ kNA, kNA, kNA, kNA, kNA, 74, kNA, kNA, kNA, kNA, kNA } }, // Nickel hs
{ Cu, { kNA, kNA, kNA, 91, 87, 68, kNA, kNA, kNA, kNA, kNA } }, // Copper
@@ -215,10 +214,10 @@ const struct ionic_radii
{ Zr, { kNA, kNA, kNA, kNA, kNA, kNA, 86, kNA, kNA, kNA, kNA } }, // Zirconium
{ Nb, { kNA, kNA, kNA, kNA, kNA, 86, 82, 78, kNA, kNA, kNA } }, // Niobium
{ Mo, { kNA, kNA, kNA, kNA, kNA, 83, 79, 75, 73, kNA, kNA } }, // Molybdenum
{ Tc, { kNA, kNA, kNA, kNA, kNA, kNA, 78.5, 74, kNA, 70, kNA } }, // Technetium
{ Ru, { kNA, kNA, kNA, kNA, kNA, 82, 76, 70.5, kNA, 52, 150 } }, // Ruthenium
{ Rh, { kNA, kNA, kNA, kNA, kNA, 80.5, 74, 69, kNA, kNA, kNA } }, // Rhodium
{ Pd, { kNA, kNA, kNA, 73, 100, 90, 75.5, kNA, kNA, kNA, kNA } }, // Palladium
{ Tc, { kNA, kNA, kNA, kNA, kNA, kNA, 78.5f, 74, kNA, 70, kNA } }, // Technetium
{ Ru, { kNA, kNA, kNA, kNA, kNA, 82, 76, 70.5f, kNA, 52, 150 } }, // Ruthenium
{ Rh, { kNA, kNA, kNA, kNA, kNA, 80.5f, 74, 69, kNA, kNA, kNA } }, // Rhodium
{ Pd, { kNA, kNA, kNA, 73, 100, 90, 75.5f, kNA, kNA, kNA, kNA } }, // Palladium
{ Ag, { kNA, kNA, kNA, 129, 108, 89, kNA, kNA, kNA, kNA, kNA } }, // Silver
{ Cd, { kNA, kNA, kNA, kNA, 109, kNA, kNA, kNA, kNA, kNA, kNA } }, // Cadmium
{ In, { kNA, kNA, kNA, kNA, kNA, 94, kNA, kNA, kNA, kNA, kNA } }, // Indium
@@ -229,32 +228,32 @@ const struct ionic_radii
{ Xe, { kNA, kNA, kNA, kNA, kNA, kNA, kNA, kNA, kNA, kNA, 62 } }, // Xenon
{ Cs, { kNA, kNA, kNA, 167, kNA, kNA, kNA, kNA, kNA, kNA, kNA } }, // Caesium
{ Ba, { kNA, kNA, kNA, kNA, 149, kNA, kNA, kNA, kNA, kNA, kNA } }, // Barium
{ La, { kNA, kNA, kNA, kNA, kNA, 117.2, kNA, kNA, kNA, kNA, kNA } }, // Lanthanum
{ La, { kNA, kNA, kNA, kNA, kNA, 117.2f, kNA, kNA, kNA, kNA, kNA } }, // Lanthanum
{ Ce, { kNA, kNA, kNA, kNA, kNA, 115, 101, kNA, kNA, kNA, kNA } }, // Cerium
{ Pr, { kNA, kNA, kNA, kNA, kNA, 113, 99, kNA, kNA, kNA, kNA } }, // Praseodymium
{ Nd, { kNA, kNA, kNA, kNA, 143, 112.3, kNA, kNA, kNA, kNA, kNA } }, // Neodymium
{ Nd, { kNA, kNA, kNA, kNA, 143, 112.3f, kNA, kNA, kNA, kNA, kNA } }, // Neodymium
{ Pm, { kNA, kNA, kNA, kNA, kNA, 111, kNA, kNA, kNA, kNA, kNA } }, // Promethium
{ Sm, { kNA, kNA, kNA, kNA, 136, 109.8, kNA, kNA, kNA, kNA, kNA } }, // Samarium
{ Eu, { kNA, kNA, kNA, kNA, 131, 108.7, kNA, kNA, kNA, kNA, kNA } }, // Europium
{ Gd, { kNA, kNA, kNA, kNA, kNA, 107.8, kNA, kNA, kNA, kNA, kNA } }, // Gadolinium
{ Tb, { kNA, kNA, kNA, kNA, kNA, 106.3, 90, kNA, kNA, kNA, kNA } }, // Terbium
{ Dy, { kNA, kNA, kNA, kNA, 121, 105.2, kNA, kNA, kNA, kNA, kNA } }, // Dysprosium
{ Ho, { kNA, kNA, kNA, kNA, kNA, 104.1, kNA, kNA, kNA, kNA, kNA } }, // Holmium
{ Sm, { kNA, kNA, kNA, kNA, 136, 109.8f, kNA, kNA, kNA, kNA, kNA } }, // Samarium
{ Eu, { kNA, kNA, kNA, kNA, 131, 108.7f, kNA, kNA, kNA, kNA, kNA } }, // Europium
{ Gd, { kNA, kNA, kNA, kNA, kNA, 107.8f, kNA, kNA, kNA, kNA, kNA } }, // Gadolinium
{ Tb, { kNA, kNA, kNA, kNA, kNA, 106.3f, 90, kNA, kNA, kNA, kNA } }, // Terbium
{ Dy, { kNA, kNA, kNA, kNA, 121, 105.2f, kNA, kNA, kNA, kNA, kNA } }, // Dysprosium
{ Ho, { kNA, kNA, kNA, kNA, kNA, 104.1f, kNA, kNA, kNA, kNA, kNA } }, // Holmium
{ Er, { kNA, kNA, kNA, kNA, kNA, 103, kNA, kNA, kNA, kNA, kNA } }, // Erbium
{ Tm, { kNA, kNA, kNA, kNA, 117, 102, kNA, kNA, kNA, kNA, kNA } }, // Thulium
{ Yb, { kNA, kNA, kNA, kNA, 116, 100.8, kNA, kNA, kNA, kNA, kNA } }, // Ytterbium
{ Lu, { kNA, kNA, kNA, kNA, kNA, 100.1, kNA, kNA, kNA, kNA, kNA } }, // Lutetium
{ Yb, { kNA, kNA, kNA, kNA, 116, 100.8f, kNA, kNA, kNA, kNA, kNA } }, // Ytterbium
{ Lu, { kNA, kNA, kNA, kNA, kNA, 100.1f, kNA, kNA, kNA, kNA, kNA } }, // Lutetium
{ Hf, { kNA, kNA, kNA, kNA, kNA, kNA, 85, kNA, kNA, kNA, kNA } }, // Hafnium
{ Ta, { kNA, kNA, kNA, kNA, kNA, 86, 82, 78, kNA, kNA, kNA } }, // Tantalum
{ W, { kNA, kNA, kNA, kNA, kNA, kNA, 80, 76, 74, kNA, kNA } }, // Tungsten
{ Re, { kNA, kNA, kNA, kNA, kNA, kNA, 77, 72, 69, 67, kNA } }, // Rhenium
{ Os, { kNA, kNA, kNA, kNA, kNA, kNA, 77, 71.5, 68.5, 66.5, 53 } }, // Osmium
{ Ir, { kNA, kNA, kNA, kNA, kNA, 82, 76.5, 71, kNA, kNA, kNA } }, // Iridium
{ Pt, { kNA, kNA, kNA, kNA, 94, kNA, 76.5, 71, kNA, kNA, kNA } }, // Platinum
{ Os, { kNA, kNA, kNA, kNA, kNA, kNA, 77, 71.5f, 68.5f, 66.5f, 53 } }, // Osmium
{ Ir, { kNA, kNA, kNA, kNA, kNA, 82, 76.5f, 71, kNA, kNA, kNA } }, // Iridium
{ Pt, { kNA, kNA, kNA, kNA, 94, kNA, 76.5f, 71, kNA, kNA, kNA } }, // Platinum
{ Au, { kNA, kNA, kNA, 151, kNA, 99, kNA, 71, kNA, kNA, kNA } }, // Gold
{ Hg, { kNA, kNA, kNA, 133, 116, kNA, kNA, kNA, kNA, kNA, kNA } }, // Mercury
{ Tl, { kNA, kNA, kNA, 164, kNA, 102.5, kNA, kNA, kNA, kNA, kNA } }, // Thallium
{ Pb, { kNA, kNA, kNA, kNA, 133, kNA, 91.5, kNA, kNA, kNA, kNA } }, // Lead
{ Tl, { kNA, kNA, kNA, 164, kNA, 102.5f, kNA, kNA, kNA, kNA, kNA } }, // Thallium
{ Pb, { kNA, kNA, kNA, kNA, 133, kNA, 91.5f, kNA, kNA, kNA, kNA } }, // Lead
{ Bi, { kNA, kNA, kNA, kNA, kNA, 117, kNA, 90, kNA, kNA, kNA } }, // Bismuth
{ Po, { kNA, kNA, kNA, kNA, kNA, kNA, 108, kNA, 81, kNA, kNA } }, // Polonium
{ At, { kNA, kNA, kNA, kNA, kNA, kNA, kNA, kNA, kNA, 76, kNA } }, // Astatine
@@ -263,16 +262,16 @@ const struct ionic_radii
{ Ac, { kNA, kNA, kNA, kNA, kNA, 126, kNA, kNA, kNA, kNA, kNA } }, // Actinium
{ Th, { kNA, kNA, kNA, kNA, kNA, kNA, 108, kNA, kNA, kNA, kNA } }, // Thorium
{ Pa, { kNA, kNA, kNA, kNA, kNA, 116, 104, 92, kNA, kNA, kNA } }, // Protactinium
{ U, { kNA, kNA, kNA, kNA, kNA, 116.5, 103, 90, 87, kNA, kNA } }, // Uranium
{ U, { kNA, kNA, kNA, kNA, kNA, 116.5f, 103, 90, 87, kNA, kNA } }, // Uranium
{ Np, { kNA, kNA, kNA, kNA, 124, 115, 101, 89, 86, 85, kNA } }, // Neptunium
{ Pu, { kNA, kNA, kNA, kNA, kNA, 114, 100, 88, 85, kNA, kNA } }, // Plutonium
{ Am, { kNA, kNA, kNA, kNA, 140, 111.5, 99, kNA, kNA, kNA, kNA } }, // Americium
{ Am, { kNA, kNA, kNA, kNA, 140, 111.5f, 99, kNA, kNA, kNA, kNA } }, // Americium
{ Cm, { kNA, kNA, kNA, kNA, kNA, 111, 99, kNA, kNA, kNA, kNA } }, // Curium
{ Bk, { kNA, kNA, kNA, kNA, kNA, 110, 97, kNA, kNA, kNA, kNA } }, // Berkelium
{ Cf, { kNA, kNA, kNA, kNA, kNA, 109, 96.1, kNA, kNA, kNA, kNA } }, // Californium
{ Es, { kNA, kNA, kNA, kNA, kNA, 92.8, kNA, kNA, kNA, kNA, kNA } }, // Einsteinium
{ Cf, { kNA, kNA, kNA, kNA, kNA, 109, 96.1f, kNA, kNA, kNA, kNA } }, // Californium
{ Es, { kNA, kNA, kNA, kNA, kNA, 92.8f, kNA, kNA, kNA, kNA, kNA } }, // Einsteinium
}, kEffectiveIonicRadii[] = {
{ H, { kNA, kNA, 139.9, -18, kNA, kNA, kNA, kNA, kNA, kNA, kNA } }, // Hydrogen
{ H, { kNA, kNA, 139.9f, -18, kNA, kNA, kNA, kNA, kNA, kNA, kNA } }, // Hydrogen
{ Li, { kNA, kNA, kNA, 76, kNA, kNA, kNA, kNA, kNA, kNA, kNA } }, // Lithium
{ Be, { kNA, kNA, kNA, kNA, 45, kNA, kNA, kNA, kNA, kNA, kNA } }, // Beryllium
{ B, { kNA, kNA, kNA, kNA, kNA, 27, kNA, kNA, kNA, kNA, kNA } }, // Boron
@@ -282,24 +281,24 @@ const struct ionic_radii
{ F, { kNA, kNA, 133, kNA, kNA, kNA, kNA, kNA, kNA, 8, kNA } }, // Fluorine
{ Na, { kNA, kNA, kNA, 102, kNA, kNA, kNA, kNA, kNA, kNA, kNA } }, // Sodium
{ Mg, { kNA, kNA, kNA, kNA, 72, kNA, kNA, kNA, kNA, kNA, kNA } }, // Magnesium
{ Al, { kNA, kNA, kNA, kNA, kNA, 53.5, kNA, kNA, kNA, kNA, kNA } }, // Aluminium
{ Al, { kNA, kNA, kNA, kNA, kNA, 53.5f, kNA, kNA, kNA, kNA, kNA } }, // Aluminium
{ Si, { kNA, kNA, kNA, kNA, kNA, kNA, 40, kNA, kNA, kNA, kNA } }, // Silicon
{ P, { 212, kNA, kNA, kNA, kNA, 44, kNA, 38, kNA, kNA, kNA } }, // Phosphorus
{ S, { kNA, 184, kNA, kNA, kNA, kNA, 37, kNA, 29, kNA, kNA } }, // Sulfur
{ Cl, { kNA, kNA, 181, kNA, kNA, kNA, kNA, 12, kNA, 27, kNA } }, // Chlorine
{ K, { kNA, kNA, kNA, 138, kNA, kNA, kNA, kNA, kNA, kNA, kNA } }, // Potassium
{ Ca, { kNA, kNA, kNA, kNA, 100, kNA, kNA, kNA, kNA, kNA, kNA } }, // Calcium
{ Sc, { kNA, kNA, kNA, kNA, kNA, 74.5, kNA, kNA, kNA, kNA, kNA } }, // Scandium
{ Ti, { kNA, kNA, kNA, kNA, 86, 67, 60.5, kNA, kNA, kNA, kNA } }, // Titanium
{ Sc, { kNA, kNA, kNA, kNA, kNA, 74.5f, kNA, kNA, kNA, kNA, kNA } }, // Scandium
{ Ti, { kNA, kNA, kNA, kNA, 86, 67, 60.5f, kNA, kNA, kNA, kNA } }, // Titanium
{ V, { kNA, kNA, kNA, kNA, 79, 64, 58, 54, kNA, kNA, kNA } }, // Vanadium
{ Cr, { kNA, kNA, kNA, kNA, 73, 61.5, 55, 49, 44, kNA, kNA } }, // Chromium ls
{ Cr, { kNA, kNA, kNA, kNA, 73, 61.5f, 55, 49, 44, kNA, kNA } }, // Chromium ls
{ Cr, { kNA, kNA, kNA, kNA, 80, kNA, kNA, kNA, kNA, kNA, kNA } }, // Chromium hs
{ Mn, { kNA, kNA, kNA, kNA, 67, 58, 53, 33, 25.5, 46, kNA } }, // Manganese ls
{ Mn, { kNA, kNA, kNA, kNA, 83, 64.5, kNA, kNA, kNA, kNA, kNA } }, // Manganese hs
{ Fe, { kNA, kNA, kNA, kNA, 61, 55, 58.5, kNA, 25, kNA, kNA } }, // Iron ls
{ Fe, { kNA, kNA, kNA, kNA, 78, 64.5, kNA, kNA, kNA, kNA, kNA } }, // Iron hs
{ Co, { kNA, kNA, kNA, kNA, 65, 54.5, kNA, kNA, kNA, kNA, kNA } }, // Cobalt ls
{ Co, { kNA, kNA, kNA, kNA, 74.5, 61, 53, kNA, kNA, kNA, kNA } }, // Cobalt hs
{ Mn, { kNA, kNA, kNA, kNA, 67, 58, 53, 33, 25.5f, 46, kNA } }, // Manganese ls
{ Mn, { kNA, kNA, kNA, kNA, 83, 64.5f, kNA, kNA, kNA, kNA, kNA } }, // Manganese hs
{ Fe, { kNA, kNA, kNA, kNA, 61, 55, 58.5f, kNA, 25, kNA, kNA } }, // Iron ls
{ Fe, { kNA, kNA, kNA, kNA, 78, 64.5f, kNA, kNA, kNA, kNA, kNA } }, // Iron hs
{ Co, { kNA, kNA, kNA, kNA, 65, 54.5f, kNA, kNA, kNA, kNA, kNA } }, // Cobalt ls
{ Co, { kNA, kNA, kNA, kNA, 74.5f, 61, 53, kNA, kNA, kNA, kNA } }, // Cobalt hs
{ Ni, { kNA, kNA, kNA, kNA, 69, 56, 48, kNA, kNA, kNA, kNA } }, // Nickel ls
{ Ni, { kNA, kNA, kNA, kNA, kNA, 60, kNA, kNA, kNA, kNA, kNA } }, // Nickel hs
{ Cu, { kNA, kNA, kNA, 77, 73, 54, kNA, kNA, kNA, kNA, kNA } }, // Copper
@@ -315,10 +314,10 @@ const struct ionic_radii
{ Zr, { kNA, kNA, kNA, kNA, kNA, kNA, 72, kNA, kNA, kNA, kNA } }, // Zirconium
{ Nb, { kNA, kNA, kNA, kNA, kNA, 72, 68, 64, kNA, kNA, kNA } }, // Niobium
{ Mo, { kNA, kNA, kNA, kNA, kNA, 69, 65, 61, 59, kNA, kNA } }, // Molybdenum
{ Tc, { kNA, kNA, kNA, kNA, kNA, kNA, 64.5, 60, kNA, 56, kNA } }, // Technetium
{ Ru, { kNA, kNA, kNA, kNA, kNA, 68, 62, 56.5, kNA, 38, 36 } }, // Ruthenium
{ Rh, { kNA, kNA, kNA, kNA, kNA, 66.5, 60, 55, kNA, kNA, kNA } }, // Rhodium
{ Pd, { kNA, kNA, kNA, 59, 86, 76, 61.5, kNA, kNA, kNA, kNA } }, // Palladium
{ Tc, { kNA, kNA, kNA, kNA, kNA, kNA, 64.5f, 60, kNA, 56, kNA } }, // Technetium
{ Ru, { kNA, kNA, kNA, kNA, kNA, 68, 62, 56.5f, kNA, 38, 36 } }, // Ruthenium
{ Rh, { kNA, kNA, kNA, kNA, kNA, 66.5f, 60, 55, kNA, kNA, kNA } }, // Rhodium
{ Pd, { kNA, kNA, kNA, 59, 86, 76, 61.5f, kNA, kNA, kNA, kNA } }, // Palladium
{ Ag, { kNA, kNA, kNA, 115, 94, 75, kNA, kNA, kNA, kNA, kNA } }, // Silver
{ Cd, { kNA, kNA, kNA, kNA, 95, kNA, kNA, kNA, kNA, kNA, kNA } }, // Cadmium
{ In, { kNA, kNA, kNA, kNA, kNA, 80, kNA, kNA, kNA, kNA, kNA } }, // Indium
@@ -329,48 +328,48 @@ const struct ionic_radii
{ Xe, { kNA, kNA, kNA, kNA, kNA, kNA, kNA, kNA, kNA, kNA, 48 } }, // Xenon
{ Cs, { kNA, kNA, kNA, 167, kNA, kNA, kNA, kNA, kNA, kNA, kNA } }, // Caesium
{ Ba, { kNA, kNA, kNA, kNA, 135, kNA, kNA, kNA, kNA, kNA, kNA } }, // Barium
{ La, { kNA, kNA, kNA, kNA, kNA, 103.2, kNA, kNA, kNA, kNA, kNA } }, // Lanthanum
{ La, { kNA, kNA, kNA, kNA, kNA, 103.2f, kNA, kNA, kNA, kNA, kNA } }, // Lanthanum
{ Ce, { kNA, kNA, kNA, kNA, kNA, 101, 87, kNA, kNA, kNA, kNA } }, // Cerium
{ Pr, { kNA, kNA, kNA, kNA, kNA, 99, 85, kNA, kNA, kNA, kNA } }, // Praseodymium
{ Nd, { kNA, kNA, kNA, kNA, 129, 98.3, kNA, kNA, kNA, kNA, kNA } }, // Neodymium
{ Nd, { kNA, kNA, kNA, kNA, 129, 98.3f, kNA, kNA, kNA, kNA, kNA } }, // Neodymium
{ Pm, { kNA, kNA, kNA, kNA, kNA, 97, kNA, kNA, kNA, kNA, kNA } }, // Promethium
{ Sm, { kNA, kNA, kNA, kNA, 122, 95.8, kNA, kNA, kNA, kNA, kNA } }, // Samarium
{ Eu, { kNA, kNA, kNA, kNA, 117, 94.7, kNA, kNA, kNA, kNA, kNA } }, // Europium
{ Gd, { kNA, kNA, kNA, kNA, kNA, 93.5, kNA, kNA, kNA, kNA, kNA } }, // Gadolinium
{ Tb, { kNA, kNA, kNA, kNA, kNA, 92.3, 76, kNA, kNA, kNA, kNA } }, // Terbium
{ Dy, { kNA, kNA, kNA, kNA, 107, 91.2, kNA, kNA, kNA, kNA, kNA } }, // Dysprosium
{ Ho, { kNA, kNA, kNA, kNA, kNA, 90.1, kNA, kNA, kNA, kNA, kNA } }, // Holmium
{ Sm, { kNA, kNA, kNA, kNA, 122, 95.8f, kNA, kNA, kNA, kNA, kNA } }, // Samarium
{ Eu, { kNA, kNA, kNA, kNA, 117, 94.7f, kNA, kNA, kNA, kNA, kNA } }, // Europium
{ Gd, { kNA, kNA, kNA, kNA, kNA, 93.5f, kNA, kNA, kNA, kNA, kNA } }, // Gadolinium
{ Tb, { kNA, kNA, kNA, kNA, kNA, 92.3f, 76, kNA, kNA, kNA, kNA } }, // Terbium
{ Dy, { kNA, kNA, kNA, kNA, 107, 91.2f, kNA, kNA, kNA, kNA, kNA } }, // Dysprosium
{ Ho, { kNA, kNA, kNA, kNA, kNA, 90.1f, kNA, kNA, kNA, kNA, kNA } }, // Holmium
{ Er, { kNA, kNA, kNA, kNA, kNA, 89, kNA, kNA, kNA, kNA, kNA } }, // Erbium
{ Tm, { kNA, kNA, kNA, kNA, 103, 88, kNA, kNA, kNA, kNA, kNA } }, // Thulium
{ Yb, { kNA, kNA, kNA, kNA, 102, 86.8, kNA, kNA, kNA, kNA, kNA } }, // Ytterbium
{ Lu, { kNA, kNA, kNA, kNA, kNA, 86.1, kNA, kNA, kNA, kNA, kNA } }, // Lutetium
{ Yb, { kNA, kNA, kNA, kNA, 102, 86.8f, kNA, kNA, kNA, kNA, kNA } }, // Ytterbium
{ Lu, { kNA, kNA, kNA, kNA, kNA, 86.1f, kNA, kNA, kNA, kNA, kNA } }, // Lutetium
{ Hf, { kNA, kNA, kNA, kNA, kNA, kNA, 71, kNA, kNA, kNA, kNA } }, // Hafnium
{ Ta, { kNA, kNA, kNA, kNA, kNA, 72, 68, 64, kNA, kNA, kNA } }, // Tantalum
{ W, { kNA, kNA, kNA, kNA, kNA, kNA, 66, 62, 60, kNA, kNA } }, // Tungsten
{ Re, { kNA, kNA, kNA, kNA, kNA, kNA, 63, 58, 55, 53, kNA } }, // Rhenium
{ Os, { kNA, kNA, kNA, kNA, kNA, kNA, 63, 57.5, 54.5, 52.5, 39 } }, // Osmium
{ Ir, { kNA, kNA, kNA, kNA, kNA, 68, 62.5, 57, kNA, kNA, kNA } }, // Iridium
{ Pt, { kNA, kNA, kNA, kNA, 80, kNA, 62.5, 57, kNA, kNA, kNA } }, // Platinum
{ Os, { kNA, kNA, kNA, kNA, kNA, kNA, 63, 57.5f, 54.5f, 52.5f, 39 } }, // Osmium
{ Ir, { kNA, kNA, kNA, kNA, kNA, 68, 62.5f, 57, kNA, kNA, kNA } }, // Iridium
{ Pt, { kNA, kNA, kNA, kNA, 80, kNA, 62.5f, 57, kNA, kNA, kNA } }, // Platinum
{ Au, { kNA, kNA, kNA, 137, kNA, 85, kNA, 57, kNA, kNA, kNA } }, // Gold
{ Hg, { kNA, kNA, kNA, 119, 102, kNA, kNA, kNA, kNA, kNA, kNA } }, // Mercury
{ Tl, { kNA, kNA, kNA, 150, kNA, 88.5, kNA, kNA, kNA, kNA, kNA } }, // Thallium
{ Pb, { kNA, kNA, kNA, kNA, 119, kNA, 77.5, kNA, kNA, kNA, kNA } }, // Lead
{ Tl, { kNA, kNA, kNA, 150, kNA, 88.5f, kNA, kNA, kNA, kNA, kNA } }, // Thallium
{ Pb, { kNA, kNA, kNA, kNA, 119, kNA, 77.5f, kNA, kNA, kNA, kNA } }, // Lead
{ Bi, { kNA, kNA, kNA, kNA, kNA, 103, kNA, 76, kNA, kNA, kNA } }, // Bismuth
{ Po, { kNA, 223, kNA, kNA, kNA, kNA, 94, kNA, 67, kNA, kNA } }, // Polonium
{ At, { kNA, kNA, kNA, kNA, kNA, kNA, kNA, kNA, kNA, 62, kNA } }, // Astatine
{ Fr, { kNA, kNA, kNA, 180, kNA, kNA, kNA, kNA, kNA, kNA, kNA } }, // Francium
{ Ra, { kNA, kNA, kNA, kNA, 148, kNA, kNA, kNA, kNA, kNA, kNA } }, // Radium
{ Ac, { kNA, kNA, kNA, kNA, kNA, 106.5, kNA, kNA, kNA, kNA, kNA } }, // Actinium
{ Ac, { kNA, kNA, kNA, kNA, kNA, 106.5f, kNA, kNA, kNA, kNA, kNA } }, // Actinium
{ Th, { kNA, kNA, kNA, kNA, kNA, kNA, 94, kNA, kNA, kNA, kNA } }, // Thorium
{ Pa, { kNA, kNA, kNA, kNA, kNA, 104, 90, 78, kNA, kNA, kNA } }, // Protactinium
{ U, { kNA, kNA, kNA, kNA, kNA, 102.5, 89, 76, 73, kNA, kNA } }, // Uranium
{ U, { kNA, kNA, kNA, kNA, kNA, 102.5f, 89, 76, 73, kNA, kNA } }, // Uranium
{ Np, { kNA, kNA, kNA, kNA, 110, 101, 87, 75, 72, 71, kNA } }, // Neptunium
{ Pu, { kNA, kNA, kNA, kNA, kNA, 100, 86, 74, 71, kNA, kNA } }, // Plutonium
{ Am, { kNA, kNA, kNA, kNA, 126, 97.5, 85, kNA, kNA, kNA, kNA } }, // Americium
{ Am, { kNA, kNA, kNA, kNA, 126, 97.5f, 85, kNA, kNA, kNA, kNA } }, // Americium
{ Cm, { kNA, kNA, kNA, kNA, kNA, 97, 85, kNA, kNA, kNA, kNA } }, // Curium
{ Bk, { kNA, kNA, kNA, kNA, kNA, 96, 83, kNA, kNA, kNA, kNA } }, // Berkelium
{ Cf, { kNA, kNA, kNA, kNA, kNA, 95, 82.1, kNA, kNA, kNA, kNA } }, // Californium
{ Es, { kNA, kNA, kNA, kNA, kNA, 83.5, kNA, kNA, kNA, kNA, kNA } }, // Einsteinium
{ Cf, { kNA, kNA, kNA, kNA, kNA, 95, 82.1f, kNA, kNA, kNA, kNA } }, // Californium
{ Es, { kNA, kNA, kNA, kNA, kNA, 83.5f, kNA, kNA, kNA, kNA, kNA } }, // Einsteinium
};
@@ -1028,6 +1027,9 @@ atom_type_traits::atom_type_traits(const std::string& symbol)
break;
}
}
if (symbol == "X")
m_info = &data::kKnownAtoms[0];
if (m_info == nullptr)
throw std::invalid_argument("Not a known element: " + symbol);
@@ -1075,6 +1077,26 @@ bool atom_type_traits::is_metal(const std::string& symbol)
return result;
}
bool atom_type_traits::has_sf(int charge) const
{
auto type = m_info->type;
if (type == D)
type = H;
bool result = false;
for (auto& sf: data::kWKSFData)
{
if (sf.symbol == type and sf.charge == charge)
{
result = true;
break;
}
}
return result;
}
auto atom_type_traits::wksf(int charge) const -> const SFData&
{
auto type = m_info->type;

View File

@@ -24,14 +24,14 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++/category.hpp"
#include "cif++/datablock.hpp"
#include "cif++/parser.hpp"
#include "cif++/utilities.hpp"
#include <numeric>
#include <stack>
#include <cif++/category.hpp>
#include <cif++/datablock.hpp>
#include <cif++/parser.hpp>
#include <cif++/utilities.hpp>
// TODO: Find out what the rules are exactly for linked items, the current implementation
// is inconsistent. It all depends whether a link is satified if a field taking part in the
// set of linked items is null at one side and not null in the other.
@@ -51,7 +51,7 @@ class row_comparator
{
auto cv = cat.get_cat_validator();
for (auto k : cv->m_keys)
for (auto &k : cv->m_keys)
{
uint16_t ix = cat.add_column(k);
@@ -78,13 +78,8 @@ class row_comparator
row_handle rhb(m_category, *b);
int d = 0;
for (auto &c : m_comparator)
for (const auto &[k, f] : m_comparator)
{
uint16_t k;
compareFunc f;
std::tie(k, f) = c;
std::string_view ka = rha[k].text();
std::string_view kb = rhb[k].text();
@@ -103,29 +98,30 @@ class row_comparator
row_handle rhb(m_category, *b);
int d = 0, i = 0;
for (auto &c : m_comparator)
int d = 0;
auto ai = a.begin();
for (const auto &[k, f] : m_comparator)
{
uint16_t k;
compareFunc f;
assert(ai != a.end());
std::tie(k, f) = c;
std::string_view ka = a[i++].value();
std::string_view ka = ai->value();
std::string_view kb = rhb[k].text();
d = f(ka, kb);
if (d != 0)
break;
++ai;
}
return d;
}
private:
typedef std::function<int(std::string_view, std::string_view)> compareFunc;
typedef std::tuple<uint16_t, compareFunc> key_comparator;
using compareFunc = std::function<int(std::string_view, std::string_view)>;
using key_comparator = std::tuple<uint16_t, compareFunc>;
std::vector<key_comparator> m_comparator;
category &m_category;
@@ -139,13 +135,7 @@ class row_comparator
class category_index
{
public:
category_index(category *cat)
: m_category(*cat)
, m_row_comparator(m_category)
, m_root(nullptr)
{
reconstruct();
}
category_index(category *cat);
~category_index()
{
@@ -158,9 +148,6 @@ class category_index
void insert(row *r);
void erase(row *r);
// batch create
void reconstruct();
// reorder the row's and returns new head and tail
std::tuple<row *, row *> reorder()
{
@@ -241,7 +228,7 @@ class category_index
h->m_right->m_red = not h->m_right->m_red;
}
bool is_red(entry *h) const
constexpr bool is_red(entry *h) const
{
return h != nullptr and h->m_red;
}
@@ -342,6 +329,15 @@ class category_index
entry *m_root;
};
category_index::category_index(category *cat)
: m_category(*cat)
, m_row_comparator(m_category)
, m_root(nullptr)
{
for (auto r : m_category)
insert(r.get_row());
}
row *category_index::find(row *k) const
{
const entry *r = m_root;
@@ -482,83 +478,6 @@ category_index::entry *category_index::erase(entry *h, row *k)
return fix_up(h);
}
void category_index::reconstruct()
{
delete m_root;
m_root = nullptr;
for (auto r : m_category)
insert(r.get_row());
// maybe reconstruction can be done quicker by using the following commented code.
// however, I've not had the time to think of a way to set the red/black flag correctly in that case.
// std::vector<row*> rows;
// transform(mCat.begin(), mCat.end(), backInserter(rows),
// [](Row r) -> row* { assert(r.mData); return r.mData; });
//
// assert(std::find(rows.begin(), rows.end(), nullptr) == rows.end());
//
// // don't use sort here, it will run out of the stack of something.
// // quicksort is notorious for using excessive recursion.
// // Besides, most of the time, the data is ordered already anyway.
//
// stable_sort(rows.begin(), rows.end(), [this](row* a, row* b) -> bool { return this->mComp(a, b) < 0; });
//
// for (size_t i = 0; i < rows.size() - 1; ++i)
// assert(mComp(rows[i], rows[i + 1]) < 0);
//
// deque<entry*> e;
// transform(rows.begin(), rows.end(), back_inserter(e),
// [](row* r) -> entry* { return new entry(r); });
//
// while (e.size() > 1)
// {
// deque<entry*> ne;
//
// while (not e.empty())
// {
// entry* a = e.front();
// e.pop_front();
//
// if (e.empty())
// ne.push_back(a);
// else
// {
// entry* b = e.front();
// b->mLeft = a;
//
// assert(mComp(a->mRow, b->mRow) < 0);
//
// e.pop_front();
//
// if (not e.empty())
// {
// entry* c = e.front();
// e.pop_front();
//
// assert(mComp(b->mRow, c->mRow) < 0);
//
// b->mRight = c;
// }
//
// ne.push_back(b);
//
// if (not e.empty())
// {
// ne.push_back(e.front());
// e.pop_front();
// }
// }
// }
//
// swap (e, ne);
// }
//
// assert(e.size() == 1);
// mRoot = e.front();
}
size_t category_index::size() const
{
std::stack<entry *> s;
@@ -595,14 +514,12 @@ category::category(const category &rhs)
, m_columns(rhs.m_columns)
, m_validator(rhs.m_validator)
, m_cat_validator(rhs.m_cat_validator)
, m_parent_links(rhs.m_parent_links)
, m_child_links(rhs.m_child_links)
, m_cascade(rhs.m_cascade)
{
for (auto r = rhs.m_head; r != nullptr; r = r->m_next)
insert_impl(end(), clone_row(*r));
if (m_cat_validator != nullptr)
if (m_cat_validator != nullptr and m_index == nullptr)
m_index = new category_index(this);
}
@@ -645,10 +562,8 @@ category &category::operator=(const category &rhs)
m_validator = rhs.m_validator;
m_cat_validator = rhs.m_cat_validator;
m_parent_links = rhs.m_parent_links;
m_child_links = rhs.m_child_links;
if (m_cat_validator != nullptr)
if (m_cat_validator != nullptr and m_index == nullptr)
m_index = new category_index(this);
}
@@ -659,9 +574,6 @@ category &category::operator=(category &&rhs)
{
if (this != &rhs)
{
if (not empty())
clear();
m_name = std::move(rhs.m_name);
m_columns = std::move(rhs.m_columns);
m_cascade = rhs.m_cascade;
@@ -669,12 +581,10 @@ category &category::operator=(category &&rhs)
m_cat_validator = rhs.m_cat_validator;
m_parent_links = rhs.m_parent_links;
m_child_links = rhs.m_child_links;
m_index = rhs.m_index;
m_head = rhs.m_head;
m_tail = rhs.m_tail;
rhs.m_head = rhs.m_tail = nullptr;
rhs.m_index = nullptr;
std::swap(m_index, rhs.m_index);
std::swap(m_head, rhs.m_head);
std::swap(m_tail, rhs.m_tail);
}
return *this;
@@ -983,11 +893,19 @@ bool category::validate_links() const
row_handle category::operator[](const key_type &key)
{
if (m_index == nullptr)
throw std::logic_error("Category " + m_name + " does not have an index");
row_handle result{};
auto row = m_index->find_by_value(key);
return row != nullptr ? row_handle{ *this, *row } : row_handle{};
if (not empty())
{
if (m_index == nullptr)
throw std::logic_error("Category " + m_name + " does not have an index");
auto row = m_index->find_by_value(key);
if (row != nullptr)
result = { *this, *row };
}
return result;
}
// --------------------------------------------------------------------
@@ -1142,10 +1060,6 @@ category::iterator category::erase(iterator pos)
row *r = rh.get_row();
iterator result = ++pos;
iset keys;
if (m_cat_validator)
keys = iset(m_cat_validator->m_keys.begin(), m_cat_validator->m_keys.end());
if (m_head == nullptr)
throw std::runtime_error("erase");
@@ -1174,7 +1088,7 @@ category::iterator category::erase(iterator pos)
// in mmcif_pdbx.dic dictionary.
//
// For each link group in _pdbx_item_linked_group_list
// a std::set of keys from one category is mapped to another.
// a set of keys from one category is mapped to another.
// If all values in a child are the same as the specified parent ones
// the child is removed as well, recursively of course.
@@ -1198,25 +1112,29 @@ category::iterator category::erase(iterator pos)
return result;
}
size_t category::erase(condition &&cond)
template<typename T>
class save_value
{
size_t result = 0;
cond.prepare(*this);
auto ri = begin();
while (ri != end())
public:
save_value(T &v, const T nv = {})
: m_v(v)
, m_sv(std::exchange(m_v, nv))
{
if (cond(*ri))
{
ri = erase(ri);
++result;
}
else
++ri;
}
return result;
~save_value()
{
m_v = m_sv;
}
private:
T &m_v;
const T m_sv;
};
size_t category::erase(condition &&cond)
{
return erase(std::move(cond), {});
}
size_t category::erase(condition &&cond, std::function<void(row_handle)> &&visit)
@@ -1225,12 +1143,26 @@ size_t category::erase(condition &&cond, std::function<void(row_handle)> &&visit
cond.prepare(*this);
std::map<category *, condition> potential_orphans;
auto ri = begin();
while (ri != end())
{
if (cond(*ri))
{
visit(*ri);
if (visit)
visit(*ri);
for (auto &&[childCat, link] : m_child_links)
{
auto ccond = get_children_condition(*ri, *childCat);
if (not ccond)
continue;
potential_orphans[childCat] = std::move(potential_orphans[childCat]) or std::move(ccond);
}
save_value sv(m_validator);
ri = erase(ri);
++result;
}
@@ -1238,6 +1170,9 @@ size_t category::erase(condition &&cond, std::function<void(row_handle)> &&visit
++ri;
}
for (auto &&[childCat, condition] : potential_orphans)
childCat->erase_orphans(std::move(condition), *this);
return result;
}
@@ -1292,23 +1227,37 @@ std::string category::get_unique_id(std::function<std::string(int)> generator)
{
using namespace cif::literals;
std::string id_tag = "id";
if (m_cat_validator != nullptr and m_cat_validator->m_keys.size() == 1)
id_tag = m_cat_validator->m_keys.front();
// calling size() often is a waste of resources
if (m_last_unique_num == 0)
m_last_unique_num = static_cast<uint32_t>(size());
for (;;)
std::string result = generator(static_cast<int>(m_last_unique_num++));
std::string id_tag = "id";
if (m_cat_validator != nullptr and m_cat_validator->m_keys.size() == 1)
{
std::string result = generator(static_cast<int>(m_last_unique_num++));
if (exists(key(id_tag) == result))
continue;
return result;
if (m_index == nullptr and m_cat_validator != nullptr)
m_index = new category_index(this);
for (;;)
{
if (m_index->find_by_value({{ id_tag, result }}) == nullptr)
break;
result = generator(static_cast<int>(m_last_unique_num++));
}
}
else
{
for (;;)
{
if (not exists(key(id_tag) == result))
break;
result = generator(static_cast<int>(m_last_unique_num++));
}
}
return result;
}
void category::update_value(const std::vector<row_handle> &rows, std::string_view tag, std::string_view value)

View File

@@ -24,16 +24,15 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++.hpp"
#include <filesystem>
#include <fstream>
#include <map>
#include <mutex>
#include <numeric>
#include <shared_mutex>
#include <filesystem>
#include <fstream>
#include <cif++/compound.hpp>
namespace fs = std::filesystem;
namespace cif
@@ -223,6 +222,28 @@ bool compound::atoms_bonded(const std::string &atomId_1, const std::string &atom
return i != m_bonds.end();
}
float compound::bond_length(const std::string &atomId_1, const std::string &atomId_2) const
{
auto i = find_if(m_bonds.begin(), m_bonds.end(),
[&](const compound_bond &b)
{
return (b.atom_id[0] == atomId_1 and b.atom_id[1] == atomId_2) or (b.atom_id[0] == atomId_2 and b.atom_id[1] == atomId_1);
});
float result = std::numeric_limits<float>::max();
if (i != m_bonds.end())
{
auto a = get_atom_by_atom_id(atomId_1);
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});
}
return result;
}
// --------------------------------------------------------------------
// known amino acids and bases
@@ -655,13 +676,13 @@ compound_factory::compound_factory()
{
auto ccd = cif::load_resource("components.cif");
if (ccd)
m_impl.reset(new CCD_compound_factory_impl(m_impl));
m_impl = std::make_shared<CCD_compound_factory_impl>(m_impl);
else if (cif::VERBOSE > 0)
std::cerr << "CCD components.cif file was not found" << std::endl;
const char *clibd_mon = getenv("CLIBD_MON");
if (clibd_mon != nullptr and fs::is_directory(clibd_mon))
m_impl.reset(new CCP4_compound_factory_impl(clibd_mon));
m_impl = std::make_shared<CCP4_compound_factory_impl>(clibd_mon, m_impl);
else if (cif::VERBOSE > 0)
std::cerr << "CCP4 monomers library not found, CLIBD_MON is not defined" << std::endl;
}

View File

@@ -24,8 +24,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cif++/category.hpp>
#include <cif++/condition.hpp>
#include "cif++/category.hpp"
#include "cif++/condition.hpp"
namespace cif
{
@@ -63,7 +63,7 @@ namespace detail
condition_impl *key_equals_condition_impl::prepare(const category &c)
{
m_item_ix = get_column_ix(c, m_item_tag);
m_item_ix = c.get_column_ix(m_item_tag);
m_icase = is_column_type_uchar(c, m_item_tag);
if (c.get_cat_validator() != nullptr and
@@ -76,53 +76,82 @@ namespace detail
return this;
}
condition_impl *and_condition_impl::prepare(const category &c)
bool found_in_range(condition_impl *c, std::vector<and_condition_impl *>::iterator b, std::vector<and_condition_impl *>::iterator e)
{
for (auto &sub : mSub)
sub = sub->prepare(c);
bool result = true;
for (;;)
for (auto s = b; s != e; ++s)
{
auto si = find_if(mSub.begin(), mSub.end(), [](condition_impl *sub) { return dynamic_cast<and_condition_impl *>(sub) != nullptr; });
if (si == mSub.end())
auto &cs = (*s)->m_sub;
if (find_if(cs.begin(), cs.end(), [c](const condition_impl *i) { return i->equals(c); }) == cs.end())
{
result = false;
break;
and_condition_impl *sub_and = static_cast<and_condition_impl *>(*si);
mSub.erase(si);
mSub.insert(mSub.end(), sub_and->mSub.begin(), sub_and->mSub.end());
sub_and->mSub.clear();
delete sub_and;
}
}
return this;
return result;
}
condition_impl *and_condition_impl::combine_equal(std::vector<and_condition_impl *> &subs, or_condition_impl *oc)
{
and_condition_impl *and_result = nullptr;
auto first = subs.front();
auto &fc = first->m_sub;
for (auto c : fc)
{
if (not found_in_range(c, subs.begin() + 1, subs.end()))
continue;
if (and_result == nullptr)
and_result = new and_condition_impl();
and_result->m_sub.push_back(c);
fc.erase(remove(fc.begin(), fc.end(), c), fc.end());
for (auto sub : subs)
{
auto &ssub = sub->m_sub;
for (auto sc : ssub)
{
if (not sc->equals(c))
continue;
ssub.erase(remove(ssub.begin(), ssub.end(), sc), ssub.end());
delete sc;
break;
}
}
}
if (and_result != nullptr)
{
and_result->m_sub.push_back(oc);
return and_result;
}
return oc;
}
condition_impl *or_condition_impl::prepare(const category &c)
{
condition_impl *result = this;
std::vector<and_condition_impl *> and_conditions;
mA = mA->prepare(c);
mB = mB->prepare(c);
key_equals_condition_impl *equals = dynamic_cast<key_equals_condition_impl*>(mA);
key_is_empty_condition_impl *empty = dynamic_cast<key_is_empty_condition_impl*>(mB);
if (equals == nullptr and empty == nullptr)
for (auto &sub : m_sub)
{
equals = dynamic_cast<key_equals_condition_impl*>(mB);
empty = dynamic_cast<key_is_empty_condition_impl*>(mA);
sub = sub->prepare(c);
if (typeid(*sub) == typeid(and_condition_impl))
and_conditions.push_back(static_cast<and_condition_impl *>(sub));
}
if (equals != nullptr and empty != nullptr and equals->m_item_tag == empty->m_item_tag)
{
result = new detail::key_equals_or_empty_condition_impl(equals);
result = result->prepare(c);
delete this;
}
if (and_conditions.size() == m_sub.size())
return and_condition_impl::combine_equal(and_conditions, this);
return result;
return this;
}
} // namespace detail

View File

@@ -24,11 +24,35 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cif++/datablock.hpp>
#include "cif++/datablock.hpp"
namespace cif
{
datablock::datablock(const datablock &db)
: std::list<category>(db)
, m_name(db.m_name)
, m_validator(db.m_validator)
{
for (auto &cat : *this)
cat.update_links(*this);
}
datablock &datablock::operator=(const datablock &db)
{
if (this != &db)
{
std::list<category>::operator=(db);
m_name = db.m_name;
m_validator = db.m_validator;
for (auto &cat : *this)
cat.update_links(*this);
}
return *this;
}
void datablock::set_validator(const validator *v)
{
m_validator = v;

View File

@@ -24,10 +24,10 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cif++/condition.hpp>
#include <cif++/dictionary_parser.hpp>
#include <cif++/file.hpp>
#include <cif++/parser.hpp>
#include "cif++/condition.hpp"
#include "cif++/dictionary_parser.hpp"
#include "cif++/file.hpp"
#include "cif++/parser.hpp"
namespace cif
{
@@ -117,7 +117,7 @@ class dictionary_parser : public parser
if (not m_collected_item_types)
m_collected_item_types = collect_item_types();
std::string saveFrameName = m_token_value;
std::string saveFrameName { m_token_value };
if (saveFrameName.empty())
error("Invalid save frame, should contain more than just 'save_' here");
@@ -127,7 +127,7 @@ class dictionary_parser : public parser
datablock dict(m_token_value);
datablock::iterator cat = dict.end();
match(CIFToken::SAVE);
match(CIFToken::SAVE_NAME);
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag)
{
if (m_lookahead == CIFToken::LOOP)
@@ -183,7 +183,7 @@ class dictionary_parser : public parser
}
}
match(CIFToken::SAVE);
match(CIFToken::SAVE_);
if (isCategorySaveFrame)
{
@@ -481,4 +481,11 @@ validator parse_dictionary(std::string_view name, std::istream &is)
return result;
}
} // namespace cif
void extend_dictionary(validator &v, std::istream &is)
{
file f;
dictionary_parser p(v, is, f);
p.load_dictionary();
}
} // namespace cif

View File

@@ -24,8 +24,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cif++/file.hpp>
#include <cif++/gzio.hpp>
#include "cif++/file.hpp"
#include "cif++/gzio.hpp"
namespace cif
{
@@ -125,7 +125,7 @@ void file::load_dictionary(std::string_view name)
bool file::contains(std::string_view name) const
{
return std::find_if(begin(), end(), [name](const datablock &db) { return db.name() == name; }) != end();
return std::find_if(begin(), end(), [name](const datablock &db) { return iequals(db.name(), name); }) != end();
}
datablock &file::operator[](std::string_view name)

View File

@@ -24,9 +24,9 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cassert>
#include "cif++/row.hpp"
#include <cif++/row.hpp>
#include <cassert>
namespace cif
{

File diff suppressed because it is too large Load Diff

View File

@@ -24,35 +24,167 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++/utilities.hpp"
#include "cif++/forward_decl.hpp"
#include "cif++/parser.hpp"
#include "cif++/file.hpp"
#include <cassert>
#include <iostream>
#include <map>
#include <regex>
#include <stack>
#include <cif++/utilities.hpp>
#include <cif++/forward_decl.hpp>
#include <cif++/parser.hpp>
#include <cif++/file.hpp>
namespace cif
{
extern int VERBOSE;
}
namespace cif
{
// --------------------------------------------------------------------
class reserved_words_automaton
{
public:
reserved_words_automaton() {}
enum move_result
{
undefined,
no_keyword,
data,
global,
loop,
save,
save_plus,
stop
};
constexpr bool finished() const
{
return m_state <= 0;
}
constexpr bool matched() const
{
return m_state < 0;
}
constexpr move_result move(int ch)
{
move_result result = undefined;
switch (m_state)
{
case 0:
break;
case -1: // data_
if (sac_parser::is_non_blank(ch))
m_seen_trailing_chars = true;
else if (m_seen_trailing_chars)
result = data;
else
result = no_keyword;
break;
case -2: // global_
result = sac_parser::is_non_blank(ch) ? no_keyword : global;
break;
case -3: // loop_
result = sac_parser::is_non_blank(ch) ? no_keyword : loop;
break;
case -4: // save_
if (sac_parser::is_non_blank(ch))
m_seen_trailing_chars = true;
else if (m_seen_trailing_chars)
result = save_plus;
else
result = save;
break;
case -5: // stop_
result = sac_parser::is_non_blank(ch) ? no_keyword : stop;
break;
default:
assert(m_state > 0 and m_state < NODE_COUNT);
for (;;)
{
if (s_dag[m_state].ch == (ch & ~0x20))
{
m_state = s_dag[m_state].next_match;
break;
}
m_state = s_dag[m_state].next_nomatch;
if (m_state == 0)
{
result = no_keyword;
break;
}
}
break;
}
if (result != undefined)
m_state = 0;
return result;
}
private:
static constexpr struct node
{
int16_t ch;
int8_t next_match;
int8_t next_nomatch;
} s_dag[] = {
{ 0 },
{ 'D', 5, 2 },
{ 'G', 9, 3 },
{ 'L', 15, 4 },
{ 'S', 19, 0 },
{ 'A', 6, 0 },
{ 'T', 7, 0 },
{ 'A', 8, 0 },
{ '_', -1, 0 },
{ 'L', 10, 0 },
{ 'O', 11, 0 },
{ 'B', 12, 0 },
{ 'A', 13, 0 },
{ 'L', 14, 0 },
{ '_', -2, 0 },
{ 'O', 16, 0},
{ 'O', 17, 0 },
{ 'P', 18, 0 },
{ '_', -3, 0 },
{ 'A', 21, 20 },
{ 'T', 24, 0 },
{ 'V', 22, 0 },
{ 'E', 23, 0 },
{ '_', -4, 0 },
{ 'O', 25, 0 },
{ 'P', 26, 0 },
{ '_', -5, 0 },
};
static constexpr int NODE_COUNT = sizeof(s_dag) / sizeof(node);
int m_state = 1;
bool m_seen_trailing_chars = false;
};
// --------------------------------------------------------------------
sac_parser::sac_parser(std::istream &is, bool init)
: m_source(*is.rdbuf())
{
m_token_buffer.reserve(8192);
if (is.rdbuf() == nullptr)
throw std::runtime_error("Attempt to read from uninitialised stream");
m_validate = true;
m_line_nr = 1;
m_bol = true;
@@ -60,45 +192,54 @@ sac_parser::sac_parser(std::istream &is, bool init)
m_lookahead = get_next_token();
}
bool sac_parser::is_unquoted_string(std::string_view text)
{
bool result = text.empty() or is_ordinary(text.front());
if (result)
{
reserved_words_automaton automaton;
for (char ch : text)
{
if (not is_non_blank(ch))
{
result = false;
break;
}
automaton.move(ch);
}
if (automaton.matched())
result = false;
}
return result;
}
// get_next_char takes a char from the buffer, or if it is empty
// from the istream. This function also does carriage/linefeed
// translation.
int sac_parser::get_next_char()
{
int result = std::char_traits<char>::eof();
if (m_buffer.empty())
result = m_source.sbumpc();
else
{
result = m_buffer.back();
m_buffer.pop_back();
}
// very simple CR/LF translation into LF
if (result == '\r')
{
int lookahead = m_source.sbumpc();
if (lookahead != '\n')
m_buffer.push_back(lookahead);
result = '\n';
}
int result = m_source.sbumpc();
if (result == std::char_traits<char>::eof())
m_token_value.push_back(0);
m_token_buffer.push_back(0);
else
m_token_value.push_back(std::char_traits<char>::to_char_type(result));
if (result == '\n')
++m_line_nr;
if (VERBOSE >= 6)
{
std::cerr << "get_next_char => ";
if (iscntrl(result) or not isprint(result))
std::cerr << int(result) << std::endl;
else
std::cerr << char(result) << std::endl;
if (result == '\r')
{
if (m_source.sgetc() == '\n')
m_source.sbumpc();
++m_line_nr;
result = '\n';
}
else if (result == '\n')
++m_line_nr;
m_token_buffer.push_back(std::char_traits<char>::to_char_type(result));
}
return result;
@@ -106,44 +247,22 @@ int sac_parser::get_next_char()
void sac_parser::retract()
{
assert(not m_token_value.empty());
assert(not m_token_buffer.empty());
char ch = m_token_value.back();
char ch = m_token_buffer.back();
if (ch == '\n')
--m_line_nr;
m_buffer.push_back(ch == 0 ? std::char_traits<char>::eof() : std::char_traits<char>::to_int_type(ch));
m_token_value.pop_back();
}
int sac_parser::restart(int start)
{
int result = 0;
while (not m_token_value.empty())
retract();
switch (start)
if (ch != 0)
{
case State::Start:
result = State::Float;
break;
// since we always putback at most a single character,
// the test below should never fail.
case State::Float:
result = State::Int;
break;
case State::Int:
result = State::Value;
break;
default:
error("Invalid state in SacParser");
if (m_source.sputbackc(ch) == std::char_traits<char>::eof())
throw std::runtime_error("putback failure");
}
m_bol = false;
return result;
m_token_buffer.pop_back();
}
sac_parser::CIFToken sac_parser::get_next_token()
@@ -152,11 +271,13 @@ sac_parser::CIFToken sac_parser::get_next_token()
CIFToken result = CIFToken::Unknown;
int quoteChar = 0;
int state = State::Start, start = State::Start;
State state = State::Start;
m_bol = false;
m_token_value.clear();
mTokenType = CIFValue::Unknown;
m_token_buffer.clear();
m_token_value = {};
reserved_words_automaton dag;
while (result == CIFToken::Unknown)
{
@@ -180,23 +301,27 @@ sac_parser::CIFToken sac_parser::get_next_token()
state = State::Tag;
else if (ch == ';' and m_bol)
state = State::TextField;
else if (ch == '?')
state = State::QuestionMark;
else if (ch == '\'' or ch == '"')
{
quoteChar = ch;
state = State::QuotedString;
}
else if (dag.move(ch) == reserved_words_automaton::undefined)
state = State::Reserved;
else
state = start = restart(start);
state = State::Value;
break;
case State::White:
if (ch == kEOF)
result = CIFToken::Eof;
else if (not isspace(ch))
else if (not is_space(ch))
{
state = State::Start;
retract();
m_token_value.clear();
m_token_buffer.clear();
}
else
m_bol = (ch == '\n');
@@ -207,38 +332,40 @@ sac_parser::CIFToken sac_parser::get_next_token()
{
state = State::Start;
m_bol = true;
m_token_value.clear();
m_token_buffer.clear();
}
else if (ch == kEOF)
result = CIFToken::Eof;
else if (not is_any_print(ch))
error("invalid character in comment");
break;
case State::QuestionMark:
if (not is_non_blank(ch))
{
retract();
result = CIFToken::Value;
}
else
state = State::Value;
break;
case State::TextField:
if (ch == '\n')
state = State::TextField + 1;
state = State::TextFieldNL;
else if (ch == kEOF)
error("unterminated textfield");
// else if (ch == '\\')
// state = State::Esc;
else if (not is_any_print(ch) and cif::VERBOSE > 2)
warning("invalid character in text field '" + std::string({static_cast<char>(ch)}) + "' (" + std::to_string((int)ch) + ")");
break;
// case State::Esc:
// if (ch == '\n')
// break;
case State::TextField + 1:
case State::TextFieldNL:
if (is_text_lead(ch) or ch == ' ' or ch == '\t')
state = State::TextField;
else if (ch == ';')
{
assert(m_token_value.length() >= 2);
m_token_value = m_token_value.substr(1, m_token_value.length() - 3);
mTokenType = CIFValue::TextField;
assert(m_token_buffer.size() >= 2);
m_token_value = std::string_view(m_token_buffer.data() + 1, m_token_buffer.size() - 3);
result = CIFToken::Value;
}
else if (ch == kEOF)
@@ -261,12 +388,10 @@ sac_parser::CIFToken sac_parser::get_next_token()
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::String;
if (m_token_value.length() < 2)
if (m_token_buffer.size() < 2)
error("Invalid quoted string token");
m_token_value = m_token_value.substr(1, m_token_value.length() - 2);
m_token_value = std::string_view(m_token_buffer.data() + 1, m_token_buffer.size() - 2);
}
else if (ch == quoteChar)
;
@@ -283,148 +408,68 @@ sac_parser::CIFToken sac_parser::get_next_token()
{
retract();
result = CIFToken::Tag;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
}
break;
case State::Float:
if (ch == '+' or ch == '-')
case State::Reserved:
switch (dag.move(ch))
{
state = State::Float + 1;
case reserved_words_automaton::undefined:
break;
case reserved_words_automaton::no_keyword:
if (not is_non_blank(ch))
{
retract();
result = CIFToken::Value;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
}
else
state = State::Value;
break;
case reserved_words_automaton::data:
retract();
m_token_value = std::string_view(m_token_buffer.data() + 5, m_token_buffer.size() - 5);
result = CIFToken::DATA;
break;
case reserved_words_automaton::global:
retract();
result = CIFToken::GLOBAL;
break;
case reserved_words_automaton::loop:
retract();
result = CIFToken::LOOP;
break;
case reserved_words_automaton::save:
retract();
result = CIFToken::SAVE_;
break;
case reserved_words_automaton::save_plus:
retract();
m_token_value = std::string_view(m_token_buffer.data() + 5, m_token_buffer.size() - 5);
result = CIFToken::SAVE_NAME;
break;
case reserved_words_automaton::stop:
retract();
result = CIFToken::STOP;
break;
}
else if (isdigit(ch))
state = State::Float + 1;
else
state = start = restart(start);
break;
case State::Float + 1:
// if (ch == '(') // numeric???
// mState = State::NumericSuffix;
// else
if (ch == '.')
state = State::Float + 2;
else if (tolower(ch) == 'e')
state = State::Float + 3;
else if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Int;
}
else
state = start = restart(start);
break;
// parsed '.'
case State::Float + 2:
if (tolower(ch) == 'e')
state = State::Float + 3;
else if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Float;
}
else
state = start = restart(start);
break;
// parsed 'e'
case State::Float + 3:
if (ch == '-' or ch == '+')
state = State::Float + 4;
else if (isdigit(ch))
state = State::Float + 5;
else
state = start = restart(start);
break;
case State::Float + 4:
if (isdigit(ch))
state = State::Float + 5;
else
state = start = restart(start);
break;
case State::Float + 5:
if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Float;
}
else
state = start = restart(start);
break;
case State::Int:
if (isdigit(ch) or ch == '+' or ch == '-')
state = State::Int + 1;
else
state = start = restart(start);
break;
case State::Int + 1:
if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Int;
}
else
state = start = restart(start);
break;
case State::Value:
if (ch == '_')
{
std::string s = to_lower_copy(m_token_value);
if (s == "global_")
result = CIFToken::GLOBAL;
else if (s == "stop_")
result = CIFToken::STOP;
else if (s == "loop_")
result = CIFToken::LOOP;
else if (s == "data_")
{
state = State::DATA;
continue;
}
else if (s == "save_")
{
state = State::SAVE;
continue;
}
}
if (result == CIFToken::Unknown and not is_non_blank(ch))
{
retract();
result = CIFToken::Value;
if (m_token_value == ".")
mTokenType = CIFValue::Inapplicable;
else if (m_token_value == "?")
{
mTokenType = CIFValue::Unknown;
m_token_value.clear();
}
}
break;
case State::DATA:
case State::SAVE:
if (not is_non_blank(ch))
{
retract();
if (state == State::DATA)
result = CIFToken::DATA;
else
result = CIFToken::SAVE;
m_token_value.erase(m_token_value.begin(), m_token_value.begin() + 5);
result = CIFToken::Value;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
break;
}
break;
@@ -438,8 +483,6 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (VERBOSE >= 5)
{
std::cerr << get_token_name(result);
if (mTokenType != CIFValue::Unknown)
std::cerr << ' ' << get_value_name(mTokenType);
if (result != CIFToken::Eof)
std::cerr << " " << std::quoted(m_token_value);
std::cerr << std::endl;
@@ -511,7 +554,7 @@ bool sac_parser::parse_single_datablock(const std::string &datablock)
break;
case string_quote:
if (std::isspace(ch))
if (is_space(ch))
state = start;
else
state = string;
@@ -523,7 +566,7 @@ bool sac_parser::parse_single_datablock(const std::string &datablock)
break;
case data:
if (isspace(ch) and dblk[si] == 0)
if (is_space(ch) and dblk[si] == 0)
found = true;
else if (dblk[si++] != ch)
state = start;
@@ -601,7 +644,7 @@ sac_parser::datablock_index sac_parser::index_datablocks()
break;
case string_quote:
if (std::isspace(ch))
if (is_space(ch))
state = start;
else
state = string;
@@ -625,7 +668,7 @@ sac_parser::datablock_index sac_parser::index_datablocks()
case data_name:
if (is_non_blank(ch))
datablock.insert(datablock.end(), char(ch));
else if (isspace(ch))
else if (is_space(ch))
{
if (not datablock.empty())
index[datablock] = m_source.pubseekoff(0, std::ios_base::cur, std::ios_base::in);
@@ -701,7 +744,7 @@ void sac_parser::parse_datablock()
static const std::string kUnitializedCategory("<invalid>");
std::string cat = kUnitializedCategory; // intial value acts as a guard for empty category names
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag or m_lookahead == CIFToken::SAVE)
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag or m_lookahead == CIFToken::SAVE_NAME)
{
switch (m_lookahead)
{
@@ -766,7 +809,7 @@ void sac_parser::parse_datablock()
break;
}
case CIFToken::SAVE:
case CIFToken::SAVE_NAME:
parse_save_frame();
break;
@@ -784,13 +827,16 @@ void sac_parser::parse_save_frame()
// --------------------------------------------------------------------
void parser::produce_datablock(const std::string &name)
void parser::produce_datablock(std::string_view name)
{
if (VERBOSE >= 4)
std::cerr << "producing data_" << name << std::endl;
const auto &[iter, ignore] = m_file.emplace(name);
m_datablock = &(*iter);
}
void parser::produce_category(const std::string &name)
void parser::produce_category(std::string_view name)
{
if (VERBOSE >= 4)
std::cerr << "producing category " << name << std::endl;
@@ -801,7 +847,7 @@ void parser::produce_category(const std::string &name)
void parser::produce_row()
{
if (VERBOSE >= 4)
if (VERBOSE >= 4 and m_category != nullptr)
std::cerr << "producing row for category " << m_category->name() << std::endl;
if (m_category == nullptr)
@@ -812,7 +858,7 @@ void parser::produce_row()
// m_row.lineNr(m_line_nr);
}
void parser::produce_item(const std::string &category, const std::string &item, const std::string &value)
void parser::produce_item(std::string_view category, std::string_view item, std::string_view value)
{
if (VERBOSE >= 4)
std::cerr << "producing _" << category << '.' << item << " -> " << value << std::endl;
@@ -823,4 +869,4 @@ void parser::produce_item(const std::string &category, const std::string &item,
m_row[item] = m_token_value;
}
} // namespace cif
} // namespace cif

View File

@@ -24,6 +24,10 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++.hpp"
#include "cif++/pdb/cif2pdb.hpp"
#include "cif++/gzio.hpp"
#include <cmath>
#include <deque>
#include <iomanip>
@@ -31,9 +35,6 @@
#include <regex>
#include <set>
#include <cif++.hpp>
#include <cif++/pdb/cif2pdb.hpp>
#include <cif++/gzio.hpp>
namespace cif::pdb
{
@@ -121,9 +122,9 @@ std::string cifSoftware(const datablock &db, SoftwareType sw)
{
switch (sw)
{
case eRefinement: result = db["computing"].find1<std::string>(key("entry_id") == db.name(), "structure_refinement"); break;
case eDataScaling: result = db["computing"].find1<std::string>(key("entry_id") == db.name(), "pdbx_data_reduction_ds"); break;
case eDataReduction: result = db["computing"].find1<std::string>(key("entry_id") == db.name(), "pdbx_data_reduction_ii"); break;
case eRefinement: result = db["computing"].find_first<std::string>(key("entry_id") == db.name(), "structure_refinement"); break;
case eDataScaling: result = db["computing"].find_first<std::string>(key("entry_id") == db.name(), "pdbx_data_reduction_ds"); break;
case eDataReduction: result = db["computing"].find_first<std::string>(key("entry_id") == db.name(), "pdbx_data_reduction_ii"); break;
default: break;
}
@@ -135,11 +136,11 @@ std::string cifSoftware(const datablock &db, SoftwareType sw)
switch (sw)
{
case eRefinement: r = software.find1(key("classification") == "refinement"); break;
case eDataScaling: r = software.find1(key("classification") == "data scaling"); break;
case eDataExtraction: r = software.find1(key("classification") == "data extraction"); break;
case eDataReduction: r = software.find1(key("classification") == "data reduction"); break;
case ePhasing: r = software.find1(key("classification") == "phasing"); break;
case eRefinement: r = software.find_first(key("classification") == "refinement"); break;
case eDataScaling: r = software.find_first(key("classification") == "data scaling"); break;
case eDataExtraction: r = software.find_first(key("classification") == "data extraction"); break;
case eDataReduction: r = software.find_first(key("classification") == "data reduction"); break;
case ePhasing: r = software.find_first(key("classification") == "phasing"); break;
}
if (not r.empty())
@@ -671,6 +672,7 @@ class Fi : public FBase
virtual void out(std::ostream &os)
{
std::string s{ text() };
if (s.empty())
{
os << "NULL";
@@ -678,7 +680,18 @@ class Fi : public FBase
os << std::string(os.width() - 4, ' ');
}
else
os << std::stol(s);
{
long l = 0;
auto r = std::from_chars(s.data(), s.data() + s.length(), l);
if (r.ec != std::errc())
{
if (VERBOSE > 0)
std::cerr << "Failed to write '" << s << "' as a long from field " << mField << ", this indicates an error in the code for writing PDB files" << std::endl;
os << s;
}
else
os << l;
}
}
};
@@ -711,7 +724,7 @@ class Ff : public FBase
if (r.ec != std::errc())
{
if (VERBOSE > 0)
std::cerr << "Failed to write '" << s << "' as a double, this indicates an error in the code for writing PDB files" << std::endl;
std::cerr << "Failed to write '" << s << "' as a double from field " << mField << ", this indicates an error in the code for writing PDB files" << std::endl;
os << s;
}
else
@@ -957,7 +970,7 @@ void WriteRemark3BusterTNT(std::ostream &pdbFile, const datablock &db)
for (auto t : tls)
{
std::string id = t["id"].as<std::string>();
auto g = db["pdbx_refine_tls_group"].find1(key("refine_tls_id") == id);
auto g = db["pdbx_refine_tls_group"].find_first(key("refine_tls_id") == id);
pdbFile << RM3("") << std::endl
<< RM3(" TLS GROUP : ") << id << std::endl
@@ -1383,7 +1396,7 @@ void WriteRemark3Refmac(std::ostream &pdbFile, const datablock &db)
unit = " : ";
pdbFile << RM3(" ", 18) << type
<< SEP("", -2) << Fi(l, "pdbx_ens_id")
<< SEP("", -2) << Fs(l, "pdbx_ens_id")
<< SEP(" ", 1) << Fs(l, "pdbx_auth_asym_id")
<< SEP(unit.c_str(), -6) << Fi(l, "pdbx_number")
<< SEP(" ;", -6, 3) << Ff(l, "rms_dev_position")
@@ -1393,10 +1406,24 @@ void WriteRemark3Refmac(std::ostream &pdbFile, const datablock &db)
}
}
// TODO: add twin information
pdbFile << RM3("") << std::endl
<< RM3(" TWIN DETAILS") << std::endl;
// { R"(TWIN DETAILS)", "", {} },
// { R"(NUMBER OF TWIN DOMAINS)", "", {} },
auto &twins = db["pdbx_reflns_twin"];
if (twins.empty())
pdbFile << RM3(" NUMBER OF TWIN DOMAINS : NULL") << std::endl;
else
{
pdbFile << RM3(" NUMBER OF TWIN DOMAINS : ") << twins.size() << std::endl;
int nr = 1;
for (auto twin : twins)
{
pdbFile << RM3(" TWIN DOMAIN : ") << nr++ << std::endl
<< RM3(" TWIN OPERATOR : ") << Fs(twin, "operator") << std::endl
<< RM3(" TWIN FRACTION : ") << SEP("", -6, 3) << Ff(twin, "fraction") << std::endl;
}
}
auto &tls = db["pdbx_refine_tls"];
@@ -1655,7 +1682,7 @@ void WriteRemark3Phenix(std::ostream &pdbFile, const datablock &db)
{
std::string id = t["id"].as<std::string>();
auto pdbx_refine_tls_group = db["pdbx_refine_tls_group"].find1(key("refine_tls_id") == id);
auto pdbx_refine_tls_group = db["pdbx_refine_tls_group"].find_first(key("refine_tls_id") == id);
pdbFile << RM3(" TLS GROUP : ") << id << std::endl
<< RM3(" SELECTION: ") << Fs(pdbx_refine_tls_group, "selection_details") << std::endl
@@ -2187,20 +2214,20 @@ void WriteRemark200(std::ostream &pdbFile, const datablock &db)
std::string diffrn_id = diffrn["id"].as<std::string>();
std::string crystal_id = diffrn["crystal_id"].as<std::string>();
auto diffrn_radiation = db["diffrn_radiation"].find1(key("diffrn_id") == diffrn_id);
auto diffrn_radiation_wavelength = db["diffrn_radiation_wavelength"].find1(key("id") == diffrn_radiation["wavelength_id"].as<std::string>());
auto diffrn_source = db["diffrn_source"].find1(key("diffrn_id") == diffrn_id);
auto diffrn_detector = db["diffrn_detector"].find1(key("diffrn_id") == diffrn_id);
auto exptl = db["exptl"].find1(key("entry_id") == db.name());
auto exptl_crystal = db["exptl_crystal"].find1(key("id") == crystal_id);
auto exptl_crystal_grow = db["exptl_crystal_grow"].find1(key("crystal_id") == crystal_id);
auto computing = db["computing"].find1(key("entry_id") == db.name());
auto reflns = db["reflns"].find1(key("entry_id") == db.name());
auto diffrn_radiation = db["diffrn_radiation"].find_first(key("diffrn_id") == diffrn_id);
auto diffrn_radiation_wavelength = db["diffrn_radiation_wavelength"].find_first(key("id") == diffrn_radiation["wavelength_id"].as<std::string>());
auto diffrn_source = db["diffrn_source"].find_first(key("diffrn_id") == diffrn_id);
auto diffrn_detector = db["diffrn_detector"].find_first(key("diffrn_id") == diffrn_id);
auto exptl = db["exptl"].find_first(key("entry_id") == db.name());
auto exptl_crystal = db["exptl_crystal"].find_first(key("id") == crystal_id);
auto exptl_crystal_grow = db["exptl_crystal_grow"].find_first(key("crystal_id") == crystal_id);
auto computing = db["computing"].find_first(key("entry_id") == db.name());
auto reflns = db["reflns"].find_first(key("entry_id") == db.name());
std::string pdbx_diffrn_id = reflns["pdbx_diffrn_id"].as<std::string>();
auto reflns_shell = db["reflns_shell"].find1(key("pdbx_diffrn_id") == pdbx_diffrn_id);
auto refine = db["refine"].find1(key("pdbx_diffrn_id") == pdbx_diffrn_id);
auto reflns_shell = db["reflns_shell"].find_first(key("pdbx_diffrn_id") == pdbx_diffrn_id);
auto refine = db["refine"].find_first(key("pdbx_diffrn_id") == pdbx_diffrn_id);
std::string date =
diffrn_detector.empty() ? "NULL" : cif2pdbDate(diffrn_detector["pdbx_collection_date"].as<std::string>());
@@ -2325,7 +2352,7 @@ void WriteRemark280(std::ostream &pdbFile, const datablock &db)
for (auto exptl_crystal : db["exptl_crystal"])
{
std::string crystal_id = exptl_crystal["id"].as<std::string>();
auto exptl_crystal_grow = db["exptl_crystal_grow"].find1(key("crystal_id") == crystal_id);
auto exptl_crystal_grow = db["exptl_crystal_grow"].find_first(key("crystal_id") == crystal_id);
pdbFile
<< RM("") << std::endl
@@ -2457,7 +2484,7 @@ void WriteRemark350(std::ostream &pdbFile, const datablock &db)
}
}
auto gen = db["pdbx_struct_assembly_gen"].find1(key("assembly_id") == id);
auto gen = db["pdbx_struct_assembly_gen"].find_first(key("assembly_id") == id);
if (gen)
{
@@ -2471,7 +2498,7 @@ void WriteRemark350(std::ostream &pdbFile, const datablock &db)
for (auto oper_id : split<std::string>(oper_id_list, ",", true))
{
auto r = db["pdbx_struct_oper_list"].find1(key("id") == oper_id);
auto r = db["pdbx_struct_oper_list"].find_first(key("id") == oper_id);
pdbFile << RM(" BIOMT1 ", -3) << Fs(r, "id")
<< SEP(" ", -9, 6) << Ff(r, "matrix[1][1]")
@@ -2951,7 +2978,7 @@ int WriteHeterogen(std::ostream &pdbFile, const datablock &db)
if (id == water_comp_id)
continue;
std::string syn = db["chem_comp"].find1<std::string>(key("id") == id, "pdbx_synonyms");
std::string syn = db["chem_comp"].find_first<std::string>(key("id") == id, "pdbx_synonyms");
if (syn.empty())
continue;
@@ -3104,7 +3131,7 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
std::string initResName, initChainID, initICode, endResName, endChainID, endICode;
int initSeqNum, endSeqNum;
auto r1 = db["struct_sheet_range"].find1(key("sheet_id") == sheetID and key("id") == rangeID1);
auto r1 = db["struct_sheet_range"].find_first(key("sheet_id") == sheetID and key("id") == rangeID1);
cif::tie(initResName, initICode, endResName, endICode,
initResName, initChainID, initSeqNum, endResName, endChainID, endSeqNum) = r1.get("beg_label_comp_id", "pdbx_beg_PDB_ins_code", "end_label_comp_id",
@@ -3119,7 +3146,7 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
std::string initResName, initChainID, initICode, endResName, endChainID, endICode, curAtom, curResName, curChainID, curICode, prevAtom, prevResName, prevChainID, prevICode;
int initSeqNum, endSeqNum, curResSeq, prevResSeq;
auto r2 = db["struct_sheet_range"].find1(key("sheet_id") == sheetID and key("id") == rangeID2);
auto r2 = db["struct_sheet_range"].find_first(key("sheet_id") == sheetID and key("id") == rangeID2);
cif::tie(initResName, initICode, endResName, endICode,
initResName, initChainID, initSeqNum, endResName, endChainID, endSeqNum) = r2.get("beg_label_comp_id", "pdbx_beg_PDB_ins_code", "end_label_comp_id",
@@ -3288,10 +3315,10 @@ int WriteMiscellaneousFeatures(std::ostream &pdbFile, const datablock &db)
void WriteCrystallographic(std::ostream &pdbFile, const datablock &db)
{
auto r = db["symmetry"].find1(key("entry_id") == db.name());
auto r = db["symmetry"].find_first(key("entry_id") == db.name());
std::string symmetry = r["space_group_name_H-M"].as<std::string>();
r = db["cell"].find1(key("entry_id") == db.name());
r = db["cell"].find_first(key("entry_id") == db.name());
pdbFile << cif::format("CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11.11s%4d", r["length_a"].as<double>(), r["length_b"].as<double>(), r["length_c"].as<double>(), r["angle_alpha"].as<double>(), r["angle_beta"].as<double>(), r["angle_gamma"].as<double>(), symmetry, r["Z_PDB"].as<int>()) << std::endl;
}
@@ -3338,10 +3365,16 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
const std::map<std::string, std::tuple<std::string, int, std::string>> &last_resseq_for_chain_map,
std::set<std::string> &terminatedChains, int model_nr)
{
using namespace cif::literals;
int numCoord = 0, numTer = 0;
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();
@@ -3358,10 +3391,21 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
cif::tie(nextResName, nextChainID, nextICode, nextResSeq, modelNum) =
(*ri).get("label_comp_id", "auth_asym_id", "pdbx_PDB_ins_code", "auth_seq_id", "pdbx_PDB_model_num");
if (modelNum.empty() == false and stol(modelNum) != model_nr)
if (modelNum.empty() == false)
{
++ri;
continue;
int nr = 0;
auto r = std::from_chars(modelNum.data(), modelNum.data() + modelNum.length(), nr);
if (r.ec != std::errc())
{
if (VERBOSE > 0)
std::cerr << "Model number '" << modelNum << "' is not a valid integer" << std::endl;
}
if (nr != model_nr)
{
++ri;
continue;
}
}
if (chainID.empty() == false and terminatedChains.count(chainID) == 0)
@@ -3404,6 +3448,26 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
r.get("id", "group_PDB", "label_atom_id", "label_alt_id", "auth_comp_id", "auth_asym_id", "auth_seq_id",
"pdbx_PDB_ins_code", "Cartn_x", "Cartn_y", "Cartn_z", "occupancy", "B_iso_or_equiv", "type_symbol", "pdbx_formal_charge");
if (resName != "HOH")
{
int entity_id = r.get<int>("label_entity_id");
try
{
auto type = entity.find1<std::string>("id"_key == entity_id, "type");
if (type == "branched") // find the real auth_seq_num, since sugars have their auth_seq_num reused as sugar number... sigh.
resSeq = pdbx_branch_scheme.find1<int>("asym_id"_key == r.get<std::string>("label_asym_id") and "pdb_seq_num"_key == resSeq, "auth_seq_num");
// else if (type == "non-polymer") // same for non-polymers
// resSeq = pdbx_nonpoly_scheme.find1<int>("asym_id"_key == r.get<std::string>("label_asym_id") and "pdb_seq_num"_key == resSeq, "auth_seq_num");
// else if (type == "polymer")
// resSeq = pdbx_poly_seq_scheme.find1<int>("asym_id"_key == r.get<std::string>("label_asym_id") and "pdb_seq_num"_key == resSeq, "auth_seq_num");
}
catch (const std::exception &ex)
{
std::cerr << "Oops, there was not exactly one entity with id " << entity_id << std::endl;
}
}
if (chainID.length() > 1)
throw std::runtime_error("Chain ID " + chainID + " won't fit into a PDB file");
@@ -3418,7 +3482,7 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
++numCoord;
auto ai = atom_site_anisotrop.find1(key("id") == id);
auto ai = atom_site_anisotrop.find_first(key("id") == id);
if (not ai.empty())
//
// auto ai = find_if(atom_site_anisotrop.begin(), atom_site_anisotrop.end(), [id](row_handle r) -> bool { return r["id"] == id; });

View File

@@ -24,17 +24,16 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cif++.hpp>
#include <cif++/pdb/pdb2cif.hpp>
#include <cif++/pdb/pdb2cif_remark_3.hpp>
#include <cif++/gzio.hpp>
#include <iomanip>
#include <map>
#include <set>
#include <stack>
#include <cif++.hpp>
#include <cif++/pdb/pdb2cif.hpp>
#include <cif++/pdb/pdb2cif_remark_3.hpp>
#include <cif++/gzio.hpp>
using cif::category;
using cif::datablock;
using cif::iequals;
@@ -1583,7 +1582,7 @@ void PDBFileParser::ParseTitle()
if (not iequals(key, "MOL_ID") and mCompounds.empty())
{
if (cif::VERBOSE >= 0)
if (cif::VERBOSE > 0)
std::cerr << "Ignoring invalid COMPND record" << std::endl;
break;
}
@@ -3820,41 +3819,48 @@ void PDBFileParser::ConstructEntities()
for (std::string monID : monIds)
{
std::string authMonID, authSeqNum, authInsCode;
std::string authMonID, authSeqNum, authInsCode{'.'};
if (res.mSeen)
{
authMonID = monID;
authSeqNum = std::to_string(res.mSeqNum);
if (res.mIcode != ' ' and res.mIcode != 0)
authInsCode = std::string{ res.mIcode };
cat->emplace({
{ "asym_id", asymID },
{ "entity_id", mMolID2EntityID[chain.mMolID] },
{ "seq_id", seqID },
{ "mon_id", monID },
{ "ndb_seq_num", seqID },
{ "pdb_seq_num", res.mSeqNum },
{ "auth_seq_num", authSeqNum },
{ "pdb_mon_id", authMonID },
{ "auth_mon_id", authMonID },
{ "pdb_strand_id", std::string{ chain.mDbref.chainID } },
{ "pdb_ins_code", authInsCode },
{ "hetero", res.mAlts.empty() ? "n" : "y" } });
}
else
{
authMonID = res.mMonID;
authSeqNum = std::to_string(res.mSeqNum);
if (res.mIcode != ' ' and res.mIcode != 0)
authInsCode = std::string{ res.mIcode } + "A";
else
authInsCode = "A";
cat->emplace({
{ "asym_id", asymID },
{ "entity_id", mMolID2EntityID[chain.mMolID] },
{ "seq_id", seqID },
{ "mon_id", monID },
{ "ndb_seq_num", seqID },
{ "pdb_seq_num", res.mSeqNum },
{ "auth_seq_num", "." },
{ "pdb_mon_id", "." },
{ "auth_mon_id", "." },
{ "pdb_strand_id", std::string{ chain.mDbref.chainID } },
{ "pdb_ins_code", authInsCode },
{ "hetero", res.mAlts.empty() ? "n" : "y" } });
}
if (authInsCode.empty())
authInsCode = ".";
cat->emplace({
{ "asym_id", asymID },
{ "entity_id", mMolID2EntityID[chain.mMolID] },
{ "seq_id", seqID },
{ "mon_id", monID },
{ "ndb_seq_num", seqID },
{ "pdb_seq_num", res.mSeqNum },
{ "auth_seq_num", authSeqNum },
{ "pdb_mon_id", authMonID },
{ "auth_mon_id", authMonID },
{ "pdb_strand_id", std::string{ chain.mDbref.chainID } },
{ "pdb_ins_code", authInsCode },
{ "hetero", res.mAlts.empty() ? "n" : "y" } });
}
}
}
@@ -4196,7 +4202,8 @@ void PDBFileParser::ConstructEntities()
// done with the sugar, resume operation as before
std::map<char, std::string> waterChains;
std::map<std::tuple<std::string, std::string>, int> ndbSeqNum; // for nonpoly scheme
std::map<std::tuple<std::string, std::string>, int> ndbSeqNum; // for nonpoly scheme
std::map<std::string,int> entityAuthSeqNum; // for nonpoly scheme too
for (size_t i = 0; i < mHets.size(); ++i)
{
@@ -4325,6 +4332,7 @@ void PDBFileParser::ConstructEntities()
}
int seqNr = ++ndbSeqNum[std::make_tuple(hetID, asymID)];
int authSeqNr = ++entityAuthSeqNum[hetID];
std::string iCode{ het.iCode };
cif::trim(iCode);
@@ -4333,13 +4341,13 @@ void PDBFileParser::ConstructEntities()
getCategory("pdbx_nonpoly_scheme")->emplace({
{ "asym_id", asymID },
{ "entity_id", mHet2EntityID[hetID] },
{ "mon_id", hetID },
{ "ndb_seq_num", seqNr },
{ "pdb_seq_num", het.seqNum },
// { "auth_seq_num", het.seqNum }, // ????
{ "entity_id", mHet2EntityID[hetID] },
{ "mon_id", hetID },
{ "ndb_seq_num", seqNr },
{ "pdb_seq_num", het.seqNum },
{ "auth_seq_num", authSeqNr }, // Yes
{ "pdb_mon_id", hetID },
// { "auth_mon_id", hetID },
{ "auth_mon_id", hetID },
{ "pdb_strand_id", std::string{ het.chainID } },
{ "pdb_ins_code", iCode } });
@@ -6102,13 +6110,6 @@ int PDBFileParser::PDBChain::AlignResToSeqRes()
switch (tb(x, y))
{
case -1:
// if (cif::VERBOSE > 0)
// std::cerr << "A residue found in the ATOM records "
// << "(" << ry[y].mMonID << " @ " << mDbref.chainID << ":" << ry[y].mSeqNum
// << ((ry[y].mIcode == ' ' or ry[y].mIcode == 0) ? "" : std::string{ ry[y].mIcode }) << ")"
// << " was not found in the SEQRES records" << std::endl;
// --y;
throw std::runtime_error("A residue found in the ATOM records (" + ry[y].mMonID +
" @ " + std::string{ mDbref.chainID } + ":" + std::to_string(ry[y].mSeqNum) +
((ry[y].mIcode == ' ' or ry[y].mIcode == 0) ? "" : std::string{ ry[y].mIcode }) +
@@ -6123,10 +6124,11 @@ int PDBFileParser::PDBChain::AlignResToSeqRes()
break;
case 0:
if (cif::VERBOSE > 3 and rx[x].mMonID != ry[y].mMonID)
std::cerr << "Warning, unaligned residues at " << x << "/" << y << "(" << rx[x].mMonID << '/' << ry[y].mMonID << ')' << std::endl;
else if (cif::VERBOSE > 4)
std::cerr << rx[x].mMonID << " -> " << ry[y].mMonID << " (" << ry[y].mSeqNum << ')' << std::endl;
if (rx[x].mMonID != ry[y].mMonID)
{
std::cerr << "Warning, unaligned residues at " << x << "/" << y << "(" << rx[x].mMonID << '/' << ry[y].mMonID << ") SEQRES does not agree with ATOM records" << std::endl;
rx[x].mMonID = ry[y].mMonID;
}
rx[x].mSeqNum = ry[y].mSeqNum;
rx[x].mIcode = ry[y].mIcode;
@@ -6211,7 +6213,16 @@ file read(std::istream &is)
if (ch == 'h' or ch == 'H')
ReadPDBFile(is, result);
else
result.load(is);
{
try
{
result.load(is);
}
catch (const std::exception &ex)
{
std::throw_with_nested(std::runtime_error("Since the file did not start with a valid PDB HEADER line mmCIF was assumed, but that failed."));
}
}
}
// Must be a PDB like file, right?
@@ -6237,4 +6248,4 @@ file read(const std::filesystem::path &file)
}
}
} // namespace pdbx
} // namespace pdbx

View File

@@ -24,13 +24,12 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cif++.hpp>
#include "cif++.hpp"
#include "cif++/pdb/pdb2cif_remark_3.hpp"
#include <map>
#include <set>
#include <cif++/pdb/pdb2cif_remark_3.hpp>
namespace cif::pdb
{
@@ -1234,7 +1233,9 @@ void Remark3Parser::storeCapture(const char *category, std::initializer_list<con
{
cat.emplace({ // #warning("crystal id, diffrn id, what should be put here?")
{ "crystal_id", 1 },
{ "diffrn_id", 1 } });
{ "diffrn_id", 1 },
{ "operator", "" },
{ "fraction", 0.f } });
}
else if (iequals(category, "reflns"))
cat.emplace({ { "pdbx_ordinal", cat.size() + 1 },

View File

@@ -27,12 +27,12 @@
// #include <sys/ioctl.h>
// #include <termios.h>
#include "cif++.hpp"
#include "cif++/pdb/tls.hpp"
#include <iomanip>
#include <iostream>
#include <cif++.hpp>
#include <cif++/pdb/tls.hpp>
namespace cif
{
@@ -320,7 +320,7 @@ struct tls_selection_not : public tls_selection
for (auto &r : residues)
r.selected = not r.selected;
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
{
std::cout << std::string(indentLevel * 2, ' ') << "NOT" << std::endl;
dump_selection(residues, indentLevel);
@@ -339,7 +339,7 @@ struct tls_selection_all : public tls_selection
for (auto &r : residues)
r.selected = true;
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
{
std::cout << std::string(indentLevel * 2, ' ') << "ALL" << std::endl;
dump_selection(residues, indentLevel);
@@ -361,7 +361,7 @@ struct tls_selection_chain : public tls_selection_all
for (auto &r : residues)
r.selected = allChains or r.chainID == m_chain;
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
{
std::cout << std::string(indentLevel * 2, ' ') << "CHAIN " << m_chain << std::endl;
dump_selection(residues, indentLevel);
@@ -384,7 +384,7 @@ struct tls_selection_res_id : public tls_selection_all
for (auto &r : residues)
r.selected = r.seqNr == m_seq_nr and r.iCode == m_icode;
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
{
std::cout << std::string(indentLevel * 2, ' ') << "ResID " << m_seq_nr << (m_icode ? std::string{ m_icode } : "") << std::endl;
dump_selection(residues, indentLevel);
@@ -411,7 +411,7 @@ struct tls_selection_range_seq : public tls_selection_all
(r.seqNr <= m_last or m_last == kResidueNrWildcard));
}
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
{
std::cout << std::string(indentLevel * 2, ' ') << "Range " << m_first << ':' << m_last << std::endl;
dump_selection(residues, indentLevel);
@@ -461,7 +461,7 @@ struct tls_selection_range_id : public tls_selection_all
}
}
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
{
std::cout << std::string(indentLevel * 2, ' ') << "Through " << m_first << ':' << m_last << std::endl;
dump_selection(residues, indentLevel);
@@ -502,7 +502,7 @@ struct tls_selection_union : public tls_selection
for (auto ai = a.begin(), bi = b.begin(), ri = residues.begin(); ri != residues.end(); ++ai, ++bi, ++ri)
ri->selected = ai->selected or bi->selected;
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
{
std::cout << std::string(indentLevel * 2, ' ') << "Union" << std::endl;
dump_selection(residues, indentLevel);
@@ -543,7 +543,7 @@ struct tls_selection_intersection : public tls_selection
for (auto ai = a.begin(), bi = b.begin(), ri = residues.begin(); ri != residues.end(); ++ai, ++bi, ++ri)
ri->selected = ai->selected and bi->selected;
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
{
std::cout << std::string(indentLevel * 2, ' ') << "Intersection" << std::endl;
dump_selection(residues, indentLevel);
@@ -567,7 +567,7 @@ struct tls_selection_by_name : public tls_selection_all
for (auto &r : residues)
r.selected = r.name == m_name;
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
{
std::cout << std::string(indentLevel * 2, ' ') << "Name " << m_name << std::endl;
dump_selection(residues, indentLevel);
@@ -595,7 +595,7 @@ struct tls_selection_by_element : public tls_selection_all
for (auto &r : residues)
r.selected = iequals(r.name, m_element);
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
{
std::cout << std::string(indentLevel * 2, ' ') << "Element " << m_element << std::endl;
dump_selection(residues, indentLevel);
@@ -1404,7 +1404,7 @@ std::tuple<std::string, int> TLSSelectionParserImplBuster::ParseAtom()
match(':');
std::string atom = m_value_s;
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
std::cerr << "Warning: ignoring atom ID '" << atom << "' in TLS selection" << std::endl;
match(bt_IDENT);
@@ -1958,14 +1958,14 @@ std::unique_ptr<tls_selection> parse_tls_selection_details(const std::string &pr
if (not result)
{
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
std::cerr << "Falling back to old BUSTER" << std::endl;
result = busterOld.Parse(selection);
}
if (not result)
{
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
std::cerr << "Falling back to PHENIX" << std::endl;
result = phenix.Parse(selection);
}
@@ -1976,35 +1976,35 @@ std::unique_ptr<tls_selection> parse_tls_selection_details(const std::string &pr
if (not result)
{
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
std::cerr << "Falling back to BUSTER" << std::endl;
result = buster.Parse(selection);
}
if (not result)
{
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
std::cerr << "Falling back to old BUSTER" << std::endl;
result = busterOld.Parse(selection);
}
}
else
{
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
std::cerr << "No known program specified, trying PHENIX" << std::endl;
result = phenix.Parse(selection);
if (not result)
{
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
std::cerr << "Falling back to BUSTER" << std::endl;
result = buster.Parse(selection);
}
if (not result)
{
if (cif::VERBOSE)
if (cif::VERBOSE > 0)
std::cerr << "Falling back to old BUSTER" << std::endl;
result = busterOld.Parse(selection);
}

View File

@@ -24,253 +24,15 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++/point.hpp"
#include "cif++/matrix.hpp"
#include <cassert>
#include <random>
#include <cif++/point.hpp>
namespace cif
{
// --------------------------------------------------------------------
// We're using expression templates here
template <typename M>
class MatrixExpression
{
public:
uint32_t dim_m() const { return static_cast<const M &>(*this).dim_m(); }
uint32_t dim_n() const { return static_cast<const M &>(*this).dim_n(); }
double &operator()(uint32_t i, uint32_t j)
{
return static_cast<M &>(*this).operator()(i, j);
}
double operator()(uint32_t i, uint32_t j) const
{
return static_cast<const M &>(*this).operator()(i, j);
}
};
// --------------------------------------------------------------------
// matrix is m x n, addressing i,j is 0 <= i < m and 0 <= j < n
// element m i,j is mapped to [i * n + j] and thus storage is row major
class Matrix : public MatrixExpression<Matrix>
{
public:
template <typename M2>
Matrix(const MatrixExpression<M2> &m)
: m_m(m.dim_m())
, m_n(m.dim_n())
, m_data(m_m * m_n)
{
for (uint32_t i = 0; i < m_m; ++i)
{
for (uint32_t j = 0; j < m_n; ++j)
operator()(i, j) = m(i, j);
}
}
Matrix(size_t m, size_t n, double v = 0)
: m_m(m)
, m_n(n)
, m_data(m_m * m_n)
{
std::fill(m_data.begin(), m_data.end(), v);
}
Matrix() = default;
Matrix(Matrix &&m) = default;
Matrix(const Matrix &m) = default;
Matrix &operator=(Matrix &&m) = default;
Matrix &operator=(const Matrix &m) = default;
uint32_t dim_m() const { return m_m; }
uint32_t dim_n() const { return m_n; }
double operator()(uint32_t i, uint32_t j) const
{
assert(i < m_m);
assert(j < m_n);
return m_data[i * m_n + j];
}
double &operator()(uint32_t i, uint32_t j)
{
assert(i < m_m);
assert(j < m_n);
return m_data[i * m_n + j];
}
private:
uint32_t m_m = 0, m_n = 0;
std::vector<double> m_data;
};
// --------------------------------------------------------------------
class SymmetricMatrix : public MatrixExpression<SymmetricMatrix>
{
public:
SymmetricMatrix(uint32_t n, double v = 0)
: m_n(n)
, m_data((m_n * (m_n + 1)) / 2)
{
std::fill(m_data.begin(), m_data.end(), v);
}
SymmetricMatrix() = default;
SymmetricMatrix(SymmetricMatrix &&m) = default;
SymmetricMatrix(const SymmetricMatrix &m) = default;
SymmetricMatrix &operator=(SymmetricMatrix &&m) = default;
SymmetricMatrix &operator=(const SymmetricMatrix &m) = default;
uint32_t dim_m() const { return m_n; }
uint32_t dim_n() const { return m_n; }
double operator()(uint32_t i, uint32_t j) const
{
return i < j
? m_data[(j * (j + 1)) / 2 + i]
: m_data[(i * (i + 1)) / 2 + j];
}
double &operator()(uint32_t i, uint32_t j)
{
if (i > j)
std::swap(i, j);
assert(j < m_n);
return m_data[(j * (j + 1)) / 2 + i];
}
private:
uint32_t m_n;
std::vector<double> m_data;
};
class IdentityMatrix : public MatrixExpression<IdentityMatrix>
{
public:
IdentityMatrix(uint32_t n)
: m_n(n)
{
}
uint32_t dim_m() const { return m_n; }
uint32_t dim_n() const { return m_n; }
double operator()(uint32_t i, uint32_t j) const
{
return i == j ? 1 : 0;
}
private:
uint32_t m_n;
};
// --------------------------------------------------------------------
// matrix functions, implemented as expression templates
template <typename M1, typename M2>
class MatrixSubtraction : public MatrixExpression<MatrixSubtraction<M1, M2>>
{
public:
MatrixSubtraction(const M1 &m1, const M2 &m2)
: m_m1(m1)
, m_m2(m2)
{
assert(m_m1.dim_m() == m_m2.dim_m());
assert(m_m1.dim_n() == m_m2.dim_n());
}
uint32_t dim_m() const { return m_m1.dim_m(); }
uint32_t dim_n() const { return m_m1.dim_n(); }
double operator()(uint32_t i, uint32_t j) const
{
return m_m1(i, j) - m_m2(i, j);
}
private:
const M1 &m_m1;
const M2 &m_m2;
};
template <typename M1, typename M2>
MatrixSubtraction<M1, M2> operator-(const MatrixExpression<M1> &m1, const MatrixExpression<M2> &m2)
{
return MatrixSubtraction(*static_cast<const M1 *>(&m1), *static_cast<const M2 *>(&m2));
}
template <typename M>
class MatrixMultiplication : public MatrixExpression<MatrixMultiplication<M>>
{
public:
MatrixMultiplication(const M &m, double v)
: m_m(m)
, m_v(v)
{
}
uint32_t dim_m() const { return m_m.dim_m(); }
uint32_t dim_n() const { return m_m.dim_n(); }
double operator()(uint32_t i, uint32_t j) const
{
return m_m(i, j) * m_v;
}
private:
const M &m_m;
double m_v;
};
template <typename M>
MatrixMultiplication<M> operator*(const MatrixExpression<M> &m, double v)
{
return MatrixMultiplication(*static_cast<const M *>(&m), v);
}
// --------------------------------------------------------------------
template <class M1>
Matrix Cofactors(const M1 &m)
{
Matrix cf(m.dim_m(), m.dim_m());
const size_t ixs[4][3] = {
{ 1, 2, 3 },
{ 0, 2, 3 },
{ 0, 1, 3 },
{ 0, 1, 2 }
};
for (size_t x = 0; x < 4; ++x)
{
const size_t *ix = ixs[x];
for (size_t y = 0; y < 4; ++y)
{
const size_t *iy = ixs[y];
cf(x, y) =
m(ix[0], iy[0]) * m(ix[1], iy[1]) * m(ix[2], iy[2]) +
m(ix[0], iy[1]) * m(ix[1], iy[2]) * m(ix[2], iy[0]) +
m(ix[0], iy[2]) * m(ix[1], iy[0]) * m(ix[2], iy[1]) -
m(ix[0], iy[2]) * m(ix[1], iy[1]) * m(ix[2], iy[0]) -
m(ix[0], iy[1]) * m(ix[1], iy[0]) * m(ix[2], iy[2]) -
m(ix[0], iy[0]) * m(ix[1], iy[2]) * m(ix[2], iy[1]);
if ((x + y) % 2 == 1)
cf(x, y) *= -1;
}
}
return cf;
}
// --------------------------------------------------------------------
template<typename T>
@@ -299,13 +61,14 @@ quaternion_type<T> normalize(quaternion_type<T> q)
quaternion construct_from_angle_axis(float angle, point axis)
{
auto q = std::cos((angle * kPI / 180) / 2);
auto s = std::sqrt(1 - q * q);
angle = (angle * kPI / 180) / 2;
auto s = std::sin(angle);
auto c = std::cos(angle);
axis.normalize();
return normalize(quaternion{
static_cast<float>(q),
static_cast<float>(c),
static_cast<float>(s * axis.m_x),
static_cast<float>(s * axis.m_y),
static_cast<float>(s * axis.m_z) });
@@ -355,6 +118,21 @@ point center_points(std::vector<point> &Points)
return t;
}
quaternion construct_for_dihedral_angle(point p1, point p2, point p3, point p4,
float angle, float esd)
{
p1 -= p3;
p2 -= p3;
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);
}
point centroid(const std::vector<point> &pts)
{
point result;
@@ -427,8 +205,8 @@ double LargestDepressedQuarticSolution(double a, double b, double c)
quaternion align_points(const std::vector<point> &pa, const std::vector<point> &pb)
{
// First calculate M, a 3x3 Matrix containing the sums of products of the coordinates of A and B
Matrix M(3, 3, 0);
// First calculate M, a 3x3 matrix containing the sums of products of the coordinates of A and B
matrix3x3<double> M;
for (uint32_t i = 0; i < pa.size(); ++i)
{
@@ -446,8 +224,8 @@ quaternion align_points(const std::vector<point> &pa, const std::vector<point> &
M(2, 2) += a.m_z * b.m_z;
}
// Now calculate N, a symmetric 4x4 Matrix
SymmetricMatrix N(4);
// Now calculate N, a symmetric 4x4 matrix
symmetric_matrix4x4<double> N(4);
N(0, 0) = M(0, 0) + M(1, 1) + M(2, 2);
N(0, 1) = M(1, 2) - M(2, 1);
@@ -496,16 +274,22 @@ quaternion align_points(const std::vector<point> &pa, const std::vector<point> &
double lambda = LargestDepressedQuarticSolution(C, D, E);
// calculate t = (N - λI)
Matrix t = N - IdentityMatrix(4) * lambda;
matrix<double> t(N - identity_matrix(4) * lambda);
// calculate a Matrix of cofactors for t
Matrix cf = Cofactors(t);
// calculate a matrix of cofactors for t
auto cf = matrix_cofactors(t);
int maxR = 0;
double maxCF = std::abs(cf(0, 0));
for (int r = 1; r < 4; ++r)
{
if (std::abs(cf(r, 0)) > std::abs(cf(maxR, 0)))
auto cfr = std::abs(cf(r, 0));
if (maxCF < cfr)
{
maxCF = cfr;
maxR = r;
}
}
quaternion q(
@@ -522,15 +306,17 @@ quaternion align_points(const std::vector<point> &pa, const std::vector<point> &
point nudge(point p, float offset)
{
static const float kPI_f = static_cast<float>(kPI);
static std::random_device rd;
static std::mt19937_64 rng(rd());
std::uniform_real_distribution<float> randomAngle(0, 2 * kPI);
std::normal_distribution<> randomOffset(0, offset);
std::uniform_real_distribution<float> randomAngle(0, 2 * kPI_f);
std::normal_distribution<float> randomOffset(0, offset);
float theta = randomAngle(rng);
float phi1 = randomAngle(rng) - kPI;
float phi2 = randomAngle(rng) - kPI;
float phi1 = randomAngle(rng) - kPI_f;
float phi2 = randomAngle(rng) - kPI_f;
quaternion q = spherical(1.0f, theta, phi1, phi2);

View File

@@ -24,7 +24,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cif++/category.hpp>
#include "cif++/category.hpp"
namespace cif
{

View File

@@ -1,17 +1,17 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -24,23 +24,301 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++/symmetry.hpp"
#include "cif++/datablock.hpp"
#include "cif++/point.hpp"
#include <stdexcept>
#include <cif++/symmetry.hpp>
#include "symop_table_data.hpp"
#include "./symop_table_data.hpp"
#include <Eigen/Eigenvalues>
namespace cif
{
// --------------------------------------------------------------------
// Unfortunately, clipper has a different numbering scheme than PDB
// for rotation numbers. So we created a table to map those.
// Perhaps a bit over the top, but hey....
cell::cell(float a, float b, float c, float alpha, float beta, float gamma)
: m_a(a)
, m_b(b)
, m_c(c)
, m_alpha(alpha)
, m_beta(beta)
, m_gamma(gamma)
{
init();
}
cell::cell(const datablock &db)
{
auto &_cell = db["cell"];
tie(m_a, m_b, m_c, m_alpha, m_beta, m_gamma) =
_cell.front().get("length_a", "length_b", "length_c", "angle_alpha", "angle_beta", "angle_gamma");
init();
}
void cell::init()
{
auto alpha = (m_alpha * kPI) / 180;
auto beta = (m_beta * kPI) / 180;
auto gamma = (m_gamma * kPI) / 180;
auto alpha_star = std::acos((std::cos(gamma) * std::cos(beta) - std::cos(alpha)) / (std::sin(beta) * std::sin(gamma)));
m_orthogonal = identity_matrix(3);
m_orthogonal(0, 0) = m_a;
m_orthogonal(0, 1) = m_b * std::cos(gamma);
m_orthogonal(0, 2) = m_c * std::cos(beta);
m_orthogonal(1, 1) = m_b * std::sin(gamma);
m_orthogonal(1, 2) = -m_c * std::sin(beta) * std::cos(alpha_star);
m_orthogonal(2, 2) = m_c * std::sin(beta) * std::sin(alpha_star);
m_fractional = inverse(m_orthogonal);
}
// --------------------------------------------------------------------
int get_space_group_number(std::string spacegroup)
sym_op::sym_op(std::string_view s)
{
auto b = s.data();
auto e = b + s.length();
int rnri = 256; // default to unexisting number
auto r = std::from_chars(b, e, rnri);
m_nr = rnri;
m_ta = r.ptr[1] - '0';
m_tb = r.ptr[2] - '0';
m_tc = r.ptr[3] - '0';
if (r.ec != std::errc() or rnri > 192 or r.ptr[0] != '_' or m_ta > 9 or m_tb > 9 or m_tc > 9)
throw std::invalid_argument("Could not convert string into sym_op");
}
std::string sym_op::string() const
{
char b[9];
auto r = std::to_chars(b, b + sizeof(b), m_nr);
if (r.ec != std::errc() or r.ptr > b + 4)
throw std::runtime_error("Could not write out symmetry operation to string");
*r.ptr++ = '_';
*r.ptr++ = '0' + m_ta;
*r.ptr++ = '0' + m_tb;
*r.ptr++ = '0' + m_tc;
*r.ptr = 0;
return { b, static_cast<size_t>(r.ptr - b) };
}
// --------------------------------------------------------------------
transformation::transformation(const symop_data &data)
{
const auto &d = data.data();
m_rotation(0, 0) = d[0];
m_rotation(0, 1) = d[1];
m_rotation(0, 2) = d[2];
m_rotation(1, 0) = d[3];
m_rotation(1, 1) = d[4];
m_rotation(1, 2) = d[5];
m_rotation(2, 0) = d[6];
m_rotation(2, 1) = d[7];
m_rotation(2, 2) = d[8];
try_create_quaternion();
m_translation.m_x = d[9] == 0 ? 0 : 1.0 * d[9] / d[10];
m_translation.m_y = d[11] == 0 ? 0 : 1.0 * d[11] / d[12];
m_translation.m_z = d[13] == 0 ? 0 : 1.0 * d[13] / d[14];
}
transformation::transformation(const matrix3x3<float> &r, const cif::point &t)
: m_rotation(r)
, m_translation(t)
{
try_create_quaternion();
}
void transformation::try_create_quaternion()
{
float Qxx = m_rotation(0, 0);
float Qxy = m_rotation(0, 1);
float Qxz = m_rotation(0, 2);
float Qyx = m_rotation(1, 0);
float Qyy = m_rotation(1, 1);
float Qyz = m_rotation(1, 2);
float Qzx = m_rotation(2, 0);
float Qzy = m_rotation(2, 1);
float Qzz = m_rotation(2, 2);
Eigen::Matrix4f em;
em << Qxx - Qyy - Qzz, Qyx + Qxy, Qzx + Qxz, Qzy - Qyz,
Qyx + Qxy, Qyy - Qxx - Qzz, Qzy + Qyz, Qxz - Qzx,
Qzx + Qxz, Qzy + Qyz, Qzz - Qxx - Qyy, Qyx - Qxy,
Qzy - Qyz, Qxz - Qzx, Qyx - Qxy, Qxx + Qyy + Qzz;
Eigen::EigenSolver<Eigen::Matrix4f> es(em / 3);
auto ev = es.eigenvalues();
for (size_t j = 0; j < 4; ++j)
{
if (std::abs(ev[j].real() - 1) > 0.01)
continue;
auto col = es.eigenvectors().col(j);
m_q = normalize(cif::quaternion{
static_cast<float>(col(3).real()),
static_cast<float>(col(0).real()),
static_cast<float>(col(1).real()),
static_cast<float>(col(2).real()) });
break;
}
}
transformation operator*(const transformation &lhs, const transformation &rhs)
{
auto r = lhs.m_rotation * rhs.m_rotation;
auto t = lhs.m_rotation * rhs.m_translation;
t = t + lhs.m_translation;
return transformation(r, t);
}
transformation inverse(const transformation &t)
{
auto inv_matrix = inverse(t.m_rotation);
return { inv_matrix, -(inv_matrix * t.m_translation) };
}
// --------------------------------------------------------------------
spacegroup::spacegroup(int nr)
: m_nr(nr)
{
const size_t N = kSymopNrTableSize;
int32_t L = 0, R = static_cast<int32_t>(N - 1);
while (L <= R)
{
int32_t i = (L + R) / 2;
if (kSymopNrTable[i].spacegroup() < m_nr)
L = i + 1;
else
R = i - 1;
}
m_index = L;
for (size_t i = L; i < N and kSymopNrTable[i].spacegroup() == m_nr; ++i)
emplace_back(kSymopNrTable[i].symop().data());
}
std::string spacegroup::get_name() const
{
for (auto &s : kSpaceGroups)
{
if (s.nr == m_nr)
return s.name;
}
throw std::runtime_error("Spacegroup has an invalid number: " + std::to_string(m_nr));
}
point offsetToOrigin(const cell &c, const point &p)
{
point d{};
while (p.m_x + d.m_x < -(c.get_a()))
d.m_x += c.get_a();
while (p.m_x + d.m_x > (c.get_a()))
d.m_x -= c.get_a();
while (p.m_y + d.m_y < -(c.get_b()))
d.m_y += c.get_b();
while (p.m_y + d.m_y > (c.get_b()))
d.m_y -= c.get_b();
while (p.m_z + d.m_z < -(c.get_c()))
d.m_z += c.get_c();
while (p.m_z + d.m_z > (c.get_c()))
d.m_z -= c.get_c();
return d;
};
point offsetToOriginFractional(const point &p)
{
point d{};
while (p.m_x + d.m_x < -0.5f)
d.m_x += 1;
while (p.m_x + d.m_x > 0.5f)
d.m_x -= 1;
while (p.m_y + d.m_y < -0.5f)
d.m_y += 1;
while (p.m_y + d.m_y > 0.5f)
d.m_y -= 1;
while (p.m_z + d.m_z < -0.5f)
d.m_z += 1;
while (p.m_z + d.m_z > 0.5f)
d.m_z -= 1;
return d;
};
point spacegroup::operator()(const point &pt, const cell &c, sym_op symop) const
{
if (symop.m_nr < 1 or symop.m_nr > size())
throw std::out_of_range("symmetry operator number out of range");
transformation t = at(symop.m_nr - 1);
t.m_translation.m_x += symop.m_ta - 5;
t.m_translation.m_y += symop.m_tb - 5;
t.m_translation.m_z += symop.m_tc - 5;
auto fpt = fractional(pt, c);
auto o = offsetToOriginFractional(fpt);
auto spt = t(fpt + o) - o;
return orthogonal(spt, c);
}
point spacegroup::inverse(const point &pt, const cell &c, sym_op symop) const
{
if (symop.m_nr < 1 or symop.m_nr > size())
throw std::out_of_range("symmetry operator number out of range");
transformation t = at(symop.m_nr - 1);
t.m_translation.m_x += symop.m_ta - 5;
t.m_translation.m_y += symop.m_tb - 5;
t.m_translation.m_z += symop.m_tc - 5;
auto fpt = fractional(pt, c);
auto o = offsetToOriginFractional(fpt);
auto it = cif::inverse(t);
auto spt = it(fpt + o) - o;
return orthogonal(spt, c);
}
// --------------------------------------------------------------------
int get_space_group_number(std::string_view spacegroup)
{
if (spacegroup == "P 21 21 2 A")
spacegroup = "P 21 21 2 (a)";
@@ -73,7 +351,7 @@ int get_space_group_number(std::string spacegroup)
{
for (size_t i = 0; i < kNrOfSpaceGroups; ++i)
{
auto& sp = kSpaceGroups[i];
auto &sp = kSpaceGroups[i];
if (sp.xHM == spacegroup)
{
result = sp.nr;
@@ -83,14 +361,14 @@ int get_space_group_number(std::string spacegroup)
}
if (result == 0)
throw std::runtime_error("Spacegroup name " + spacegroup + " was not found in table");
throw std::runtime_error("Spacegroup name " + std::string(spacegroup) + " was not found in table");
return result;
}
// --------------------------------------------------------------------
int get_space_group_number(std::string spacegroup, space_group_name type)
int get_space_group_number(std::string_view spacegroup, space_group_name type)
{
if (spacegroup == "P 21 21 2 A")
spacegroup = "P 21 21 2 (a)";
@@ -145,9 +423,99 @@ int get_space_group_number(std::string spacegroup, space_group_name type)
// not found, see if we can find a match based on xHM name
if (result == 0)
throw std::runtime_error("Spacegroup name " + spacegroup + " was not found in table");
throw std::runtime_error("Spacegroup name " + std::string(spacegroup) + " was not found in table");
return result;
}
int get_space_group_number(const datablock &db)
{
auto &_symmetry = db["symmetry"];
if (_symmetry.size() != 1)
throw std::runtime_error("Could not find a unique symmetry in this mmCIF file");
return _symmetry.front().get<int>("Int_Tables_number");
}
// --------------------------------------------------------------------
std::tuple<float,point,sym_op> crystal::closest_symmetry_copy(point a, point b) const
{
if (m_cell.get_a() == 0 or m_cell.get_b() == 0 or m_cell.get_c() == 0)
throw std::runtime_error("Invalid cell, contains a dimension that is zero");
point result_fsb;
float result_d = std::numeric_limits<float>::max();
sym_op result_s;
auto fa = fractional(a, m_cell);
auto fb = fractional(b, m_cell);
auto o = offsetToOriginFractional(fa);
fa = fa + o;
fb = fb + o;
a = orthogonal(fa, m_cell);
for (size_t i = 0; i < m_spacegroup.size(); ++i)
{
sym_op s(i + 1);
auto &t = m_spacegroup[i];
auto fsb = t(fb);
while (fsb.m_x - 0.5f > fa.m_x)
{
fsb.m_x -= 1;
s.m_ta -= 1;
}
while (fsb.m_x + 0.5f < fa.m_x)
{
fsb.m_x += 1;
s.m_ta += 1;
}
while (fsb.m_y - 0.5f > fa.m_y)
{
fsb.m_y -= 1;
s.m_tb -= 1;
}
while (fsb.m_y + 0.5f < fa.m_y)
{
fsb.m_y += 1;
s.m_tb += 1;
}
while (fsb.m_z - 0.5f > fa.m_z)
{
fsb.m_z -= 1;
s.m_tc -= 1;
}
while (fsb.m_z + 0.5f < fa.m_z)
{
fsb.m_z += 1;
s.m_tc += 1;
}
auto p = orthogonal(fsb, m_cell);
auto dsq = distance_squared(a, p);
if (result_d > dsq)
{
result_d = dsq;
result_fsb = fsb;
result_s = s;
}
}
auto p = orthogonal(result_fsb - o, m_cell);
return { std::sqrt(result_d), p, result_s };
}
} // namespace cif

View File

@@ -27,6 +27,7 @@
#include <cassert>
#include <array>
#include <charconv>
#include <iostream>
#include <iomanip>
#include <fstream>
@@ -169,7 +170,7 @@ class SymopParser
}
Token m_lookahead;
int m_nr;
int m_nr = -1;
std::string m_s;
std::string::const_iterator m_p, m_e;
@@ -230,14 +231,15 @@ int main(int argc, char* const argv[])
try
{
if (argc != 3)
if (argc != 4)
{
std::cerr << "Usage symop-map-generator <input-file> <output-file>" << std::endl;
std::cerr << "Usage symop-map-generator <syminfo.lib-file> <symop.lib-file> < <output-file>" << std::endl;
exit(1);
}
fs::path input(argv[1]);
fs::path output(argv[2]);
fs::path syminfolib(argv[1]);
fs::path symoplib(argv[2]);
fs::path output(argv[3]);
tmpFile = output.parent_path() / (output.filename().string() + ".tmp");
@@ -261,22 +263,51 @@ int main(int argc, char* const argv[])
};
std::map<int,SymInfoBlock> symInfo;
int symopnr, mysymnr = 10000;
std::ifstream file(input);
std::ifstream file(symoplib);
if (not file.is_open())
throw std::runtime_error("Could not open symop.lib file");
std::string line;
int sgnr = 0;
int rnr = 0;
while (getline(file, line))
{
if (line.empty())
continue;
if (std::isdigit(line[0])) // start of new spacegroup
{
auto r = std::from_chars(line.data(), line.data() + line.length(), sgnr);
if (r.ec != std::errc())
throw std::runtime_error("Error parsing symop.lib file");
rnr = 1;
continue;
}
if (not std::isspace(line[0]) or sgnr == 0)
throw std::runtime_error("Error parsing symop.lib file");
SymopParser p;
data.emplace_back(sgnr, rnr, p.parse(line));
++rnr;
}
file.close();
file.open(syminfolib);
if (not file.is_open())
throw std::runtime_error("Could not open syminfo.lib file");
enum class State { skip, spacegroup } state = State::skip;
std::string line;
const std::regex rx(R"(^symbol +(Hall|xHM|old) +'(.+?)'(?: +'(.+?)')?$)"),
rx2(R"(symbol ccp4 (\d+))");;
SymInfoBlock cur = {};
std::vector<std::array<int,15>> symops, cenops;
// std::vector<std::array<int,15>> symops, cenops;
while (getline(file, line))
{
@@ -286,9 +317,7 @@ int main(int argc, char* const argv[])
if (line == "begin_spacegroup")
{
state = State::spacegroup;
symopnr = 1;
++mysymnr;
cur = { mysymnr };
cur = {};
}
break;
@@ -314,34 +343,34 @@ int main(int argc, char* const argv[])
if (nr != 0)
cur.nr = nr;
}
else if (line.compare(0, 6, "symop ") == 0)
{
SymopParser p;
symops.emplace_back(p.parse(line.substr(6)));
}
else if (line.compare(0, 6, "cenop ") == 0)
{
SymopParser p;
cenops.emplace_back(p.parse(line.substr(6)));
}
// else if (line.compare(0, 6, "symop ") == 0)
// {
// SymopParser p;
// symops.emplace_back(p.parse(line.substr(6)));
// }
// else if (line.compare(0, 6, "cenop ") == 0)
// {
// SymopParser p;
// cenops.emplace_back(p.parse(line.substr(6)));
// }
else if (line == "end_spacegroup")
{
for (auto& cenop: cenops)
{
for (auto symop: symops)
{
symop = move_symop(symop, cenop);
// for (auto& cenop: cenops)
// {
// for (auto symop: symops)
// {
// symop = move_symop(symop, cenop);
data.emplace_back(cur.nr, symopnr, symop);
++symopnr;
}
}
// data.emplace_back(cur.nr, symopnr, symop);
// ++symopnr;
// }
// }
symInfo.emplace(cur.nr, cur);
state = State::skip;
symops.clear();
cenops.clear();
// symops.clear();
// cenops.clear();
}
break;
}
@@ -358,7 +387,7 @@ int main(int argc, char* const argv[])
// and $CLIBD/syminfo.lib using symop-map-generator,
// part of the PDB-REDO suite of programs.
#include <cif++/symmetry.hpp>
#include "cif++/symmetry.hpp"
namespace cif
{
@@ -383,10 +412,10 @@ const space_group kSpaceGroups[] =
old = '"' + old + '"' + std::string(20 - old.length(), ' ');
xHM = '"' + xHM + '"' + std::string(30 - xHM.length(), ' ');
for (std::string::size_type p = Hall.length(); p > 0; --p)
for (auto p = Hall.begin(); p != Hall.end(); ++p)
{
if (Hall[p - 1] == '"')
Hall.insert(p - 1, "\\", 1);
if (*p == '"')
p = Hall.insert(p, '\\') + 1;
}
Hall = '"' + Hall + '"' + std::string(40 - Hall.length(), ' ');

File diff suppressed because it is too large Load Diff

View File

@@ -24,11 +24,11 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++/text.hpp"
#include <algorithm>
#include <cassert>
#include <cif++/text.hpp>
namespace cif
{
@@ -236,28 +236,19 @@ std::string cif_id_for_number(int number)
{
std::string result;
if (number >= 26 * 26 * 26)
result = 'L' + std::to_string(number);
else
do
{
if (number >= 26 * 26)
{
int v = number / (26 * 26);
result += char('A' - 1 + v);
number %= (26 * 26);
}
int r = number % 26;
result += 'A' + r;
if (number >= 26)
{
int v = number / 26;
result += char('A' - 1 + v);
number %= 26;
}
result += char('A' + number);
number = (number - r) / 26 - 1;
}
while (number >= 0);
std::reverse(result.begin(), result.end());
assert(not result.empty());
return result;
}

View File

@@ -24,9 +24,14 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++/utilities.hpp"
#include "revision.hpp"
#include <atomic>
#include <cassert>
#include <cmath>
#include <condition_variable>
#include <cstring>
#include <deque>
#include <fstream>
@@ -35,7 +40,6 @@
#include <iostream>
#include <map>
#include <mutex>
#include <regex>
#include <sstream>
#include <thread>
@@ -44,10 +48,6 @@
#include <termios.h>
#endif
#include <cif++/utilities.hpp>
#include "revision.hpp"
namespace fs = std::filesystem;
// --------------------------------------------------------------------
@@ -124,7 +124,8 @@ std::string get_executable_path()
{
using namespace std::literals;
char path[PATH_MAX] = "";
// This used to be PATH_MAX, but lets simply assume 1024 is enough...
char path[1024] = "";
if (readlink("/proc/self/exe", path, sizeof(path)) == -1)
throw std::runtime_error("could not get exe path "s + strerror(errno));
return {path};
@@ -134,62 +135,81 @@ std::string get_executable_path()
// --------------------------------------------------------------------
struct ProgressImpl
struct progress_bar_impl
{
ProgressImpl(int64_t inMax, const std::string &inAction)
: mMax(inMax)
, mConsumed(0)
, mAction(inAction)
, mMessage(inAction)
, mThread(std::bind(&ProgressImpl::Run, this))
progress_bar_impl(int64_t inMax, const std::string &inAction)
: m_max_value(inMax)
, m_consumed(0)
, m_action(inAction)
, m_message(inAction)
, m_thread(std::bind(&progress_bar_impl::run, this))
{
}
void Run();
void Stop()
{
mStop = true;
if (mThread.joinable())
mThread.join();
}
progress_bar_impl(const progress_bar_impl&) = delete;
progress_bar_impl &operator=(const progress_bar_impl &) = delete;
void PrintProgress();
void PrintDone();
~progress_bar_impl();
int64_t mMax;
std::atomic<int64_t> mConsumed;
int64_t mLastConsumed = 0;
int mSpinnerIndex = 0;
std::string mAction, mMessage;
std::mutex mMutex;
std::thread mThread;
std::chrono::time_point<std::chrono::system_clock>
mStart = std::chrono::system_clock::now();
bool mStop = false;
void run();
void consumed(int64_t n);
void progress(int64_t p);
void message(const std::string &msg);
void print_progress();
void print_done();
using time_point = std::chrono::time_point<std::chrono::system_clock>;
int64_t m_max_value;
std::atomic<int64_t> m_consumed;
int64_t m_last_consumed = 0;
int m_spinner_index = 0;
std::string m_action, m_message;
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;
};
void ProgressImpl::Run()
progress_bar_impl::~progress_bar_impl()
{
using namespace std::literals;
assert(m_thread.joinable());
m_stop = true;
m_thread.join();
}
void progress_bar_impl::run()
{
using namespace std::literals;
bool printedAny = false;
try
{
for (;;)
while (not m_stop)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
auto now = std::chrono::system_clock::now();
std::unique_lock lock(mMutex);
if (mStop or mConsumed == mMax)
break;
auto elapsed = std::chrono::system_clock::now() - mStart;
if (elapsed < std::chrono::seconds(5))
if (now - m_start < 2s or now - m_last < 100ms)
{
std::this_thread::sleep_for(10ms);
continue;
}
std::lock_guard lock(m_mutex);
if (not printedAny and isatty(STDOUT_FILENO))
std::cout << "\e[?25l";
print_progress();
PrintProgress();
printedAny = true;
m_last = std::chrono::system_clock::now();
}
}
catch (...)
@@ -197,93 +217,93 @@ void ProgressImpl::Run()
}
if (printedAny)
PrintDone();
{
print_done();
if (isatty(STDOUT_FILENO))
std::cout << "\e[?25h";
}
}
void ProgressImpl::PrintProgress()
void progress_bar_impl::consumed(int64_t n)
{
// const char* kBlocks[] = {
// " ", // 0
// u8"\u258F", // 1
// u8"\u258E", // 2
// u8"\u258D", // 3
// u8"\u258C", // 4
// u8"\u258B", // 5
// u8"\u258A", // 6
// u8"\u2589", // 7
// u8"\u2588", // 8
// };
m_consumed += n;
}
void progress_bar_impl::progress(int64_t p)
{
m_consumed = p;
}
void progress_bar_impl::message(const std::string &msg)
{
std::unique_lock lock(m_mutex);
m_message = msg;
}
const char* kSpinner[] = {
// "▉", "▊", "▋", "▌", "▍", "▎", "▏", "▎", "▍", "▌", "▋", "▊", "▉"
".", "o", "O", "0", "O", "o", ".", " "
};
const size_t kSpinnerCount = sizeof(kSpinner) / sizeof(char*);
const uint32_t kMinBarWidth = 40, kMinMsgWidth = 12;
void progress_bar_impl::print_progress()
{
const char *kBlocks[] = {
" ", // 0
" ", // 1
" ", // 2
"-", // 3
"-", // 4
"-", // 5
"=", // 6
"=", // 7
"=", // 8
// "▯", // 0
// "▮", // 1
"=",
"-"
};
uint32_t width = get_terminal_width();
std::string msg;
msg.reserve(width + 1);
if (mMessage.length() <= 20)
{
msg = mMessage;
if (msg.length() < 20)
msg.append(20 - msg.length(), ' ');
}
float progress = static_cast<float>(m_consumed) / m_max_value;
if (width < kMinBarWidth)
std::cout << (100 * progress) << '%' << std::endl;
else
msg = mMessage.substr(0, 17) + "...";
msg += " |";
int64_t consumed = mConsumed;
float progress = static_cast<float>(consumed) / mMax;
int pi = static_cast<int>(std::ceil(progress * 33 * 8));
// int tw = width - 28;
// int twd = static_cast<int>(tw * progress + 0.5f);
// msg.append(twd, '=');
// msg.append(tw - twd, ' ');
for (int i = 0; i < 33; ++i)
{
if (pi <= 0)
msg += kBlocks[0];
else if (pi >= 8)
msg += kBlocks[8];
uint32_t bar_width = 7 * width / 10;
uint32_t pct_width = 7;
uint32_t msg_width = width - bar_width - pct_width - 1;
if (msg_width < kMinMsgWidth)
{
bar_width += kMinMsgWidth - msg_width;
msg_width = kMinMsgWidth;
}
std::ostringstream msg;
if (m_message.length() <= msg_width)
{
msg << m_message;
if (m_message.length() < msg_width)
msg << std::string(msg_width - m_message.length(), ' ');
}
else
msg += kBlocks[pi];
pi -= 8;
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() / 200) % kSpinnerCount;
msg << kSpinner[m_spinner_index];
std::cout << '\r' << msg.str();
std::cout.flush();
}
msg.append("| ");
const char kSpinner[] = {' ', '.', 'o', 'O', '0', 'O', 'o', '.'};
const size_t kSpinnerCount = sizeof(kSpinner);
if (mLastConsumed < consumed)
{
mLastConsumed = consumed;
mSpinnerIndex = (mSpinnerIndex + 1) % kSpinnerCount;
}
const char spinner[2] = {kSpinner[mSpinnerIndex], 0};
msg.append(spinner);
// int perc = static_cast<int>(100 * progress);
// if (perc < 100)
// msg += ' ';
// if (perc < 10)
// msg += ' ';
// msg += to_string(perc);
// msg += '%';
std::cout << '\r' << msg;
std::cout.flush();
}
namespace
@@ -322,12 +342,12 @@ namespace
} // namespace
void ProgressImpl::PrintDone()
void progress_bar_impl::print_done()
{
std::chrono::duration<double> elapsed = std::chrono::system_clock::now() - mStart;
std::chrono::duration<double> elapsed = std::chrono::system_clock::now() - m_start;
std::ostringstream msgstr;
msgstr << mAction << " done in " << elapsed << " seconds";
msgstr << m_action << " done in " << elapsed << " seconds";
auto msg = msgstr.str();
uint32_t width = get_terminal_width();
@@ -338,46 +358,34 @@ void ProgressImpl::PrintDone()
std::cout << '\r' << msg << std::endl;
}
Progress::Progress(int64_t inMax, const std::string &inAction)
progress_bar::progress_bar(int64_t inMax, const std::string &inAction)
: m_impl(nullptr)
{
if (isatty(STDOUT_FILENO) and VERBOSE >= 0)
m_impl = new ProgressImpl(inMax, inAction);
m_impl = new progress_bar_impl(inMax, inAction);
}
Progress::~Progress()
progress_bar::~progress_bar()
{
if (m_impl != nullptr)
m_impl->Stop();
delete m_impl;
}
void Progress::consumed(int64_t inConsumed)
{
if (m_impl != nullptr and
(m_impl->mConsumed += inConsumed) >= m_impl->mMax)
{
m_impl->Stop();
}
}
void Progress::progress(int64_t inProgress)
{
if (m_impl != nullptr and
(m_impl->mConsumed = inProgress) >= m_impl->mMax)
{
m_impl->Stop();
}
}
void Progress::message(const std::string &inMessage)
void progress_bar::consumed(int64_t inConsumed)
{
if (m_impl != nullptr)
{
std::unique_lock lock(m_impl->mMutex);
m_impl->mMessage = inMessage;
}
m_impl->consumed(inConsumed);
}
void progress_bar::progress(int64_t inProgress)
{
if (m_impl != nullptr)
m_impl->progress(inProgress);
}
void progress_bar::message(const std::string &inMessage)
{
if (m_impl != nullptr)
m_impl->message(inMessage);
}
} // namespace cif
@@ -405,9 +413,9 @@ struct rsrc_imp
#if _MSC_VER
extern "C" const mrsrc::rsrc_imp *gResourceIndexDefault[1] = {};
extern "C" const char *gResourceDataDefault[1] = {};
extern "C" const char *gResourceNameDefault[1] = {};
extern "C" CIFPP_EXPORT const mrsrc::rsrc_imp *gResourceIndexDefault[1] = {};
extern "C" CIFPP_EXPORT const char *gResourceDataDefault[1] = {};
extern "C" CIFPP_EXPORT const char *gResourceNameDefault[1] = {};
extern "C" const mrsrc::rsrc_imp gResourceIndex[];
extern "C" const char gResourceData[];
@@ -819,12 +827,12 @@ namespace cif
// --------------------------------------------------------------------
class ResourcePool
class resource_pool
{
public:
static ResourcePool &instance()
static resource_pool &instance()
{
static std::unique_ptr<ResourcePool> s_instance(new ResourcePool);
static std::unique_ptr<resource_pool> s_instance(new resource_pool);
return *s_instance;
}
@@ -854,7 +862,7 @@ class ResourcePool
std::unique_ptr<std::istream> load(fs::path name);
private:
ResourcePool();
resource_pool();
std::unique_ptr<std::ifstream> open(fs::path &p)
{
@@ -880,7 +888,7 @@ class ResourcePool
std::deque<fs::path> mDirs;
};
ResourcePool::ResourcePool()
resource_pool::resource_pool()
{
#if defined(DATA_DIR)
pushDir(DATA_DIR);
@@ -897,7 +905,7 @@ ResourcePool::ResourcePool()
#endif
}
std::unique_ptr<std::istream> ResourcePool::load(fs::path name)
std::unique_ptr<std::istream> resource_pool::load(fs::path name)
{
std::unique_ptr<std::istream> result;
std::error_code ec;
@@ -907,6 +915,9 @@ std::unique_ptr<std::istream> ResourcePool::load(fs::path name)
if (mLocalResources.count(name.string()))
result = open(mLocalResources[name.string()]);
if (fs::exists(p, ec) and not ec)
result = open(p);
for (auto di = mDirs.begin(); not result and di != mDirs.end(); ++di)
{
auto p2 = *di / p;
@@ -929,17 +940,17 @@ std::unique_ptr<std::istream> ResourcePool::load(fs::path name)
void add_data_directory(std::filesystem::path dataDir)
{
ResourcePool::instance().pushDir(dataDir);
resource_pool::instance().pushDir(dataDir);
}
void add_file_resource(const std::string &name, std::filesystem::path dataFile)
{
ResourcePool::instance().pushAlias(name, dataFile);
resource_pool::instance().pushAlias(name, dataFile);
}
std::unique_ptr<std::istream> load_resource(std::filesystem::path name)
{
return ResourcePool::instance().load(name);
return resource_pool::instance().load(name);
}
} // namespace cif

View File

@@ -24,6 +24,11 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++/validate.hpp"
#include "cif++/dictionary_parser.hpp"
#include "cif++/gzio.hpp"
#include "cif++/utilities.hpp"
#include <cassert>
#include <fstream>
#include <iostream>
@@ -41,12 +46,6 @@ using boost::regex;
using std::regex;
#endif
#include <cif++/dictionary_parser.hpp>
#include <cif++/validate.hpp>
#include <cif++/utilities.hpp>
#include <cif++/gzio.hpp>
namespace cif
{
@@ -402,92 +401,99 @@ void validator::report_error(const std::string &msg, bool fatal) const
const validator &validator_factory::operator[](std::string_view dictionary_name)
{
std::lock_guard lock(m_mutex);
for (auto &validator : m_validators)
try
{
if (iequals(validator.name(), dictionary_name))
return validator;
}
// not found, try to see if it helps if we tweak the name a little
// too bad clang version 10 did not have a constructor for std::filesystem::path that accepts a std::string_view
std::filesystem::path dictionary(dictionary_name.data(), dictionary_name.data() + dictionary_name.length());
if (dictionary.extension() != ".dic")
{
auto dict_name = dictionary.filename().string() + ".dic";
std::lock_guard lock(m_mutex);
for (auto &validator : m_validators)
{
if (iequals(validator.name(), dict_name))
if (iequals(validator.name(), dictionary_name))
return validator;
}
}
// not found, add it
// not found, try to see if it helps if we tweak the name a little
// too bad clang version 10 did not have a constructor for std::filesystem::path that accepts a std::string_view
std::filesystem::path dictionary(dictionary_name.data(), dictionary_name.data() + dictionary_name.length());
auto data = load_resource(dictionary_name);
if (not data and dictionary.extension().string() != ".dic")
data = load_resource(dictionary.parent_path() / (dictionary.filename().string() + ".dic"));
if (data)
construct_validator(dictionary_name, *data);
else
{
std::error_code ec;
// might be a compressed dictionary on disk
std::filesystem::path p = dictionary;
if (p.extension() == ".dic")
p = p.parent_path() / (p.filename().string() + ".gz");
else
p = p.parent_path() / (p.filename().string() + ".dic.gz");
#if defined(CACHE_DIR) or defined(DATA_DIR)
if (not std::filesystem::exists(p, ec) or ec)
if (dictionary.extension() != ".dic")
{
for (const char *dir : {
#if defined(CACHE_DIR)
CACHE_DIR,
#endif
#if defined(DATA_DIR)
DATA_DIR
#endif
})
auto dict_name = dictionary.filename().string() + ".dic";
for (auto &validator : m_validators)
{
auto p2 = std::filesystem::path(dir) / p;
if (std::filesystem::exists(p2, ec) and not ec)
{
swap(p, p2);
break;
}
if (iequals(validator.name(), dict_name))
return validator;
}
}
// not found, add it
auto data = load_resource(dictionary_name);
if (not data and dictionary.extension().string() != ".dic")
data = load_resource(dictionary.parent_path() / (dictionary.filename().string() + ".dic"));
if (data)
construct_validator(dictionary_name, *data);
else
{
std::error_code ec;
// might be a compressed dictionary on disk
std::filesystem::path p = dictionary;
if (p.extension() == ".dic")
p = p.parent_path() / (p.filename().string() + ".gz");
else
p = p.parent_path() / (p.filename().string() + ".dic.gz");
#if defined(CACHE_DIR) or defined(DATA_DIR)
if (not std::filesystem::exists(p, ec) or ec)
{
for (const char *dir : {
#if defined(CACHE_DIR)
CACHE_DIR,
#endif
#if defined(DATA_DIR)
DATA_DIR
#endif
})
{
auto p2 = std::filesystem::path(dir) / p;
if (std::filesystem::exists(p2, ec) and not ec)
{
swap(p, p2);
break;
}
}
}
#endif
if (std::filesystem::exists(p, ec) and not ec)
{
gzio::ifstream in(p);
if (std::filesystem::exists(p, ec) and not ec)
{
gzio::ifstream in(p);
if (not in.is_open())
throw std::runtime_error("Could not open dictionary (" + p.string() + ")");
if (not in.is_open())
throw std::runtime_error("Could not open dictionary (" + p.string() + ")");
construct_validator(dictionary_name, in);
construct_validator(dictionary_name, in);
}
else
throw std::runtime_error("Dictionary not found or defined (" + dictionary.string() + ")");
}
else
throw std::runtime_error("Dictionary not found or defined (" + dictionary.string() + ")");
}
return m_validators.back();
return m_validators.back();
}
catch (const std::exception &ex)
{
std::string msg = "Error while loading dictionary ";
msg += dictionary_name;
std::throw_with_nested(std::runtime_error(msg));
}
}
void validator_factory::construct_validator(std::string_view name, std::istream &is)
const validator &validator_factory::construct_validator(std::string_view name, std::istream &is)
{
m_validators.emplace_back(parse_dictionary(name, is));
return m_validators.emplace_back(parse_dictionary(name, is));
}
} // namespace cif

BIN
test/2bi3.cif.gz Normal file

Binary file not shown.

BIN
test/3bwh.cif.gz Normal file

Binary file not shown.

BIN
test/4wvp.cif.gz Normal file

Binary file not shown.

39
test/io-test.cpp Normal file
View File

@@ -0,0 +1,39 @@
#include <cif++.hpp>
class dummy_parser : public cif::sac_parser
{
public:
dummy_parser(std::istream &is)
: sac_parser(is)
{
}
void produce_datablock(std::string_view name) override
{
}
void produce_category(std::string_view name) override
{
}
void produce_row() override
{
}
void produce_item(std::string_view category, std::string_view item, std::string_view value) override
{
}
};
int main()
{
cif::gzio::ifstream in("/srv/data/pdb/mmCIF/gl/8glv.cif.gz");
dummy_parser parser(in);
parser.parse_file();
// cif::file f("/srv/data/pdb/mmCIF/gl/8glv.cif.gz");
return 0;
}

View File

@@ -58,6 +58,12 @@ bool init_unit_test()
// not a test, just initialize test dir
if (boost::unit_test::framework::master_test_suite().argc == 2)
gTestDir = boost::unit_test::framework::master_test_suite().argv[1];
else
{
while (not gTestDir.empty() and not std::filesystem::exists(gTestDir / "test"))
gTestDir = gTestDir.parent_path();
gTestDir /= "test";
}
// do this now, avoids the need for installing
cif::add_file_resource("mmcif_pdbx.dic", gTestDir / ".." / "rsrc" / "mmcif_pdbx.dic");
@@ -194,6 +200,123 @@ _atom_type.symbol C
}
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(create_nonpoly_2)
{
cif::VERBOSE = 1;
cif::file file;
file.load_dictionary("mmcif_pdbx.dic");
file.emplace("TEST"); // create a datablock
cif::mm::structure structure(file);
cif::file lig(gTestDir / "HEM.cif");
auto &chem_comp_atom = lig["HEM"]["chem_comp_atom"];
std::vector<cif::row_initializer> atoms;
for (const auto &[type_symbol, label_atom_id, Cartn_x, Cartn_y, Cartn_z] :
chem_comp_atom.rows<std::string,std::string,float,float,float>(
"type_symbol", "atom_id", "model_Cartn_x", "model_Cartn_y", "model_Cartn_z"))
{
atoms.emplace_back(cif::row_initializer{
{ "type_symbol", type_symbol },
{ "label_atom_id", label_atom_id },
{ "auth_atom_id", label_atom_id },
{ "Cartn_x", Cartn_x },
{ "Cartn_y", Cartn_y },
{ "Cartn_z", Cartn_z }
});
if (atoms.size() == 4)
break;
}
std::string entity_id = structure.create_non_poly_entity("HEM");
structure.create_non_poly(entity_id, atoms);
auto expected = R"(
data_TEST
#
_pdbx_nonpoly_scheme.asym_id A
_pdbx_nonpoly_scheme.ndb_seq_num 1
_pdbx_nonpoly_scheme.entity_id 1
_pdbx_nonpoly_scheme.mon_id HEM
_pdbx_nonpoly_scheme.pdb_seq_num 1
_pdbx_nonpoly_scheme.auth_seq_num 1
_pdbx_nonpoly_scheme.pdb_mon_id HEM
_pdbx_nonpoly_scheme.auth_mon_id HEM
_pdbx_nonpoly_scheme.pdb_strand_id A
_pdbx_nonpoly_scheme.pdb_ins_code .
#
loop_
_atom_site.id
_atom_site.auth_asym_id
_atom_site.label_alt_id
_atom_site.label_asym_id
_atom_site.label_atom_id
_atom_site.label_comp_id
_atom_site.label_entity_id
_atom_site.label_seq_id
_atom_site.type_symbol
_atom_site.group_PDB
_atom_site.pdbx_PDB_ins_code
_atom_site.Cartn_x
_atom_site.Cartn_y
_atom_site.Cartn_z
_atom_site.occupancy
_atom_site.pdbx_formal_charge
_atom_site.auth_seq_id
_atom_site.auth_comp_id
_atom_site.auth_atom_id
_atom_site.pdbx_PDB_model_num
1 A ? A CHA HEM 1 . C HETATM ? 2.748 -19.531 39.896 1.00 ? 1 HEM CHA 1
2 A ? A CHB HEM 1 . C HETATM ? 3.258 -17.744 35.477 1.00 ? 1 HEM CHB 1
3 A ? A CHC HEM 1 . C HETATM ? 1.703 -21.9 33.637 1.00 ? 1 HEM CHC 1
4 A ? A CHD HEM 1 . C HETATM ? 1.149 -23.677 38.059 1.00 ? 1 HEM CHD 1
#
_chem_comp.id HEM
_chem_comp.type NON-POLYMER
_chem_comp.name 'PROTOPORPHYRIN IX CONTAINING FE'
_chem_comp.formula 'C34 H32 Fe N4 O4'
_chem_comp.formula_weight 616.487000
#
_pdbx_entity_nonpoly.entity_id 1
_pdbx_entity_nonpoly.name 'PROTOPORPHYRIN IX CONTAINING FE'
_pdbx_entity_nonpoly.comp_id HEM
#
_entity.id 1
_entity.type non-polymer
_entity.pdbx_description 'PROTOPORPHYRIN IX CONTAINING FE'
_entity.formula_weight 616.487000
#
_struct_asym.id A
_struct_asym.entity_id 1
_struct_asym.pdbx_blank_PDB_chainid_flag N
_struct_asym.pdbx_modified N
_struct_asym.details ?
#
_atom_type.symbol C
)"_cf;
expected.load_dictionary("mmcif_pdbx.dic");
if (not (expected.front() == structure.get_datablock()))
{
BOOST_TEST(false);
std::cout << expected.front() << std::endl
<< std::endl
<< structure.get_datablock() << std::endl;
expected.save("/tmp/a");
file.save("/tmp/b");
}
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(test_atom_id)

View File

@@ -39,6 +39,12 @@ int main(int argc, char* argv[])
if (argc == 3)
testdir = argv[2];
else
{
while (not testdir.empty() and not std::filesystem::exists(testdir / "test"))
testdir = testdir.parent_path();
testdir /= "test";
}
if (std::filesystem::exists(testdir / ".." / "data" / "ccd-subset.cif"))
cif::add_file_resource("components.cif", testdir / ".." / "data" / "ccd-subset.cif");

49
test/spinner-test.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include <cif++/utilities.hpp>
#include <random>
#include <thread>
void test_one()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distrib(100, 1000);
cif::progress_bar pb(10, "test");
for (int i = 0; i < 10; ++i)
{
std::this_thread::sleep_for(std::chrono::milliseconds(distrib(gen)));
pb.message("step " + std::to_string(i));
pb.consumed(1);
}
}
void test_two()
{
cif::progress_bar pb(10, "test");
for (int i = 0; i < 5; ++i)
pb.consumed(1);
}
void test_three()
{
using namespace std::literals;
cif::progress_bar pb(10, "test");
pb.consumed(10);
std::this_thread::sleep_for(100ms);
}
int main()
{
test_one();
test_two();
test_three();
return 0;
}

View File

@@ -96,87 +96,87 @@ BOOST_AUTO_TEST_CASE(sugar_name_1)
}
}
// --------------------------------------------------------------------
// // --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(create_sugar_1)
{
using namespace cif::literals;
// BOOST_AUTO_TEST_CASE(create_sugar_1)
// {
// using namespace cif::literals;
const std::filesystem::path example(gTestDir / "1juh.cif.gz");
cif::file file(example.string());
cif::mm::structure s(file);
// const std::filesystem::path example(gTestDir / "1juh.cif.gz");
// cif::file file(example.string());
// cif::mm::structure s(file);
// collect atoms from asym L first
auto &NAG = s.get_residue("L");
auto nagAtoms = NAG.atoms();
// // collect atoms from asym L first
// auto &NAG = s.get_residue("L");
// auto nagAtoms = NAG.atoms();
std::vector<cif::row_initializer> ai;
// std::vector<cif::row_initializer> ai;
auto &db = s.get_datablock();
auto &as = db["atom_site"];
// auto &db = s.get_datablock();
// auto &as = db["atom_site"];
// NOTE, row_initializer does not actually hold the data, so copy it first
// before it gets destroyed by remove_residue
// // NOTE, row_initializer does not actually hold the data, so copy it first
// // before it gets destroyed by remove_residue
for (auto r : as.find("label_asym_id"_key == "L"))
/*auto &ri = */ai.emplace_back(r);
// for (auto r : as.find("label_asym_id"_key == "L"))
// /*auto &ri = */ai.emplace_back(r);
s.remove_residue(NAG);
// s.remove_residue(NAG);
auto &branch = s.create_branch(ai);
// auto &branch = s.create_branch(ai);
BOOST_CHECK_EQUAL(branch.name(), "2-acetamido-2-deoxy-beta-D-glucopyranose");
BOOST_CHECK_EQUAL(branch.size(), 1);
// BOOST_CHECK_EQUAL(branch.name(), "2-acetamido-2-deoxy-beta-D-glucopyranose");
// BOOST_CHECK_EQUAL(branch.size(), 1);
BOOST_CHECK_EQUAL(branch[0].atoms().size(), nagAtoms.size());
// BOOST_CHECK_EQUAL(branch[0].atoms().size(), nagAtoms.size());
BOOST_CHECK(file.is_valid());
// BOOST_CHECK(file.is_valid());
file.save(gTestDir / "test-create_sugar_1.cif");
}
// file.save(gTestDir / "test-create_sugar_1.cif");
// }
// --------------------------------------------------------------------
// // --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(create_sugar_2)
{
using namespace cif::literals;
// BOOST_AUTO_TEST_CASE(create_sugar_2)
// {
// using namespace cif::literals;
const std::filesystem::path example(gTestDir / "1juh.cif.gz");
cif::file file(example.string());
cif::mm::structure s(file);
// const std::filesystem::path example(gTestDir / "1juh.cif.gz");
// cif::file file(example.string());
// cif::mm::structure s(file);
// Get branch for H
auto &bH = s.get_branch_by_asym_id("H");
// // Get branch for H
// auto &bH = s.get_branch_by_asym_id("H");
BOOST_CHECK_EQUAL(bH.size(), 2);
// BOOST_CHECK_EQUAL(bH.size(), 2);
std::vector<cif::row_initializer> ai[2];
// std::vector<cif::row_initializer> ai[2];
auto &db = s.get_datablock();
auto &as = db["atom_site"];
// auto &db = s.get_datablock();
// auto &as = db["atom_site"];
for (size_t i = 0; i < 2; ++i)
{
for (auto r : as.find("label_asym_id"_key == "H" and "auth_seq_id"_key == i + 1))
/*auto &ri = */ai[i].emplace_back(r);
}
// for (size_t i = 0; i < 2; ++i)
// {
// for (auto r : as.find("label_asym_id"_key == "H" and "auth_seq_id"_key == i + 1))
// /*auto &ri = */ai[i].emplace_back(r);
// }
s.remove_branch(bH);
// s.remove_branch(bH);
BOOST_CHECK(file.is_valid());
// BOOST_CHECK(file.is_valid());
auto &bN = s.create_branch(ai[0]);
s.extend_branch(bN.get_asym_id(), ai[1], 1, "O4");
// auto &bN = s.create_branch(ai[0]);
// s.extend_branch(bN.get_asym_id(), ai[1], 1, "O4");
BOOST_CHECK_EQUAL(bN.name(), "2-acetamido-2-deoxy-beta-D-glucopyranose-(1-4)-2-acetamido-2-deoxy-beta-D-glucopyranose");
BOOST_CHECK_EQUAL(bN.size(), 2);
// BOOST_CHECK_EQUAL(bN.name(), "2-acetamido-2-deoxy-beta-D-glucopyranose-(1-4)-2-acetamido-2-deoxy-beta-D-glucopyranose");
// BOOST_CHECK_EQUAL(bN.size(), 2);
BOOST_CHECK(file.is_valid());
// BOOST_CHECK(file.is_valid());
file.save(gTestDir / "test-create_sugar_2.cif");
// file.save(gTestDir / "test-create_sugar_2.cif");
BOOST_CHECK_NO_THROW(cif::mm::structure s2(file));
}
// BOOST_CHECK_NO_THROW(cif::mm::structure s2(file));
// }
// --------------------------------------------------------------------

View File

@@ -31,10 +31,10 @@
#include <cif++.hpp>
#include <cif++/dictionary_parser.hpp>
#include <cif++/parser.hpp>
#include <Eigen/Eigenvalues>
namespace tt = boost::test_tools;
namespace utf = boost::unit_test;
std::filesystem::path gTestDir = std::filesystem::current_path(); // filled in first test
@@ -87,19 +87,20 @@ BOOST_AUTO_TEST_CASE(t1)
// q = Normalize(q);
// Quaternion q{ 0.1, 0.2, 0.3, 0.4 };
cif::quaternion q{0.5, 0.5, 0.5, 0.5};
cif::quaternion q{ 0.5, 0.5, 0.5, 0.5 };
q = normalize(q);
const auto &&[angle0, axis0] = cif::quaternion_to_angle_axis(q);
std::vector<cif::point> p1{
{16.979, 13.301, 44.555},
{18.150, 13.525, 43.680},
{18.656, 14.966, 43.784},
{17.890, 15.889, 44.078},
{17.678, 13.270, 42.255},
{16.248, 13.734, 42.347},
{15.762, 13.216, 43.724}};
{ 16.979f, 13.301f, 44.555f },
{ 18.150f, 13.525f, 43.680f },
{ 18.656f, 14.966f, 43.784f },
{ 17.890f, 15.889f, 44.078f },
{ 17.678f, 13.270f, 42.255f },
{ 16.248f, 13.734f, 42.347f },
{ 15.762f, 13.216f, 43.724f }
};
auto p2 = p1;
@@ -119,7 +120,7 @@ BOOST_AUTO_TEST_CASE(t1)
for (auto &p : p1)
p.rotate(q2);
float rmsd = cif::RMSd(p1, p2);
auto rmsd = cif::RMSd(p1, p2);
BOOST_TEST(rmsd < 1e-5);
@@ -136,7 +137,7 @@ BOOST_AUTO_TEST_CASE(t2)
cif::point xp = cif::cross_product(p[1] - p[0], p[2] - p[0]);
auto q = cif::construct_from_angle_axis(45, xp); //mmcif::Normalize(Quaternion{45 * mmcif::kPI / 180, xp.mX, xp.mY, xp.mZ});
auto q = cif::construct_from_angle_axis(45, xp); // mmcif::Normalize(Quaternion{45 * mmcif::kPI / 180, xp.mX, xp.mY, xp.mZ});
auto &&[angle, axis] = cif::quaternion_to_angle_axis(q);
@@ -153,7 +154,7 @@ BOOST_AUTO_TEST_CASE(t3)
cif::point xp = cif::cross_product(p[1] - p[0], p[2] - p[0]);
auto q = cif::construct_from_angle_axis(45, xp); //mmcif::Normalize(Quaternion{45 * mmcif::kPI / 180, xp.mX, xp.mY, xp.mZ});
auto q = cif::construct_from_angle_axis(45, xp); // mmcif::Normalize(Quaternion{45 * mmcif::kPI / 180, xp.mX, xp.mY, xp.mZ});
auto v = p[1];
v -= p[0];
@@ -166,3 +167,376 @@ BOOST_AUTO_TEST_CASE(t3)
BOOST_TEST(a == 45, tt::tolerance(0.01));
}
BOOST_AUTO_TEST_CASE(dh_q_0)
{
cif::point axis(1, 0, 0);
cif::point p(1, 1, 0);
cif::point t[3] =
{
{ 0, 1, 0 },
{ 0, 0, 0 },
{ 1, 0, 0 }
};
auto a = cif::dihedral_angle(t[0], t[1], t[2], p);
BOOST_TEST(a == 0, tt::tolerance(0.01f));
auto q = cif::construct_from_angle_axis(90, axis);
p.rotate(q);
BOOST_TEST(p.m_x == 1, tt::tolerance(0.01f));
BOOST_TEST(p.m_y == 0, tt::tolerance(0.01f));
BOOST_TEST(p.m_z == 1, tt::tolerance(0.01f));
a = cif::dihedral_angle(t[0], t[1], t[2], p);
BOOST_TEST(a == 90, tt::tolerance(0.01f));
q = cif::construct_from_angle_axis(-90, axis);
p.rotate(q);
BOOST_TEST(p.m_x == 1, tt::tolerance(0.01f));
BOOST_TEST(p.m_y == 1, tt::tolerance(0.01f));
BOOST_TEST(p.m_z == 0, tt::tolerance(0.01f));
a = cif::dihedral_angle(t[0], t[1], t[2], p);
BOOST_TEST(a == 0, tt::tolerance(0.01f));
}
BOOST_AUTO_TEST_CASE(dh_q_1)
{
struct
{
float angle;
cif::point pts[4];
} tests[] = {
{ -97.5f,
{ { 68.8649979f, -7.34800005f, 54.3769989f },
{ 68.1350021f, -8.18700027f, 53.6489983f },
{ 68.7760239f, -9.07335377f, 52.7140236f },
{ 68.9000015f, -10.3944235f, 53.2217026f } } },
{ 80.3f,
{ { 0.304512024f, 0.531184196f, 2.25860214f },
{ 0.956512451f, 0.0321846008f, 1.07460022f },
{ 0, 0, 0 },
{ 0.21336633f, -1.09552193f, -0.878999829f } } },
{ -97.5f,
{ { 0.088973999f, 1.72535372f, 1.66297531f },
{ -0.641021729f, 0.886353493f, 0.93497467f },
{ 0, 0, 0 },
{ 1.29433727f, -0.395142615f, 0.432300746f } } },
{ -97.5f,
{
{ 0.088973999f, 1.72535372f, 1.66297531f },
{ -0.641021729f, 0.886353493f, 0.93497467f },
{ 0, 0, 0 },
{ 1.33983064f, 0.384027064f, -0.275154471f },
} }
};
for (auto &&[angle, pts] : tests)
{
auto q = cif::construct_for_dihedral_angle(pts[0], pts[1], pts[2], pts[3], angle, 1);
pts[3].rotate(q, pts[2]);
auto dh = cif::dihedral_angle(pts[0], pts[1], pts[2], pts[3]);
BOOST_TEST(dh == angle, tt::tolerance(0.1f));
}
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(m2q_0, *utf::tolerance(0.001f))
{
for (size_t i = 0; i < cif::kSymopNrTableSize; ++i)
{
auto d = cif::kSymopNrTable[i].symop().data();
cif::matrix3x3<float> rot;
float Qxx = rot(0, 0) = d[0];
float Qxy = rot(0, 1) = d[1];
float Qxz = rot(0, 2) = d[2];
float Qyx = rot(1, 0) = d[3];
float Qyy = rot(1, 1) = d[4];
float Qyz = rot(1, 2) = d[5];
float Qzx = rot(2, 0) = d[6];
float Qzy = rot(2, 1) = d[7];
float Qzz = rot(2, 2) = d[8];
Eigen::Matrix4f em;
em << Qxx - Qyy - Qzz, Qyx + Qxy, Qzx + Qxz, Qzy - Qyz,
Qyx + Qxy, Qyy - Qxx - Qzz, Qzy + Qyz, Qxz - Qzx,
Qzx + Qxz, Qzy + Qyz, Qzz - Qxx - Qyy, Qyx - Qxy,
Qzy - Qyz, Qxz - Qzx, Qyx - Qxy, Qxx + Qyy + Qzz;
Eigen::EigenSolver<Eigen::Matrix4f> es(em / 3);
auto ev = es.eigenvalues();
size_t bestJ = 0;
float bestEV = -1;
for (size_t j = 0; j < 4; ++j)
{
if (bestEV < ev[j].real())
{
bestEV = ev[j].real();
bestJ = j;
}
}
if (std::abs(bestEV - 1) > 0.01)
continue; // not a rotation matrix
auto col = es.eigenvectors().col(bestJ);
auto q = normalize(cif::quaternion{
static_cast<float>(col(3).real()),
static_cast<float>(col(0).real()),
static_cast<float>(col(1).real()),
static_cast<float>(col(2).real()) });
cif::point p1{ 1, 1, 1 };
cif::point p2 = p1;
p2.rotate(q);
cif::point p3 = rot * p1;
BOOST_TEST(p2.m_x == p3.m_x);
BOOST_TEST(p2.m_y == p3.m_y);
BOOST_TEST(p2.m_z == p3.m_z);
}
}
// BOOST_AUTO_TEST_CASE(m2q_1, *utf::tolerance(0.001f))
// {
// for (size_t i = 0; i < cif::kSymopNrTableSize; ++i)
// {
// auto d = cif::kSymopNrTable[i].symop().data();
// cif::matrix3x3<float> rot;
// float Qxx = rot(0, 0) = d[0];
// float Qxy = rot(0, 1) = d[1];
// float Qxz = rot(0, 2) = d[2];
// float Qyx = rot(1, 0) = d[3];
// float Qyy = rot(1, 1) = d[4];
// float Qyz = rot(1, 2) = d[5];
// float Qzx = rot(2, 0) = d[6];
// float Qzy = rot(2, 1) = d[7];
// float Qzz = rot(2, 2) = d[8];
// cif::matrix4x4<float> m({
// Qxx - Qyy - Qzz, Qyx + Qxy, Qzx + Qxz, Qzy - Qyz,
// Qyx + Qxy, Qyy - Qxx - Qzz, Qzy + Qyz, Qxz - Qzx,
// Qzx + Qxz, Qzy + Qyz, Qzz - Qxx - Qyy, Qyx - Qxy,
// Qzy - Qyz, Qxz - Qzx, Qyx - Qxy, Qxx + Qyy + Qzz
// });
// auto &&[ev, em] = cif::eigen(m * (1/3.0f), false);
// size_t bestJ = 0;
// float bestEV = -1;
// for (size_t j = 0; j < 4; ++j)
// {
// if (bestEV < ev[j])
// {
// bestEV = ev[j];
// bestJ = j;
// }
// }
// if (std::abs(bestEV - 1) > 0.01)
// continue; // not a rotation matrix
// auto q = normalize(cif::quaternion{
// static_cast<float>(em(bestJ, 3)),
// static_cast<float>(em(bestJ, 0)),
// static_cast<float>(em(bestJ, 1)),
// static_cast<float>(em(bestJ, 2)) });
// cif::point p1{ 1, 1, 1 };
// cif::point p2 = p1;
// p2.rotate(q);
// cif::point p3 = rot * p1;
// BOOST_TEST(p2.m_x == p3.m_x);
// BOOST_TEST(p2.m_y == p3.m_y);
// BOOST_TEST(p2.m_z == p3.m_z);
// }
// }
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(symm_1)
{
cif::cell c(10, 10, 10);
cif::point p{ 1, 1, 1 };
cif::point f = fractional(p, c);
BOOST_TEST(f.m_x == 0.1f, tt::tolerance(0.01));
BOOST_TEST(f.m_y == 0.1f, tt::tolerance(0.01));
BOOST_TEST(f.m_z == 0.1f, tt::tolerance(0.01));
cif::point o = orthogonal(f, c);
BOOST_TEST(o.m_x == 1.f, tt::tolerance(0.01));
BOOST_TEST(o.m_y == 1.f, tt::tolerance(0.01));
BOOST_TEST(o.m_z == 1.f, tt::tolerance(0.01));
}
BOOST_AUTO_TEST_CASE(symm_2)
{
using namespace cif::literals;
auto symop = "1_555"_symop;
BOOST_TEST(symop.is_identity() == true);
}
BOOST_AUTO_TEST_CASE(symm_3)
{
using namespace cif::literals;
cif::spacegroup sg(18);
BOOST_TEST(sg.size() == 4);
BOOST_TEST(sg.get_name() == "P 21 21 2");
}
BOOST_AUTO_TEST_CASE(symm_4, *utf::tolerance(0.1f))
{
using namespace cif::literals;
// based on 2b8h
auto sg = cif::spacegroup(154); // p 32 2 1
auto c = cif::cell(107.516, 107.516, 338.487, 90.00, 90.00, 120.00);
cif::point a{ -8.688, 79.351, 10.439 }; // O6 NAG A 500
cif::point b{ -35.356, 33.693, -3.236 }; // CG2 THR D 400
cif::point sb( -6.916, 79.34, 3.236); // 4_565 copy of b
BOOST_TEST(distance(a, sg(a, c, "1_455"_symop)) == static_cast<float>(c.get_a()));
BOOST_TEST(distance(a, sg(a, c, "1_545"_symop)) == static_cast<float>(c.get_b()));
BOOST_TEST(distance(a, sg(a, c, "1_554"_symop)) == static_cast<float>(c.get_c()));
auto sb2 = sg(b, c, "4_565"_symop);
BOOST_TEST(sb.m_x == sb2.m_x);
BOOST_TEST(sb.m_y == sb2.m_y);
BOOST_TEST(sb.m_z == sb2.m_z);
BOOST_TEST(distance(a, sb2) == 7.42f);
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(symm_4wvp_1, *utf::tolerance(0.1f))
{
using namespace cif::literals;
cif::file f(gTestDir / "4wvp.cif.gz");
auto &db = f.front();
cif::mm::structure s(db);
cif::crystal c(db);
cif::point p{ -78.722, 98.528, 11.994 };
auto a = s.get_residue("A", 10, "").get_atom_by_atom_id("O");
auto sp1 = c.symmetry_copy(a.get_location(), "2_565"_symop);
BOOST_TEST(sp1.m_x == p.m_x);
BOOST_TEST(sp1.m_y == p.m_y);
BOOST_TEST(sp1.m_z == p.m_z);
const auto &[d, sp2, so] = c.closest_symmetry_copy(p, a.get_location());
BOOST_TEST(d < 1);
BOOST_TEST(sp2.m_x == p.m_x);
BOOST_TEST(sp2.m_y == p.m_y);
BOOST_TEST(sp2.m_z == p.m_z);
}
BOOST_AUTO_TEST_CASE(symm_2bi3_1, *utf::tolerance(0.1f))
{
cif::file f(gTestDir / "2bi3.cif.gz");
auto &db = f.front();
cif::mm::structure s(db);
cif::crystal c(db);
auto struct_conn = db["struct_conn"];
for (const auto &[
asym1, seqid1, authseqid1, atomid1, symm1,
asym2, seqid2, authseqid2, atomid2, symm2,
dist] : struct_conn.find<
std::string,int,std::string,std::string,std::string,
std::string,int,std::string,std::string,std::string,
float>(
cif::key("ptnr1_symmetry") != "1_555" or cif::key("ptnr2_symmetry") != "1_555",
"ptnr1_label_asym_id", "ptnr1_label_seq_id", "ptnr1_auth_seq_id", "ptnr1_label_atom_id", "ptnr1_symmetry",
"ptnr2_label_asym_id", "ptnr2_label_seq_id", "ptnr2_auth_seq_id", "ptnr2_label_atom_id", "ptnr2_symmetry",
"pdbx_dist_value"
))
{
auto &r1 = s.get_residue(asym1, seqid1, authseqid1);
auto &r2 = s.get_residue(asym2, seqid2, authseqid2);
auto a1 = r1.get_atom_by_atom_id(atomid1);
auto a2 = r2.get_atom_by_atom_id(atomid2);
auto sa1 = c.symmetry_copy(a1.get_location(), cif::sym_op(symm1));
auto sa2 = c.symmetry_copy(a2.get_location(), cif::sym_op(symm2));
BOOST_TEST(cif::distance(sa1, sa2) == dist);
auto pa1 = a1.get_location();
const auto &[d, p, so] = c.closest_symmetry_copy(pa1, a2.get_location());
BOOST_TEST(p.m_x == sa2.m_x);
BOOST_TEST(p.m_y == sa2.m_y);
BOOST_TEST(p.m_z == sa2.m_z);
BOOST_TEST(d == dist);
BOOST_TEST(so.string() == symm2);
}
}
BOOST_AUTO_TEST_CASE(symm_3bwh_1, *utf::tolerance(0.1f))
{
cif::file f(gTestDir / "3bwh.cif.gz");
auto &db = f.front();
cif::crystal c(db);
cif::mm::structure s(db);
for (auto a1 : s.atoms())
{
for (auto a2 : s.atoms())
{
if (a1 == a2)
continue;
const auto&[ d, p, so ] = c.closest_symmetry_copy(a1.get_location(), a2.get_location());
BOOST_TEST(d == distance(a1.get_location(), p));
}
}
}

View File

@@ -31,8 +31,8 @@
#include <cif++.hpp>
#include <cif++/dictionary_parser.hpp>
#include <cif++/parser.hpp>
#include "cif++/dictionary_parser.hpp"
namespace tt = boost::test_tools;
@@ -75,26 +75,50 @@ bool init_unit_test()
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(id_1)
{
BOOST_TEST(cif::cif_id_for_number(0) == "A");
BOOST_TEST(cif::cif_id_for_number(25) == "Z");
BOOST_TEST(cif::cif_id_for_number(26) == "AA");
BOOST_TEST(cif::cif_id_for_number(26 + 1) == "AB");
BOOST_TEST(cif::cif_id_for_number(26 + 26 * 26 - 1) == "ZZ");
BOOST_TEST(cif::cif_id_for_number(26 + 26 * 26) == "AAA");
BOOST_TEST(cif::cif_id_for_number(26 + 26 * 26 + 1) == "AAB");
std::set<std::string> testset;
for (int i = 0; i < 100000; ++i)
{
std::string id = cif::cif_id_for_number(i);
BOOST_TEST(testset.count(id) == 0);
testset.insert(id);
}
BOOST_TEST(testset.size() == 100000);
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(cc_1)
{
std::tuple<std::string_view, float, char> tests[] = {
{ "1.0", 1.0, 0 },
{ "1.0e10", 1.0e10, 0 },
{ "-1.1e10", -1.1e10, 0 },
{ "-.2e11", -.2e11, 0 },
{ "1.3e-10", 1.3e-10, 0 },
{ "1.0", 1.0f, 0 },
{ "1.0e10", 1.0e10f, 0 },
{ "-1.1e10", -1.1e10f, 0 },
{ "-.2e11", -.2e11f, 0 },
{ "1.3e-10", 1.3e-10f, 0 },
{ "1.0 ", 1.0, ' ' },
{ "1.0e10 ", 1.0e10, ' ' },
{ "-1.1e10 ", -1.1e10, ' ' },
{ "-.2e11 ", -.2e11, ' ' },
{ "1.3e-10 ", 1.3e-10, ' ' },
{ "1.0 ", 1.0f, ' ' },
{ "1.0e10 ", 1.0e10f, ' ' },
{ "-1.1e10 ", -1.1e10f, ' ' },
{ "-.2e11 ", -.2e11f, ' ' },
{ "1.3e-10 ", 1.3e-10f, ' ' },
{ "3.0", 3.0, 0 },
{ "3.0 ", 3.0, ' ' },
{ "3.0", 3.0f, 0 },
{ "3.0 ", 3.0f, ' ' },
{ "3.000000", 3.0, 0 },
{ "3.000000 ", 3.0, ' ' },
{ "3.000000", 3.0f, 0 },
{ "3.000000 ", 3.0f, ' ' },
};
for (const auto &[txt, val, ch] : tests)
@@ -112,7 +136,7 @@ BOOST_AUTO_TEST_CASE(cc_1)
BOOST_AUTO_TEST_CASE(cc_2)
{
std::tuple<float, int, std::string_view> tests[] = {
{ 1.1, 1, "1.1" }
{ 1.1f, 1, "1.1" }
};
for (const auto &[val, prec, test] : tests)
@@ -131,7 +155,7 @@ BOOST_AUTO_TEST_CASE(item_1)
using namespace cif;
item i1("1", "1");
item i2("2", 2.0);
item i2("2", 2.0f);
item i3("3", '3');
item ci1(i1);
@@ -163,23 +187,23 @@ BOOST_AUTO_TEST_CASE(r_1)
c.emplace({
{ "f-1", 1 },
{ "f-2", "two" },
{ "f-3", 3.0, 3 },
{ "f-3", 3.0f, 3 },
});
auto row = c.front();
BOOST_CHECK_EQUAL(row["f-1"].compare(1), 0);
BOOST_CHECK_EQUAL(row["f-2"].compare("two"), 0);
BOOST_CHECK_EQUAL(row["f-3"].compare(3.0), 0); // This fails when running in valgrind... sigh
BOOST_CHECK_EQUAL(row["f-3"].compare(3.0f), 0); // This fails when running in valgrind... sigh
const auto &[f1, f2, f3] = row.get<int, std::string, float>("f-1", "f-2", "f-3");
BOOST_CHECK_EQUAL(f1, 1);
BOOST_CHECK_EQUAL(f2, "two");
BOOST_CHECK_EQUAL(f3, 3.0); // This fails when running in valgrind... sigh
BOOST_CHECK_EQUAL(f3, 3.0f); // This fails when running in valgrind... sigh
BOOST_CHECK_EQUAL(row.get<int>("f-1"), 1);
BOOST_CHECK_EQUAL(row.get<std::string>("f-2"), "two");
BOOST_CHECK_EQUAL(row.get<float>("f-3"), 3.0);
BOOST_CHECK_EQUAL(row.get<float>("f-3"), 3.0f);
int f_1;
std::string f_2;
@@ -189,7 +213,7 @@ BOOST_AUTO_TEST_CASE(r_1)
BOOST_CHECK_EQUAL(f_1, 1);
BOOST_CHECK_EQUAL(f_2, "two");
BOOST_CHECK_EQUAL(f_3, 3.0); // This fails when running in valgrind... sigh
BOOST_CHECK_EQUAL(f_3, 3.0f); // This fails when running in valgrind... sigh
}
BOOST_AUTO_TEST_CASE(r_2)
@@ -486,14 +510,14 @@ _test.value
BOOST_CHECK_EQUAL(++n, 1);
BOOST_CHECK_EQUAL(r["id"].as<int>(), 1);
BOOST_CHECK_EQUAL(r["name"].as<std::string>(), "aap");
BOOST_CHECK_EQUAL(r["value"].as<float>(), 1.0);
BOOST_CHECK_EQUAL(r["value"].as<float>(), 1.0f);
}
auto t = test.find(cif::key("id") == 1);
BOOST_CHECK(not t.empty());
BOOST_CHECK_EQUAL(t.front()["name"].as<std::string>(), "aap");
auto t2 = test.find(cif::key("value") == 1.2);
auto t2 = test.find(cif::key("value") == 1.2f);
BOOST_CHECK(not t2.empty());
BOOST_CHECK_EQUAL(t2.front()["name"].as<std::string>(), "mies");
}
@@ -1774,8 +1798,6 @@ _test.name
BOOST_AUTO_TEST_CASE(c3)
{
cif::VERBOSE = 1;
auto f = R"(data_TEST
#
loop_
@@ -1811,6 +1833,43 @@ _test.name
BOOST_CHECK_EQUAL(name, "aap");
}
BOOST_AUTO_TEST_CASE(c4)
{
auto f = R"(data_TEST
#
loop_
_test.id
_test.name
1 aap
2 noot
3 mies
4 .
5 ?
)"_cf;
auto &db = f.front();
// query tests
BOOST_TEST(db["test"].find_max<int>("id") == 5);
BOOST_TEST(db["test"].find_max<int>("id", cif::key("name") != cif::null) == 3);
BOOST_TEST(db["test"].find_min<int>("id") == 1);
BOOST_TEST(db["test"].find_min<int>("id", cif::key("name") == cif::null) == 4);
// count tests
BOOST_TEST(db["test"].count(cif::all()) == 5);
BOOST_TEST(db["test"].count(cif::key("name") != cif::null) == 3);
BOOST_TEST(db["test"].count(cif::key("name") == cif::null) == 2);
// find_first tests
BOOST_TEST(db["test"].find_first<int>(cif::key("id") == 1, "id") == 1);
BOOST_TEST(db["test"].find_first<int>(cif::all(), "id") == 1);
// find1 tests
BOOST_TEST(db["test"].find1<int>(cif::key("id") == 1, "id") == 1);
BOOST_CHECK_THROW(db["test"].find1<int>(cif::all(), "id"), cif::multiple_results_error);
}
// --------------------------------------------------------------------
// rename test
@@ -2326,11 +2385,16 @@ BOOST_AUTO_TEST_CASE(output_test_1)
data_Q
loop_
_test.text
"stop_the_crap"
stop_the_crap
'and stop_ this too'
'data_dinges'
'blablaglobal_bla'
blablaglobal_bla
boo.data_.whatever
'data_.whatever'
'stop_'
'loop_'
'global_'
'_with.leading_underscore'
)"_cf;
auto &db1 = data1.front();
@@ -2341,11 +2405,16 @@ boo.data_.whatever
const char *s;
bool q;
} kS[] = {
{ "stop_the_crap", false },
{ "stop_the_crap", true },
{ "and stop_ this too", false },
{ "data_dinges", false },
{ "blablaglobal_bla", false },
{ "boo.data_.whatever", true }
{ "blablaglobal_bla", true },
{ "boo.data_.whatever", true },
{ "data_.whatever", false },
{ "stop_", false },
{ "loop_", false },
{ "global_", false },
{ "_with.leading_underscore", false }
};
BOOST_CHECK_EQUAL(test1.size(), sizeof(kS) / sizeof(T));
@@ -2816,7 +2885,7 @@ save__cat_1.name
std::istream is_dict(&buffer);
auto validator = cif::parse_dictionary("test_dict.dic", is_dict);
auto &validator = cif::validator_factory::instance().construct_validator("test_dict.dic", is_dict);
cif::file f;
f.set_validator(&validator);
@@ -2854,8 +2923,6 @@ _cat_1.name
ss << f;
cif::file f2(ss);
f2.set_validator(&validator);
BOOST_ASSERT(f2.is_valid());
auto &audit_conform = f2.front()["audit_conform"];
@@ -3058,3 +3125,29 @@ _date today
BOOST_TEST(db == db2);
}
BOOST_AUTO_TEST_CASE(find1_opt_1)
{
using namespace cif::literals;
using namespace std::literals;
auto f = R"(data_TEST
#
loop_
_test.id
_test.name
_test.value
1 aap 1.0
2 noot 1.1
3 mies 1.2
)"_cf;
auto &db = f.front();
auto &test = db["test"];
auto v = test.find1<std::optional<float>>("id"_key == 1, "value");
BOOST_CHECK(v.has_value());
BOOST_TEST(*v == 1.0f);
v = test.find1<std::optional<float>>("id"_key == 4, "value");
BOOST_CHECK(v.has_value() == false);
}

View File

@@ -1,55 +1,71 @@
#!/bin/bash
#!/bin/sh
# Code updated based on a bug report in Ubuntu:
# Bug #1999259 reported by Kyler Laird on 2022-12-09
set -e
if [ "$EUID" -ne 0 ]
then echo "Please run as root"
# Get the effective UID, but do so in a compatible way (we may be running dash)
euid=${EUID:-$(id -u)}
if [ "${euid}" -ne 0 ]; then
echo "Please run as root"
exit
fi
if [ -f /etc/libcifpp.conf ] ; then
. /etc/libcifpp.conf
if [ -f "@CIFPP_ETC_DIR@/libcifpp.conf" ]; then
. "@CIFPP_ETC_DIR@/libcifpp.conf"
fi
# check to see if we're supposed to run at all
if [ "$update" != "true" ] ; then
if [ "$update" != "true" ]; then
exit
fi
# if cache directory doesn't exist, exit.
if ! [ -d @CIFPP_CACHE_DIR@ ]; then
# if cache directory doesn't exist, exit.
if ! [ -d "@CIFPP_CACHE_DIR@" ]; then
exit
fi
fetch_dictionary () {
# Create a temp file in the right directory and
# make sure it is cleaned up when this script exits
tmpfile=$(mktemp)
trap "rm -f \"${tmpfile}\"" EXIT
update_dictionary() {
dict=$1
source=$2
wget -O${dict}.gz ${source}
# Using curl with
# --location (follow redirects)
# --silent (no diagnostic output at all)
# --time-cond (only fetch if source is newer)
#
# Output is extracted and written to $tmpfile and when successful
# the tmpfile is placed at the desired location and updated is set
# to true
# be careful not to nuke an existing dictionary file
# extract to a temporary file first
gunzip -c ${dict}.gz > ${dict}-tmp
# then move the extracted file to the final location
mv ${dict}-tmp ${dict}
# and clean up afterwards
rm ${dict}.gz
curl --silent --location --compressed --time-cond "${dict}" "${source}" | (
# uncompress the file on the fly, if it is compressed
if [ "${source%.gz}" != "${source}" ]; then
gunzip -c > "${tmpfile}" 2>/dev/null
else
cat > "${tmpfile}"
fi
) && (
mv "${tmpfile}" "${dict}" && chmod a+r "${dict}"
) || true
}
# fetch the dictionaries
# Update the dictionaries
fetch_dictionary "@CIFPP_CACHE_DIR@/mmcif_pdbx.dic" "https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic.gz"
fetch_dictionary "@CIFPP_CACHE_DIR@/components.cif" "ftp://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif.gz"
update_dictionary "@CIFPP_CACHE_DIR@/components.cif" "https://ftp.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"
# notify subscribers
# notify subscribers, will fail on FreeBSD
if [ -d /etc/libcifpp/cache-update.d ] && [ -x /bin/run-parts ]; then
run-parts --arg "@CIFPP_CACHE_DIR@" -- /etc/libcifpp/cache-update.d
if [ -d "@CIFPP_ETC_DIR@/libcifpp/cache-update.d" ] && [ -x /bin/run-parts ]; then
run-parts --arg "@CIFPP_CACHE_DIR@" -- "@CIFPP_ETC_DIR@/libcifpp/cache-update.d"
fi
wget -O/var/cache/libcifpp/mmcif_ma.dic "https://github.com/ihmwg/ModelCIF/raw/master/dist/mmcif_ma.dic"