Commit 35fcc049 by Maarten L. Hekkelman

Moving cif::Structure back in as model

parent 9485bec2
......@@ -190,6 +190,8 @@ set(project_sources
${PROJECT_SOURCE_DIR}/src/compound.cpp
${PROJECT_SOURCE_DIR}/src/point.cpp
${PROJECT_SOURCE_DIR}/src/symmetry.cpp
${PROJECT_SOURCE_DIR}/src/model.cpp
)
set(project_headers
......@@ -212,6 +214,8 @@ set(project_headers
${PROJECT_SOURCE_DIR}/include/cif++/compound.hpp
${PROJECT_SOURCE_DIR}/include/cif++/point.hpp
${PROJECT_SOURCE_DIR}/include/cif++/symmetry.hpp
${PROJECT_SOURCE_DIR}/include/cif++/model.hpp
)
add_library(cifpp ${project_sources} ${project_headers} ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp)
......@@ -366,7 +370,7 @@ if(ENABLE_TESTING)
find_package(Boost REQUIRED headers)
list(APPEND CIFPP_tests unit-v2 unit-3d)
list(APPEND CIFPP_tests unit-v2 unit-3d format model rename-compound sugar)
foreach(CIFPP_TEST IN LISTS CIFPP_tests)
set(CIFPP_TEST "${CIFPP_TEST}-test")
......
......@@ -29,6 +29,10 @@
#include <cif++/utilities.hpp>
#include <cif++/file.hpp>
#include <cif++/parser.hpp>
#include <cif++/format.hpp>
#include <cif++/compound.hpp>
#include <cif++/point.hpp>
\ No newline at end of file
#include <cif++/point.hpp>
#include <cif++/symmetry.hpp>
#include <cif++/model.hpp>
\ No newline at end of file
......@@ -595,6 +595,10 @@ class category
// --------------------------------------------------------------------
void swap_item(size_t column_ix, row_handle &a, row_handle &b);
// --------------------------------------------------------------------
std::string m_name;
std::vector<item_column> m_columns;
const validator *m_validator = nullptr;
......
......@@ -111,7 +111,7 @@ class compound
const std::vector<compound_atom> &atoms() const { return m_atoms; }
const std::vector<compound_bond> &bonds() const { return m_bonds; }
compound_atom get_atom_by_id(const std::string &atom_id) const;
compound_atom get_atom_by_atom_id(const std::string &atom_id) const;
bool atoms_bonded(const std::string &atomId_1, const std::string &atomId_2) const;
// float atomBondValue(const std::string &atomId_1, const std::string &atomId_2) const;
......
......@@ -88,6 +88,10 @@ class datablock : public std::list<category>
return os;
}
// --------------------------------------------------------------------
bool operator==(const datablock &rhs) const;
private:
std::string m_name;
const validator *m_validator = nullptr;
......
......@@ -84,6 +84,8 @@ class file : public std::list<datablock>
void load_dictionary();
void load_dictionary(std::string_view name);
bool contains(std::string_view name) const;
datablock &operator[](std::string_view name);
const datablock &operator[](std::string_view name) const;
......
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <string>
/// \file format.hpp
/// File containing a basic reimplementation of boost::format
/// but then a bit more simplistic. Still this allowed me to move my code
/// from using boost::format to something without external dependency easily.
namespace cif
{
namespace detail
{
template <typename T>
struct to_varg
{
using type = T;
to_varg(const T &v)
: m_value(v)
{
}
type operator*() { return m_value; }
T m_value;
};
// template <>
// struct to_varg<char>
// {
// using type = const char *;
// to_varg(const char &v)
// : m_value({ v })
// {
// }
// type operator*() { return m_value.c_str(); }
// std::string m_value;
// };
template <>
struct to_varg<const char *>
{
using type = const char *;
to_varg(const char *v)
: m_value(v)
{
}
type operator*() { return m_value.c_str(); }
std::string m_value;
};
template <>
struct to_varg<std::string>
{
using type = const char *;
to_varg(const std::string &v)
: m_value(v)
{
}
type operator*() { return m_value.c_str(); }
std::string m_value;
};
} // namespace
template <typename... Args>
class format_plus_arg
{
public:
using args_vector_type = std::tuple<detail::to_varg<Args>...>;
using vargs_vector_type = std::tuple<typename detail::to_varg<Args>::type...>;
format_plus_arg(const format_plus_arg &) = delete;
format_plus_arg &operator=(const format_plus_arg &) = delete;
format_plus_arg(std::string_view fmt, Args... args)
: m_fmt(fmt)
, m_args(std::forward<Args>(args)...)
{
auto ix = std::make_index_sequence<sizeof...(Args)>();
copy_vargs(ix);
}
std::string str()
{
char buffer[1024];
std::string::size_type r = std::apply(snprintf, std::tuple_cat(std::make_tuple(buffer, sizeof(buffer), m_fmt.c_str()), m_vargs));
return { buffer, r };
}
friend std::ostream &operator<<(std::ostream &os, const format_plus_arg &f)
{
char buffer[1024];
std::string::size_type r = std::apply(snprintf, std::tuple_cat(std::make_tuple(buffer, sizeof(buffer), f.m_fmt.c_str()), f.m_vargs));
os.write(buffer, r);
return os;
}
private:
template <size_t... I>
void copy_vargs(std::index_sequence<I...>)
{
((std::get<I>(m_vargs) = *std::get<I>(m_args)), ...);
}
std::string m_fmt;
args_vector_type m_args;
vargs_vector_type m_vargs;
};
template <typename... Args>
constexpr auto format(std::string_view fmt, Args... args)
{
return format_plus_arg(fmt, std::forward<Args>(args)...);
}
// --------------------------------------------------------------------
/// A streambuf that fills out lines with spaces up until a specified width
class fill_out_streambuf : public std::streambuf
{
public:
using base_type = std::streambuf;
using int_type = base_type::int_type;
using char_type = base_type::char_type;
using traits_type = base_type::traits_type;
fill_out_streambuf(std::ostream &os, int width = 80)
: m_os(os)
, m_upstream(os.rdbuf())
, m_width(width)
{
}
~fill_out_streambuf()
{
m_os.rdbuf(m_upstream);
}
virtual int_type
overflow(int_type ic = traits_type::eof())
{
char ch = traits_type::to_char_type(ic);
int_type result = ic;
if (ch == '\n')
{
for (int i = m_column_count; result != traits_type::eof() and i < m_width; ++i)
result = m_upstream->sputc(' ');
}
if (result != traits_type::eof())
result = m_upstream->sputc(ch);
if (result != traits_type::eof())
{
if (ch == '\n')
{
m_column_count = 0;
++m_line_count;
}
else
++m_column_count;
}
return result;
}
std::streambuf *get_upstream() const { return m_upstream; }
int get_line_count() const { return m_line_count; }
private:
std::ostream &m_os;
std::streambuf *m_upstream;
int m_width;
int m_line_count = 0;
int m_column_count = 0;
};
} // namespace pdbx
......@@ -300,6 +300,11 @@ struct item_handle
static const item_handle s_null_item;
friend void swap(item_handle a, item_handle b)
{
a.swap(b);
}
private:
item_handle();
......@@ -318,21 +323,24 @@ struct item_handle::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<T> an
static value_type convert(const item_handle &ref)
{
auto txt = ref.text();
value_type result = {};
std::from_chars_result r = selected_charconv<value_type>::from_chars(txt.data(), txt.data() + txt.size(), result);
if (r.ec != std::errc())
if (not ref.empty())
{
result = {};
if (cif::VERBOSE)
auto txt = ref.text();
std::from_chars_result r = selected_charconv<value_type>::from_chars(txt.data(), txt.data() + txt.size(), result);
if (r.ec != std::errc())
{
if (r.ec == std::errc::invalid_argument)
std::cerr << "Attempt to convert " << std::quoted(txt) << " into a number" << std::endl;
else if (r.ec == std::errc::result_out_of_range)
std::cerr << "Conversion of " << std::quoted(txt) << " into a type that is too small" << std::endl;
result = {};
if (cif::VERBOSE)
{
if (r.ec == std::errc::invalid_argument)
std::cerr << "Attempt to convert " << std::quoted(txt) << " into a number" << std::endl;
else if (r.ec == std::errc::result_out_of_range)
std::cerr << "Conversion of " << std::quoted(txt) << " into a type that is too small" << std::endl;
}
}
}
......
......@@ -126,6 +126,35 @@ class row
m_tail = m_tail->m_next = iv;
}
void remove(item_value *iv)
{
if (iv == m_head)
{
if (m_tail == m_head)
{
assert(iv->m_next == nullptr);
m_head = m_tail = nullptr;
}
else
m_head = m_head->m_next;
}
else
{
for (auto v = m_head; v->m_next != nullptr; v = v->m_next)
{
if (v->m_next != iv)
continue;
v->m_next = iv->m_next;
iv->m_next = nullptr;
if (m_tail == iv)
m_tail = v;
break;
}
}
}
row *m_next = nullptr;
item_value *m_head = nullptr, *m_tail = nullptr;
};
......@@ -240,6 +269,8 @@ class row_handle
assign(i.name(), i.value(), updateLinked);
}
void swap(size_t column, row_handle &r);
category *m_category = nullptr;
row *m_row = nullptr;
};
......
......@@ -50,8 +50,8 @@ struct space_group
int nr;
};
// extern const Spacegroup kSpaceGroups[];
// extern const std::size_t kNrOfSpaceGroups;
extern const space_group kSpaceGroups[];
extern const std::size_t kNrOfSpaceGroups;
// --------------------------------------------------------------------
......@@ -134,8 +134,8 @@ struct symop_datablock
static_assert(sizeof(symop_datablock) == sizeof(uint64_t), "Size of symop_data is wrong");
// extern const symop_data_block kSymopNrTable[];
// extern const std::size_t kSymopNrTableSize;
extern const symop_datablock kSymopNrTable[];
extern const std::size_t kSymopNrTableSize;
// --------------------------------------------------------------------
......
......@@ -170,7 +170,7 @@ class Progress
Progress(const Progress &) = delete;
Progress &operator=(const Progress &) = delete;
struct ProgressImpl *mImpl;
struct ProgressImpl *m_impl;
};
// --------------------------------------------------------------------
......
......@@ -1328,46 +1328,18 @@ void category::update_value(row *row, size_t column, std::string_view value, boo
}
// first remove old value with cix
if (row->m_head == nullptr)
; // nothing to do
else if (row->m_head->m_column_ix == column)
{
auto iv = row->m_head;
row->m_head = iv->m_next;
iv->m_next = nullptr;
delete_item(iv);
}
else
for (auto iv = row->m_head; iv != nullptr; iv = iv->m_next)
{
for (auto iv = row->m_head; iv->m_next != nullptr; iv = iv->m_next)
{
if (iv->m_next->m_column_ix != column)
continue;
auto nv = iv->m_next;
iv->m_next = nv->m_next;
nv->m_next = nullptr;
delete_item(nv);
if (iv->m_column_ix != column)
continue;
break;
}
row->remove(iv);
delete_item(iv);
break;
}
if (not value.empty())
{
auto nv = create_item(column, value);
if (row->m_head == nullptr)
row->m_head = nv;
else
{
auto iv = row->m_head;
while (iv->m_next != nullptr)
iv = iv->m_next;
iv->m_next = nv;
}
}
row->append(create_item(column, value));
if (reinsert)
m_index->insert(row);
......@@ -1671,6 +1643,87 @@ category::iterator category::erase_impl(const_iterator pos)
// return iterator(*this, cur);
}
void category::swap_item(size_t column_ix, row_handle &a, row_handle &b)
{
assert(this == a.m_category);
assert(this == b.m_category);
item_value *va = nullptr, *vb = nullptr;
auto ra = a.m_row;
auto rb = b.m_row;
if (ra->m_head != nullptr and ra->m_head->m_column_ix == column_ix)
{
va = ra->m_head;
ra->m_head = va->m_next;
va->m_next = nullptr;
if (ra->m_tail == va)
ra->m_tail = ra->m_head;
}
else
{
for (auto v = ra->m_head; v->m_next != nullptr; v = v->m_next)
{
if (v->m_next->m_column_ix != column_ix)
continue;
va = v->m_next;
v->m_next = va->m_next;
va->m_next = nullptr;
if (ra->m_tail == va)
ra->m_tail = v;
break;
}
}
if (rb->m_head != nullptr and rb->m_head->m_column_ix == column_ix)
{
vb = rb->m_head;
rb->m_head = vb->m_next;
vb->m_next = nullptr;
if (rb->m_tail == vb)
rb->m_tail = rb->m_head;
}
else
{
for (auto v = rb->m_head; v->m_next != nullptr; v = v->m_next)
{
if (v->m_next->m_column_ix != column_ix)
continue;
vb = v->m_next;
v->m_next = vb->m_next;
vb->m_next = nullptr;
if (rb->m_tail = vb)
rb->m_tail = v;
break;
}
}
if (ra->m_head == nullptr)
ra->m_head = ra->m_tail = vb;
else
{
ra->m_tail->m_next = vb;
ra->m_tail = vb;
}
if (rb->m_head == nullptr)
rb->m_head = rb->m_tail = va;
else
{
rb->m_tail->m_next = va;
rb->m_tail = va;
}
}
void category::reorder_by_index()
{
if (m_index)
......@@ -1933,4 +1986,100 @@ void category::write(std::ostream &os, const std::vector<uint16_t> &order, bool
os << "# " << '\n';
}
bool category::operator==(const category &rhs) const
{
auto &a = *this;
auto &b = rhs;
using namespace std::placeholders;
// set<std::string> tagsA(a.fields()), tagsB(b.fields());
//
// if (tagsA != tagsB)
// std::cout << "Unequal number of fields" << std::endl;
auto validator = a.get_validator();
auto catValidator = validator->get_validator_for_category(a.name());
if (catValidator == nullptr)
throw std::runtime_error("missing cat validator");
typedef std::function<int(std::string_view,std::string_view)> compType;
std::vector<std::tuple<std::string,compType>> tags;
auto keys = catValidator->m_keys;
std::vector<size_t> keyIx;
for (auto& tag: a.fields())
{
auto iv = catValidator->get_validator_for_item(tag);
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");
tags.push_back(std::make_tuple(tag, std::bind(&cif::type_validator::compare, tv, std::placeholders::_1, std::placeholders::_2)));
auto pred = [tag](const std::string& s) -> bool { return cif::iequals(tag, s) == 0; };
if (find_if(keys.begin(), keys.end(), pred) == keys.end())
keyIx.push_back(tags.size() - 1);
}
// a.reorderByIndex();
// b.reorderByIndex();
auto rowEqual = [&](const row_handle& a, const row_handle& b)
{
int d = 0;
for (auto kix: keyIx)
{
std::string tag;
compType compare;
std::tie(tag, compare) = tags[kix];
d = compare(a[tag].text(), b[tag].text());
if (d != 0)
break;
}
return d == 0;
};
auto ai = a.begin(), bi = b.begin();
while (ai != a.end() or bi != b.end())
{
if (ai == a.end() or bi == b.end())
return false;
auto ra = *ai, rb = *bi;
if (not rowEqual(ra, rb))
return false;
std::vector<std::string> missingA, missingB, different;
for (auto& tt: tags)
{
std::string tag;
compType compare;
std::tie(tag, compare) = tt;
// make it an option to compare unapplicable to empty or something
auto ta = ra[tag].text(); if (ta == "." or ta == "?") ta = "";
auto tb = rb[tag].text(); if (tb == "." or tb == "?") tb = "";
if (compare(ta, tb) != 0)
return false;
}
++ai;
++bi;
}
return true;
}
} // namespace cif
\ No newline at end of file
......@@ -194,7 +194,7 @@ compound::compound(cif::datablock &db, const std::string &id, const std::string
}
}
compound_atom compound::get_atom_by_id(const std::string &atom_id) const
compound_atom compound::get_atom_by_atom_id(const std::string &atom_id) const
{
compound_atom result = {};
for (auto &a : m_atoms)
......@@ -367,9 +367,9 @@ compound_factory_impl::compound_factory_impl(const fs::path &file, std::shared_p
{
cif::file cifFile(file);
auto &compList = cifFile["comp_list"];
if (not compList.empty()) // So this is a CCP4 restraints file, special handling
if (cifFile.contains("comp_list")) // So this is a CCP4 restraints file, special handling
{
auto &compList = cifFile["comp_list"];
auto &chemComp = compList["chem_comp"];
for (const auto &[id, name, group] : chemComp.rows<std::string, std::string, std::string>("id", "name", "group"))
......@@ -726,7 +726,7 @@ const compound *compound_factory::create(std::string id)
{
// static bool warned = false;
// if (mImpl and warned == false)
// if (m_impl and warned == false)
// {
// std::cerr << "Warning: no compound information library was found, resulting data may be incorrect or incomplete" << std::endl;
// warned = true;
......
......@@ -241,4 +241,71 @@ void datablock::write(std::ostream &os, const std::vector<std::string> &tag_orde
}
}
bool datablock::operator==(const datablock &rhs) const
{
auto &dbA = *this;
auto &dbB = rhs;
std::vector<std::string> catA, catB;
for (auto &cat : dbA)
{
if (not cat.empty())
catA.push_back(cat.name());
}
std::sort(catA.begin(), catA.end());
for (auto &cat : dbB)
{
if (not cat.empty())
catB.push_back(cat.name());
}
std::sort(catB.begin(), catB.end());
// loop over categories twice, to group output
// First iteration is to list missing categories.
std::vector<std::string> missingA, missingB;
auto catA_i = catA.begin(), catB_i = catB.begin();
while (catA_i != catA.end() and catB_i != catB.end())
{
if (not iequals(*catA_i, *catB_i))
return false;
++catA_i, ++catB_i;
}
if (catA_i != catA.end() or catB_i != catB.end())
return false;
// Second loop, now compare category values
catA_i = catA.begin(), catB_i = catB.begin();
while (catA_i != catA.end() and catB_i != catB.end())
{
std::string nA = *catA_i;
to_lower(nA);
std::string nB = *catB_i;
to_lower(nB);
int d = nA.compare(nB);
if (d > 0)
++catB_i;
else if (d < 0)
++catA_i;
else
{
if (not (*dbA.get(*catA_i) == *dbB.get(*catB_i)))
return false;
++catA_i;
++catB_i;
}
}
return true;
}
} // namespace cif::cif
\ No newline at end of file
......@@ -111,6 +111,11 @@ void file::load_dictionary(std::string_view name)
set_validator(&validator_factory::instance()[name]);
}
bool file::contains(std::string_view name) const
{
return std::find_if(begin(), end(), [name](const datablock &db) { return db.name() == name; }) != end();
}
datablock &file::operator[](std::string_view name)
{
auto i = std::find_if(begin(), end(), [name](const datablock &c)
......
......@@ -60,4 +60,11 @@ void item_handle::assign_value(const item &v)
m_row_handle.assign(m_column, v.value(), true);
}
void item_handle::swap(item_handle &b)
{
assert(m_column == b.m_column);
// assert(&m_row_handle.m_category == &b.m_row_handle.m_category);
m_row_handle.swap(m_column, b.m_row_handle);
}
}
This diff is collapsed. Click to expand it.
......@@ -53,6 +53,11 @@ uint16_t row_handle::add_column(std::string_view name)
return m_category->add_column(name);
}
void row_handle::swap(size_t column, row_handle &b)
{
m_category->swap_item(column, *this, b);
}
// --------------------------------------------------------------------
row_initializer::row_initializer(row_handle rh)
......
......@@ -338,44 +338,44 @@ void ProgressImpl::PrintDone()
}
Progress::Progress(int64_t inMax, const std::string &inAction)
: mImpl(nullptr)
: m_impl(nullptr)
{
if (isatty(STDOUT_FILENO))
mImpl = new ProgressImpl(inMax, inAction);
m_impl = new ProgressImpl(inMax, inAction);
}
Progress::~Progress()
{
if (mImpl != nullptr)
mImpl->Stop();
if (m_impl != nullptr)
m_impl->Stop();
delete mImpl;
delete m_impl;
}
void Progress::consumed(int64_t inConsumed)
{
if (mImpl != nullptr and
(mImpl->mConsumed += inConsumed) >= mImpl->mMax)
if (m_impl != nullptr and
(m_impl->mConsumed += inConsumed) >= m_impl->mMax)
{
mImpl->Stop();
m_impl->Stop();
}
}
void Progress::progress(int64_t inProgress)
{
if (mImpl != nullptr and
(mImpl->mConsumed = inProgress) >= mImpl->mMax)
if (m_impl != nullptr and
(m_impl->mConsumed = inProgress) >= m_impl->mMax)
{
mImpl->Stop();
m_impl->Stop();
}
}
void Progress::message(const std::string &inMessage)
{
if (mImpl != nullptr)
if (m_impl != nullptr)
{
std::unique_lock lock(mImpl->mMutex);
mImpl->mMessage = inMessage;
std::unique_lock lock(m_impl->mMutex);
m_impl->mMessage = inMessage;
}
}
......
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define BOOST_TEST_ALTERNATIVE_INIT_API
#include <boost/test/included/unit_test.hpp>
#include <stdexcept>
#include <cif++.hpp>
namespace tt = boost::test_tools;
std::filesystem::path gTestDir = std::filesystem::current_path(); // filled in first test
// --------------------------------------------------------------------
cif::file operator""_cf(const char *text, size_t length)
{
struct membuf : public std::streambuf
{
membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
} buffer(const_cast<char *>(text), length);
std::istream is(&buffer);
return cif::file(is);
}
// --------------------------------------------------------------------
bool init_unit_test()
{
cif::VERBOSE = 1;
// // not a test, just initialize test dir
// if (boost::unit_test::framework::master_test_suite().argc == 2)
// gTestDir = boost::unit_test::framework::master_test_suite().argv[1];
// // do this now, avoids the need for installing
// cif::add_file_resource("mmcif_pdbx.dic", gTestDir / ".." / "rsrc" / "mmcif_pdbx.dic");
// // initialize CCD location
// cif::add_file_resource("components.cif", gTestDir / ".." / "data" / "ccd-subset.cif");
return true;
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(fmt_1)
{
std::ostringstream os;
std::string world("world");
os << cif::format("Hello, %-10.10s, the magic number is %d and pi is %g", world, 42, M_PI);
BOOST_CHECK_EQUAL(os.str(), "Hello, world , the magic number is 42 and pi is 3.14159");
BOOST_CHECK_EQUAL(cif::format("Hello, %-10.10s, the magic number is %d and pi is %g", world, 42, M_PI).str(),
"Hello, world , the magic number is 42 and pi is 3.14159");
}
\ No newline at end of file
#include "../include/cif++/Cif++.hpp"
#include "../include/cif++/PDB2Cif.hpp"
#include "../include/cif++/Structure.hpp"
#include <iostream>
#include <fstream>
int main(int argc, char* argv[])
{
cif::VERBOSE = 3;
try
{
std::filesystem::path testdir = std::filesystem::current_path();
if (argc == 3)
testdir = argv[2];
if (std::filesystem::exists(testdir / ".." / "data" / "ccd-subset.cif"))
cif::add_file_resource("components.cif", testdir / ".." / "data" / "ccd-subset.cif");
if (std::filesystem::exists(testdir / ".." / "rsrc" / "mmcif_pdbx.dic"))
cif::add_file_resource("mmcif_pdbx.dic", testdir / ".." / "rsrc" / "mmcif_pdbx.dic");
mmcif::CompoundFactory::instance().pushDictionary(testdir / "REA.cif");
mmcif::CompoundFactory::instance().pushDictionary(testdir / "RXA.cif");
mmcif::file f(testdir / ".."/"examples"/"1cbs.cif.gz");
mmcif::Structure structure(f);
auto &res = structure.getResidue("B");
structure.change_residue(res, "RXA", {});
structure.cleanupEmptyCategories();
f.save(std::cout);
}
catch (const std::exception& e)
{
std::cerr << e.what() << std::endl;
exit(1);
}
return 0;
}
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define BOOST_TEST_ALTERNATIVE_INIT_API
#include <boost/test/included/unit_test.hpp>
#include <stdexcept>
#include <cif++/cif.hpp>
#include <cif++/structure/Structure.hpp>
// --------------------------------------------------------------------
cif::file operator""_cf(const char* text, size_t length)
{
struct membuf : public std::streambuf
{
membuf(char* text, size_t length)
{
this->setg(text, text, text + length);
}
} buffer(const_cast<char*>(text), length);
std::istream is(&buffer);
return cif::file(is);
}
// --------------------------------------------------------------------
std::filesystem::path gTestDir = std::filesystem::current_path();
bool init_unit_test()
{
cif::VERBOSE = 1;
// not a test, just initialize test dir
if (boost::unit_test::framework::master_test_suite().argc == 2)
gTestDir = boost::unit_test::framework::master_test_suite().argv[1];
// do this now, avoids the need for installing
cif::add_file_resource("mmcif_pdbx.dic", gTestDir / ".." / "rsrc" / "mmcif_pdbx.dic");
// initialize CCD location
cif::add_file_resource("components.cif", gTestDir / ".." / "data" / "ccd-subset.cif");
mmcif::CompoundFactory::instance().pushDictionary(gTestDir / "HEM.cif");
return true;
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(sugar_name_1)
{
using namespace cif::literals;
const std::filesystem::path example(gTestDir / "1juh.cif.gz");
mmcif::file file(example.string());
mmcif::Structure s(file);
auto &db = s.datablock();
auto &entity = db["entity"];
auto &branches = s.branches();
BOOST_CHECK_EQUAL(branches.size(), 4);
for (auto &branch : branches)
{
auto entityID = branch.front().entityID();
auto name = entity.find1<std::string>("id"_key == entityID, "pdbx_description");
BOOST_CHECK_EQUAL(branch.name(), name);
}
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(create_sugar_1)
{
using namespace cif::literals;
const std::filesystem::path example(gTestDir / "1juh.cif.gz");
mmcif::file file(example.string());
mmcif::Structure s(file);
// collect atoms from asym L first
auto &NAG = s.getResidue("L");
auto nagAtoms = NAG.atoms();
std::vector<std::vector<cif::Item>> ai;
auto &db = s.datablock();
auto &as = db["atom_site"];
for (auto r : as.find("label_asym_id"_key == "L"))
ai.emplace_back(r.begin(), r.end());
s.remove_residue(NAG);
auto &branch = s.create_branch(ai);
BOOST_CHECK_EQUAL(branch.name(), "2-acetamido-2-deoxy-beta-D-glucopyranose");
BOOST_CHECK_EQUAL(branch.size(), 1);
BOOST_CHECK_EQUAL(branch[0].atoms().size(), nagAtoms.size());
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(create_sugar_2)
{
using namespace cif::literals;
const std::filesystem::path example(gTestDir / "1juh.cif.gz");
mmcif::file file(example.string());
mmcif::Structure s(file);
// Get branch for H
auto &bH = s.getBranchByAsymID("H");
BOOST_CHECK_EQUAL(bH.size(), 2);
std::vector<std::vector<cif::Item>> ai[2];
auto &db = s.datablock();
auto &as = db["atom_site"];
for (size_t i = 0; i < 2; ++i)
{
for (auto r : as.find("label_asym_id"_key == "H" and "auth_seq_id"_key == i + 1))
ai[i].emplace_back(r.begin(), r.end());
}
s.remove_branch(bH);
auto &bN = s.create_branch(ai[0]);
s.extend_branch(bN.asymID(), ai[1], 1, "O4");
BOOST_CHECK_EQUAL(bN.name(), "2-acetamido-2-deoxy-beta-D-glucopyranose-(1-4)-2-acetamido-2-deoxy-beta-D-glucopyranose");
BOOST_CHECK_EQUAL(bN.size(), 2);
file.save(gTestDir / "test-create_sugar_2.cif");
BOOST_CHECK_NO_THROW(mmcif::Structure s2(file));
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(delete_sugar_1)
{
using namespace cif::literals;
const std::filesystem::path example(gTestDir / "1juh.cif.gz");
mmcif::file file(example.string());
mmcif::Structure s(file);
// Get branch for H
auto &bG = s.getBranchByAsymID("G");
BOOST_CHECK_EQUAL(bG.size(), 4);
s.remove_residue(bG[1]);
BOOST_CHECK_EQUAL(bG.size(), 1);
auto &bN = s.getBranchByAsymID("G");
BOOST_CHECK_EQUAL(bN.name(), "2-acetamido-2-deoxy-beta-D-glucopyranose");
BOOST_CHECK_EQUAL(bN.size(), 1);
file.save(gTestDir / "test-create_sugar_3.cif");
BOOST_CHECK_NO_THROW(mmcif::Structure s2(file));
}
......@@ -524,6 +524,37 @@ _test.value
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(sw_1)
{
using namespace cif::literals;
auto f = R"(data_TEST
#
loop_
_test.id
_test.name
_test.value
1 aap 1.0
2 noot 1.1
3 mies 1.2
)"_cf;
auto &db = f.front();
auto &test = db["test"];
swap(test.front()["name"], test.back()["name"]);
BOOST_CHECK_EQUAL(test.find1<std::string>("id"_key == 1, "name"), "mies");
BOOST_CHECK_EQUAL(test.find1<std::string>("id"_key == 3, "name"), "aap");
swap(test.front()["name"], test.back()["name"]);
BOOST_CHECK_EQUAL(test.find1<std::string>("id"_key == 1, "name"), "aap");
BOOST_CHECK_EQUAL(test.find1<std::string>("id"_key == 3, "name"), "mies");
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(d1)
{
const char dict[] = R"(
......@@ -2017,7 +2048,7 @@ _cat_3.num
// for (const auto &[compound, seqnr] : std::initializer_list<std::tuple<std::string, int>>{{"PRO", 1}, {"ASN", 2}, {"PHE", 3}})
// {
// auto &res = structure.getResidue("A", compound, seqnr, "");
// auto &res = structure.get_residue("A", compound, seqnr, "");
// auto atoms = res.atoms();
// auto dc = components.get(compound);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment