Commit 747c6d30 by Maarten L. Hekkelman

Added better support for std::optional in conditions

parent 84af564a
......@@ -161,6 +161,50 @@ Returning a complete set if often not required, if you only want to have the fir
There are cases when you really need exactly one result. The :cpp:func:`cif::category::find1` can be used in that case, it will throw an exception if the query does not result in exactly one row.
NULL and ANY
------------
Sometimes items may be empty. The trouble is a bit that empty comes in two flavors: unknown and null. Null in *CIF* parlance means the item should not contain a value since it makes no sense in this case, the value stored in the file is a single dot character: ``'.'``. E.g. *atom_site* records may have a NULL value for label_seq_id for atoms that are part of a *non-polymer*.
The other empty value is indicated by a question mark character: ``'?'``. This means the value is simply unknown.
Both these are NULL in *libcifpp* conditions and can be searched for using :cpp:var:`cif::null`.
So you can search for:
.. code-block:: cpp
cif::condition c = "label_seq_id"_key == cif::null;
You might also want to look for a certain value and don't care in which item it is stored, in that case you can use :cpp:var:`cif::any`.
.. code-block:: cpp
cif::condition c = cif::any == "foo";
And in linked record you might have the items that have a value in both parent and child or both should be NULL. For that, you can request the value to return by find to be of type std::optional and then use that value to build the query. An example to explain this, let's find the location of the atom that is referenced as the first atom in a struct_conn record:
.. code-block:: cpp
// Take references to the two categories we need
auto struct_conn = db["struct_conn"];
auto atom_site = db["atom_site"];
// Loop over all rows in struct_conn taking only the values we need
// Note that the label_seq_id is returned as a std::optional<int>
// That means it may contain an integer or may be empty
for (const auto &[asym1, seqid1, authseqid1, atomid1] :
struct_conn.rows<std::string,std::optional<int>,std::string,std::string,std::string>(
"ptnr1_label_asym_id", "ptnr1_label_seq_id", "ptnr1_auth_seq_id", "ptnr1_label_atom_id"
))
{
// Find the location of the first atom
cif::point p1 = atom_site.find1<float,float,float>(
"label_asym_id"_key == asym1 and "label_seq_id"_key == seqid1 and "auth_seq_id"_key == authseqid1 and "label_atom_id"_key == atomid1,
"cartn_x", "cartn_y", "cartn_z");
}
Validation
----------
......
......@@ -20,6 +20,20 @@ Matrix
Although Quaternions are the preferred way of doing rotations, not every manipulation is a rotation and thus we need a matrix class as well. Matrices and their operations are encoded as matrix_expressions in *libcifpp* allowing the compiler to generate very fast code. See the :ref:`file_cif++_matrix.hpp` for what is on offer.
Crystals
--------
The *CIF* and *mmCIF* were initially developed to store crystallographic information on structures. Apart from coordinates and the chemical information the crystallographic information is important. This information can be split into two parts, a unit cell and a set of :ref:`symmetry-ops` making up a spacegroup. The spacegroup number and name are stored in the *symmetry* category. The corresponding symmetry operations can be obtained in *libcifpp* by using the :cpp:class:`cif::spacegroup`. The cell is stored in the category *cell* and likewise can be loaded using the :cpp:class:`cif::cell`. Together these two classes make up a crystal and so we have a :cpp:class:`cif::crystal` which contains both. You can easily create such a crystal object by passing the datablock containing the data to the constructor. As in:
.. code:: cpp
// Load the file
cif::file f("1cbs.cif.gz");
auto &db = f.front();
cif::crystal c(db);
.. _symmetry-ops:
Symmetry operations
-------------------
......@@ -31,21 +45,28 @@ To give an idea how this works, here's a piece of code copied from one of the un
.. code:: cpp
using namespace cif::literals;
// Load the file
cif::file f(gTestDir / "2bi3.cif.gz");
// Simply assume we can use the first datablock
auto &db = f.front();
cif::mm::structure s(db);
// Load the crystal information
cif::crystal c(db);
// Take references to the two categories we need
auto struct_conn = db["struct_conn"];
auto atom_site = db["atom_site"];
// Loop over all rows in struct_conn taking only the values we need
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,
std::string,std::optional<int>,std::string,std::string,std::string,
std::string,std::optional<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",
......@@ -53,25 +74,35 @@ To give an idea how this works, here's a piece of code copied from one of the un
"pdbx_dist_value"
))
{
auto &r1 = s.get_residue(asym1, seqid1, authseqid1);
auto &r2 = s.get_residue(asym2, seqid2, authseqid2);
// Find the location of the first atom
cif::point p1 = atom_site.find1<float,float,float>(
"label_asym_id"_key == asym1 and "label_seq_id"_key == seqid1 and "auth_seq_id"_key == authseqid1 and "label_atom_id"_key == atomid1,
"cartn_x", "cartn_y", "cartn_z");
auto a1 = r1.get_atom_by_atom_id(atomid1);
auto a2 = r2.get_atom_by_atom_id(atomid2);
// Find the location of the second atom
cif::point p2 = atom_site.find1<float,float,float>(
"label_asym_id"_key == asym2 and "label_seq_id"_key == seqid2 and "auth_seq_id"_key == authseqid2 and "label_atom_id"_key == atomid2,
"cartn_x", "cartn_y", "cartn_z");
auto sa1 = c.symmetry_copy(a1.get_location(), cif::sym_op(symm1));
auto sa2 = c.symmetry_copy(a2.get_location(), cif::sym_op(symm2));
// Calculate the position of the first atom using the symmetry operator defined in struct_conn
auto sa1 = c.symmetry_copy(p1, cif::sym_op(symm1));
BOOST_TEST(cif::distance(sa1, sa2) == dist);
// Calculate the position of the second atom using the symmetry operator defined in struct_conn
auto sa2 = c.symmetry_copy(p2, cif::sym_op(symm2));
auto pa1 = a1.get_location();
// The distance between these symmetry atoms should be equal to the distance in the struct_conn record
assert(cif::distance(sa1, sa2) == dist);
const auto &[d, p, so] = c.closest_symmetry_copy(pa1, a2.get_location());
// And to show how you can obtain the closest symmetry copy of an atom near another one:
// here we request the symmetry copy of p2 that lies closest to p1
const auto &[d, p, so] = c.closest_symmetry_copy(p1, p2);
BOOST_TEST(p.m_x == sa2.m_x);
BOOST_TEST(p.m_y == sa2.m_y);
BOOST_TEST(p.m_z == sa2.m_z);
// And that should of course be equal to the location in struct_conn for p2
assert(p.m_x == sa2.m_x);
assert(p.m_y == sa2.m_y);
assert(p.m_z == sa2.m_z);
BOOST_TEST(d == dist);
BOOST_TEST(so.string() == symm2);
// Distance and symmetry operator string should also be the same
assert(d == dist);
assert(so.string() == symm2);
}
\ No newline at end of file
......@@ -1062,6 +1062,19 @@ inline condition operator!=(const key &key, const empty_type &)
}
/**
* @brief Create a condition to search any column for a value @a v if @a v contains a value
* compare to null if not.
*/
template <typename T>
condition operator==(const key &key, const std::optional<T> &v)
{
if (v.has_value())
return condition(new detail::key_equals_condition_impl({ key.m_item_tag, *v }));
else
return condition(new detail::key_is_empty_condition_impl(key.m_item_tag));
}
/**
* @brief Operator to create a boolean opposite of the condition in @a rhs
*/
inline condition operator not(condition &&rhs)
......
......@@ -527,14 +527,14 @@ BOOST_AUTO_TEST_CASE(symm_2bi3_1a, *utf::tolerance(0.1f))
cif::crystal c(db);
auto struct_conn = db["struct_conn"];
auto atom_site = db["struct_conn"];
auto atom_site = db["atom_site"];
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,
std::string,std::optional<int>,std::string,std::string,std::string,
std::string,std::optional<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",
......
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