mirror of
https://github.com/PDB-REDO/libcifpp.git
synced 2026-06-04 22:14:24 +08:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eaa5032e11 | ||
|
|
8400247674 | ||
|
|
08e1b197ac | ||
|
|
20971e1ee9 | ||
|
|
ce6a953eff | ||
|
|
87c20c26ec | ||
|
|
025ad93d06 | ||
|
|
e98fe2608a | ||
|
|
0399d99ca6 | ||
|
|
71b24a678e | ||
|
|
dc03cb6a70 | ||
|
|
de9b33a918 | ||
|
|
56c75490f2 | ||
|
|
20695404c1 | ||
|
|
46ea0ca930 | ||
|
|
0da8ba85bf | ||
|
|
5d3734e2a5 | ||
|
|
e692ed6c87 | ||
|
|
da8e81f871 | ||
|
|
0aef601bfd | ||
|
|
39644c1bd1 | ||
|
|
421758a01c | ||
|
|
a3a0e5cc70 | ||
|
|
9268aa3cfc | ||
|
|
187d7fc314 | ||
|
|
6df8b435ec | ||
|
|
399827db41 | ||
|
|
57aaf36bde |
2
.github/workflows/build-documentation.yml
vendored
2
.github/workflows/build-documentation.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
- name: Run Sphinx
|
||||
run: |
|
||||
cmake --build ${{ steps.strings.outputs.build-output-dir }} --target Sphinx-libcifpp
|
||||
cmake --build ${{ steps.strings.outputs.build-output-dir }} --target Sphinx-cifpp
|
||||
ls -l ${{ steps.strings.outputs.build-output-dir }}
|
||||
ls -l ${{ steps.strings.outputs.build-output-dir }}/docs/sphinx
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ endif()
|
||||
# set the project name
|
||||
project(
|
||||
libcifpp
|
||||
VERSION 10.0.1
|
||||
VERSION 10.0.4
|
||||
LANGUAGES CXX C)
|
||||
|
||||
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
@@ -138,13 +138,6 @@ if(MSVC)
|
||||
# make msvc standards compliant...
|
||||
add_compile_options(/permissive- /bigobj)
|
||||
add_link_options(/NODEFAULTLIB:library)
|
||||
|
||||
# This is dubious...
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||
else()
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Libraries
|
||||
@@ -367,8 +360,9 @@ else()
|
||||
endif()
|
||||
|
||||
if(NOT STD_CHARCONV_COMPILING)
|
||||
target_include_directories(cifpp PRIVATE ${FastFloat_INCLUDE_DIRS})
|
||||
target_compile_definitions(cifpp PRIVATE USE_FAST_FLOAT)
|
||||
get_target_property(FF_INC_DIR FastFloat::fast_float INTERFACE_INCLUDE_DIRECTORIES)
|
||||
target_include_directories(cifpp PRIVATE ${FF_INC_DIR})
|
||||
target_compile_definitions(cifpp PRIVATE USE_FAST_FLOAT)
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
|
||||
16
changelog
16
changelog
@@ -1,3 +1,19 @@
|
||||
Version 10.0.4
|
||||
- Fixed find_by_value in the index of a category,
|
||||
avoid swapping columns in the search keys
|
||||
|
||||
Version 10.0.3
|
||||
- Clear pdbx_nonpoly_scheme before filling it in reconstruction
|
||||
- Changed handling of numbers with a preceding plus character,
|
||||
these are now stored as strings to avoid inadvertently
|
||||
mutilating phone numbers.
|
||||
|
||||
Version 10.0.2
|
||||
- Fixed regression in reconstruction introduced in 10.0.1
|
||||
- Fixed symmetry operations
|
||||
- Added validation for pdbx_item_enumeration as well as
|
||||
case-sensitive checks for enumerations when needed.
|
||||
|
||||
Version 10.0.1
|
||||
- Fixed some regressions, like assigning to items.
|
||||
- At emplace time (in category) values that are
|
||||
|
||||
@@ -331,8 +331,11 @@ class item_value
|
||||
case TEXT:
|
||||
{
|
||||
auto sv = m_data.sv();
|
||||
auto sp = sv.data();
|
||||
if (*sp == '+')
|
||||
++sp;
|
||||
int64_t v;
|
||||
auto &&[ptr, ec] = from_chars(sv.data(), sv.data() + sv.length(), v);
|
||||
auto &&[ptr, ec] = from_chars(sp, sv.data() + sv.length(), v);
|
||||
if (ec != std::errc{})
|
||||
throw std::system_error(std::make_error_code(ec));
|
||||
if (ptr != sv.data() + sv.length())
|
||||
@@ -361,8 +364,11 @@ class item_value
|
||||
case TEXT:
|
||||
{
|
||||
auto sv = m_data.sv();
|
||||
auto sp = sv.data();
|
||||
if (*sp == '+')
|
||||
++sp;
|
||||
double v;
|
||||
auto &&[ptr, ec] = from_chars(sv.data(), sv.data() + sv.length(), v);
|
||||
auto &&[ptr, ec] = from_chars(sp, sv.data() + sv.length(), v);
|
||||
if (ec != std::errc{})
|
||||
throw std::system_error(std::make_error_code(ec));
|
||||
if (ptr != sv.data() + sv.length())
|
||||
|
||||
@@ -122,7 +122,7 @@ class atom
|
||||
|
||||
// const compound *compound() const;
|
||||
|
||||
[[nodiscard]] const item_value &get_property(std::string_view name) const;
|
||||
[[nodiscard]] const item_handle get_property(std::string_view name) const;
|
||||
void set_property(const std::string_view name, item_value value);
|
||||
|
||||
row_handle row()
|
||||
@@ -227,7 +227,7 @@ class atom
|
||||
explicit operator bool() const { return m_impl.operator bool(); }
|
||||
|
||||
/// \brief Return the item named @a name in the _atom_site category for this atom
|
||||
[[nodiscard]] const item_value &get_property_value(std::string_view name) const
|
||||
[[nodiscard]] const item_handle get_property_value(std::string_view name) const
|
||||
{
|
||||
if (not m_impl)
|
||||
throw std::logic_error("Error trying to fetch a property from an uninitialized atom");
|
||||
|
||||
@@ -902,74 +902,4 @@ quaternion align_points(const std::vector<point> &a, const std::vector<point> &b
|
||||
/// \brief The RMSd for the points in \a a and \a b
|
||||
double RMSd(const std::vector<point> &a, const std::vector<point> &b);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/**
|
||||
* @brief Helper class to generate evenly divided points on a sphere
|
||||
*
|
||||
* We use a fibonacci sphere to calculate even distribution of the dots
|
||||
*
|
||||
* @tparam N The number of points on the sphere is 2 * N + 1
|
||||
*/
|
||||
template <int N>
|
||||
class spherical_dots
|
||||
{
|
||||
public:
|
||||
/// \brief the number of points
|
||||
constexpr static int P = 2 * N * 1;
|
||||
|
||||
/// \brief the *weight* of the fibonacci sphere
|
||||
constexpr static double W = (4 * std::numbers::pi) / P;
|
||||
|
||||
/// \brief the internal storage type
|
||||
using array_type = typename std::array<point, P>;
|
||||
|
||||
/// \brief iterator type
|
||||
using iterator = typename array_type::const_iterator;
|
||||
|
||||
/// \brief singleton instance
|
||||
static spherical_dots &instance()
|
||||
{
|
||||
static spherical_dots sInstance;
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/// \brief The number of points
|
||||
[[nodiscard]] std::size_t size() const { return P; }
|
||||
|
||||
/// \brief Access a point by index
|
||||
const point operator[](uint32_t inIx) const { return m_points[inIx]; }
|
||||
|
||||
/// \brief iterator pointing to the first point
|
||||
[[nodiscard]] iterator begin() const { return m_points.begin(); }
|
||||
|
||||
/// \brief iterator pointing past the last point
|
||||
[[nodiscard]] iterator end() const { return m_points.end(); }
|
||||
|
||||
/// \brief return the *weight*,
|
||||
[[nodiscard]] double weight() const { return W; }
|
||||
|
||||
spherical_dots()
|
||||
{
|
||||
const double
|
||||
kGoldenRatio = std::numbers::phi;
|
||||
|
||||
auto p = m_points.begin();
|
||||
|
||||
for (int32_t i = -N; i <= N; ++i)
|
||||
{
|
||||
double lat = std::asin((2.0 * i) / P);
|
||||
double lon = std::fmod(i, kGoldenRatio) * 2 * std::numbers::pi / kGoldenRatio;
|
||||
|
||||
p->m_x = std::sin(lon) * std::cos(lat);
|
||||
p->m_y = std::cos(lon) * std::cos(lat);
|
||||
p->m_z = std::sin(lat);
|
||||
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
array_type m_points;
|
||||
};
|
||||
|
||||
} // namespace cif
|
||||
|
||||
@@ -196,7 +196,7 @@ class validation_exception : public std::runtime_error
|
||||
public:
|
||||
// Constructors
|
||||
/// @cond
|
||||
|
||||
|
||||
validation_exception(validation_error err)
|
||||
: validation_exception(make_error_code(err))
|
||||
{
|
||||
@@ -337,7 +337,7 @@ struct item_validator
|
||||
std::string m_item_name; ///< The item name
|
||||
bool m_mandatory; ///< Flag indicating this item is mandatory
|
||||
const type_validator *m_type; ///< The type for this item
|
||||
cif::iset m_enums; ///< If filled, the set of allowed values
|
||||
std::set<std::string> m_enums; ///< If filled, the set of allowed values
|
||||
std::string m_default; ///< If filled, a default value for this item
|
||||
std::string m_category; ///< The category this item_validator belongs to
|
||||
std::vector<item_alias> m_aliases; ///< The aliases for this item
|
||||
@@ -502,15 +502,29 @@ class validator
|
||||
/// @brief Bottleneck function to report an error in validation
|
||||
void report_error(std::error_code ec, bool fatal = true) const;
|
||||
|
||||
/// @brief Bottleneck function to report an error in validation
|
||||
void report_error(validation_error err, std::string value, std::string_view category,
|
||||
std::string_view item, bool fatal = true) const
|
||||
{
|
||||
report_error(make_error_code(err), value, category, item, fatal);
|
||||
}
|
||||
|
||||
/// @brief Bottleneck function to report an error in validation
|
||||
void report_error(validation_error err, std::string_view category,
|
||||
std::string_view item, bool fatal = true) const
|
||||
{
|
||||
report_error(make_error_code(err), category, item, fatal);
|
||||
report_error(make_error_code(err), "", category, item, fatal);
|
||||
}
|
||||
|
||||
/// @brief Bottleneck function to report an error in validation
|
||||
void report_error(std::error_code ec, std::string_view category,
|
||||
std::string_view item, bool fatal = true) const
|
||||
{
|
||||
report_error(ec, "", category, item, fatal);
|
||||
}
|
||||
|
||||
/// @brief Bottleneck function to report an error in validation
|
||||
void report_error(std::error_code ec, std::string value, std::string_view category,
|
||||
std::string_view item, bool fatal = true) const;
|
||||
|
||||
/// @brief Write out the audit_conform data for this validator
|
||||
|
||||
3825
rsrc/mmcif_pdbx.dic
3825
rsrc/mmcif_pdbx.dic
File diff suppressed because it is too large
Load Diff
161
src/category.cpp
161
src/category.cpp
@@ -118,6 +118,7 @@ class row_comparator
|
||||
|
||||
for (const auto &[k, f] : m_comparator)
|
||||
{
|
||||
assert(cat.get_item_name(k) == ai->name);
|
||||
d = f(ai->value, rhb[k].value());
|
||||
|
||||
if (d != 0)
|
||||
@@ -363,10 +364,9 @@ row *category_index::find_by_value(const category &cat, const category::key_type
|
||||
// sort the values in k first
|
||||
|
||||
category::key_type k2;
|
||||
for (auto &f : cat.key_item_indices())
|
||||
auto cv = cat.get_cat_validator();
|
||||
for (auto &fld : cv->m_keys)
|
||||
{
|
||||
auto fld = cat.get_item_name(f);
|
||||
|
||||
auto ki = std::ranges::find_if(k, [&fld](auto &i)
|
||||
{ return i.name == fld; });
|
||||
if (ki == k.end())
|
||||
@@ -614,11 +614,11 @@ void category::drop_empty_items()
|
||||
{
|
||||
std::vector<bool> is_empty(get_item_count(), true);
|
||||
|
||||
for (auto row : *this)
|
||||
for (size_t ix = 0; ix < get_item_count(); ++ix)
|
||||
{
|
||||
for (size_t ix = 0; ix < get_item_count(); ++ix)
|
||||
for (auto row : *this)
|
||||
{
|
||||
if (is_empty[ix] and not row[ix].empty())
|
||||
if (not row[ix].empty())
|
||||
{
|
||||
is_empty[ix] = false;
|
||||
break;
|
||||
@@ -749,16 +749,34 @@ void category::set_validator(const validator *v, datablock &db)
|
||||
|
||||
bool number = type->m_primitive_type == DDL_PrimitiveType::Numb;
|
||||
if (number)
|
||||
continue;
|
||||
|
||||
for (auto row = m_head; row != nullptr; row = row->m_next)
|
||||
{
|
||||
if (cix >= row->size() or row->operator[](cix).empty())
|
||||
continue;
|
||||
for (auto row = m_head; row != nullptr; row = row->m_next)
|
||||
{
|
||||
if (cix >= row->size() or row->operator[](cix).empty())
|
||||
continue;
|
||||
|
||||
item_value &v = row->operator[](cix);
|
||||
if (v.is_number())
|
||||
v = v.str();
|
||||
item_value &v = row->operator[](cix);
|
||||
if (not v.is_number())
|
||||
{
|
||||
// Try cast the value to a number and throw in case of failure
|
||||
if (auto sv = v.sv(); sv.find_first_of(".eE") == std::string_view::npos)
|
||||
v.cast_to_int();
|
||||
else
|
||||
v.cast_to_float();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto row = m_head; row != nullptr; row = row->m_next)
|
||||
{
|
||||
if (cix >= row->size() or row->operator[](cix).empty())
|
||||
continue;
|
||||
|
||||
item_value &v = row->operator[](cix);
|
||||
if (v.is_number())
|
||||
v = v.str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -890,11 +908,13 @@ bool category::is_valid() const
|
||||
seen = true;
|
||||
std::error_code ec;
|
||||
|
||||
iv->validate_value(*ri->get(cix), ec);
|
||||
auto &v = *ri->get(cix);
|
||||
|
||||
iv->validate_value(v, ec);
|
||||
|
||||
if (ec != std::errc{})
|
||||
{
|
||||
m_validator->report_error(ec, m_name, m_items[cix].m_name, false);
|
||||
m_validator->report_error(ec, v.str(), m_name, m_items[cix].m_name, false);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1450,7 +1470,12 @@ void category::update_value(const std::vector<row_handle> &rows, std::string_vie
|
||||
|
||||
std::error_code ec;
|
||||
col.m_validator->validate_value(value, ec);
|
||||
if (ec)
|
||||
if (ec == cif::make_error_code(cif::validation_error::value_is_not_in_enumeration_list))
|
||||
{
|
||||
if (cif::VERBOSE >= 0)
|
||||
m_validator->report_error(ec, m_name, item_name, false);
|
||||
}
|
||||
else if (ec)
|
||||
throw validation_exception(ec, m_name, item_name);
|
||||
}
|
||||
}
|
||||
@@ -1577,7 +1602,17 @@ void category::update_value(row *row, uint16_t item, item_value value, bool upda
|
||||
|
||||
// check the value
|
||||
if (col.m_validator and validate)
|
||||
col.m_validator->validate_value(value);
|
||||
{
|
||||
std::error_code ec;
|
||||
col.m_validator->validate_value(value, ec);
|
||||
if (ec == cif::make_error_code(cif::validation_error::value_is_not_in_enumeration_list))
|
||||
{
|
||||
if (cif::VERBOSE >= 0)
|
||||
m_validator->report_error(ec, m_name, m_items[item].m_name, false);
|
||||
}
|
||||
else if (ec)
|
||||
throw validation_exception(ec, m_name, m_items[item].m_name);
|
||||
}
|
||||
|
||||
// If the item is part of the Key for this category, remove it from the index
|
||||
// before updating
|
||||
@@ -1789,6 +1824,12 @@ category::iterator category::insert_impl(const_iterator pos, row *n)
|
||||
}
|
||||
else if (ec == cif::make_error_code(cif::validation_error::value_is_not_a_char_string))
|
||||
v->cast_to_string();
|
||||
else if (ec == cif::make_error_code(cif::validation_error::value_is_not_in_enumeration_list))
|
||||
{
|
||||
if (cif::VERBOSE >= 0)
|
||||
m_validator->report_error(ec, m_name, m_items[ix].m_name, false);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
throw std::system_error(ec, "Attempt to insert invalid value");
|
||||
|
||||
@@ -2158,7 +2199,7 @@ void category::write_cif(std::ostream &os, const std::vector<uint16_t> &order, b
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
offset = detail::write_value(os, s, offset, w, /* right_aligned[cix] */ iv->is_number());
|
||||
offset = detail::write_value(os, s, offset, w, right_aligned[cix] /* iv->is_number() */);
|
||||
|
||||
if (offset > 132)
|
||||
{
|
||||
@@ -2680,70 +2721,20 @@ bool category::operator==(const category &rhs) const
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
// set<std::string> item_namesA(a.items()), item_namesB(b.items());
|
||||
//
|
||||
// if (item_namesA != item_namesB)
|
||||
// std::cout << "Unequal number of items\n";
|
||||
std::set<std::string> keys;
|
||||
|
||||
const category_validator *catValidator = nullptr;
|
||||
|
||||
auto validator = a.get_validator();
|
||||
if (validator != nullptr)
|
||||
catValidator = validator->get_validator_for_category(a.name());
|
||||
|
||||
using compType = std::function<int(std::string_view, std::string_view)>;
|
||||
std::vector<std::tuple<std::string, compType>> item_names;
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::size_t> keyIx;
|
||||
|
||||
if (catValidator == nullptr)
|
||||
for (const auto &items : { a.get_items(), b.get_items()})
|
||||
{
|
||||
for (auto &item_name : a.get_items())
|
||||
{
|
||||
item_names.emplace_back(item_name, [](std::string_view va, std::string_view vb)
|
||||
{ return va.compare(vb); });
|
||||
keyIx.push_back(keys.size());
|
||||
keys.push_back(item_name);
|
||||
}
|
||||
for (auto &item_name : items)
|
||||
keys.insert(item_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
keys = catValidator->m_keys;
|
||||
|
||||
for (auto &item_name : a.key_items())
|
||||
{
|
||||
auto iv = catValidator->get_validator_for_item(item_name);
|
||||
if (iv == nullptr)
|
||||
throw std::runtime_error("missing item validator");
|
||||
auto tv = iv->m_type;
|
||||
if (tv == nullptr)
|
||||
throw std::runtime_error("missing type validator");
|
||||
item_names.emplace_back(item_name, [tv](auto &&a1, auto &&a2)
|
||||
{ return tv->compare(std::forward<decltype(a1)>(a1), std::forward<decltype(a2)>(a2)); });
|
||||
|
||||
auto pred = [item_name](const std::string &s) -> bool
|
||||
{
|
||||
return cif::iequals(item_name, s) == 0;
|
||||
};
|
||||
if (std::ranges::find_if(keys, pred) == keys.end())
|
||||
keyIx.push_back(item_names.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// a.reorderByIndex();
|
||||
// b.reorderByIndex();
|
||||
|
||||
auto rowEqual = [&](const_row_handle &a, const_row_handle &b)
|
||||
{
|
||||
int d = 0;
|
||||
|
||||
for (auto kix : keyIx)
|
||||
for (const auto &item_name : keys)
|
||||
{
|
||||
std::string item_name;
|
||||
compType compare;
|
||||
|
||||
std::tie(item_name, compare) = item_names[kix];
|
||||
|
||||
d = a[item_name].compare(b[item_name]);
|
||||
|
||||
if (d != 0)
|
||||
@@ -2764,28 +2755,6 @@ bool category::operator==(const category &rhs) const
|
||||
if (not rowEqual(ra, rb))
|
||||
return false;
|
||||
|
||||
std::vector<std::string> missingA, missingB, different;
|
||||
|
||||
for (auto &tt : item_names)
|
||||
{
|
||||
std::string item_name;
|
||||
compType compare;
|
||||
|
||||
std::tie(item_name, compare) = tt;
|
||||
|
||||
// make it an option to compare unapplicable to empty or something
|
||||
|
||||
// auto ta = ra[item_name].text();
|
||||
// if (ta == "." or ta == "?")
|
||||
// ta = "";
|
||||
// auto tb = rb[item_name].text();
|
||||
// if (tb == "." or tb == "?")
|
||||
// tb = "";
|
||||
|
||||
if (ra[item_name].compare(rb[item_name]) != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
++ai;
|
||||
++bi;
|
||||
}
|
||||
|
||||
@@ -799,8 +799,8 @@ void compound_factory::report_missing_compound(std::string_view compound_id)
|
||||
std::clog << "\n"
|
||||
<< cif::coloured("Configuration error:", white, red) << "\n\n"
|
||||
<< "The attempt to retrieve compound information for " << std::quoted(compound_id) << " failed.\n\n"
|
||||
<< "This information is searched for in a CCD file called components.cif or\n"
|
||||
<< "components.cif.gz which should be located in one of the following directories:\n\n";
|
||||
<< "This information is searched for in a CCD file called components.cif\n"
|
||||
<< "which should be located in one of the following directories:\n\n";
|
||||
|
||||
cif::list_data_directories(std::clog);
|
||||
|
||||
|
||||
@@ -129,9 +129,9 @@ namespace detail
|
||||
m_item_ix = *ix;
|
||||
m_icase = is_item_type_uchar(c, m_item_name);
|
||||
|
||||
if (c.get_cat_validator() != nullptr and
|
||||
c.key_item_indices().contains(m_item_ix) and
|
||||
c.key_item_indices().size() == 1)
|
||||
if (auto cv = c.get_cat_validator();
|
||||
cv != nullptr and cv->m_keys.size() == 1 and
|
||||
cv->m_keys.front() == m_item_name)
|
||||
{
|
||||
m_single_hit = c[{ { m_item_name, m_value } }];
|
||||
}
|
||||
|
||||
@@ -28,14 +28,12 @@
|
||||
#include "cif++/item.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <format>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
#include <regex>
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
*/
|
||||
|
||||
#include "cif++/cif++.hpp"
|
||||
#include "cif++/text.hpp"
|
||||
#include "cif++/validate.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
@@ -103,7 +105,19 @@ class dictionary_parser : public parser
|
||||
error("Undefined category '" + iv.first);
|
||||
|
||||
for (auto &v : iv.second)
|
||||
{
|
||||
// enums, make lower case if needed
|
||||
auto tv = v.m_type;
|
||||
if (tv and tv->m_primitive_type == DDL_PrimitiveType::UChar)
|
||||
{
|
||||
std::set<std::string> es;
|
||||
for (auto &e : v.m_enums)
|
||||
es.emplace(cif::to_lower_copy(e));
|
||||
std::swap(es, v.m_enums);
|
||||
}
|
||||
|
||||
const_cast<category_validator *>(cv)->add_item_validator(std::move(v));
|
||||
}
|
||||
}
|
||||
|
||||
// check all item validators for having a typeValidator
|
||||
@@ -275,9 +289,11 @@ class dictionary_parser : public parser
|
||||
if (typeCode.has_value())
|
||||
tv = m_validator.get_validator_for_type(*typeCode);
|
||||
|
||||
iset ess;
|
||||
std::set<std::string> ess;
|
||||
for (auto e : dict["item_enumeration"])
|
||||
ess.insert(e["value"].get<std::string>());
|
||||
for (auto e : dict["pdbx_item_enumeration"])
|
||||
ess.insert(e["value"].get<std::string>());
|
||||
|
||||
std::string defaultValue;
|
||||
if (auto &cat = dict["item_default"]; not cat.empty())
|
||||
|
||||
@@ -239,7 +239,12 @@ void item_value::cast_to_int()
|
||||
{
|
||||
auto s = sv();
|
||||
int64_t v;
|
||||
auto [ptr, ec] = cif::from_chars(s.data(), s.data() + s.size(), v);
|
||||
|
||||
auto sp = s.data();
|
||||
if (*sp == '+')
|
||||
++sp;
|
||||
|
||||
auto [ptr, ec] = cif::from_chars(sp, s.data() + s.size(), v);
|
||||
if (ec != std::errc{})
|
||||
throw std::system_error(std::make_error_code(ec), "attempt to cast value to integer failed");
|
||||
if (ptr != s.data() + s.size())
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "cif++/cif++.hpp"
|
||||
#include "cif++/item.hpp"
|
||||
#include "cif++/row.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
@@ -76,10 +77,10 @@ void atom::atom_impl::moveTo(const point &p)
|
||||
|
||||
// const compound *compound() const;
|
||||
|
||||
const item_value &atom::atom_impl::get_property(std::string_view name) const
|
||||
const item_handle atom::atom_impl::get_property(std::string_view name) const
|
||||
{
|
||||
if (auto rh = row(); rh)
|
||||
return rh[name].value();
|
||||
return rh[name];
|
||||
throw std::runtime_error(std::format("Missing property {} for atom", name));
|
||||
}
|
||||
|
||||
@@ -1911,7 +1912,13 @@ void structure::change_residue(residue &res, const std::string &newCompound,
|
||||
for (const auto &[a1, a2] : remappedAtoms)
|
||||
{
|
||||
auto i = std::ranges::find_if(atoms, [id = a1](const atom &a)
|
||||
{ return a.get_label_atom_id() == id; });
|
||||
{
|
||||
if (a.get_row())
|
||||
{
|
||||
auto ih = a.get_property_value("label_atom_id");
|
||||
return not ih.empty() and ih.get<std::string>() == id;
|
||||
}
|
||||
return false; });
|
||||
if (i == atoms.end())
|
||||
{
|
||||
if (VERBOSE >= 0)
|
||||
@@ -2170,29 +2177,50 @@ std::string structure::create_non_poly(const std::string &entity_id, const std::
|
||||
{
|
||||
auto atom_id = atom_site.get_unique_id("");
|
||||
|
||||
auto row = atom_site.emplace({ { "group_PDB", atom.get_property("group_PDB") },
|
||||
{ "id", atom_id },
|
||||
{ "type_symbol", atom.get_property_value("type_symbol") },
|
||||
{ "label_atom_id", atom.get_property_value("label_atom_id") },
|
||||
{ "label_alt_id", atom.get_property_value("label_alt_id") },
|
||||
{ "label_comp_id", comp_id },
|
||||
{ "label_asym_id", asym_id },
|
||||
{ "label_entity_id", entity_id },
|
||||
{ "label_seq_id", nullptr },
|
||||
{ "pdbx_PDB_ins_code", "" },
|
||||
{ "Cartn_x", atom.get_property_value("Cartn_x") },
|
||||
{ "Cartn_y", atom.get_property_value("Cartn_y") },
|
||||
{ "Cartn_z", atom.get_property_value("Cartn_z") },
|
||||
{ "occupancy", atom.get_property_value("occupancy") },
|
||||
{ "B_iso_or_equiv", atom.get_property_value("B_iso_or_equiv") },
|
||||
{ "pdbx_formal_charge", atom.get_property_value("pdbx_formal_charge") },
|
||||
{ "auth_seq_id", "1" },
|
||||
{ "auth_comp_id", comp_id },
|
||||
{ "auth_asym_id", asym_id },
|
||||
{ "auth_atom_id", atom.get_property_value("label_atom_id") },
|
||||
{ "pdbx_PDB_model_num", 1 } });
|
||||
cif::row_initializer data //
|
||||
{
|
||||
{ "id", atom_id },
|
||||
{ "label_comp_id", comp_id },
|
||||
{ "label_asym_id", asym_id },
|
||||
{ "label_entity_id", entity_id },
|
||||
{ "label_seq_id", cif::item_value_type::INAPPLICABLE },
|
||||
{ "pdbx_PDB_ins_code", cif::item_value_type::MISSING },
|
||||
{ "auth_seq_id", "1" },
|
||||
{ "auth_comp_id", comp_id },
|
||||
{ "auth_asym_id", asym_id },
|
||||
{ "pdbx_PDB_model_num", m_model_nr } //
|
||||
};
|
||||
|
||||
for (auto item : std::initializer_list<std::string>{
|
||||
// clang-format off
|
||||
"group_PDB",
|
||||
"type_symbol",
|
||||
"label_atom_id",
|
||||
"label_alt_id",
|
||||
"auth_atom_id",
|
||||
"Cartn_x",
|
||||
"Cartn_y",
|
||||
"Cartn_z",
|
||||
"occupancy",
|
||||
"B_iso_or_equiv",
|
||||
"pdbx_formal_charge"
|
||||
// clang-format on
|
||||
})
|
||||
{
|
||||
auto v = atom.get_property_value(item);
|
||||
if (not v.empty())
|
||||
data.push_back({ item, v.value() });
|
||||
else
|
||||
data.push_back({ item, cif::item_value_type::INAPPLICABLE });
|
||||
}
|
||||
|
||||
auto row = atom_site.emplace(std::move(data));
|
||||
|
||||
auto &newAtom = emplace_atom(std::make_shared<atom::atom_impl>(m_db, atom_id));
|
||||
|
||||
if (newAtom.get_property_value("auth_atom_id").empty() and not newAtom.get_property_value("label_atom_id").empty())
|
||||
newAtom.set_property("auth_atom_id", newAtom.get_property("label_atom_id"));
|
||||
|
||||
res.add_atom(newAtom);
|
||||
}
|
||||
|
||||
@@ -2208,7 +2236,7 @@ std::string structure::create_non_poly(const std::string &entity_id, const std::
|
||||
{ "pdb_mon_id", comp_id },
|
||||
{ "auth_mon_id", comp_id },
|
||||
{ "pdb_strand_id", asym_id },
|
||||
{ "pdb_ins_code", nullptr },
|
||||
{ "pdb_ins_code", cif::item_value_type::MISSING },
|
||||
});
|
||||
|
||||
return asym_id;
|
||||
@@ -2244,12 +2272,14 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector
|
||||
|
||||
atom.set_value_if_empty({ "group_PDB", "HETATM" });
|
||||
atom.set_value_if_empty({ "label_comp_id", comp_id });
|
||||
atom.set_value_if_empty({ "label_seq_id", nullptr });
|
||||
atom.set_value_if_empty({ "label_seq_id", cif::item_value_type::INAPPLICABLE });
|
||||
atom.set_value_if_empty({ "auth_comp_id", comp_id });
|
||||
atom.set_value_if_empty({ "auth_seq_id", "1" });
|
||||
atom.set_value_if_empty({ "pdbx_PDB_model_num", 1 });
|
||||
atom.set_value_if_empty({ "label_alt_id", "" });
|
||||
atom.set_value_if_empty({ "pdbx_PDB_model_num", m_model_nr });
|
||||
atom.set_value_if_empty({ "label_alt_id", cif::item_value_type::INAPPLICABLE });
|
||||
atom.set_value_if_empty({ "occupancy", { 1.0, 2 } });
|
||||
atom.set_value_if_empty({ "pdbx_formal_charge", cif::item_value_type::MISSING });
|
||||
atom.set_value_if_empty({ "pdbx_tls_group_id", cif::item_value_type::MISSING });
|
||||
|
||||
auto row = atom_site.emplace(atom.begin(), atom.end());
|
||||
|
||||
@@ -2299,7 +2329,7 @@ std::string structure::create_non_poly(const std::string &compound_id, bool skip
|
||||
{ "Cartn_x", ax },
|
||||
{ "Cartn_y", ay },
|
||||
{ "Cartn_z", az },
|
||||
{ "B_iso_or_equiv", 30.00 } });
|
||||
{ "B_iso_or_equiv", { 30.00, 2 } } });
|
||||
}
|
||||
|
||||
return create_non_poly(create_non_poly_entity(compound_id), atoms);
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
#include "cif++/cif++.hpp"
|
||||
#include "cif++/utilities.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
@@ -635,15 +636,28 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
|
||||
if (result == CIFToken::VALUE_NUMERIC_INTEGER)
|
||||
{
|
||||
// Avoid interpreting phone numbers as integers, TODO: check if this is an issue
|
||||
auto [ptr, ec] = from_chars(m_token_buffer.data(), m_token_buffer.data() + m_token_buffer.size(), m_token_value_int);
|
||||
if (ec != std::errc{})
|
||||
error("Invalid integer value: " + std::make_error_code(ec).message());
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
std::clog << "Invalid integer value: " << std::make_error_code(ec).message() << '\n';
|
||||
|
||||
result = CIFToken::VALUE_CHARSTRING;
|
||||
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
|
||||
}
|
||||
}
|
||||
else if (result == CIFToken::VALUE_NUMERIC_FLOAT)
|
||||
{
|
||||
auto [ptr, ec] = from_chars(m_token_buffer.data(), m_token_buffer.data() + m_token_buffer.size(), m_token_value_float);
|
||||
if (ec != std::errc{})
|
||||
error("Invalid integer value: " + std::make_error_code(ec).message());
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
std::clog << "Invalid floating point value: " << std::make_error_code(ec).message() << '\n';
|
||||
|
||||
result = CIFToken::VALUE_CHARSTRING;
|
||||
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -1811,13 +1811,10 @@ void WriteRemark3Phenix(std::ostream &pdbFile, const datablock &db)
|
||||
void WriteRemark3XPlor(std::ostream &pdbFile, const datablock &db)
|
||||
{
|
||||
auto refine = db["refine"].front();
|
||||
auto ls_shell = db["refine_ls_shell"].front();
|
||||
auto hist = db["refine_hist"].front();
|
||||
auto reflns = db["reflns"].front();
|
||||
auto analyze = db["refine_analyze"].front();
|
||||
auto &ls_restr = db["refine_ls_restr"];
|
||||
auto ls_restr_ncs = db["refine_ls_restr_ncs"].front();
|
||||
auto pdbx_xplor_file = db["pdbx_xplor_file"].front();
|
||||
|
||||
pdbFile << RM3("") << '\n'
|
||||
<< RM3(" DATA USED IN REFINEMENT.") << '\n'
|
||||
@@ -1837,7 +1834,11 @@ void WriteRemark3XPlor(std::ostream &pdbFile, const datablock &db)
|
||||
<< RM3(" FREE R VALUE : ", 7, 3) << Ff(refine, "ls_R_factor_R_free") << '\n'
|
||||
<< RM3(" FREE R VALUE TEST SET SIZE (%) : ", 7, 3) << Ff(refine, "ls_percent_reflns_R_free") << '\n'
|
||||
<< RM3(" FREE R VALUE TEST SET COUNT : ", 12, 6) << Fi(refine, "ls_number_reflns_R_free") << '\n'
|
||||
<< RM3(" ESTIMATED ERROR OF FREE R VALUE : ", 7, 3) << Ff(refine, "ls_R_factor_R_free_error") << '\n'
|
||||
<< RM3(" ESTIMATED ERROR OF FREE R VALUE : ", 7, 3) << Ff(refine, "ls_R_factor_R_free_error") << '\n';
|
||||
if (not db["refine_ls_shell"].empty())
|
||||
{
|
||||
auto ls_shell = db["refine_ls_shell"].front();
|
||||
pdbFile
|
||||
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" FIT IN THE HIGHEST RESOLUTION BIN.") << '\n'
|
||||
@@ -1850,60 +1851,68 @@ void WriteRemark3XPlor(std::ostream &pdbFile, const datablock &db)
|
||||
<< RM3(" BIN FREE R VALUE : ", 7, 3) << Ff(ls_shell, "R_factor_R_free") << '\n'
|
||||
<< RM3(" BIN FREE R VALUE TEST SET SIZE (%) : ", 5, 1) << Ff(ls_shell, "percent_reflns_R_free") << '\n'
|
||||
<< RM3(" BIN FREE R VALUE TEST SET COUNT : ", 12, 6) << Fi(ls_shell, "number_reflns_R_free") << '\n'
|
||||
<< RM3(" ESTIMATED ERROR OF BIN FREE R VALUE : ", 7, 3) << Ff(ls_shell, "R_factor_R_free_error") << '\n'
|
||||
<< RM3(" ESTIMATED ERROR OF BIN FREE R VALUE : ", 7, 3) << Ff(ls_shell, "R_factor_R_free_error") << '\n';
|
||||
}
|
||||
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" NUMBER OF NON-HYDROGEN ATOMS USED IN REFINEMENT.") << '\n'
|
||||
<< RM3(" PROTEIN ATOMS : ", 12, 6) << Fi(hist, "pdbx_number_atoms_protein") << '\n'
|
||||
<< RM3(" NUCLEIC ACID ATOMS : ", 12, 6) << Fi(hist, "pdbx_number_atoms_nucleic_acid") << '\n'
|
||||
<< RM3(" HETEROGEN ATOMS : ", 12, 6) << Fi(hist, "pdbx_number_atoms_ligand") << '\n'
|
||||
<< RM3(" SOLVENT ATOMS : ", 12, 6) << Fi(hist, "number_atoms_solvent") << '\n'
|
||||
pdbFile
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" NUMBER OF NON-HYDROGEN ATOMS USED IN REFINEMENT.") << '\n'
|
||||
<< RM3(" PROTEIN ATOMS : ", 12, 6) << Fi(hist, "pdbx_number_atoms_protein") << '\n'
|
||||
<< RM3(" NUCLEIC ACID ATOMS : ", 12, 6) << Fi(hist, "pdbx_number_atoms_nucleic_acid") << '\n'
|
||||
<< RM3(" HETEROGEN ATOMS : ", 12, 6) << Fi(hist, "pdbx_number_atoms_ligand") << '\n'
|
||||
<< RM3(" SOLVENT ATOMS : ", 12, 6) << Fi(hist, "number_atoms_solvent") << '\n'
|
||||
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" B VALUES.") << '\n'
|
||||
<< RM3(" FROM WILSON PLOT (A**2) : ", 7, 2) << Ff(reflns, "B_iso_Wilson_estimate") << '\n'
|
||||
<< RM3(" MEAN B VALUE (OVERALL, A**2) : ", 7, 2) << Ff(refine, "B_iso_mean") << '\n'
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" B VALUES.") << '\n'
|
||||
<< RM3(" FROM WILSON PLOT (A**2) : ", 7, 2) << Ff(reflns, "B_iso_Wilson_estimate") << '\n'
|
||||
<< RM3(" MEAN B VALUE (OVERALL, A**2) : ", 7, 2) << Ff(refine, "B_iso_mean") << '\n'
|
||||
|
||||
<< RM3(" OVERALL ANISOTROPIC B VALUE.") << '\n'
|
||||
<< RM3(" B11 (A**2) : ", -7, 2) << Ff(refine, "aniso_B[1][1]") << '\n'
|
||||
<< RM3(" B22 (A**2) : ", -7, 2) << Ff(refine, "aniso_B[2][2]") << '\n'
|
||||
<< RM3(" B33 (A**2) : ", -7, 2) << Ff(refine, "aniso_B[3][3]") << '\n'
|
||||
<< RM3(" B12 (A**2) : ", -7, 2) << Ff(refine, "aniso_B[1][2]") << '\n'
|
||||
<< RM3(" B13 (A**2) : ", -7, 2) << Ff(refine, "aniso_B[1][3]") << '\n'
|
||||
<< RM3(" B23 (A**2) : ", -7, 2) << Ff(refine, "aniso_B[2][3]") << '\n'
|
||||
<< RM3(" OVERALL ANISOTROPIC B VALUE.") << '\n'
|
||||
<< RM3(" B11 (A**2) : ", -7, 2) << Ff(refine, "aniso_B[1][1]") << '\n'
|
||||
<< RM3(" B22 (A**2) : ", -7, 2) << Ff(refine, "aniso_B[2][2]") << '\n'
|
||||
<< RM3(" B33 (A**2) : ", -7, 2) << Ff(refine, "aniso_B[3][3]") << '\n'
|
||||
<< RM3(" B12 (A**2) : ", -7, 2) << Ff(refine, "aniso_B[1][2]") << '\n'
|
||||
<< RM3(" B13 (A**2) : ", -7, 2) << Ff(refine, "aniso_B[1][3]") << '\n'
|
||||
<< RM3(" B23 (A**2) : ", -7, 2) << Ff(refine, "aniso_B[2][3]") << '\n'
|
||||
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" ESTIMATED COORDINATE ERROR.") << '\n'
|
||||
<< RM3(" ESD FROM LUZZATI PLOT (A) : ", 7, 2) << Ff(analyze, "Luzzati_coordinate_error_obs") << '\n'
|
||||
<< RM3(" ESD FROM SIGMAA (A) : ", 7, 2) << Ff(analyze, "Luzzati_sigma_a_obs") << '\n'
|
||||
<< RM3(" LOW RESOLUTION CUTOFF (A) : ", 7, 2) << Ff(analyze, "Luzzati_d_res_low_obs") << '\n'
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" ESTIMATED COORDINATE ERROR.") << '\n'
|
||||
<< RM3(" ESD FROM LUZZATI PLOT (A) : ", 7, 2) << Ff(analyze, "Luzzati_coordinate_error_obs") << '\n'
|
||||
<< RM3(" ESD FROM SIGMAA (A) : ", 7, 2) << Ff(analyze, "Luzzati_sigma_a_obs") << '\n'
|
||||
<< RM3(" LOW RESOLUTION CUTOFF (A) : ", 7, 2) << Ff(analyze, "Luzzati_d_res_low_obs") << '\n'
|
||||
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" CROSS-VALIDATED ESTIMATED COORDINATE ERROR.") << '\n'
|
||||
<< RM3(" ESD FROM C-V LUZZATI PLOT (A) : ", 7, 2) << Ff(analyze, "Luzzati_coordinate_error_free") << '\n'
|
||||
<< RM3(" ESD FROM C-V SIGMAA (A) : ", 7, 2) << Ff(analyze, "Luzzati_sigma_a_free") << '\n'
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" CROSS-VALIDATED ESTIMATED COORDINATE ERROR.") << '\n'
|
||||
<< RM3(" ESD FROM C-V LUZZATI PLOT (A) : ", 7, 2) << Ff(analyze, "Luzzati_coordinate_error_free") << '\n'
|
||||
<< RM3(" ESD FROM C-V SIGMAA (A) : ", 7, 2) << Ff(analyze, "Luzzati_sigma_a_free") << '\n'
|
||||
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" RMS DEVIATIONS FROM IDEAL VALUES.") << '\n'
|
||||
<< RM3(" BOND LENGTHS (A) : ", 7, 3) << Ff(ls_restr, key("type") == "x_bond_d", "dev_ideal") << '\n'
|
||||
<< RM3(" BOND ANGLES (DEGREES) : ", 7, 2) << Ff(ls_restr, key("type") == "x_angle_deg", "dev_ideal") << '\n'
|
||||
<< RM3(" DIHEDRAL ANGLES (DEGREES) : ", 7, 2) << Ff(ls_restr, key("type") == "x_dihedral_angle_d", "dev_ideal") << '\n'
|
||||
<< RM3(" IMPROPER ANGLES (DEGREES) : ", 7, 2) << Ff(ls_restr, key("type") == "x_improper_angle_d", "dev_ideal") << '\n'
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" RMS DEVIATIONS FROM IDEAL VALUES.") << '\n'
|
||||
<< RM3(" BOND LENGTHS (A) : ", 7, 3) << Ff(ls_restr, key("type") == "x_bond_d", "dev_ideal") << '\n'
|
||||
<< RM3(" BOND ANGLES (DEGREES) : ", 7, 2) << Ff(ls_restr, key("type") == "x_angle_deg", "dev_ideal") << '\n'
|
||||
<< RM3(" DIHEDRAL ANGLES (DEGREES) : ", 7, 2) << Ff(ls_restr, key("type") == "x_dihedral_angle_d", "dev_ideal") << '\n'
|
||||
<< RM3(" IMPROPER ANGLES (DEGREES) : ", 7, 2) << Ff(ls_restr, key("type") == "x_improper_angle_d", "dev_ideal") << '\n'
|
||||
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" ISOTROPIC THERMAL MODEL : ") << Fs(refine, "pdbx_isotropic_thermal_model") << '\n'
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" ISOTROPIC THERMAL MODEL : ") << Fs(refine, "pdbx_isotropic_thermal_model") << '\n'
|
||||
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" ISOTROPIC THERMAL FACTOR RESTRAINTS. RMS SIGMA") << '\n'
|
||||
<< RM3(" MAIN-CHAIN BOND (A**2) : ", 6, 2) << Ff(ls_restr, key("type") == "x_mcbond_it", "dev_ideal") << SEP("; ", 6, 2)
|
||||
<< Ff(ls_restr, key("type") == "x_mcbond_it", "dev_ideal_target") << '\n'
|
||||
<< RM3(" MAIN-CHAIN ANGLE (A**2) : ", 6, 2) << Ff(ls_restr, key("type") == "x_mcangle_it", "dev_ideal") << SEP("; ", 6, 2)
|
||||
<< Ff(ls_restr, key("type") == "x_mcangle_it", "dev_ideal_target") << '\n'
|
||||
<< RM3(" SIDE-CHAIN BOND (A**2) : ", 6, 2) << Ff(ls_restr, key("type") == "x_scbond_it", "dev_ideal") << SEP("; ", 6, 2)
|
||||
<< Ff(ls_restr, key("type") == "x_scbond_it", "dev_ideal_target") << '\n'
|
||||
<< RM3(" SIDE-CHAIN ANGLE (A**2) : ", 6, 2) << Ff(ls_restr, key("type") == "x_scangle_it", "dev_ideal") << SEP("; ", 6, 2)
|
||||
<< Ff(ls_restr, key("type") == "x_scangle_it", "dev_ideal_target") << '\n'
|
||||
<< RM3("") << '\n'
|
||||
<< RM3("") << '\n'
|
||||
<< RM3(" ISOTROPIC THERMAL FACTOR RESTRAINTS. RMS SIGMA") << '\n'
|
||||
<< RM3(" MAIN-CHAIN BOND (A**2) : ", 6, 2) << Ff(ls_restr, key("type") == "x_mcbond_it", "dev_ideal") << SEP("; ", 6, 2)
|
||||
<< Ff(ls_restr, key("type") == "x_mcbond_it", "dev_ideal_target") << '\n'
|
||||
<< RM3(" MAIN-CHAIN ANGLE (A**2) : ", 6, 2) << Ff(ls_restr, key("type") == "x_mcangle_it", "dev_ideal") << SEP("; ", 6, 2)
|
||||
<< Ff(ls_restr, key("type") == "x_mcangle_it", "dev_ideal_target") << '\n'
|
||||
<< RM3(" SIDE-CHAIN BOND (A**2) : ", 6, 2) << Ff(ls_restr, key("type") == "x_scbond_it", "dev_ideal") << SEP("; ", 6, 2)
|
||||
<< Ff(ls_restr, key("type") == "x_scbond_it", "dev_ideal_target") << '\n'
|
||||
<< RM3(" SIDE-CHAIN ANGLE (A**2) : ", 6, 2) << Ff(ls_restr, key("type") == "x_scangle_it", "dev_ideal") << SEP("; ", 6, 2)
|
||||
<< Ff(ls_restr, key("type") == "x_scangle_it", "dev_ideal_target") << '\n'
|
||||
<< RM3("") << '\n';
|
||||
|
||||
if (not db["refine_ls_restr_ncs"].empty())
|
||||
{
|
||||
auto ls_restr_ncs = db["refine_ls_restr_ncs"].front();
|
||||
|
||||
pdbFile
|
||||
<< RM3(" NCS MODEL : ") << Fs(ls_restr_ncs, "ncs_model_details") << '\n'
|
||||
|
||||
<< RM3("") << '\n'
|
||||
@@ -1913,7 +1922,14 @@ void WriteRemark3XPlor(std::ostream &pdbFile, const datablock &db)
|
||||
<< RM3(" GROUP 1 POSITIONAL (A) : ", 4, 2) << Ff(ls_restr_ncs, "rms_dev_position") << SEP("; ", 6, 2)
|
||||
<< Ff(ls_restr_ncs, "weight_position") << SEP("; ", 6, 2) << '\n'
|
||||
<< RM3(" GROUP 1 B-FACTOR (A**2) : ", 4, 2) << Ff(ls_restr_ncs, "rms_dev_B_iso") << SEP("; ", 6, 2)
|
||||
<< Ff(ls_restr_ncs, "weight_B_iso") << SEP("; ", 6, 2) << '\n'
|
||||
<< Ff(ls_restr_ncs, "weight_B_iso") << SEP("; ", 6, 2) << '\n';
|
||||
}
|
||||
|
||||
if (not db["pdbx_xplor_file"].empty())
|
||||
{
|
||||
auto pdbx_xplor_file = db["pdbx_xplor_file"].front();
|
||||
|
||||
pdbFile
|
||||
|
||||
// TODO: using only files from serial_no 1 here
|
||||
<< RM3("") << '\n'
|
||||
@@ -1921,6 +1937,7 @@ void WriteRemark3XPlor(std::ostream &pdbFile, const datablock &db)
|
||||
<< RM3(" TOPOLOGY FILE 1 : ") << Fs(pdbx_xplor_file, "topol_file") << '\n'
|
||||
|
||||
<< RM3("") << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRemark3NuclSQ(std::ostream &pdbFile, const datablock &db)
|
||||
@@ -2258,25 +2275,28 @@ void WriteRemark200(std::ostream &pdbFile, const datablock &db)
|
||||
std::string iis = cifSoftware(db, eDataReduction);
|
||||
std::string dss = cifSoftware(db, eDataScaling);
|
||||
|
||||
auto source = diffrn_source["source"].get<std::string>();
|
||||
std::string synchrotron, type;
|
||||
|
||||
if (source.empty())
|
||||
synchrotron = "NULL";
|
||||
else if (iequals(source, "SYNCHROTRON"))
|
||||
std::string source, synchrotron, type;
|
||||
if (not diffrn_source.empty())
|
||||
{
|
||||
synchrotron = "Y";
|
||||
source = diffrn_source["pdbx_synchrotron_site"].get<std::string>();
|
||||
source = diffrn_source["source"].get<std::string>();
|
||||
|
||||
if (source.empty())
|
||||
source = "NULL";
|
||||
type = "NULL";
|
||||
}
|
||||
else
|
||||
{
|
||||
synchrotron = "N";
|
||||
type = diffrn_source["type"].get<std::string>();
|
||||
if (type.empty())
|
||||
synchrotron = "NULL";
|
||||
else if (iequals(source, "SYNCHROTRON"))
|
||||
{
|
||||
synchrotron = "Y";
|
||||
source = diffrn_source["pdbx_synchrotron_site"].get<std::string>();
|
||||
if (source.empty())
|
||||
source = "NULL";
|
||||
type = "NULL";
|
||||
}
|
||||
else
|
||||
{
|
||||
synchrotron = "N";
|
||||
type = diffrn_source["type"].get<std::string>();
|
||||
if (type.empty())
|
||||
type = "NULL";
|
||||
}
|
||||
}
|
||||
|
||||
if (source.empty())
|
||||
@@ -2343,7 +2363,7 @@ void WriteRemark200(std::ostream &pdbFile, const datablock &db)
|
||||
|
||||
for (auto &t : kTail)
|
||||
{
|
||||
auto s = t.r[t.field].get<std::string>();
|
||||
auto s = t.r.empty() ? "" : t.r[t.field].get<std::string>();
|
||||
|
||||
if (s.empty())
|
||||
{
|
||||
@@ -2384,6 +2404,9 @@ void WriteRemark280(std::ostream &pdbFile, const datablock &db)
|
||||
<< RM("MATTHEWS COEFFICIENT, VM (ANGSTROMS**3/DA): ", 6, 2) << Ff(exptl_crystal, "density_Matthews") << '\n'
|
||||
<< RM("") << '\n';
|
||||
|
||||
if (exptl_crystal_grow.empty())
|
||||
continue;
|
||||
|
||||
std::vector<std::string> conditions;
|
||||
auto add = [&conditions](const std::string c)
|
||||
{
|
||||
@@ -3341,9 +3364,9 @@ void WriteCrystallographic(std::ostream &pdbFile, const datablock &db)
|
||||
if (r)
|
||||
{
|
||||
auto symmetry = r["space_group_name_H-M"].get<std::string>();
|
||||
|
||||
|
||||
r = db["cell"].find_first(key("entry_id") == db.name());
|
||||
|
||||
|
||||
pdbFile << std::format("CRYST1{:9.3f}{:9.3f}{:9.3f}{:7.2f}{:7.2f}{:7.2f} {:<11.11s}{:4}", r["length_a"].get<double>(), r["length_b"].get<double>(), r["length_c"].get<double>(), r["angle_alpha"].get<double>(), r["angle_beta"].get<double>(), r["angle_gamma"].get<double>(), symmetry, r["Z_PDB"].get<int>()) << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2193,7 +2193,7 @@ void PDBFileParser::ParseRemarks()
|
||||
if (std::regex_match(line, m, rx))
|
||||
{
|
||||
models[0] = std::stoi(m[1].str());
|
||||
models[1] = stoi(m[2].str());
|
||||
models[1] = std::stoi(m[2].str());
|
||||
}
|
||||
else
|
||||
headerSeen = cif::contains(line, "RES C SSSEQI");
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
#include "cif++/cif++.hpp"
|
||||
#include "cif++/validate.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
@@ -759,51 +760,31 @@ void checkAtomAnisotropRecords(datablock &db)
|
||||
|
||||
void checkStructAsym(datablock &db)
|
||||
{
|
||||
auto &atom_site = db["atom_site"];
|
||||
// auto &atom_site = db["atom_site"];
|
||||
auto &struct_asym = db["struct_asym"];
|
||||
|
||||
if (struct_asym.empty())
|
||||
cql::connection conn(db);
|
||||
cql::transaction tx(conn);
|
||||
|
||||
for (const auto [asym_id, entity_id] : tx.stream<std::optional<std::string>, std::string>(
|
||||
"select distinct label_asym_id, label_entity_id from atom_site"))
|
||||
{
|
||||
for (const auto &[label_asym_id, entity_id] : atom_site.rows<std::string, std::string>("label_asym_id", "label_entity_id"))
|
||||
if (not asym_id)
|
||||
throw std::runtime_error("File contains atom_site records without a label_asym_id");
|
||||
|
||||
auto sa = struct_asym.find_first(key("id") == asym_id);
|
||||
if (sa)
|
||||
{
|
||||
if (label_asym_id.empty())
|
||||
throw std::runtime_error("File contains atom_site records without a label_asym_id");
|
||||
if (struct_asym.count(key("id") == label_asym_id) == 0)
|
||||
{
|
||||
struct_asym.emplace({
|
||||
// clang-format off
|
||||
{ "id", label_asym_id },
|
||||
{ "entity_id", entity_id }
|
||||
//clang-format on
|
||||
});
|
||||
}
|
||||
if (sa["entity_id"].empty())
|
||||
sa.assign("entity_id", entity_id, false, true);
|
||||
else if (sa.get<std::string>("entity_id") != entity_id)
|
||||
throw std::runtime_error("Inconsistent entity ID's in struct_asym");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto &[label_asym_id, entity_id] :
|
||||
atom_site.rows<std::string, std::string>("label_asym_id", "label_entity_id"))
|
||||
else
|
||||
{
|
||||
if (label_asym_id.empty())
|
||||
throw std::runtime_error("File contains atom_site records without a label_asym_id");
|
||||
|
||||
auto sa = struct_asym.find_first(key("id") == label_asym_id);
|
||||
if (sa)
|
||||
{
|
||||
if (sa["entity_id"].empty())
|
||||
sa.assign("entity_id", entity_id, false, true);
|
||||
else if (sa.get<std::string>("entity_id") != entity_id)
|
||||
throw std::runtime_error("Inconsistent entity ID's in struct_asym");
|
||||
}
|
||||
else
|
||||
{
|
||||
struct_asym.emplace({
|
||||
// clang-format off
|
||||
{ "id", label_asym_id },
|
||||
{ "entity_id", entity_id }
|
||||
//clang-format on
|
||||
});
|
||||
}
|
||||
struct_asym.emplace({ //
|
||||
{ "id", asym_id },
|
||||
{ "entity_id", entity_id } });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1121,20 +1102,13 @@ void createPdbxPolySeqScheme(datablock &db)
|
||||
|
||||
auto &atom_site = db["atom_site"];
|
||||
auto &entity_poly = db["entity_poly"];
|
||||
// auto &entity_poly_seq = db["entity_poly_seq"];
|
||||
auto &entity_poly_seq = db["entity_poly_seq"];
|
||||
auto &struct_asym = db["struct_asym"];
|
||||
auto &pdbx_poly_seq_scheme = db["pdbx_poly_seq_scheme"];
|
||||
|
||||
cql::connection conn(db);
|
||||
cql::transaction tx(conn);
|
||||
|
||||
if (not pdbx_poly_seq_scheme.empty() and tx.exec(R"(
|
||||
select a.label_entity_id, a.label_seq_id, a.label_comp_id from pdbx_poly_seq_scheme a where a.label_entity_id in (select id from entity where type = 'polymer')
|
||||
except
|
||||
select b.entity_id, b.num, b.mon_id from entity_poly_seq b where b.entity_id in (select id from entity where type = 'polymer');
|
||||
)").empty())
|
||||
return;
|
||||
|
||||
using namespace literals;
|
||||
|
||||
// Recreate it
|
||||
@@ -1152,30 +1126,45 @@ void createPdbxPolySeqScheme(datablock &db)
|
||||
}
|
||||
}
|
||||
|
||||
std::string last_entity_id, last_asym_id;
|
||||
std::optional<int> last_seq_id;
|
||||
|
||||
for (auto col : { "label_asym_id", "label_entity_id", "label_seq_id", "label_comp_id", "auth_seq_id", "auth_comp_id", "pdbx_PDB_ins_code"})
|
||||
for (auto col : { "label_asym_id", "label_entity_id", "label_seq_id", "label_comp_id", "auth_seq_id", "auth_comp_id", "pdbx_PDB_ins_code" })
|
||||
atom_site.add_item(col);
|
||||
|
||||
// entity_id, mon_id, num, asym_id
|
||||
using key = std::tuple<std::string, std::string, int, std::string>;
|
||||
|
||||
// auth_seq_num, auth_mon_id, ins_code
|
||||
using value = std::tuple<std::string, std::string, std::optional<std::string>>;
|
||||
|
||||
std::map<key, value> data;
|
||||
|
||||
for (const auto [entity_id, mon_id, num, asym_id, auth_seq_num, auth_mon_id, ins_code] :
|
||||
tx.stream<std::string, std::string, int, std::string, std::string, std::string, std::optional<std::string>>(
|
||||
R"(
|
||||
select distinct label_entity_id, label_comp_id, label_seq_id, label_asym_id, auth_seq_id, auth_comp_id, pdbx_PDB_ins_code from atom_site
|
||||
where label_entity_id in (select id from entity where type = 'polymer')
|
||||
)"))
|
||||
{
|
||||
assert(not data.contains(key{ entity_id, mon_id, num, asym_id }));
|
||||
data.emplace(key{ entity_id, mon_id, num, asym_id }, value{ auth_seq_num, auth_mon_id, ins_code });
|
||||
}
|
||||
|
||||
std::string last_asym_id;
|
||||
int last_seq_id = -1;
|
||||
|
||||
for (auto entity_id : entity_poly.rows<std::string>("entity_id"))
|
||||
{
|
||||
for (auto asym_id : struct_asym.find<std::string>("entity_id"_key == entity_id, "id"))
|
||||
{
|
||||
for (auto &&[seq_id, comp_id, label_asym_id, auth_seq_id, auth_comp_id, pdb_ins_code] :
|
||||
tx.stream<std::optional<int>, std::string, std::optional<std::string>, std::string, std::string, std::optional<std::string>>(
|
||||
R"(select distinct b.num, b.mon_id, label_asym_id, auth_seq_id, auth_comp_id, pdbx_PDB_ins_code
|
||||
from entity_poly_seq b left join atom_site a on a.label_entity_id = b.entity_id and a.label_seq_id = b.num and a.label_comp_id = b.mon_id
|
||||
where b.entity_id=')" + entity_id + R"(' order by b.num;)"))
|
||||
for (const auto [mon_id, seq_id] : entity_poly_seq.find<std::string, int>("entity_id"_key == entity_id, "mon_id", "num"))
|
||||
{
|
||||
// Should be fixed in the SQL statement
|
||||
if (label_asym_id.has_value() and *label_asym_id != asym_id)
|
||||
continue;
|
||||
std::optional<std::string> auth_seq_num, auth_mon_id, ins_code;
|
||||
|
||||
if (seq_id.has_value() and *seq_id == 0)
|
||||
seq_id.reset();
|
||||
if (auto i = data.find(key{ entity_id, mon_id, seq_id, asym_id }); i != data.end())
|
||||
{
|
||||
std::tie(auth_seq_num, auth_mon_id, ins_code) = i->second;
|
||||
}
|
||||
|
||||
std::string hetero = (entity_id == last_entity_id and asym_id == last_asym_id and seq_id == last_seq_id) ? "y" : "n";
|
||||
std::string hetero = (asym_id == last_asym_id and seq_id == last_seq_id) ? "y" : "n";
|
||||
|
||||
if (hetero == "y")
|
||||
pdbx_poly_seq_scheme.back().assign("hetero", "y", false);
|
||||
@@ -1184,20 +1173,40 @@ void createPdbxPolySeqScheme(datablock &db)
|
||||
{ "asym_id", asym_id },
|
||||
{ "entity_id", entity_id },
|
||||
{ "seq_id", seq_id },
|
||||
{ "mon_id", comp_id },
|
||||
{ "ndb_seq_num", seq_id.value_or(0) },
|
||||
{ "pdb_seq_num", auth_seq_id },
|
||||
{ "auth_seq_num", auth_seq_id },
|
||||
{ "pdb_mon_id", auth_comp_id },
|
||||
{ "auth_mon_id", auth_comp_id },
|
||||
{ "mon_id", mon_id },
|
||||
{ "ndb_seq_num", seq_id },
|
||||
{ "pdb_seq_num", auth_seq_num },
|
||||
{ "auth_seq_num", auth_seq_num },
|
||||
{ "pdb_mon_id", auth_mon_id },
|
||||
{ "auth_mon_id", auth_mon_id },
|
||||
{ "pdb_strand_id", asym_id_to_pdb_strand_map[asym_id] },
|
||||
{ "pdb_ins_code", pdb_ins_code },
|
||||
{ "pdb_ins_code", ins_code },
|
||||
{ "hetero", hetero } });
|
||||
|
||||
last_entity_id = entity_id;
|
||||
last_asym_id = asym_id;
|
||||
last_seq_id = seq_id;
|
||||
}
|
||||
// std::string hetero = (asym_id == last_asym_id and seq_id == last_seq_id) ? "y" : "n";
|
||||
|
||||
// if (hetero == "y")
|
||||
// pdbx_poly_seq_scheme.back().assign("hetero", "y", false);
|
||||
|
||||
// pdbx_poly_seq_scheme.emplace({ //
|
||||
// { "asym_id", asym_id },
|
||||
// { "entity_id", entity_id },
|
||||
// { "seq_id", seq_id },
|
||||
// { "mon_id", comp_id },
|
||||
// { "ndb_seq_num", seq_id },
|
||||
// { "pdb_seq_num", auth_seq_id },
|
||||
// { "auth_seq_num", auth_seq_id },
|
||||
// { "pdb_mon_id", auth_comp_id },
|
||||
// { "auth_mon_id", auth_comp_id },
|
||||
// { "pdb_strand_id", asym_id_to_pdb_strand_map[asym_id] },
|
||||
// { "pdb_ins_code", pdb_ins_code },
|
||||
// { "hetero", hetero } });
|
||||
|
||||
// last_asym_id = asym_id;
|
||||
// last_seq_id = seq_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1248,7 +1257,7 @@ void comparePolySeqSchemes(datablock &db)
|
||||
auto pdbx_range = pdbx_poly_seq_scheme.find(key("asym_id") == asym_id);
|
||||
|
||||
for (auto ndb_i = ndb_range.begin(), pdbx_i = pdbx_range.begin();
|
||||
ndb_i != ndb_range.end() or pdbx_i != pdbx_range.end(); ++ndb_i, ++pdbx_i)
|
||||
ndb_i != ndb_range.end() or pdbx_i != pdbx_range.end(); ++ndb_i, ++pdbx_i)
|
||||
{
|
||||
if (ndb_i == ndb_range.end() or pdbx_i == pdbx_range.end())
|
||||
{
|
||||
@@ -1304,8 +1313,7 @@ void createPdbxEntityNonpoly(datablock &db)
|
||||
pdbx_entity_nonpoly.emplace({ //
|
||||
{ "entity_id", entity_id },
|
||||
{ "name", "water" },
|
||||
{ "comp_id", comp_id }
|
||||
});
|
||||
{ "comp_id", comp_id } });
|
||||
else
|
||||
{
|
||||
auto c = cif::compound_factory::instance().create(comp_id);
|
||||
@@ -1317,8 +1325,7 @@ void createPdbxEntityNonpoly(datablock &db)
|
||||
pdbx_entity_nonpoly.emplace({ //
|
||||
{ "entity_id", entity_id },
|
||||
{ "name", name },
|
||||
{ "comp_id", comp_id }
|
||||
});
|
||||
{ "comp_id", comp_id } });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1334,7 +1341,9 @@ void createPdbxNonpolyScheme(datablock &db)
|
||||
auto &pdbx_nonpoly_scheme = db["pdbx_nonpoly_scheme"];
|
||||
auto &atom_site = db["atom_site"];
|
||||
|
||||
for (const auto &[entity_id, comp_id] : pdbx_entity_nonpoly.rows<std::string,std::string>("entity_id", "comp_id"))
|
||||
pdbx_nonpoly_scheme.clear();
|
||||
|
||||
for (const auto &[entity_id, comp_id] : pdbx_entity_nonpoly.rows<std::string, std::string>("entity_id", "comp_id"))
|
||||
{
|
||||
for (int ndb_nr = 1; auto row : atom_site.find("label_entity_id"_key == entity_id and "label_comp_id"_key == comp_id))
|
||||
{
|
||||
@@ -1345,7 +1354,7 @@ void createPdbxNonpolyScheme(datablock &db)
|
||||
|
||||
int num = row.get<int>("auth_seq_id");
|
||||
|
||||
pdbx_nonpoly_scheme.emplace({//
|
||||
pdbx_nonpoly_scheme.emplace({ //
|
||||
|
||||
{ "asym_id", row.get<std::string>("label_asym_id") },
|
||||
{ "entity_id", entity_id },
|
||||
@@ -1354,7 +1363,7 @@ void createPdbxNonpolyScheme(datablock &db)
|
||||
{ "pdb_seq_num", std::to_string(num) },
|
||||
{ "auth_seq_num", std::to_string(num) },
|
||||
{ "pdb_mon_id", row.get<std::string>("auth_comp_id") },
|
||||
{ "auth_mon_id", row.get<std::string>("auth_comp_id") },
|
||||
{ "auth_mon_id", row.get<std::string>("auth_comp_id") },
|
||||
{ "pdb_strand_id", row.get<std::string>("auth_asym_id") },
|
||||
{ "pdb_ins_code", row.get<std::string>("pdbx_PDB_ins_code") }
|
||||
|
||||
@@ -1654,7 +1663,7 @@ bool reconstruct_pdbx(file &file, const validator &validator)
|
||||
if (not iv)
|
||||
{
|
||||
// Drop this item
|
||||
cat.remove_item(item_name);
|
||||
// cat.remove_item(item_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1683,6 +1692,13 @@ bool reconstruct_pdbx(file &file, const validator &validator)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ec == cif::make_error_code(cif::validation_error::value_is_not_in_enumeration_list))
|
||||
{
|
||||
if (VERBOSE > 0)
|
||||
std::clog << "Value (" << std::quoted(row[ix].str()) << ") for item " << item_name << " in category " << cat.name() << " is not valid since it is not in the list of allowed values\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (VERBOSE > 0)
|
||||
std::clog << "Replacing value (" << std::quoted(row[ix].str()) << ") for item " << item_name << " in category " << cat.name() << " since it does not validate: " << ec.message() << "\n";
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cif++/symmetry.hpp"
|
||||
#include "cif++/cif++.hpp"
|
||||
#include "symop_table_data.hpp"
|
||||
|
||||
@@ -292,12 +293,7 @@ point spacegroup::operator()(const point &pt, const cell &c, sym_op symop) const
|
||||
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);
|
||||
return orthogonal(t(fractional(pt, c)), c);
|
||||
}
|
||||
|
||||
point spacegroup::inverse(const point &pt, const cell &c, sym_op symop) const
|
||||
@@ -311,13 +307,8 @@ point spacegroup::inverse(const point &pt, const cell &c, sym_op symop) const
|
||||
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);
|
||||
return orthogonal(it(fractional(pt, c)), c);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -462,9 +453,9 @@ std::tuple<float, point, sym_op> crystal::closest_symmetry_copy(point a, point b
|
||||
|
||||
a = orthogonal(fa, m_cell);
|
||||
|
||||
for (std::size_t i = 0; i < m_spacegroup.size(); ++i)
|
||||
for (uint8_t i = 0; std::cmp_less(i, m_spacegroup.size()); ++i)
|
||||
{
|
||||
sym_op s(static_cast<uint8_t>(i + 1));
|
||||
sym_op s(i + 1);
|
||||
auto &t = m_spacegroup[i];
|
||||
|
||||
auto fsb = t(fb);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "cif++/validate.hpp"
|
||||
|
||||
#include "cif++/cif++.hpp"
|
||||
#include "cif++/text.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <charconv>
|
||||
@@ -245,8 +246,17 @@ bool item_validator::validate_value(const item_value &value, std::error_code &ec
|
||||
ec = make_error_code(validation_error::value_does_not_match_rx);
|
||||
}
|
||||
|
||||
if (ec == std::errc{} and not m_enums.empty() and m_enums.count(value.str()) == 0)
|
||||
ec = make_error_code(validation_error::value_is_not_in_enumeration_list);
|
||||
if (ec == std::errc{} and not m_enums.empty())
|
||||
{
|
||||
bool valid =
|
||||
m_type->m_primitive_type == DDL_PrimitiveType::UChar ? //
|
||||
m_enums.contains(cif::to_lower_copy(value.sv()))
|
||||
: //
|
||||
m_enums.contains(std::string{ value.sv() });
|
||||
|
||||
if (not valid)
|
||||
ec = make_error_code(validation_error::value_is_not_in_enumeration_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -436,8 +446,8 @@ void validator::report_error(std::error_code ec, bool fatal) const
|
||||
std::cerr << ec.message() << '\n';
|
||||
}
|
||||
|
||||
void validator::report_error(std::error_code ec, std::string_view category,
|
||||
std::string_view item, bool fatal) const
|
||||
void validator::report_error(std::error_code ec, std::string value,
|
||||
std::string_view category, std::string_view item, bool fatal) const
|
||||
{
|
||||
if (m_strict or fatal)
|
||||
{
|
||||
@@ -448,10 +458,19 @@ void validator::report_error(std::error_code ec, std::string_view category,
|
||||
}
|
||||
|
||||
if (VERBOSE > 0)
|
||||
std::cerr << ec.message()
|
||||
<< "; category: " << std::quoted(category)
|
||||
<< " item: " << std::quoted(item)
|
||||
<< '\n';
|
||||
{
|
||||
if (value.empty())
|
||||
std::cerr << ec.message()
|
||||
<< "; category: " << std::quoted(category)
|
||||
<< " item: " << std::quoted(item)
|
||||
<< '\n';
|
||||
else
|
||||
std::cerr << ec.message()
|
||||
<< "; value: " << std::quoted(value)
|
||||
<< "; category: " << std::quoted(category)
|
||||
<< " item: " << std::quoted(item)
|
||||
<< '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void validator::fill_audit_conform(category &audit_conform) const
|
||||
|
||||
BIN
test/476d.cif.gz
Normal file
BIN
test/476d.cif.gz
Normal file
Binary file not shown.
@@ -443,8 +443,6 @@ TEST_CASE("test_alternates_1")
|
||||
const std::filesystem::path example(gTestDir / ".." / "examples" / "1cbs.cif.gz");
|
||||
cif::file file(example.string());
|
||||
|
||||
auto &db = file.front();
|
||||
|
||||
cif::mm::structure s(file);
|
||||
|
||||
for (auto atom : s.atoms())
|
||||
|
||||
2187
test/reconstruct/1cbs-seqscheme.cif
Normal file
2187
test/reconstruct/1cbs-seqscheme.cif
Normal file
File diff suppressed because it is too large
Load Diff
11498
test/reconstruct/9d3j_fixDMC.cif
Normal file
11498
test/reconstruct/9d3j_fixDMC.cif
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
#include "cif++/cif++.hpp"
|
||||
#include "cif++/symmetry.hpp"
|
||||
#include "test-main.hpp"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
@@ -35,7 +36,7 @@
|
||||
# pragma warning(disable : 4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
// #include <Eigen/Eigenvalues>
|
||||
#include <Eigen/Eigen>
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
@@ -229,102 +230,31 @@ TEST_CASE("dh_q_1")
|
||||
}
|
||||
}
|
||||
|
||||
/* TEST_CASE("m2q_0a")
|
||||
{
|
||||
for (std::size_t i = 0; i < cif::kSymopNrTableSize; ++i)
|
||||
{
|
||||
auto d = cif::kSymopNrTable[i].symop().data();
|
||||
|
||||
Eigen::Matrix3f rot;
|
||||
rot << static_cast<float>(d[0]), static_cast<float>(d[1]), static_cast<float>(d[2]), static_cast<float>(d[3]), static_cast<float>(d[4]), static_cast<float>(d[5]), static_cast<float>(d[6]), static_cast<float>(d[7]), static_cast<float>(d[8]);
|
||||
|
||||
// check to see if this matrix contains a true rotation
|
||||
if (rot * rot.transpose() != Eigen::Matrix3f::Identity() or rot.determinant() != 1)
|
||||
continue;
|
||||
|
||||
Eigen::Quaternionf qe(rot);
|
||||
|
||||
auto q = normalize(cif::quaternion{ qe.w(), qe.x(), qe.y(), qe.z() });
|
||||
|
||||
cif::point p1{ 1, 1, 1 };
|
||||
cif::point p2 = p1;
|
||||
p2.rotate(q);
|
||||
|
||||
cif::matrix3x3<float> rot_c({ static_cast<float>(d[0]),
|
||||
static_cast<float>(d[1]),
|
||||
static_cast<float>(d[2]),
|
||||
static_cast<float>(d[3]),
|
||||
static_cast<float>(d[4]),
|
||||
static_cast<float>(d[5]),
|
||||
static_cast<float>(d[6]),
|
||||
static_cast<float>(d[7]),
|
||||
static_cast<float>(d[8]) });
|
||||
|
||||
cif::point p3 = rot_c * p1;
|
||||
|
||||
CHECK_THAT(p2.m_x, Catch::Matchers::WithinRel(p3.m_x, 0.01f));
|
||||
CHECK_THAT(p2.m_y, Catch::Matchers::WithinRel(p3.m_y, 0.01f));
|
||||
CHECK_THAT(p2.m_z, Catch::Matchers::WithinRel(p3.m_z, 0.01f));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// "TEST_CASE(m2q_1")
|
||||
// TEST_CASE("m2q_1")
|
||||
// {
|
||||
// for (std::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::Matrix3f rot;
|
||||
|
||||
// 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
|
||||
// });
|
||||
// rot << d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8];
|
||||
|
||||
// auto &&[ev, em] = cif::eigen(m * (1/3.0f), false);
|
||||
|
||||
// std::size_t bestJ = 0;
|
||||
// float bestEV = -1;
|
||||
|
||||
// for (std::size_t j = 0; j < 4; ++j)
|
||||
// if (rot * rot.transpose() == Eigen::Matrix3f::Identity() and rot.determinant() == 1)
|
||||
// {
|
||||
// if (bestEV < ev[j])
|
||||
// {
|
||||
// bestEV = ev[j];
|
||||
// bestJ = j;
|
||||
// }
|
||||
// Eigen::Quaternionf qe(rot);
|
||||
// auto q = normalize(cif::quaternion{ qe.w(), qe.x(), qe.y(), qe.z() });
|
||||
|
||||
// cif::point p1{ 1, 1, 1 };
|
||||
// cif::point p2 = p1;
|
||||
// p2.rotate(q);
|
||||
|
||||
// auto p3 = rot * Eigen::Vector3f{ p1.m_x, p1.m_y, p1.m_z };
|
||||
|
||||
// CHECK_THAT(p2.m_x, Catch::Matchers::WithinRel(p3[0], 0.01f));
|
||||
// CHECK_THAT(p2.m_y, Catch::Matchers::WithinRel(p3[1], 0.01f));
|
||||
// CHECK_THAT(p2.m_z, Catch::Matchers::WithinRel(p3[2], 0.01f));
|
||||
// }
|
||||
|
||||
// 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;
|
||||
|
||||
// REQUIRE(p2.m_x == p3.m_x);
|
||||
// REQUIRE(p2.m_y == p3.m_y);
|
||||
// REQUIRE(p2.m_z == p3.m_z);
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -394,35 +324,6 @@ TEST_CASE("symm_4")
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("symm_4wvp_1")
|
||||
{
|
||||
using namespace cif::literals;
|
||||
|
||||
cif::file f(gTestDir / "4wvp.cif.gz");
|
||||
f.front().set_validator(cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
auto &db = f.front();
|
||||
cif::mm::structure s(db);
|
||||
|
||||
cif::crystal c(db);
|
||||
|
||||
cif::point p{ -78.722f, 98.528f, 11.994f };
|
||||
auto a = s.get_residue("A", 10, "").get_atom_by_atom_id("O");
|
||||
|
||||
auto sp1 = c.symmetry_copy(a.get_location(), "2_565"_symop);
|
||||
CHECK_THAT(sp1.m_x, Catch::Matchers::WithinAbs(p.m_x, 0.5f));
|
||||
CHECK_THAT(sp1.m_y, Catch::Matchers::WithinAbs(p.m_y, 0.5f));
|
||||
CHECK_THAT(sp1.m_z, Catch::Matchers::WithinAbs(p.m_z, 0.5f));
|
||||
|
||||
const auto &[d, sp2, so] = c.closest_symmetry_copy(p, a.get_location());
|
||||
|
||||
REQUIRE(d < 1);
|
||||
|
||||
CHECK_THAT(sp2.m_x, Catch::Matchers::WithinAbs(p.m_x, 0.5f));
|
||||
CHECK_THAT(sp2.m_y, Catch::Matchers::WithinAbs(p.m_y, 0.5f));
|
||||
CHECK_THAT(sp2.m_z, Catch::Matchers::WithinAbs(p.m_z, 0.5f));
|
||||
}
|
||||
|
||||
TEST_CASE("symm_2bi3_1")
|
||||
{
|
||||
cif::file f(gTestDir / "2bi3.cif.gz");
|
||||
@@ -538,6 +439,96 @@ TEST_CASE("symm_3bwh_1")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("symm_476d")
|
||||
{
|
||||
cif::file f(gTestDir / "476d.cif.gz");
|
||||
f.front().set_validator(cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
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 p1 = a1.get_location();
|
||||
auto p2 = a2.get_location();
|
||||
|
||||
cif::sym_op so1(symm1);
|
||||
cif::sym_op so2(symm2);
|
||||
|
||||
auto sa1 = c.symmetry_copy(p1, so1);
|
||||
auto sa2 = c.symmetry_copy(p2, so2);
|
||||
|
||||
CHECK_THAT(cif::distance(sa1, sa2), Catch::Matchers::WithinAbs(dist, 0.01f));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("symm-P_32_2_1_a")
|
||||
{
|
||||
cif::cell c{ 80, 80, 120, 90, 90, 120 };
|
||||
cif::spacegroup sg{ 154 };
|
||||
|
||||
cif::crystal crystal{ c, sg };
|
||||
|
||||
cif::point a{ 1, 90, 1 };
|
||||
cif::point p1{ 2, 2, 2 };
|
||||
|
||||
auto d = distance(a, p1);
|
||||
|
||||
auto [d2, p, so] = crystal.closest_symmetry_copy(a, p1);
|
||||
|
||||
std::cout << "d: " << d2 << " p: " << p << " so: " << so.string() << '\n';
|
||||
|
||||
auto p2 = crystal.symmetry_copy(p1, so);
|
||||
auto d3 = distance(p2, a);
|
||||
|
||||
CHECK_THAT(cif::distance(p2, p), Catch::Matchers::WithinAbs(0.f, 0.01f));
|
||||
CHECK_THAT(d3, Catch::Matchers::WithinAbs(d2, 0.01f));
|
||||
|
||||
CHECK(d2 <= d);
|
||||
}
|
||||
|
||||
TEST_CASE("symm-P_32_2_1")
|
||||
{
|
||||
cif::cell c{ 82.162, 82.162, 135.202, 90, 90, 120 };
|
||||
cif::spacegroup sg{ 154 };
|
||||
|
||||
cif::crystal crystal{ c, sg };
|
||||
|
||||
cif::point a{ 1.73727,89.1813,11.1388 };
|
||||
cif::point p1{ -8.98574, 50.3861, -11.6447 };
|
||||
|
||||
auto d = distance(a, p1);
|
||||
|
||||
auto [d2, p, so] = crystal.closest_symmetry_copy(a, p1);
|
||||
|
||||
std::cout << "d: " << d2 << " p: " << p << " so: " << so.string() << '\n';
|
||||
|
||||
auto p2 = crystal.symmetry_copy(p1, so);
|
||||
auto d3 = distance(p2, a);
|
||||
|
||||
CHECK_THAT(cif::distance(p2, p), Catch::Matchers::WithinAbs(0.f, 0.01f));
|
||||
CHECK_THAT(d3, Catch::Matchers::WithinAbs(d2, 0.01f));
|
||||
|
||||
CHECK(d2 <= d);
|
||||
}
|
||||
|
||||
TEST_CASE("volume_3bwh_1")
|
||||
{
|
||||
cif::file f(gTestDir / "1juh.cif.gz");
|
||||
|
||||
@@ -3664,5 +3664,63 @@ HETATM 2 O O . HOH A 1 . ? 10.518 -1.781 0 1 37.22 ? O HOH 2 D 1
|
||||
)");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("number-test-1")
|
||||
{
|
||||
auto data = R"(data_test
|
||||
_pdbx_contact_author.id 1
|
||||
_pdbx_contact_author.name_mi +98765432109
|
||||
)"_cf;
|
||||
|
||||
auto &db = data.front();
|
||||
db.load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
auto r = db["pdbx_contact_author"].front();
|
||||
CHECK(r["name_mi"].str() == "+98765432109");
|
||||
CHECK(r["name_mi"].get<int64_t>() == 98765432109);
|
||||
CHECK(r["name_mi"].get<double>() == 98765432109.0);
|
||||
}
|
||||
|
||||
TEST_CASE("number-test-2")
|
||||
{
|
||||
auto data = R"(data_test
|
||||
_pdbx_contact_author.id 1
|
||||
_pdbx_contact_author.name_mi '+98765432109'
|
||||
)"_cf;
|
||||
|
||||
auto &db = data.front();
|
||||
db.load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
auto r = db["pdbx_contact_author"].front();
|
||||
CHECK(r["name_mi"].str() == "+98765432109");
|
||||
CHECK(r["name_mi"].get<int64_t>() == 98765432109);
|
||||
CHECK(r["name_mi"].get<double>() == 98765432109.0);
|
||||
}// --------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("q-1")
|
||||
{
|
||||
auto data = R"(data_test
|
||||
_test.s
|
||||
;1234567890
|
||||
1234567890
|
||||
;
|
||||
)"_cf;
|
||||
|
||||
auto r = data.front()["test"].find(cif::key("s") == "1234567890\n1234567890");
|
||||
CHECK(r.size() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("large-int-1")
|
||||
{
|
||||
auto data = R"(data_test
|
||||
_entry.id 82E4475FF8B27F36
|
||||
)"_cf;
|
||||
|
||||
auto &db = data.front();
|
||||
db.load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
auto r = db["entry"].front();
|
||||
|
||||
CHECK(r["id"].get<std::string>() == "82E4475FF8B27F36");
|
||||
}
|
||||
Reference in New Issue
Block a user