Compare commits

...

14 Commits

Author SHA1 Message Date
Maarten L. Hekkelman
eaa5032e11 Fix drop_empty_items 2026-06-04 11:22:37 +02:00
Maarten L. Hekkelman
8400247674 Fix compare categories 2026-06-04 10:53:46 +02:00
Maarten L. Hekkelman
08e1b197ac Avoid out of bounds access 2026-06-03 08:37:33 +02:00
Maarten L. Hekkelman
20971e1ee9 Fix cif2pdb, somewhat... 2026-05-18 13:44:05 +02:00
Maarten L. Hekkelman
ce6a953eff Fix documentation rules 2026-05-18 12:59:03 +02:00
Maarten L. Hekkelman
87c20c26ec Fix error message 2026-05-18 12:51:26 +02:00
Maarten L. Hekkelman
025ad93d06 add more tests
don't use get_item_indices
version bump
2026-05-18 12:17:31 +02:00
Maarten L. Hekkelman
e98fe2608a Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2026-05-18 11:52:34 +02:00
Maarten L. Hekkelman
0399d99ca6 Fix in find_by_value 2026-05-18 11:52:06 +02:00
Maarten L. Hekkelman
71b24a678e Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2026-04-22 11:36:49 +02:00
Maarten L. Hekkelman
dc03cb6a70 Changed handling of numbers witth a leading plus character 2026-04-22 11:36:13 +02:00
Maarten L. Hekkelman
de9b33a918 Changed handling of numbers witth a leading plus character 2026-04-22 11:17:25 +02:00
Maarten L. Hekkelman
56c75490f2 Fix reconstruction, and reconstruction test files 2026-04-21 11:39:50 +02:00
Maarten L. Hekkelman
20695404c1 Leave unknown items untouched 2026-04-20 10:49:30 +02:00
17 changed files with 4788 additions and 4756 deletions

View File

@@ -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

View File

@@ -34,7 +34,7 @@ endif()
# set the project name
project(
libcifpp
VERSION 10.0.3
VERSION 10.0.4
LANGUAGES CXX C)
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

View File

@@ -1,5 +1,12 @@
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

View File

@@ -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())

View File

@@ -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");

View File

@@ -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;
@@ -759,14 +759,10 @@ void category::set_validator(const validator *v, datablock &db)
if (not v.is_number())
{
// Try cast the value to a number and throw in case of failure
try
{
if (auto sv = v.sv(); sv.find_first_of(".eE") == std::string_view::npos)
v.cast_to_int();
}
catch (...)
{
else
v.cast_to_float();
}
}
}
}
@@ -1616,7 +1612,6 @@ void category::update_value(row *row, uint16_t item, item_value value, bool upda
}
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
@@ -2204,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)
{
@@ -2726,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)
@@ -2810,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;
}

View File

@@ -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);

View File

@@ -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 } }];
}

View File

@@ -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())

View File

@@ -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", cif::item_value_type::INAPPLICABLE },
{ "pdbx_PDB_ins_code", cif::item_value_type::MISSING },
{ "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", m_model_nr } });
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);
}

View File

@@ -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;

View File

@@ -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';
}
}

View File

@@ -25,6 +25,7 @@
*/
#include "cif++/cif++.hpp"
#include "cif++/validate.hpp"
#include <algorithm>
#include <cstddef>
@@ -1662,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;
}
@@ -1691,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";

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

View File

@@ -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");
}