| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include "test-main.hpp" |
|
|
| #include <stdexcept> |
|
|
| #include <cif++.hpp> |
|
|
| #include <Eigen/Eigenvalues> |
|
|
| |
|
|
| 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); |
| } |
|
|
| |
|
|
| |
|
|
| TEST_CASE("t1") |
| { |
| |
| |
| |
|
|
| |
| |
|
|
| |
| cif::quaternion q{ 0.5, 0.5, 0.5, 0.5 }; |
| q = normalize(q); |
|
|
| const auto &&[angle0, axis0] = cif::quaternion_to_angle_axis(q); |
|
|
| std::vector<cif::point> p1{ |
| { 16.979f, 13.301f, 44.555f }, |
| { 18.150f, 13.525f, 43.680f }, |
| { 18.656f, 14.966f, 43.784f }, |
| { 17.890f, 15.889f, 44.078f }, |
| { 17.678f, 13.270f, 42.255f }, |
| { 16.248f, 13.734f, 42.347f }, |
| { 15.762f, 13.216f, 43.724f } |
| }; |
|
|
| auto p2 = p1; |
|
|
| cif::center_points(p1); |
|
|
| for (auto &p : p2) |
| p.rotate(q); |
|
|
| cif::center_points(p2); |
|
|
| auto q2 = cif::align_points(p1, p2); |
|
|
| const auto &&[angle, axis] = cif::quaternion_to_angle_axis(q2); |
|
|
| REQUIRE_THAT(std::fmod(360 + angle, 360), Catch::Matchers::WithinRel(std::fmod(360 - angle0, 360), 0.01)); |
|
|
| for (auto &p : p1) |
| p.rotate(q2); |
|
|
| auto rmsd = cif::RMSd(p1, p2); |
|
|
| REQUIRE(rmsd < 1e-5); |
|
|
| |
| } |
|
|
| TEST_CASE("t2") |
| { |
| cif::point p[] = { |
| { 1, 1, 0 }, |
| { 2, 1, 0 }, |
| { 1, 2, 0 } |
| }; |
|
|
| cif::point xp = cif::cross_product(p[1] - p[0], p[2] - p[0]); |
|
|
| auto q = cif::construct_from_angle_axis(45, xp); |
|
|
| auto &&[angle, axis] = cif::quaternion_to_angle_axis(q); |
|
|
| REQUIRE_THAT(angle, Catch::Matchers::WithinRel(45.f, 0.01f)); |
| } |
|
|
| TEST_CASE("t3") |
| { |
| cif::point p[] = { |
| { 1, 1, 0 }, |
| { 2, 1, 0 }, |
| { 1, 2, 0 } |
| }; |
|
|
| cif::point xp = cif::cross_product(p[1] - p[0], p[2] - p[0]); |
|
|
| auto q = cif::construct_from_angle_axis(45, xp); |
|
|
| auto v = p[1]; |
| v -= p[0]; |
| v.rotate(q); |
| v += p[0]; |
|
|
| std::cout << v << '\n'; |
|
|
| double a = cif::angle(v, p[0], p[1]); |
|
|
| REQUIRE_THAT(a, Catch::Matchers::WithinRel(45.f, 0.01f)); |
| } |
|
|
| TEST_CASE("dh_q_0") |
| { |
| cif::point axis(1, 0, 0); |
|
|
| cif::point p(1, 1, 0); |
| |
| cif::point t[3] = |
| { |
| { 0, 1, 0 }, |
| { 0, 0, 0 }, |
| { 1, 0, 0 } |
| }; |
|
|
| auto a = cif::dihedral_angle(t[0], t[1], t[2], p); |
| REQUIRE_THAT(a, Catch::Matchers::WithinRel(0, 0.01f)); |
|
|
| auto q = cif::construct_from_angle_axis(90, axis); |
|
|
| p.rotate(q); |
|
|
| REQUIRE_THAT(p.m_x, Catch::Matchers::WithinRel(1, 0.01f)); |
| REQUIRE_THAT(p.m_y, Catch::Matchers::WithinRel(0, 0.01f)); |
| REQUIRE_THAT(p.m_z, Catch::Matchers::WithinRel(1, 0.01f)); |
|
|
| a = cif::dihedral_angle(t[0], t[1], t[2], p); |
| REQUIRE_THAT(a, Catch::Matchers::WithinRel(90, 0.01f)); |
|
|
| q = cif::construct_from_angle_axis(-90, axis); |
|
|
| p.rotate(q); |
|
|
| REQUIRE_THAT(p.m_x, Catch::Matchers::WithinRel(1, 0.01f)); |
| REQUIRE_THAT(p.m_y, Catch::Matchers::WithinRel(1, 0.01f)); |
| REQUIRE_THAT(p.m_z, Catch::Matchers::WithinRel(0, 0.01f)); |
|
|
| a = cif::dihedral_angle(t[0], t[1], t[2], p); |
| REQUIRE_THAT(a, Catch::Matchers::WithinRel(0, 0.01f)); |
|
|
| } |
|
|
| TEST_CASE("dh_q_1") |
| { |
| struct |
| { |
| float angle; |
| cif::point pts[4]; |
| } tests[] = { |
| { -97.5f, |
| { { 68.8649979f, -7.34800005f, 54.3769989f }, |
| { 68.1350021f, -8.18700027f, 53.6489983f }, |
| { 68.7760239f, -9.07335377f, 52.7140236f }, |
| { 68.9000015f, -10.3944235f, 53.2217026f } } }, |
| { 80.3f, |
| { { 0.304512024f, 0.531184196f, 2.25860214f }, |
| { 0.956512451f, 0.0321846008f, 1.07460022f }, |
| { 0, 0, 0 }, |
| { 0.21336633f, -1.09552193f, -0.878999829f } } }, |
| { -97.5f, |
| { { 0.088973999f, 1.72535372f, 1.66297531f }, |
| { -0.641021729f, 0.886353493f, 0.93497467f }, |
| { 0, 0, 0 }, |
| { 1.29433727f, -0.395142615f, 0.432300746f } } }, |
| { -97.5f, |
| { |
| { 0.088973999f, 1.72535372f, 1.66297531f }, |
| { -0.641021729f, 0.886353493f, 0.93497467f }, |
| { 0, 0, 0 }, |
| { 1.33983064f, 0.384027064f, -0.275154471f }, |
|
|
| } } |
| }; |
|
|
| for (auto &&[angle, pts] : tests) |
| { |
| auto q = cif::construct_for_dihedral_angle(pts[0], pts[1], pts[2], pts[3], angle, 1); |
|
|
| pts[3].rotate(q, pts[2]); |
|
|
| auto dh = cif::dihedral_angle(pts[0], pts[1], pts[2], pts[3]); |
| REQUIRE_THAT(dh, Catch::Matchers::WithinRel(angle, 0.1f)); |
| } |
| } |
|
|
| |
|
|
| TEST_CASE("m2q_0, *utf::tolerance(0.001f)") |
| { |
| for (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::Matrix4f em; |
|
|
| em << 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; |
|
|
| Eigen::EigenSolver<Eigen::Matrix4f> es(em / 3); |
|
|
| auto ev = es.eigenvalues(); |
|
|
| size_t bestJ = 0; |
| float bestEV = -1; |
|
|
| for (size_t j = 0; j < 4; ++j) |
| { |
| if (bestEV < ev[j].real()) |
| { |
| bestEV = ev[j].real(); |
| bestJ = j; |
| } |
| } |
|
|
| if (std::abs(bestEV - 1) > 0.01) |
| continue; |
|
|
| auto col = es.eigenvectors().col(bestJ); |
|
|
| auto q = normalize(cif::quaternion{ |
| static_cast<float>(col(3).real()), |
| static_cast<float>(col(0).real()), |
| static_cast<float>(col(1).real()), |
| static_cast<float>(col(2).real()) }); |
| |
| cif::point p1{ 1, 1, 1 }; |
| cif::point p2 = p1; |
| p2.rotate(q); |
|
|
| cif::point p3 = rot * p1; |
|
|
| REQUIRE_THAT(p2.m_x, Catch::Matchers::WithinRel(p3.m_x, 0.01f)); |
| REQUIRE_THAT(p2.m_y, Catch::Matchers::WithinRel(p3.m_y, 0.01f)); |
| REQUIRE_THAT(p2.m_z, Catch::Matchers::WithinRel(p3.m_z, 0.01f)); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
|
|
| |
|
|
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
|
|
| |
| |
| |
| |
| |
|
|
| |
|
|
| TEST_CASE("symm_1") |
| { |
| cif::cell c(10, 10, 10); |
|
|
| cif::point p{ 1, 1, 1 }; |
|
|
| cif::point f = fractional(p, c); |
|
|
| REQUIRE_THAT(f.m_x, Catch::Matchers::WithinRel(0.1f, 0.01f)); |
| REQUIRE_THAT(f.m_y, Catch::Matchers::WithinRel(0.1f, 0.01f)); |
| REQUIRE_THAT(f.m_z, Catch::Matchers::WithinRel(0.1f, 0.01f)); |
|
|
| cif::point o = orthogonal(f, c); |
|
|
| REQUIRE_THAT(o.m_x, Catch::Matchers::WithinRel(1.f, 0.01f)); |
| REQUIRE_THAT(o.m_y, Catch::Matchers::WithinRel(1.f, 0.01f)); |
| REQUIRE_THAT(o.m_z, Catch::Matchers::WithinRel(1.f, 0.01f)); |
| } |
|
|
| TEST_CASE("symm_2") |
| { |
| using namespace cif::literals; |
|
|
| auto symop = "1_555"_symop; |
|
|
| REQUIRE(symop.is_identity() == true); |
| } |
|
|
| TEST_CASE("symm_3") |
| { |
| using namespace cif::literals; |
|
|
| cif::spacegroup sg(18); |
|
|
| REQUIRE(sg.size() == 4UL); |
| REQUIRE(sg.get_name() == "P 21 21 2"); |
| } |
|
|
| TEST_CASE("symm_4, *utf::tolerance(0.1f)") |
| { |
| using namespace cif::literals; |
|
|
| |
| auto sg = cif::spacegroup(154); |
| auto c = cif::cell(107.516, 107.516, 338.487, 90.00, 90.00, 120.00); |
| |
| cif::point a{ -8.688, 79.351, 10.439 }; |
| cif::point b{ -35.356, 33.693, -3.236 }; |
| cif::point sb( -6.916, 79.34, 3.236); |
|
|
| REQUIRE_THAT(distance(a, sg(a, c, "1_455"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_a()), 0.01f)); |
| REQUIRE_THAT(distance(a, sg(a, c, "1_545"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_b()), 0.01f)); |
| REQUIRE_THAT(distance(a, sg(a, c, "1_554"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_c()), 0.01f)); |
|
|
| auto sb2 = sg(b, c, "4_565"_symop); |
| REQUIRE_THAT(sb.m_x, Catch::Matchers::WithinRel(sb2.m_x, 0.01f)); |
| REQUIRE_THAT(sb.m_y, Catch::Matchers::WithinRel(sb2.m_y, 0.01f)); |
| REQUIRE_THAT(sb.m_z, Catch::Matchers::WithinRel(sb2.m_z, 0.01f)); |
|
|
| REQUIRE_THAT(distance(a, sb2), Catch::Matchers::WithinRel(7.42f, 0.01f)); |
| } |
|
|
| |
|
|
| TEST_CASE("symm_4wvp_1, *utf::tolerance(0.1f)") |
| { |
| using namespace cif::literals; |
|
|
| cif::file f(gTestDir / "4wvp.cif.gz"); |
|
|
| auto &db = f.front(); |
| cif::mm::structure s(db); |
|
|
| cif::crystal c(db); |
|
|
| cif::point p{ -78.722, 98.528, 11.994 }; |
| auto a = s.get_residue("A", 10, "").get_atom_by_atom_id("O"); |
|
|
| auto sp1 = c.symmetry_copy(a.get_location(), "2_565"_symop); |
| REQUIRE_THAT(sp1.m_x, Catch::Matchers::WithinAbs(p.m_x, 0.5f)); |
| REQUIRE_THAT(sp1.m_y, Catch::Matchers::WithinAbs(p.m_y, 0.5f)); |
| REQUIRE_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); |
|
|
| REQUIRE_THAT(sp2.m_x, Catch::Matchers::WithinAbs(p.m_x, 0.5f)); |
| REQUIRE_THAT(sp2.m_y, Catch::Matchers::WithinAbs(p.m_y, 0.5f)); |
| REQUIRE_THAT(sp2.m_z, Catch::Matchers::WithinAbs(p.m_z, 0.5f)); |
|
|
| } |
|
|
| TEST_CASE("symm_2bi3_1, *utf::tolerance(0.1f)") |
| { |
| cif::file f(gTestDir / "2bi3.cif.gz"); |
|
|
| 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 sa1 = c.symmetry_copy(a1.get_location(), cif::sym_op(symm1)); |
| auto sa2 = c.symmetry_copy(a2.get_location(), cif::sym_op(symm2)); |
|
|
| REQUIRE_THAT(cif::distance(sa1, sa2), Catch::Matchers::WithinAbs(dist, 0.5f)); |
|
|
| auto pa1 = a1.get_location(); |
|
|
| const auto &[d, p, so] = c.closest_symmetry_copy(pa1, a2.get_location()); |
|
|
| REQUIRE_THAT(p.m_x, Catch::Matchers::WithinAbs(sa2.m_x, 0.5f)); |
| REQUIRE_THAT(p.m_y, Catch::Matchers::WithinAbs(sa2.m_y, 0.5f)); |
| REQUIRE_THAT(p.m_z, Catch::Matchers::WithinAbs(sa2.m_z, 0.5f)); |
|
|
| REQUIRE_THAT(d, Catch::Matchers::WithinAbs(dist, 0.5f)); |
| REQUIRE(so.string() == symm2); |
| } |
| } |
|
|
| TEST_CASE("symm_2bi3_1a, *utf::tolerance(0.1f)") |
| { |
| using namespace cif::literals; |
|
|
| cif::file f(gTestDir / "2bi3.cif.gz"); |
|
|
| auto &db = f.front(); |
|
|
| cif::crystal c(db); |
| auto struct_conn = 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,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", |
| "ptnr2_label_asym_id", "ptnr2_label_seq_id", "ptnr2_auth_seq_id", "ptnr2_label_atom_id", "ptnr2_symmetry", |
| "pdbx_dist_value" |
| )) |
| { |
| 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"); |
| 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(p1, cif::sym_op(symm1)); |
| auto sa2 = c.symmetry_copy(p2, cif::sym_op(symm2)); |
|
|
| REQUIRE_THAT(cif::distance(sa1, sa2), Catch::Matchers::WithinAbs(dist, 0.5f)); |
|
|
| const auto &[d, p, so] = c.closest_symmetry_copy(p1, p2); |
|
|
| REQUIRE_THAT(p.m_x, Catch::Matchers::WithinAbs(sa2.m_x, 0.5f)); |
| REQUIRE_THAT(p.m_y, Catch::Matchers::WithinAbs(sa2.m_y, 0.5f)); |
| REQUIRE_THAT(p.m_z, Catch::Matchers::WithinAbs(sa2.m_z, 0.5f)); |
|
|
| REQUIRE_THAT(d, Catch::Matchers::WithinAbs(dist, 0.5f)); |
| REQUIRE(so.string() == symm2); |
| } |
| } |
|
|
| TEST_CASE("symm_3bwh_1, *utf::tolerance(0.1f)") |
| { |
| cif::file f(gTestDir / "3bwh.cif.gz"); |
|
|
| auto &db = f.front(); |
|
|
| cif::crystal c(db); |
| cif::mm::structure s(db); |
|
|
| for (auto a1 : s.atoms()) |
| { |
| for (auto a2 : s.atoms()) |
| { |
| if (a1 == a2) |
| continue; |
| |
| const auto&[ d, p, so ] = c.closest_symmetry_copy(a1.get_location(), a2.get_location()); |
|
|
| REQUIRE_THAT(d, Catch::Matchers::WithinAbs(distance(a1.get_location(), p), 0.5f)); |
| } |
| } |
| } |
|
|
| TEST_CASE("volume_3bwh_1, *utf::tolerance(0.1f)") |
| { |
| cif::file f(gTestDir / "1juh.cif.gz"); |
|
|
| auto &db = f.front(); |
|
|
| cif::crystal c(db); |
|
|
| REQUIRE_THAT(c.get_cell().get_volume(), Catch::Matchers::WithinRel(741009.625f, 0.01f)); |
| } |
|
|
|
|