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 ...@@ -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. 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 Validation
---------- ----------
......
...@@ -20,6 +20,20 @@ Matrix ...@@ -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. 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 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 ...@@ -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 .. code:: cpp
using namespace cif::literals;
// Load the file // Load the file
cif::file f(gTestDir / "2bi3.cif.gz"); cif::file f(gTestDir / "2bi3.cif.gz");
// Simply assume we can use the first datablock
auto &db = f.front(); auto &db = f.front();
cif::mm::structure s(db);
// Load the crystal information
cif::crystal c(db); cif::crystal c(db);
// Take references to the two categories we need
auto struct_conn = db["struct_conn"]; 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 &[ for (const auto &[
asym1, seqid1, authseqid1, atomid1, symm1, asym1, seqid1, authseqid1, atomid1, symm1,
asym2, seqid2, authseqid2, atomid2, symm2, asym2, seqid2, authseqid2, atomid2, symm2,
dist] : struct_conn.find< dist] : struct_conn.find<
std::string,int,std::string,std::string,std::string, std::string,std::optional<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,
float>( float>(
cif::key("ptnr1_symmetry") != "1_555" or cif::key("ptnr2_symmetry") != "1_555", 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", "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 ...@@ -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" "pdbx_dist_value"
)) ))
{ {
auto &r1 = s.get_residue(asym1, seqid1, authseqid1); // Find the location of the first atom
auto &r2 = s.get_residue(asym2, seqid2, authseqid2); 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); // Find the location of the second atom
auto a2 = r2.get_atom_by_atom_id(atomid2); 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)); // Calculate the position of the first atom using the symmetry operator defined in struct_conn
auto sa2 = c.symmetry_copy(a2.get_location(), cif::sym_op(symm2)); 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); // And that should of course be equal to the location in struct_conn for p2
BOOST_TEST(p.m_y == sa2.m_y); assert(p.m_x == sa2.m_x);
BOOST_TEST(p.m_z == sa2.m_z); assert(p.m_y == sa2.m_y);
assert(p.m_z == sa2.m_z);
BOOST_TEST(d == dist); // Distance and symmetry operator string should also be the same
BOOST_TEST(so.string() == symm2); 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 &) ...@@ -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 * @brief Operator to create a boolean opposite of the condition in @a rhs
*/ */
inline condition operator not(condition &&rhs) inline condition operator not(condition &&rhs)
......
...@@ -527,14 +527,14 @@ BOOST_AUTO_TEST_CASE(symm_2bi3_1a, *utf::tolerance(0.1f)) ...@@ -527,14 +527,14 @@ BOOST_AUTO_TEST_CASE(symm_2bi3_1a, *utf::tolerance(0.1f))
cif::crystal c(db); cif::crystal c(db);
auto struct_conn = db["struct_conn"]; auto struct_conn = db["struct_conn"];
auto atom_site = db["struct_conn"]; auto atom_site = db["atom_site"];
for (const auto &[ for (const auto &[
asym1, seqid1, authseqid1, atomid1, symm1, asym1, seqid1, authseqid1, atomid1, symm1,
asym2, seqid2, authseqid2, atomid2, symm2, asym2, seqid2, authseqid2, atomid2, symm2,
dist] : struct_conn.find< dist] : struct_conn.find<
std::string,int,std::string,std::string,std::string, std::string,std::optional<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,
float>( float>(
cif::key("ptnr1_symmetry") != "1_555" or cif::key("ptnr2_symmetry") != "1_555", 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", "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