diff --git a/modules/conop/src/compound.hh b/modules/conop/src/compound.hh index 082427879c888a8b83ab4d1c86a50034a5dc1a5a..d3f47bd973166a8bdc169729eef1be8db61a9d50 100644 --- a/modules/conop/src/compound.hh +++ b/modules/conop/src/compound.hh @@ -169,6 +169,11 @@ public: bool IsPeptideLinking() const { return chem_class_.IsPeptideLinking(); } + + bool IsNucleotideLinking() const { + return chem_class_.IsNucleotideLinking(); + } + void AddAtom(const AtomSpec& atom) { atom_specs_.push_back(atom); } @@ -223,4 +228,4 @@ private: }} -#endif \ No newline at end of file +#endif diff --git a/modules/conop/src/rule_based_builder.cc b/modules/conop/src/rule_based_builder.cc index 9a0e8785175242a4a5e559ff719ae1e81a06f7d1..2a0a50010c278a878b9d4201152fbab517516bd5 100644 --- a/modules/conop/src/rule_based_builder.cc +++ b/modules/conop/src/rule_based_builder.cc @@ -255,29 +255,36 @@ void RuleBasedBuilder::ConnectResidueToNext(mol::ResidueHandle rh, if (!next.IsValid()) { return; } + Compound::Dialect dialect=this->GetDialect()==PDB_DIALECT ? Compound::PDB : Compound::CHARMM; + mol::XCSEditor e=rh.GetEntity().EditXCS(mol::BUFFERED_EDIT); CompoundPtr mc=compound_lib_->FindCompound(rh.GetName(), dialect); CompoundPtr nc=compound_lib_->FindCompound(next.GetName(), dialect); if (!(mc && nc)) return; + // check if both of the residues are able to form a peptide bond. - if (!mc->IsPeptideLinking()) - return; - if (!nc->IsPeptideLinking()) - return; - // If we have an OXT then there is no peptide bond connecting the two - // residues. - if (rh.FindAtom("OXT")) - return; - mol::AtomHandle c=rh.FindAtom("C"); - mol::AtomHandle n=next.FindAtom("N"); - // Give subclasses a chance to give us their opinions on the feasibility of - // the peptide bond. - if (c.IsValid() && n.IsValid() && this->DoesPeptideBondExist(c, n)) { - e.Connect(c, n, 1); - rh.SetIsProtein(true); - next.SetIsProtein(true); + if (mc->IsPeptideLinking() && nc->IsPeptideLinking()) { + // If we have an OXT then there is no peptide bond connecting the two + // residues. + if (rh.FindAtom("OXT")) + return; + mol::AtomHandle c=rh.FindAtom("C"); + mol::AtomHandle n=next.FindAtom("N"); + // Give subclasses a chance to give us their opinions on the feasibility of + // the peptide bond. + if (c.IsValid() && n.IsValid() && this->DoesPeptideBondExist(c, n)) { + e.Connect(c, n, 1); + rh.SetIsProtein(true); + next.SetIsProtein(true); + } + } else if (mc->IsNucleotideLinking() && nc->IsNucleotideLinking()) { + mol::AtomHandle c=rh.FindAtom("O3'"); + mol::AtomHandle n=next.FindAtom("P"); + if (c.IsValid() && n.IsValid() && this->IsBondFeasible(c, n)) { + e.Connect(c, n, 1); + } } } diff --git a/modules/conop/tests/CMakeLists.txt b/modules/conop/tests/CMakeLists.txt index ef1005889917aa29b0e61fb14870dde66b1ccdab..aa06791bf0be4d64640499010893d9db179f3e33 100644 --- a/modules/conop/tests/CMakeLists.txt +++ b/modules/conop/tests/CMakeLists.txt @@ -1,5 +1,6 @@ set(OST_CONOP_UNIT_TESTS test_heuristic_builder.cc + test_rule_based_builder.cc tests.cc test_builder.cc test_compound.py diff --git a/modules/conop/tests/test_rule_based_builder.cc b/modules/conop/tests/test_rule_based_builder.cc new file mode 100644 index 0000000000000000000000000000000000000000..2232ce3d6531bce53d00060c6bc84c71e51b294b --- /dev/null +++ b/modules/conop/tests/test_rule_based_builder.cc @@ -0,0 +1,241 @@ +// ----------------------------------------------------------------------------- +// This file is part of the OpenStructure project <www.openstructure.org> + +// Copyright (C) 2008-2011 by the OpenStructure authors + +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 3.0 of the License, or (at your option) +// any later version. +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +// details. + +// You should have received a copy of the GNU Lesser General Public License +// along with this library; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#include <ost/mol/mol.hh> +#include <ost/platform.hh> +#include <ost/conop/rule_based_builder.hh> + +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> + +#include <ost/log.hh> +using boost::unit_test_framework::test_suite; +using namespace ost; +using namespace ost::conop; +//using namespace ost::io; +using namespace ost::mol; +namespace { +} // anon ns + +ResidueHandle make_cytosine(ChainHandle chain) +{ + XCSEditor e=chain.GetEntity().EditXCS(); + ResidueHandle res=e.AppendResidue(chain, "DC"); + + e.InsertAtom(res, "P", geom::Vec3(21.412, 34.391, 37.142)); + e.InsertAtom(res, "OP1", geom::Vec3(22.938, 34.599, 36.988)); + e.InsertAtom(res, "OP2", geom::Vec3(20.690, 35.640, 37.689)); + e.InsertAtom(res, "O5'", geom::Vec3(21.215, 33.299, 38.339)); + e.InsertAtom(res, "C5'", geom::Vec3(20.524, 33.660, 39.548)); + e.InsertAtom(res, "C4'", geom::Vec3(19.064, 33.285, 39.452)); + e.InsertAtom(res, "O4'", geom::Vec3(18.246, 34.411, 39.034)); + e.InsertAtom(res, "C3'", geom::Vec3(18.778, 32.150, 38.469)); + e.InsertAtom(res, "O3'", geom::Vec3(17.930, 31.199, 39.081)); + e.InsertAtom(res, "C2'", geom::Vec3(18.046, 32.827, 37.330)); + e.InsertAtom(res, "C1'", geom::Vec3(17.326, 33.955, 38.048)); + e.InsertAtom(res, "N1", geom::Vec3(16.891, 35.100, 37.196)); + e.InsertAtom(res, "C2", geom::Vec3(15.709, 35.782, 37.543)); + e.InsertAtom(res, "O2", geom::Vec3(15.137, 35.489, 38.610)); + e.InsertAtom(res, "N3", geom::Vec3(15.226, 36.742, 36.710)); + e.InsertAtom(res, "C4", geom::Vec3(15.878, 37.041, 35.582)); + e.InsertAtom(res, "N4", geom::Vec3(15.324, 37.943, 34.762)); + e.InsertAtom(res, "C5", geom::Vec3(17.116, 36.415, 35.238)); + e.InsertAtom(res, "C6", geom::Vec3(17.584, 35.464, 36.067)); + + return res; +} + +ResidueHandle make_uracil1(ChainHandle chain) +{ + XCSEditor e=chain.GetEntity().EditXCS(); + ResidueHandle res = e.AppendResidue(chain, "U"); + + e.InsertAtom(res, "P", geom::Vec3(18.533, 30.238, 40.226)); + e.InsertAtom(res, "OP1", geom::Vec3(20.012, 30.098, 40.003)); + e.InsertAtom(res, "OP2", geom::Vec3(17.680, 29.017, 40.258)); + e.InsertAtom(res, "O5'", geom::Vec3(18.237, 31.056, 41.570)); + e.InsertAtom(res, "C5'", geom::Vec3(17.104, 31.923, 41.642)); + e.InsertAtom(res, "C4'", geom::Vec3(16.332, 31.729, 42.937)); + e.InsertAtom(res, "O4'", geom::Vec3(15.180, 32.616, 42.841)); + e.InsertAtom(res, "C3'", geom::Vec3(15.785, 30.319, 43.194)); + e.InsertAtom(res, "O3'", geom::Vec3(15.960, 29.834, 44.527)); + e.InsertAtom(res, "C2'", geom::Vec3(14.311, 30.371, 42.766)); + e.InsertAtom(res, "O2'", geom::Vec3(13.442, 29.708, 43.677)); + e.InsertAtom(res, "C1'", geom::Vec3(13.972, 31.863, 42.765)); + e.InsertAtom(res, "N1", geom::Vec3(13.282, 32.212, 41.499)); + e.InsertAtom(res, "C2", geom::Vec3(12.072, 32.896, 41.568)); + e.InsertAtom(res, "O2", geom::Vec3(11.510, 33.142, 42.626)); + e.InsertAtom(res, "N3", geom::Vec3(11.535, 33.257, 40.349)); + e.InsertAtom(res, "C4", geom::Vec3(12.048, 32.976, 39.096)); + e.InsertAtom(res, "O4", geom::Vec3(11.490, 33.447, 38.087)); + e.InsertAtom(res, "C5", geom::Vec3(13.268, 32.207, 39.106)); + e.InsertAtom(res, "C6", geom::Vec3(13.831, 31.872, 40.261)); + + return res; +} + +ResidueHandle make_uracil2(ChainHandle chain) +{ + XCSEditor e=chain.GetEntity().EditXCS(); + ResidueHandle res = e.AppendResidue(chain, "U"); + + e.InsertAtom(res, "P", geom::Vec3(16.249, 28.254, 44.759)); + e.InsertAtom(res, "OP1", geom::Vec3(16.654, 28.055, 46.181)); + e.InsertAtom(res, "OP2", geom::Vec3(17.115, 27.731, 43.656)); + e.InsertAtom(res, "O5'", geom::Vec3(14.826, 27.572, 44.574)); + e.InsertAtom(res, "C5'", geom::Vec3(14.711, 26.206, 44.216)); + e.InsertAtom(res, "C4'", geom::Vec3(13.279, 25.889, 43.887)); + e.InsertAtom(res, "O4'", geom::Vec3(12.832, 26.793, 42.838)); + e.InsertAtom(res, "C3'", geom::Vec3(13.155, 24.434, 43.329)); + e.InsertAtom(res, "O3'", geom::Vec3(12.269, 23.625, 44.098)); + e.InsertAtom(res, "C2'", geom::Vec3(12.871, 24.595, 41.875)); + e.InsertAtom(res, "O2'", geom::Vec3(11.811, 23.752, 41.462)); + e.InsertAtom(res, "C1'", geom::Vec3(12.424, 26.056, 41.694)); + e.InsertAtom(res, "N1", geom::Vec3(13.030, 26.692, 40.497)); + e.InsertAtom(res, "C2", geom::Vec3(12.517, 26.365, 39.228)); + e.InsertAtom(res, "O2", geom::Vec3(11.579, 25.594, 39.068)); + e.InsertAtom(res, "N3", geom::Vec3(13.141, 26.987, 38.161)); + e.InsertAtom(res, "C4", geom::Vec3(14.197, 27.888, 38.210)); + e.InsertAtom(res, "O4", geom::Vec3(14.627, 28.368, 37.156)); + e.InsertAtom(res, "C5", geom::Vec3(14.671, 28.189, 39.542)); + e.InsertAtom(res, "C6", geom::Vec3(14.087, 27.597, 40.612)); + + return res; +} + +void verify_nucleotide_connectivity(const ResidueHandle& res) +{ + BOOST_CHECK(BondExists(res.FindAtom("P"), + res.FindAtom("OP1"))); + BOOST_CHECK(BondExists(res.FindAtom("P"), + res.FindAtom("OP2"))); + BOOST_CHECK(BondExists(res.FindAtom("P"), + res.FindAtom("O5'"))); + BOOST_CHECK(BondExists(res.FindAtom("O5'"), + res.FindAtom("C5'"))); + BOOST_CHECK(BondExists(res.FindAtom("C5'"), + res.FindAtom("C4'"))); + BOOST_CHECK(BondExists(res.FindAtom("C4'"), + res.FindAtom("O4'"))); + BOOST_CHECK(BondExists(res.FindAtom("C4'"), + res.FindAtom("C3'"))); + BOOST_CHECK(BondExists(res.FindAtom("C3'"), + res.FindAtom("O3'"))); + BOOST_CHECK(BondExists(res.FindAtom("C3'"), + res.FindAtom("C2'"))); + BOOST_CHECK(BondExists(res.FindAtom("C2'"), + res.FindAtom("C1'"))); + BOOST_CHECK(BondExists(res.FindAtom("C1'"), + res.FindAtom("O4'"))); + + if (res.GetKey()=="DC") { + BOOST_CHECK(BondExists(res.FindAtom("C1'"), + res.FindAtom("N1"))); + BOOST_CHECK(BondExists(res.FindAtom("N1"), + res.FindAtom("C2"))); + BOOST_CHECK(BondExists(res.FindAtom("C2"), + res.FindAtom("O2"))); + BOOST_CHECK(BondExists(res.FindAtom("C2"), + res.FindAtom("N3"))); + BOOST_CHECK(BondExists(res.FindAtom("N3"), + res.FindAtom("C4"))); + BOOST_CHECK(BondExists(res.FindAtom("C4"), + res.FindAtom("N4"))); + BOOST_CHECK(BondExists(res.FindAtom("C4"), + res.FindAtom("C5"))); + BOOST_CHECK(BondExists(res.FindAtom("C5"), + res.FindAtom("C6"))); + BOOST_CHECK(BondExists(res.FindAtom("C6"), + res.FindAtom("N1"))); + // TODO: Check that no other atoms are connected! + } + + if (res.GetKey()=="U") { + BOOST_CHECK(BondExists(res.FindAtom("O2'"), + res.FindAtom("C2'"))); + BOOST_CHECK(BondExists(res.FindAtom("C1'"), + res.FindAtom("N1"))); + BOOST_CHECK(BondExists(res.FindAtom("N1"), + res.FindAtom("C2"))); + BOOST_CHECK(BondExists(res.FindAtom("C2"), + res.FindAtom("O2"))); + BOOST_CHECK(BondExists(res.FindAtom("C2"), + res.FindAtom("N3"))); + BOOST_CHECK(BondExists(res.FindAtom("N3"), + res.FindAtom("C4"))); + BOOST_CHECK(BondExists(res.FindAtom("C4"), + res.FindAtom("O4"))); + BOOST_CHECK(BondExists(res.FindAtom("C4"), + res.FindAtom("C5"))); + BOOST_CHECK(BondExists(res.FindAtom("C5"), + res.FindAtom("C6"))); + BOOST_CHECK(BondExists(res.FindAtom("C6"), + res.FindAtom("N1"))); + // TODO: Check that no other atoms are connected! + } +} +void verify_nucleotide_link(const ResidueHandle& p3, const ResidueHandle& p5) +{ + BOOST_CHECK(BondExists(p3.FindAtom("O3'"), + p5.FindAtom("P"))); +} + +BOOST_AUTO_TEST_SUITE( conop ) + + +BOOST_AUTO_TEST_CASE(nucleotide_based_connect) +{ + SetPrefixPath(getenv("OST_ROOT")); + String lib_path=GetSharedDataPath()+"/compounds.chemlib"; + CompoundLibPtr compound_lib=CompoundLib::Load(lib_path); + if (!compound_lib) { + std::cout << "WARNING: skipping NUCLEOTIDE_BASED connect unit test. " + << "Rule-based builder is required" << std::endl; + return; + } + RuleBasedBuilder rb_builder = RuleBasedBuilder(compound_lib); + + EntityHandle e=CreateEntity(); + ChainHandle c=e.EditXCS().InsertChain("A"); + ResidueHandle c0=make_cytosine(c); + ResidueHandle u1=make_uracil1(c); + ResidueHandle u2=make_uracil2(c); + + for (AtomHandleIter i=e.AtomsBegin(),x=e.AtomsEnd(); i!=x; ++i) { + rb_builder.FillAtomProps(*i); + } + + BOOST_MESSAGE("running distance based checks on cytosine"); + rb_builder.ConnectAtomsOfResidue(c0); + verify_nucleotide_connectivity(c0); + BOOST_MESSAGE("running distance based checks on first uracil"); + rb_builder.ConnectAtomsOfResidue(u1); + verify_nucleotide_connectivity(u1); + BOOST_MESSAGE("running distance based checks on second uracil"); + rb_builder.ConnectAtomsOfResidue(u2); + verify_nucleotide_connectivity(u2); + BOOST_MESSAGE("connecting cytosine to first uracil"); + rb_builder.ConnectResidueToNext(c0, u1); + verify_nucleotide_link(c0, u1); + BOOST_MESSAGE("connecting first uracil to second uracil"); + rb_builder.ConnectResidueToNext(u1, u2); + verify_nucleotide_link(u1, u2); +} + +BOOST_AUTO_TEST_SUITE_END( )