diff --git a/database/chemical/rdkit/fpscores.gz b/database/chemical/rdkit/fpscores.gz new file mode 100644 index 0000000000..c6addc6c90 Binary files /dev/null and b/database/chemical/rdkit/fpscores.gz differ diff --git a/source/projects.settings b/source/projects.settings index f15ab29802..e1cf4157ef 100644 --- a/source/projects.settings +++ b/source/projects.settings @@ -60,7 +60,7 @@ projects = { "libxml2", "libzmq", "rdkit", - "bcl", # Fine to have here, just make sure bcl.external.settings has extras qualifier + "bcl", # Fine to have here, just make sure bcl.external.settings has extras qualifier "cmaes", ] } diff --git a/source/src/core.2.src.settings b/source/src/core.2.src.settings index b0ab1987b4..92f563448d 100644 --- a/source/src/core.2.src.settings +++ b/source/src/core.2.src.settings @@ -68,11 +68,10 @@ sources = { "util", "VariantType_mappings", ], - - "core/chemical/bcl": [ - "BCLFragmentHandler", - "util", - ], + "core/chemical/bcl": [ + "BCLFragmentHandler", + "util", + ], "core/chemical/carbohydrates": [ "CarbohydrateInfo", "CarbohydrateInfoManager", @@ -94,8 +93,10 @@ sources = { "MainchainScoreTable", "util", ], - "core/chemical/modifications/": [ + "core/chemical/modifications": [ "ChemistryBase", + "Reprotonate", + "ValenceHandler", ], "core/chemical/rings": [ "ring_conformer_io", diff --git a/source/src/core/chemical/modifications/Reprotonate.cc b/source/src/core/chemical/modifications/Reprotonate.cc new file mode 100644 index 0000000000..8b90a15512 --- /dev/null +++ b/source/src/core/chemical/modifications/Reprotonate.cc @@ -0,0 +1,259 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file core/chemical/modifications/Reprotonate.cc +/// @brief Class to add hydrogens to a ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +// Package headers +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace core { +namespace chemical { +namespace modifications { + +static basic::Tracer TR("core.chemical.modifications.Reprotonate"); + +/// @brief Comes up with a new name for a hydrogen to add. (One that isn't used.) +std::string +get_new_hydrogen_name( MutableResidueType & res ) { + core::Size index( 0 ); + std::string name; + do { + ++index; + name = "H" + std::to_string( index + 1 ); + } while( res.has( name ) ); + // Need to adjust the naming to fit the four-sized value. + if ( name.size() == 2 ) { + name = " " + name + " "; + } else if ( name.size() == 3 ) { + name = " " + name; + } + return name ; +} + +/// @brief Add hydrogens to the given Atom, based on existing geometry and Gasteiger atom type +/// Returns the number of hydrogens added. +core::Size +add_hydrogens_to_atom( MutableResidueType & res, VD atom_vd ) { + utility::vector1 > coords( determine_coordinates(res, atom_vd) ); + TR << "Adding " << coords.size() << " hydrogens to atom " << res.atom_name( atom_vd ) << " of type " << res.atom( atom_vd ).gasteiger_atom_type()->get_name() + << " and " << res.number_bonded_hydrogens( atom_vd ) << " existing hydrogens " << std::endl; + for ( Size new_hydrogens=1; new_hydrogens <= coords.size(); ++new_hydrogens ) { + std::string name = get_new_hydrogen_name( res ); + TR.Debug << "Added hydrogen name: " << name << std::endl; + VD vertex_hydrogen = res.add_atom(name); + res.atom(vertex_hydrogen).ideal_xyz(coords[new_hydrogens]); + res.atom(vertex_hydrogen).element_type( res.element_set().element( element::H ) ); //dont forget to add the element type! + res.add_bond(vertex_hydrogen, atom_vd, SingleBond); + } + return coords.size(); +} + +/// @details The current targets are: +/// Deprotonation: +/// * Carboxylates +/// * Thiocarboxylates ( C(=O)-SH as well as C(=S)-OH, if existant) +/// * Sulfates/Phosphates/Nitrates/etc (as well as thio equivalents) +/// * NOT Phenols +/// +/// Protonation +/// * (aliphatic) Amines +/// * NOT Amides, anilines, or conjugated amines +/// +/// Protonation/deprotonation will not happen on atoms with existing formal charges. +/// The appropriate formal charge will be added to the reprotonated atom. +/// +void +Reprotonate::apply( MutableResidueType & res ) { + // Heavy atoms on which to protonate/deprotonate + // We notate then alter to avoid modifying the residue type as we're iterating over it. + utility::vector1< VD > protonate; + utility::vector1< VD > deprotonate; + + mapping_ = core::chemical::VDVDMapping(true); // Start with the identity mapping. + + for ( VIterPair iters(res.atom_iterators()); iters.first != iters.second; ++iters.first ) { + VD atom_vd( *iters.first ); + core::chemical::Atom const & atom( res.atom( atom_vd ) ); + // Don't touch things with formal charges already. + if ( atom.formal_charge() != 0 ) { continue; } + + if ( atom.element() == element::O || atom.element() == element::S ) { + + // Heuristic - deprotonate an oxygen/sulfur if it's bonded to an element which is double bonded + // to an oxygen/sulfur. This should handle (thio)carboxylates as well as phosphates, nitrates, sulfates, etc. + if ( res.nbonds( atom_vd ) != 2 ) { continue; } // Only deal with ~X(=O)-O-H motifs + VD hydrogen( MutableResidueType::null_vertex ); + VD electron_sink( MutableResidueType::null_vertex ); + for ( OutEdgeIterPair eiters( boost::out_edges(atom_vd, res.graph()) ); eiters.first != eiters.second; ++eiters.first ) { + if ( res.bond( *eiters.first ).order() != SingleBondOrder ) { + // Oops - the atom of interest needs to be single bonded only + electron_sink = hydrogen = MutableResidueType::null_vertex; + break; + } + VD attached = boost::target(*eiters.first, res.graph() ); + assert( attached != atom_vd); + if ( res.atom( attached ).element() == element::H ) { + hydrogen = attached; + } else { + VD putative_sink( find_electron_sink( res, attached ) ); + if ( putative_sink != MutableResidueType::null_vertex && putative_sink != atom_vd ) { + electron_sink = putative_sink; + } + } + } // For all bonds to atom of interest + if ( hydrogen != MutableResidueType::null_vertex && electron_sink != MutableResidueType::null_vertex ) { + deprotonate.push_back( atom_vd ); + } + + } else if ( atom.element() == element::N ) { + // Heuristic - add a proton to nitrogen if it has three single bonds, and none of the attached atoms are conjugatable. + if ( res.nbonds( atom_vd ) != 3 ) { continue; } // Only deal with nitrogens with three bonds. + bool valid( true ); + for ( OutEdgeIterPair eiters( boost::out_edges(atom_vd, res.graph()) ); eiters.first != eiters.second; ++eiters.first ) { + if ( res.bond( *eiters.first ).order() != SingleBondOrder ) { + // Oops - the atom of interest needs to be single bonded only + valid = false; + break; + } + VD attached = boost::target(*eiters.first, res.graph() ); + assert( attached != atom_vd); + if ( is_conjugatable( res, attached ) ) { + valid = false; + break; + } + } // For all bonds to atom of interest + if ( valid ) { + protonate.push_back( atom_vd ); + } + } // Element if/else + } // For all atoms in residue type + + if ( deprotonate.size() == 0 && protonate.size() ==0 ) { + TR << "No protonation changes for " << res.name() << std::endl; + return; // Nothing to do, so do nothing. + } + // Update charges, delete hydrogens from all modified atoms + // Then re-do gasteiger atom typing, and add missing hydrogens to the relevant atoms. + + for ( core::Size ii(1); ii <= deprotonate.size(); ++ii ) { + VD atom( deprotonate[ii] ); + runtime_assert( res.atom( atom ).formal_charge() == 0 ); + TR << "Removing proton from " << res.atom_name( atom ) << " on " << res.name() << std::endl; + res.atom( atom ).formal_charge( -1 ); + core::Size n_removed = remove_hydrogens( res, atom ); + if ( n_removed != 1 ) { + TR.Warning << "Attempted to remove single proton from " << res.atom_name( atom ) << " but " << n_removed << " were removed instead." << std::endl; + } + } + + for ( core::Size ii(1); ii <= protonate.size(); ++ii ) { + VD atom( protonate[ii] ); + runtime_assert( res.atom( atom ).formal_charge() == 0 ); + //TR << "Adding proton to " << res.atom_name( atom ) << std::endl; + res.atom( atom ).formal_charge( +1 ); + // // If we wanted to adjust the positioning of the hydrogens on the atom, we could remove/add + // // But in all current use cases the Nitrogen should already be tetrahedral. + //remove_hydrogens( res, atom ); // They'll be added back later + } + + core::chemical::gasteiger::assign_gasteiger_atom_types( res, /*keep_existing*/ false ); + + // // Currently, we only consider di-valent oxygens or sulfurs for deprotonation - + // // we don't need to worry about rearragment of other existing hydrogens on the atom + // for( core::Size ii(1); ii <= deprotonate.size(); ++ii ) { + // VD atom( deprotonate[ii] ); + // add_hydrogens_to_atom( res, atom ); + // } + + for ( core::Size ii(1); ii <= protonate.size(); ++ii ) { + VD atom( protonate[ii] ); + TR << "Adding proton to " << res.atom_name( atom ) << " on " << res.name() << std::endl; + core::Size n_added = add_hydrogens_to_atom( res, atom ); + if ( ! n_added ) { + TR.Warning << "Attempted to add single proton to " << res.atom_name( atom ) << " but " << n_added << " were added instead." << std::endl; + } + } + + // Do standard semi-derived data updating. + + rosetta_retype_fullatom(res, false); //need to do this, fails currently + + rosetta_recharge_fullatom(res); + core::chemical::find_bonds_in_rings( res ); + res.assign_internal_coordinates(); // Also sets atom base. Needs nbr atom assignment + res.autodetermine_chi_bonds(); // Must be after internal coordinate setup + set_last_status( SUCCESS ); +} + +/// @brief Find a doubly-bonded oxygen or sulfur attached to neighbor +VD +Reprotonate::find_electron_sink(MutableResidueType const & res, VD neighbor) const { + // Iterate through all bonds, and see if there are any double bonded oxygens/sulfurs + for ( OutEdgeIterPair eiters( boost::out_edges(neighbor, res.graph()) ); eiters.first != eiters.second; ++eiters.first ) { + if ( res.bond( *eiters.first ).order() != DoubleBondOrder ) { continue; } + VD attached = boost::target(*eiters.first, res.graph() ); + if ( res.atom( attached ).element() == element::O || res.atom( attached ).element() == element::S ) { + return attached; + } + } + return MutableResidueType::null_vertex; +} + +/// @brief Is the provided atom able to participate in conjugation? +bool +Reprotonate::is_conjugatable(MutableResidueType const & res, VD atom) const { + // Iterate through all bonds, and see if there are any double, triple, atomatic, etc bonds + for ( OutEdgeIterPair eiters( boost::out_edges(atom, res.graph()) ); eiters.first != eiters.second; ++eiters.first ) { + Bond const & bond( res.bond( *eiters.first ) ); + core::chemical::BondOrder order( bond.order() ); + if ( order == DoubleBondOrder || order == TripleBondOrder ) { return true; } + if ( bond.aromaticity() == IsAromaticBond ) { return true; } + } + return false; +} + +core::Size +Reprotonate::remove_hydrogens(MutableResidueType & restype, VD atom) { + utility::vector1< VD > to_remove; + for ( AdjacentIterPair iter( restype.bonded_neighbor_iterators(atom) ); iter.first != iter.second; ++iter.first ) { + if ( restype.atom( *iter.first ).element() == element::H ) { + to_remove.push_back( *iter.first ); + } + } + for ( core::Size ii(1); ii <= to_remove.size(); ++ii ) { + mapping_.invalidate( to_remove[ii] ); + restype.delete_atom( to_remove[ii] ); + } + return to_remove.size(); +} + +std::string +Reprotonate::class_name() { + return "Reprotonate"; +} + +} +} +} + + diff --git a/source/src/core/chemical/modifications/Reprotonate.fwd.hh b/source/src/core/chemical/modifications/Reprotonate.fwd.hh new file mode 100644 index 0000000000..09e83e16fd --- /dev/null +++ b/source/src/core/chemical/modifications/Reprotonate.fwd.hh @@ -0,0 +1,33 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file core/chemical/modifications/Reprotonate.fwd.hh +/// @brief Heuristically reprotonate a molecule to be in neutral pH +/// @author Rocco Moretti (rmorettiase@gmail.com) + + +#ifndef INCLUDED_core_chemical_modifications_Reprotonate_fwd_hh +#define INCLUDED_core_chemical_modifications_Reprotonate_fwd_hh + + +#include + +namespace core { +namespace chemical { +namespace modifications { + +class Reprotonate; + +typedef utility::pointer::shared_ptr< Reprotonate > ReprotonateOP; + + +} +} +} +#endif diff --git a/source/src/core/chemical/modifications/Reprotonate.hh b/source/src/core/chemical/modifications/Reprotonate.hh new file mode 100644 index 0000000000..d7f0947271 --- /dev/null +++ b/source/src/core/chemical/modifications/Reprotonate.hh @@ -0,0 +1,72 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file core/chemical/modifications/Reprotonate.hh +/// @brief Heuristically reprotonate a molecule to be in neutral pH +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_core_chemical_modifications_Reprotonate_hh +#define INCLUDED_core_chemical_modifications_Reprotonate_hh + +#include +#include + +#include +#include + +namespace core { +namespace chemical { +namespace modifications { + + +class Reprotonate : public ChemistryBase +{ +public: + Reprotonate() : + ChemistryBase(class_name()) + {} + + /// @brief Search the provided ResidueType for protons which should be added/removed + /// in order to conform to the protonation state in neutral (pH 7-ish) aqueous solution. + /// This is done heuristically, assuming that protons have already been added. + /// The geometry of any existing hydrogen atoms will be adjusted, but not any heavy atoms. + void apply(MutableResidueType & res ) override; + + core::chemical::VDVDMapping + get_mapping() const override { + return mapping_; + } + + static std::string class_name(); + +private: + /// @brief Find a doubly-bonded oxygen or sulfur attached to neighbor + VD + find_electron_sink(MutableResidueType const & restype, VD neighbor) const; + + /// @brief Is the provided able to participate in conjugation? + bool + is_conjugatable(MutableResidueType const & restype, VD atom) const; + + /// @brief Remove hydrogens from specified atom. + /// Returns the number of hydrogens removed + core::Size + remove_hydrogens(MutableResidueType & restype, VD atom); + +private: + + core::chemical::VDVDMapping mapping_; + +}; + +} +} +} + +#endif diff --git a/source/src/core/chemical/modifications/ValenceHandler.cc b/source/src/core/chemical/modifications/ValenceHandler.cc new file mode 100644 index 0000000000..f97c838cb2 --- /dev/null +++ b/source/src/core/chemical/modifications/ValenceHandler.cc @@ -0,0 +1,524 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. +// +// This file is based off of code from the Biochemistry Library (BCL). +// The BCL is copyright Vanderbilt University (Meiler Lab), a RosettaCommons member + +/// @file core/chemical/modifications/ValenceHandler.cc +/// @brief A bunch of functions to determine where to place atoms based on hybridization of atoms and number of bonds +/// It should be noted that a vector of coordinates are returned. This is because it creates the angles for all +/// hydrogens that can be placed. If you are combining a fragment, you really only need the first index of coords +/// @author Rosetta conversion: Steven Combs / Sandeep Kothiwale + +#include +#include +#include +#include +#include +#include + +#include + +namespace core { +namespace chemical { +namespace modifications { + +static basic::Tracer TR("core.chemical.modifications.ValenceHandler"); + +utility::vector1 > determine_coordinates(core::chemical::MutableResidueType const & res, core::chemical::VD const & atom) { + utility::vector1 > positions; + + core::chemical::gasteiger::GasteigerAtomTypeDataCOP gasteiger_type( res.atom(atom).gasteiger_atom_type() ); + if ( ! gasteiger_type ) { + // Skip "unknown" atoms + TR << "Skipping atom " << res.atom_name( atom ) << " when determining missing hydrogen coordinates because it does not have an assigned Gasteiger type." << std::endl; + return positions; + } + + // get the hybrid orbital for the atom type + core::chemical::gasteiger::GasteigerAtomTypeData::HybridOrbitalType orbital_type( gasteiger_type->get_hybrid_orbital_type() ); + + if ( orbital_type == core::chemical::gasteiger::GasteigerAtomTypeData::Unhybridized ) { + TR << "Skipping atom " << res.atom_name( atom ) << " when determining missing hydrogen coordinates because its Gasteiger type (" << gasteiger_type->get_name() <<") is unhybridized." << std::endl; + return positions; + } + + // Can't use index-based bonded_neighbor(), as this ResidueType might not be finalized yet. + utility::vector1< core::chemical::VD > bonded_atoms; + for ( core::chemical::AdjacentIterPair bonded_iter( res.bonded_neighbor_iterators(atom) ); + bonded_iter.first != bonded_iter.second; + ++bonded_iter.first ) { + bonded_atoms.push_back( *bonded_iter.first ); + } + + core::Size const nr_known_bonds( bonded_atoms.size() ); + + //We assume here that each bonding partner uses one hybrid bond. + core::Size const nr_possible_bonds( gasteiger_type->get_number_hybrid_bonds() ); + + if ( nr_known_bonds > nr_possible_bonds ) { + TR.Warning << "For atom " << res.atom_name(atom) << " with type " << gasteiger_type->get_name() + << " number of known bonds (" << nr_known_bonds << ") exceeds number of possible bonds (" << nr_possible_bonds + << ") - refusing to add valences." << std::endl; + return positions; + } + + core::Size const nr_missing_bonds( nr_possible_bonds - nr_known_bonds ); + TR << "Missing " << nr_missing_bonds << " bonding partners on " << res.atom_name(atom) << " (" << nr_known_bonds << " known bonds.)" << std::endl; + if ( nr_missing_bonds == 0 ) { + //Exit early - nothing to do + return positions; + } + + // store the expected bond length, which is the covalent radius of H plus the covalent radius of ATOM + // TODO: Does this need to be calculated, or is a constant "good enough"? + const double bond_length( 1.0); + + // get the position of this atom + const numeric::xyzVector position( res.atom(atom).ideal_xyz()); + + //maybe we will need this functionality in the future, but for now, I can not imagine a case where the number of knwon bonds would be 0. + + /* if( nr_known_bonds == 0) { + positions = GetIdealizedGeometry( orbital_type, bond_length); + + // translate the points such that they are relative to position + for + ( + storage::Vector< linal::Vector3D>::iterator itr( positions.Begin()), itr_end( positions.End()); + itr != itr_end; + ++itr + ) + { + *itr += position; + } + }*/ + if ( nr_known_bonds == 1 ) { + if ( orbital_type == core::chemical::gasteiger::GasteigerAtomTypeData::SP ) { + TR << res.atom_name(atom) << ": sp1 atom with 1 known bond to "<< res.atom(bonded_atoms[1]).name() << " and " << nr_missing_bonds << " missing. " << std::endl; + assert( nr_missing_bonds == 1 ); + + positions.push_back( linear_coordinates( position, res.atom(bonded_atoms[1]).ideal_xyz(), bond_length)); + } else if ( orbital_type == core::chemical::gasteiger::GasteigerAtomTypeData::SP2 ) { + TR << res.atom_name(atom) << ": sp2 atom with 1 known bond to "<< res.atom(bonded_atoms[1]).name() << " and " << nr_missing_bonds << " missing. " << std::endl; + assert( nr_missing_bonds <= 2); + + utility::vector1< core::chemical::VD > bonds_neighbor; + for ( core::chemical::AdjacentIterPair bonded_iter( res.bonded_neighbor_iterators( bonded_atoms[1] ) ); + bonded_iter.first != bonded_iter.second; + ++bonded_iter.first ) { + bonds_neighbor.push_back( *bonded_iter.first ); + } + + utility::vector1 > neighboring_positions; + for ( core::Size i=1; i <= bonds_neighbor.size(); ++i ) { + if ( ( bonds_neighbor[i] != bonded_atoms[1] ) || ( bonds_neighbor[i] != atom ) ) { + neighboring_positions.push_back( res.atom(bonds_neighbor[i]).ideal_xyz() ); + break; + } + } + + if ( !neighboring_positions.empty() ) { + //Calculate the trans position from the first neighbor + TR << "Calculating sp2 one-bonded from first neighbor." << std::endl; + positions.push_back(position - (neighboring_positions[1] - res.atom(bonded_atoms[1]).ideal_xyz() ).normalize() * bond_length ); + } else { + TR << "Calculating sp2 one-bonded from no-neighbors." << std::endl; + // coordinate 1 + positions.push_back( + angle_coordinates( + position, + res.atom(bonded_atoms[1]).ideal_xyz(), + numeric::xyzVector( bond_length), + bond_length, + 120.0 / 180.0 * numeric::constants::d::pi, + 120.0 / 180.0 * numeric::constants::d::pi, + false, + numeric::xyzVector( 0.0) + ) + ); + } + + if ( nr_missing_bonds == 2 ) { + TR << "Adding second position to sp2 one-bonded." << std::endl; + // coordinate 2 + positions.push_back( + triganol_coordinates( + position, + res.atom(bonded_atoms[1]).ideal_xyz(), + positions[1], + bond_length + ) + ); + } + } else { // SP3 + TR << res.atom_name(atom) << ": sp3 atom with 1 known bond to "<< res.atom(bonded_atoms[1]).name() << " and " << nr_missing_bonds << " missing. " << std::endl; + assert( nr_missing_bonds <= 3 ); + + // coordinate 1 + positions.push_back( + angle_coordinates( + position, + res.atom(bonded_atoms[1]).ideal_xyz(), + numeric::xyzVector( bond_length), + bond_length, + 109.5 / 180.0 * numeric::constants::d::pi, + 109.5 / 180.0 * numeric::constants::d::pi, + false, + numeric::xyzVector( 0.0) + ) + ); + + if ( nr_missing_bonds >= 2 ) { + // helper coordinates + numeric::xyzVector foot_point( + triganol_coordinates( + position, + res.atom(bonded_atoms[1]).ideal_xyz(), + positions[1], + bond_length * std::cos( 54.75 / 180 * numeric::constants::d::pi) + ) + ); + numeric::xyzVector offset( + bond_length * std::sin( 54.75 / 180 * numeric::constants::d::pi) * + cross_product( + res.atom(bonded_atoms[1]).ideal_xyz() - position, + positions[1] - position + ).normalize() + ); + // coordinate 2 + positions.push_back( foot_point + offset); + + if ( nr_missing_bonds >= 3 ) { + // coordinate 3 + positions.push_back( foot_point - offset); + } + } + } + } else if ( nr_known_bonds == 2 ) { // 2 known bonds + if ( orbital_type == core::chemical::gasteiger::GasteigerAtomTypeData::SP2 ) { // trigonal + TR << res.atom_name(atom) << ": sp2 atom with 2 known bonds to "<< res.atom(bonded_atoms[1]).name() << " and " << res.atom(bonded_atoms[2]).name() + << " and " << nr_missing_bonds << " missing. " << std::endl; + assert( nr_missing_bonds == 1 ); + positions.push_back( + triganol_coordinates( + position, + res.atom(bonded_atoms[1]).ideal_xyz(), + res.atom(bonded_atoms[2]).ideal_xyz(), + bond_length + ) + ); + } else { // tetrahedral/SP3 (Shouldn't get here with SP1) + TR << res.atom_name(atom) << ": sp3 atom with 2 known bonds to "<< res.atom(bonded_atoms[1]).name() << " and " << res.atom(bonded_atoms[2]).name() + << " and " << nr_missing_bonds << " missing. " << std::endl; + assert( nr_missing_bonds <= 2 ); + // helper coordinates + numeric::xyzVector foot_point( + triganol_coordinates( + position, + res.atom(bonded_atoms[1]).ideal_xyz(), + res.atom(bonded_atoms[2]).ideal_xyz(), + bond_length * std::cos( 54.75 / 180 * numeric::constants::d::pi) + ) + ); + + numeric::xyzVector offset + ( + bond_length * std::sin( 54.75 / 180 * numeric::constants::d::pi) * + cross_product( + res.atom(bonded_atoms[1]).ideal_xyz() - position, + res.atom(bonded_atoms[2]).ideal_xyz() - position + ).normalize() + ); + + // coordinate 1 + positions.push_back( foot_point + offset); + + if ( nr_missing_bonds >= 2 ) { + // coordinate 2 + positions.push_back( foot_point - offset); + } + } + } else if ( nr_known_bonds == 3 ) { // tetrahedral geometry, only missing one bond + TR << res.atom_name(atom) << ": sp3 atom with 2 known bonds to "<< res.atom(bonded_atoms[1]).name() << ", " << res.atom(bonded_atoms[2]).name() + << " and " << res.atom(bonded_atoms[3]).name() << " and " << nr_missing_bonds << " missing. " << std::endl; + assert( nr_missing_bonds == 1 ); + positions.push_back + ( + tetrahedral_coordinates( + position, + res.atom(bonded_atoms[1]).ideal_xyz(), + res.atom(bonded_atoms[2]).ideal_xyz(), + res.atom(bonded_atoms[3]).ideal_xyz(), + bond_length + ) + ); + } else { + TR.Warning << "In core::chemical::modifications::determine_coordinates(), don't know how to handle atom " << res.atom_name(atom) << " with " + << nr_known_bonds << " known bonds (and " << nr_missing_bonds << " missing)" << std::endl; + } + + return positions; +} +/* + +//! @brief get the idealized geometry for a particular hybrid orbital type and bond length +//! @param ORBITAL_TYPE the type of orbital associated with the geometry +//! @param BOND_LENGTH the length of the bonds in the geometry +storage::Vector< linal::Vector3D> ValenceHandler::GetIdealizedGeometry +( +const HybridOrbitalType &ORBITAL_TYPE, +const double &BOND_LENGTH +) +{ +storage::Vector< linal::Vector3D> positions; + +if( ORBITAL_TYPE == GetHybridOrbitalTypes().e_SP3) +{ +positions.Resize( 4); +// put the H's at the vertices of a tetrahedron +// Thus, the bond lengths should have coordinates of bond length / sqrt( 3) is +const double bond_length_norm( BOND_LENGTH / std::sqrt( 3.0)); +positions( 0) = linal::Vector3D( bond_length_norm, -bond_length_norm, -bond_length_norm); +positions( 1) = linal::Vector3D( -bond_length_norm, bond_length_norm, -bond_length_norm); +positions( 2) = linal::Vector3D( -bond_length_norm, -bond_length_norm, bond_length_norm); +positions( 3) = linal::Vector3D( -bond_length_norm, -bond_length_norm, -bond_length_norm); +} +else if( ORBITAL_TYPE == GetHybridOrbitalTypes().e_SP2) +{ +positions.Resize( 3); +// put the vertices at the corners of an equalateral triangle centered at position +positions( 0) = linal::Vector3D( BOND_LENGTH, 0.0, 0.0); +positions( 1) = linal::Vector3D( -BOND_LENGTH / 2.0, BOND_LENGTH * std::sqrt( 3.0) / 2.0, 0.0); +positions( 2) = linal::Vector3D( -BOND_LENGTH / 2.0, -BOND_LENGTH * std::sqrt( 3.0) / 2.0, 0.0); +} +else if( ORBITAL_TYPE == GetHybridOrbitalTypes().e_SP) +{ +positions.Resize( 2); +positions( 0) = linal::Vector3D( BOND_LENGTH, 0.0, 0.0); +positions( 1) = linal::Vector3D( -BOND_LENGTH, 0.0, 0.0); +} + +return positions; + +} +*/ + + + + + +//! point X in B->A->X where A, B and X are on the same line +//! @brief calculates point X in B->A->X where A, B and X are on the same line +//! @param VECTOR_A first point +//! @param VECTOR_B second point +//! @param DISTANCE_XA distance between X and VECTOR_A +//! @return point X in B->A->X where A, B and X are on the same line +numeric::xyzVector linear_coordinates( + numeric::xyzVector const & vector_a, numeric::xyzVector const & vector_b, core::Real const distance_xa){ + // get the unit vector directed from B to A + numeric::xyzVector x = (vector_a - vector_b).normalize(); + // extend it the desired distance + x *= distance_xa; + // add it to point A + x += vector_a; + // return the calculated point + return x; +} + +//! @brief calculates coordinates using angle information (point X in B,C->A->X) +//! @param VECTOR_A first point +//! @param VECTOR_B second point +//! @param VECTOR_C third point +//! @param DISTANCE_XA distance between X and VECTOR_A +//! @param ANGLE_XAB angle between X, VECTOR_A, and VECTOR_B +//! @param ANGLE_XAC angle between X, VECTOR_A, and VECTOR_C +//! @param SIDE true if on a side +//! @param VECTOR_SIDE vector of the side +//! @return point X in B,C->A->X +numeric::xyzVector angle_coordinates( + const numeric::xyzVector &VECTOR_A, + const numeric::xyzVector &VECTOR_B, + const numeric::xyzVector &VECTOR_C, + const core::Real DISTANCE_XA, + const core::Real ANGLE_XAB, + const core::Real ANGLE_XAC, + const bool SIDE, + const numeric::xyzVector &VECTOR_SIDE) +{ + + // in plane components and direction + const numeric::xyzVector a( ( VECTOR_A - VECTOR_B).normalize() ); + const numeric::xyzVector b( ( VECTOR_A - VECTOR_C).normalize() ); + const numeric::xyzVector c( std::cos( numeric::constants::d::pi - ANGLE_XAB) * a); + const numeric::xyzVector d( std::cos( numeric::constants::d::pi - ANGLE_XAC) * b); + + const double bac_angl( projection_angle( c, d)); + const double a_dist( c.norm()); + const double b_dist( d.norm()); + const double c_dist + ( + std::sqrt + ( + std::max + ( + 0.0, + ( ( a_dist * a_dist ) + ( b_dist * b_dist) - 2 * a_dist * b_dist * std::cos( bac_angl)) + / ( std::sin( bac_angl) * std::sin( bac_angl) ) - + ( a_dist * a_dist) + ) + ) + ); + + //in a,b plane + const numeric::xyzVector e( cross_product( c, d).normalize()); + const numeric::xyzVector f( cross_product( e, c).normalize() * c_dist); + const numeric::xyzVector g( c + f); + + //orthogonal to a,b plane + numeric::xyzVector h( e * std::sqrt( std::max( 0.0, 1.0 - g.dot_product(g)))); + + //side + const numeric::xyzVector i( VECTOR_SIDE - VECTOR_A); + const double dihe_1( dihedral_coordinates( i, numeric::xyzVector(0.0), a, b)); + const double dihe_2( dihedral_coordinates( e, numeric::xyzVector(0.0), a, b)); + if ( dihe_2 && ( ( dihe_1 / dihe_2 > 0.0) != SIDE) ) { + h *= ( -1.0); + } + + //sum and length + const numeric::xyzVector x( numeric::xyzVector( g + h).normalize() * DISTANCE_XA); + + return VECTOR_A + x; +} + +//! @brief calculates the unit vector starting from one linal::Vector3D to another +//! @param ORIGIN vector of origin +//! @param TARGET target vector +//! @return the unit vector between ORIGIN and TARGET +numeric::xyzVector triganol_coordinates +( + const numeric::xyzVector &VECTOR_A, + const numeric::xyzVector &VECTOR_B, + const numeric::xyzVector &VECTOR_C, + const double DISTANCE_XA +) +{ + // get the average unit vector between B->A and C->A + numeric::xyzVector x( ( VECTOR_A - VECTOR_B).normalize()); + x += ( VECTOR_A - VECTOR_C).normalize(); + x.normalize(); + + // extend the unit vector to the desired length + x *= DISTANCE_XA; + + // add it to vector A ( the offset) + x += VECTOR_A; + return x; +} + + +//! point X in B,C,D->A->X +//! @brief calculates point X in B,C,D->A->X +//! @param VECTOR_A first point +//! @param VECTOR_B second point +//! @param VECTOR_C third point +//! @param VECTOR_D fourth point +//! @param DISTANCE_XA distance between X and VECTOR_A +//! @return point X in B,C,D->A->X +numeric::xyzVector tetrahedral_coordinates +( + const numeric::xyzVector &VECTOR_A, + const numeric::xyzVector &VECTOR_B, + const numeric::xyzVector &VECTOR_C, + const numeric::xyzVector &VECTOR_D, + const double DISTANCE_XA +) +{ + // get the average unit vector between B->A and C->A, D->A + numeric::xyzVector x( ( VECTOR_A - VECTOR_B).normalize()); + x += ( VECTOR_A - VECTOR_C).normalize(); + x += ( VECTOR_A - VECTOR_D).normalize(); + x.normalize(); + + // extend out to the desired distance from A + x *= DISTANCE_XA; + + // make position relative to A + x += VECTOR_A; + + return x; +} + + +//! @brief calculates projection angle between two linal::Vector3D +//! @param VECTOR_A first vector (point) +//! @param VECTOR_B second vector (point) +//! @return projection angle between two linal::Vector3D +double projection_angle( const numeric::xyzVector &VECTOR_A, const numeric::xyzVector &VECTOR_B) +{ + const double projection_angle_cosinus( projection_angle_cosin( VECTOR_A, VECTOR_B)); + + // through numerical drift it could be possible that the value is slightly higher or lower than -1 or 1 + // (VECTOR_A * VECTOR_B) / ( Norm( VECTOR_A) * Norm( VECTOR_B)) is the actual cos angle between vectors ab and cd + if ( projection_angle_cosinus >= double( 1.0) ) { + // acos( 1) == 0.0 + return 0.0; + } else if ( projection_angle_cosinus <= double( -1.0) ) { + // acos( -1) == pi + return numeric::constants::d::pi; + } + + // -1 < projection_angle_cosinus < 1, so calling acos( projection_angle_cosinus) yields the proper angle + return std::acos( projection_angle_cosinus); +} + + +double projection_angle_cosin( const numeric::xyzVector &VECTOR_A, const numeric::xyzVector &VECTOR_B) +{ + const double projection_angle_cosinus( VECTOR_A.dot_product( VECTOR_B ) / ( VECTOR_A.norm() * VECTOR_B.norm() ) ); + return projection_angle_cosinus; +} + +//! @brief dihedral angle between four points (A->B -x-> C->D) +//! @brief see http://en.wikipedia.org/wiki/Dihedral_angle for reference +//! @param VECTOR_A first vector (point) +//! @param VECTOR_B second vector (point) +//! @param VECTOR_C third vector (point) +//! @param VECTOR_D fourth vector (point) +//! @return dihedral angle between four points +double dihedral_coordinates +( + const numeric::xyzVector &VECTOR_A, + const numeric::xyzVector &VECTOR_B, + const numeric::xyzVector &VECTOR_C, + const numeric::xyzVector &VECTOR_D +) +{ + // calculate the two cross products (b1xb2 and b2xb3) + const numeric::xyzVector cross_b1_b2( cross_product( (VECTOR_B-VECTOR_A), (VECTOR_C - VECTOR_B))); + const numeric::xyzVector cross_b2_b3( cross_product( (VECTOR_C- VECTOR_B),(VECTOR_D - VECTOR_C) )); + + // calculate the vectors b1 and b2 + const numeric::xyzVector b1( VECTOR_B - VECTOR_A); + + // get the distance b -c + + const double distance( VECTOR_C.distance(VECTOR_B)); + + // calculate dihedral angle + const double dihedral_angle( std::atan2( distance * b1.dot_product(cross_b2_b3), cross_b1_b2.dot_product( cross_b2_b3 ) )); + + return dihedral_angle; +} + + +} +} +} diff --git a/source/src/core/chemical/modifications/ValenceHandler.hh b/source/src/core/chemical/modifications/ValenceHandler.hh new file mode 100644 index 0000000000..5fa6be2af5 --- /dev/null +++ b/source/src/core/chemical/modifications/ValenceHandler.hh @@ -0,0 +1,123 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. +// +// This file is based off of code from the Biochemistry Library (BCL). +// The BCL is copyright Vanderbilt University (Meiler Lab), a RosettaCommons member + +/// @file core/chemical/modifications/ValenceHandler.hh +/// @brief A bunch of functions to determine where to place atoms based on hybridization of atoms and number of bonds +/// It should be noted that a vector of coordinates are returned. This is because it creates the angles for all +/// Hydrogens that can be placed. If you are combining a fragment, you really only need the first index of coords +/// @author Rosetta conversion: Steven Combs / Sandeep Kothiwale + +#ifndef INCLUDED_core_chemical_modifications_ValenceHandler_hh +#define INCLUDED_core_chemical_modifications_ValenceHandler_hh + +#include +#include +#include + +namespace core { +namespace chemical { +namespace modifications { + +//the driver for this file. all the other commands are called by determine coordinates; however, you can use the other functions as much as you want +utility::vector1 > determine_coordinates(core::chemical::MutableResidueType const & res, core::chemical::VD const & atom); +//numeric::xyzVector get_idealized_geometry(); + +//! point X in B->A->X where A, B and X are on the same line +//! @brief calculates point X in B->A->X where A, B and X are on the same line +//! @param VECTOR_A first point +//! @param VECTOR_B second point +//! @param DISTANCE_XA distance between X and VECTOR_A +//! @return point X in B->A->X where A, B and X are on the same line +numeric::xyzVector linear_coordinates(numeric::xyzVector const & vector_a, numeric::xyzVector const & vector_b, core::Real const distance_xa); + + +//! @brief calculates coordinates using angle information (point X in B,C->A->X) +//! @param VECTOR_A first point +//! @param VECTOR_B second point +//! @param VECTOR_C third point +//! @param DISTANCE_XA distance between X and VECTOR_A +//! @param ANGLE_XAB angle between X, VECTOR_A, and VECTOR_B +//! @param ANGLE_XAC angle between X, VECTOR_A, and VECTOR_C +//! @param SIDE true if on a side +//! @param VECTOR_SIDE vector of the side +//! @return point X in B,C->A->X +numeric::xyzVector angle_coordinates( + const numeric::xyzVector &VECTOR_A, + const numeric::xyzVector &VECTOR_B, + const numeric::xyzVector &VECTOR_C, + const core::Real DISTANCE_XA, + const core::Real ANGLE_XAB, + const core::Real ANGLE_XAC, + const bool SIDE, + const numeric::xyzVector &VECTOR_SIDE +); + +//! @brief calculates the unit vector starting from one linal::Vector3D to another +//! @param ORIGIN vector of origin +//! @param TARGET target vector +//! @return the unit vector between ORIGIN and TARGET +numeric::xyzVector triganol_coordinates +( + const numeric::xyzVector &VECTOR_A, + const numeric::xyzVector &VECTOR_B, + const numeric::xyzVector &VECTOR_C, + const double DISTANCE_XA +); + + +//! point X in B,C,D->A->X +//! @brief calculates point X in B,C,D->A->X +//! @param VECTOR_A first point +//! @param VECTOR_B second point +//! @param VECTOR_C third point +//! @param VECTOR_D fourth point +//! @param DISTANCE_XA distance between X and VECTOR_A +//! @return point X in B,C,D->A->X +numeric::xyzVector tetrahedral_coordinates +( + const numeric::xyzVector &VECTOR_A, + const numeric::xyzVector &VECTOR_B, + const numeric::xyzVector &VECTOR_C, + const numeric::xyzVector &VECTOR_D, + const double DISTANCE_XA +); + + +//! @brief calculates projection angle between two linal::Vector3D +//! @param VECTOR_A first vector (point) +//! @param VECTOR_B second vector (point) +//! @return projection angle between two linal::Vector3D +double projection_angle( const numeric::xyzVector &VECTOR_A, const numeric::xyzVector &VECTOR_B); + +double projection_angle_cosin( const numeric::xyzVector &VECTOR_A, const numeric::xyzVector &VECTOR_B); + +//! @brief dihedral angle between four points (A->B -x-> C->D) +//! @brief see http://en.wikipedia.org/wiki/Dihedral_angle for reference +//! @param VECTOR_A first vector (point) +//! @param VECTOR_B second vector (point) +//! @param VECTOR_C third vector (point) +//! @param VECTOR_D fourth vector (point) +//! @return dihedral angle between four points +double dihedral_coordinates +( + const numeric::xyzVector &VECTOR_A, + const numeric::xyzVector &VECTOR_B, + const numeric::xyzVector &VECTOR_C, + const numeric::xyzVector &VECTOR_D +); + + +} +} +} + +#endif /* VALENCEHANDLER_HH_ */ diff --git a/source/src/core/chemical/rdkit/RDKit.fwd.hh b/source/src/core/chemical/rdkit/RDKit.fwd.hh index b6fe9835c9..bf95594ce4 100644 --- a/source/src/core/chemical/rdkit/RDKit.fwd.hh +++ b/source/src/core/chemical/rdkit/RDKit.fwd.hh @@ -43,6 +43,7 @@ class ChemicalReaction; typedef ::RDKit::RWMOL_SPTR RWMolOP; typedef ::RDKit::ROMOL_SPTR ROMolOP; +typedef utility::pointer::shared_ptr< Atom > AtomOP; typedef utility::pointer::shared_ptr< ChemicalReaction > ChemicalReactionOP; typedef utility::pointer::shared_ptr< ChemicalReaction const > ChemicalReactionCOP; diff --git a/source/src/core/chemical/rdkit/RestypeToRDMol.hh b/source/src/core/chemical/rdkit/RestypeToRDMol.hh index 11d2c0b869..ea1926c3c8 100644 --- a/source/src/core/chemical/rdkit/RestypeToRDMol.hh +++ b/source/src/core/chemical/rdkit/RestypeToRDMol.hh @@ -12,11 +12,11 @@ /// @brief /// A class for creating an RDKit RWMol object from a Rosetta ResidueType /// -/// @author Rocco Moretti (rmorettiase@gmail.com +/// @author Rocco Moretti (rmorettiase@gmail.com) -#ifndef INCLUDED_core_chemical_rdkit_RestypeToFragment_hh -#define INCLUDED_core_chemical_rdkit_RestypeToFragment_hh +#ifndef INCLUDED_core_chemical_rdkit_RestypeToRDMol_hh +#define INCLUDED_core_chemical_rdkit_RestypeToRDMol_hh #include diff --git a/source/src/numeric/Calculator.cc b/source/src/numeric/Calculator.cc index ec72ff5879..69f89ed149 100644 --- a/source/src/numeric/Calculator.cc +++ b/source/src/numeric/Calculator.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,23 @@ double do_max( std::vector a ) { return numeric::max( utility::vector1 a ) { return numeric::min( utility::vector1(a) ); } double do_mean( std::vector a ) { return numeric::mean( utility::vector1(a) ); } double do_median( std::vector a) { return numeric::median( utility::vector1(a) ); } +// Bandpass filter with a sine transition. +// Min and max are the edges of the 1.0 region +// min_trans and max_trans are the full width of the transition region +double do_sinpass( double x, double min, double max, double min_trans, double max_trans ) { + runtime_assert( min <= max ); + runtime_assert( min_trans >= 0 ); + runtime_assert( max_trans >= 0 ); + if ( x >= min && x <= max ) { return 1.0; } + if ( x <= min - min_trans || x >= max + max_trans ) { return 0.0; } + if ( x < min && x > min - min_trans ) { + return std::cos( (x-min) * constants::d::pi / min_trans )/2 + 0.5; + } + if ( x > max && x < max + max_trans ) { + return std::cos( (x-max) * constants::d::pi / max_trans )/2 + 0.5; + } + return 0.0; // Should never get here. +} class CalculatorParser : qi::grammar { @@ -127,6 +145,7 @@ class CalculatorParser : qi::grammar> '(' >> (expression % ',') >> ')' ) [ _val = phoenix::bind(do_median, qi::_1) ] | ( no_case["min"] >> '(' >> (expression % ',') >> ')' ) [ _val = phoenix::bind(do_min, qi::_1) ] | ( no_case["max"] >> '(' >> (expression % ',') >> ')' ) [ _val = phoenix::bind(do_max, qi::_1) ] + | ( no_case["sinpass"] >> '(' >> expression >> ',' >> expression >> ',' >> expression >>',' >> expression >>',' >> expression >>')' ) [ _val = phoenix::bind(do_sinpass, qi::_1, qi::_2, qi::_3, qi::_4, qi::_5) ] ; parser_ = *(assignment >> ';') >> expression; //assigment is omitted, so expression is the value for all. diff --git a/source/src/protocols.3.src.settings b/source/src/protocols.3.src.settings index dfdf56e32e..5956063072 100644 --- a/source/src/protocols.3.src.settings +++ b/source/src/protocols.3.src.settings @@ -131,6 +131,10 @@ sources = { "fibril_util", "SetupForFibrilMover", ], + "protocols/rotamer_gen" : [ + "FragmentRotamers", + "RDKitRotamers", + ], "protocols/interface": [ "util", ], diff --git a/source/src/protocols/chemistries/ChemistryCreators.cc b/source/src/protocols/chemistries/ChemistryCreators.cc index fa30471227..ee9659b342 100644 --- a/source/src/protocols/chemistries/ChemistryCreators.cc +++ b/source/src/protocols/chemistries/ChemistryCreators.cc @@ -19,6 +19,21 @@ namespace protocols { namespace chemistries { +ChemistryOP +ReprotonateCreator::create_chemistry() const { + return ChemistryOP( new ReprotonateChemistry ); +} + +std::string +ReprotonateCreator::keyname() const { + return ReprotonateChemistry::class_name(); +} + +void +ReprotonateCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const { + ReprotonateChemistry::provide_xml_schema( xsd ); +} + } //namespace chemistries } //namespace protocols diff --git a/source/src/protocols/chemistries/ChemistryCreators.hh b/source/src/protocols/chemistries/ChemistryCreators.hh index 3cac5abe85..69128ec2d9 100644 --- a/source/src/protocols/chemistries/ChemistryCreators.hh +++ b/source/src/protocols/chemistries/ChemistryCreators.hh @@ -21,6 +21,13 @@ namespace protocols { namespace chemistries { +class ReprotonateCreator : public ChemistryCreator { +public: + ChemistryOP create_chemistry() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & ) const override; +}; + } //namespace chemistries } //namespace protocols diff --git a/source/src/protocols/chemistries/WrappedChemistries.cc b/source/src/protocols/chemistries/WrappedChemistries.cc index 2e0dac5ce0..50305b327e 100644 --- a/source/src/protocols/chemistries/WrappedChemistries.cc +++ b/source/src/protocols/chemistries/WrappedChemistries.cc @@ -17,6 +17,8 @@ #include +#include + #include namespace protocols { @@ -52,6 +54,36 @@ WrappedBaseChemistry::get_mapping() const { return sub_chemistry_->get_mapping(); } +//////////////////////////////////////////////////////////////////////////////// + + +ReprotonateChemistry::ReprotonateChemistry() : + WrappedBaseChemistry( class_name() ) +{ + using namespace core::chemical::modifications; + set_subchemistry( ChemistryBaseOP( new Reprotonate ) ); +} + +void +ReprotonateChemistry::parse_my_tag( + utility::tag::TagCOP, + basic::datacache::DataMap &) { + // Don't need to do anything. +} + +std::string +ReprotonateChemistry::class_name() { + return "ReprotonateChemistry"; +} + +void +ReprotonateChemistry::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) { + utility::tag::AttributeList attributes; + xsd_type_definition_w_attributes( + xsd, class_name(), + "The Reprotonate chemistry will heuristically add and remove hydrogens and charges to change a ResidueType into a form that would be found at neutral pH.", + attributes ); +} } } diff --git a/source/src/protocols/chemistries/WrappedChemistries.hh b/source/src/protocols/chemistries/WrappedChemistries.hh index 75547a14ee..8795dc3c1b 100644 --- a/source/src/protocols/chemistries/WrappedChemistries.hh +++ b/source/src/protocols/chemistries/WrappedChemistries.hh @@ -60,6 +60,18 @@ private: /////////////////////////////////////////////////////////////// // Actual usage +class ReprotonateChemistry : public WrappedBaseChemistry { +public: + ReprotonateChemistry(); + + void parse_my_tag( + utility::tag::TagCOP, + basic::datacache::DataMap &) override; + + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + static std::string class_name(); +}; + } } diff --git a/source/src/protocols/drug_design/AtomExistsFilter.cc b/source/src/protocols/drug_design/AtomExistsFilter.cc new file mode 100644 index 0000000000..b1c0b75b16 --- /dev/null +++ b/source/src/protocols/drug_design/AtomExistsFilter.cc @@ -0,0 +1,121 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/AtomExistsFilter.cc +/// @brief +/// @author Rocco Moretti (rmorettiase@gmail.com) + + +//Unit Headers +#include +#include + +//Project Headers +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR( "protocols.drug_design.AtomExistsFilter" ); + +protocols::filters::FilterOP +AtomExistsFilterCreator::create_filter() const { return protocols::filters::FilterOP( new AtomExistsFilter ); } + +std::string +AtomExistsFilterCreator::keyname() const { return AtomExistsFilter::class_name(); } + +void AtomExistsFilterCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const { + AtomExistsFilter::provide_xml_schema( xsd ); +} + +std::string AtomExistsFilter::class_name() { + return "AtomExists"; +} + +void AtomExistsFilter::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) +{ + using namespace utility::tag; + AttributeList attlist; + + attlist + + XMLSchemaAttribute::required_attribute( "residue", xsct_refpose_enabled_residue_number, "Residue that the atom is in" ) + + XMLSchemaAttribute::required_attribute( "atom_name", xs_string, "Name of atom to test for" ); + + protocols::filters::xsd_type_definition_w_attributes( xsd, class_name(), + "Tests if the given atom exists in the protein.", + attlist ); +} + +void +AtomExistsFilter::parse_my_tag( utility::tag::TagCOP tag, basic::datacache::DataMap & ) +{ + residue_ = tag->getOption< std::string >( "residue" ); + atom_name_ = tag->getOption< std::string >( "atom_name" ); + + TR << "AtomExists filter for atom "< pose.total_residue() ) { + TR.Warning << "Attempted to access residue " << residue_ << " in pose with " << pose.total_residue() << " residues. Failing filter. " << std::endl; + return 0.0; + } + core::chemical::ResidueType const & restype( pose.residue(resnum).type() ); + TR << "Looking for atom " << atom_name_ << " in residue " << residue_ << ", of type " << restype.name() << std::endl; + if ( restype.has( atom_name_ ) ) { + return 1.0; + } else { + return 0.0; + } +} + +} +} diff --git a/source/src/protocols/drug_design/AtomExistsFilter.fwd.hh b/source/src/protocols/drug_design/AtomExistsFilter.fwd.hh new file mode 100644 index 0000000000..060d91e3e7 --- /dev/null +++ b/source/src/protocols/drug_design/AtomExistsFilter.fwd.hh @@ -0,0 +1,28 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/AtomExistsFilter.fwd.hh +/// @brief +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_AtomExistsFilter_fwd_hh +#define INCLUDED_protocols_drug_design_AtomExistsFilter_fwd_hh + + +namespace protocols { +namespace drug_design { + +class AtomExistsFilter; + +} +} + +#endif + + diff --git a/source/src/protocols/drug_design/AtomExistsFilter.hh b/source/src/protocols/drug_design/AtomExistsFilter.hh new file mode 100644 index 0000000000..291e28da86 --- /dev/null +++ b/source/src/protocols/drug_design/AtomExistsFilter.hh @@ -0,0 +1,69 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/AtomExistsFilter.hh +/// @brief definition of filter class AtomExistsFilter. +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_AtomExistsFilter_hh +#define INCLUDED_protocols_drug_design_AtomExistsFilter_hh + +//unit headers +#include + +// Project Headers +#include +#include +#include +#include +#include + +#include + +namespace protocols { +namespace drug_design { + +class AtomExistsFilter : public filters::Filter +{ +public: + AtomExistsFilter(): + Filter( class_name() ) + {} + + AtomExistsFilter( std::string const & residue, std::string const & atom_name ): + Filter( class_name() ), + residue_( residue ), + atom_name_( atom_name ) + {} + + bool apply( core::pose::Pose const & pose ) const override; + filters::FilterOP clone() const override { + return filters::FilterOP( new AtomExistsFilter( *this ) ); + } + filters::FilterOP fresh_instance() const override { + return filters::FilterOP( new AtomExistsFilter() ); + } + + void report( std::ostream & out, core::pose::Pose const & pose ) const override; + core::Real report_sm( core::pose::Pose const & pose ) const override; + core::Real compute( core::pose::Pose const &pose ) const; + void parse_my_tag( utility::tag::TagCOP tag, basic::datacache::DataMap & ) override; + + static std::string class_name(); + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + +private: + std::string residue_; + std::string atom_name_; +}; + +} +} + +#endif diff --git a/source/src/protocols/drug_design/AtomExistsFilterCreator.hh b/source/src/protocols/drug_design/AtomExistsFilterCreator.hh new file mode 100644 index 0000000000..6b0cd3bee1 --- /dev/null +++ b/source/src/protocols/drug_design/AtomExistsFilterCreator.hh @@ -0,0 +1,40 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/AtomExistsFilterCreator.hh +/// @brief FilterCreator for the AtomExistsFilter +/// @author Rocco Moretti (rmorettiase@gmail.com) + + +#ifndef INCLUDED_protocols_drug_design_AtomExistsFilterCreator_hh +#define INCLUDED_protocols_drug_design_AtomExistsFilterCreator_hh + +// Package Headers +#include + +// Utility Headers + +// c++ headers +#include + +namespace protocols { +namespace drug_design { + +class AtomExistsFilterCreator : public protocols::filters::FilterCreator +{ +public: + protocols::filters::FilterOP create_filter() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const override; +}; + +} +} + +#endif diff --git a/source/src/protocols/drug_design/DrugDesignMover.cc b/source/src/protocols/drug_design/DrugDesignMover.cc new file mode 100644 index 0000000000..78b4ec8e72 --- /dev/null +++ b/source/src/protocols/drug_design/DrugDesignMover.cc @@ -0,0 +1,653 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/DrugDesignMover.cc +/// @brief MonteCarlo protocol to design drugs in a protein context +/// @author Rocco Moretti (rmorettiase@gmail.com) + +// Unit Headers +#include +#include + +// Package Headers +#include +#include + +// Project Headers +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +// Utility headers +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Debugging output +#include +#include + +// C/C++ headers +#include + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR("protocols.drug_design.DrugDesignMover"); + +using namespace core; +using namespace protocols::moves; +using namespace protocols::filters; + +std::string +DrugDesignMoverCreator::keyname() const +{ + return DrugDesignMover::mover_name(); +} + +protocols::moves::MoverOP +DrugDesignMoverCreator::create_mover() const { + return protocols::moves::MoverOP( new DrugDesignMover ); +} + +void DrugDesignMoverCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const +{ + DrugDesignMover::provide_xml_schema( xsd ); +} + +/// @brief default constructor +DrugDesignMover::DrugDesignMover(): + Mover(mover_name()), + redocker_( nullptr ), + scorer_( nullptr ), + temperature_( 1.0 ), + maxtrials_( 10 ), + chain_( 'X' ), + restypeset_( new core::chemical::PoseResidueTypeSet(core::chemical::FULL_ATOM_t) ) +{} + +/// @brief destructor +DrugDesignMover::~DrugDesignMover(){} + +/// @brief clone this object +MoverOP +DrugDesignMover::clone() const +{ + return MoverOP( new DrugDesignMover( *this ) ); +} + +/// @brief create this type of object +MoverOP +DrugDesignMover::fresh_instance() const +{ + return MoverOP( new DrugDesignMover() ); +} + +/// @brief Return the redocker being used +MoverCOP +DrugDesignMover::redocker() const { + return redocker_; +} + +/// @brief Return the scoring filter +FilterCOP +DrugDesignMover::scorer() const { + return scorer_; +} + +/// @brief Add a chemistry to use +void +DrugDesignMover::add_chemistry( protocols::chemistries::ChemistryOP chemistry, core::Real weight /*= 1.0*/ ) +{ + assert( chemistries_.size() == weights_.size() ); + chemistries_.push_back( chemistry ); + weights_.push_back( weight ); +} + +void +DrugDesignMover::add_before_chemistry( protocols::chemistries::ChemistryOP chemistry ) +{ + before_chemistries_.push_back( chemistry ); +} + +void +DrugDesignMover::add_after_chemistry( protocols::chemistries::ChemistryOP chemistry ) +{ + after_chemistries_.push_back( chemistry ); +} + +/// @brief set redocker +void +DrugDesignMover::redocker( MoverOP const & mover ) +{ + redocker_ = mover; +} + +/// @brief set scoring filter +void +DrugDesignMover::scorer( FilterOP const & scorer ) +{ + scorer_ = scorer; +} + +/// @brief Replace residues using only those atoms which are true in mask. +void +replace_residue_type_with_mask( core::pose::Pose & pose, core::Size seqpos, core::chemical::ResidueType const & new_rsd_type, utility::vector1< bool > const & mask ) { + core::conformation::Residue const & source_rsd( pose.residue( seqpos ) ); + core::conformation::ResidueOP new_rsd( core::conformation::ResidueFactory::create_residue( new_rsd_type ) ); + core::conformation::Residue & target_rsd( *new_rsd ); + core::conformation::Conformation & conformation( pose.conformation() ); + + Size const natoms( target_rsd.natoms() ); + + utility::vector1< bool > missing( natoms, false ); + bool any_missing( false ); + + for ( Size i=1; i<= natoms; ++i ) { + if ( i > mask.size() || mask[i] != true ) { + any_missing = true; + missing[i] = true; + continue; + } + std::string const & atom_name( target_rsd.atom_name(i) ); + if ( source_rsd.has( atom_name ) ) { + target_rsd.atom( i ).xyz( source_rsd.atom( atom_name ).xyz() ); + } else { + TR.Debug << "replace_residue_type_with_mask: missing atom " << target_rsd.name() << ' ' << + atom_name << std::endl; + any_missing = true; + missing[i] = true; + } + } + + if ( any_missing ) { + target_rsd.seqpos( source_rsd.seqpos() ); // in case fill_missing_atoms needs context info + target_rsd.chain ( source_rsd.chain () ); + target_rsd.fill_missing_atoms( missing, conformation ); + } + + pose.replace_residue( seqpos, *new_rsd, false ); +} + +/// @brief Set the filter to use before redocking +void +DrugDesignMover::prefilter( protocols::filters::FilterOP const & setting ) { + prefilter_ = setting; +} + +/// @brief Set the filter to use after redocking +void +DrugDesignMover::postfilter( protocols::filters::FilterOP const & setting ) { + postfilter_ = setting; +} + +void +DrugDesignMover::apply( Pose & pose ) +{ + using namespace core::chemical; + using namespace core::pose; + + if ( ! chemistries_.size() ) { + TR.Warning << "No chemistries specified! " << std::endl; + return; + } + if ( chemistries_.size() != weights_.size() ) { + TR.Warning << "The number of weights aren't equal to the number of chemistries - resizing" << std::endl; + weights_.resize( chemistries_.size(), 1.0 ); + } + if ( !redocker_ ) { + TR.Warning << "Redocking mover is empty! " << std::endl; + return; + } + if ( !scorer_ ) { + TR.Warning << "No scoring Filter specified! " << std::endl; + return; + } + + // Statistics for attempts (MC won't show earlier aborts.) + std::map< std::string, core::Size > stat_attempt, stat_failed, stat_tested, stat_accepted; + + Real initial_score = scorer_->report_sm( pose ); + assert( temperature() ); // Can't work with a temperature of zero + core::Size res_pos( find_design_position( pose ) ); + TR << "For round " << 0 << " ligand " << pose.residue( res_pos ).name() << " score " << initial_score << std::endl; + protocols::moves::MonteCarlo mc( pose, initial_score, temperature() ); + + //Setup data for the iteration + numeric::random::WeightedSampler sampler(weights_); + + // TODO: can I pull the trial number out of the MC object instead? + for ( Size ii=1; ii<=maxtrials_; ii++ ) { + TR << "Trial number: " << ii <name() ); // Need this as chemistries aren't necessarily all that good with naming. + + if ( pre_process_restype(restype, index_vd_mapping, pose ) ) { + // ResidueType is unusable + continue; + } + + // Pick chemistry to apply + Size chem_i( sampler.random_sample(numeric::random::rg()) ); + assert( chem_i != 0 && chem_i <= chemistries_.size() ); + assert( chemistries_[chem_i] ); + protocols::chemistries::Chemistry & chemistry( *chemistries_[chem_i] ); + + ++stat_attempt[ chemistry.name() ]; + + // Apply the selected chemistry + dump_molecule( *restype, "before_mod" ); + chemistry.apply( *restype, pose ); // Allow context-sensitive chemistries + dump_molecule( *restype, "after_mod" ); + if ( chemistry.get_last_status() != core::chemical::modifications::SUCCESS ) { + TR.Warning << "Chemistry " << chemistry.name() << " reported failure on " << original_name << std::endl; + ++stat_failed[ chemistry.name() ]; + continue; + } + + index_vd_mapping = combine( index_vd_mapping, chemistry.get_mapping() ); + + std::string new_name( find_new_res_name( original_name, ii ) ); + //Tracer output for post-processing grep, etc. + TR << "MODIFICATION: " << original_name << " into " << new_name << " via " << chemistry.name() << std::endl; + + if ( post_process_restype(restype, index_vd_mapping, new_name, pose ) ) { + // ResidueType is unusable + ++stat_failed[ chemistry.name() ]; + continue; + } + + dump_molecule( *restype, "after_postprocess" ); + + if ( emplace_residue_type(pose, restype, index_vd_mapping ) ) { + // Emplacing the residue type on the pose failed - reset and continue + pose = mc.last_accepted_pose(); + ++stat_failed[ chemistry.name() ]; + continue; + } + + // Because the position may change during docking + res_pos = find_design_position( pose ); + dump_molecule( pose.residue( res_pos ), "tested" ); + ++stat_tested[ chemistry.name() ]; + + // MonteCarlo + Real score = scorer_->report_sm( pose ); + TR << "For round " << ii << " ligand " << pose.residue( res_pos ).name() << " score " << score << std::endl; + if ( mc.boltzmann( pose, score, chemistry.name() ) ) { + ++stat_accepted[ chemistry.name() ]; + } + mc.show_scores(); + + } // i<=maxtrials_ + + pose = mc.lowest_score_pose(); + + TR << "DockDesignMover finished." << std::endl; + + // Output the final docked ligand as sdf, for convenience + res_pos = find_design_position( pose ); + dump_molecule( pose.residue( res_pos ), "final" ); + + // Stats -- should probably turn this into a full stats sub-object. + for ( core::Size cc(1); cc <= chemistries_.size(); ++ cc ) { + std::string const & name( chemistries_[cc]->name() ); + TR << "Stats for " << name << ": Attempts: " << stat_attempt[name] << " (" << stat_attempt[name]/float(maxtrials_) << ")" + << " Hard Fails: " << stat_failed[name] << " (" << stat_failed[name]/float(stat_attempt[name]) << ")" + << " Tested: " << stat_tested[name] << " (" << stat_tested[name]/float(stat_attempt[name]) << ")" + << " Accepted: " << stat_accepted[name] << " (" << stat_accepted[name]/float(stat_attempt[name]) << ")" << std::endl; + } + + mc.show_scores(); + mc.show_state(); + mc.show_counters(); + + +}// apply + +/// @brief Return a unique string representing which job this is +std::string +DrugDesignMover::get_jobname() const { + // TODO: This needs to be de-JD2-ized at some point. + return protocols::jd2::JobDistributor::get_instance()->current_output_name(); +} + + +bool // Returns true if we can't use this residuetype +DrugDesignMover::pre_process_restype( + core::chemical::MutableResidueTypeOP restype, // Can/will be modified! + core::chemical::IndexVDMapping & index_vd_mapping, // Can/will be modified! + core::pose::Pose const & pose // Const reference pose for context +) { + + for ( core::Size bc(1); bc <= before_chemistries_.size(); ++bc ) { + before_chemistries_[bc]->apply( *restype, pose ); // Allow context sensitive chemistries + if ( before_chemistries_[bc]->get_last_status() != core::chemical::modifications::SUCCESS ) { + TR.Warning << "Before chemistry " << before_chemistries_[bc]->name() << " reported failure on " << restype->name() << std::endl; + return true; + } + index_vd_mapping = combine( index_vd_mapping, before_chemistries_[bc]->get_mapping() ); + } + + return false; +} + +bool // Returns true if we can't use this residuetype +DrugDesignMover::post_process_restype( + core::chemical::MutableResidueTypeOP restype, // Can/will be modified! + core::chemical::IndexVDMapping & index_vd_mapping, // Can/will be modified! + std::string const & new_name, + core::pose::Pose const & pose // Const reference pose for context +) { + using namespace core::chemical; + + for ( core::Size ac(1); ac <= after_chemistries_.size(); ++ac ) { + after_chemistries_[ac]->apply( *restype, pose ); + if ( after_chemistries_[ac]->get_last_status() != core::chemical::modifications::SUCCESS ) { + TR.Warning << "After chemistry " << after_chemistries_[ac]->name() << " reported failure on " << restype->name() << std::endl; + return true; + } + index_vd_mapping = combine( index_vd_mapping, after_chemistries_[ac]->get_mapping() ); + } + + // Do now, because the after chemistries can possibly change the name + if ( new_name.size() != 0 ) { + restype->name( new_name ); + } + + // For placement into the pose, it's best to have the root atom be in the original residue type. + if ( index_vd_mapping.reverse_lookup( restype->root_atom() ) == index_vd_mapping.invalid_key() ) { + TR << "Icoord root placed on new atom - rerooting." <has( itr->second ) && restype->atom( itr->second ).element() != core::chemical::element::H ) { + possible_root = itr->second; + break; + } + } + if ( possible_root == index_vd_mapping.invalid_entry() ) { + TR << "Unable to find acceptable root atom. Skipping rerooting." << std::endl; // Should have reasonable root already + } else { + TR << "Re-rooting on atom '" << restype->atom_name( possible_root ) << "'" << std::endl; + core::chemical::reroot_restype( *restype, restype->graph(), possible_root ); + } + } + + // Residues with less than three heavy atoms have issues with being properly placed in the pose + // Register this as a fail + if ( restype->nheavyatoms() < 3 ) { + TR << "Chemistry resulted in ligand with less than three heavy atoms. Skipping." <add_unpatchable_residue_type( mutrestype ); + core::chemical::ResidueTypeCOP restype( restypeset_->name_mapOP( mutrestype->name() ) ); + restypes_.push_back( restype ); + + // Mapping from the indicies in the original residue type to the indicies in the new residue type + IndexIndexMapping index_index_mapping( index_vd_mapping.downstream_combine( VDNameMapping( *mutrestype ) ).downstream_combine( NameIndexMapping( *restype ) ) ); + + dump_molecule( pose.residue( res_pos ), "before_pose" ); + place_new_restype( pose, res_pos, *restype, index_index_mapping ); + dump_molecule( pose.residue( res_pos ), "after_pose" ); + + if ( prefilter_ && ! prefilter_->apply( pose ) ) { + TR << "Prefilter reported failure - rejecting." << std::endl; + dump_molecule( pose.residue( res_pos ), "prefilter_fail" ); + return true; + } + + // Apply the redocker + dump_molecule( pose.residue( res_pos ), "before_dock" ); + redocker_->apply( pose ); + dump_molecule( pose.residue( res_pos ), "after_dock" ); + + // Debugging - check to make sure that the we're not leaking constraints + //pose.constraint_set()->show_definition(TR, pose); + + if ( postfilter_ && ! postfilter_->apply( pose ) ) { + TR << "Postfilter reported failure - rejecting." << std::endl; + dump_molecule( pose.residue( res_pos ), "postfilter_fail" ); + return true; + } + + return false; +} + +std::string +DrugDesignMover::get_name() const { + return DrugDesignMover::mover_name(); +} + +/// @brief Is position n a valid designable residue according to the settings +bool +DrugDesignMover::check_design_position( Pose const & pose, Size n ) { + using namespace core::pose; + // Right now we're doing chain letters only - could be extended later + PDBInfoCOP pdb_info( pose.pdb_info() ); + if ( ! pdb_info ) return false; // No chain info - not valid position. + return pdb_info->chain(n) == chain_; +} + +/// @brief Determine which residue on this pose should be designed +Size +DrugDesignMover::find_design_position( Pose const & pose ) { + using namespace core::pose; + // Right now we're doing chain letters only - could be extended later + PDBInfoCOP pdb_info( pose.pdb_info() ); + if ( ! pdb_info ) { + utility_exit_with_message("Pose does not have PDBInfo - can't determine chain to use."); + } + Size res_pos(0); + for ( Size ii(1); ii <= pdb_info->nres(); ++ii ) { + if ( pdb_info->chain(ii) == chain_ ) { + if ( res_pos != 0 ) { + TR.Warning << "Multiple residues are in designable chain " << chain_ << " not only " << res_pos << " but also " << ii << std::endl; + } + res_pos = ii; + } + } + if ( res_pos == 0 ) { + utility_exit_with_message( std::string("No designable residue found. Missing chain ") + chain_ ); + } + return res_pos; +} + +/// @brief Find a new residue name, if the name already exists in the residue type set. +std::string +DrugDesignMover::find_new_res_name( std::string original_name, core::Size iteration, core::Size subiteration /*=0*/ ) const { + // Default is just to append the iteration to the ligand name + static const std::string letters( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ); + std::string newresname( original_name + "-" + std::to_string(iteration) ); + if ( subiteration != 0 ) { + newresname += letters[ (subiteration-1)%letters.size() ]; + } + std::string jobname( get_jobname() ); + // First thing - we prepend the job name if it doesn't have it on the ligand name already. + if ( ! utility::startswith( newresname, jobname ) ) { + newresname = jobname + "-" + newresname; + } + if ( ! restypeset_->has_name( newresname ) ) { + return newresname; + } + // Okay. Let's just increment a number until we get success + core::Size mult(2); + while ( restypeset_->has_name( newresname ) ) { + newresname = newresname + "x" + std::to_string(mult); + ++mult; + } + return newresname; +} + +/// @brief Dump the ligand to the given file (appending) +void +DrugDesignMover::dump_molecule( core::chemical::MutableResidueType const & restype, std::string const & stage) const { + if ( ! debug_prefix_.empty() ) { + std::string filename( debug_prefix_ + "_" + stage + "_" + get_jobname() + ".sdf" ); + core::chemical::sdf::MolWriter writer; + utility::io::ozstream output( filename, std::ios_base::out | std::ios_base::app ); + writer.output_residue( output, restype ); + output.close(); + } +} + +/// @brief Dump the ligand to the given file (appending) +void +DrugDesignMover::dump_molecule( core::conformation::Residue const & residue, std::string const & stage) const { + if ( ! debug_prefix_.empty() ) { + std::string filename( debug_prefix_ + "_" + stage + "_" + get_jobname() + ".sdf" ); + core::chemical::sdf::MolWriter writer; + utility::io::ozstream output( filename, std::ios_base::out | std::ios_base::app ); + writer.output_residue( output, residue ); + output.close(); + } +} + +std::string +DrugDesignMover::mover_name() { + return "DrugDesignMover"; +} + +void +DrugDesignMover::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) { + using namespace utility::tag; + AttributeList attlist; + + attlist + + XMLSchemaAttribute::attribute_w_default( "trials", xsct_non_negative_integer, "The number of Monte Carlo trials", "10") + + XMLSchemaAttribute::attribute_w_default( "temperature", xsct_real, "The Monte Carlo temperature", "1.0" ) + + XMLSchemaAttribute::attribute_w_default( "chain", xsct_char, "The chain of the ligand to use", "X" ) + + XMLSchemaAttribute( "prefilter", xs_string, "If set, apply this filter to the pose prior to the redocker mover." ) + + XMLSchemaAttribute::attribute_w_default( "redocker", xs_string, "The name of the mover to use prior to scoring", "null_mover" ) + + XMLSchemaAttribute( "postfilter", xs_string, "If set, apply this filter to the pose after the redocker mover has been applied." ) + + XMLSchemaAttribute::required_attribute( "scorer", xs_string, "The name of the filter to use for scoring" ) + + XMLSchemaAttribute( "debug_prefix", xs_string, "If set, use this prefix when dumping intermediate structures" ); + + AttributeList add_subelement_attlist; + add_subelement_attlist + + XMLSchemaAttribute::required_attribute( "chemistry", xs_string, "The name of the chemistry to use" ) + + XMLSchemaAttribute::attribute_w_default( "weight", xsct_real, "Weight for this chemistry when randomly picking.", "1.0" ); + + AttributeList ba_subelement_attlist; + ba_subelement_attlist + + XMLSchemaAttribute::required_attribute( "chemistry", xs_string, "The name of the chemistry to use" ); + + XMLSchemaSimpleSubelementList subelements; + subelements + .add_simple_subelement( "Add", add_subelement_attlist, "Add a chemistry to the list of possibilities to randomly choose from" ) + .add_simple_subelement( "Before", ba_subelement_attlist, "Chemistry to always use before the randomly chosen one" ) + .add_simple_subelement( "After", ba_subelement_attlist, "Chemistry to always use after the randomly chosen one" ); + + protocols::moves::xsd_type_definition_w_attributes_and_repeatable_subelements( + xsd, mover_name(), + "Run a Monte Carlo drug design protocol on a given ligand.", + attlist, subelements ); + +} + +// @brief utility function to get chemistry from datamap +protocols::chemistries::ChemistryOP +DrugDesignMover::chemistry_from_subtag( utility::tag::TagCOP const subtag, basic::datacache::DataMap & data ) const { + std::string const chemistry_name( subtag->getOption< std::string >( "chemistry" ) ); + if ( ! data.has( "chemistry", chemistry_name ) ) { + utility_exit_with_message( "Cannot find chemistry named " + chemistry_name + ". Was it defined in the tag?" ); + } + return data.get_ptr< protocols::chemistries::Chemistry >( "chemistry", chemistry_name ); +} + +/// @brief parse xml file +void +DrugDesignMover::parse_my_tag( TagCOP const tag, basic::datacache::DataMap & data ) +{ + maxtrials_ = tag->getOption< core::Size >( "trials", 10 ); + temperature_ = tag->getOption< Real >( "temperature", 1.0 ); + chain_ = tag->getOption< char >( "chain", 'X' ); + debug_prefix_ = tag->getOption< std::string >( "debug_prefix", "" ); + + if ( ! tag->hasOption( "scorer" ) ) { + utility_exit_with_message("You must provide a scorer option to the DrugDesignMover for scoring."); + } + std::string const filter_name( tag->getOption< std::string >( "scorer" ) ); + scorer( protocols::rosetta_scripts::parse_filter(filter_name, data) ); + std::string const mover_name( tag->getOption< std::string >( "redocker", "null_mover" ) ); + redocker( protocols::rosetta_scripts::parse_mover(mover_name, data) ); + + std::string const prefilter_name( tag->getOption< std::string >( "prefilter", "" ) ); + if ( prefilter_name.size() ) { + prefilter( protocols::rosetta_scripts::parse_filter(prefilter_name, data) ); + } else { + prefilter( nullptr ); + } + + std::string const postfilter_name( tag->getOption< std::string >( "postfilter", "" ) ); + if ( postfilter_name.size() ) { + postfilter( protocols::rosetta_scripts::parse_filter(postfilter_name, data) ); + } else { + postfilter( nullptr ); + } + + for ( TagCOP const & subtag : tag->getTags() ) { + if ( subtag->getName() == "Add" ) { + core::Real const weight( subtag->getOption< core::Real >( "weight", 1.0 ) ); + add_chemistry( chemistry_from_subtag( subtag, data ) , weight ) ; + } else if ( subtag->getName() == "Before" ) { + add_before_chemistry( chemistry_from_subtag( subtag, data ) ); + } else if ( subtag->getName() == "After" ) { + add_after_chemistry( chemistry_from_subtag( subtag, data ) ); + } else { + utility_exit_with_message( "tag name " + subtag->getName() + " unrecognized." ); + } + }//foreach subtag +} + +} // ns drug_design +} // ns protocols diff --git a/source/src/protocols/drug_design/DrugDesignMover.fwd.hh b/source/src/protocols/drug_design/DrugDesignMover.fwd.hh new file mode 100644 index 0000000000..5d08c98b0d --- /dev/null +++ b/source/src/protocols/drug_design/DrugDesignMover.fwd.hh @@ -0,0 +1,32 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/DrugDesignMover.fwd.hh +/// @brief +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_DrugDesignMover_fwd_hh +#define INCLUDED_protocols_drug_design_DrugDesignMover_fwd_hh + + +// Utility headers +#include + +namespace protocols { +namespace drug_design { + +class DrugDesignMover; +typedef utility::pointer::shared_ptr< DrugDesignMover > DrugDesignMoverOP; +typedef utility::pointer::shared_ptr< DrugDesignMover const > DrugDesignMoverCOP; + + +} // namespace drug_design +} // namespace protocols + +#endif diff --git a/source/src/protocols/drug_design/DrugDesignMover.hh b/source/src/protocols/drug_design/DrugDesignMover.hh new file mode 100644 index 0000000000..10c66d840f --- /dev/null +++ b/source/src/protocols/drug_design/DrugDesignMover.hh @@ -0,0 +1,249 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/DrugDesignMover.hh +/// @brief do MonteCarlo drug design in a protein context +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_DrugDesignMover_hh +#define INCLUDED_protocols_drug_design_DrugDesignMover_hh + +// Unit header +#include + +// Package headers +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Project Headers +#include + +// Utility headers +#include +#include +#include + +// External headers + +// C/C++ headers +#include + +namespace protocols { +namespace drug_design { + +class DrugDesignMover : public protocols::moves::Mover { + // TODO: Checkpointing +public: + /// @brief default constructor + DrugDesignMover(); + + /// @brief destructor + ~DrugDesignMover() override; + + /// @brief create copy constructor + protocols::moves::MoverOP clone() const override; + + /// @brief create this type of objectt + protocols::moves::MoverOP fresh_instance() const override; + + /// @brief apply DrugDesignMover + void apply( core::pose::Pose & pose ) override; + + std::string get_name() const override; + +public: // accessors + + /// @brief Return the redocker being used + protocols::moves::MoverCOP redocker() const; + + /// @brief Return the scoring filter + protocols::filters::FilterCOP scorer() const; + + /// @brief Return the scoring temperature + core::Real temperature() const { + return temperature_; + } + + /// @brief Returns maximum number of trials + core::Size maxtrials() const { return maxtrials_; } + + /// @brief What chain is the ligand supposed to be? + char chain() const { return chain_; } + + /// @brief What's the prefix of the filename to which intermediate debug structures are dumped? + std::string debug_prefix() const { return debug_prefix_; } + +public: // setters + + /// @brief Add a chemistry to use + void add_chemistry( protocols::chemistries::ChemistryOP chemistry, core::Real weight = 1.0 ); + + /// @brief Add a chemistry to use + void add_before_chemistry( protocols::chemistries::ChemistryOP chemistry ); + + /// @brief Add a chemistry to use + void add_after_chemistry( protocols::chemistries::ChemistryOP chemistry ); + + /// @brief set redocking mover + void redocker( protocols::moves::MoverOP const & mover ); + + /// @brief Set the scoring filter in use + void scorer( protocols::filters::FilterOP const & scorer ); + + /// @brief Set the filter to use before redocking + void prefilter( protocols::filters::FilterOP const & setting ); + + /// @brief Set the filter to use after redocking + void postfilter( protocols::filters::FilterOP const & setting ); + + /// @brief set temperature + void temperature( core::Real const temp ) { + temperature_ = temp; + } + + /// @brief set max trials of MC trials + void maxtrials( core::Size const ntrial ) { + maxtrials_ = ntrial; + } + + /// @brief set the ligand chain to use for design + void chain( char chain ) { + chain_ = chain; + } + + /// @brief What's the prefix of the filename to which intermediate debug structures are dumped? + void debug_prefix( std::string const & setting ) { + debug_prefix_ = setting; + } + +public: // Other functions + + /// @brief Is position n a valid designable residue according to the settings + bool + check_design_position( core::pose::Pose const & pose, core::Size n ); + + /// @brief Determine which residue on this pose should be designed + core::Size + find_design_position( core::pose::Pose const & pose ); + + /// @brief parse xml file + void + parse_my_tag( TagCOP tag, basic::datacache::DataMap & data ) override; + + static std::string mover_name(); + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + +protected: + + /// @brief Return a unique string representing which job this is + std::string + get_jobname() const; + + /// @brief Subclass access to the chemistries + utility::vector1< protocols::chemistries::ChemistryOP > & chemistries() { return chemistries_; } + + /// @brief Pre-process the ResidueType + /// * Apply the always chemistries + bool // Returns true if we can't use this residuetype + pre_process_restype( + core::chemical::MutableResidueTypeOP restype, // Can/will be modified! + core::chemical::IndexVDMapping & index_vd_mapping, // Can/will be modified! + core::pose::Pose const & pose // for context + ); + + /// @brief Post-process the residuetype + /// * Apply the always chemistries + /// * Fix up some after issues + bool // Returns true if we can't use this residuetype + post_process_restype( + core::chemical::MutableResidueTypeOP restype, // Can/will be modified! + core::chemical::IndexVDMapping & index_vd_mapping, // Can/will be modified! + std::string const & new_name, + core::pose::Pose const & pose // for context + ); + + /// @brief Take a new residuetype, place it on the pose, + /// and see if it passes filters. + /// @details Differs from place_residue_type() in that it also filters + bool // Returns true if we can't use this residuetype + emplace_residue_type( + core::pose::Pose & pose, // Can/will be modified! + core::chemical::MutableResidueTypeOP restype, // Can/will be modified! + core::chemical::IndexVDMapping const & index_VD_mapping + ); + + /// @brief Find a new residue name, if the name already exists in the residue type set. + std::string + find_new_res_name( std::string original_name, core::Size iteration, core::Size subiteration = 0 ) const; + + /// @brief Dump the ligand to the given file (appending) + void + dump_molecule( core::chemical::MutableResidueType const & restype, std::string const & stage) const; + + /// @brief Dump the ligand to the given file (appending) + void + dump_molecule( core::conformation::Residue const & residue, std::string const & stage) const; + + protocols::chemistries::ChemistryOP + chemistry_from_subtag( utility::tag::TagCOP const subtag, basic::datacache::DataMap & data ) const; + +private: // Data + + /// @brief What chemistries can be done in the design + utility::vector1< protocols::chemistries::ChemistryOP > chemistries_; + + /// @brief The weight to use each chemistry at + utility::vector1< core::Real > weights_; + + /// @brief What chemistries are always applied before the randomly chosen one. + utility::vector1< protocols::chemistries::ChemistryOP > before_chemistries_; + + /// @brief What chemistries are always applied after the randomly chosen one. + utility::vector1< protocols::chemistries::ChemistryOP > after_chemistries_; + + /// @brief Mover which is used for redocking + protocols::moves::MoverOP redocker_; + + /// @brief The filter used to do scoring + protocols::filters::FilterOP scorer_; + + /// @brief acceptance criterion temperature + core::Real temperature_; + + /// @brief the number of MC trials to run in the complete run. + core::Real maxtrials_; + + /// @brief What chain is the ligand? + char chain_; + + /// @brief The pass/fail filter to use prior to redocking + protocols::filters::FilterOP prefilter_; + + /// @brief The pass/fail filter to use after redocking + protocols::filters::FilterOP postfilter_; + + /// @brief If non-empty, dump intermediate structures to files based on this prefix. + std::string debug_prefix_; + + /// @brief TODO: HACK - needed to make sure that restypes don't go out of scope. Need to handle this better. + utility::vector1< core::chemical::ResidueTypeCOP > restypes_; + core::chemical::PoseResidueTypeSetOP restypeset_; +}; + +} // namespace drug_design +} // namespace protocols + +#endif diff --git a/source/src/protocols/drug_design/DrugDesignMoverCreator.hh b/source/src/protocols/drug_design/DrugDesignMoverCreator.hh new file mode 100644 index 0000000000..339f979dd2 --- /dev/null +++ b/source/src/protocols/drug_design/DrugDesignMoverCreator.hh @@ -0,0 +1,33 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +///@file protocols/drug_design/DrugDesignMoverDrugDesignMoverCreator.hh +///@brief This class will create instances of Mover DrugDesignMover for the MoverFactory +///@author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_DrugDesignMoverCreator_hh +#define INCLUDED_protocols_drug_design_DrugDesignMoverCreator_hh + +#include + +namespace protocols { +namespace drug_design { + +class DrugDesignMoverCreator : public protocols::moves::MoverCreator { +public: + moves::MoverOP create_mover() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const override; +}; + +} +} + +#endif + diff --git a/source/src/protocols/drug_design/DrugPolishMover.cc b/source/src/protocols/drug_design/DrugPolishMover.cc new file mode 100644 index 0000000000..8d24932ae4 --- /dev/null +++ b/source/src/protocols/drug_design/DrugPolishMover.cc @@ -0,0 +1,282 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/DrugPolishMover.cc +/// @brief MonteCarlo protocol to design drugs in a protein context +/// @author Rocco Moretti (rmorettiase@gmail.com) + +// Unit Headers +#include +#include + +// Package Headers +#include +#include + +// Project Headers +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// Utility headers +#include +#include +#include +#include +#include + +// Debugging output +#include +#include +#include +#include + +// External headers +#include + +// C/C++ headers +#include + +#define foreach BOOST_FOREACH + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR("protocols.drug_design.DrugPolishMover"); + +using namespace core; +using namespace protocols::moves; +using namespace protocols::filters; + +std::string +DrugPolishMoverCreator::keyname() const +{ + return DrugPolishMoverCreator::mover_name(); +} + +protocols::moves::MoverOP +DrugPolishMoverCreator::create_mover() const { + return protocols::moves::MoverOP( new DrugPolishMover ); +} + +std::string +DrugPolishMoverCreator::mover_name() +{ + return "DrugPolishMover"; +} + +/// @brief default constructor +DrugPolishMover::DrugPolishMover() {} + +/// @brief destructor +DrugPolishMover::~DrugPolishMover() {} + +/// @brief clone this object +MoverOP +DrugPolishMover::clone() const +{ + return MoverOP( new DrugPolishMover( *this ) ); +} + +/// @brief create this type of object +MoverOP +DrugPolishMover::fresh_instance() const +{ + return MoverOP( new DrugPolishMover() ); +} + +void +DrugPolishMover::apply( Pose & pose ) +{ + using namespace core::chemical; + using namespace core::pose; + + if ( chemistries().size() != 1 ) { + utility_exit_with_message("Something's wrong - chemistry not found for DrugPolishMover."); + } + if ( !redocker() ) { + TR.Warning << "Redocking mover is empty! " << std::endl; + return; + } + if ( !scorer() ) { + TR.Warning << "No scoring Filter specified! " << std::endl; + return; + } + + Real initial_score = scorer()->report_sm( pose ); + core::Size res_pos( find_design_position( pose ) ); + TR << "Starting score for ligand " << pose.residue( res_pos ).name() << " is " << initial_score << std::endl; + + // Repeated application until we can't find an applicable reaction to use. + for ( Size ii=1; ii<=maxtrials(); ii++ ) { + TR << "Cycle number: " << ii < poses; + utility::vector1< core::Real > scores; + + // Make a working copy of the restype of interest. + res_pos = find_design_position( pose ); + MutableResidueTypeOP restype( new MutableResidueType( pose.residue_type( res_pos ) ) ); + IndexVDMapping starting_index_vd_mapping( combine( IndexNameMapping( pose.residue_type( res_pos ) ), NameVDMapping(*restype) ) ); // Starting index to current vds. + + std::string original_name( restype->name() ); // Need this as chemistries aren't necessarily all that good with naming. + + if ( pre_process_restype(restype, starting_index_vd_mapping, pose ) ) { + TR.Warning << "Issue pre-processing ResidueType " << restype->name() << " in cycle " << ii << std::endl; + // ResidueType is unusable for futher processing. + break; + } + + // Apply the chemistry + debug_assert( chemistries()[1] ); + protocols::chemistries::Chemistry & chemistry( *chemistries()[1] ); + + dump_molecule( *restype, "before_mod" ); + chemistry.apply( *restype, pose ); // Allow context sensitive + dump_molecule( *restype, "after_mod" ); + + core::Size subiteration( 0 ); + + while ( restype && chemistry.get_last_status() == core::chemical::modifications::SUCCESS ) { + ++subiteration; + + IndexVDMapping index_vd_mapping( combine( starting_index_vd_mapping, chemistry.get_mapping() ) ); + + std::string new_name( find_new_res_name( original_name, ii, subiteration ) ); + + if ( post_process_restype(restype, index_vd_mapping, new_name, pose ) ) { + // ResidueType is unusable + continue; + } + + core::pose::PoseOP working_pose( pose.clone() ); + + if ( ! emplace_residue_type(*working_pose, restype, index_vd_mapping ) ) { + // No errors on emplacement + + // Because the position may change during docking + res_pos = find_design_position( *working_pose ); + dump_molecule( working_pose->residue( res_pos ), "tested" ); + + Real score = scorer()->report_sm( *working_pose ); + debug_assert( poses.size() == scores.size() ); + poses.push_back( working_pose ); + scores.push_back( score ); + TR << "For cycle " << ii << " output " << poses.size() << " ligand " << working_pose->residue( res_pos ).name() << " score " << score << std::endl; + } + + // Try next ResidueType + restype = chemistry.get_additional_output(); + } + + // Figure out the best pose. + if ( poses.size() == 0 ) { + TR << "After " << ii << " cycles, no more applicable reactions." << std::endl; + break; + } + + debug_assert( poses.size() == scores.size() ); + core::Size selected( 0 ); + core::Real best_score( 999999999 ); + for ( core::Size jj(1); jj <= scores.size(); ++jj ) { + if ( scores[jj] - bonus_ >= initial_score ) { + continue; + } + if ( scores[jj] < best_score ) { + best_score = scores[jj]; + selected = jj; + } + } + + if ( selected == 0 ) { + TR << "After " << ii << " cycles, no products passed the score cutoffs. " << std::endl; + break; + } + + // Substitute in the best pose, and repeat the reactions. + pose = *poses[ selected ]; + + } // i<=maxtrials_ + + TR << "DrugPolishMover finished." << std::endl; + + // Output the final docked ligand as sdf, for convenience + res_pos = find_design_position( pose ); + dump_molecule( pose.residue( res_pos ), "final" ); + +}// apply + +std::string +DrugPolishMover::get_name() const { + return DrugPolishMoverCreator::mover_name(); +} + +/// @brief parse xml file +void +DrugPolishMover::parse_my_tag( TagCOP const tag, basic::datacache::DataMap & data ) +{ + maxtrials( tag->getOption< core::Size >( "maxcycles", 10 ) ); + chain( tag->getOption< char >( "chain", 'X' ) ); + + if ( ! tag->hasOption( "scorer" ) ) { + utility_exit_with_message("You must provide a scorer option to the DrugPolishMover for scoring."); + } + std::string const filter_name( tag->getOption< std::string >( "scorer" ) ); + scorer( protocols::rosetta_scripts::parse_filter(filter_name, data) ); + std::string const mover_name( tag->getOption< std::string >( "redocker", "null_mover" ) ); + redocker( protocols::rosetta_scripts::parse_mover(mover_name, data) ); + + bonus( tag->getOption< core::Real >( "bonus" ) ); + + std::string const prefilter_name( tag->getOption< std::string >( "prefilter", "" ) ); + if ( prefilter_name.size() ) { + prefilter( protocols::rosetta_scripts::parse_filter(prefilter_name, data) ); + } else { + prefilter( nullptr ); + } + + std::string const postfilter_name( tag->getOption< std::string >( "postfilter", "" ) ); + if ( postfilter_name.size() ) { + postfilter( protocols::rosetta_scripts::parse_filter(postfilter_name, data) ); + } else { + postfilter( nullptr ); + } + + utility::vector1< TagCOP > const sub_tags( tag->getTags() ); + BOOST_FOREACH ( TagCOP const subtag, sub_tags ) { + if ( subtag->getName() == "Before" ) { + add_before_chemistry( chemistry_from_subtag( subtag, data ) ); + } else if ( subtag->getName() == "After" ) { + add_after_chemistry( chemistry_from_subtag( subtag, data ) ); + } else { + utility_exit_with_message( "tag name " + subtag->getName() + " unrecognized." ); + } + }//foreach subtag +} + +} // ns drug_design +} // ns protocols diff --git a/source/src/protocols/drug_design/DrugPolishMover.fwd.hh b/source/src/protocols/drug_design/DrugPolishMover.fwd.hh new file mode 100644 index 0000000000..77c3bca248 --- /dev/null +++ b/source/src/protocols/drug_design/DrugPolishMover.fwd.hh @@ -0,0 +1,32 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/DrugPolishMover.fwd.hh +/// @brief +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_DrugPolishMover_fwd_hh +#define INCLUDED_protocols_drug_design_DrugPolishMover_fwd_hh + + +// Utility headers +#include + +namespace protocols { +namespace drug_design { + +class DrugPolishMover; +typedef utility::pointer::shared_ptr< DrugPolishMover > DrugPolishMoverOP; +typedef utility::pointer::shared_ptr< DrugPolishMover const > DrugPolishMoverCOP; + + +} // namespace drug_design +} // namespace protocols + +#endif diff --git a/source/src/protocols/drug_design/DrugPolishMover.hh b/source/src/protocols/drug_design/DrugPolishMover.hh new file mode 100644 index 0000000000..01ac84abf6 --- /dev/null +++ b/source/src/protocols/drug_design/DrugPolishMover.hh @@ -0,0 +1,90 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/DrugPolishMover.hh +/// @brief Exhaustively enumerate reactions on a ligand +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_DrugPolishMover_hh +#define INCLUDED_protocols_drug_design_DrugPolishMover_hh + +// Unit header +#include + +#include + +// Package headers +#include +#include +#include +#include +#include +#include +#include + +// Project Headers + +// Utility headers +#include +#include + +// External headers + +// C/C++ headers +#include + +namespace protocols { +namespace drug_design { + +class DrugPolishMover : public protocols::drug_design::DrugDesignMover { + // TODO: Checkpointing +public: + /// @brief default constructor + DrugPolishMover(); + + /// @brief destructor + ~DrugPolishMover() override; + + /// @brief create copy constructor + protocols::moves::MoverOP clone() const override; + + /// @brief create this type of objectt + protocols::moves::MoverOP fresh_instance() const override; + + /// @brief apply DrugPolishMover + void apply( core::pose::Pose & pose ) override; + + std::string get_name() const override; + +public: // accessors + + /// @brief The bonus given to scoring for applying (another) reaction + core::Real bonus() const { return bonus_; } + +public: // setters + + /// @brief The bonus given to scoring for applying (another) reaction + void bonus(core::Real setting) { bonus_ = setting; } + +public: // Other functions + + /// @brief parse xml file + void + parse_my_tag( TagCOP tag, basic::datacache::DataMap & data ) override; + +private: // Data + + /// @brief The bonus given to scoring for applying (another) reaction + core::Real bonus_; +}; + +} // namespace drug_design +} // namespace protocols + +#endif diff --git a/source/src/protocols/drug_design/DrugPolishMoverCreator.hh b/source/src/protocols/drug_design/DrugPolishMoverCreator.hh new file mode 100644 index 0000000000..089014e131 --- /dev/null +++ b/source/src/protocols/drug_design/DrugPolishMoverCreator.hh @@ -0,0 +1,33 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +///@file protocols/drug_design/DrugPolishMoverDrugPolishMoverCreator.hh +///@brief This class will create instances of Mover DrugPolishMover for the MoverFactory +///@author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_DrugPolishMoverCreator_hh +#define INCLUDED_protocols_drug_design_DrugPolishMoverCreator_hh + +#include + +namespace protocols { +namespace drug_design { + +class DrugPolishMoverCreator : public protocols::moves::MoverCreator { +public: + moves::MoverOP create_mover() const; + std::string keyname() const; + static std::string mover_name(); +}; + +} +} + +#endif + diff --git a/source/src/protocols/drug_design/HeteroHeteroBondFilter.cc b/source/src/protocols/drug_design/HeteroHeteroBondFilter.cc new file mode 100644 index 0000000000..42604ad303 --- /dev/null +++ b/source/src/protocols/drug_design/HeteroHeteroBondFilter.cc @@ -0,0 +1,147 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/HeteroHeteroBondFilter.cc +/// @brief +/// @author Rocco Moretti (rmorettiase@gmail.com) + + +//Unit Headers +#include +#include + +//Project Headers +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR( "protocols.drug_design.HeteroHeteroBondFilter" ); + +protocols::filters::FilterOP +HeteroHeteroBondFilterCreator::create_filter() const { return protocols::filters::FilterOP( new HeteroHeteroBondFilter ); } + +std::string +HeteroHeteroBondFilterCreator::keyname() const { return HeteroHeteroBondFilter::class_name(); } + +void HeteroHeteroBondFilterCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const { + HeteroHeteroBondFilter::provide_xml_schema( xsd ); +} + +std::string HeteroHeteroBondFilter::class_name() { + return "HeteroHeteroBond"; +} + +void HeteroHeteroBondFilter::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) +{ + using namespace utility::tag; + AttributeList attlist; + + attlist + + XMLSchemaAttribute::required_attribute( "residue", xsct_refpose_enabled_residue_number, "Residue to test" ) + + XMLSchemaAttribute::attribute_w_default( "exclude_ates", xsct_rosetta_bool, + "Exclude hetero-hetero bonds in things like phosphates, sulfates, etc. (Hetero-hetero bonds when there's also a double bond to oxygen in the group.)", "true" ) + + XMLSchemaAttribute( "threshold", xsct_real, "Fail if the number of hetero-hetero bonds is greater than this." ); + + protocols::filters::xsd_type_definition_w_attributes( xsd, class_name(), + "Test the number of heteroatom-heteroatom (non C/H) bonds in a residue.", + attlist ); +} + +void +HeteroHeteroBondFilter::parse_my_tag( utility::tag::TagCOP tag, basic::datacache::DataMap & ) +{ + residue_ = tag->getOption< std::string >( "residue" ); + threshold_ = tag->getOption( "threshold", threshold_ ); + exclude_ates_ = tag->getOption( "exclude_ates", exclude_ates_ ); + TR << "HeteroHeteroBond filter for residue " << residue_ << " with an upper threshold of " << threshold_ << (exclude_ates_?" excluding":" including") << " oxidixed groups." << std::endl; +} + +bool +HeteroHeteroBondFilter::apply( core::pose::Pose const & pose ) const { + core::Size const value( compute( pose ) ); + if ( value > threshold_ ) { + TR << "Failing HeteroHeteroBond filter on residue " << residue_ << " with value " << value << " which is above the threshold of " << threshold_ << std::endl; + return false; + } else { + return true; + } +} + +void +HeteroHeteroBondFilter::report( std::ostream & out, core::pose::Pose const & pose ) const { + core::Size const value( compute( pose ) ); + out << "HeteroHeteroBond value for residue " << residue_ << " is " << value << std::endl; +} + +core::Real +HeteroHeteroBondFilter::report_sm( core::pose::Pose const & pose ) const { + return( compute( pose ) ); +} + +core::Size +HeteroHeteroBondFilter::compute( core::pose::Pose const & pose ) const { + core::Size resnum( core::pose::parse_resnum( residue_, pose ) ); + if ( resnum == 0 || resnum > pose.total_residue() ) { + TR.Warning << "Attempted to access residue " << residue_ << " in pose with " << pose.total_residue() << " residues. Failing filter. " << std::endl; + utility_exit_with_message("Cannot apply HeteroHeteroBond filter on non-existant residue!"); + } + using namespace core::chemical; + ResidueType const & restype( pose.residue(resnum).type() ); + + core::Size count(0); + + for ( auto const & bondpair: restype.bonds() ) { + element::Elements elem1( restype.element( bondpair.first ) ); + element::Elements elem2( restype.element( bondpair.second ) ); + if ( elem1 == element::H || elem1 == element::C || elem2 == element::H || elem2 == element::C ) { + continue; + } + if ( exclude_ates_ ) { + bool is_ate( false ); + // Look at the bonds attached to each of the two atoms in the bond, and reject if there exists a doublebond to oxygen. + for ( core::Size nbr1: restype.bonded_neighbor( bondpair.first ) ) { + if ( restype.bond_type( bondpair.first, nbr1 ) == DoubleBond && restype.element( nbr1 ) == element::O ) { + is_ate = true; + break; + } + } + for ( core::Size nbr2: restype.bonded_neighbor( bondpair.second ) ) { + if ( restype.bond_type( bondpair.second, nbr2 ) == DoubleBond && restype.element( nbr2 ) == element::O ) { + is_ate = true; + break; + } + } + if ( is_ate ) { + continue; + } + } + ++count; + } + + TR << "HeteroHeteroBond for residue " << residue_ << ", of type " << restype.name() << " is " << count << ( exclude_ates_ ? " excluding":"including") << " oxidized groups." << std::endl; + + return count; +} + +} +} diff --git a/source/src/protocols/drug_design/HeteroHeteroBondFilter.fwd.hh b/source/src/protocols/drug_design/HeteroHeteroBondFilter.fwd.hh new file mode 100644 index 0000000000..f189b8a6a3 --- /dev/null +++ b/source/src/protocols/drug_design/HeteroHeteroBondFilter.fwd.hh @@ -0,0 +1,28 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/HeteroHeteroBondFilter.fwd.hh +/// @brief +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_HeteroHeteroBondFilter_fwd_hh +#define INCLUDED_protocols_drug_design_HeteroHeteroBondFilter_fwd_hh + + +namespace protocols { +namespace drug_design { + +class HeteroHeteroBondFilter; + +} +} + +#endif + + diff --git a/source/src/protocols/drug_design/HeteroHeteroBondFilter.hh b/source/src/protocols/drug_design/HeteroHeteroBondFilter.hh new file mode 100644 index 0000000000..95b7d6ac04 --- /dev/null +++ b/source/src/protocols/drug_design/HeteroHeteroBondFilter.hh @@ -0,0 +1,83 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/HeteroHeteroBondFilter.hh +/// @brief definition of filter class HeteroHeteroBondFilter. +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_HeteroHeteroBondFilter_hh +#define INCLUDED_protocols_drug_design_HeteroHeteroBondFilter_hh + +//unit headers +#include + +// Project Headers +#include +#include +#include +#include +#include + +#include + +namespace protocols { +namespace drug_design { + +class HeteroHeteroBondFilter : public filters::Filter +{ +public: + HeteroHeteroBondFilter(): + Filter( class_name() ), + threshold_( 9999 ), + exclude_ates_( true ) + {} + + HeteroHeteroBondFilter( std::string const & residue, core::Size threshold, bool exclude_ates = true ): + Filter( class_name() ), + residue_( residue ), + threshold_( threshold ), + exclude_ates_( exclude_ates ) + {} + + void residue( std::string const & residue ) { residue_ = residue; } + std::string const & residue() const { return residue_; } + + void threshold( core::Size setting ) { threshold_ = setting; } + core::Size threshold() const { return threshold_; } + + void exclude_ates( bool setting ) { exclude_ates_ = setting; } + bool exclude_ates() const { return exclude_ates_; } + + bool apply( core::pose::Pose const & pose ) const override; + + filters::FilterOP clone() const override { + return filters::FilterOP( new HeteroHeteroBondFilter( *this ) ); + } + filters::FilterOP fresh_instance() const override { + return filters::FilterOP( new HeteroHeteroBondFilter() ); + } + + void report( std::ostream & out, core::pose::Pose const & pose ) const override; + core::Real report_sm( core::pose::Pose const & pose ) const override; + core::Size compute( core::pose::Pose const &pose ) const; + void parse_my_tag( utility::tag::TagCOP tag, basic::datacache::DataMap & ) override; + + static std::string class_name(); + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + +private: + std::string residue_; + core::Size threshold_; + bool exclude_ates_; +}; + +} +} + +#endif diff --git a/source/src/protocols/drug_design/HeteroHeteroBondFilterCreator.hh b/source/src/protocols/drug_design/HeteroHeteroBondFilterCreator.hh new file mode 100644 index 0000000000..460b238593 --- /dev/null +++ b/source/src/protocols/drug_design/HeteroHeteroBondFilterCreator.hh @@ -0,0 +1,40 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/HeteroHeteroBondFilterCreator.hh +/// @brief FilterCreator for the HeteroHeteroBondFilter +/// @author Rocco Moretti (rmorettiase@gmail.com) + + +#ifndef INCLUDED_protocols_drug_design_HeteroHeteroBondFilterCreator_hh +#define INCLUDED_protocols_drug_design_HeteroHeteroBondFilterCreator_hh + +// Package Headers +#include + +// Utility Headers + +// c++ headers +#include + +namespace protocols { +namespace drug_design { + +class HeteroHeteroBondFilterCreator : public protocols::filters::FilterCreator +{ +public: + protocols::filters::FilterOP create_filter() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const override; +}; + +} +} + +#endif diff --git a/source/src/protocols/drug_design/NChiFilter.cc b/source/src/protocols/drug_design/NChiFilter.cc new file mode 100644 index 0000000000..cb7a7f8bee --- /dev/null +++ b/source/src/protocols/drug_design/NChiFilter.cc @@ -0,0 +1,120 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/NChiFilter.cc +/// @brief +/// @author Rocco Moretti (rmorettiase@gmail.com) + + +//Unit Headers +#include +#include + +//Project Headers +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR( "protocols.drug_design.NChiFilter" ); + +protocols::filters::FilterOP +NChiFilterCreator::create_filter() const { return protocols::filters::FilterOP( new NChiFilter ); } + +std::string +NChiFilterCreator::keyname() const { return NChiFilter::class_name(); } + +void NChiFilterCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const { + NChiFilter::provide_xml_schema( xsd ); +} + +std::string NChiFilter::class_name() { + return "NChi"; +} + +void NChiFilter::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) +{ + using namespace utility::tag; + AttributeList attlist; + + attlist + + XMLSchemaAttribute::required_attribute( "residue", xsct_refpose_enabled_residue_number, "Residue for which to calculate the number of rotatable bonds" ) + + XMLSchemaAttribute::attribute_w_default( "exclude_proton", xsct_rosetta_bool, "Don't count proton chis", "true" ) + + XMLSchemaAttribute( "threshold", xsct_real, "Fail if the number of rotatable bonds is greater than this." ); + + protocols::filters::xsd_type_definition_w_attributes( xsd, class_name(), + "Count the number of rotatable bonds (as determined by Rosetta).", + attlist ); +} + +void +NChiFilter::parse_my_tag( utility::tag::TagCOP tag, basic::datacache::DataMap & ) +{ + residue_ = tag->getOption< std::string >( "residue" ); + threshold_ = tag->getOption( "threshold", threshold_ ); + exclude_proton_ = tag->getOption( "exclude_proton", exclude_proton_ ); + TR << "NChi filter for residue " << residue_ << " with an upper threshold of " << threshold_ << (exclude_proton_?" excluding":" including") << " proton chis." << std::endl; +} + +bool +NChiFilter::apply( core::pose::Pose const & pose ) const { + core::Size const value( compute( pose ) ); + if ( value > threshold_ ) { + TR << "Failing NChi filter on residue " << residue_ << " with value " << value << " which is above the threshold of " << threshold_ << std::endl; + return false; + } else { + return true; + } +} + +void +NChiFilter::report( std::ostream & out, core::pose::Pose const & pose ) const { + core::Size const value( compute( pose ) ); + out << "NChi value for residue " << residue_ << " is " << value << std::endl; +} + +core::Real +NChiFilter::report_sm( core::pose::Pose const & pose ) const { + return( compute( pose ) ); +} + +core::Size +NChiFilter::compute( core::pose::Pose const & pose ) const { + core::Size resnum( core::pose::parse_resnum( residue_, pose ) ); + if ( resnum == 0 || resnum > pose.total_residue() ) { + TR.Warning << "Attempted to access residue " << residue_ << " in pose with " << pose.total_residue() << " residues. Failing filter. " << std::endl; + utility_exit_with_message("Cannot apply NChi filter on non-existant residue!"); + } + core::chemical::ResidueType const & restype( pose.residue(resnum).type() ); + + core::Real retval( restype.nchi() ); + + if ( exclude_proton_ ) { + retval -= restype.n_proton_chi(); + } + + TR << "NChi for residue " << residue_ << ", of type " << restype.name() << " is " << retval << ( exclude_proton_ ? " excluding":"including") << " proton chis." << std::endl; + + return retval; +} + +} +} diff --git a/source/src/protocols/drug_design/NChiFilter.fwd.hh b/source/src/protocols/drug_design/NChiFilter.fwd.hh new file mode 100644 index 0000000000..af68115277 --- /dev/null +++ b/source/src/protocols/drug_design/NChiFilter.fwd.hh @@ -0,0 +1,28 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/NChiFilter.fwd.hh +/// @brief +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_NChiFilter_fwd_hh +#define INCLUDED_protocols_drug_design_NChiFilter_fwd_hh + + +namespace protocols { +namespace drug_design { + +class NChiFilter; + +} +} + +#endif + + diff --git a/source/src/protocols/drug_design/NChiFilter.hh b/source/src/protocols/drug_design/NChiFilter.hh new file mode 100644 index 0000000000..6f8d1ded08 --- /dev/null +++ b/source/src/protocols/drug_design/NChiFilter.hh @@ -0,0 +1,83 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/NChiFilter.hh +/// @brief definition of filter class NChiFilter. +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_NChiFilter_hh +#define INCLUDED_protocols_drug_design_NChiFilter_hh + +//unit headers +#include + +// Project Headers +#include +#include +#include +#include +#include + +#include + +namespace protocols { +namespace drug_design { + +class NChiFilter : public filters::Filter +{ +public: + NChiFilter(): + Filter( class_name() ), + threshold_( 9999 ), + exclude_proton_( true ) + {} + + NChiFilter( std::string const & residue, core::Size threshold, bool exclude_proton = true ): + Filter( class_name() ), + residue_( residue ), + threshold_( threshold ), + exclude_proton_( exclude_proton ) + {} + + void residue( std::string const & residue ) { residue_ = residue; } + std::string const & residue() const { return residue_; } + + void threshold( core::Size setting ) { threshold_ = setting; } + core::Size threshold() const { return threshold_; } + + void exclude_proton( bool setting ) { exclude_proton_ = setting; } + bool exclude_proton() const { return exclude_proton_; } + + bool apply( core::pose::Pose const & pose ) const override; + + filters::FilterOP clone() const override{ + return filters::FilterOP( new NChiFilter( *this ) ); + } + filters::FilterOP fresh_instance() const override{ + return filters::FilterOP( new NChiFilter() ); + } + + void report( std::ostream & out, core::pose::Pose const & pose ) const override; + core::Real report_sm( core::pose::Pose const & pose ) const override; + core::Size compute( core::pose::Pose const &pose ) const; + void parse_my_tag( utility::tag::TagCOP tag, basic::datacache::DataMap & ) override; + + static std::string class_name(); + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + +private: + std::string residue_; + core::Size threshold_; + bool exclude_proton_; +}; + +} +} + +#endif diff --git a/source/src/protocols/drug_design/NChiFilterCreator.hh b/source/src/protocols/drug_design/NChiFilterCreator.hh new file mode 100644 index 0000000000..7f9a49a38e --- /dev/null +++ b/source/src/protocols/drug_design/NChiFilterCreator.hh @@ -0,0 +1,40 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/NChiFilterCreator.hh +/// @brief FilterCreator for the NChiFilter +/// @author Rocco Moretti (rmorettiase@gmail.com) + + +#ifndef INCLUDED_protocols_drug_design_NChiFilterCreator_hh +#define INCLUDED_protocols_drug_design_NChiFilterCreator_hh + +// Package Headers +#include + +// Utility Headers + +// c++ headers +#include + +namespace protocols { +namespace drug_design { + +class NChiFilterCreator : public protocols::filters::FilterCreator +{ +public: + protocols::filters::FilterOP create_filter() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const override; +}; + +} +} + +#endif diff --git a/source/src/protocols/drug_design/RDKitMetricFilter.cc b/source/src/protocols/drug_design/RDKitMetricFilter.cc new file mode 100644 index 0000000000..ba6cc2c8d5 --- /dev/null +++ b/source/src/protocols/drug_design/RDKitMetricFilter.cc @@ -0,0 +1,150 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/RDKitMetricFilter.cc +/// @brief +/// @author Rocco Moretti (rmorettiase@gmail.com) + +//Unit Headers +#include +#include + +//Project Headers +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR( "protocols.drug_design.RDKitMetricFilter" ); + +protocols::filters::FilterOP +RDKitMetricFilterCreator::create_filter() const { return protocols::filters::FilterOP( new RDKitMetricFilter ); } + +std::string +RDKitMetricFilterCreator::keyname() const { return RDKitMetricFilter::class_name(); } + +void RDKitMetricFilterCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const { + RDKitMetricFilter::provide_xml_schema( xsd ); +} + +std::string RDKitMetricFilter::class_name() { + return "RDKitMetric"; +} + +void RDKitMetricFilter::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) +{ + using namespace utility::tag; + AttributeList attlist; + + attlist + + XMLSchemaAttribute::required_attribute( "residue", xsct_refpose_enabled_residue_number, "Residue to compute the metrics on" ) + + XMLSchemaAttribute::required_attribute( "metric", xs_string, "Metric to calculate" ) + + XMLSchemaAttribute( "lower_threshold", xsct_real, + "Filter is false if the metric is less than this value" ) + + XMLSchemaAttribute( "upper_threshold", xsct_real, + "Filter is false if the metric is greater than this value" ); + + protocols::filters::xsd_type_definition_w_attributes( xsd, class_name(), + "Calculates the specified metric from RDKit on the given residue.", + attlist ); +} + +void +RDKitMetricFilter::parse_my_tag( utility::tag::TagCOP tag, basic::datacache::DataMap & ) +{ + residue_ = tag->getOption< std::string >( "residue" ); + metric( tag->getOption( "metric" ) ); // For value checking. + lower_threshold_ = tag->getOption( "lower_threshold", lower_threshold_ ); + upper_threshold_ = tag->getOption( "upper_threshold", upper_threshold_ ); + TR << "RDKitMetric filter for residue " << residue_ << " metric " << metric_ << " between " << lower_threshold_ << " and " << upper_threshold_ << std::endl; +} + +void +RDKitMetricFilter::metric( std::string const & setting ) { + if ( setting.size() != 0 ) { + std::map< std::string, std::string > const metrics( core::chemical::rdkit::get_metric_names() ); + if ( metrics.count( setting ) == 0 ) { + TR.Error << "Metric " << setting << " not recognized by Rosetta as a valid RDKit metric. Valid metrics are:\n\n"; + for ( std::map< std::string, std::string >::const_iterator itr( metrics.begin() ), itr_end( metrics.end()); + itr != itr_end; ++itr ) { + TR.Error << itr->first << "\t-\t" << itr->second << "\n"; + } + TR << std::endl; + utility_exit_with_message("Rosetta doesn't understand '"+setting+"' as an RDKit metric."); + } + } + metric_ = setting; +} + +bool +RDKitMetricFilter::apply( core::pose::Pose const & pose ) const { + core::Real const value( compute( pose ) ); + if ( value < lower_threshold_ ) { + TR << "Failing RDKitMetric filter on residue " << residue_ << " with " << metric_ << " value " << value << " which is below the lower threshold of " << lower_threshold_ << std::endl; + return false; + } else if ( value > upper_threshold_ ) { + TR << "Failing RDKitMetric filter on residue " << residue_ << " with " << metric_ << " value " << value << " which is above the upper threshold of " << upper_threshold_ << std::endl; + return false; + } else { + return true; + } +} + +void +RDKitMetricFilter::report( std::ostream & out, core::pose::Pose const & pose ) const { + core::Real const value( compute( pose ) ); + out << "RDKit metric '" << metric_ << "' for residue " << residue_ << " is " << value << std::endl; +} + +core::Real +RDKitMetricFilter::report_sm( core::pose::Pose const & pose ) const { + return( compute( pose ) ); +} + +core::Real +RDKitMetricFilter::compute( core::pose::Pose const & pose ) const { + core::Size resnum( core::pose::parse_resnum( residue_, pose ) ); + if ( resnum == 0 || resnum > pose.total_residue() ) { + TR.Error << "Attempted to access residue " << residue_ << " in pose with " << pose.total_residue() << " residues. Failing filter. " << std::endl; + utility_exit_with_message("Cannot apply RDKitMetric filter on non-existant residue!"); + } + if ( metric_.size() == 0 ) { + TR.Error << "Metric must be set for RDKitMetric" << std::endl; + utility_exit_with_message("Must set metric in RDKitMetricFilter."); + } + + core::chemical::MutableResidueTypeOP restype( utility::pointer::make_shared< core::chemical::MutableResidueType >(pose.residue(resnum).type()) ); + TR << "Calculating RDKit metric '" << metric_ << "' value for residue " << residue_ << ", of type " << restype->name() << std::endl; + + core::Real retval(0); + + // Use a neutral, hydrogen free molecule for calculating the metric values - should be the default + core::chemical::rdkit::RestypeToRDMol converter(*restype); + ::RDKit::RWMolOP rdmol(converter.Mol() ); + retval = core::chemical::rdkit::rdkit_metric( *rdmol, metric_ ); + + return retval; +} + +} +} diff --git a/source/src/protocols/drug_design/RDKitMetricFilter.fwd.hh b/source/src/protocols/drug_design/RDKitMetricFilter.fwd.hh new file mode 100644 index 0000000000..73f5af44f2 --- /dev/null +++ b/source/src/protocols/drug_design/RDKitMetricFilter.fwd.hh @@ -0,0 +1,28 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/RDKitMetricFilter.fwd.hh +/// @brief +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_RDKitMetricFilter_fwd_hh +#define INCLUDED_protocols_drug_design_RDKitMetricFilter_fwd_hh + + +namespace protocols { +namespace drug_design { + +class RDKitMetricFilter; + +} +} + +#endif + + diff --git a/source/src/protocols/drug_design/RDKitMetricFilter.hh b/source/src/protocols/drug_design/RDKitMetricFilter.hh new file mode 100644 index 0000000000..f22cb8b5ad --- /dev/null +++ b/source/src/protocols/drug_design/RDKitMetricFilter.hh @@ -0,0 +1,89 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/RDKitMetricFilter.hh +/// @brief definition of filter class RDKitMetricFilter. +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_RDKitMetricFilter_hh +#define INCLUDED_protocols_drug_design_RDKitMetricFilter_hh + +//unit headers +#include + +// Project Headers +#include +#include +#include +#include +#include + +#include + +namespace protocols { +namespace drug_design { + +class RDKitMetricFilter : public filters::Filter +{ +public: + RDKitMetricFilter(): + Filter( class_name() ), + lower_threshold_( -9999 ), + upper_threshold_( 9999 ) + {} + + RDKitMetricFilter( std::string const & residue, std::string const & metric_name, core::Real lower, core::Real upper ): + Filter( class_name() ), + residue_( residue ), + lower_threshold_( lower ), + upper_threshold_( upper ) + { + metric( metric_name ); + } + + void residue( std::string const & residue ) { residue_ = residue; } + std::string const & residue() const { return residue_; } + + void lower( core::Real lower ) { lower_threshold_ = lower; } + core::Real lower() const { return lower_threshold_; } + + void upper( core::Real upper ) { upper_threshold_ = upper; } + core::Real upper() const { return upper_threshold_; } + + void metric( std::string const & setting ); + std::string const & metric() const { return metric_; } + + bool apply( core::pose::Pose const & pose ) const override; + + filters::FilterOP clone() const override { + return filters::FilterOP( new RDKitMetricFilter( *this ) ); + } + filters::FilterOP fresh_instance() const override { + return filters::FilterOP( new RDKitMetricFilter() ); + } + + void report( std::ostream & out, core::pose::Pose const & pose ) const override; + core::Real report_sm( core::pose::Pose const & pose ) const override; + core::Real compute( core::pose::Pose const &pose ) const; + void parse_my_tag( utility::tag::TagCOP tag, basic::datacache::DataMap & ) override; + + static std::string class_name(); + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + +private: + std::string residue_; + std::string metric_; + core::Real lower_threshold_; + core::Real upper_threshold_; +}; + +} +} + +#endif diff --git a/source/src/protocols/drug_design/RDKitMetricFilterCreator.hh b/source/src/protocols/drug_design/RDKitMetricFilterCreator.hh new file mode 100644 index 0000000000..e8fadd65f2 --- /dev/null +++ b/source/src/protocols/drug_design/RDKitMetricFilterCreator.hh @@ -0,0 +1,40 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/RDKitMetricFilterCreator.hh +/// @brief FilterCreator for the RDKitMetricFilter +/// @author Rocco Moretti (rmorettiase@gmail.com) + + +#ifndef INCLUDED_protocols_drug_design_RDKitMetricFilterCreator_hh +#define INCLUDED_protocols_drug_design_RDKitMetricFilterCreator_hh + +// Package Headers +#include + +// Utility Headers + +// c++ headers +#include + +namespace protocols { +namespace drug_design { + +class RDKitMetricFilterCreator : public protocols::filters::FilterCreator +{ +public: + protocols::filters::FilterOP create_filter() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const override; +}; + +} +} + +#endif diff --git a/source/src/protocols/drug_design/RandomFragmentLigand.cc b/source/src/protocols/drug_design/RandomFragmentLigand.cc new file mode 100644 index 0000000000..ff91c42b19 --- /dev/null +++ b/source/src/protocols/drug_design/RandomFragmentLigand.cc @@ -0,0 +1,306 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/RandomFragmentLigand.hh +/// @brief Fragment a ligand to make it smaller +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR("protocols.drug_design.RandomFragmentLigand"); + +//------------------------- Creator ----------------------------- + +protocols::chemistries::ChemistryOP +RandomFragmentLigandCreator::create_chemistry() const { + return protocols::chemistries::ChemistryOP( new RandomFragmentLigand ); +} + +std::string +RandomFragmentLigandCreator::keyname() const { + return RandomFragmentLigand::class_name(); +} + +void +RandomFragmentLigandCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const { + RandomFragmentLigand::provide_xml_schema( xsd ); +} + + + +//------------------------- Chemistry ----------------------------- + +RandomFragmentLigand::RandomFragmentLigand(): + Chemistry(class_name()), + keep_bigger_( false ), + ccbond_( false ) +{} + +void +RandomFragmentLigand::keep_atom( std::string const & keep_atom ) { + keep_atom_ = keep_atom; + utility::strip_whitespace( keep_atom_ ); +} + +void +RandomFragmentLigand::apply( core::chemical::MutableResidueType & restype ) +{ + using namespace core::chemical; + + if ( keep_bigger_ && keep_atom_ != "" ) { + utility_exit_with_message("Cannot set both keep_bigger and keep_atom for RandomFragmentLigand."); + } + + ResidueGraph const & graph( restype.graph() ); + + // Make sure rings are annotated. + find_bonds_in_rings( restype ); + + // Pick a random single bond, not on a ring and not connected to a hydrogen + utility::vector1 valid_bonds; + EIter iter, iter_end; + for ( boost::tie(iter, iter_end) = boost::edges(graph); iter != iter_end; ++iter ) { + Atom const & a1( graph[ boost::source( *iter, graph ) ] ); + assert( a1.element_type() ); + Atom const & a2( graph[ boost::target( *iter, graph ) ] ); + assert( a2.element_type() ); + if ( graph[ *iter ].ringness() != BondNotInRing ) continue; + if ( a1.element_type()->element() == element::H || + a2.element_type()->element() == element::H ) continue; + // If ccbond_ is set, skip bonds which aren't carbon-carbon + if ( ccbond_ && + ( a1.element_type()->element() != element::C || + a2.element_type()->element() != element::C ) ) continue; + + valid_bonds.push_back( *iter ); + } + + if ( valid_bonds.size() == 0 ) { + TR << "No valid bonds on which to fragment ligand - skipping." << std::endl; + set_last_status( core::chemical::modifications::FAIL_DO_NOT_RETRY ); + return; + } + + ED selected_bond = numeric::random::rg().random_element(valid_bonds); + VD a1( boost::source( selected_bond, graph ) ); + VD a2( boost::target( selected_bond, graph ) ); + + TR << "Deleting bond between " << restype.atom_name( a1 ) << " (" << a1 << ") and " << restype.atom_name( a2 ) << " (" << a2 << ")" << std::endl; + // Delete the bond. + restype.delete_bond( a1, a2 ); + + // Determine the connected components (Do either side of bond, look at connections recursively). + utility::vector1 side1, side2; + + typedef std::map< core::chemical::VD, core::Size > ComponentMap; + ComponentMap components; + core::Size ncomponent; + boost::associative_property_map< ComponentMap > component_property_map(components); + ncomponent = boost::connected_components( graph, component_property_map ); + + if ( ncomponent != 2 ) { + utility_exit_with_message("Expected to generate two pieces when fragmenting along non-ring single bond. Found "+utility::to_string(ncomponent)+" instead." ); + } + VD side1_attach(MutableResidueType::null_vertex), side2_attach(MutableResidueType::null_vertex); + for ( ComponentMap::const_iterator iter(components.begin()), iter_end(components.end()); iter != iter_end; ++iter ) { + // The connected_components documentation doesn't mention, but I'm assuming the components are numbered 0 to ncomponent-1 + if ( iter->second == 0 ) { + side1.push_back( iter->first ); + if ( iter->first == a1 || iter->first == a2 ) { + side1_attach = iter->first; + } + } else if ( iter->second == 1 ) { + side2.push_back( iter->first ); + if ( iter->first == a1 || iter->first == a2 ) { + side2_attach = iter->first; + } + } else { + utility_exit_with_message("Found unexpected component "+utility::to_string(iter->second) ); + } + } + + assert( side1_attach != MutableResidueType::null_vertex && side2_attach != MutableResidueType::null_vertex); + + // Pick which atoms to keep - either the bigger side, the side with a certain atom, or random + VD keep_atom = side1_attach; // The atom on the bond we're breaking that's on the side we're keeping. + + if ( keep_atom_.size() ) { + // Search for atom + bool found1(false), found2(false); + for ( core::Size ii(1); ii <= side1.size(); ++ii ) { + if ( utility::stripped_whitespace( graph[ side1[ii] ].name() ) == keep_atom_ ) { + found1 = true; + break; + } + } + for ( core::Size ii(1); ii <= side2.size(); ++ii ) { + if ( utility::stripped_whitespace( graph[ side2[ii] ].name() ) == keep_atom_ ) { + found2 = true; + break; + } + } + if ( !found1 && !found2 ) { + utility_exit_with_message("Cannot find atom named "+keep_atom_+" in residue type "+restype.name()); + } + if ( !found1 && found2 ) { + side1.swap(side2); // Put the side to keep first + keep_atom=side2_attach; + } + } else if ( keep_bigger_ ) { + if ( side2.size() > side1.size() ) { + side1.swap(side2); // Put the side to keep first + keep_atom=side2_attach; + } + } else { // Randomly pick the side to keep. + if ( numeric::random::random_range(0,1) == 1 ) { + side1.swap(side2); // Put the side to keep first + keep_atom=side2_attach; + } + } + + // Delete the atoms which aren't on the side we want + + for ( core::Size ii(1); ii <= side2.size(); ++ii ) { // VD's should stay valid through the deletion + restype.delete_atom( side2[ii] ); + } + if ( ! restype.has( keep_atom ) ) { + TR << "Can't find atom " << keep_atom << std::endl; + restype.show_all_atom_names(TR); + runtime_assert( restype.has( keep_atom ) ); + } + + // All atoms now in ResidueTypes should keep the same VDs in the final residue + mapping_ = core::chemical::VDVDMapping(restype); // Identity map for existing atoms. + + // Add a hydrogen to the atom we just kept. + + // TODO: From the AddHydrogens mover - we probably want to generalize this out. + if ( restype.gasteiger_atom_typeset() == nullptr ) { + restype.set_gasteiger_atom_typeset(core::chemical::ChemicalManager::get_instance()->gasteiger_atom_type_set("default")); + } + //assign the gastieger atom types, which is an absolute if you want to add hydrogens + core::chemical::gasteiger::assign_gasteiger_atom_types( restype, restype.gasteiger_atom_typeset(), /*keep_existing=*/ false ); + + utility::vector1 > coords( modifications::determine_coordinates(restype, keep_atom)); + for ( core::Size new_hydrogens=1; new_hydrogens <= coords.size(); ++new_hydrogens ) { + core::Size hydro_number( restype.natoms() + 1 ); + std::string name = "H" + utility::to_string( hydro_number ); + while ( restype.has( name ) ) { + ++hydro_number; + name = "H" + utility::to_string( hydro_number ); + } + VD vertex_hydrogen = restype.add_atom(name); + restype.atom(vertex_hydrogen).ideal_xyz(coords[new_hydrogens]); + restype.atom(vertex_hydrogen).element_type(restype.element_set().element(element::name_from_elements(element::H))); //dont forget to add the element type! + restype.add_bond(vertex_hydrogen, keep_atom, SingleBond); + } + + // Redo the entire atom tree, typing, etc. + + //restype.setup_atom_ordering(); // Needed to redo atom_ordering map, etc. TODO: We need to fix this up to be consistent. + + // TODO: also from AddHydrogens - need to normalize, etc. + //rename_atoms(res, false); //not sure why, but if you try and add more hydrogens, shit hits the fan. Try and re do this + rosetta_retype_fullatom(restype, false); //need to do this, fails currently + + rosetta_recharge_fullatom(restype); + core::chemical::find_bonds_in_rings( restype ); + + VD nbr_atom = restype.nbr_vertex(); + if ( nbr_atom == MutableResidueType::null_vertex || ! restype.has( nbr_atom ) || restype.atom(nbr_atom).element_type()->element() == element::H ) { + TR << "Dumping neighbor atom and recreating." << std::endl; + nbr_atom = MutableResidueType::null_vertex; + } + restype.nbr_radius( find_nbr_dist( restype, nbr_atom ) ); + restype.nbr_atom( nbr_atom ); + restype.assign_internal_coordinates( nbr_atom ); // Also sets atom base. Needs nbr atom assignment + restype.autodetermine_chi_bonds(); // Must be after internal coordinate setup + set_last_status( core::chemical::modifications::SUCCESS ); +} + +core::chemical::VDVDMapping +RandomFragmentLigand::get_mapping() const { + return mapping_; +} + +std::string +RandomFragmentLigand::class_name() { + return "RandomFragmentLigand"; +} + +void +RandomFragmentLigand::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) { + using namespace utility::tag; + AttributeList attlist; + attlist + + XMLSchemaAttribute::attribute_w_default("keep_bigger", xsct_rosetta_bool, + "If true, keep the fragment which is bigger instead of a random one", "0") + + XMLSchemaAttribute("keep_atom", xs_string, + "If set, keep the fragment which contains the atom of the given name") + + XMLSchemaAttribute::attribute_w_default("ccbonds", xsct_rosetta_bool, + "If true, only fragment on carbon-carbon bonds", "0"); + + protocols::chemistries::xsd_type_definition_w_attributes( + xsd, class_name(), + "Fragment a ResidueType on a random bond, and discard one half. ", + attlist ); +} + +/// @brief Initialize any data members of this instance from an input tag +/// and a DataMap object +void +RandomFragmentLigand::parse_my_tag( + utility::tag::TagCOP tag, + basic::datacache::DataMap & ) { + + keep_bigger( tag->getOption("keep_bigger", false) ); + keep_atom( tag->getOption("keep_atom", "") ); + + if ( keep_bigger_ && keep_atom_ != "" ) { + throw CREATE_EXCEPTION(utility::excn::RosettaScriptsOptionError, "Cannot set both keep_bigger and keep_atom for RandomFragmentLigand."); + } + + ccbond( tag->getOption("ccbonds", false) ); + +} + + +} +} diff --git a/source/src/protocols/drug_design/RandomFragmentLigand.fwd.hh b/source/src/protocols/drug_design/RandomFragmentLigand.fwd.hh new file mode 100644 index 0000000000..1813f78213 --- /dev/null +++ b/source/src/protocols/drug_design/RandomFragmentLigand.fwd.hh @@ -0,0 +1,33 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/RandomFragmentLigand.hh +/// @brief Fragment a ligand to make it smaller +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_RandomFragmentLigand_fwd_hh +#define INCLUDED_protocols_drug_design_RandomFragmentLigand_fwd_hh + +#include + +namespace protocols { +namespace drug_design { + + +class RandomFragmentLigand; // fwd declaration +typedef utility::pointer::shared_ptr< RandomFragmentLigand > RandomFragmentLigandOP; +typedef utility::pointer::shared_ptr< RandomFragmentLigand const > RandomFragmentLigandCOP; + + +} // namespace drug_design +} // namespace protocols + +#endif // INCLUDED_protocols_drug_design_RandomFragmentLigand_fwd_hh + + diff --git a/source/src/protocols/drug_design/RandomFragmentLigand.hh b/source/src/protocols/drug_design/RandomFragmentLigand.hh new file mode 100644 index 0000000000..911501f504 --- /dev/null +++ b/source/src/protocols/drug_design/RandomFragmentLigand.hh @@ -0,0 +1,74 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/RandomFragmentLigand.hh +/// @brief Randomly frament a ligand to make it smaller +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_RandomFragmentLigand_hh +#define INCLUDED_protocols_drug_design_RandomFragmentLigand_hh + +#include +#include +#include + +#include + +#include + +#include + +namespace protocols { +namespace drug_design { + +class RandomFragmentLigand : public protocols::chemistries::Chemistry { +public: + RandomFragmentLigand(); + + void keep_bigger( bool keep_bigger ) { keep_bigger_ = keep_bigger; } + void keep_atom( std::string const & keep_atom ); + void ccbond( bool setting ) { ccbond_ = setting; } + + bool keep_bigger() const { return keep_bigger_; } + std::string keep_atom() const { return keep_atom_; } + bool ccbond() const { return ccbond_; } + + void apply( core::chemical::MutableResidueType & ) override; + + /// @brief Initialize any data members of this instance from an input tag + /// and a DataMap object + void parse_my_tag( + utility::tag::TagCOP, + basic::datacache::DataMap & + ) override; + + core::chemical::VDVDMapping + get_mapping() const override; + + static std::string class_name(); + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + +private: + + /// @brief Keep the fragment which is bigger. + bool keep_bigger_; + + /// @brief Keep the fragment which contains the given atom. + std::string keep_atom_; + + /// @brief Only break C-C bonds + bool ccbond_; + + core::chemical::VDVDMapping mapping_; +}; + +} // namespace drug_design +} // namespace protocols + +#endif diff --git a/source/src/protocols/drug_design/RandomFragmentLigandCreator.hh b/source/src/protocols/drug_design/RandomFragmentLigandCreator.hh new file mode 100644 index 0000000000..82f33157f1 --- /dev/null +++ b/source/src/protocols/drug_design/RandomFragmentLigandCreator.hh @@ -0,0 +1,43 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/RandomFragmentLigandCreator.hh +/// @brief Class for instantiating a RandomFragmentLigand +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_RandomFragmentLigandCreator_HH +#define INCLUDED_protocols_drug_design_RandomFragmentLigandCreator_HH + +// Package headers +#include +#include + +#include + +// Utility headers + +// C++ headers +#include + +namespace protocols { +namespace drug_design { + +class RandomFragmentLigandCreator : public protocols::chemistries::ChemistryCreator { +public: + protocols::chemistries::ChemistryOP create_chemistry() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & ) const override; +}; + + +} //namespace drug_design +} //namespace protocols + + +#endif diff --git a/source/src/protocols/drug_design/ReactionChemistry.cc b/source/src/protocols/drug_design/ReactionChemistry.cc new file mode 100644 index 0000000000..dc8c300360 --- /dev/null +++ b/source/src/protocols/drug_design/ReactionChemistry.cc @@ -0,0 +1,249 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/ReactionChemistry.hh +/// @brief apply RDKit's reaction-based fragment addition to a ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +// For 3D conformation building +#include +#include +#include +// For substructure mapping +#include +#include // For MolToSmiles +#include // For MolToMolBlock + + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR("protocols.drug_design.ReactionChemistry"); + +ReactionChemistry::ReactionChemistry( std::string const & type ) : + Chemistry(type) +{} + +/// @brief The file which contains the reactions which to use. +void +ReactionChemistry::reaction_file( std::string filename, bool append /*=false*/ ) { + + if ( ! append ) { + reactions_.clear(); + } + + if ( filename.size() == 0 ) { + utility_exit_with_message("Cannot open empty reaction file in protocols::drug_design::ReactionChemistry."); + } + + // Use a local reaction file if present, else use the one in the database + std::string actual_filename( filename ); + utility::io::izstream data( actual_filename ); + if ( ! data.good() ) { + actual_filename = basic::database::full_name("protocol_data/drug_design/"+filename); + utility::io::izstream data( actual_filename ); + if ( ! data.good() ) { + TR.Error << "Can't find reaction file, either (./)" << filename << " or " << actual_filename << std::endl; + utility_exit_with_message("ERROR: cannot find reaction file."); + } + } + + if ( utility::endswith(actual_filename, ".sdf" ) ) { + // This should be possible, but I don't know how yet. + TR.Error << "Cannot currently read reactions in sdf format." << std::endl; + utility_exit_with_message("Error trying to read an sdf-format reaction file."); + } + // SMARTS format file + std::string line; + while ( getline( data, line ) ) { + utility::vector1< std::string > parts( utility::split_whitespace(line) ); + if ( parts.size() == 0 ) { continue; } + if ( parts[1].size() < 1 || parts[1][0] == '#' ) { continue; } + core::Real weight( 1.0 ); + if ( parts.size() > 1 && parts[2].size() >=1 && parts[2][0] != '#' ) { + weight = utility::string2Size( parts[2] ); + } + TR.Debug << "Parsing Reaction '" << parts[1] << "' " << std::endl; + ::RDKit::ChemicalReactionOP rxn( ::RDKit::RxnSmartsToChemicalReaction( parts[1] ) ); + if ( ! rxn ) { + TR.Error << "Cannot parse as chemical reaction: " << parts[1] << std::endl; + continue; + } + rxn->initReactantMatchers(); + TR.Debug << "Reaction as parsed: " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + add_reaction( rxn, weight ); + } + + if ( reactions_.size() == 0 ) { + TR.Warning << "No reactions found in reaction file " << actual_filename << std::endl; + } +} + +/// @brief Default reaction addition - just add as is; +void +ReactionChemistry::add_reaction( ::RDKit::ChemicalReactionOP rxn, core::Real weight) { + if ( rxn->getNumProductTemplates() < 1 ) { + TR.Error << "Reaction does not have listed products: " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + return; + } + if ( rxn->getNumReactantTemplates() < 1 ) { + TR.Error << "Reaction does not have listed reactants: " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + return; + } + std::pair< ::RDKit::ChemicalReactionOP, core::Real > rxn_pair( rxn, weight ); + reactions_.push_back( rxn_pair ); +} + +void +ReactionChemistry::prefilter_reactions( utility::vector1< ::RDKit::ROMolOP > const & reactants, bool exclude_first ) { + if ( reactions_.empty() ) { + utility_exit_with_message("No reactions found when trying to pre-filter!"); + } + utility::vector1< std::pair< ::RDKit::ChemicalReactionOP, core::Real > > filtered_reactions; + + for ( core::Size rr(1); rr <= reactions_.size(); ++rr ) { + bool valid = true; + ::RDKit::ChemicalReactionOP rxn( reactions_[rr].first ); + for ( ::RDKit::MOL_SPTR_VECT::const_iterator itr( rxn->beginReactantTemplates() ), + itr_end( rxn->endReactantTemplates() ); itr != itr_end; ++itr ) { + if ( exclude_first && itr == rxn->beginReactantTemplates() ) { continue; } + bool reactant_found = false; + for ( core::Size ff(1); ff <= reactants.size(); ++ff ) { + ::RDKit::MatchVectType tvect; + if ( SubstructMatch(*reactants[ff],**itr,tvect) ) { + reactant_found = true; + break; + } + } + if ( !reactant_found ) { + valid = false; + break; + } + } + if ( valid ) { + filtered_reactions.push_back( reactions_[rr] ); + } + } + + TR << "Prefiltered reactions from " << reactions_.size() << " to " << filtered_reactions.size() << " that are compatible with the reactants." << std::endl; + swap( reactions_, filtered_reactions ); // Replace reactions_, and discard the old one. + if ( reactions_.empty() ) { + utility_exit_with_message("After filtering, no usable reactions remain!"); + } +} + +/// @brief Filter the reactions to just the ones which can be applied to rdmol +void +ReactionChemistry::filter_reactions( + ::RDKit::ROMol const & rdmol, + utility::vector1< ::RDKit::ChemicalReactionOP > & rxns, + numeric::random::WeightedSampler & rxn_sampler ) const +{ + ::RDKit::MatchVectType tvect; + for ( core::Size rr(1); rr <= reactions_.size(); ++rr ) { + ::RDKit::ROMOL_SPTR first_reactant( *reactions_[rr].first->beginReactantTemplates() ); + if ( ::RDKit::SubstructMatch(rdmol,*first_reactant,tvect) ) { + rxns.push_back( reactions_[rr].first ); + rxn_sampler.add_weight( reactions_[rr].second ); + } + } +} + +/// @brief attempt to clean up the product of an RDKit reaction +/// Modifies the RWMol in place - if modification unsuccessful, it will return true. +bool +ReactionChemistry::cleanup_product( ::RDKit::RWMol & prod ) const { + // Clean up product molecule + try { + ::RDKit::MolOps::sanitizeMol(prod); + } catch (::RDKit::MolSanitizeException &se){ + TR.Error << "Cannot Sanitize product with RDKit: " << se.what() << std::endl; + if ( TR.Debug.visible() ) { + TR.Debug << "\n" << ::RDKit::MolToMolBlock( prod, /*includeStero*/ true, /*confId*/ -1, /*kekulize*/ false ) << std::endl; + } + return true; + } + + // The geometry of the post-reaction compounds is typically bad (especially if we added fragments) + // Re-embed the molecule to fix the conformation + ::RDKit::MolOps::addHs(prod,/*explicitOnly=*/false,/*addCoords=*/false); // Embedding (& min) works better if we have hydrogens. + + // This long call is needed such that we can put "useExpTorsionAnglePrefs=true" and "useBasicKnowledge=true" so embedding doesn't mess up rings, etc. + // We also use a constant seed for embedding, to make the generated conformations consistent. + int conf_num = ::RDKit::DGeomHelpers::EmbedMolecule(prod, /*maxIterations*/ 0, /*seed*/ 111111, /*clearConfs*/ true, + /*useRandomCoords*/ false, /*boxSizeMult*/ 2.0, /*randNegEig*/ true, /*numZeroFail*/ 1, /*coordMap*/ nullptr, /*optimizerForceTol*/ 1e-3, + /*ignoreSmoothingFailures*/ false, /*enforceChirality*/ true, /*useExpTorsionAnglePrefs*/ true, /*useBasicKnowledge*/ true); + if ( conf_num == -1 ) { + ::RDKit::MolOps::removeHs(prod,false,true); + TR.Warning << "Could not find 3D coordinates for reaction product: " << ::RDKit::MolToSmiles( prod ) << std::endl; + if ( TR.Debug.visible() ) { + TR.Debug << "\n" << ::RDKit::MolToMolBlock( prod, /*includeStero*/ true, /*confId*/ -1, /*kekulize*/ false ) << std::endl; + } + return true; + } + + ::RDKit::ForceFieldOP ff( core::chemical::rdkit::get_forcefield(prod, conf_num) ); + if ( ! ff ) { + ::RDKit::MolOps::removeHs(prod,false,true); + return true; // More detailed error messages should be from the get_forcefield() function + } + + TR.Debug << "Starting Energy: " << ff->calcEnergy() << std::endl; + if ( ff->minimize(200) == 1 ) { + TR.Debug << "Try with looser tolerances" << std::endl; + TR.Debug << "Energy: " << ff->calcEnergy() << std::endl; + // Try with looser tolerances + if ( ff->minimize(200, 1e-2, 1e-4) == 1 ) { + TR.Debug << "Okay, really loose tolerances" << std::endl; + TR.Debug << "Energy: " << ff->calcEnergy() << std::endl; + if ( ff->minimize(200, 1, 1) == 1 ) { + TR.Warning << "In reaction cleanup, minimization did not converge." << std::endl; + TR.Debug << "Energy: " << ff->calcEnergy() << std::endl; + TR << "PostMin: \n" << ::RDKit::MolToMolBlock( prod ) << std::endl; + ::RDKit::MolOps::removeHs(prod); + TR << "Product: " << ::RDKit::MolToSmiles( prod ) << std::endl; + return true; + } + } + } + TR.Debug << "Final Energy: " << ff->calcEnergy() << std::endl; + + ::RDKit::MolOps::removeHs(prod,false,true); // Renormalize the molecule into the "explicit" hydrogen form. + return false; +} + +utility::vector1< std::pair< ::RDKit::ChemicalReactionOP, core::Real > > const & +ReactionChemistry::get_reactions() const { + return reactions_; +} + + +} +} diff --git a/source/src/protocols/drug_design/ReactionChemistry.hh b/source/src/protocols/drug_design/ReactionChemistry.hh new file mode 100644 index 0000000000..ae6b8a8325 --- /dev/null +++ b/source/src/protocols/drug_design/ReactionChemistry.hh @@ -0,0 +1,98 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/ReactionChemistry.hh +/// @brief An abstract base class for Chemistries which use RDKit reactions +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_ReactionChemistry_hh +#define INCLUDED_protocols_drug_design_ReactionChemistry_hh + +#include + +#include +#include + +#include + +#include + +#include + +#include + +#ifdef PYROSETTA +#include // Needed to properly wrap vector1< ChemicalReactionOP > +#endif + +namespace protocols { +namespace drug_design { + +class ReactionChemistry : public protocols::chemistries::Chemistry { + +private: + ReactionChemistry(); // Have to set the type string + +public: + ReactionChemistry( std::string const & type ); + + void apply( core::chemical::MutableResidueType & ) override = 0; + + /// @brief The file which contains the reactions which to use. + virtual void + reaction_file( std::string filename, bool append=false ); + + /// @brief Add a reaction to the list of reactions known by this Chemistry. + virtual void + add_reaction( ::RDKit::ChemicalReactionOP rxn, core::Real weight); + + /// @brief Filter reaction list for those compatible with the given reactants + /// @details Discarded reactions will be discarded permanently. + /// Only call with the finalized fragment list. + void + prefilter_reactions( utility::vector1< ::RDKit::ROMolOP > const & reactants, bool exclude_first = true); + + /// @brief Initialize any data members of this instance from an input tag + /// and a DataMap object + void parse_my_tag( + utility::tag::TagCOP tag, + basic::datacache::DataMap & datacache + ) override = 0; + + core::chemical::VDVDMapping + get_mapping() const override = 0; + +protected: // methods + + /// @brief Filter the reactions to just the ones which can be applied to rdmol + void + filter_reactions( + ::RDKit::ROMol const & rdmol, + utility::vector1< ::RDKit::ChemicalReactionOP > & rxns, // Return by reference. + numeric::random::WeightedSampler & rxn_sampler ) const; + + /// @brief attempt to clean up the product of an RDKit reaction + /// Modifies the RWMol in place - if modification unsuccessful, it will return true. + bool + cleanup_product( ::RDKit::RWMol & prod ) const; + + utility::vector1< std::pair< ::RDKit::ChemicalReactionOP, core::Real > > const & + get_reactions() const; + +private: // data + + /// @brief The chemical reactions to use + utility::vector1< std::pair< ::RDKit::ChemicalReactionOP, core::Real > > reactions_; + +}; + +} // namespace drug_design +} // namespace protocols + +#endif diff --git a/source/src/protocols/drug_design/ReactionFragment.cc b/source/src/protocols/drug_design/ReactionFragment.cc new file mode 100644 index 0000000000..0a25c7e8d3 --- /dev/null +++ b/source/src/protocols/drug_design/ReactionFragment.cc @@ -0,0 +1,362 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/ReactionFragment.hh +/// @brief apply RDKit's reaction-based fragment addition to a ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include // For MolToSmiles +#include // For MolToSmarts + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR("protocols.drug_design.ReactionFragment"); + +//------------------------- Creator ----------------------------- + +protocols::chemistries::ChemistryOP +ReactionFragmentCreator::create_chemistry() const { + return protocols::chemistries::ChemistryOP( new ReactionFragment ); +} + +std::string +ReactionFragmentCreator::keyname() const { + return ReactionFragment::class_name(); +} + +void +ReactionFragmentCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const { + ReactionFragment::provide_xml_schema( xsd ); +} + + +//------------------------- Chemistry ----------------------------- + +ReactionFragment::ReactionFragment(): + ReactionChemistry(class_name()), + keep_bigger_( false ), + keep_random_( false ) +{} + +void +ReactionFragment::keep_atom( std::string const & keep_atom ) { + keep_atom_ = keep_atom; + utility::strip_whitespace( keep_atom_ ); +} + +// Overloaded function which reverses the reaction +void +ReactionFragment::add_reaction( ::RDKit::ChemicalReactionOP rxn, core::Real weight ) { + if ( rxn->getNumProductTemplates() != 1 ) { + TR.Error << "Input reaction should have one and only one listed product: " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + return; + } + if ( rxn->getNumReactantTemplates() < 1 ) { + TR.Error << "Input reaction does not have listed reactants: " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + return; + } + + TR.Debug << "Reaction before reversing: " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + ::RDKit::ChemicalReactionOP rev_rxn( new ::RDKit::ChemicalReaction ); + rev_rxn->addReactantTemplate( *rxn->beginProductTemplates() ); + for ( ::RDKit::MOL_SPTR_VECT::const_iterator itr( rxn->beginReactantTemplates()), + itr_end(rxn->endReactantTemplates()); itr != itr_end; ++itr ) { + rev_rxn->addProductTemplate( *itr ); + } + rev_rxn->initReactantMatchers(); + + TR.Debug << "Reaction after reversing: " << ::RDKit::ChemicalReactionToRxnSmarts( *rev_rxn ) << std::endl; + ReactionChemistry::add_reaction( rev_rxn, weight ); + +} + +/// @brief Check if the produced products are valid given the reaction. +/// (The ability to run the reaction comes from matching the *reacting* templates +/// the product templates may have additional restrictions which could invalidate a product.) +bool +invalid_products( ::RDKit::MOL_SPTR_VECT const & products, ::RDKit::ChemicalReactionCOP rxn ) { + if ( products.size() == 0 ) { + TR.Debug << "Skipping reaction because it doesn't produce any products! " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + return true; + } + + ::RDKit::MOL_SPTR_VECT::const_iterator templ_itr( rxn->beginProductTemplates() ), templ_itr_end( rxn->endProductTemplates() ); + ::RDKit::MOL_SPTR_VECT::const_iterator prod_itr( products.begin() ), prod_itr_end( products.end() ); + for ( /*none*/ ; templ_itr != templ_itr_end && prod_itr != prod_itr_end; ++templ_itr, ++prod_itr ) { + // We need to tidy the products a bit before we can use them in a query. + ::RDKit::RWMOL_SPTR cleaned( new ::RDKit::RWMol(**prod_itr) ); + try { + ::RDKit::MolOps::sanitizeMol(*cleaned); + } catch (::RDKit::MolSanitizeException &se){ + TR.Debug << "Cannot Sanitize fragment with RDKit: " << se.what() << std::endl; + return true; + } + + ::RDKit::MatchVectType tvect; + if ( ! SubstructMatch(*cleaned,**templ_itr,tvect) ) { + // Not really an error - the reactions are generally going to be more lax in the reverse direction + TR << "When fragmenting, fragment: " << ::RDKit::MolToSmiles( *cleaned ) << " doesn't match " << ::RDKit::MolToSmarts( **templ_itr ) << std::endl; + TR << " For reaction: " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + TR << " -- Ignoring potential fragmentation." << std::endl; + return true; + } + } + TR.Debug << "Products are valid!" << std::endl; + return false; +} + +void +ReactionFragment::apply( core::chemical::MutableResidueType & rsdtype ) +{ + + if ( keep_bigger_ && keep_random_ ) { + utility_exit_with_message("Cannot set both keep_bigger and keep_random for ReactionFragment."); + } + if ( keep_bigger_ && keep_atom_ != "" ) { + utility_exit_with_message("Cannot set both keep_bigger and keep_atom for ReactionFragment."); + } + if ( keep_random_ && keep_atom_ != "" ) { + utility_exit_with_message("Cannot set both keep_random and keep_atom for ReactionFragment."); + } + + core::chemical::rdkit::RestypeToRDMol to_converter(rsdtype); + ::RDKit::RWMOL_SPTR rdmol( to_converter.Mol() ); // Convert + core::chemical::rdkit::label_with_index(*rdmol, "Original_Index"); // (Re)Label with "Original_Index" to use quick mapping behavior. + // Properties carry through the reaction, except for a "bug" with the explicitly mentioned atoms. + + TR.Debug << "On molecule: " << ::RDKit::MolToSmiles( *rdmol ) << std::endl; + + // Select Reactions + utility::vector1< ::RDKit::ChemicalReactionOP > rxns; + numeric::random::WeightedSampler rxn_sampler; + filter_reactions(*rdmol, rxns, rxn_sampler); + + if ( rxns.size() == 0 ) { + TR.Warning << "No suitable reactions found. Doing nothing." << std::endl; + mapping_.clear(); + mapping_.identity( true ); + set_last_status( core::chemical::modifications::FAIL_DO_NOT_RETRY ); + return; + } + + // Keep trying until we get a reaction which works + // but don't get into an infinite loop + core::Size iterations_to_try( rxns.size()*10 ); + for ( core::Size cc(1); rxn_sampler.update_cumulative_distribution() && cc <= iterations_to_try; ++cc ) { + + // Pick a random reaction + core::Size rxn_num( rxn_sampler.random_sample() ); + ::RDKit::ChemicalReactionOP rxn( rxns[rxn_num] ); + + TR.Debug << " trying fragmenting with reaction: " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + + // Run the reaction + ::RDKit::MOL_SPTR_VECT reactants; + reactants.push_back( rdmol ); + std::vector< ::RDKit::MOL_SPTR_VECT > all_products( rxn->runReactants( reactants ) ); + if ( all_products.empty() ) { + // This shouldn't happen, because we should have pre-checked for it, but ... + utility_exit_with_message("Error in pre-checking reaction: " + ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) ); + } + + // Filter out the reactions which don't give valid products + // (Since we reversed the reaction, they might be more specific than RDKit would generate.) + // use the erase-remove idiom + all_products.erase( + std::remove_if( all_products.begin(), all_products.end(), + std::bind( invalid_products, std::placeholders::_1, rxn ) ), + all_products.end() ); + + if ( all_products.empty() ) { + // No proper products - remove this reaction from consideration and move on to the next. + TR.Debug << " => Reaction produced only products that didn't match the reactant conditions." << std::endl; + rxn_sampler.set_weight(rxn_num, 0.0 ); + continue; + } + // Pick a random product set + core::Size prod_num( numeric::random::random_range(0,all_products.size()-1) ); + ::RDKit::MOL_SPTR_VECT const & products( all_products[ prod_num ] ); + + if ( products.size() == 0 ) { + TR.Warning << "Skipping reaction because it doesn't produce any products! " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + // Ignore this reaction -- the arity of products should be the same for all variants + rxn_sampler.set_weight(rxn_num, 0.0 ); + continue; + } + + // Pick which product to use + core::Size selected_product( -1 ); + if ( keep_atom_.size() ) { + if ( ! rsdtype.has( keep_atom_ ) ) { + utility_exit_with_message("Cannot find atom '" + keep_atom_ + "' in residue " + rsdtype.name() ); + } + core::Size reactant_atom_index( to_converter.vd_to_index()[ rsdtype.atom_vertex( keep_atom_ ) ] ); + // Pick the fragment with a given atom + for ( core::Size ii(0); ii < products.size(); ++ii ) { + // ?? Do we need to tidy the product before finding a mapping? + // Hopefully, the "Original_Index" property carries through, and can be used as a quick shortcut. + core::chemical::IndexIndexMapping rxn_map( core::chemical::rdkit::find_mapping( rdmol, products[ii], "Original_Index") ); // -1 is invalid + core::Size keep_index = rxn_map[ reactant_atom_index ]; + if ( keep_index != core::Size( -1 ) ) { + selected_product = ii; + break; + } + } + TR.Debug << "Keeping product " << selected_product << " with atom " << keep_atom_ << std::endl; + } else if ( keep_bigger_ ) { + core::Size max_size(0); + for ( core::Size ii(0); ii < products.size(); ++ii ) { + if ( products[ii]->getNumHeavyAtoms() > max_size ) { + max_size = products[ii]->getNumHeavyAtoms(); + selected_product = ii; + } + } + TR.Debug << "Keeping biggest product " << selected_product << " of size " << max_size << std::endl; + } else if ( keep_random_ ) { + // Pick a random product + selected_product = numeric::random::random_range(0,products.size()); + TR.Debug << "Keeping random product " << selected_product << std::endl; + } else { + // Pick the first product + // This makes a nice symmetry with ReactionGrow, which goes the other direction and assumes you start with the first + selected_product = 0; + } + + if ( selected_product == core::Size(-1) ) { + TR << "Unable to find the desired product! Using the first one instead." << std::endl; + selected_product = 0; + } + + ::RDKit::RWMolOP prod( new ::RDKit::RWMol( *products[ selected_product ] ) ); + + if ( cleanup_product( *prod ) ) { + TR.Debug << " => Reaction product failed in cleanup." << std::endl; + continue; // Failed at cleanup - try the reaction again. + } + + // We need to find a mapping from the pre-reaction molecule to the post-reaction molecule + // Hopefully, the "Original_Index" property carries through, and can be used as a quick shortcut. + core::chemical::IndexIndexMapping rxn_map( core::chemical::rdkit::find_mapping( rdmol, prod, "Original_Index") ); // -1 is invalid + + if ( rxn_map.empty() ) { + TR.Warning << "Cannot find substructure mapping of fragment to parent." << std::endl; + TR.Debug << "Fragment: " << ::RDKit::MolToSmiles( *prod ) << std::endl; + TR.Debug << "Parent: " << ::RDKit::MolToSmiles( *rdmol ) << std::endl; + TR.Debug << "Reaction: " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + continue; + } + + TR.Debug << "Selected fragmentation product: " << ::RDKit::MolToSmiles( *prod ) << std::endl; + + // Should be good. Now convert the residue into a Rosetta residue type. + core::chemical::VDIndexMapping restype_prod_map( combine( to_converter.vd_to_index(), rxn_map ) ); + + core::chemical::rdkit::RDMolToRestype from_converter(*prod); + from_converter.set_nbr( restype_prod_map[rsdtype.nbr_vertex()] ); + + core::chemical::MutableResidueTypeOP new_resop( from_converter.generate_restype(rsdtype,restype_prod_map) ); + + TR << "Removed " << rsdtype.nheavyatoms() - new_resop->nheavyatoms() << " heavyatoms from " << rsdtype.name() << std::endl; + + mapping_ = combine( restype_prod_map, from_converter.index_to_vd() ); + rsdtype = *new_resop; + mapping_ = combine( mapping_, combine( core::chemical::VDStringMapping(*new_resop), core::chemical::StringVDMapping(rsdtype)) ); + + // That's it, we're good + set_last_status( core::chemical::modifications::SUCCESS ); + return; + } + + TR.Warning << "Tried to run the fragmenting reactions " << iterations_to_try << " times, but none of them worked. Giving up." << std::endl; + set_last_status( core::chemical::modifications::FAIL_RETRY ); + +} + +core::chemical::VDVDMapping +ReactionFragment::get_mapping() const { + return mapping_; +} + +std::string +ReactionFragment::class_name() { + return "ReactionFragment"; +} + +void +ReactionFragment::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) { + using namespace utility::tag; + AttributeList attlist; + attlist + + XMLSchemaAttribute::required_attribute("reactions", xs_string, + "The name of the file containing the SMARTS-based reactions to use, written in the synthetic direction.") + + XMLSchemaAttribute::attribute_w_default("keep_random", xsct_rosetta_bool, + "If true, keep a random reaction product (instead of the first).", "0") + + XMLSchemaAttribute::attribute_w_default("keep_bigger", xsct_rosetta_bool, + "If true, keep the biggest reaction product", "0") + + XMLSchemaAttribute("keep_atom", xs_string, + "If set, keep the reaction product with the given object"); + + protocols::chemistries::xsd_type_definition_w_attributes( + xsd, class_name(), + "Split a molecule in two based on a list of reactions, keeping on one portion.", + attlist ); +} + +/// @brief Initialize any data members of this instance from an input tag +/// and a DataMap object +void +ReactionFragment::parse_my_tag( + utility::tag::TagCOP tag, + basic::datacache::DataMap &) { + + reaction_file( tag->getOption("reactions") ); + + keep_bigger( tag->getOption("keep_bigger", false) ); + keep_random( tag->getOption("keep_random", false) ); + keep_atom( tag->getOption("keep_atom", "") ); + + if ( keep_bigger_ && keep_random_ ) { + throw CREATE_EXCEPTION(utility::excn::RosettaScriptsOptionError, "Cannot set both keep_bigger and keep_random for RandomFragmentLigand."); + } + if ( keep_bigger_ && keep_atom_ != "" ) { + throw CREATE_EXCEPTION(utility::excn::RosettaScriptsOptionError, "Cannnot set both keep_bigger and keep_atom for RandomFragmentLigand."); + } + if ( keep_random_ && keep_atom_ != "" ) { + throw CREATE_EXCEPTION(utility::excn::RosettaScriptsOptionError, "Cannot set both keep_random and keep_atom for RandomFragmentLigand."); + } + +} + + +} +} diff --git a/source/src/protocols/drug_design/ReactionFragment.fwd.hh b/source/src/protocols/drug_design/ReactionFragment.fwd.hh new file mode 100644 index 0000000000..9546b02ee0 --- /dev/null +++ b/source/src/protocols/drug_design/ReactionFragment.fwd.hh @@ -0,0 +1,33 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/ReactionFragment.hh +/// @brief apply RDKit's reaction facilities to split a ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_ReactionFragment_fwd_hh +#define INCLUDED_protocols_drug_design_ReactionFragment_fwd_hh + +#include + +namespace protocols { +namespace drug_design { + + +class ReactionFragment; // fwd declaration +typedef utility::pointer::shared_ptr< ReactionFragment > ReactionFragmentOP; +typedef utility::pointer::shared_ptr< ReactionFragment const > ReactionFragmentCOP; + + +} // namespace drug_design +} // namespace protocols + +#endif // INCLUDED_protocols_drug_design_ReactionFragment_fwd_hh + + diff --git a/source/src/protocols/drug_design/ReactionFragment.hh b/source/src/protocols/drug_design/ReactionFragment.hh new file mode 100644 index 0000000000..24d3cfbc8d --- /dev/null +++ b/source/src/protocols/drug_design/ReactionFragment.hh @@ -0,0 +1,83 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/ReactionFragment.hh +/// @brief apply RDKit's reaction mechanism to split a ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_ReactionFragment_hh +#define INCLUDED_protocols_drug_design_ReactionFragment_hh + +#include +#include + +#include + +#include + +#include + +#include + +#include + +namespace protocols { +namespace drug_design { + +class ReactionFragment : public ReactionChemistry { +public: + ReactionFragment(); + + void keep_bigger( bool setting ) { keep_bigger_ = setting; } + void keep_random( bool setting ) { keep_random_ = setting; } + void keep_atom( std::string const & keep_atom ); + + bool keep_bigger() const { return keep_bigger_; } + bool keep_random() const { return keep_random_; } + std::string keep_atom() const { return keep_atom_; } + + void apply( core::chemical::MutableResidueType & ) override; + + /// @brief Initialize any data members of this instance from an input tag + /// and a DataMap object + void parse_my_tag( + utility::tag::TagCOP tag, + basic::datacache::DataMap & datacache + ) override; + + core::chemical::VDVDMapping + get_mapping() const override; + + /// @brief Add a reaction to the list of reactions to use. + /// Reaction should be written in the synthetic direction with a single product + void + add_reaction( ::RDKit::ChemicalReactionOP rxn, core::Real weight ) override; + + static std::string class_name(); + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + +private: + + /// @brief Keep the fragment which is bigger. + bool keep_bigger_; + + /// @brief Keep a random fragment. + bool keep_random_; + + /// @brief Keep the fragment which contains the given atom. + std::string keep_atom_; + + core::chemical::VDVDMapping mapping_; + +}; + +} // namespace drug_design +} // namespace protocols + +#endif diff --git a/source/src/protocols/drug_design/ReactionFragmentCreator.hh b/source/src/protocols/drug_design/ReactionFragmentCreator.hh new file mode 100644 index 0000000000..9e05cfb79f --- /dev/null +++ b/source/src/protocols/drug_design/ReactionFragmentCreator.hh @@ -0,0 +1,42 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/ReactionFragmentCreator.hh +/// @brief Class for instantiating a ReactionFragment +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_ReactionFragmentCreator_HH +#define INCLUDED_protocols_drug_design_ReactionFragmentCreator_HH + +// Package headers +#include +#include + +// Utility headers +#include + +// C++ headers +#include + +namespace protocols { +namespace drug_design { + +class ReactionFragmentCreator : public protocols::chemistries::ChemistryCreator { +public: + protocols::chemistries::ChemistryOP create_chemistry() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & ) const override; +}; + + +} //namespace drug_design +} //namespace protocols + + +#endif diff --git a/source/src/protocols/drug_design/ReactionGrow.cc b/source/src/protocols/drug_design/ReactionGrow.cc new file mode 100644 index 0000000000..6e92980114 --- /dev/null +++ b/source/src/protocols/drug_design/ReactionGrow.cc @@ -0,0 +1,334 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/ReactionGrow.hh +/// @brief apply RDKit's reaction-based fragment addition to a ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include // For MolToSmiles +#include // For MolToSmarts + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR("protocols.drug_design.ReactionGrow"); + +//------------------------- Creator ----------------------------- + +protocols::chemistries::ChemistryOP +ReactionGrowCreator::create_chemistry() const { + return protocols::chemistries::ChemistryOP( new ReactionGrow ); +} + +std::string +ReactionGrowCreator::keyname() const { + return ReactionGrow::class_name(); +} + +void +ReactionGrowCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const { + ReactionGrow::provide_xml_schema( xsd ); +} + +//------------------------- Chemistry ----------------------------- + +ReactionGrow::ReactionGrow(): + ReactionChemistry(class_name()) +{} + +void +ReactionGrow::add_reaction( ::RDKit::ChemicalReactionOP rxn, core::Real weight ) { + if ( rxn->getNumProductTemplates() != 1 ) { + TR.Error << "Input reaction should have one and only one listed product: " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + return; + } + if ( rxn->getNumReactantTemplates() < 1 ) { + TR.Error << "Input reaction does not have listed reactants: " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + return; + } + + ReactionChemistry::add_reaction( rxn, weight ); +} + +/// @brief The file which contains the fragments to add to input residue type. +void +ReactionGrow::fragment_database( std::string filename, bool append /*=false*/ ) { + if ( ! append ) { + fragments_.clear(); + } + + core::chemical::rdkit::load_sdf( filename, fragments_, /*removeHs=*/ true ); + + if ( fragments_.size() == 0 ) { + TR.Warning << "No molecule fragments found in file " << filename << std::endl; + } +} + +/// @brief Reduce the fragment set to those which are compatible with the reactions. +void +ReactionGrow::prefilter_fragments() { + + utility::vector1< ::RDKit::ROMolOP > filtered_fragments; + + utility::vector1< std::pair< ::RDKit::ChemicalReactionOP, core::Real > > const & reactions( get_reactions() ); + + for ( core::Size ff(1); ff <= fragments_.size(); ++ff ) { + bool found = false; + for ( core::Size rr(1); rr <= reactions.size(); ++rr ) { + ::RDKit::ChemicalReactionOP rxn( reactions[rr].first ); + for ( ::RDKit::MOL_SPTR_VECT::const_iterator itr( ++(rxn->beginReactantTemplates()) ), // Skip the first reactant + itr_end( rxn->endReactantTemplates() ); itr != itr_end; ++itr ) { + ::RDKit::MatchVectType tvect; + if ( SubstructMatch(*fragments_[ff],**itr,tvect) ) { + filtered_fragments.push_back( fragments_[ff] ); + found = true; + break; + } + } + if ( found ) { break; } + } + } + + TR << "Prefiltered fragments from " << fragments_.size() << " to " << filtered_fragments.size() << " that are compatible with the reactions." << std::endl; + swap( fragments_, filtered_fragments ); // Replace fragments_, and discard the old one. + if ( fragments_.empty() ) { + // Fragment set may be empty, if someone is using a reaction set which is 1->1 + TR.Warning << "After filtering, no fragments remain!" << std::endl; + } +} + +void +ReactionGrow::apply( core::chemical::MutableResidueType & rsdtype ) +{ + // Fragment set may be empty, if someone is using a reaction set which is 1->1 + + core::chemical::rdkit::RestypeToRDMol to_converter(rsdtype); // Neutralize and remove hydrogens. + ::RDKit::RWMOL_SPTR rdmol( to_converter.Mol() ); // Convert + core::chemical::rdkit::label_with_index(*rdmol, "Original_Index"); // (Re)Label with "Original_Index" to use quick mapping behavior. + // Properties carry through the reaction, except for a "bug" with the explicitly mentioned atoms. + + // Select Reactions + utility::vector1< ::RDKit::ChemicalReactionOP > rxns; + numeric::random::WeightedSampler rxn_sampler; + filter_reactions(*rdmol, rxns, rxn_sampler); + + TR.Debug << "Found " << rxns.size() << " reactions compatible with current molecule." << std::endl; + + if ( rxns.size() == 0 ) { + TR.Warning << "No suitable reactions found. Doing nothing." << std::endl; + mapping_.clear(); + mapping_.identity( true ); + set_last_status( core::chemical::modifications::FAIL_DO_NOT_RETRY ); + return; + } + + // Keep trying until we get a reaction which works + // but don't get into an infinite loop + for ( core::Size cc(1); cc <= rxns.size()*10; ++cc ) { + + // Pick a random reaction + core::Size rxn_num( rxn_sampler.random_sample() ); + ::RDKit::ChemicalReactionOP rxn( rxns[rxn_num] ); + + // Pick fragments which work as reactants + ::RDKit::MOL_SPTR_VECT reactants; + for ( ::RDKit::MOL_SPTR_VECT::const_iterator itr( rxn->beginReactantTemplates() ), + itr_end( rxn->endReactantTemplates() ); + itr != itr_end; ++itr ) { + if ( reactants.size() == 0 ) { + // First reactant is the molecule we're building. + reactants.push_back( rdmol ); + continue; + } else { + // Fill out the rest of the reactions with fragments. + utility::vector1< ::RDKit::ROMolOP > fragments; + numeric::random::WeightedSampler fragment_sampler; + for ( core::Size ff(1); ff <= fragments_.size(); ++ff ) { + ::RDKit::MatchVectType tvect; + if ( SubstructMatch(*fragments_[ff],**itr,tvect) ) { + core::Real weight( 1.0 ); + if ( property_name_.size() && fragments_[ff]->hasProp(property_name_) ) { + weight = fragments_[ff]->getProp(property_name_); + } + fragments.push_back( fragments_[ff] ); + fragment_sampler.add_weight( weight ); + } else if ( TR.Trace.visible() ) { + TR.Trace << "Fragment " << ::RDKit::MolToSmiles( *fragments_[ff] ) << " doesn't match " << ::RDKit::MolToSmarts( **itr ) << std::endl; + } + } + TR.Debug << "From " << fragments_.size() << " total fragments, " << fragments.size() << " are compatible with reactant# " << reactants.size()+1 << std::endl; + if ( fragments.size() == 0 ) { + TR.Warning << "Reaction cannot be fufilled with current fragment set." << std::endl; + TR.Warning << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << " :: reactant #" << reactants.size()+1 << std::endl; + break; + } + // Pick one of the applicable fragments to use. + core::Size frag_num( fragment_sampler.random_sample() ); + reactants.push_back( fragments[ frag_num ] ); + } + } // end pick reactants for loop + + if ( reactants.size() != rxn->getNumReactantTemplates() ) { + // Couldn't find suitable products remove reaction from sampler. + rxn_sampler.set_weight(rxn_num, 0.0 ); + if ( !rxn_sampler.update_cumulative_distribution() ) { + // Removed all the reactions from sampler. + TR.Warning << "No possible reactions with given fragment set. Doing nothing." << std::endl; + mapping_.clear(); + mapping_.identity( true ); + set_last_status( core::chemical::modifications::FAIL_DO_NOT_RETRY ); + return; + } + TR.Debug << "Couldn't find fragments for selected reaction." << std::endl; + continue; + } + + if ( TR.Debug.visible() ) { + TR.Debug << "Choosing the reaction " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + for ( core::Size jj(0); jj < reactants.size(); ++jj ) { + TR.Debug << " Reactant " << jj+1 << ": " << ::RDKit::MolToSmiles( *reactants[jj] ) << std::endl; + } + } + + // Actually run the reaction + std::vector< ::RDKit::MOL_SPTR_VECT > products( rxn->runReactants( reactants ) ); + if ( products.size() == 0 ) { + TR.Debug << "No products found for reaction = retrying." << std::endl; + continue; + } + core::Size prod_num( numeric::random::random_range(0,products.size()-1) ); + if ( products[ prod_num].size() != 1 ) { + TR.Error << "Bad reaction in ReactionGrow! Too many products (" << products[ prod_num].size() << "): " + << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + rxn_sampler.set_weight(rxn_num, 0.0 ); + if ( !rxn_sampler.update_cumulative_distribution () ) { + // Removed all the reactions from sampler. + TR.Warning << "No possible reactions with given fragment set. Doing nothing." << std::endl; + mapping_.clear(); + mapping_.identity( true ); + set_last_status( core::chemical::modifications::FAIL_DO_NOT_RETRY ); + return; + } + continue; + } + + ::RDKit::RWMolOP prod( new ::RDKit::RWMol( *products[ prod_num ][0]) ); + + if ( cleanup_product( *prod ) ) { + TR.Debug << "Failed cleanup." << std::endl; // More extensive debugging information should be from the cleanup + continue; // Failed at cleanup - try the reaction again. + } + + TR.Debug << " Product " << ::RDKit::MolToSmiles( *prod ) << std::endl; + + // We need to find a mapping from the pre-reaction molecule to the post-reaction molecule + // Hopefully, the "Original_Index" property carries through, and can be used as a quick shortcut. + core::chemical::IndexIndexMapping rxn_map( core::chemical::rdkit::find_mapping( rdmol, prod, "Original_Index") ); // -1 is invalid + + if ( rxn_map.empty() ) { + TR.Warning << "Cannot find substructure mapping of reactant to product." << std::endl; + continue; + } + + // Should be good. Now convert the residue into a Rosetta residue type. + + core::chemical::VDIndexMapping restype_prod_map( combine( to_converter.vd_to_index(), rxn_map ) ); + + core::chemical::rdkit::RDMolToRestype from_converter(*prod); + from_converter.set_nbr( restype_prod_map[rsdtype.nbr_vertex()] ); + + core::chemical::MutableResidueTypeOP new_resop( from_converter.generate_restype(rsdtype,restype_prod_map) ); + + TR << "Added " << new_resop->nheavyatoms() - rsdtype.nheavyatoms() << " heavyatoms to " << rsdtype.name() << std::endl; + + mapping_ = combine( restype_prod_map, from_converter.index_to_vd() ); + rsdtype = *new_resop; + mapping_ = combine( mapping_, combine( core::chemical::VDStringMapping(*new_resop), core::chemical::StringVDMapping(rsdtype)) ); + + // That's it, we're successful + set_last_status( core::chemical::modifications::SUCCESS ); + return; + } + + TR.Warning << "Tried " << rxns.size()*10 << " reactions, but none of them worked. Giving up." << std::endl; + set_last_status( core::chemical::modifications::FAIL_RETRY ); + +} + +core::chemical::VDVDMapping +ReactionGrow::get_mapping() const { + return mapping_; +} + +std::string +ReactionGrow::class_name() { + return "ReactionGrow"; +} + +void +ReactionGrow::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) { + using namespace utility::tag; + AttributeList attlist; + attlist + + XMLSchemaAttribute::required_attribute("fragments", xs_string, + "The name of the SDF file which contains the other reactants (fragments to add).") + + XMLSchemaAttribute::attribute_w_default("weight_by_property", xs_string, + "When randomly picking the other reactants, weight by the given property from the SDF", "") + + XMLSchemaAttribute::required_attribute("reactions", xs_string, + "The name of the file containing the SMARTS-based reactions to use."); + + protocols::chemistries::xsd_type_definition_w_attributes( + xsd, class_name(), + "Grow a molecule by adding another molecule to the structure based on a given list of reactions.", + attlist ); +} + +/// @brief Initialize any data members of this instance from an input tag +/// and a DataMap object +void +ReactionGrow::parse_my_tag( + utility::tag::TagCOP tag, + basic::datacache::DataMap &) { + + fragment_database( tag->getOption("fragments") ); + reaction_file( tag->getOption("reactions") ); + weight_by_property( tag->getOption("weight_by_property", "") ); + + // Reduce size of the fragments and reactions, so we avoid picking any that are bad. + prefilter_reactions(); + prefilter_fragments(); +} + + +} +} diff --git a/source/src/protocols/drug_design/ReactionGrow.fwd.hh b/source/src/protocols/drug_design/ReactionGrow.fwd.hh new file mode 100644 index 0000000000..0f026d3dc3 --- /dev/null +++ b/source/src/protocols/drug_design/ReactionGrow.fwd.hh @@ -0,0 +1,33 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/ReactionGrow.hh +/// @brief apply RDKit's reaction based growth scheme to ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_ReactionGrow_fwd_hh +#define INCLUDED_protocols_drug_design_ReactionGrow_fwd_hh + +#include + +namespace protocols { +namespace drug_design { + + +class ReactionGrow; // fwd declaration +typedef utility::pointer::shared_ptr< ReactionGrow > ReactionGrowOP; +typedef utility::pointer::shared_ptr< ReactionGrow const > ReactionGrowCOP; + + +} // namespace drug_design +} // namespace protocols + +#endif // INCLUDED_protocols_drug_design_ReactionGrow_fwd_hh + + diff --git a/source/src/protocols/drug_design/ReactionGrow.hh b/source/src/protocols/drug_design/ReactionGrow.hh new file mode 100644 index 0000000000..28762907a3 --- /dev/null +++ b/source/src/protocols/drug_design/ReactionGrow.hh @@ -0,0 +1,90 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/ReactionGrow.hh +/// @brief apply RDKit's reaction mechanism to add a fragment to a ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_ReactionGrow_hh +#define INCLUDED_protocols_drug_design_ReactionGrow_hh + +#include +#include + +#include + +#include + +#include + +#include + +#include + +namespace protocols { +namespace drug_design { + +class ReactionGrow : public ReactionChemistry { +public: + ReactionGrow(); + + void apply( core::chemical::MutableResidueType & ) override; + + /// @brief The file which contains the fragments to add to input residue type. + void fragment_database( std::string filename, bool append=false ); + + /// @brief Reduce the fragment set to those which are compatible with the reactions. + /// @details Discarded fragments are discarded permanently. + /// Only call after all reactions are finalized for this Chemistry + void prefilter_fragments(); + + /// @brief Filter reaction list for those compatible with the given fragments + /// @details Discarded reactions will be discarded permanently. + /// Only call with the finalized fragment list. + void + prefilter_reactions() { ReactionChemistry::prefilter_reactions( fragments_, true ); } + + /// @brief If not empty, use property weighting based on the given property. + void weight_by_property( std::string const & setting ) { property_name_ = setting; } + std::string const & weight_by_property() const { return property_name_; } + + /// @brief Initialize any data members of this instance from an input tag + /// and a DataMap object + void parse_my_tag( + utility::tag::TagCOP tag, + basic::datacache::DataMap & datacache + ) override; + + core::chemical::VDVDMapping + get_mapping() const override; + + /// @brief Add a reaction to the list of reactions to use. + /// Reaction should be written in the synthetic direction with a single product + void + add_reaction( ::RDKit::ChemicalReactionOP rxn, core::Real weight ) override; + + static std::string class_name(); + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + +private: + + /// @brief The fragments to apply + utility::vector1< ::RDKit::ROMolOP > fragments_; + + /// @brief If not empty, pick fragments based on the weighting by the given property. + std::string property_name_; + + core::chemical::VDVDMapping mapping_; + +}; + +} // namespace drug_design +} // namespace protocols + +#endif diff --git a/source/src/protocols/drug_design/ReactionGrowCreator.hh b/source/src/protocols/drug_design/ReactionGrowCreator.hh new file mode 100644 index 0000000000..19998cd659 --- /dev/null +++ b/source/src/protocols/drug_design/ReactionGrowCreator.hh @@ -0,0 +1,42 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/ReactionGrowCreator.hh +/// @brief Class for instantiating a ReactionGrow +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_ReactionGrowCreator_HH +#define INCLUDED_protocols_drug_design_ReactionGrowCreator_HH + +// Package headers +#include +#include + +// Utility headers +#include + +// C++ headers +#include + +namespace protocols { +namespace drug_design { + +class ReactionGrowCreator : public protocols::chemistries::ChemistryCreator { +public: + protocols::chemistries::ChemistryOP create_chemistry() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & ) const override; +}; + + +} //namespace drug_design +} //namespace protocols + + +#endif diff --git a/source/src/protocols/drug_design/ReactionMultiTransform.cc b/source/src/protocols/drug_design/ReactionMultiTransform.cc new file mode 100644 index 0000000000..4a72d2104c --- /dev/null +++ b/source/src/protocols/drug_design/ReactionMultiTransform.cc @@ -0,0 +1,239 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/ReactionMultiTransform.hh +/// @brief apply RDKit's reaction-based chemistry to transform a ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include // For MolToSmiles + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR("protocols.drug_design.ReactionMultiTransform"); + +//------------------------- Creator ----------------------------- + +protocols::chemistries::ChemistryOP +ReactionMultiTransformCreator::create_chemistry() const { + return protocols::chemistries::ChemistryOP( new ReactionMultiTransform ); +} + +std::string +ReactionMultiTransformCreator::keyname() const { + return ReactionMultiTransform::class_name(); +} + +void +ReactionMultiTransformCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const { + ReactionMultiTransform::provide_xml_schema( xsd ); +} + +//------------------------- Chemistry ----------------------------- + +ReactionMultiTransform::ReactionMultiTransform(): + ReactionChemistry(class_name()), + last_product_(0) +{} + +void +ReactionMultiTransform::add_reaction( ::RDKit::ChemicalReactionOP rxn, core::Real weight ) { + if ( rxn->getNumProductTemplates() != 1 ) { + TR.Error << "Input reaction should have one and only one listed product: " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + return; + } + if ( rxn->getNumReactantTemplates() != 1 ) { + TR.Error << "Input reaction should have one and only one listed reactant: " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + return; + } + + ReactionChemistry::add_reaction( rxn, weight ); +} + +void +ReactionMultiTransform::apply( core::chemical::MutableResidueType & rsdtype ) +{ + + core::chemical::rdkit::RestypeToRDMol to_converter(rsdtype); + rdmol_ = to_converter.Mol(); + core::chemical::rdkit::label_with_index(*rdmol_, "Original_Index"); // (Re)Label with "Original_Index" to use quick mapping behavior. + // Properties carry through the reaction, except for a "bug" with the explicitly mentioned atoms. + input_map_ = to_converter.vd_to_index(); + + ::RDKit::RWMOL_SPTR rdmol( to_converter.Mol() ); // Convert + ::RDKit::MOL_SPTR_VECT reactants; + reactants.push_back( rdmol ); + + products_.clear(); + last_product_ = 0; + + // Select Reactions + utility::vector1< ::RDKit::ChemicalReactionOP > rxns; + numeric::random::WeightedSampler rxn_sampler; + filter_reactions(*rdmol, rxns, rxn_sampler); + + // Try to apply each reaction, and assemble the different products + for ( core::Size rxn_num(1); rxn_num <= rxns.size(); ++rxn_num ) { + ::RDKit::ChemicalReactionOP rxn( rxns[rxn_num] ); + std::vector< ::RDKit::MOL_SPTR_VECT > products( rxn->runReactants( reactants ) ); + if ( products.size() == 0 ) { continue; } + for ( core::Size prod_num(0); prod_num < products.size(); ++ prod_num ) { + if ( products[ prod_num].size() != 1 ) { // Shouldn't happen + TR.Error << "Bad reaction in ReactionMultiTransform! Too many products (" << products[ prod_num].size() << "): " + << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + continue; + } + + ::RDKit::RWMolOP prod( new ::RDKit::RWMol( *products[ prod_num ][0]) ); + + if ( cleanup_product( *prod ) ) { + // Cleanup has more extensive error information + TR.Debug << "Failed cleanup for product of " << ::RDKit::ChemicalReactionToRxnSmarts( *rxn ) << std::endl; + continue; + } + + products_.push_back( prod ); + } + } + + if ( products_.size() == 0 ) { + mapping_.clear(); + // Not having any reactions isn't actually an issue for this chemistry. + TR << "[NOTICE]: No products found for reaction set. - Returning as no-op" << std::endl; + set_last_status( core::chemical::modifications::FAIL_DO_NOT_RETRY ); + return; + } + + // Randomize order in case we only want one reaction. + numeric::random::random_permutation( products_ ); + last_product_ = 0; + + // For later conversion + ref_restype_ = core::chemical::MutableResidueTypeOP( new core::chemical::MutableResidueType( rsdtype ) ); + + // ( reuse the conversion code ) + core::chemical::MutableResidueTypeOP picked( get_additional_output() ); + + rsdtype = *picked; + + //Need to massage mapping for reassignment + core::chemical::VDStringMapping vd_name( *picked ); + core::chemical::StringVDMapping name_vd( rsdtype ); + + mapping_ = combine( mapping_, combine( vd_name, name_vd ) ); + + set_last_status( core::chemical::modifications::SUCCESS ); + +} + +/// @brief Are there alternate ResidueTypes which are availible from the last time we called apply? +/// (That is, will get_addtional_output() return non-null?) +bool +ReactionMultiTransform::has_additional_output() const { + if ( last_product_ >= products_.size() || products_.size() == 0 ) { + return false; + } else { + return true; + } +} + +/// @brief Get additional generated ResidueTypes, if any. +core::chemical::MutableResidueTypeOP +ReactionMultiTransform::get_additional_output() { + if ( ! has_additional_output() ) { + if ( get_last_status() == core::chemical::modifications::SUCCESS ) { + set_last_status( core::chemical::modifications::FAIL_RETRY ); + } + return core::chemical::MutableResidueTypeOP( 0 ); + } + last_product_ += 1; + debug_assert( last_product_ <= products_.size() ); + + ::RDKit::RWMolOP prod( products_[ last_product_ ] ); + + // We need to find a mapping from the pre-reaction molecule to the post-reaction molecule + // Hopefully, the "Original_Index" property carries through, and can be used as a quick shortcut. + core::chemical::IndexIndexMapping rxn_map( core::chemical::rdkit::find_mapping( rdmol_, prod, "Original_Index") ); // -1 is invalid + + // Should be good. Now convert the residue into a Rosetta residue type. + core::chemical::VDIndexMapping restype_prod_map( combine( input_map_, rxn_map ) ); + + core::chemical::rdkit::RDMolToRestype from_converter(*prod); + from_converter.set_nbr( restype_prod_map[ref_restype_->nbr_vertex()] ); + + core::chemical::MutableResidueTypeOP new_resop( from_converter.generate_restype(*ref_restype_,restype_prod_map) ); + + mapping_ = combine( restype_prod_map, from_converter.index_to_vd() ); + + set_last_status( core::chemical::modifications::SUCCESS ); + return new_resop; +} + + +core::chemical::VDVDMapping +ReactionMultiTransform::get_mapping() const { + return mapping_; +} + +std::string +ReactionMultiTransform::class_name() { + return "ReactionMultiTransform"; +} + +void +ReactionMultiTransform::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) { + using namespace utility::tag; + AttributeList attlist; + attlist + + XMLSchemaAttribute::required_attribute("reactions", xs_string, + "The name of the file containing the SMARTS-based reactions to use."); + + protocols::chemistries::xsd_type_definition_w_attributes( + xsd, class_name(), + "Apply all of the reactions listed in the file, and randomly pick one of the products.", + attlist ); +} + +/// @brief Initialize any data members of this instance from an input tag +/// and a DataMap object +void +ReactionMultiTransform::parse_my_tag( + utility::tag::TagCOP tag, + basic::datacache::DataMap &) +{ + reaction_file( tag->getOption("reactions") ); +} + + +} +} diff --git a/source/src/protocols/drug_design/ReactionMultiTransform.fwd.hh b/source/src/protocols/drug_design/ReactionMultiTransform.fwd.hh new file mode 100644 index 0000000000..4be734c11d --- /dev/null +++ b/source/src/protocols/drug_design/ReactionMultiTransform.fwd.hh @@ -0,0 +1,33 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/ReactionMultiTransform.hh +/// @brief apply RDKit's reaction transformation to ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_ReactionMultiTransform_fwd_hh +#define INCLUDED_protocols_drug_design_ReactionMultiTransform_fwd_hh + +#include + +namespace protocols { +namespace drug_design { + + +class ReactionMultiTransform; // fwd declaration +typedef utility::pointer::shared_ptr< ReactionMultiTransform > ReactionMultiTransformOP; +typedef utility::pointer::shared_ptr< ReactionMultiTransform const > ReactionMultiTransformCOP; + + +} // namespace drug_design +} // namespace protocols + +#endif // INCLUDED_protocols_drug_design_ReactionMultiTransform_fwd_hh + + diff --git a/source/src/protocols/drug_design/ReactionMultiTransform.hh b/source/src/protocols/drug_design/ReactionMultiTransform.hh new file mode 100644 index 0000000000..bbb81e3173 --- /dev/null +++ b/source/src/protocols/drug_design/ReactionMultiTransform.hh @@ -0,0 +1,94 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/ReactionMultiTransform.hh +/// @brief apply RDKit's reaction mechanism to transform a ResidueType. +/// Allow for multiple outputs +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_ReactionMultiTransform_hh +#define INCLUDED_protocols_drug_design_ReactionMultiTransform_hh + +#include +#include + +#include + +#include + +#include + +#include + +#include + +namespace protocols { +namespace drug_design { + +class ReactionMultiTransform : public ReactionChemistry { +public: + ReactionMultiTransform(); + + void apply( core::chemical::MutableResidueType & ) override; + + /// @brief Are there alternate ResidueTypes which are availible from the last time we called apply? + /// (That is, will get_addtional_output() return non-null?) + bool + has_additional_output() const override; + + /// @brief Get additional generated ResidueTypes, if any. + core::chemical::MutableResidueTypeOP + get_additional_output() override; + + core::chemical::VDVDMapping + get_mapping() const override; + + /// @brief Initialize any data members of this instance from an input tag + /// and a DataMap object + void parse_my_tag( + utility::tag::TagCOP tag, + basic::datacache::DataMap & datacache + ) override; + + /// @brief Add a reaction to the list of reactions to use. + /// Reaction should be written in the synthetic direction with a single reactant and product + void + add_reaction( ::RDKit::ChemicalReactionOP rxn, core::Real weight ) override; + + static std::string class_name(); + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + +private: + + /// @brief The converted input (needed for correspondance mapping of products) + /// @details Internal implementation detail - do not expose + ::RDKit::RWMolOP rdmol_; + + /// @brief The mapping of input VD to rdmol_ index (needed for correspondance mapping of products) + /// @details Internal implementation detail - do not expose + core::chemical::VDIndexMapping input_map_; + + /// @brief A copy of the input residue type to apply(), for use in converting the output + /// @details Internal implementation detail - do not expose + core::chemical::MutableResidueTypeOP ref_restype_; + + /// @brief The products generated by the last call to apply() + utility::vector1< ::RDKit::RWMolOP > products_; + + /// @brief The last product returned by apply()/get_additional_output() + core::Size last_product_; + + /// @brief The main Input->Output mapping for ResidueTypes + core::chemical::VDVDMapping mapping_; +}; + +} // namespace drug_design +} // namespace protocols + +#endif diff --git a/source/src/protocols/drug_design/ReactionMultiTransformCreator.hh b/source/src/protocols/drug_design/ReactionMultiTransformCreator.hh new file mode 100644 index 0000000000..4d079d681a --- /dev/null +++ b/source/src/protocols/drug_design/ReactionMultiTransformCreator.hh @@ -0,0 +1,42 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/ReactionMultiTransformCreator.hh +/// @brief Class for instantiating a ReactionMultiTransform +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_ReactionMultiTransformCreator_HH +#define INCLUDED_protocols_drug_design_ReactionMultiTransformCreator_HH + +// Package headers +#include +#include + +// Utility headers +#include + +// C++ headers +#include + +namespace protocols { +namespace drug_design { + +class ReactionMultiTransformCreator : public protocols::chemistries::ChemistryCreator { +public: + protocols::chemistries::ChemistryOP create_chemistry() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & ) const override; +}; + + +} //namespace drug_design +} //namespace protocols + + +#endif diff --git a/source/src/protocols/drug_design/SAScoreFilter.cc b/source/src/protocols/drug_design/SAScoreFilter.cc new file mode 100644 index 0000000000..fec21baa84 --- /dev/null +++ b/source/src/protocols/drug_design/SAScoreFilter.cc @@ -0,0 +1,413 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/SAScoreFilter.cc +/// @brief A filter to calculate the Novartis Sythetic Accesibility score using RDKit +/// @author Rocco Moretti (rmorettiase@gmail.com) + +/// @ details This filter is a translation of Contrib/SA_Score/sascorer.py from RDKit +/// http://www.rdkit.org/ +/// +/// Copyright information for sascorer.py: +/// +//////////////////////////////////////////////////////////////////////////////////////// +/// Copyright (c) 2013, Novartis Institutes for BioMedical Research Inc. +/// All rights reserved. +/// +/// Redistribution and use in source and binary forms, with or without +/// modification, are permitted provided that the following conditions are +/// met: +/// +/// * Redistributions of source code must retain the above copyright +/// notice, this list of conditions and the following disclaimer. +/// * 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. +/// * Neither the name of Novartis Institutes for BioMedical Research Inc. +/// nor the names of its contributors may be used to endorse or promote +/// products derived from this software without specific prior written permission. +/// +/// 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. +///////////////////////////////////////////////////////////////////////////////////////// +/// +/// calculation of synthetic accessibility score as described in: +/// +/// Estimation of Synthetic Accessibility Score of Drug-like Molecules based on Molecular Complexity and Fragment Contributions +/// Peter Ertl and Ansgar Schuffenhauer +/// Journal of Cheminformatics 1:8 (2009) +/// http://www.jcheminf.com/content/1/1/8 +/// +/// several small modifications to the original paper are included +/// particularly slightly different formula for marocyclic penalty +/// and taking into account also molecule symmetry (fingerprint density) +/// +/// for a set of 10k diverse molecules the agreement between the original method +/// as implemented in PipelinePilot and this implementation is r2 = 0.97 +/// +/// peter ertl & greg landrum, september 2013 + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR("protocols.drug_design.SAScoreFilter"); + +protocols::filters::FilterOP +SAScoreFilterCreator::create_filter() const { return protocols::filters::FilterOP( new SAScoreFilter ); } + +std::string +SAScoreFilterCreator::keyname() const { return SAScoreFilter::class_name(); } + +void SAScoreFilterCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const { + SAScoreFilter::provide_xml_schema( xsd ); +} + +std::string SAScoreFilter::class_name() { + return "SAScore"; +} + +void SAScoreFilter::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) +{ + using namespace utility::tag; + AttributeList attlist; + + attlist + + XMLSchemaAttribute::required_attribute( "residue", xsct_refpose_enabled_residue_number, "Residue to calculate SAScore on" ) + + XMLSchemaAttribute( "threshold", xsct_real, "Fail if the SAScore is greater than or equal to this value." ); + + protocols::filters::xsd_type_definition_w_attributes( xsd, class_name(), + "Calculate the synthetic accessiblity score (of Ertl and Schuffenhauer doi:10.1186/1758-2946-1-8) for a residue.", + attlist ); +} + +void +SAScoreFilter::parse_my_tag( utility::tag::TagCOP tag, basic::datacache::DataMap & ) +{ + residue_ = tag->getOption( "residue" ); + threshold_ = tag->getOption( "threshold" ); + + TR << "SAScore filter on residue "< pose.total_residue() ) { + TR.Error << "Attempted to access residue " << residue_ << " in pose with " << pose.total_residue() << " residues. Failing filter. " << std::endl; + utility_exit_with_message("Cannot apply SAScore filter on non-existant residue!"); + } + + core::chemical::MutableResidueTypeOP restype( utility::pointer::make_shared< core::chemical::MutableResidueType >(pose.residue(resnum).type()) ); + // Want to have neutralized, no-hydro molecules: this should be the default. + core::chemical::rdkit::RestypeToRDMol to_converter(*restype); + ::RDKit::RWMOL_SPTR rdmol( to_converter.Mol() ); // Convert + + return calculate_rdkit( *rdmol ); + +} + +core::Size +SAScoreFilter::num_spiro(RDKit::ROMol const & mol) const { + using namespace RDKit; + RingInfo *ri = mol.getRingInfo(); // Do NOT delete pointer + std::vector< std::set< int > > arings; + for ( VECT_INT_VECT::const_iterator itr( ri->atomRings().begin() ), itr_end( ri->atomRings().end() ); + itr != itr_end; ++itr ) { + std::set< int > aset( itr->begin(), itr->end() ); + arings.push_back( aset ); + } + std::set< int > spiros; + for ( std::vector< std::set< int > >::const_iterator itr( arings.begin() ), itr_end( arings.end() ); + itr != itr_end; ++itr ) { + for ( std::vector< std::set< int > >::const_iterator itr2( itr + 1 ); itr2 != itr_end; ++itr2 ) { + std::vector out( std::min( itr->size(), itr2->size() ) ); + std::vector::iterator out_end; + out_end = std::set_intersection( itr->begin(), itr->end(), itr2->begin(), itr2->end(), out.begin() ); + if ( (out_end - out.begin()) == 1 ) { + spiros.insert( *out.begin() ); + } + } + } + return spiros.size(); +} + +core::Size +SAScoreFilter::num_bridgeheads(RDKit::ROMol const & mol) const { + using namespace RDKit; + RingInfo *ri = mol.getRingInfo(); // Do NOT delete pointer + + // find bonds that are shared between rings that share at least 2 bonds: + std::vector< std::set< int > > brings; + for ( VECT_INT_VECT::const_iterator itr( ri->bondRings().begin() ), itr_end( ri->bondRings().end() ); + itr != itr_end; ++itr ) { + std::set< int > bset( itr->begin(), itr->end() ); + brings.push_back( bset ); + } + + std::set< int > bridges; + + for ( std::vector< std::set< int > >::const_iterator itr( brings.begin() ), itr_end( brings.end() ); + itr != itr_end; ++itr ) { + for ( std::vector< std::set< int > >::const_iterator itr2( itr + 1 ); itr2 != itr_end; ++itr2 ) { + std::vector out( std::min( itr->size(), itr2->size() ) ); + std::vector::iterator out_end; + out_end = std::set_intersection( itr->begin(), itr->end(), itr2->begin(), itr2->end(), out.begin() ); + if ( (out_end - out.begin()) > 1 ) { + std::map< int, core::Size > atomCounts; + + for ( std::vector::const_iterator out_itr( out.begin() ); out_itr != out_end; ++out_itr ) { + Bond const * bond( mol.getBondWithIdx(*out_itr) ); + // std::map::operator[] will auto create an entry of 0 if one does not already exist + atomCounts[bond->getBeginAtomIdx()] +=1; + atomCounts[bond->getEndAtomIdx()] +=1; + } + + for ( std::map< int, core::Size >::const_iterator ac_itr( atomCounts.begin() ), ac_end( atomCounts.end() ); + ac_itr != ac_end; ++ac_itr ) { + if ( ac_itr->second == 1 ) { + bridges.insert(ac_itr->first); + } + } + } + } + } + return bridges.size(); +} + +core::Size +SAScoreFilter::num_chiral(RDKit::ROMol & mol, bool includeUnassigned ) const { + using namespace RDKit; + MolOps::assignStereochemistry(mol,false,true,false); + core::Size nchiral(0); + for ( ROMol::AtomIterator itr( mol.beginAtoms() ), itr_end( mol.endAtoms() ); itr != itr_end; ++itr ) { + if ( (*itr)->hasProp("_CIPCode") ) { + ++nchiral; + } else if ( includeUnassigned && (*itr)->hasProp("_ChiralityPossible") ) { + ++nchiral; + } + } + return nchiral; +} + +/// @details The caller takes ownership of the returned item +RDKit::SparseIntVect * +SAScoreFilter::get_morgan_fingerprint(const RDKit::ROMol &mol, int radius) const { + + //int nBits = -1; + bool useChirality = false; + bool useBondTypes = true; + //bool useFeatures = false; + bool useCounts = true; + + std::vector *invars = nullptr; + // Ignore invariants & useFeatures + std::vector *froms = nullptr; + // Ignore fromAtoms + RDKit::MorganFingerprints::BitInfoMap *bitInfoMap = nullptr; + // Ignore bitInfo + RDKit::SparseIntVect *res; + // Assume nBits < 0 + res = RDKit::MorganFingerprints::getFingerprint(mol, + static_cast(radius), + invars,froms,useChirality, + useBondTypes,useCounts,false,bitInfoMap); + if ( bitInfoMap ) { + delete bitInfoMap; + } + if ( invars ) delete invars; + if ( froms ) delete froms; + return res; +} + +core::Real +SAScoreFilter::calculate_rdkit(RDKit::ROMol & mol) const { + using namespace RDKit; + SAScoreData const & sas_data( *SAScoreData::get_instance() ); + + // fragment score - 2 is the *radius* of the circular fingerprint + RDKit::SparseIntVect *fp( get_morgan_fingerprint(mol, 2) ); // Delete me! + typedef std::map StorageType; + StorageType const & fps( fp->getNonzeroElements() ); + core::Real score1 = 0.0; + core::Size nf = 0; + for ( StorageType::const_iterator itr(fps.begin()), itr_end(fps.end()); itr != itr_end; ++itr ) { + nf += itr->second; + score1 += sas_data[ itr->first ] * itr->second; + } + score1 /= nf; + + // features score + core::Real nAtoms = mol.getNumAtoms(); + core::Real nChiralCenters = num_chiral(mol,true); + core::Real nBridgeheads = num_bridgeheads(mol); + core::Real nSpiro = num_spiro(mol); + core::Real nMacrocycles=0; + VECT_INT_VECT const & atomRings( mol.getRingInfo()->atomRings() ); + for ( VECT_INT_VECT::const_iterator itr( atomRings.begin() ), itr_end( atomRings.end() ); + itr != itr_end; ++itr ) { + if ( itr->size() > 8 ) { + ++nMacrocycles; + } + } + + core::Real sizePenalty = std::pow(nAtoms,1.005) - nAtoms; + core::Real stereoPenalty = std::log10(nChiralCenters+1); + core::Real spiroPenalty = std::log10(nSpiro+1); + core::Real bridgePenalty = std::log10(nBridgeheads+1); + // --------------------------------------- + // This differs from the paper, which defines: + // macrocyclePenalty = math.log10(nMacrocycles+1) + // This form generates better results when 2 or more macrocycles are present + core::Real macrocyclePenalty = 0.; + if ( nMacrocycles > 0 ) { + macrocyclePenalty = std::log10(2); + } + + core::Real score2 = 0.0 -sizePenalty -stereoPenalty -spiroPenalty -bridgePenalty -macrocyclePenalty; + + // correction for the fingerprint density + // not in the original publication, added in version 1.1 + // to make highly symmetrical molecules easier to synthetise + core::Real score3 = 0.0; + if ( nAtoms > fps.size() ) { + score3 = std::log( nAtoms / fps.size() ) * 0.5; + } + + core::Real sascore = score1 + score2 + score3; + + // need to transform "raw" value into scale between 1 and 10 + core::Real const min = -4.0; + core::Real const max = 2.5; + sascore = 11. - (sascore - min + 1) / (max - min) * 9.; + // smooth the 10-end + if ( sascore > 8.0 ) { + sascore = 8. + std::log(sascore+1.-9.); + } + if ( sascore > 10.0 ) { + sascore = 10.0; + } + if ( sascore < 1. ) { + sascore = 1.0; + } + + delete fp; + return sascore; +} + +///////////////// SAScoreData +SAScoreData::SAScoreData(): + default_(-4.0) +{ + load_data_from_file("fpscores.gz"); +} + +void +SAScoreData::load_data_from_file(std::string const & filename ) { + //Right now, just load from database - in future may want some sort of local loading. + fscores_.clear(); + + utility::io::izstream input; + if ( ! basic::database::open( input, "chemical/rdkit/" + filename ) ) { + utility_exit_with_message("Cannot open SAScore database file."); + } + boost::uint32_t item; + float value; + std::string line; + while ( getline( input, line ) ) { + std::istringstream l( line ); + l >> value; + l >> item; + while ( l ) { + fscores_[item] = value; + l >> item; + } + } + TR << "Loaded " << fscores_.size() << " fragment scores for SAScoring. (Should be 705292)" << std::endl; +} + + +float +SAScoreData::operator[] ( boost::uint32_t index ) const { + fscores_t::const_iterator entry( fscores_.find(index) ); + if ( entry == fscores_.end() ) { + return default_; + } else { + return entry->second; + } +} + +} // namespace drug_design +} // namespace protocols diff --git a/source/src/protocols/drug_design/SAScoreFilter.fwd.hh b/source/src/protocols/drug_design/SAScoreFilter.fwd.hh new file mode 100644 index 0000000000..8f3200f1d1 --- /dev/null +++ b/source/src/protocols/drug_design/SAScoreFilter.fwd.hh @@ -0,0 +1,28 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/SAScoreFilter.fwd.hh +/// @brief +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_SAScoreFilter_fwd_hh +#define INCLUDED_protocols_drug_design_SAScoreFilter_fwd_hh + + +namespace protocols { +namespace drug_design { + +class SAScoreFilter; + +} +} + +#endif + + diff --git a/source/src/protocols/drug_design/SAScoreFilter.hh b/source/src/protocols/drug_design/SAScoreFilter.hh new file mode 100644 index 0000000000..876ce78728 --- /dev/null +++ b/source/src/protocols/drug_design/SAScoreFilter.hh @@ -0,0 +1,142 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/SAScoreFilter.hh +/// @brief A filter which computes the Novartis SA_Score metric +/// @author Rocco Moretti (rmorettiase@gmail.com) + +/// @details +/// calculation of synthetic accessibility score as described in: +/// +/// Estimation of Synthetic Accessibility Score of Drug-like Molecules based on Molecular Complexity and Fragment Contributions +/// Peter Ertl and Ansgar Schuffenhauer +/// Journal of Cheminformatics 1:8 (2009) +/// http://www.jcheminf.com/content/1/1/8 + +#ifndef INCLUDED_protocols_drug_design_SAScoreFilter_hh +#define INCLUDED_protocols_drug_design_SAScoreFilter_hh + +//unit headers +#include + +// Project Headers +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +namespace protocols { +namespace drug_design { + +class SAScoreFilter : public filters::Filter +{ +public: + SAScoreFilter(): + Filter( class_name() ) + {} + + SAScoreFilter( std::string const & residue ): + Filter( class_name() ), + residue_( residue ) + {} + + core::Real threshold() const { return threshold_; } + + void threshold(core::Real setting) { threshold_ = setting; } + + bool apply( core::pose::Pose const & pose ) const override; + filters::FilterOP clone() const override { + return filters::FilterOP( new SAScoreFilter( *this ) ); + } + filters::FilterOP fresh_instance() const override { + return filters::FilterOP( new SAScoreFilter() ); + } + + void report( std::ostream & out, core::pose::Pose const & pose ) const override; + core::Real report_sm( core::pose::Pose const & pose ) const override; + core::Real compute( core::pose::Pose const &pose ) const; + void parse_my_tag( utility::tag::TagCOP tag, basic::datacache::DataMap & ) override; + + static std::string class_name(); + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + +private: + + core::Size + num_spiro(RDKit::ROMol const & mol) const; + + core::Size + num_bridgeheads(RDKit::ROMol const & mol) const; + + /// @details nonconst mol as it recalculates annotations + core::Size + num_chiral(RDKit::ROMol & mol, bool includeUnassigned=false ) const; + + RDKit::SparseIntVect * + get_morgan_fingerprint(RDKit::ROMol const & mol, int radius) const; + +public: // public for direct access in tests and other movers + + // @details nonconst mol because it calls num_chiral() + core::Real + calculate_rdkit(RDKit::ROMol & mol) const; + +private: + /// @brief Which residue to calculate the score for + std::string residue_; + + /// @brief Threshold (maximum) for truth value context + core::Real threshold_; + +}; + +/////////////////////////////////////////////////////////////////////////////// +class SAScoreData : public utility::SingletonBase< SAScoreData > +{ + +public: + friend class utility::SingletonBase< SAScoreData >; + + float + operator[] ( boost::uint32_t index ) const; + +private: + SAScoreData(); + SAScoreData( SAScoreData const & ) = delete; // unimplemented + SAScoreData const & operator = ( SAScoreData const & ) = delete; // unimplemented + + /// @brief function that actually loads the data + void + load_data_from_file( std::string const & filename ); + +private: // Data + // float to save space + typedef std::map< boost::uint32_t, float > fscores_t; + /// @brief The data for computation + fscores_t fscores_; + // @brief The default value to use. + float const default_; + +}; + + +} +} + +#endif diff --git a/source/src/protocols/drug_design/SAScoreFilterCreator.hh b/source/src/protocols/drug_design/SAScoreFilterCreator.hh new file mode 100644 index 0000000000..5d32574b1b --- /dev/null +++ b/source/src/protocols/drug_design/SAScoreFilterCreator.hh @@ -0,0 +1,40 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/SAScoreFilterCreator.hh +/// @brief FilterCreator for the SAScoreFilter +/// @author Rocco Moretti (rmorettiase@gmail.com) + + +#ifndef INCLUDED_protocols_drug_design_SAScoreFilterCreator_hh +#define INCLUDED_protocols_drug_design_SAScoreFilterCreator_hh + +// Package Headers +#include + +// Utility Headers + +// c++ headers +#include + +namespace protocols { +namespace drug_design { + +class SAScoreFilterCreator : public protocols::filters::FilterCreator +{ +public: + protocols::filters::FilterOP create_filter() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const override; +}; + +} +} + +#endif diff --git a/source/src/protocols/drug_design/SubstituentReplace.cc b/source/src/protocols/drug_design/SubstituentReplace.cc new file mode 100644 index 0000000000..0a36a7d043 --- /dev/null +++ b/source/src/protocols/drug_design/SubstituentReplace.cc @@ -0,0 +1,410 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/SubstituentReplace.hh +/// @brief use RDKit to replace a subsituent on a substructure in a ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include // For MolToSmiles +#include +#include +#include + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR("protocols.drug_design.SubstituentReplace"); + +//------------------------- Creator ----------------------------- + +protocols::chemistries::ChemistryOP +SubstituentReplaceCreator::create_chemistry() const { + return protocols::chemistries::ChemistryOP( new SubstituentReplace ); +} + +std::string +SubstituentReplaceCreator::keyname() const { + return SubstituentReplace::class_name(); +} + +void +SubstituentReplaceCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const { + SubstituentReplace::provide_xml_schema( xsd ); +} + +//------------------------- Chemistry ----------------------------- + +SubstituentReplace::SubstituentReplace(): + protocols::chemistries::Chemistry( class_name() ), + H_as_dummy_( false ), + V_as_dummy_( false ) +{} + +void +SubstituentReplace::template_database( std::string filename, bool append /*=false*/ ) { + if ( ! append ) { + templates_.clear(); + } + + core::Size new_item_start( templates_.size() + 1 ); + + core::chemical::rdkit::load_sdf( filename, templates_, /*removeHs=*/ false ); + + if ( templates_.size() < new_item_start ) { + TR.Warning << "No molecule fragments found in file " << filename << std::endl; + } + + for ( core::Size ii(new_item_start); ii <= templates_.size(); ++ii ) { + // Do H/V mangling, if needed. + if ( H_as_dummy_ || V_as_dummy_ ) { + ::RDKit::RWMolOP mod_mol( new ::RDKit::RWMol( *templates_[ii] ) ); + ::RDKit::AtomOP qatom( new ::RDKit::QueryAtom(0) ); + qatom->setQuery( ::RDKit::makeAtomNullQuery() ); + for ( unsigned int jj(0); jj < mod_mol->getNumAtoms(); ++jj ) { + if ( (H_as_dummy_ && mod_mol->getAtomWithIdx(jj)->getAtomicNum() == 1 ) || + (V_as_dummy_ && mod_mol->getAtomWithIdx(jj)->getAtomicNum() == 23 ) // Vanadium is 23 + ) { + mod_mol->replaceAtom( jj, qatom.get() ); // Atom will be copied. + } + } + templates_[ii] = mod_mol; + } + // Check that dummy atoms are only bonded to one other atom. + utility::vector1< unsigned int > substruct_dummies; + find_dummies( *templates_[ii], substruct_dummies ); + for ( core::Size jj(1); jj <= substruct_dummies.size(); ++jj ) { + ::RDKit::ROMol::OBOND_ITER_PAIR bond_itrs( templates_[ii]->getAtomBonds( templates_[ii]->getAtomWithIdx( substruct_dummies[jj] ) ) ); + if ( (bond_itrs.second - bond_itrs.first) != 1 ) { + TR.Error << "In file " << filename << " molecule '" << core::chemical::rdkit::get_name( *templates_[ii] ) << ": "; + TR.Error << ::RDKit::MolToSmiles( *templates_[ii] ) << std::endl; + TR.Error << "Dummy atom " << substruct_dummies[jj] << " has " << (bond_itrs.second - bond_itrs.first) << " bonds, should only be one." << std::endl; + utility_exit_with_message("Dummy atom in template structure has too many bonds."); + } + } + } + +} + +/// @brief The file which contains the fragments to add to input residue type. +void +SubstituentReplace::substituents_database( std::string filename, bool append /*=false*/ ) { + if ( ! append ) { + substituents_.clear(); + } + + core::Size new_item_start( substituents_.size() + 1 ); + + core::chemical::rdkit::load_sdf( filename, substituents_, /*removeHs=*/ false ); + + if ( substituents_.size() < new_item_start ) { + TR.Warning << "No molecule fragments found in file " << filename << std::endl; + } + + // There isn't necessarily any dummies in the substituents database. +} + +void +SubstituentReplace::apply( core::chemical::MutableResidueType & rsdtype ) +{ + + if ( templates_.size() < 1 ) { + utility_exit_with_message("Not enough substructures found for SubstituentReplace!"); + } + + if ( substituents_.size() < 1 ) { + utility_exit_with_message("Not enough substructures found for SubstituentReplace!"); + } + + // We need hydrogens as physical entities, as we may be swapping them. + core::chemical::rdkit::RestypeToRDMol to_converter(rsdtype, /*neutralize=*/ false, /*keep_hydro=*/ true); + ::RDKit::RWMolOP rdmol( to_converter.Mol() ); // Convert + + // Select location to do the replacement on + MoleculeSubstitutionOP template_molsub( pick_template( rdmol, templates_, false ) ); + + if ( ! template_molsub ) { + TR.Warning << "No suitable template structure found. Doing nothing." << std::endl; + mapping_.clear(); + mapping_.identity( true ); + set_last_status( core::chemical::modifications::FAIL_DO_NOT_RETRY ); + return; + } + + unsigned int tdx( AtomSubstitution::invalid_index ); + MoleculeSubstitutionOP replace_molsub( pick_replacement( template_molsub, tdx ) ); + + if ( ! replace_molsub ) { + TR.Warning << "No suitable substituent sources found for template " << ::RDKit::MolToSmiles( *template_molsub->templt() ) <substitution_for_tdx( tdx ).mdx() ); + unsigned int rdx( replace_molsub->substitution_for_tdx( tdx ).rdx() ); + + unsigned int tdx_bnd( get_bonded_atom( *replace_molsub->templt(), tdx ) ); + unsigned int mdx_bnd( replace_molsub->substitution_for_tdx( tdx_bnd ).mdx() ); + unsigned int rdx_bnd( replace_molsub->substitution_for_tdx( tdx_bnd ).rdx() ); + + // Now build a new molecule + ::RDKit::RWMolOP new_mol( new ::RDKit::RWMol ); + new_mol->addConformer( new ::RDKit::Conformer(0) ); // Make new zero-atom conformer - RDKit takes ownership! + + // If there is any global-level information that would need to be copied, here's the place to do it. + + replace_molsub->add_newmol( new_mol ); + + // We want all the atoms from the original molecule which are connected to the non-dummy side of the bond, + // and everything connected to it, skipping the dummy side of the bond + + copy_attached_atoms( *replace_molsub, OriginalMol, ::RDGeom::Transform3D(), mdx_bnd, mdx ); + + // Compute the transform from the replacement frame to the original molecule frame + // The easiest is probably just to get the transform across all mapped atoms. + ::RDKit::MatchVectType atom_mapping; + for ( unsigned int mm(0); mm < replace_molsub->mol()->getNumAtoms(); ++mm ) { // 0 Based!! + unsigned int rr( replace_molsub->substitution_for_mdx( mm ).rdx() ); + if ( rr != AtomSubstitution::invalid_index ) { + atom_mapping.push_back( std::pair< unsigned int, unsigned int >( rr, mm ) ); + } + } + ::RDGeom::Transform3D transform_replace_to_mol; + ::RDKit::MolAlign::getAlignmentTransform( *replace_molsub->replace(), *replace_molsub->mol(), + transform_replace_to_mol, -1, -1, &atom_mapping); // Needs raw pointer + + // Now we want to copy the replace atoms from the dummy side of the bond. + + copy_attached_atoms( *replace_molsub, ReplaceMol, transform_replace_to_mol, rdx, rdx_bnd ); + + // Add the bond between the two fragments + ::RDKit::Bond::BondType bt( replace_molsub->mol()->getBondBetweenAtoms(mdx, mdx_bnd)->getBondType() ); + unsigned int bgn_ndx( replace_molsub->substitution_for_mdx(mdx_bnd).ndx() ); + unsigned int end_ndx( replace_molsub->substitution_for_rdx(rdx).ndx() ); + TR << "Bridging fragments with bond between " << bgn_ndx << " and " << end_ndx << std::endl; + debug_assert( new_mol->getBondBetweenAtoms(bgn_ndx, end_ndx) == nullptr ); // Bond should not already exist + new_mol->addBond( bgn_ndx, end_ndx, bt ); + + // Now clean up the molecule. + try { + ::RDKit::MolOps::sanitizeMol(*new_mol); + } catch (::RDKit::MolSanitizeException &se){ + TR.Warning << "Cannot clean up molecule made by replacement - skipping." << std::endl; + mapping_.clear(); + mapping_.identity( true ); + set_last_status( core::chemical::modifications::FAIL_RETRY ); // RETRY as a different template/replacement might work + return; + } + + // Check to make sure everything got matched up correctly. (Rosetta can't handle disconnected Restypes.) + std::vector frag_mapping; + unsigned int num_frags( ::RDKit::MolOps::getMolFrags(*new_mol, frag_mapping) ); + if ( num_frags != 1 ) { + TR.Error << "When substituting " << ::RDKit::MolToSmiles( *rdmol ) << std::endl; + TR.Error << "with template " << ::RDKit::MolToSmiles( *replace_molsub->templt() ) << std::endl; + TR.Error << "and replacement " << ::RDKit::MolToSmiles( *replace_molsub->replace() ) << std::endl; + TR.Error << "disconnected molecule obtained: " << ::RDKit::MolToSmiles( *new_mol ) << std::endl; + utility_exit_with_message("SubstituentReplace made a molecule that was disconnected!"); + } + + // Minimize with somewhat loose tolerances to fix up bond geometries + ::RDKit::ForceFieldOP ff( core::chemical::rdkit::get_forcefield( *new_mol ) ); + if ( ff ) { + ff->minimize(200, 1e-2, 1e-4); + } else { + TR.Warning << "Cannot find appropriate forcefield for minimization - skipping min." << std::endl; + } + + // We need to find a mapping from the molecule to the post molecule + core::chemical::IndexIndexMapping sub_map( replace_molsub->find_mdx_to_ndx_mapping() ); + + // Should be good. Now convert the residue into a Rosetta residue type. + + core::chemical::VDIndexMapping restype_prod_map( combine( to_converter.vd_to_index(), sub_map ) ); + + core::chemical::rdkit::RDMolToRestype from_converter(*new_mol); + from_converter.set_nbr( restype_prod_map[rsdtype.nbr_vertex()] ); + + core::chemical::MutableResidueTypeOP new_resop( from_converter.generate_restype(rsdtype,restype_prod_map) ); + + TR << "Replaced a substituent of '" << rsdtype.name() << "'" << std::endl; + TR << "using template " << ::RDKit::MolToSmiles( *replace_molsub->templt() ) << std::endl; + TR << "and replacement " << ::RDKit::MolToSmiles( *replace_molsub->replace() ) << std::endl; + TR << "to convert " << ::RDKit::MolToSmiles( *replace_molsub->mol() ) << std::endl; + TR << "to " << ::RDKit::MolToSmiles( *new_mol ) << std::endl; + + mapping_ = combine( restype_prod_map, from_converter.index_to_vd() ); + rsdtype = *new_resop; + mapping_ = combine( mapping_, combine( core::chemical::VDStringMapping(*new_resop), core::chemical::StringVDMapping(rsdtype)) ); + + // That's it, we're successful + set_last_status( core::chemical::modifications::SUCCESS ); + return; + +} + +MoleculeSubstitutionOP +SubstituentReplace::pick_replacement(MoleculeSubstitutionOP template_molsub, unsigned int & tdx_out) const { + + // Subset the possible substitutuent sources to those which are applicable. + utility::vector1< ::RDKit::ROMolOP > poss_subst; + numeric::random::WeightedSampler subst_sampler; + for ( core::Size jj(1); jj <= substituents_.size(); ++jj ) { + ::RDKit::MatchVectType match_vect; + // Only need pass/fail at this point. + if ( ::RDKit::SubstructMatch(*substituents_[jj], *template_molsub->templt(), match_vect) ) { + core::Real weight( 1.0 ); + if ( property_name_.size() && substituents_[jj]->hasProp(property_name_) ) { + weight = substituents_[jj]->getProp(property_name_); + } + subst_sampler.add_weight( weight ); + poss_subst.push_back( substituents_[jj] ); + } + } + + if ( poss_subst.size() == 0 ) { + TR.Warning << "Found no matching cores for substituent replacment. Are you missing hydrogens?" << std::endl; + return nullptr; + } + + while ( subst_sampler.update_cumulative_distribution() ) { // While there are valid samples. + // Pick possible swaps from the possibilities + core::Size selected_subst( subst_sampler.random_sample() ); + ::RDKit::ROMolOP subst_source( poss_subst[ selected_subst ] ); + + // Pick a template match from the subst_source (this deals with orientation issues.) + std::vector< ::RDKit::MatchVectType > matches; + ::RDKit::SubstructMatch(*subst_source, *template_molsub->templt(), matches, /*uniquify=*/ false); // Want redundancy in orientation + while ( matches.size() > 0 ) { + core::Size picked_match_index( numeric::random::random_range(0,matches.size()-1) ); // All are equivalent weight + ::RDKit::MatchVectType picked_match( matches[ picked_match_index ] ); + std::map< unsigned int, unsigned int > r_to_t_map( convert_matchvect_to_map(picked_match) ); + MoleculeSubstitutionOP replace_molsub( template_molsub->add_replacement( subst_source, r_to_t_map ) ); + + utility::vector1< unsigned int > possible_dummies( replace_molsub->template_dummies() ); // Make copy - we'll modify this + while ( possible_dummies.size() > 0 ) { + // Now pick a random dummy atom from the template + core::Size dummy_index( numeric::random::random_range(1,possible_dummies.size()) ); + + unsigned int tdx( possible_dummies[ dummy_index ] ); + + unsigned int mdx( replace_molsub->substitution_for_tdx( tdx ).mdx() ); + debug_assert( mdx != AtomSubstitution::invalid_index ); + unsigned int rdx( replace_molsub->substitution_for_tdx( tdx ).rdx() ); + debug_assert( rdx != AtomSubstitution::invalid_index ); + + unsigned int tdx_bnd( get_bonded_atom( *replace_molsub->templt(), tdx ) ); + debug_assert( tdx_bnd != AtomSubstitution::invalid_index ); + unsigned int mdx_bnd( replace_molsub->substitution_for_tdx( tdx_bnd ).mdx() ); + debug_assert( mdx_bnd != AtomSubstitution::invalid_index ); + unsigned int rdx_bnd( replace_molsub->substitution_for_tdx( tdx_bnd ).rdx() ); + debug_assert( rdx_bnd != AtomSubstitution::invalid_index ); + + TR.Debug << "Bond on original is " << mdx << " -- (" << mdx_bnd <<")* " << std::endl; + TR.Debug << "Bond on replacement is " << rdx << "* -- (" << rdx_bnd <<") " << std::endl; + + if ( ! bond_is_in_ring( *replace_molsub->mol(), mdx, mdx_bnd ) && + ! bond_is_in_ring( *replace_molsub->replace(), rdx, rdx_bnd) && + ( replace_molsub->mol()->getBondBetweenAtoms(mdx, mdx_bnd)->getBondType() == + replace_molsub->replace()->getBondBetweenAtoms(rdx, rdx_bnd)->getBondType() ) ) { + tdx_out = tdx; + return replace_molsub; + } + // This stub won't work - remove it from the list + // (the erase-remove idiom - oh, for proper delete methods in C++) + possible_dummies.erase( std::remove( possible_dummies.begin(), possible_dummies.end(), tdx), possible_dummies.end() ); + } + // This template match won't work - remove it from the list + matches.erase( matches.begin() + picked_match_index ); //For a decent index delete ... + } + // This molecule won't work - turn it off. + subst_sampler.set_weight(selected_subst , 0); + } + + TR.Warning << "Cannot find possible replacement in SubstituentReplace." << std::endl; + return nullptr; // Can't return anything - it's not possible. +} + +core::chemical::VDVDMapping +SubstituentReplace::get_mapping() const { + return mapping_; +} + +std::string +SubstituentReplace::class_name() { + return "SubstituentReplace"; +} + +void +SubstituentReplace::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) { + using namespace utility::tag; + AttributeList attlist; + attlist + + XMLSchemaAttribute::required_attribute("templates", xs_string, + "The name of the SDF file which contains the templates which define the substituents.") + + XMLSchemaAttribute::required_attribute("substituent", xs_string, + "The name of the SDF file which contains the database of substituents to use in the replacement.") + + XMLSchemaAttribute::attribute_w_default("weight_by_property", xs_string, + "When randomly picking the substructure from the file, weight by the given property from the SDF", "") + + XMLSchemaAttribute::attribute_w_default("H_as_dummy", xsct_rosetta_bool, + "If true, use hydrogens in the input file as attachment points", "0") + + XMLSchemaAttribute::attribute_w_default("V_as_dummy", xsct_rosetta_bool, + "If true, use vanadium atoms in the input file as attachment points", "0"); + + protocols::chemistries::xsd_type_definition_w_attributes( + xsd, class_name(), + "Replace a substituent on a given substructure of a ResidueType with another.", + attlist ); +} + +/// @brief Initialize any data members of this instance from an input tag +/// and a DataMap object +void +SubstituentReplace::parse_my_tag( + utility::tag::TagCOP tag, + basic::datacache::DataMap &) +{ + // These need to be set prior to loading the database + H_as_dummy( tag->getOption("H_as_dummy", false) ); + V_as_dummy( tag->getOption("V_as_dummy", false) ); + + template_database( tag->getOption("templates") ); + substituents_database( tag->getOption("substituent") ); + weight_by_property( tag->getOption("weight_by_property", "") ); + + TR << "Defined SubstituentReplace Chemistry with " << templates_.size() << " templates and " << substituents_.size() << " substituents, weighting by property '" << weight_by_property() << "'" << std::endl; +} + + +} +} diff --git a/source/src/protocols/drug_design/SubstituentReplace.fwd.hh b/source/src/protocols/drug_design/SubstituentReplace.fwd.hh new file mode 100644 index 0000000000..262e250dbb --- /dev/null +++ b/source/src/protocols/drug_design/SubstituentReplace.fwd.hh @@ -0,0 +1,33 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/SubstituentReplace.hh +/// @brief use RDKit to replace a substructure in a ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_SubstituentReplace_fwd_hh +#define INCLUDED_protocols_drug_design_SubstituentReplace_fwd_hh + +#include + +namespace protocols { +namespace drug_design { + + +class SubstituentReplace; // fwd declaration +typedef utility::pointer::shared_ptr< SubstituentReplace > SubstituentReplaceOP; +typedef utility::pointer::shared_ptr< SubstituentReplace const > SubstituentReplaceCOP; + + +} // namespace drug_design +} // namespace protocols + +#endif // INCLUDED_protocols_drug_design_SubstituentReplace_fwd_hh + + diff --git a/source/src/protocols/drug_design/SubstituentReplace.hh b/source/src/protocols/drug_design/SubstituentReplace.hh new file mode 100644 index 0000000000..83d9dd0a8b --- /dev/null +++ b/source/src/protocols/drug_design/SubstituentReplace.hh @@ -0,0 +1,100 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/SubstituentReplace.hh +/// @brief use RDKit to replace a subsitutent of a substructure in a ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_SubstituentReplace_hh +#define INCLUDED_protocols_drug_design_SubstituentReplace_hh + +#include + +#include + +#include + +#include +#include + +#include + +#include + +#include + +namespace protocols { +namespace drug_design { + +class SubstituentReplace : public protocols::chemistries::Chemistry { +public: + SubstituentReplace(); + + void apply( core::chemical::MutableResidueType & ) override; + + /// @brief The file which contains the templates to search for to add to input residue type. + void template_database( std::string filename, bool append=false ); + + /// @brief The file which contains the substituents to add to input residue type. + void substituents_database( std::string filename, bool append=false ); + + /// @brief Will hydrogens in the input be converted to dummy stubs? + void H_as_dummy( core::Real setting ) { H_as_dummy_ = setting; } + bool H_as_dummy() const { return H_as_dummy_; } + + /// @brief Will V atoms (Vanadium, but used commonly in Rosetta for "virtual" designations) + /// in the input be converted to dummy stubs? + void V_as_dummy( core::Real setting ) { V_as_dummy_ = setting; } + bool V_as_dummy() const { return V_as_dummy_; } + + /// @brief If not empty, use property weighting based on the given property. + void weight_by_property( std::string const & setting ) { property_name_ = setting; } + std::string const & weight_by_property() const { return property_name_; } + + /// @brief Initialize any data members of this instance from an input tag + /// and a DataMap object + void parse_my_tag( + utility::tag::TagCOP tag, + basic::datacache::DataMap & datacache + ) override; + + core::chemical::VDVDMapping + get_mapping() const override; + + static std::string class_name(); + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + +private: + + MoleculeSubstitutionOP + pick_replacement(MoleculeSubstitutionOP template_molsub, unsigned int & tdx_out) const; + +private: + + /// @brief The templates to possibly use + utility::vector1< ::RDKit::ROMolOP > templates_; + /// @brief The fragments to apply + utility::vector1< ::RDKit::ROMolOP > substituents_; + + /// @brief Do we consider Hydrogens to be dummy atoms? + bool H_as_dummy_; + /// @brief Do we consider V atoms to be dummy atoms? + bool V_as_dummy_; + + /// @brief If not empty, pick fragments based on the weighting by the given property. + std::string property_name_; + + core::chemical::VDVDMapping mapping_; + +}; + +} // namespace drug_design +} // namespace protocols + +#endif diff --git a/source/src/protocols/drug_design/SubstituentReplaceCreator.hh b/source/src/protocols/drug_design/SubstituentReplaceCreator.hh new file mode 100644 index 0000000000..d5e04f70c5 --- /dev/null +++ b/source/src/protocols/drug_design/SubstituentReplaceCreator.hh @@ -0,0 +1,42 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/SubstituentReplaceCreator.hh +/// @brief Class for instantiating a SubstituentReplace +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_SubstituentReplaceCreator_HH +#define INCLUDED_protocols_drug_design_SubstituentReplaceCreator_HH + +// Package headers +#include +#include + +// Utility headers +#include + +// C++ headers +#include + +namespace protocols { +namespace drug_design { + +class SubstituentReplaceCreator : public protocols::chemistries::ChemistryCreator { +public: + protocols::chemistries::ChemistryOP create_chemistry() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & ) const override; +}; + + +} //namespace drug_design +} //namespace protocols + + +#endif diff --git a/source/src/protocols/drug_design/SubstructureReplace.cc b/source/src/protocols/drug_design/SubstructureReplace.cc new file mode 100644 index 0000000000..66e7cf89e2 --- /dev/null +++ b/source/src/protocols/drug_design/SubstructureReplace.cc @@ -0,0 +1,400 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/SubstructureReplace.hh +/// @brief use RDKit to replace a substructure in a ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include // For MolToSmiles +#include +#include +#include + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR("protocols.drug_design.SubstructureReplace"); + +//------------------------- Creator ----------------------------- + +protocols::chemistries::ChemistryOP +SubstructureReplaceCreator::create_chemistry() const { + return protocols::chemistries::ChemistryOP( new SubstructureReplace ); +} + +std::string +SubstructureReplaceCreator::keyname() const { + return SubstructureReplace::class_name(); +} + +void +SubstructureReplaceCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const { + SubstructureReplace::provide_xml_schema( xsd ); +} + +//------------------------- Chemistry ----------------------------- + +SubstructureReplace::SubstructureReplace(): + protocols::chemistries::Chemistry( class_name() ), + H_as_dummy_( false ), + V_as_dummy_( false ), + dist_threshold_(1.0) +{} + +/// @brief The file which contains the fragments to add to input residue type. +void +SubstructureReplace::substructure_database( std::string filename, bool append /*=false*/ ) { + if ( ! append ) { + substructures_.clear(); + } + + core::Size new_item_start( substructures_.size() + 1 ); + + core::chemical::rdkit::load_sdf( filename, substructures_, /*removeHs=*/ false ); + + if ( substructures_.size() < new_item_start ) { + TR.Warning << "No molecule fragments found in file " << filename << std::endl; + } + + for ( core::Size ii(new_item_start); ii <= substructures_.size(); ++ii ) { + // Do H/V mangling, if needed. + if ( H_as_dummy_ || V_as_dummy_ ) { + ::RDKit::RWMolOP mod_mol( new ::RDKit::RWMol( *substructures_[ii] ) ); + ::RDKit::AtomOP qatom( new ::RDKit::QueryAtom(0) ); + qatom->setQuery( ::RDKit::makeAtomNullQuery() ); + for ( unsigned int jj(0); jj < mod_mol->getNumAtoms(); ++jj ) { + if ( (H_as_dummy_ && mod_mol->getAtomWithIdx(jj)->getAtomicNum() == 1 ) || + (V_as_dummy_ && mod_mol->getAtomWithIdx(jj)->getAtomicNum() == 23 ) // Vanadium is 23 + ) { + mod_mol->replaceAtom( jj, qatom.get() ); // Atom will be copied. + } + } + substructures_[ii] = mod_mol; + } + // Check that dummy atoms are only bonded to one other atom. + utility::vector1< unsigned int > substruct_dummies; + find_dummies( *substructures_[ii], substruct_dummies ); + for ( core::Size jj(1); jj <= substruct_dummies.size(); ++jj ) { + ::RDKit::ROMol::OBOND_ITER_PAIR bond_itrs( substructures_[ii]->getAtomBonds( substructures_[ii]->getAtomWithIdx( substruct_dummies[jj] ) ) ); + if ( (bond_itrs.second - bond_itrs.first) != 1 ) { + TR.Error << "In file " << filename << " molecule '" << core::chemical::rdkit::get_name( *substructures_[ii] ) << ": "; + TR.Error << ::RDKit::MolToSmiles( *substructures_[ii] ) << std::endl; + TR.Error << "Dummy atom " << substruct_dummies[jj] << " has " << (bond_itrs.second - bond_itrs.first) << " bonds, should only be one." << std::endl; + utility_exit_with_message("Dummy atom in template structure has too many bonds."); + } + } + } + +} + +void +SubstructureReplace::apply( core::chemical::MutableResidueType & rsdtype ) +{ + + if ( substructures_.size() <= 1 ) { + utility_exit_with_message("Not enough substructures found for SubstructureReplace!"); + } + + // We need hydrogens as physical entities, as we may be swapping them. + core::chemical::rdkit::RestypeToRDMol to_converter(rsdtype, /*neutralize=*/ false, /*keep_hydro=*/ true); + ::RDKit::RWMolOP rdmol( to_converter.Mol() ); // Convert + + TR.Debug << "Number of atoms in ResType: " << rsdtype.natoms() << " in converted: " << rdmol->getNumAtoms() << std::endl; + + // Select location to do the replacement on + MoleculeSubstitutionOP template_molsub( pick_template( rdmol, substructures_ ) ); + + if ( ! template_molsub ) { + TR.Warning << "No suitable template structure found. Doing nothing." << std::endl; + mapping_.clear(); + mapping_.identity( true ); + set_last_status( core::chemical::modifications::FAIL_DO_NOT_RETRY ); + return; + } + + MoleculeSubstitutionOP replacement_molsub( pick_replacement( template_molsub, substructures_, dist_threshold_, property_name_ ) ); + + if ( ! replacement_molsub ) { + TR.Warning << "No suitable replacement structure found. Doing nothing." << std::endl; + mapping_.clear(); + mapping_.identity( true ); + set_last_status( core::chemical::modifications::FAIL_RETRY ); // RETRY, as a different template match might work. + return; + } + + TR.Debug << "Number of atoms in template: " << replacement_molsub->templt()->getNumAtoms() << " in replacement " << replacement_molsub->replace()->getNumAtoms() << std::endl; + + // Compute the transform from the template/replacement frame to the original molecule frame + ::RDKit::MatchVectType atom_mapping( replacement_molsub->make_match_vector() ); + RDGeom::Transform3D transform_replace_to_mol; + ::RDKit::MolAlign::getAlignmentTransform( *replacement_molsub->templt(), *replacement_molsub->mol(), + transform_replace_to_mol, -1, -1, &atom_mapping); // Needs raw pointer + + // Now build a new molecule + ::RDKit::RWMolOP new_mol( new ::RDKit::RWMol ); + new_mol->addConformer( new ::RDKit::Conformer(0) ); // Make new zero-atom conformer - RDKit takes ownership! + + // If there is any global-level information that would need to be copied, here's the place to do it. + + // Copy over atoms from the replacement + ::RDKit::ROMolOP replace( replacement_molsub->replace() ); + + for ( core::Size rdx(0); rdx < replace->getNumAtoms(); ++rdx ) { // 0 start! + // All replacement atoms should have atom sub entries + AtomSubstitution & atom_sub( replacement_molsub->substitution_for_rdx( rdx ) ); + if ( !replacement_molsub->rdx_is_dummy( rdx ) ) { + // Regular atom - transfer it + unsigned int ndx( new_mol->addAtom( replace->getAtomWithIdx( rdx ) ) ); + ::RDGeom::Point3D old_pos( replace->getConformer().getAtomPos(rdx) ); + new_mol->getConformer().setAtomPos( ndx, transform_replace_to_mol * old_pos ); // transformed position + atom_sub.set_ndx( ndx ); + } else if ( atom_sub.mdx() == AtomSubstitution::invalid_index ) { + // Stub we don't have matched with the original molecule - turn it into a hydrogen + unsigned int ndx( new_mol->addAtom( replace->getAtomWithIdx( rdx ) ) ); + new_mol->getAtomWithIdx(ndx)->setAtomicNum(1); + ::RDGeom::Point3D old_pos( replace->getConformer().getAtomPos(rdx) ); + new_mol->getConformer().setAtomPos( ndx, transform_replace_to_mol * old_pos ); // transformed position + atom_sub.set_ndx( ndx ); + } // else - a dummy atom that we're replacing with a sidechain + } + + // Copy over bonds from the replacement + for ( core::Size bnd(0); bnd < replace->getNumBonds(); ++bnd ) { // 0 start! + ::RDKit::Bond* r_bond( replace->getBondWithIdx(bnd) ); + unsigned int bgn_rdx( r_bond->getBeginAtomIdx() ); + unsigned int end_rdx( r_bond->getEndAtomIdx() ); + unsigned int bgn_ndx( replacement_molsub->substitution_for_rdx(bgn_rdx).ndx() ); + unsigned int end_ndx( replacement_molsub->substitution_for_rdx(end_rdx).ndx() ); + if ( bgn_ndx != AtomSubstitution::invalid_index && end_ndx != AtomSubstitution::invalid_index ) { + new_mol->addBond( bgn_ndx, end_ndx, r_bond->getBondType() ); + } + } + + // Now we need to copy over all the sidechains from the original molecule + // Sidechains are atoms that match a dummy atom in the replacement, and everything connected to it that doesn't match a template. + utility::vector1< unsigned int > const & template_dummies( replacement_molsub->template_dummies()); + utility::vector1< unsigned int > mdx_atoms_to_copy; + for ( core::Size ii(1); ii <= template_dummies.size(); ++ii ) { + AtomSubstitution & atom_sub( replacement_molsub->substitution_for_tdx( template_dummies[ii] ) ); + if ( atom_sub.mdx() != AtomSubstitution::invalid_index && atom_sub.rdx() != AtomSubstitution::invalid_index ) { + // Matches a replacement stub. + mdx_atoms_to_copy.push_back( atom_sub.mdx() ); + } + } + + for ( core::Size anum(1); anum <= mdx_atoms_to_copy.size(); ++anum ) { // Add to list while iterating - don't cache size! + // Copy atom. + unsigned int mdx( mdx_atoms_to_copy[ anum ] ); + AtomSubstitution & atom_sub( replacement_molsub->substitution_for_mdx( mdx ) ); + if ( atom_sub.ndx() != AtomSubstitution::invalid_index ) { // Already made a copy - skip it. + continue; + } + unsigned int ndx( new_mol->addAtom( rdmol->getAtomWithIdx( mdx ) ) ); + ::RDGeom::Point3D old_pos( rdmol->getConformer().getAtomPos(mdx) ); + new_mol->getConformer().setAtomPos( ndx, old_pos ); // not transformed! + atom_sub.set_ndx( ndx ); + + // Now add any bonded atoms. + for ( ::RDKit::ROMol::OBOND_ITER_PAIR bonds( rdmol->getAtomBonds( rdmol->getAtomWithIdx(mdx) ) ); + bonds.first != bonds.second; ++bonds.first ) { + unsigned int mdx_bnd( (*rdmol)[ *bonds.first ]->getOtherAtomIdx(mdx) ); + if ( replacement_molsub->substitution_for_mdx( mdx_bnd ).ndx() != AtomSubstitution::invalid_index // Already added + || replacement_molsub->substitution_for_mdx( mdx_bnd ).tdx() != AtomSubstitution::invalid_index ) { // matches the template + continue; + } + mdx_atoms_to_copy.push_back( mdx_bnd ); // Copy the bonded atom + } + } + + // for( core::Size mdx(0); mdx < rdmol->getNumAtoms(); ++mdx ) { // 0 start! + // // All mol atoms should have infos + // AtomSubstitution & atom_sub( replacement_molsub->substitution_for_mdx( mdx ) ); + // if( atom_sub.tdx() == AtomSubstitution::invalid_index || // Not matched to template - it's a sidechain + // (atom_sub.rdx() != AtomSubstitution::invalid_index && + // replacement_molsub->rdx_is_dummy( atom_sub.rdx() ) )// We match a dummy in the replacement + // ) { + // // Quick spot check for hydrogens which are bonded to the core, but not matched to dummies in the template - ignore these. + // if( rdmol->getAtomWithIdx( mdx )->getAtomicNum() == 1 ) { + // unsigned int mdx_bnd( get_bonded_atom( *rdmol, mdx ) ); // Should only be a single bond to hydrogen. + // unsigned int tdx_bnd( replacement_molsub->substitution_for_mdx( mdx_bnd ).tdx() ); + // if( tdx_bnd != AtomSubstitution::invalid_index && replacement_molsub->templt()->getAtomWithIdx(tdx_bnd)->getAtomicNum() != 0 ) { + // continue; // Ignoring hydrogen bonded to core, but not matched to template + // } + // } + // + // unsigned int ndx( new_mol->addAtom( rdmol->getAtomWithIdx( mdx ) ) ); + // ::RDGeom::Point3D old_pos( rdmol->getConformer().getAtomPos(mdx) ); + // debug_assert( atom_sub.ndx() == AtomSubstitution::invalid_index ); // We shouldn't already have an associated atom + // new_mol->getConformer().setAtomPos( ndx, old_pos ); // not transformed! + // atom_sub.set_ndx( ndx ); + // } // else // We're matched to the template without a correspondence to the replacement - ignore + // } + + // Copy over bonds from original molecule + for ( core::Size bnd(0); bnd < rdmol->getNumBonds(); ++bnd ) { // 0 start! + ::RDKit::Bond* m_bond( rdmol->getBondWithIdx(bnd) ); + unsigned int bgn_mdx( m_bond->getBeginAtomIdx() ); + unsigned int end_mdx( m_bond->getEndAtomIdx() ); + unsigned int bgn_ndx( replacement_molsub->substitution_for_mdx(bgn_mdx).ndx() ); + unsigned int end_ndx( replacement_molsub->substitution_for_mdx(end_mdx).ndx() ); + if ( bgn_ndx != AtomSubstitution::invalid_index && end_ndx != AtomSubstitution::invalid_index ) { + debug_assert( new_mol->getBondBetweenAtoms(bgn_ndx, end_ndx) == nullptr ); // Bond should not already exist + new_mol->addBond( bgn_ndx, end_ndx, m_bond->getBondType() ); + } + } + + // Okay, now need to bond the sidechains from the original molecule with the core of the new one. + // We're looking for t_dummies which are matched with r_dummies + //utility::vector1< unsigned int > const & template_dummies( replacement_molsub->template_dummies()); + for ( core::Size ii(1); ii <= template_dummies.size(); ++ii ) { + AtomSubstitution & atom_sub( replacement_molsub->substitution_for_tdx( template_dummies[ii] ) ); + if ( atom_sub.rdx() != AtomSubstitution::invalid_index && replacement_molsub->rdx_is_dummy( atom_sub.rdx() ) ) { + unsigned int bnd_rdx( get_bonded_atom( *replacement_molsub->replace(), atom_sub.rdx() ) ); + // We attach the core atom on the new replacement to the original equivalent of the template dummy + unsigned int ndx_orig( atom_sub.ndx() ); + unsigned int ndx_bnd( replacement_molsub->substitution_for_rdx(bnd_rdx).ndx() ); + ::RDKit::Bond::BondType bt( get_first_bondtype(*replacement_molsub->replace(), atom_sub.rdx()) ); + debug_assert( new_mol->getBondBetweenAtoms(ndx_orig, ndx_bnd) == nullptr ); // Bond should not already exist + new_mol->addBond( ndx_orig, ndx_bnd, bt ); + } + } + + TR.Debug << "Pre sanitation new molecule size " << new_mol->getNumAtoms() << std::endl; + // Now clean up the molecule. + try { + ::RDKit::MolOps::sanitizeMol(*new_mol); + } catch (::RDKit::MolSanitizeException &se){ + TR.Warning << "Cannot clean up molecule made by replacement - skipping." << std::endl; + mapping_.clear(); + mapping_.identity( true ); + set_last_status( core::chemical::modifications::FAIL_RETRY ); // RETRY as a different template/replacement might work + return; + } + + TR.Debug << "Post sanitation new molecule size " << new_mol->getNumAtoms() << std::endl; + + // Check to make sure everything got matched up correctly. (Rosetta can't handle disconnected Restypes.) + std::vector frag_mapping; + unsigned int num_frags( ::RDKit::MolOps::getMolFrags(*new_mol, frag_mapping) ); + if ( num_frags != 1 ) { + TR.Error << "When substituting " << ::RDKit::MolToSmiles( *rdmol ) << std::endl; + TR.Error << "with template " << ::RDKit::MolToSmiles( *replacement_molsub->templt() ) << std::endl; + TR.Error << "and replacement " << ::RDKit::MolToSmiles( *replacement_molsub->replace() ) << std::endl; + TR.Error << "disconnected molecule obtained: " << ::RDKit::MolToSmiles( *new_mol ) << std::endl; + utility_exit_with_message("SubstructureReplace made a molecule that was disconnected!"); + } + + // Minimize with somewhat loose tolerances to fix up bond geometries + ::RDKit::ForceFieldOP ff( core::chemical::rdkit::get_forcefield( *new_mol ) ); + if ( ff ) { + ff->minimize(200, 1e-2, 1e-4); + } else { + TR.Warning << "Cannot find appropriate forcefield for minimization - skipping min." << std::endl; + } + + // We need to find a mapping from the molecule to the post molecule + core::chemical::IndexIndexMapping sub_map( replacement_molsub->find_mdx_to_ndx_mapping() ); + + // Should be good. Now convert the residue into a Rosetta residue type. + + core::chemical::VDIndexMapping restype_prod_map( combine( to_converter.vd_to_index(), sub_map ) ); + + core::chemical::rdkit::RDMolToRestype from_converter(*new_mol); + from_converter.set_nbr( restype_prod_map[rsdtype.nbr_vertex()] ); + + core::chemical::MutableResidueTypeOP new_resop( from_converter.generate_restype(rsdtype,restype_prod_map) ); + + TR << "Replaced the core of " << rsdtype.name() << std::endl; + TR << "using template " << ::RDKit::MolToSmiles( *replacement_molsub->templt() ) << std::endl; + TR << "and replacement " << ::RDKit::MolToSmiles( *replacement_molsub->replace() ) << std::endl; + TR << "to convert " << ::RDKit::MolToSmiles( *replacement_molsub->mol() ) << std::endl; + TR << "to " << ::RDKit::MolToSmiles( *new_mol ) << std::endl; + + mapping_ = combine( restype_prod_map, from_converter.index_to_vd() ); + rsdtype = *new_resop; + mapping_ = combine( mapping_, combine( core::chemical::VDStringMapping(*new_resop), core::chemical::StringVDMapping(rsdtype)) ); + + // That's it, we're successful + set_last_status( core::chemical::modifications::SUCCESS ); + return; + +} + +core::chemical::VDVDMapping +SubstructureReplace::get_mapping() const { + return mapping_; +} + +std::string +SubstructureReplace::class_name() { + return "SubstructureReplace"; +} + +void +SubstructureReplace::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) { + using namespace utility::tag; + AttributeList attlist; + attlist + + XMLSchemaAttribute::required_attribute("substructures", xs_string, + "The name of the SDF file which contains the *aligned* substructures to swap.") + + XMLSchemaAttribute::attribute_w_default("weight_by_property", xs_string, + "When randomly picking the substructure from the file, weight by the given property from the SDF", "") + + XMLSchemaAttribute::attribute_w_default("distance_threshold", xsct_real, + "When replacing, how far apart can two attachment point atoms be and still be considered the same point", "") + + XMLSchemaAttribute::attribute_w_default("H_as_dummy", xsct_rosetta_bool, + "If true, use hydrogens in the input file as attachment points", "0") + + XMLSchemaAttribute::attribute_w_default("V_as_dummy", xsct_rosetta_bool, + "If true, use vanadium atoms in the input file as attachment points", "0"); + + protocols::chemistries::xsd_type_definition_w_attributes( + xsd, class_name(), + "Replace a given substructure of a ResidueType with another, grafting substituents.", + attlist ); +} + +/// @brief Initialize any data members of this instance from an input tag +/// and a DataMap object +void +SubstructureReplace::parse_my_tag( + utility::tag::TagCOP tag, + basic::datacache::DataMap &) +{ + // These need to be set prior to loading the database + H_as_dummy( tag->getOption("H_as_dummy", false) ); + V_as_dummy( tag->getOption("V_as_dummy", false) ); + + substructure_database( tag->getOption("substructures") ); + weight_by_property( tag->getOption("weight_by_property", "") ); + distance_threshold( tag->getOption("distance_threshold", 1.0) ); +} + + +} +} diff --git a/source/src/protocols/drug_design/SubstructureReplace.fwd.hh b/source/src/protocols/drug_design/SubstructureReplace.fwd.hh new file mode 100644 index 0000000000..431ee4f5de --- /dev/null +++ b/source/src/protocols/drug_design/SubstructureReplace.fwd.hh @@ -0,0 +1,33 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/SubstructureReplace.hh +/// @brief use RDKit to replace a substructure in a ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_SubstructureReplace_fwd_hh +#define INCLUDED_protocols_drug_design_SubstructureReplace_fwd_hh + +#include + +namespace protocols { +namespace drug_design { + + +class SubstructureReplace; // fwd declaration +typedef utility::pointer::shared_ptr< SubstructureReplace > SubstructureReplaceOP; +typedef utility::pointer::shared_ptr< SubstructureReplace const > SubstructureReplaceCOP; + + +} // namespace drug_design +} // namespace protocols + +#endif // INCLUDED_protocols_drug_design_SubstructureReplace_fwd_hh + + diff --git a/source/src/protocols/drug_design/SubstructureReplace.hh b/source/src/protocols/drug_design/SubstructureReplace.hh new file mode 100644 index 0000000000..f29c0fb49d --- /dev/null +++ b/source/src/protocols/drug_design/SubstructureReplace.hh @@ -0,0 +1,95 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/SubstructureReplace.hh +/// @brief use RDKit to replace a substructure in a ResidueType +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_SubstructureReplace_hh +#define INCLUDED_protocols_drug_design_SubstructureReplace_hh + +#include + +#include + +#include +#include + +#include + +#include + +#include + +namespace protocols { +namespace drug_design { + +class SubstructureReplace : public protocols::chemistries::Chemistry { +public: + SubstructureReplace(); + + void apply( core::chemical::MutableResidueType & ) override; + + /// @brief The file which contains the fragments to add to input residue type. + void substructure_database( std::string filename, bool append=false ); + + /// @brief The largest distance at which two dummy stubs on the fragments will be considered equivalent. + void distance_threshold( core::Real setting ) { dist_threshold_ = setting; } + core::Real distance_threshold() const { return dist_threshold_; } + + /// @brief Will hydrogens in the input be converted to dummy stubs? + void H_as_dummy( core::Real setting ) { H_as_dummy_ = setting; } + bool H_as_dummy() const { return H_as_dummy_; } + + /// @brief Will V atoms (Vanadium, but used commonly in Rosetta for "virtual" designations) + /// in the input be converted to dummy stubs? + void V_as_dummy( core::Real setting ) { V_as_dummy_ = setting; } + bool V_as_dummy() const { return V_as_dummy_; } + + /// @brief If not empty, use property weighting based on the given property. + void weight_by_property( std::string const & setting ) { property_name_ = setting; } + std::string const & weight_by_property() const { return property_name_; } + + /// @brief Initialize any data members of this instance from an input tag + /// and a DataMap object + void parse_my_tag( + utility::tag::TagCOP tag, + basic::datacache::DataMap & datacache + ) override; + + core::chemical::VDVDMapping + get_mapping() const override; + + static std::string class_name(); + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + +private: + + /// @brief The fragments to apply + utility::vector1< ::RDKit::ROMolOP > substructures_; + + /// @brief Do we consider Hydrogens to be dummy atoms? + bool H_as_dummy_; + /// @brief Do we consider V atoms to be dummy atoms? + bool V_as_dummy_; + + /// @brief The largest distance at which two dummy stubs on the fragments will be considered equivalent. + core::Real dist_threshold_; + + /// @brief If not empty, pick fragments based on the weighting by the given property. + std::string property_name_; + + core::chemical::VDVDMapping mapping_; + +}; + +} // namespace drug_design +} // namespace protocols + +#endif diff --git a/source/src/protocols/drug_design/SubstructureReplaceCreator.hh b/source/src/protocols/drug_design/SubstructureReplaceCreator.hh new file mode 100644 index 0000000000..7beea56a7d --- /dev/null +++ b/source/src/protocols/drug_design/SubstructureReplaceCreator.hh @@ -0,0 +1,41 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/SubstructureReplaceCreator.hh +/// @brief Class for instantiating a SubstructureReplace +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_SubstructureReplaceCreator_HH +#define INCLUDED_protocols_drug_design_SubstructureReplaceCreator_HH + +// Package headers +#include +#include + +#include + +// C++ headers +#include + +namespace protocols { +namespace drug_design { + +class SubstructureReplaceCreator : public protocols::chemistries::ChemistryCreator { +public: + protocols::chemistries::ChemistryOP create_chemistry() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & ) const override; +}; + + +} //namespace drug_design +} //namespace protocols + + +#endif diff --git a/source/src/protocols/drug_design/substitution_support.cc b/source/src/protocols/drug_design/substitution_support.cc new file mode 100644 index 0000000000..63a06e0010 --- /dev/null +++ b/source/src/protocols/drug_design/substitution_support.cc @@ -0,0 +1,540 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/substitution_support.cc +/// @brief use RDKit to substitute items based on matched templates +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include // For MolToSmiles + + +namespace protocols { +namespace drug_design { + +static basic::Tracer TR("protocols.drug_design.substructure_support"); + +::RDKit::Bond::BondType +get_first_bondtype( ::RDKit::ROMol const & mol, ::RDKit::Atom const * atom ) { + ::RDKit::ROMol::OBOND_ITER_PAIR bond_itrs( mol.getAtomBonds( atom ) ); + // We assume that each dummy atom is only bonded to a single other atom. + debug_assert( bond_itrs.first != bond_itrs.second ); + return mol[ *bond_itrs.first ]->getBondType(); +} + +::RDKit::Bond::BondType +get_first_bondtype( ::RDKit::ROMol const & mol, unsigned int idx ) { + return get_first_bondtype( mol, mol.getAtomWithIdx( idx ) ); +} + +::RDKit::Bond const & +get_first_bond( ::RDKit::ROMol const & mol, unsigned int idx ) { + ::RDKit::Atom const * atom( mol.getAtomWithIdx( idx ) ); + ::RDKit::ROMol::OBOND_ITER_PAIR bond_itrs( mol.getAtomBonds( atom ) ); + // We assume that each dummy atom is only bonded to a single other atom. + debug_assert( bond_itrs.first != bond_itrs.second ); + return *mol[ *bond_itrs.first ]; +} + +unsigned int +get_bonded_atom( ::RDKit::ROMol const & mol, unsigned int idx ) { + ::RDKit::Bond const & bond( get_first_bond( mol, idx) ); + return bond.getOtherAtomIdx( idx ); +} + +bool +is_dummy( ::RDKit::ROMol const & mol, unsigned int idx ) { + debug_assert( idx < mol.getNumAtoms() ); + return mol.getAtomWithIdx(idx)->getAtomicNum() == 0; +} + +void +find_dummies( ::RDKit::ROMol const & mol, utility::vector1< unsigned int > & dummy_list ) { + dummy_list.clear(); + for ( core::Size idx(0); idx < mol.getNumAtoms(); ++idx ) { // std::vector + if ( is_dummy( mol, idx) ) { + dummy_list.push_back( idx ); + } + } +} + +bool +bond_is_in_ring( ::RDKit::ROMol const & rdmol, unsigned int atm1, unsigned int atm2 ) { + if ( rdmol.getRingInfo()->isInitialized() ) { + ::RDKit::MolOps::findSSSR(rdmol); + } + debug_assert( rdmol.getBondBetweenAtoms(atm1, atm2) ); + unsigned int bond_idx( rdmol.getBondBetweenAtoms(atm1, atm2)->getIdx() ); + return rdmol.getRingInfo()->numBondRings( bond_idx ) != 0; +} + +std::map< unsigned int, unsigned int > +convert_matchvect_to_map( ::RDKit::MatchVectType const & pairings ) { + std::map< unsigned int, unsigned int > retval; + for ( core::Size ii(0); ii < pairings.size(); ++ii ) { // std::vector + int t_idx( pairings[ii].first ); + int m_idx( pairings[ii].second ); + retval[ m_idx ] = t_idx; + } + return retval; +} + +unsigned int +AtomSubstitution::idx(MoleculeSelection sele) const { + switch( sele ) { + case OriginalMol : + return mdx(); + case TemplateMol : + return tdx(); + case ReplaceMol : + return rdx(); + case NewMol : + return ndx(); + default : + utility_exit_with_message("Not a valid MoleculeSelection"); + return AtomSubstitution::invalid_index; + } +} + +void +AtomSubstitution::set_idx(MoleculeSelection sele, unsigned int setting) { + switch( sele ) { + case OriginalMol : + set_mdx(setting); + break; + case TemplateMol : + set_tdx(setting); + break; + case ReplaceMol : + set_rdx(setting); + break; + case NewMol : + set_ndx(setting); + break; + default : + utility_exit_with_message("Not a valid MoleculeSelection"); + } +} + +MoleculeSubstitution::MoleculeSubstitution( ::RDKit::ROMolOP mol ): + mol_(mol) +{ + // Add entries for all mol items + for ( core::Size mdx(0); mdx < mol->getNumAtoms(); ++mdx ) { + AtomSubstitutionOP atom_sub( new AtomSubstitution( mdx ) ); + atom_substitutions_.push_back( atom_sub ); + by_mdx_[ mdx ] = atom_sub; + } +} + +/// @brief Add a template to this MoleculeSubstitution. +void +MoleculeSubstitution::add_template(::RDKit::ROMolOP templt, ::RDKit::MatchVectType const & pairings ) +{ + templt_ = templt; + + // Set pairings (should have all template entries) + for ( core::Size ii(0); ii < pairings.size(); ++ii ) { // std::vector + int tdx( pairings[ii].first ); + int mdx( pairings[ii].second ); + // Should have the mdx entry already + debug_assert( by_mdx_.count( mdx ) == 1 ); + by_tdx_[ tdx ] = by_mdx_[ mdx ]; + by_tdx_[ tdx ]->set_tdx( tdx ); + } + + find_dummies( *templt_, template_dummies_ ); +} + +::RDKit::MatchVectType +MoleculeSubstitution::make_match_vector() const { + ::RDKit::MatchVectType match_vect; + for ( core::Size ii(1); ii <= atom_substitutions_.size(); ++ii ) { + if ( atom_substitutions_[ii]->mdx() != AtomSubstitution::invalid_index && + atom_substitutions_[ii]->tdx() != AtomSubstitution::invalid_index ) { + match_vect.push_back( std::pair( atom_substitutions_[ii]->tdx(), atom_substitutions_[ii]->mdx() ) ); + } + } + return match_vect; +} + +/// @brief Create a mapping from mdx to ndx +core::chemical::IndexIndexMapping +MoleculeSubstitution::find_mdx_to_ndx_mapping() const { + core::chemical::IndexIndexMapping retval( core::Size(-1), core::Size(-1) ); // Use -1 as invalid as zero is valid + for ( core::Size ii(1); ii <= atom_substitutions_.size(); ++ii ) { + if ( atom_substitutions_[ii]->mdx() != AtomSubstitution::invalid_index && + atom_substitutions_[ii]->ndx() != AtomSubstitution::invalid_index ) { + retval[ atom_substitutions_[ii]->mdx() ] = atom_substitutions_[ii]->ndx(); + } + } + return retval; +} + +/// @brief Make a *new* AtomSubstitution class with the replacement information +MoleculeSubstitutionOP +MoleculeSubstitution::add_replacement( + ::RDKit::ROMolOP replacement, + std::map< unsigned int, unsigned int > & r_to_t_mapping +) const { + // First we need to deep copy the MoleculeSubstitution + MoleculeSubstitutionOP new_molsub( new MoleculeSubstitution ); + new_molsub->mol_ = mol_; + new_molsub->templt_ = templt_; + new_molsub->template_dummies_ = template_dummies_; + for ( core::Size ii(1); ii <= atom_substitutions_.size(); ++ii ) { + AtomSubstitutionOP atomsub_copy( new AtomSubstitution( *atom_substitutions_[ii] ) ); + new_molsub->atom_substitutions_.push_back( atomsub_copy ); + new_molsub->by_mdx_[ atomsub_copy->mdx() ] = atomsub_copy; + new_molsub->by_tdx_[ atomsub_copy->tdx() ] = atomsub_copy; + } + + new_molsub->replace_ = replacement; + find_dummies( *replacement, new_molsub->replace_dummies_ ); + // Now make entries for each replacement atom, using existing items if possible. + for ( unsigned int rdx(0); rdx < replacement->getNumAtoms(); ++rdx ) { // std::vector + if ( r_to_t_mapping.count( rdx ) > 0 ) { + unsigned int tdx( r_to_t_mapping[ rdx ] ); + debug_assert( new_molsub->by_tdx_.count(tdx) ); + new_molsub->by_tdx_[ tdx ]->set_rdx( rdx ); + new_molsub->by_rdx_[ rdx ] = new_molsub->by_tdx_[ tdx ]; + } else { + AtomSubstitutionOP rdx_atomsub( new AtomSubstitution ); + rdx_atomsub->set_rdx( rdx ); + new_molsub->atom_substitutions_.push_back(rdx_atomsub); + new_molsub->by_rdx_[ rdx ] = rdx_atomsub; + } + } + + return new_molsub; +} + +::RDKit::ROMolOP +MoleculeSubstitution::get_romol( MoleculeSelection sele ) { + switch( sele ) { + case OriginalMol : + return mol(); + case TemplateMol : + return templt(); + case ReplaceMol : + return replace(); + case NewMol : + utility_exit_with_message("NewMol entry not set!"); + default : + utility_exit_with_message("Not a valid MoleculeSelection"); + } +} + +bool +MoleculeSubstitution::tdx_is_dummy( unsigned int tdx ) const { + return is_dummy( *templt_, tdx ); +} + +bool +MoleculeSubstitution::rdx_is_dummy( unsigned int rdx ) const { + return is_dummy( *replace_, rdx ); +} + +AtomSubstitution & +MoleculeSubstitution::substitution_for_idx( MoleculeSelection sele, unsigned int idx ) { + switch( sele ) { + case OriginalMol : + return substitution_for_mdx(idx); + case TemplateMol : + return substitution_for_tdx(idx); + case ReplaceMol : + return substitution_for_rdx(idx); + case NewMol : + utility_exit_with_message("ndx's aren't indexed!"); + default : + utility_exit_with_message("Not a valid MoleculeSelection"); + } +} + +AtomSubstitution & +MoleculeSubstitution::substitution_for_mdx( unsigned int idx ) { + debug_assert( by_mdx_.count( idx ) > 0 ); + return *by_mdx_[ idx ]; +} +AtomSubstitution & +MoleculeSubstitution::substitution_for_tdx( unsigned int idx ) { + debug_assert( by_tdx_.count( idx ) > 0 ); + return *by_tdx_[ idx ]; +} +AtomSubstitution & +MoleculeSubstitution::substitution_for_rdx( unsigned int idx ) { + debug_assert( by_rdx_.count( idx ) > 0 ); + return *by_rdx_[ idx ]; +} + +/// @brief Pick a template to use, return it and the atom pairing of template->rdmol +MoleculeSubstitutionOP +pick_template( + ::RDKit::ROMolOP rdmol, + utility::vector1< ::RDKit::ROMolOP > & templates, + bool dummy_only +) +{ + utility::vector1< ::RDKit::ROMolOP > matching_templates; + utility::vector1< ::RDKit::MatchVectType > pairings; + + TR.Trace << "Finding template for " << ::RDKit::MolToSmiles( *rdmol ) << std::endl; + for ( core::Size tt(1); tt <= templates.size(); ++tt ) { + std::vector< ::RDKit::MatchVectType > matches; + ::RDKit::SubstructMatch(*rdmol, *templates[tt], matches, /*uniquify=*/ false); // Want redundancy in orientation + core::Size nmatches(0); + for ( core::Size ii(0); ii < matches.size(); ++ii ) { // NOTE: std::vector iteration! + bool match_good( true ); + // Want to make sure we don't have any heavy atoms connected to the template by non-dummy means. + if ( dummy_only ) { + std::map< unsigned int, unsigned int > match_map( convert_matchvect_to_map( matches[ii] ) ); // molecule:template mapping + for ( unsigned int jj(0); jj < rdmol->getNumAtoms(); ++jj ) { // No guarantees that RDKit atoms are ordered + if ( match_map.count(jj) == 1 || rdmol->getAtomWithIdx(jj)->getAtomicNum() == 1 ) { + // Ignore atoms matched to the template, and hydrogens + continue; + } + // We're a heavy atom not matched to the template - we want to make sure we're not bonded to a non-dummy atom in the template. + for ( ::RDKit::ROMol::OBOND_ITER_PAIR bonds( rdmol->getAtomBonds( rdmol->getAtomWithIdx(jj) ) ); + bonds.first != bonds.second; ++bonds.first ) { + unsigned int other_jj( (*rdmol)[ *bonds.first ]->getOtherAtomIdx(jj) ); + if ( match_map.count(other_jj) && templates[tt]->getAtomWithIdx( match_map[other_jj])->getAtomicNum() != 0 ) { + match_good = false; + break; + } + } + if ( ! match_good ) break; + } + } + + if ( match_good ) { + ++nmatches; + matching_templates.push_back( templates[tt] ); + pairings.push_back( matches[ii] ); + } + } + TR.Trace << "Template " << tt << " -- " << ::RDKit::MolToSmiles( *templates[tt] ) << " -- has " << nmatches << " matches." << std::endl; + } + + TR.Debug << "Found " << matching_templates.size() << " substructures compatible with current molecule." << std::endl; + + if ( matching_templates.size() == 0 ) { + TR << "[NOTICE]: No matching templates found." << std::endl; + return MoleculeSubstitutionOP(); + } + + debug_assert( matching_templates.size() == pairings.size() ); + + core::Size picked( numeric::random::random_range( 1, matching_templates.size() ) ); + + MoleculeSubstitutionOP molsub( new MoleculeSubstitution( rdmol ) ); + molsub->add_template(matching_templates[ picked ], pairings[ picked ]); + return molsub; +} + +MoleculeSubstitutionOP +test_replacement( + MoleculeSubstitutionOP current_molsub, + ::RDKit::ROMolOP possible_replacement, + core::Real dist_threshold +) { + ::RDKit::ROMolOP mol( current_molsub->mol() ); + ::RDKit::ROMolOP templt( current_molsub->templt() ); + + if ( templt.get() == possible_replacement.get() ) { + // Replacement is the template - don't propose for replacement + return nullptr; + } + + ::RDKit::Conformer const & replace_conf( possible_replacement->getConformer() ); + ::RDKit::Conformer const & templt_conf( templt->getConformer() ); + + utility::vector1< unsigned int > const & template_dummies( current_molsub->template_dummies() ); + utility::vector1< unsigned int > replacement_dummies; + find_dummies( *possible_replacement, replacement_dummies ); + + std::map< unsigned int, unsigned int > r_to_t_mapping; + + // For each matched dummy, find the closest replacement dummy. + for ( core::Size ii(1); ii <= template_dummies.size(); ++ii ) { + unsigned int tdx( template_dummies[ii] ); + unsigned int mdx( current_molsub->substitution_for_tdx(tdx).mdx() ); + ::RDKit::Bond::BondType t_bt( get_first_bondtype( *current_molsub->templt(), tdx ) ); + ::RDGeom::Point3D const & t_pos( templt_conf.getAtomPos( tdx ) ); + unsigned int min_rdx = AtomSubstitution::invalid_index; + core::Real min_dist = dist_threshold; + for ( core::Size jj(1); jj <= replacement_dummies.size(); ++jj ) { + ::RDKit::Bond::BondType r_bt( get_first_bondtype( *possible_replacement, replacement_dummies[jj] ) ); + // The bond types of the two dummies need to match. + if ( r_bt != t_bt ) { continue; } + ::RDGeom::Point3D const & r_pos( replace_conf.getAtomPos( replacement_dummies[jj] ) ); + core::Real dist( (r_pos-t_pos).length() ); + if ( dist < min_dist ) { + min_dist = dist; + min_rdx = replacement_dummies[jj]; + } + } + // See if we have successfully found a match + if ( min_rdx == AtomSubstitution::invalid_index ) { + if ( mol->getAtomWithIdx( mdx )->getAtomicNum() <= 1 ) { + // Stub to remove is just a hydrogen - just ignore + continue; + } else { + // Can't find appropriate stub for heavyatom - the replacement won't work + return nullptr; + } + } else if ( r_to_t_mapping.count( min_rdx ) != 0 ) { + // We're doubling up on a replacement dummy + if ( current_molsub->mol()->getAtomWithIdx( mdx )->getAtomicNum() <= 1 ) { + // Current stub is just a hydrogen - just ignore it + continue; + } + unsigned int old_tdx( r_to_t_mapping[ min_rdx ] ); + if ( mol->getAtomWithIdx( current_molsub->substitution_for_tdx(old_tdx).mdx() )->getAtomicNum() > 1 ) { + // OOPS - we're trying to place two heavy atoms on this same item. + // (Theoretically we could match up with second best, but I'm not doing that now.) + return nullptr; + } else { + // We're heavy and the previous one is hydrogen - replace it + r_to_t_mapping[ min_rdx ] = tdx; + } + } else { + // First assigment to this valid rdx + r_to_t_mapping[ min_rdx ] = tdx; + } + } + + MoleculeSubstitutionOP replacement_molsub( current_molsub->add_replacement( possible_replacement, r_to_t_mapping ) ); + + // We should also fail if we have any unpaired replacement dummies which cannot be replaced with hydrogens. + utility::vector1< unsigned int > const & replace_dummies( replacement_molsub->replace_dummies() ); + for ( core::Size jj(1); jj <= replace_dummies.size(); ++jj ) { + unsigned int tdx( replacement_molsub->substitution_for_rdx( replace_dummies[jj] ).tdx() ); + if ( tdx == AtomSubstitution::invalid_index ) { + ::RDKit::Bond::BondType r_bt( get_first_bondtype( *possible_replacement, replace_dummies[jj] ) ); + if ( r_bt != ::RDKit::Bond::SINGLE ) { + // We can't replace a missing non-single bond with a hydrogen + return nullptr; + } + } + } + + return replacement_molsub; +} + +MoleculeSubstitutionOP +pick_replacement( + MoleculeSubstitutionOP current_molsub, + utility::vector1< ::RDKit::ROMolOP > & possible_replacements, + core::Real distance_threshold, + std::string weighting_property /*= ""*/ +) { + utility::vector1< MoleculeSubstitutionOP > replacements; + numeric::random::WeightedSampler replacements_sampler; + + for ( core::Size ii(1); ii <= possible_replacements.size(); ++ii ) { + TR.Trace << "Testing for match: possible replacement #" << ii << " -- " << ::RDKit::MolToSmiles( *possible_replacements[ii] ) << std::endl; + MoleculeSubstitutionOP replace_molsub( test_replacement( current_molsub, possible_replacements[ii], distance_threshold ) ); + if ( replace_molsub ) { + core::Real weight( 1.0 ); + if ( weighting_property.size() && possible_replacements[ii]->hasProp(weighting_property) ) { + weight = possible_replacements[ii]->getProp(weighting_property); + } + TR.Trace << "Possible replacement #" << ii << " is a match. " << std::endl; + replacements.push_back( replace_molsub ); + replacements_sampler.add_weight( weight ); + } + } + + if ( replacements.size() == 0 ) { + TR << "[NOTICE]: No replacements found." << std::endl; + return nullptr; + } + + // Pick a replacement from the possibilities + core::Size replacement_num( replacements_sampler.random_sample() ); + return replacements[ replacement_num ]; +} + +/// @brief Copies all atom and bonds attached to start in the source molecule to the new molecule. +/// If skip is a valid index, it will be supressed in the source molecule, and will not be counted for copying or for attachment purposes +/// The transform is applied to the coordinates of the atom to get the coordinates of the new atom. +void +copy_attached_atoms( MoleculeSubstitution & molsub, MoleculeSelection source, ::RDGeom::Transform3D const & transform, unsigned int start, unsigned int skip ) { + ::RDKit::ROMolOP source_mol( molsub.get_romol(source) ); + ::RDKit::RWMolOP new_mol( molsub.newmol() ); + + debug_assert( start < source_mol->getNumAtoms() ); + debug_assert( skip != start ); + + utility::vector1< unsigned int > atoms_to_copy; + atoms_to_copy.push_back( start ); + + for ( core::Size anum(1); anum <= atoms_to_copy.size(); ++anum ) { // Add to list while iterating - don't cache size! + // Copy atom. + unsigned int idx( atoms_to_copy[ anum ] ); + AtomSubstitution & atom_sub( molsub.substitution_for_idx( source, idx ) ); + if ( atom_sub.ndx() != AtomSubstitution::invalid_index ) { // Already made a copy - skip it. + continue; + } + unsigned int ndx( new_mol->addAtom( source_mol->getAtomWithIdx( idx ) ) ); + ::RDGeom::Point3D old_pos( source_mol->getConformer().getAtomPos( idx ) ); + new_mol->getConformer().setAtomPos( ndx, transform * old_pos ); + atom_sub.set_ndx( ndx ); + //TR << "Adding atom " << idx << " from molecule " << source << " as atom " << ndx << std::endl; + + // Now add any bonded atoms that aren't supressed. + // (We're non-ring, so we shouldn't cross that bond by another route.) + for ( ::RDKit::ROMol::OBOND_ITER_PAIR bonds( source_mol->getAtomBonds( source_mol->getAtomWithIdx( idx ) ) ); + bonds.first != bonds.second; ++bonds.first ) { + unsigned int idx_bnd( (*source_mol)[ *bonds.first ]->getOtherAtomIdx( idx ) ); + if ( idx_bnd == skip ) { // Don't follow trace through skipped atom. (Should never trigger for invalid entry skip + continue; + } + atoms_to_copy.push_back( idx_bnd ); // Copy the bonded atom + } + } + + // Convert to a set to uniquify and for fast lookup + std::set< unsigned int > copied_atoms(atoms_to_copy.begin(), atoms_to_copy.end()); + + for ( std::set< unsigned int>::const_iterator itr( copied_atoms.begin() ), itr_end( copied_atoms.end() ); + itr != itr_end; ++itr ) { + for ( ::RDKit::ROMol::OBOND_ITER_PAIR bonds( source_mol->getAtomBonds( source_mol->getAtomWithIdx( *itr ) ) ); + bonds.first != bonds.second; ++bonds.first ) { + unsigned int idx_bnd( (*source_mol)[ *bonds.first ]->getOtherAtomIdx( *itr ) ); + if ( copied_atoms.count( idx_bnd ) != 1 ) { // Don't make bonds to atoms we didn't copy. + continue; + } + unsigned int bgn_ndx( molsub.substitution_for_idx(source, *itr).ndx() ); + unsigned int end_ndx( molsub.substitution_for_idx(source, idx_bnd).ndx() ); + debug_assert( bgn_ndx != AtomSubstitution::invalid_index ); + debug_assert( end_ndx != AtomSubstitution::invalid_index ); + if ( new_mol->getBondBetweenAtoms(bgn_ndx, end_ndx) == nullptr ) { // Bond should not already exist { + //TR << "Adding bond between new atoms " << bgn_ndx << " and " << end_ndx << " ( was " << *itr << " -- " << idx_bnd << " ) " << std::endl; + new_mol->addBond( bgn_ndx, end_ndx, source_mol->getBondBetweenAtoms(*itr,idx_bnd)->getBondType() ); + } + } + } +} + +} +} diff --git a/source/src/protocols/drug_design/substitution_support.fwd.hh b/source/src/protocols/drug_design/substitution_support.fwd.hh new file mode 100644 index 0000000000..c17a26dab8 --- /dev/null +++ b/source/src/protocols/drug_design/substitution_support.fwd.hh @@ -0,0 +1,33 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/substituion_support.fwd.hh +/// @brief use RDKit to substitute items based on matched templates +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_substitution_support_fwd_hh +#define INCLUDED_protocols_drug_design_substitution_support_fwd_hh + +#include + +namespace protocols { +namespace drug_design { + +class AtomSubstitution; +typedef utility::pointer::shared_ptr< AtomSubstitution > AtomSubstitutionOP; +typedef utility::pointer::shared_ptr< AtomSubstitution const > AtomSubstitutionCOP; + +class MoleculeSubstitution; +typedef utility::pointer::shared_ptr< MoleculeSubstitution > MoleculeSubstitutionOP; +typedef utility::pointer::shared_ptr< MoleculeSubstitution const > MoleculeSubstitutionCOP; + +} +} + +#endif // Include guard diff --git a/source/src/protocols/drug_design/substitution_support.hh b/source/src/protocols/drug_design/substitution_support.hh new file mode 100644 index 0000000000..59b7a6aeb3 --- /dev/null +++ b/source/src/protocols/drug_design/substitution_support.hh @@ -0,0 +1,256 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file src/protocols/drug_design/substituion_support.hh +/// @brief use RDKit to substitute items based on matched templates +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_drug_design_substitution_support_hh +#define INCLUDED_protocols_drug_design_substitution_support_hh + +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +namespace protocols { +namespace drug_design { + +/// @brief Get the RDKit bond type of the first bond to atom +::RDKit::Bond::BondType +get_first_bondtype( ::RDKit::ROMol const & mol, ::RDKit::Atom const * atom ); + +/// @brief Get the RDKit bond type of the first bond to the given atom +::RDKit::Bond::BondType +get_first_bondtype( ::RDKit::ROMol const & mol, unsigned int idx ); + +/// @brief Get the RDKit bond of the first bond to the given atom +::RDKit::Bond const & +get_first_bond( ::RDKit::ROMol const & mol, unsigned int idx ); + +/// @brief Get the atom number of the atom bonded to idx (it's the first atom) +unsigned int +get_bonded_atom( ::RDKit::ROMol const & mol, unsigned int idx ); + +/// @brief Is the given atom a dummy (atom number 0)? +bool +is_dummy( ::RDKit::ROMol const & mol, unsigned int idx ); + +/// @brief Populate the list with the index of all the dummies in the molecule +/// @details Existing contents will be cleared. +void +find_dummies( ::RDKit::ROMol const & mol, utility::vector1< unsigned int > & dummy_list ); + +///// @brief Is the bond in a ring? +//bool +//bond_is_in_ring( ::RDKit::Bond const & bond ); + +/// @brief Is the bond between the atoms in a ring? +bool +bond_is_in_ring( ::RDKit::ROMol const & rdmol, unsigned int atm1, unsigned int atm2 ); + +/// @brief Convert the RDKit MatcVect into a map (real molecule index to query index) +std::map< unsigned int, unsigned int > +convert_matchvect_to_map( ::RDKit::MatchVectType const & pairings ); + +enum MoleculeSelection { + OriginalMol, + TemplateMol, + ReplaceMol, + NewMol +}; + +/// @brief A class representing a substitution of an atom from an original molecule +/// through a pair of matched templates to a substituted molecule. +/// Intended to be used through MoleculeSubstitution +class AtomSubstitution { + +public: + AtomSubstitution( + unsigned int mdx = invalid_index, + unsigned int tdx = invalid_index, + unsigned int rdx = invalid_index, + unsigned int ndx = invalid_index ): + mdx_(mdx), + tdx_(tdx), + rdx_(rdx), + ndx_(ndx) + {} + + unsigned int idx(MoleculeSelection sele) const; + + /// @brief The atom index in the original molecule + unsigned int mdx() const { return mdx_; } + /// @brief The atom index in the template which matches the original molecule + unsigned int tdx() const { return tdx_; } + /// @brief The atom index in the replacement template + unsigned int rdx() const { return rdx_; } + /// @brief The atom index in the new (post-replacement) molecule + unsigned int ndx() const { return ndx_; } + + void set_idx(MoleculeSelection sele, unsigned int setting); + + void set_mdx(unsigned int setting) { mdx_ = setting; } + void set_tdx(unsigned int setting) { tdx_ = setting; } + void set_rdx(unsigned int setting) { rdx_ = setting; } + void set_ndx(unsigned int setting) { ndx_ = setting; } + +public: + + static unsigned int const invalid_index=65500; // Near the top end of the range for unsigned int + +private: + + unsigned int mdx_; + unsigned int tdx_; + unsigned int rdx_; + unsigned int ndx_; + +}; + +/// @brief A class representing a substitution of a molecule from an original molecule +/// through a pair of matched templates to a substituted molecule. +/// @details The MoleculeSubstitution can be made progressively from molecule outwards. +/// It should contain an indexed entry for each item in all four molecules +/// (At least for those molecules which it's currently aware of.) +class MoleculeSubstitution { + +public: + + MoleculeSubstitution() + {} + + /// @brief Construct a MoleculeSubstitution from an RDKit molecule + /// (The source molecule.) + MoleculeSubstitution( ::RDKit::ROMolOP mol ); + + /// @brief Add a template to this MoleculeSubstitution. + void + add_template(::RDKit::ROMolOP templt, ::RDKit::MatchVectType const & pairings ); + + /// @brief Reconstitute the original pairings. + /// This is a vector of pairs of (tdx, mdx) + ::RDKit::MatchVectType + make_match_vector() const; + + /// @brief Create a mapping from mdx to ndx + core::chemical::IndexIndexMapping + find_mdx_to_ndx_mapping() const; + + /// @brief Make a *new* MoleculeSubstitution class with the replacement information + /// @details Assumes that the new (post-replacement) molecule hasn't been set. + MoleculeSubstitutionOP + add_replacement( + ::RDKit::ROMolOP replacement, + std::map< unsigned int, unsigned int > & r_to_t_mapping + ) const; + + void + add_newmol( ::RDKit::RWMolOP newmol) { newmol_ = newmol; } + + ::RDKit::ROMolOP get_romol( MoleculeSelection sele ); + + ::RDKit::ROMolOP mol() { return mol_; } + ::RDKit::ROMolOP templt() { return templt_; } + ::RDKit::ROMolOP replace() { return replace_; } + ::RDKit::RWMolOP newmol() { return newmol_; } + + AtomSubstitution & substitution_for_idx( MoleculeSelection sele, unsigned int idx ); + + AtomSubstitution & substitution_for_mdx( unsigned int idx ); + AtomSubstitution & substitution_for_tdx( unsigned int idx ); + AtomSubstitution & substitution_for_rdx( unsigned int idx ); + //AtomSubstitution & substitution_for_ndx( unsigned int idx ); + + bool tdx_is_dummy( unsigned int tdx ) const; + bool rdx_is_dummy( unsigned int rdx ) const; + + /// @brief The indexes for dummy atoms on the matching template + utility::vector1< unsigned int > const & + template_dummies() const { return template_dummies_; } + + /// @brief The indexes for dummy atoms on the replacement template + utility::vector1< unsigned int > const & + replace_dummies() const { return replace_dummies_; } + +private: + + ::RDKit::ROMolOP mol_; // Original molecule + ::RDKit::ROMolOP templt_; // Template + ::RDKit::ROMolOP replace_; // Replacement + ::RDKit::RWMolOP newmol_; // Replacement + + std::map< unsigned int, AtomSubstitutionOP > by_mdx_; + std::map< unsigned int, AtomSubstitutionOP > by_tdx_; + std::map< unsigned int, AtomSubstitutionOP > by_rdx_; + //std::map< unsigned int, AtomSubstitutionOP > by_ndx_; + + /// @brief What idx's correspond to dummy atoms? + utility::vector1< unsigned int > template_dummies_; + utility::vector1< unsigned int > replace_dummies_; + + /// @brief Storage for the individual atom substitutions + utility::vector1< AtomSubstitutionOP > atom_substitutions_; + +}; + +/// @brief Pick a template to use, return it and the atom pairing of template->rdmol +MoleculeSubstitutionOP +pick_template( + ::RDKit::ROMolOP rdmol, + utility::vector1< ::RDKit::ROMolOP > & templates, + bool dummy_only = true // Only match for templates where attached (non-hydrogen) atoms are connected through dummies. +); + +/// @brief Is the replacement molecule template compatible with the molecule and template in template_frag_infos +/// If the replacement is appropriate, the return value will be a *new* MoleculeSubstitution which +/// has the appropriate replacement entries set. If the replacement doesn't work, it will return a null pointer +/// @details current_molsub should have the original and template molecules set. +MoleculeSubstitutionOP +test_replacement( + MoleculeSubstitutionOP current_molsub, + ::RDKit::ROMolOP possible_replacement, + core::Real dist_threshold +); + +/// @brief Given the molecule and template info in template_frag_infos, +/// pick a possible replacement molecule from possible_replacements +/// @details current_molsub should have the original and template molecules set. +/// If multiple replacements are possible, one will be picked randomly, +/// based on the value of weighting_property in the replacement ROMol properties +MoleculeSubstitutionOP +pick_replacement( + MoleculeSubstitutionOP current_molsub, + utility::vector1< ::RDKit::ROMolOP > & possible_replacements, + core::Real distance_threshold, + std::string weighting_property = "" +); + +/// @brief Copies all atom and bonds attached to start in the source molecule to the new molecule. +/// If skip is a valid index, it will be supressed in the source molecule, and will not be counted for copying or for attachment purposes +/// The transform is applied to the coordinates of the atom to get the coordinates of the new atom. +void +copy_attached_atoms( MoleculeSubstitution & molsub, + MoleculeSelection source, + ::RDGeom::Transform3D const & transform, + unsigned int start, + unsigned int skip = AtomSubstitution::invalid_index ); + +} +} + +#endif // Include guard diff --git a/source/src/protocols/init/init.ChemistryCreators.ihh b/source/src/protocols/init/init.ChemistryCreators.ihh index 0b938ff036..6565505fb0 100644 --- a/source/src/protocols/init/init.ChemistryCreators.ihh +++ b/source/src/protocols/init/init.ChemistryCreators.ihh @@ -20,3 +20,12 @@ #include #include +#include +#include + +#include +#include +#include +#include +#include +#include diff --git a/source/src/protocols/init/init.ChemistryRegistrators.ihh b/source/src/protocols/init/init.ChemistryRegistrators.ihh index 28a66b9c93..a7e2fcdfac 100644 --- a/source/src/protocols/init/init.ChemistryRegistrators.ihh +++ b/source/src/protocols/init/init.ChemistryRegistrators.ihh @@ -20,4 +20,16 @@ using chemistries::ChemistryRegistrator; static ChemistryRegistrator< chemistries::PatchChemistryCreator > reg_PatchChemistryCreator; +static ChemistryRegistrator< chemistries::ReprotonateCreator > reg_ReprotonateChemistryCreator; + +static ChemistryRegistrator< rotamer_gen::FragmentRotamersCreator > FragmentRotamersCreator_registrator; +static ChemistryRegistrator< rotamer_gen::RDKitRotamersCreator > RDKitRotamersCreator_registrator; + +static ChemistryRegistrator< drug_design::RandomFragmentLigandCreator > RandomFragmentLigandCreator_registrator; +static ChemistryRegistrator< drug_design::ReactionGrowCreator > ReactionGrowCreator_registrator; +static ChemistryRegistrator< drug_design::ReactionFragmentCreator > ReactionFragmentCreator_registrator; +static ChemistryRegistrator< drug_design::ReactionMultiTransformCreator > ReactionMultiTransformCreator_registrator; +static ChemistryRegistrator< drug_design::SubstituentReplaceCreator > SubstituentReplaceCreator_registrator; +static ChemistryRegistrator< drug_design::SubstructureReplaceCreator > SubstructureReplaceCreator_registrator; + } //namespace protocols diff --git a/source/src/protocols/init/init.DataLoaderCreators.ihh b/source/src/protocols/init/init.DataLoaderCreators.ihh index 938bb49db3..a8c9f47ae3 100644 --- a/source/src/protocols/init/init.DataLoaderCreators.ihh +++ b/source/src/protocols/init/init.DataLoaderCreators.ihh @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff --git a/source/src/protocols/init/init.DataLoaderRegistrators.ihh b/source/src/protocols/init/init.DataLoaderRegistrators.ihh index 2fb54c3316..8c54593dbc 100644 --- a/source/src/protocols/init/init.DataLoaderRegistrators.ihh +++ b/source/src/protocols/init/init.DataLoaderRegistrators.ihh @@ -31,9 +31,11 @@ static DataLoaderRegistrator< ConstraintGeneratorLoaderCreator > reg_ConstraintG static DataLoaderRegistrator< SimpleMetricLoaderCreator > reg_SimpleMetricLoaderCreator; static DataLoaderRegistrator< chemistries::ChemistryLoaderCreator > reg_ChemistryLoaderCreator; + static DataLoaderRegistrator< ligand_docking::InterfaceBuilderLoaderCreator > reg_InterfaceBuilderLoaderCreator; static DataLoaderRegistrator< ligand_docking::MoveMapBuilderLoaderCreator > reg_MoveMapBuilderLoaderCreator; static DataLoaderRegistrator< ligand_docking::LigandAreaLoaderCreator > reg_LigandAreaLoaderCreator; + static DataLoaderRegistrator< loops::loops_definers::LoopsDefinerLoaderCreator > reg_LoopsDefinerLoaderCreator; } //namespace protocols diff --git a/source/src/protocols/init/init.FilterCreators.ihh b/source/src/protocols/init/init.FilterCreators.ihh index 3fdd691d7f..9c5cc1bc31 100644 --- a/source/src/protocols/init/init.FilterCreators.ihh +++ b/source/src/protocols/init/init.FilterCreators.ihh @@ -69,6 +69,12 @@ #include #include +#include +#include +#include +#include +#include + #include #include diff --git a/source/src/protocols/init/init.FilterRegistrators.ihh b/source/src/protocols/init/init.FilterRegistrators.ihh index fb9df3f89e..7d71adf3a1 100644 --- a/source/src/protocols/init/init.FilterRegistrators.ihh +++ b/source/src/protocols/init/init.FilterRegistrators.ihh @@ -93,6 +93,12 @@ static FilterRegistrator< protocols::denovo_design::filters::SSShapeComplementar static FilterRegistrator< protocols::hbnet::HBNetScoreFilterCreator > reg_HBNetScoreFilterCreator; static FilterRegistrator< protocols::helical_bundle::BundleReporterFilterCreator > reg_BundleReporterFilterCreator; +static FilterRegistrator< protocols::drug_design::AtomExistsFilterCreator > reg_AtomExistsFilterCreator; +static FilterRegistrator< protocols::drug_design::HeteroHeteroBondFilterCreator > reg_HeteroHeteroBondFilterCreator; +static FilterRegistrator< protocols::drug_design::NChiFilterCreator > reg_NChiFilterCreator; +static FilterRegistrator< protocols::drug_design::RDKitMetricFilterCreator > reg_RDKitMetricFilterCreator; +static FilterRegistrator< protocols::drug_design::SAScoreFilterCreator > reg_SAScoreFilterCreator; + static FilterRegistrator< protocols::indexed_structure_store::filters::FragmentLookupFilterCreator > reg_FragmentLookupFilterCreator; static FilterRegistrator< protocols::ligand_docking::AtomCountFilterCreator > reg_AtomCountFilterCreator; diff --git a/source/src/protocols/init/init.MoverCreators.ihh b/source/src/protocols/init/init.MoverCreators.ihh index 69c6e7cb2b..a55db22d96 100644 --- a/source/src/protocols/init/init.MoverCreators.ihh +++ b/source/src/protocols/init/init.MoverCreators.ihh @@ -140,6 +140,9 @@ #include #include +#include +#include +#include #include #include diff --git a/source/src/protocols/init/init.MoverRegistrators.ihh b/source/src/protocols/init/init.MoverRegistrators.ihh index 0dcf8d1eab..5f2c2e42c0 100644 --- a/source/src/protocols/init/init.MoverRegistrators.ihh +++ b/source/src/protocols/init/init.MoverRegistrators.ihh @@ -158,8 +158,9 @@ static MoverRegistrator< docking::EnsureExclusivelySharedJumpMoverCreator > reg_ static MoverRegistrator< docking::membrane::MPDockingMoverCreator > reg_MPDockingMoverCreator; static MoverRegistrator< docking::membrane::MPDockingSetupMoverCreator > reg_MPDockingSetupMoverCreator; -static MoverRegistrator< drug_design::ApplyChemistryMoverCreator > reg_ApplyChemistryMoverCreator; static MoverRegistrator< drug_design::bcl::BCLFragmentMutateMoverCreator > reg_BCLFragmentMutateMoverCreator; + +static MoverRegistrator< drug_design::DrugDesignMoverCreator > reg_DrugDesignMoverCreator; static MoverRegistrator< drug_design::RDKitMetricsMoverCreator > reg_RDKitMetricsMoverCreator; // E ////////////////////////////////////////////////////////////////////////// diff --git a/source/src/protocols/rotamer_gen/FragmentRotamers.cc b/source/src/protocols/rotamer_gen/FragmentRotamers.cc new file mode 100644 index 0000000000..7de13ce187 --- /dev/null +++ b/source/src/protocols/rotamer_gen/FragmentRotamers.cc @@ -0,0 +1,170 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + + +#ifdef USEBCL +#include +#include +#include +#include +#include +//#include +#include + +#endif + +namespace protocols { +namespace rotamer_gen { + +static basic::Tracer TR("protocol.rotamer_gen.FragmentRotamers"); + +///////////////////////////////////////////// + +std::string +FragmentRotamersCreator::keyname() const { + return FragmentRotamers::class_name(); +} + +protocols::chemistries::ChemistryOP +FragmentRotamersCreator::create_chemistry() const { + return protocols::chemistries::ChemistryOP( new FragmentRotamers ); +} + +void +FragmentRotamersCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const { + FragmentRotamers::provide_xml_schema( xsd ); +} + +//////////////////////////////////////////// + +void FragmentRotamers::apply(core::chemical::MutableResidueType & rsdtype) { + +#ifndef USEBCL + TR.Error << "Cannot generate BCL Fragment rotamers for residue type " << rsdtype.name() << std::endl; + core::chemical::bcl::require_bcl(); +#else + //because fragment generation takes awhile, we only really want to do this once. If we have created + //rotamers for this residuetype already, we should probably not do it again. + //But if you're calling this Chemistry explicitly, you probably mean to + if( rsdtype.rotamer_library_specification() ) { + TR << "Resetting rotamers for " << rsdtype.name() << " using the BCL." << std::endl; + } + std::clock_t begin = std::clock(); + + core::chemical::bcl::BCLFragmentHandler frag_handler; + ::bcl::chemistry::FragmentComplete fragment = frag_handler.restype_to_fragment( rsdtype ); + + core::chemical::IndexVDMapping bcl_to_rosetta( frag_handler.get_vd_to_index().reverse() ); + + if(!conformation_sampler_.IsDefined()){ + ::bcl::chemistry::RotamerLibraryFile rotamer_lib_file; + rotamer_lib_file.AssertRead( ::bcl::util::ObjectDataLabel( "(number_of_files=100)")); + // The default "SymmetryRMSD" comparer is *phenomenally* slow for some residues. Use an alternative comparer to get reasonable speeds. + conformation_sampler_ = ::bcl::util::ShPtr< ::bcl::chemistry::SampleConformations>( new ::bcl::chemistry::SampleConformations( rotamer_lib_file, "DihedralBins", /*tolerance*/ 1, /*number_conf*/ 100, /*max_iter*/ 200, /*change_chirality*/ false ) ); + } + + ::bcl::storage::Pair< ::bcl::chemistry::FragmentEnsemble, ::bcl::chemistry::FragmentEnsemble> output( conformation_sampler_->operator()(fragment)); + + //use the conformation sampler to create all the fragments! + ::bcl::chemistry::FragmentEnsemble &all_fragments( output.First()); + + core::chemical::rotamers::StoredRotamerLibrarySpecificationOP rotamers_spec( new core::chemical::rotamers::StoredRotamerLibrarySpecification() ); + + //This is where we convert the ensemble of rotamers created in the BCL to residues (rotamers) for Rosetta + for + ( + ::bcl::storage::List< ::bcl::chemistry::FragmentComplete>::iterator itr_mols( all_fragments.Begin()), itr_mols_end( all_fragments.End()); + itr_mols != itr_mols_end; + ++itr_mols + ) + { + std::map< std::string, core::Vector > single_rotamer_spec; + + ::bcl::util::SiPtrVector< const ::bcl::linal::Vector3D> atom_coords( itr_mols->GetAtomCoordinates()); + + //keep track of what the atom number we are on + core::Size i=0; + + for + ( + ::bcl::util::SiPtrVector< const ::bcl::linal::Vector3D>::const_iterator itr_coords( atom_coords.Begin()), itr_coords_end( atom_coords.End()); + itr_coords != itr_coords_end; + ++itr_coords, ++i + ) + { + //convert BCL fragment index base to Rosetta's VD based atoms + //This relies on the fact that the BCL manipulation doesn't change indicies + core::chemical::VD vd = bcl_to_rosetta[i]; + + //get the xyz coordinates + core::Real x, y, z; + x= ( *itr_coords)->X(); + y = ( *itr_coords)->Y(); + z = ( *itr_coords)->Z(); + + //set the xyz coordinates in Rosetta + single_rotamer_spec[ rsdtype.atom(vd).name() ] = core::Vector( x, y, z ); + } + rotamers_spec->add_rotamer( single_rotamer_spec ); + } + + rsdtype.rotamer_library_specification( rotamers_spec ); + + TR << " number of rotamers created for "<< rsdtype.name() << ": " << rotamers_spec->coordinates().size() << std::endl; + + TR << "Rotamer generation took: " << double(std::clock()-begin)/ CLOCKS_PER_SEC << "s total" << std::endl; + +#endif + + // Unless we crash, we always succeed, so there isn't a reason to change the success code. +} + +std::string +FragmentRotamers::class_name() { + return "FragmentRotamers"; +} + +void +FragmentRotamers::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) { + utility::tag::AttributeList attributes; + protocols::chemistries::xsd_type_definition_w_attributes( + xsd, class_name(), + "The FragmentRotamers chemistry will call the BCL to generate a rotameric library " + "for the ResidueType using the protocol of Kothiwale et al. (doi:10.1186/s13321-015-0095-1) " + "and attach it to the ResidueType. " + "Attempting to use this will result in a runtime error unless Rosetta was successfully compiled with `extras=bcl`.", + attributes ); +} + +void +FragmentRotamers::parse_my_tag( + utility::tag::TagCOP, + basic::datacache::DataMap &) +{ + // Previous settings no longer used - ignore all. +} + + +} +} diff --git a/source/src/protocols/rotamer_gen/FragmentRotamers.fwd.hh b/source/src/protocols/rotamer_gen/FragmentRotamers.fwd.hh new file mode 100644 index 0000000000..2a8bde7774 --- /dev/null +++ b/source/src/protocols/rotamer_gen/FragmentRotamers.fwd.hh @@ -0,0 +1,29 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + + +/// @author Steven Combs +/// + +#ifndef INCLUDED_protocols_rotamer_gen_FragmentRotamers_fwd_hh +#define INCLUDED_protocols_rotamer_gen_FragmentRotamers_fwd_hh + +#include +#include + +namespace protocols { +namespace rotamer_gen { + +class FragmentRotamers; +typedef utility::pointer::shared_ptr< FragmentRotamers > FragmentRotamersOP; + +} // namespace rotamer_gen +} // namespace protocols + +#endif diff --git a/source/src/protocols/rotamer_gen/FragmentRotamers.hh b/source/src/protocols/rotamer_gen/FragmentRotamers.hh new file mode 100644 index 0000000000..931f9e2b95 --- /dev/null +++ b/source/src/protocols/rotamer_gen/FragmentRotamers.hh @@ -0,0 +1,69 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/rotamer_gen/FragmentRotamers.hh +/// @brief generate rotamers for a residuetype +/// @author Steven Combs + +#ifndef INCLUDED_protocols_rotamer_gen_FragmentRotamers_hh +#define INCLUDED_protocols_rotamer_gen_FragmentRotamers_hh + +// Unit Headers +#include +#include + +//// Scripter Headers +#include +#include +#include +#include + +#ifdef USEBCL +#include +#endif + +/////////////////////////////////////////////////////////////////////// + +namespace protocols { +namespace rotamer_gen { + + +class FragmentRotamers: public protocols::chemistries::Chemistry +{ +public: + + FragmentRotamers(): + Chemistry(class_name()) + {} + + void + parse_my_tag( + utility::tag::TagCOP, + basic::datacache::DataMap &) override; + + void + apply(core::chemical::MutableResidueType & restype) override; + + // We can use the identity mapping for get_mapping() + + static std::string class_name(); + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + +private: + +#ifdef USEBCL +::bcl::util::ShPtr< ::bcl::chemistry::SampleConformations> conformation_sampler_; +#endif + +}; // class FragmentRotamers + +} //namespace rotamer_gen +} //namespace protocols + +#endif diff --git a/source/src/protocols/rotamer_gen/FragmentRotamersCreator.hh b/source/src/protocols/rotamer_gen/FragmentRotamersCreator.hh new file mode 100644 index 0000000000..1bd7d89618 --- /dev/null +++ b/source/src/protocols/rotamer_gen/FragmentRotamersCreator.hh @@ -0,0 +1,33 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_rotamer_gen_FragmentRotamersCreator_hh +#define INCLUDED_protocols_rotamer_gen_FragmentRotamersCreator_hh + +#include +#include +#include + +namespace protocols { +namespace rotamer_gen { + +class FragmentRotamersCreator : public protocols::chemistries::ChemistryCreator { +public: + protocols::chemistries::ChemistryOP create_chemistry() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & ) const override; +}; + +} +} + + +#endif /* FRAGMENTROTAMERSCREATOR_HH_ */ diff --git a/source/src/protocols/rotamer_gen/RDKitRotamers.cc b/source/src/protocols/rotamer_gen/RDKitRotamers.cc new file mode 100644 index 0000000000..d98f90c7d7 --- /dev/null +++ b/source/src/protocols/rotamer_gen/RDKitRotamers.cc @@ -0,0 +1,168 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +namespace protocols { +namespace rotamer_gen { + +static basic::Tracer TR("protocol.rotamer_gen.RDKitRotamers"); + +///////////////////////////////////////////// + +std::string +RDKitRotamersCreator::keyname() const { + return RDKitRotamers::class_name(); +} + +protocols::chemistries::ChemistryOP +RDKitRotamersCreator::create_chemistry() const { + return protocols::chemistries::ChemistryOP( new RDKitRotamers ); +} + +void +RDKitRotamersCreator::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) const { + RDKitRotamers::provide_xml_schema( xsd ); +} + +//////////////////////////////////////////// + +void RDKitRotamers::apply(core::chemical::MutableResidueType & rsdtype) { + + //because fragment generation takes awhile, we only really want to do this once. If we have created + //rotamers for this residuetype already, we should probably not do it again. + //But if you're calling this Chemistry explicitly, you probably mean to + if ( rsdtype.rotamer_library_specification() ) { + TR << "Resetting rotamers for " << rsdtype.name() << " using RDKit." << std::endl; + } + + // nconf logic adapted from Jean-Paul Ebejer's presentation + // at the London RDKit User General Meeting + // http://rdkit.org/UGM/2012/Ebejer_20110926_RDKit_1stUGM.pdf + core::Size nconf( 50 ); + if ( rsdtype.nchi() > 12 ) { + nconf = 300; + } else if ( rsdtype.nchi() >= 8 ) { + nconf = 200; + } + + core::chemical::rotamers::StoredRotamerLibrarySpecificationOP rotamers_spec( new core::chemical::rotamers::StoredRotamerLibrarySpecification() ); + + rotamers_spec->set_rotamers( generate_conformers( rsdtype, nconf ) ); + + rsdtype.rotamer_library_specification( rotamers_spec ); + + // Unless we crash, we always succeed, so there isn't a reason to change the success code. +} + +utility::vector1< std::map< std::string, core::Vector > > +RDKitRotamers::generate_conformers( core::chemical::MutableResidueType const & rsdtype, core::Size nconf, bool minimize ) { + + utility::vector1< std::map< std::string, core::Vector > > retval; + + std::clock_t begin = std::clock(); + + core::chemical::rdkit::RestypeToRDMol to_rdmol(rsdtype, /*neutralize = */ false, /*keep_hydro=*/ true); + ::RDKit::RWMOL_SPTR rdmol( to_rdmol.Mol() ); + + if ( !rdmol ) { + TR.Warning << "Can't convert restype " << rsdtype.name() << " to rdmol - skipping RDKit conformer generation." << std::endl; + return retval; + } + + core::chemical::VDIndexMapping const & rosetta_to_rdkit( to_rdmol.vd_to_index() ); + + + //::RDKit::MolOps::addHs( *rdmol, false, true ); // Not needed, because we keep them from above. + + // Generate the rotamers + ::RDKit::INT_VECT conf_ids; // List of generated conformations + ::RDKit::DGeomHelpers::EmbedMultipleConfs(*rdmol, conf_ids, nconf, + /*numThreads*/ 1, /*maxIterations*/ 30, /*seed*/ 11111, /*clearConfs*/ true, /*useRandomCoords*/ false, /*boxSizeMult*/ 2.0, // We use a fixed seed such that conformer generation is repeatable. + /*randNegEig*/ true, /*numZeroFail*/ 1, /*pruneRmsThresh*/ -1.0, /*coordMap*/ nullptr, /*optimizerForceTol*/ 1e-3, /*ignoreSmoothingFailures*/ false, + /*enforceChirality*/ true, /*useExpTorsionAnglePrefs*/ true, /*useBasicKnowledge*/ true // These are the ones we're changing + ); + + TR << "Embedding took " << double(std::clock()-begin)/ CLOCKS_PER_SEC << "s " << std::endl; + + if ( minimize ) { + std::vector< std::pair > status; // Output status for each conf + ::RDKit::UFF::UFFOptimizeMoleculeConfs(*rdmol, status); + //(We ignore the outputs status, because we're lazy.) + TR << "Embedding && minimization took " << double(std::clock()-begin)/ CLOCKS_PER_SEC << "s " << std::endl; + } + + // Now convert the coordinates to rotamers for Rosetta + for ( core::Size ii(0); ii < conf_ids.size(); ++ii ) { + + ::RDKit::Conformer const & conf( rdmol->getConformer(conf_ids[ii]) ); + std::map< std::string, core::Vector > single_rotamer_spec; + + for ( core::chemical::VD atm: rsdtype.all_atoms() ) { + if ( rosetta_to_rdkit[ atm ] != rosetta_to_rdkit.invalid_entry() ) { + ::RDGeom::Point3D const & pos( conf.getAtomPos( rosetta_to_rdkit[ atm ] ) ); + //set the xyz coordinates in Rosetta + single_rotamer_spec[ rsdtype.atom_name(atm) ] = core::Vector( pos.x, pos.y, pos.z ); + } + } + + retval.push_back( single_rotamer_spec ); + } + + TR << "Generated " << retval.size() << " rotamers for " << rsdtype.name() << " in " << double(std::clock()-begin)/ CLOCKS_PER_SEC << "s total." << std::endl; + + return retval; +} + +std::string +RDKitRotamers::class_name() { + return "RDKitRotamers"; +} + +void +RDKitRotamers::provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ) { + utility::tag::AttributeList attributes; + protocols::chemistries::xsd_type_definition_w_attributes( + xsd, class_name(), + "The RDKitRotamers chemistry will use the conformer generation protocol of " + "Ebejer et al. (doi:10.1021/ci2004658) to generate a rotameric library " + "and attach it to the given ResidueType.", + attributes ); +} + +void +RDKitRotamers::parse_my_tag( + utility::tag::TagCOP, + basic::datacache::DataMap &) +{ + // Previous settings no longer used - ignore all. +} + + +} +} diff --git a/source/src/protocols/rotamer_gen/RDKitRotamers.fwd.hh b/source/src/protocols/rotamer_gen/RDKitRotamers.fwd.hh new file mode 100644 index 0000000000..c5680f9d31 --- /dev/null +++ b/source/src/protocols/rotamer_gen/RDKitRotamers.fwd.hh @@ -0,0 +1,30 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + + +/// @author Rocco Moretti (rmorettiase@gmail.com) +/// + +#ifndef INCLUDED_protocols_rotamer_gen_RDKitRotamers_fwd_hh +#define INCLUDED_protocols_rotamer_gen_RDKitRotamers_fwd_hh + +#include +#include + +namespace protocols { +namespace rotamer_gen { + + +class RDKitRotamers; +typedef utility::pointer::shared_ptr< RDKitRotamers > RDKitRotamersOP; + +} // namespace rotamer_gen +} // namespace protocols + +#endif diff --git a/source/src/protocols/rotamer_gen/RDKitRotamers.hh b/source/src/protocols/rotamer_gen/RDKitRotamers.hh new file mode 100644 index 0000000000..9462c5a6e1 --- /dev/null +++ b/source/src/protocols/rotamer_gen/RDKitRotamers.hh @@ -0,0 +1,66 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/rotamer_gen/RDKitRotamers.hh +/// @brief generate rotamers for a ResidueType using RDKit +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_rotamer_gen_RDKitRotamers_hh +#define INCLUDED_protocols_rotamer_gen_RDKitRotamers_hh + +// Unit Headers +#include +#include + +//// Scripter Headers +#include +#include +#include +#include + +#include + +/////////////////////////////////////////////////////////////////////// + +namespace protocols { +namespace rotamer_gen { + +class RDKitRotamers: public protocols::chemistries::Chemistry +{ +public: + + RDKitRotamers(): + Chemistry(class_name()) + {} + + void + parse_my_tag( + utility::tag::TagCOP, + basic::datacache::DataMap &) override; + + void + apply(core::chemical::MutableResidueType & restype) override; + + // We can use the identity mapping for get_mapping() + + static std::string class_name(); + static void provide_xml_schema( utility::tag::XMLSchemaDefinition & xsd ); + + static + utility::vector1< std::map< std::string, core::Vector > > + generate_conformers( core::chemical::MutableResidueType const & rsdtype, core::Size nconf, bool minimize=false ); +private: + +}; // class RDKitRotamers + + +} //namespace rotamer_gen +} //namespace protocols + +#endif diff --git a/source/src/protocols/rotamer_gen/RDKitRotamersCreator.hh b/source/src/protocols/rotamer_gen/RDKitRotamersCreator.hh new file mode 100644 index 0000000000..2baa7ced63 --- /dev/null +++ b/source/src/protocols/rotamer_gen/RDKitRotamersCreator.hh @@ -0,0 +1,33 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @author Rocco Moretti (rmorettiase@gmail.com) + +#ifndef INCLUDED_protocols_rotamer_gen_RDKitRotamersCreator_hh +#define INCLUDED_protocols_rotamer_gen_RDKitRotamersCreator_hh + +#include +#include + +#include + +namespace protocols { +namespace rotamer_gen { + +class RDKitRotamersCreator : public protocols::chemistries::ChemistryCreator { +public: + protocols::chemistries::ChemistryOP create_chemistry() const override; + std::string keyname() const override; + void provide_xml_schema( utility::tag::XMLSchemaDefinition & ) const override; +}; + +} +} + +#endif diff --git a/source/src/protocols_h.5.src.settings b/source/src/protocols_h.5.src.settings index 284c2c1ee0..cd1a056948 100644 --- a/source/src/protocols_h.5.src.settings +++ b/source/src/protocols_h.5.src.settings @@ -55,6 +55,22 @@ sources = { "RDKitMetric", "RDKitMetricsMover", "util", + + "AtomExistsFilter", + "DrugDesignMover", + "DrugPolishMover", + "HeteroHeteroBondFilter", + "NChiFilter", + "RandomFragmentLigand", + "RDKitMetricFilter", + "ReactionChemistry", + "ReactionFragment", + "ReactionGrow", + "ReactionMultiTransform", + "SAScoreFilter", + "SubstituentReplace", + "SubstructureReplace", + "substitution_support", ], "protocols/drug_design/bcl": [ "BCLFragmentBaseMover", diff --git a/source/test/core.test.settings b/source/test/core.test.settings index 1ecc2b4267..3ca16dd035 100644 --- a/source/test/core.test.settings +++ b/source/test/core.test.settings @@ -52,6 +52,8 @@ sources = { "GasteigerAtomTyper", ], "chemical/modifications" :[ + "Reprotonate", + "ValenceHandler", ], "chemical/rdkit" : [ "RDMolToRestype", @@ -793,6 +795,7 @@ testinputfiles = [ "chemical/gasteiger/THI.params", "chemical/mm_atom_properties.txt", "chemical/modifications/foldit_ligand.params", + "chemical/modifications/test_in.pdb", "chemical/modifications/1p1m0.params", "chemical/modifications/1p1m1.params", "chemical/modifications/1p2m0.params", @@ -984,6 +987,12 @@ testinputfiles = [ "chemical/params/U32.cen.params", "chemical/params/U33.cen.params", "chemical/params/U34.cen.params", + "chemical/rdkit/ATP.sdf", + "chemical/rdkit/benzoate.sdf", + "chemical/rdkit/nitro.sdf", + "chemical/rdkit/nmethylpyridine.sdf", + "chemical/rdkit/purine.sdf", + "chemical/rdkit/pyridineNoxide.sdf", "chemical/orbitals/orbital_properties.txt", "chemical/rdkit/ATP.sdf", "chemical/rdkit/benzoate.sdf", diff --git a/source/test/core/chemical/ResidueGraphTypesTests.cxxtest.hh b/source/test/core/chemical/ResidueGraphTypesTests.cxxtest.hh index f482751aca..978d714fab 100644 --- a/source/test/core/chemical/ResidueGraphTypesTests.cxxtest.hh +++ b/source/test/core/chemical/ResidueGraphTypesTests.cxxtest.hh @@ -184,4 +184,36 @@ public: } + void test_vertex_index(){ + core::chemical::ResidueGraph graph; + utility::vector0 storage; + for ( core::Size i=0; i<= 9; ++i ) { + storage.push_back( graph.add_vertex()); + } + + for ( core::Size i=0; i<= 9; ++i ) { + TS_ASSERT_EQUALS(i, boost::get(boost::vertex_index, graph, storage[i])); + } + + graph.remove_vertex(storage[5]); + graph.remove_vertex(storage[6]); + + core::chemical::regenerate_graph_vertex_index(graph); + core::chemical::VIter itr, end; + + core::Size count(0); + for ( boost::tie(itr,end) = boost::vertices(graph); itr !=end; ++itr ) { + TS_ASSERT_EQUALS(count, boost::get(boost::vertex_index, graph, *itr)); + ++count; + //boost::put(boost::vertex_index, graph, *itr, index); + } + + core::chemical::VD vd = graph.add_vertex(); + core::chemical::regenerate_graph_vertex_index(graph); + + TS_ASSERT_EQUALS(boost::get(boost::vertex_index, graph, vd), 8); + + + } + }; diff --git a/source/test/core/chemical/gasteiger/GasteigerAtomTyper.cxxtest.hh b/source/test/core/chemical/gasteiger/GasteigerAtomTyper.cxxtest.hh index ad78e35f38..4d332f3bb1 100644 --- a/source/test/core/chemical/gasteiger/GasteigerAtomTyper.cxxtest.hh +++ b/source/test/core/chemical/gasteiger/GasteigerAtomTyper.cxxtest.hh @@ -26,6 +26,9 @@ #include #include +#include +#include + #include #include @@ -366,8 +369,33 @@ public: TS_ASSERT_EQUALS( azo_test->atom("N5").gasteiger_atom_type()->get_name(), "N_DiDiPiPi" ); // -[N-]-*[N+]*#N TS_ASSERT_EQUALS( azo_test->atom("N6").gasteiger_atom_type()->get_name(), "N_Di2DiPiPi" ); // -[N-]-[N+]#*N* + // for( core::Size ii(1); ii <= nitr_test->natoms(); ++ii ) { + // TR << ii << "Atom " << nitr_test->atom_name(ii); + // if( nitr_test->atom(ii).gasteiger_atom_type() ) { + // TR << nitr_test->atom(ii).gasteiger_atom_type()->get_name() << std::endl; + // } else { + // TR << " unassigned" << std::endl; + // } + // } + } + void test_foldit_ligand() { + using namespace core::chemical; + ChemicalManager * cm(ChemicalManager::get_instance()); + ResidueTypeSetCOP rsd_types( cm->residue_type_set(FA_STANDARD) ); + + core::chemical::MutableResidueTypeOP test = read_topology_file("core/chemical/modifications/foldit_ligand.params", rsd_types ); + + core::chemical::gasteiger::assign_gasteiger_atom_types( *test, atom_type_set_, /*keep_existing=*/ false ); + + TS_ASSERT_EQUALS( test->atom("O1").gasteiger_atom_type()->get_name(), "O_Te2Te2TeTe" ); + //TS_ASSERT_EQUALS( test->atom("N1").gasteiger_atom_type()->get_name(), "N_TrTrTrPi2" ); // Should this be N_Te2TeTeTe ? + //TS_ASSERT_EQUALS( test->atom("N2").gasteiger_atom_type()->get_name(), "N_TrTrTrPi2" ); // Should this be N_Te2TeTeTe ? + TS_ASSERT_EQUALS( test->atom("C3").gasteiger_atom_type()->get_name(), "C_TeTeTeTe" ); + TS_ASSERT_EQUALS( test->atom("C6").gasteiger_atom_type()->get_name(), "C_TeTeTeTe" ); + + } // Test various oxides, particularly in the case of formal charge separation void test_oxides() { diff --git a/source/test/core/chemical/modifications/Reprotonate.cxxtest.hh b/source/test/core/chemical/modifications/Reprotonate.cxxtest.hh new file mode 100644 index 0000000000..6f292edb72 --- /dev/null +++ b/source/test/core/chemical/modifications/Reprotonate.cxxtest.hh @@ -0,0 +1,203 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file core/chemical/modifications/Reprotonate.cxxtest.hh +/// @brief test suite for core::chemical::modifications::Reprotonate.cc +/// @author Rocco Moretti (rmorettiase@gmail.com) + + +// Test headers +#include + +// Unit headers +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +// Project headers +#include + +using namespace core; +using namespace core::chemical; +using namespace core::chemical::gasteiger; + +static basic::Tracer TR("core.chemical.modifications.Reprotonate.cxxtest"); + +// --------------- Test Class --------------- // + +class ReprotonateTests : public CxxTest::TestSuite { + +public: + + // --------------- Suite-level Fixture --------------- // + + ReprotonateTests() { + } + + virtual ~ReprotonateTests() {} + + + // --------------- Fixtures --------------- // + + // Shared initialization goes here. + void setUp() { + core_init(); + } + + // Shared finalization goes here. + void tearDown() { + } + + + // --------------- Test Cases --------------- // + + /// @brief Test that deprotonation works + void test_deprotonate() { + using namespace core::chemical; + ChemicalManager * cm(ChemicalManager::get_instance()); + std::string const tag(FA_STANDARD); + AtomTypeSetCOP atom_types = cm->atom_type_set(tag); + ElementSetCOP element_types = cm->element_set("default"); + MMAtomTypeSetCOP mm_atom_types = cm->mm_atom_type_set(tag); + orbitals::OrbitalTypeSetCOP orbital_types = cm->orbital_type_set(tag); + + core::chemical::MutableResidueTypeOP restype = read_topology_file("core/chemical/modifications/DEP.params", + atom_types, element_types, mm_atom_types, orbital_types ); + + core::chemical::modifications::Reprotonate reprot; + + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O6")), 1 ); // Carboxylate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O7")), 0 ); // Carboxylate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("S1")), 1 ); // Thiocarboxylate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O8")), 0 ); // Thiocarboxylate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O9")), 1 ); // phosphate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("S2")), 1 ); // phosphate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O10")), 0 ); // phosphate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O13")), 1 ); // sulfate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O12")), 0 ); // sulfate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("S4")), 1 ); // thiophosphate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O11")), 1 ); // thiophosphate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("S3")), 0 ); // thiophosphate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O14")), 1 ); // nitrate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O15")), 0 ); // nitrate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O17")), 1 ); // enol + vinyl hydroxyl + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O16")), 1 ); // phenol + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("S6")), 1 ); // thiophenol + + reprot.apply(*restype); + + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O6")), 0 ); // Carboxylate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O7")), 0 ); // Carboxylate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("S1")), 0 ); // Thiocarboxylate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O8")), 0 ); // Thiocarboxylate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O9")), 0 ); // phosphate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("S2")), 0 ); // phosphate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O10")), 0 ); // phosphate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O13")), 0 ); // sulfate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O12")), 0 ); // sulfate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("S4")), 0 ); // thiophosphate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O11")), 0 ); // thiophosphate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("S3")), 0 ); // thiophosphate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O14")), 0 ); // nitrate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O15")), 0 ); // nitrate + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O17")), 1 ); // enol + vinyl hydroxyl + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("O16")), 1 ); // phenol + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("S6")), 1 ); // thiophenol + + TS_ASSERT_EQUALS( restype->atom("O6").formal_charge(), -1 ); // Carboxylate + TS_ASSERT_EQUALS( restype->atom("O7").formal_charge(), 0 ); // Carboxylate + TS_ASSERT_EQUALS( restype->atom("S1").formal_charge(), -1 ); // Thiocarboxylate + TS_ASSERT_EQUALS( restype->atom("O8").formal_charge(), 0 ); // Thiocarboxylate + TS_ASSERT_EQUALS( restype->atom("O9").formal_charge(), -1 ); // phosphate + TS_ASSERT_EQUALS( restype->atom("S2").formal_charge(), -1 ); // phosphate + TS_ASSERT_EQUALS( restype->atom("O10").formal_charge(), 0 ); // phosphate + TS_ASSERT_EQUALS( restype->atom("O13").formal_charge(), -1 ); // sulfate + TS_ASSERT_EQUALS( restype->atom("O12").formal_charge(), 0 ); // sulfate + TS_ASSERT_EQUALS( restype->atom("S4").formal_charge(), -1 ); // thiophosphate + TS_ASSERT_EQUALS( restype->atom("O11").formal_charge(), -1 ); // thiophosphate + TS_ASSERT_EQUALS( restype->atom("S3").formal_charge(), 0 ); // thiophosphate + TS_ASSERT_EQUALS( restype->atom("O14").formal_charge(), -1 ); // nitrate + TS_ASSERT_EQUALS( restype->atom("O15").formal_charge(), 0 ); // nitrate + TS_ASSERT_EQUALS( restype->atom("O17").formal_charge(), 0 ); // enol + vinyl hydroxyl + TS_ASSERT_EQUALS( restype->atom("O16").formal_charge(), 0 ); // phenol + TS_ASSERT_EQUALS( restype->atom("S6").formal_charge(), 0 ); // thiophenol + } + + /// @brief Test that protonation works + void test_protonate() { + using namespace core::chemical; + ChemicalManager * cm(ChemicalManager::get_instance()); + std::string const tag(FA_STANDARD); + AtomTypeSetCOP atom_types = cm->atom_type_set(tag); + ElementSetCOP element_types = cm->element_set("default"); + MMAtomTypeSetCOP mm_atom_types = cm->mm_atom_type_set(tag); + orbitals::OrbitalTypeSetCOP orbital_types = cm->orbital_type_set(tag); + + core::chemical::MutableResidueTypeOP restype = read_topology_file("core/chemical/modifications/PRO.params", + atom_types, element_types, mm_atom_types, orbital_types ); + + core::chemical::modifications::Reprotonate reprot; + + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N1")), 2 ); // Primary amine + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N2")), 1 ); // secondary amine + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N3")), 0 ); // tertiary amine + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N4")), 3 ); // Primary amine, already protonated + TS_ASSERT_EQUALS( restype->atom("N4").formal_charge(), 1 ); // Primary amine, already protonated + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N5")), 0 ); // quaternary amine + TS_ASSERT_EQUALS( restype->atom("N5").formal_charge(), 1 ); // quaternary amine + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N6")), 2 ); // terminal amide + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N7")), 0 ); // filled amide + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N8")), 0 ); // cyano + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N9")), 1 ); // imine + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N11")), 2 ); // aniline + + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N10")), 0 ); // aromatic + + reprot.apply(*restype); + + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N1")), 3 ); // Primary amine + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N2")), 2 ); // secondary amine + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N3")), 1 ); // tertiary amine + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N4")), 3 ); // Primary amine, already protonated + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N5")), 0 ); // quaternary amine + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N6")), 2 ); // terminal amide + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N7")), 0 ); // filled amide + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N8")), 0 ); // cyano + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N9")), 1 ); // imine - no change. pKa for conjugate acid is 4-5 + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N11")), 2 ); // aniline - no change. pKa for conjugate acid is 4-5 + + + TS_ASSERT_EQUALS( restype->atom("N1").formal_charge(), 1 ); // Primary amine + TS_ASSERT_EQUALS( restype->atom("N2").formal_charge(), 1 ); // secondary amine + TS_ASSERT_EQUALS( restype->atom("N3").formal_charge(), 1 ); // tertiary amine + TS_ASSERT_EQUALS( restype->atom("N4").formal_charge(), 1 ); // Primary amine, already protonated + TS_ASSERT_EQUALS( restype->atom("N5").formal_charge(), 1 ); // quaternary amine + TS_ASSERT_EQUALS( restype->atom("N6").formal_charge(), 0 ); // terminal amide + TS_ASSERT_EQUALS( restype->atom("N7").formal_charge(), 0 ); // filled amide + TS_ASSERT_EQUALS( restype->atom("N8").formal_charge(), 0 ); // cyano + TS_ASSERT_EQUALS( restype->atom("N9").formal_charge(), 0 ); // imine + TS_ASSERT_EQUALS( restype->atom("N11").formal_charge(), 0 ); // aniline + + // Depending on system, protonation may be possible. However, we'll punt and leave aromatics as-is + // (Anyway, pyridine conjugate acid is pKa is ~5.3) + TS_ASSERT_EQUALS( restype->number_bonded_hydrogens(restype->atom_vertex("N10")), 0 ); // aromatic + TS_ASSERT_EQUALS( restype->atom("N10").formal_charge(), 0 ); // aromatic + } +}; + + diff --git a/source/test/core/chemical/modifications/ValenceHandler.cxxtest.hh b/source/test/core/chemical/modifications/ValenceHandler.cxxtest.hh new file mode 100644 index 0000000000..47c2411cea --- /dev/null +++ b/source/test/core/chemical/modifications/ValenceHandler.cxxtest.hh @@ -0,0 +1,560 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file core/chemical/modifications/ValenceHandler.cxxtest.hh +/// @brief test suite for core/chemical/modifications/ValenceHandler.hh +/// @author Steven Combs + + +// Test headers +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +///#include + +#include + +static basic::Tracer TR("core.chemical.modifications.ValenceHandler.cxxtest"); + +class ValenceHandlerTests : public CxxTest::TestSuite { + +public: + + //Shared data elements go here. + core::chemical::gasteiger::GasteigerAtomTypeSetCOP atom_type_set_; + core::chemical::ResidueTypeSetCOP const_residue_set_; + + ValenceHandlerTests() {} + virtual ~ValenceHandlerTests() {} + void tearDown() { + } + + void setUp(){ + core_init(); + utility::vector1< std::string > params_files; + + atom_type_set_ = core::chemical::ChemicalManager::get_instance()->gasteiger_atom_type_set(); + + const_residue_set_ = core::chemical::ChemicalManager::get_instance()->residue_type_set( core::chemical::FA_STANDARD ); + } + + // Return loaded residue type, but without adding it to a ResidueTypeSet + core::chemical::MutableResidueTypeOP load_residue_type( std::string name ) { + std::string const filename( "core/chemical/modifications/"+name+".params" ); + core::chemical::MutableResidueTypeOP residue_type = core::chemical::read_topology_file( filename, const_residue_set_ ); + + //this code relies heavily on gasteiger atom types. Make sure to assign them before trying to go through other functions + core::chemical::gasteiger::assign_gasteiger_atom_types( *residue_type, atom_type_set_, /*keep_existing=*/ false ); + return residue_type; + } + + void test_linear_coordinates() { + // Pythagorean Quadruple: 2,3,6,7 + numeric::xyzVector a(1,2,3); + numeric::xyzVector b(3,5,9); + + numeric::xyzVector x( core::chemical::modifications::linear_coordinates(a,b,7) ); + + TS_ASSERT_DELTA( x.distance(a), 7.0, 1e-4 ); + TS_ASSERT_DELTA( numeric::angle_degrees(x,a,b), 180.0, 1e-4); + //TS_ASSERT_DELTA( c.x(), 5.0, 1e-4); + //TS_ASSERT_DELTA( c.y(), 8.0, 1e-4); + //TS_ASSERT_DELTA( c.z(), 15.0, 1e-4); + } + + void test_angle_coordinates() { + numeric::xyzVector a(1,2,3); + numeric::xyzVector b(3,5,9); + numeric::xyzVector c(2,1,4); + + core::Real length( 1.5 ); + core::Real angle_d( 145.0 ); + core::Real angle_d2( 113.0 ); + core::Real angle( angle_d / 180.0 * numeric::constants::d::pi ); + core::Real angle2( angle_d2 / 180.0 * numeric::constants::d::pi ); + + numeric::xyzVector x( core::chemical::modifications::angle_coordinates( + a, b, c, + length, angle, angle2, + false, numeric::xyzVector( 0.0) ) ); + + TS_ASSERT_DELTA( x.distance( a ), length, 1e-4); + TS_ASSERT_DELTA( numeric::angle_degrees(x,a,b), angle_d, 1e-4); + TS_ASSERT_DELTA( numeric::angle_degrees(x,a,c), angle_d2, 1e-4); + } + + void test_trigonal_coordinates() { + numeric::xyzVector a(1,2,3); + numeric::xyzVector b(3,5,9); + numeric::xyzVector c(2,1,4); + + core::Real length( 3.4 ); + numeric::xyzVector x( core::chemical::modifications::triganol_coordinates( + a, b, c, length ) ); + + TS_ASSERT_DELTA( x.distance( a ), length, 1e-4); + core::Real angle( numeric::angle_degrees(x,a,b) ); + // It should be split equally between the two vectors. + TS_ASSERT_DELTA( numeric::angle_degrees(x,a,c), angle, 1e-2); + // Also, it should be planar, so all the angle should add up to 360 + TS_ASSERT_DELTA( numeric::angle_degrees(b,a,c) + 2*angle, 360, 1e-2); + } + + void test_tetrahedral_coordinates() { + using numeric::constants::d::pi; + numeric::xyzVector a(1,3.1,3); + numeric::xyzVector b(1+1.5*sin(60.0/180.0*pi),2,3-1.5*cos(60.0/180.0*pi)); + numeric::xyzVector c(1-1.5*sin(60.0/180.0*pi),2,3-1.5*cos(60.0/180.0*pi)); + numeric::xyzVector d(1,2,3+1.5); + + core::Real length( 1.4 ); + numeric::xyzVector x( core::chemical::modifications::tetrahedral_coordinates( + a, b, c, d, length ) ); + + TS_ASSERT_DELTA( x.distance( a ), length, 1e-4); + // Since the b,c,d are symmetric around the x-a line, the angles should all match + core::Real angle( numeric::angle_degrees(x,a,b) ); + TS_ASSERT_DELTA( numeric::angle_degrees(x,a,c), angle, 1e-2); + TS_ASSERT_DELTA( numeric::angle_degrees(x,a,d), angle, 1e-2); + + TR << "Tet: " << x.x() << " , " << x.y() << " , " << x.z() << std::endl; + } + + void test_one_bond_sp_missing_zero(){ + //N1 is supposed to be an sp nitrogen with one bond and should have no free valences open for bonds! + core::chemical::MutableResidueTypeOP restype( load_residue_type("1p1m0") ); + TR << restype->name() << std::endl; + core::chemical::VD atom = restype->atom_vertex( "N1" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *restype, atom ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 0 ); + } + + void test_one_bond_sp_missing_one(){ + //C4 is supposed to be an sp carbon with one bond and should have one free valences open for bonds! + core::chemical::MutableResidueTypeOP one_bond_sp_( load_residue_type("1p1m1") ); + TR << one_bond_sp_->name() << std::endl; + core::chemical::VD C4 = one_bond_sp_->atom_vertex( "C4" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *one_bond_sp_, C4 ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 1 ); + for ( core::Size i(1); i <= coordinates.size(); ++i ) { + core::Real sp_angle( numeric::angle_degrees( coordinates[i], one_bond_sp_->atom(C4).ideal_xyz(), one_bond_sp_->atom("C2").ideal_xyz() ) ); + TS_ASSERT_DELTA( sp_angle, 180.0, 1.0 ); + } + + using namespace ObjexxFCL::format; + for ( core::chemical::VD atm: one_bond_sp_->all_atoms() ) { + core::PointPosition coords = one_bond_sp_->atom(atm).ideal_xyz(); + std::string outline( "HETATM" + I( 5, 1 ) + " C AAA A" + + I( 4, 1 ) + " " + + F( 8, 3, coords.x() ) + F( 8, 3, coords.y() ) + F( 8, 3, coords.z() ) + + F( 6, 2, 1.0 ) + ' ' + F( 5, 2, 1.0 )); + TR <name() << std::endl; + core::chemical::VD atom = restype->atom_vertex( "O1" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *restype, atom ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 0 ); + } + + void test_one_bond_sp2_missing_one(){ + //N1 is supposed to be an sp2 nitrogen with one bond and should have one free valences open for bonds! + core::chemical::MutableResidueTypeOP restype( load_residue_type("1p2m1") ); + TR << restype->name() << std::endl; + core::chemical::VD atom = restype->atom_vertex( "N1" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *restype, atom ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 1 ); + for ( core::Size i(1); i <= coordinates.size(); ++i ) { + core::Real angle( numeric::angle_degrees( coordinates[i], restype->atom(atom).ideal_xyz(), restype->atom("C1").ideal_xyz() ) ); + TS_ASSERT_DELTA( angle, 120.0, 2.5 ); + core::Real dihedral( numeric::dihedral_degrees( coordinates[i], restype->atom(atom).ideal_xyz(), restype->atom("C1").ideal_xyz(), restype->atom("C2").ideal_xyz() ) ); + if ( dihedral < -90 ) { + TS_ASSERT_DELTA( dihedral, -180.0, 1.0 ); + } else if ( dihedral > 90 ) { + TS_ASSERT_DELTA( dihedral, 180.0, 1.0 ); + } else { + TS_ASSERT_DELTA( dihedral, 0.0, 1.0 ); + } + } + } + + void test_one_bond_sp2_missing_two(){ + //C4 is supposed to be an sp2 carbon with one bond and should have two free valences open for bonds! + core::chemical::MutableResidueTypeOP one_bond_sp2_( load_residue_type("1p2m2") ); + TR << one_bond_sp2_->name() << std::endl; + core::chemical::VD C4 = one_bond_sp2_->atom_vertex( "C4" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *one_bond_sp2_, C4 ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 2 ); + for ( core::Size i(1); i <= coordinates.size(); ++i ) { + core::Real sp2_angle( numeric::angle_degrees( coordinates[i], one_bond_sp2_->atom(C4).ideal_xyz(), one_bond_sp2_->atom("C2").ideal_xyz() ) ); + TS_ASSERT_DELTA( sp2_angle, 120.0, 2.5 ); + core::Real sp2_dihedral( numeric::dihedral_degrees( coordinates[i], one_bond_sp2_->atom(C4).ideal_xyz(), one_bond_sp2_->atom("C2").ideal_xyz(), one_bond_sp2_->atom("C1").ideal_xyz() ) ); + if ( sp2_dihedral < -90 ) { + TS_ASSERT_DELTA( sp2_dihedral, -180.0, 1.0 ); + } else if ( sp2_dihedral > 90 ) { + TS_ASSERT_DELTA( sp2_dihedral, 180.0, 1.0 ); + } else { + TS_ASSERT_DELTA( sp2_dihedral, 0.0, 1.0 ); + } + } + + using namespace ObjexxFCL::format; + for ( core::chemical::VD atm: one_bond_sp2_->all_atoms() ) { + core::PointPosition coords = one_bond_sp2_->atom(atm).ideal_xyz(); + std::string outline( "HETATM" + I( 5, 1 ) + " C AAA A" + + I( 4, 1 ) + " " + + F( 8, 3, coords.x() ) + F( 8, 3, coords.y() ) + F( 8, 3, coords.z() ) + + F( 6, 2, 1.0 ) + ' ' + F( 5, 2, 1.0 )); + TR <name() << std::endl; + core::chemical::VD atom = restype->atom_vertex( "CL1" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *restype, atom ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 0 ); + } + + void test_one_bond_sp3_missing_one(){ + //O1 is supposed to be an sp3 oxygen with one bond and should have one valence open for bonds! + core::chemical::MutableResidueTypeOP one_bond_sp3_( load_residue_type("1p3m1") ); + TR << one_bond_sp3_->name() << std::endl; + core::chemical::VD O1 = one_bond_sp3_->atom_vertex( "O1" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *one_bond_sp3_, O1 ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 1 ); + for ( core::Size i(1); i <= coordinates.size(); ++i ) { + core::Real sp3_angle( numeric::angle_degrees( coordinates[i], one_bond_sp3_->atom(O1).ideal_xyz(), one_bond_sp3_->atom("C2").ideal_xyz() ) ); + TS_ASSERT_DELTA( sp3_angle, 109.5, 1.0 ); + } + + using namespace ObjexxFCL::format; + for ( core::chemical::VD atm: one_bond_sp3_->all_atoms() ) { + core::PointPosition coords = one_bond_sp3_->atom(atm).ideal_xyz(); + std::string outline( "HETATM" + I( 5, 1 ) + " C AAA A" + + I( 4, 1 ) + " " + + F( 8, 3, coords.x() ) + F( 8, 3, coords.y() ) + F( 8, 3, coords.z() ) + + F( 6, 2, 1.0 ) + ' ' + F( 5, 2, 1.0 )); + TR <name() << std::endl; + core::chemical::VD atom = restype->atom_vertex( "N1" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *restype, atom ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 2 ); + for ( core::Size i(1); i <= coordinates.size(); ++i ) { + core::Real angle( numeric::angle_degrees( coordinates[i], restype->atom(atom).ideal_xyz(), restype->atom("C1").ideal_xyz() ) ); + TS_ASSERT_DELTA( angle, 109.5, 1.0 ); + } + + using namespace ObjexxFCL::format; + for ( core::chemical::VD atm: restype->all_atoms() ) { + core::PointPosition coords = restype->atom(atm).ideal_xyz(); + std::string outline( "HETATM" + I( 5, 1 ) + " C AAA A" + + I( 4, 1 ) + " " + + F( 8, 3, coords.x() ) + F( 8, 3, coords.y() ) + F( 8, 3, coords.z() ) + + F( 6, 2, 1.0 ) + ' ' + F( 5, 2, 1.0 )); + TR <name() << std::endl; + core::chemical::VD atom = restype->atom_vertex( "C2" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *restype, atom ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 3 ); + for ( core::Size i(1); i <= coordinates.size(); ++i ) { + core::Real angle( numeric::angle_degrees( coordinates[i], restype->atom(atom).ideal_xyz(), restype->atom("C1").ideal_xyz() ) ); + TS_ASSERT_DELTA( angle, 109.5, 1.0 ); + } + } + + void test_two_bond_sp_missing_zero(){ + //C2 is supposed to be an sp carbon with two bonds and should have no free valences open for bonds! + core::chemical::MutableResidueTypeOP restype( load_residue_type("2p1m0") ); + TR << restype->name() << std::endl; + core::chemical::VD atom = restype->atom_vertex( "C2" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *restype, atom ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 0 ); + } + + void test_two_bond_sp2_missing_zero(){ + //N1 is supposed to be an sp2 nitrogen with two bonds and should have no free valences open for bonds! + core::chemical::MutableResidueTypeOP restype( load_residue_type("2p2m0") ); + TR << restype->name() << std::endl; + core::chemical::VD atom = restype->atom_vertex( "N1" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *restype, atom ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 0 ); + } + + void test_two_bond_sp2_missing_one(){ + //C4 is supposed to be an sp2 carbon with two bonds and should have one free valence open for bonds! + core::chemical::MutableResidueTypeOP two_bond_sp2_( load_residue_type("2p2m1") ); + TR << two_bond_sp2_->name() << std::endl; + core::chemical::VD C4 = two_bond_sp2_->atom_vertex( "C4" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *two_bond_sp2_, C4 ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 1 ); + for ( core::Size i(1); i <= coordinates.size(); ++i ) { + core::Real sp2_angle( numeric::angle_degrees( coordinates[i], two_bond_sp2_->atom(C4).ideal_xyz(), two_bond_sp2_->atom("C2").ideal_xyz() ) ); + TS_ASSERT_DELTA( sp2_angle, 120.0, 1.0 ); + core::Real sp2_dihedral( numeric::dihedral_degrees( coordinates[i], two_bond_sp2_->atom(C4).ideal_xyz(), two_bond_sp2_->atom("C2").ideal_xyz(), two_bond_sp2_->atom("C1").ideal_xyz() ) ); + if ( sp2_dihedral < -90 ) { + TS_ASSERT_DELTA( sp2_dihedral, -180.0, 1.0 ); + } else if ( sp2_dihedral > 90 ) { + TS_ASSERT_DELTA( sp2_dihedral, 180.0, 1.0 ); + } else { + TS_ASSERT_DELTA( sp2_dihedral, 0.0, 1.0 ); + } + } + + using namespace ObjexxFCL::format; + for ( core::chemical::VD atm: two_bond_sp2_->all_atoms() ) { + core::PointPosition coords = two_bond_sp2_->atom(atm).ideal_xyz(); + std::string outline( "HETATM" + I( 5, 1 ) + " C AAA A" + + I( 4, 1 ) + " " + + F( 8, 3, coords.x() ) + F( 8, 3, coords.y() ) + F( 8, 3, coords.z() ) + + F( 6, 2, 1.0 ) + ' ' + F( 5, 2, 1.0 )); + TR <name() << std::endl; + core::chemical::VD atom = restype->atom_vertex( "O1" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *restype, atom ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 0 ); + } + + void test_two_bond_sp3_missing_one(){ + //N1 is supposed to be an sp3 nitrogen with two bonds and should have one free valences open for bonds! + core::chemical::MutableResidueTypeOP restype( load_residue_type("2p3m1") ); + TR << restype->name() << std::endl; + core::chemical::VD atom = restype->atom_vertex( "N1" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *restype, atom ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 1 ); + for ( core::Size i(1); i <= coordinates.size(); ++i ) { + core::Real angle( numeric::angle_degrees( coordinates[i], restype->atom(atom).ideal_xyz(), restype->atom("C2").ideal_xyz() ) ); + TS_ASSERT_DELTA( angle, 109.5, 1.0 ); + angle = numeric::angle_degrees( coordinates[i], restype->atom(atom).ideal_xyz(), restype->atom("C1").ideal_xyz() ); + TS_ASSERT_DELTA( angle, 109.5, 1.0 ); + } + } + + void test_two_bond_sp3_missing_two(){ + //C4 is supposed to be an sp3 carbon with two bonds and should have two free valences open for bonds! + core::chemical::MutableResidueTypeOP two_bond_sp3_( load_residue_type("2p3m2") ); + TR << two_bond_sp3_->name() << std::endl; + core::chemical::VD C3 = two_bond_sp3_->atom_vertex( "C3" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *two_bond_sp3_, C3 ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 2 ); + for ( core::Size i(1); i <= coordinates.size(); ++i ) { + core::Real sp3_angle( numeric::angle_degrees( coordinates[i], two_bond_sp3_->atom(C3).ideal_xyz(), two_bond_sp3_->atom("C2").ideal_xyz() ) ); + TS_ASSERT_DELTA( sp3_angle, 109.5, 1.0 ); + } + + using namespace ObjexxFCL::format; + for ( core::chemical::VD atm: two_bond_sp3_->all_atoms() ) { + core::PointPosition coords = two_bond_sp3_->atom(atm).ideal_xyz(); + std::string outline( "HETATM" + I( 5, 1 ) + " C AAA A" + + I( 4, 1 ) + " " + + F( 8, 3, coords.x() ) + F( 8, 3, coords.y() ) + F( 8, 3, coords.z() ) + + F( 6, 2, 1.0 ) + ' ' + F( 5, 2, 1.0 )); + TR <name() << std::endl; + core::chemical::VD atom = restype->atom_vertex( "N1" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *restype, atom ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 0 ); + } + + void test_three_bond_sp3_missing_one(){ + //C3 is supposed to be an sp3 carbon with three bonds and should have one free valence open for bonds! + core::chemical::MutableResidueTypeOP three_bond_sp3_( load_residue_type("3p3m1") ); + TR << three_bond_sp3_->name() << std::endl; + core::chemical::VD C3 = three_bond_sp3_->atom_vertex( "C3" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *three_bond_sp3_, C3 ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 1 ); + for ( core::Size i(1); i <= coordinates.size(); ++i ) { + core::Real sp3_angle( numeric::angle_degrees( coordinates[i], three_bond_sp3_->atom(C3).ideal_xyz(), three_bond_sp3_->atom("C2").ideal_xyz() ) ); + TS_ASSERT_DELTA( sp3_angle, 109.5, 1.0 ); + } + + using namespace ObjexxFCL::format; + for ( core::chemical::VD atm: three_bond_sp3_->all_atoms() ) { + core::PointPosition coords = three_bond_sp3_->atom(atm).ideal_xyz(); + std::string outline( "HETATM" + I( 5, 1 ) + " C AAA A" + + I( 4, 1 ) + " " + + F( 8, 3, coords.x() ) + F( 8, 3, coords.y() ) + F( 8, 3, coords.z() ) + + F( 6, 2, 1.0 ) + ' ' + F( 5, 2, 1.0 )); + TR <name() << std::endl; + core::chemical::VD atom = restype->atom_vertex( "C1" ); + utility::vector1 > coordinates( core::chemical::modifications::determine_coordinates( *restype, atom ) ); + + TS_ASSERT_EQUALS( coordinates.size(), 0 ); + } + +}; + + diff --git a/source/test/core/scoring/constraints/DihedralConstraint.cxxtest.hh b/source/test/core/scoring/constraints/DihedralConstraint.cxxtest.hh index ffce3271ec..d6da1f1a03 100644 --- a/source/test/core/scoring/constraints/DihedralConstraint.cxxtest.hh +++ b/source/test/core/scoring/constraints/DihedralConstraint.cxxtest.hh @@ -237,17 +237,17 @@ public: //[old behavior] Objects are equivalent (same data) but not identical (different places in memory) //TS_ASSERT( constraints_orig[i] != constraints_copy[i] ); // Objects are identical (same place in memory) - TS_ASSERT( constraints_orig[i] == constraints_copy[i] ); - TS_ASSERT( constraints_orig[i].get() == constraints_copy[i].get() ); + TS_ASSERT_EQUALS( constraints_orig[i], constraints_copy[i] ); + TS_ASSERT_EQUALS( constraints_orig[i].get(), constraints_copy[i].get() ); TS_ASSERT_EQUALS( constraints_orig[i]->to_string(), "DihedralConstraint 116 2 116 6 116 7 116 8 CIRCULARHARMONIC 2.44839 0.0872665\n" ); TS_ASSERT_EQUALS( constraints_orig[i]->to_string(), constraints_copy[i]->to_string() ); } core::Size const size_before = pose.constraint_set()->get_all_constraints().size(); - TS_ASSERT( size_before == 1 ); - TS_ASSERT( pose.remove_constraint(constraint) == true ); + TS_ASSERT_EQUALS( size_before, 1 ); + TS_ASSERT(pose.remove_constraint(constraint) == true); core::Size const size_after = pose.constraint_set()->get_all_constraints().size(); - TS_ASSERT( size_after == 0 ); + TS_ASSERT_EQUALS( size_after, 0 ); // Can't remove a constraint twice: TS_ASSERT( pose.remove_constraint(constraint) == false ); } diff --git a/source/test/numeric/Calculator.cxxtest.hh b/source/test/numeric/Calculator.cxxtest.hh index 691415e1e1..af116b1d3f 100644 --- a/source/test/numeric/Calculator.cxxtest.hh +++ b/source/test/numeric/Calculator.cxxtest.hh @@ -29,19 +29,19 @@ public: // Shared initialization goes here. void setUp() { - delta_ = 0.0001; + delta_ = 0.0001; } // Shared finalization goes here. void tearDown() {} - void test_simple() { + void test_simple() { numeric::Calculator calc("3.14 "); std::map< std::string, numeric::Real > vars; numeric::Real out = 9999; - - TS_ASSERT( ! calc.compute( vars, out ) ); + + TS_ASSERT( ! calc.compute( vars, out ) ); TS_ASSERT_DELTA( out, 3.14, delta_ ); } @@ -50,19 +50,19 @@ public: std::map< std::string, numeric::Real > vars; numeric::Real out = 9999; - - TS_ASSERT( ! calc.compute( vars, out ) ); + + TS_ASSERT( ! calc.compute( vars, out ) ); TS_ASSERT_DELTA( out, 4.5 , delta_ ); } void test_exponent() { - numeric::Calculator calc("1.5 * 2 ^ 3 ^ 2 / 2"); // 1.5 * ( 2^(3^2) ) / 2 + numeric::Calculator calc("1.5 * 2 ^ 3 ^ 2 / 2"); // 1.5 * ( 2^(3^2) ) / 2 - std::map< std::string, numeric::Real > vars; - numeric::Real out = 9999; + std::map< std::string, numeric::Real > vars; + numeric::Real out = 9999; - TS_ASSERT( ! calc.compute( vars, out ) ); - TS_ASSERT_DELTA( out, 384 , delta_ ); + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 384 , delta_ ); } void test_variables() { @@ -78,30 +78,96 @@ public: } void test_assign() { - numeric::Calculator calc(" alpha = 1 + 3; beta = 3/4; alpha * beta "); + numeric::Calculator calc(" alpha = 1 + 3; beta = 3/4; alpha * beta "); - std::map< std::string, numeric::Real > vars; - numeric::Real out = 9999; + std::map< std::string, numeric::Real > vars; + numeric::Real out = 9999; - TS_ASSERT( ! calc.compute( vars, out ) ); - TS_ASSERT_DELTA( out, 3 , delta_ ); + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 3 , delta_ ); } void test_functions() { - std::map< std::string, numeric::Real > vars; - vars["cosy"] = 1.5; - vars["logs"] = 8; - numeric::Real out = 9999; + std::map< std::string, numeric::Real > vars; + vars["cosy"] = 1.5; + vars["logs"] = 8; + numeric::Real out = 9999; numeric::Calculator calc("log(100)"); - TS_ASSERT( ! calc.compute( vars, out ) ); - TS_ASSERT_DELTA( out, 2 , delta_ ); + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 2 , delta_ ); + + numeric::Calculator calc2(" cos(SIN( d2r(-180)) ) + ln(exp(log(10)+2)) + sqrt(9)*LOG2(logs) + cosy*(2-1) + log(27,3) + mean(1, 1.5, 3, 4.5) + ABS(min(-0.2, 3, 0.6))"); // 1 + 3 + 3*3 + 1.5 + 3 + 2.5 + 0.2 + + out = 9999; + TS_ASSERT( ! calc2.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 20.2 , delta_ ); + } + + void test_sinpass() { + + numeric::Calculator calc("sinpass(x, 30.0, 50.0, 10.0, 16.0)"); + std::map< std::string, numeric::Real > vars; + numeric::Real out = 9999; + + //for ( core::Size i(0); i <= 1000; i++ ) { + // vars["x"] = i * 0.1; + // calc.compute( vars, out ); + // std::cout << vars["x"] << " " << out << std::endl; + //} + + vars["x"] = 30; + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 1.0 , delta_ ); + + vars["x"] = 43.2; + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 1.0 , delta_ ); + + vars["x"] = 50; + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 1.0 , delta_ ); + + vars["x"] = 30-0.00001; + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 1.0 , delta_ ); // Not exactly, but within delta + + vars["x"] = 50+0.00001; + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 1.0 , delta_ ); // Not exactly, but within delta + + vars["x"] = 20; + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 0.0 , delta_ ); + + vars["x"] = 66; + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 0.0 , delta_ ); - numeric::Calculator calc2(" cos(SIN( d2r(-180)) ) + ln(exp(log(10)+2)) + sqrt(9)*LOG2(logs) + cosy*(2-1) + log(27,3) + mean(1, 1.5, 3, 4.5) + ABS(min(-0.2, 3, 0.6))"); // 1 + 3 + 3*3 + 1.5 + 3 + 2.5 + 0.2 + vars["x"] = 20+0.00001; + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 0.0 , delta_ ); // Not exactly, but within delta + + vars["x"] = 66-0.00001; + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 0.0 , delta_ ); // Not exactly, but within delta + + vars["x"] = -135.7; + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 0.0 , delta_ ); + + vars["x"] = 2673.4; + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 0.0 , delta_ ); + + vars["x"] = 25; + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 0.5 , delta_ ); + + vars["x"] = 58; + TS_ASSERT( ! calc.compute( vars, out ) ); + TS_ASSERT_DELTA( out, 0.5 , delta_ ); - out = 9999; - TS_ASSERT( ! calc2.compute( vars, out ) ); - TS_ASSERT_DELTA( out, 20.2 , delta_ ); } }; diff --git a/source/test/protocols.test.settings b/source/test/protocols.test.settings index 521ede9962..6b5252f490 100644 --- a/source/test/protocols.test.settings +++ b/source/test/protocols.test.settings @@ -173,6 +173,16 @@ sources = { "SetupDockingFoldTree", ], + "drug_design" : [ + "orphan_restypes", + "ApplyChemistryMover", + "ReactionFragment", + "ReactionGrow", + "SAScoreFilter", + "SubstructureReplace", + "SubstituentReplace", + ], + "docking/membrane" : [ "MPDockingSetupMoverTest", ], @@ -333,6 +343,10 @@ sources = { "ConsensusLoopDesignOperationTests", ], + "drug_design": [ + "orphan_restypes", + ], + "generalized_kinematic_closure" : [ "GeneralizedKIC", "GeneralizedKIC_ramaprepro", @@ -430,11 +444,11 @@ sources = { #"kinematic_closure" : [ # "ClosureTests", #], + "legacy_sewing" : [ "SewingHasherTests", ], - "ligand_docking": [ "HighResEnsemble", "ligand_scores", @@ -1052,6 +1066,29 @@ testinputfiles = [ "docking/DockingSlideIntoContact.pdb", "docking/DockingPacking.pdb", "docking/DockingRigidBodyMinimize.pdb", +# "drug_design/amide_rxn.txt", +# "drug_design/benzene.sdf", +# "drug_design/benzene_stub.sdf", +# "drug_design/ester_rxn.txt", +# "drug_design/ester_rxn2.txt", +# "drug_design/ethylbenzene_stub.sdf", +# "drug_design/ethylpyrimidine_stub.sdf", +# "drug_design/ethoxypyrimidine.sdf", +# "drug_design/IClBrbenzene.sdf", +# "drug_design/IClBrpyrimidine.sdf", +# "drug_design/methylclstyrene.sdf", +# "drug_design/piperazine_prot.sdf", +# "drug_design/piperazine_quat.sdf", +# "drug_design/piperazine_stub.sdf", +# "drug_design/pyridazine_stub.sdf", +# "drug_design/pyrimidine_stub.sdf", +# "drug_design/pyrimidine_stub1.sdf", +# "drug_design/pyrimidine_Vstub.sdf", +# "drug_design/styrene_stub.sdf", +# "drug_design/test_fragments1.sdf", +# "drug_design/test_fragments2.sdf", +# "drug_design/vinylpyrimidine_stub.sdf", +# "drug_design/zim.100.sascore.txt", "drug_design/carboxypyrimidine.sdf", "energy_based_clustering/alpha_aa.silent", "energy_based_clustering/oligourea.silent", diff --git a/source/test/protocols/drug_design/IClBrbenzene.sdf b/source/test/protocols/drug_design/IClBrbenzene.sdf new file mode 100644 index 0000000000..3ace0be90a --- /dev/null +++ b/source/test/protocols/drug_design/IClBrbenzene.sdf @@ -0,0 +1,29 @@ +IClBrbenzene + ChemPy 3D 0 + + 12 12 0 0 1 0 0 0 0 0999 V2000 + -1.3868 -0.0717 0.0215 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7595 1.1747 0.0083 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6335 1.2397 0.0245 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4004 0.0764 0.0538 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7606 -1.1620 0.0668 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6319 -1.2453 0.0509 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4134 2.7727 0.0082 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7911 -2.7459 0.1067 Br 0 0 0 0 0 0 0 0 0 0 0 0 + -3.4620 -0.1817 -0.0025 I 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3434 2.0917 -0.0146 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4851 0.1382 0.0663 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.1192 -2.2169 0.0613 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 1 6 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 10 1 0 0 0 0 + 3 4 2 0 0 0 0 + 3 7 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 11 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 8 1 0 0 0 0 + 6 12 1 0 0 0 0 + 9 1 1 0 0 0 0 +M END diff --git a/source/test/protocols/drug_design/IClBrpyrimidine.sdf b/source/test/protocols/drug_design/IClBrpyrimidine.sdf new file mode 100644 index 0000000000..ebba338231 --- /dev/null +++ b/source/test/protocols/drug_design/IClBrpyrimidine.sdf @@ -0,0 +1,26 @@ +IClBrpyrimidine + OpenBabel09031514263D + + 10 10 0 0 0 0 0 0 0 0999 V2000 + -1.3868 -0.0717 0.0215 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7595 1.1747 0.0083 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6335 1.2397 0.0245 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4004 0.0764 0.0538 N 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7606 -1.1620 0.0668 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6319 -1.2453 0.0509 N 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4134 2.7727 0.0082 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7911 -2.7459 0.1067 Br 0 0 0 0 0 0 0 0 0 0 0 0 + -3.4620 -0.1817 -0.0025 I 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3434 2.0917 -0.0146 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 1 6 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 10 1 0 0 0 0 + 3 4 2 0 0 0 0 + 3 7 1 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 8 1 0 0 0 0 + 9 1 1 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/ReactionFragment.cxxtest.hh b/source/test/protocols/drug_design/ReactionFragment.cxxtest.hh new file mode 100644 index 0000000000..825b6b9569 --- /dev/null +++ b/source/test/protocols/drug_design/ReactionFragment.cxxtest.hh @@ -0,0 +1,198 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/ReactionFragment.cxxtest.hh +/// @brief test for ReactionFragment Chemistry +/// @author Rocco Moretti (rmorettiase@gmail.com) + +// Test headers +#include +#include +#include + +// Project Headers +#include + +#include +#include +#include +#include +#include + +// Utility Headers +#include +#include + +static basic::Tracer TR("protocols.drug_design.ReactionFragment.cxxtest.hh"); + +// --------------- Test Class --------------- // + +class ReactionFragmentTests : public CxxTest::TestSuite { + +private: +public: + + void setUp() { + core_init(); + } + + void tearDown() { + } + + void test_default() { + core::chemical::ResidueTypeSetCOP residue_set( core::chemical::ChemicalManager::get_instance()->residue_type_set(core::chemical::FA_STANDARD) ); + { + core::chemical::MutableResidueTypeOP restype( core::chemical::read_topology_file("core/chemical/params/U27.params",residue_set) ); + core::chemical::NameVDMapping orig_name_map( *restype ); + + protocols::drug_design::ReactionFragment fragment; + fragment.reaction_file("protocols/drug_design/ester_rxn.txt" ); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 18); + fragment.apply(*restype); + core::chemical::NameVDMapping mapping( orig_name_map.downstream_combine( fragment.get_mapping()) ); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 12); + //TS_ASSERT_DIFFERS( mapping[ " O1 " ], core::chemical::MutableResidueType::null_vertex ); // Atoms involved are currently ignored + //TS_ASSERT_EQUALS( mapping[ " O2 " ], core::chemical::MutableResidueType::null_vertex ); // Atoms involved are currently ignored + TS_ASSERT_DIFFERS( mapping[ " N1 " ], core::chemical::MutableResidueType::null_vertex ); + TS_ASSERT_EQUALS( mapping[ " C13" ], core::chemical::MutableResidueType::null_vertex ); + } + { + core::chemical::MutableResidueTypeOP restype( core::chemical::read_topology_file("core/chemical/params/U27.params",residue_set) ); + core::chemical::NameVDMapping orig_name_map( *restype ); + + protocols::drug_design::ReactionFragment fragment; + fragment.reaction_file("protocols/drug_design/ester_rxn2.txt" ); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 18); + fragment.apply(*restype); + core::chemical::NameVDMapping mapping( orig_name_map.downstream_combine( fragment.get_mapping()) ); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 6); + //TS_ASSERT_EQUALS( mapping[ " O1 " ], core::chemical::MutableResidueType::null_vertex ); // Atoms involved are currently ignored + //TS_ASSERT_DIFFERS( mapping[ " O2 " ], core::chemical::MutableResidueType::null_vertex ); // Atoms involved are currently ignored + TS_ASSERT_EQUALS( mapping[ " N1 " ], core::chemical::MutableResidueType::null_vertex ); + TS_ASSERT_DIFFERS( mapping[ " C13" ], core::chemical::MutableResidueType::null_vertex ); + } + } + + void test_larger() { + core::chemical::ResidueTypeSetCOP residue_set( core::chemical::ChemicalManager::get_instance()->residue_type_set(core::chemical::FA_STANDARD) ); + { + core::chemical::MutableResidueTypeOP restype( core::chemical::read_topology_file("core/chemical/params/U13.params",residue_set) ); + core::chemical::NameVDMapping orig_name_map( *restype ); + + protocols::drug_design::ReactionFragment fragment; + fragment.reaction_file("protocols/drug_design/amide_rxn.txt" ); + fragment.keep_bigger(true); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 29); + fragment.apply(*restype); + core::chemical::NameVDMapping mapping( orig_name_map.downstream_combine( fragment.get_mapping()) ); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 15); // The carbonyl one. + //TS_ASSERT_DIFFERS( mapping[ " O1 " ], core::chemical::MutableResidueType::null_vertex ); // Atoms involved are currently ignored + //TS_ASSERT_EQUALS( mapping[ " N2 " ], core::chemical::MutableResidueType::null_vertex ); // Atoms involved are currently ignored + TS_ASSERT_DIFFERS( mapping[ " N1 " ], core::chemical::MutableResidueType::null_vertex ); + TS_ASSERT_EQUALS( mapping[ " C21" ], core::chemical::MutableResidueType::null_vertex ); + } + { + core::chemical::MutableResidueTypeOP restype( core::chemical::read_topology_file("core/chemical/params/U27.params",residue_set) ); + core::chemical::NameVDMapping orig_name_map( *restype ); + + protocols::drug_design::ReactionFragment fragment; + fragment.reaction_file("protocols/drug_design/ester_rxn2.txt" ); + fragment.keep_bigger(true); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 18); + fragment.apply(*restype); + core::chemical::NameVDMapping mapping( orig_name_map.downstream_combine( fragment.get_mapping()) ); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 12); + //TS_ASSERT_DIFFERS( mapping[ " O1 " ], core::chemical::MutableResidueType::null_vertex ); // Atoms involved are currently ignored + //TS_ASSERT_EQUALS( mapping[ " O2 " ], core::chemical::MutableResidueType::null_vertex ); // Atoms involved are currently ignored + TS_ASSERT_DIFFERS( mapping[ " N1 " ], core::chemical::MutableResidueType::null_vertex ); + TS_ASSERT_EQUALS( mapping[ " C13" ], core::chemical::MutableResidueType::null_vertex ); + } + } + + void test_keep_atom() { + core::chemical::ResidueTypeSetCOP residue_set( core::chemical::ChemicalManager::get_instance()->residue_type_set(core::chemical::FA_STANDARD) ); + core::chemical::MutableResidueTypeOP restype( core::chemical::read_topology_file("core/chemical/params/U13.params",residue_set) ); + core::chemical::NameVDMapping orig_name_map( *restype ); + + protocols::drug_design::ReactionFragment fragment; + fragment.reaction_file("protocols/drug_design/amide_rxn.txt" ); + fragment.keep_bigger(false); + //fragment.keep_atom("N4"); // Positivly charges - will be lost in conversion + fragment.keep_atom("C21"); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 29); + TS_ASSERT( restype->has("C21")); + fragment.apply(*restype); + core::chemical::NameVDMapping mapping( orig_name_map.downstream_combine( fragment.get_mapping()) ); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 14); + TS_ASSERT_DIFFERS( mapping[ " C21" ], core::chemical::MutableResidueType::null_vertex ); + TS_ASSERT_EQUALS( mapping[ " N1 " ], core::chemical::MutableResidueType::null_vertex ); + //TS_ASSERT_DIFFERS( mapping[ " N2 " ], core::chemical::MutableResidueType::null_vertex ); // Atoms involved are currently ignored + //TS_ASSERT_EQUALS( mapping[ " O1 " ], core::chemical::MutableResidueType::null_vertex ); // Atoms involved are currently ignored + } + + void test_bad_reaction() { + // A reaction which doesn't apply to the residue. + core::chemical::ResidueTypeSetCOP residue_set( core::chemical::ChemicalManager::get_instance()->residue_type_set(core::chemical::FA_STANDARD) ); + core::chemical::MutableResidueTypeOP restype( core::chemical::read_topology_file("core/chemical/params/U13.params",residue_set) ); + core::chemical::NameVDMapping orig_name_map( *restype ); + + protocols::drug_design::ReactionFragment fragment; + fragment.reaction_file("protocols/drug_design/ester_rxn.txt"); + fragment.keep_bigger(false); + //fragment.keep_atom("N4"); // Positivly charges - will be lost in conversion + fragment.keep_atom("C21"); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 29); + TS_ASSERT( restype->has("C21")); + fragment.apply(*restype); // Should be a no-op + core::chemical::NameVDMapping mapping( orig_name_map.downstream_combine( fragment.get_mapping()) ); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 29); + TS_ASSERT_DIFFERS( mapping[ " C21" ], core::chemical::MutableResidueType::null_vertex ); + TS_ASSERT_DIFFERS( mapping[ " N2 " ], core::chemical::MutableResidueType::null_vertex ); + TS_ASSERT_DIFFERS( mapping[ " O1 " ], core::chemical::MutableResidueType::null_vertex ); + TS_ASSERT_DIFFERS( mapping[ " N1 " ], core::chemical::MutableResidueType::null_vertex ); + } + + void test_multiple_reaction() { + // Should find the appropriate reaction, even if there are multiple ones. + core::chemical::ResidueTypeSetCOP residue_set( core::chemical::ChemicalManager::get_instance()->residue_type_set(core::chemical::FA_STANDARD) ); + core::chemical::MutableResidueTypeOP restype( core::chemical::read_topology_file("core/chemical/params/U13.params",residue_set) ); + core::chemical::NameVDMapping orig_name_map( *restype ); + + protocols::drug_design::ReactionFragment fragment; + fragment.reaction_file("protocols/drug_design/ester_rxn.txt", /*append=*/ true ); + fragment.reaction_file("protocols/drug_design/amide_rxn.txt", /*append=*/ true ); + fragment.keep_bigger(false); + //fragment.keep_atom("N4"); // Positivly charges - will be lost in conversion + fragment.keep_atom("C21"); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 29); + TS_ASSERT( restype->has("C21")); + fragment.apply(*restype); + core::chemical::NameVDMapping mapping( orig_name_map.downstream_combine( fragment.get_mapping()) ); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 14); + TS_ASSERT_DIFFERS( mapping[ " C21" ], core::chemical::MutableResidueType::null_vertex ); + TS_ASSERT_EQUALS( mapping[ " N1 " ], core::chemical::MutableResidueType::null_vertex ); + //TS_ASSERT_DIFFERS( mapping[ " N2 " ], core::chemical::MutableResidueType::null_vertex ); // Atoms involved are currently ignored + //TS_ASSERT_EQUALS( mapping[ " O1 " ], core::chemical::MutableResidueType::null_vertex ); // Atoms involved are currently ignored + } + +}; diff --git a/source/test/protocols/drug_design/ReactionGrow.cxxtest.hh b/source/test/protocols/drug_design/ReactionGrow.cxxtest.hh new file mode 100644 index 0000000000..26d6933c35 --- /dev/null +++ b/source/test/protocols/drug_design/ReactionGrow.cxxtest.hh @@ -0,0 +1,123 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/ReactionGrow.cxxtest.hh +/// @brief test for ReactionGrow Chemistry +/// @author Rocco Moretti (rmorettiase@gmail.com) + +// Test headers +#include +#include +#include + +// Project Headers +#include + +#include +#include +#include +#include +#include + +// Utility Headers +#include +#include + +static basic::Tracer TR("protocols.drug_design.ReactionGrow.cxxtest.hh"); + +// --------------- Test Class --------------- // + +class ReactionGrowTests : public CxxTest::TestSuite { + +private: +public: + + void setUp() { + core_init(); + } + + void tearDown() { + } + + void test_grow() { + core::chemical::ResidueTypeSetCOP residue_set( core::chemical::ChemicalManager::get_instance()->residue_type_set(core::chemical::FA_STANDARD) ); + core::chemical::MutableResidueTypeOP restype( core::chemical::read_topology_file("core/chemical/params/U28.params",residue_set) ); + + protocols::drug_design::ReactionGrow grow; + grow.reaction_file("protocols/drug_design/ester_rxn2.txt" ); + grow.fragment_database("protocols/drug_design/test_fragments1.sdf"); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 26); + grow.apply(*restype); + // U28 has hydroxyl, fragment file has a 7 atom aldehyde + TS_ASSERT_EQUALS( restype->nheavyatoms(), 26+7 ); + } + + //The input molecule should only match the first reactant + void test_no_first_match() { + core::chemical::ResidueTypeSetCOP residue_set( core::chemical::ChemicalManager::get_instance()->residue_type_set(core::chemical::FA_STANDARD) ); + core::chemical::MutableResidueTypeOP restype( core::chemical::read_topology_file("core/chemical/params/U28.params",residue_set) ); + + protocols::drug_design::ReactionGrow grow; + grow.reaction_file("protocols/drug_design/ester_rxn.txt" ); + grow.fragment_database("protocols/drug_design/test_fragments1.sdf"); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 26); + grow.apply(*restype); + // U28 has hydroxyl, fragment file has a 6 atom aldehyde + TS_ASSERT_EQUALS( restype->nheavyatoms(), 26 ); + } + + // Test if the reaction can't match the ligand at all. + void test_no_reaction() { + core::chemical::ResidueTypeSetCOP residue_set( core::chemical::ChemicalManager::get_instance()->residue_type_set(core::chemical::FA_STANDARD) ); + core::chemical::MutableResidueTypeOP restype( core::chemical::read_topology_file("core/chemical/params/U13.params",residue_set) ); + + protocols::drug_design::ReactionGrow grow; + grow.reaction_file("protocols/drug_design/ester_rxn2.txt" ); + grow.fragment_database("protocols/drug_design/test_fragments1.sdf"); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 29); + grow.apply(*restype); + // U13 has no esterable groups + TS_ASSERT_EQUALS( restype->nheavyatoms(), 29); + } + + // Test if the reaction can handle a case where there isn't a matching fragment in the fragment set + void test_no_fragment() { + core::chemical::ResidueTypeSetCOP residue_set( core::chemical::ChemicalManager::get_instance()->residue_type_set(core::chemical::FA_STANDARD) ); + core::chemical::MutableResidueTypeOP restype( core::chemical::read_topology_file("core/chemical/params/U28.params",residue_set) ); + + protocols::drug_design::ReactionGrow grow; + grow.reaction_file("protocols/drug_design/ester_rxn2.txt" ); + grow.fragment_database("protocols/drug_design/test_fragments2.sdf"); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 26); + grow.apply(*restype); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 26 ); + } + + void test_multiple_reaction() { + // Should find the appropriate reaction, even if there are multiple ones. + core::chemical::ResidueTypeSetCOP residue_set( core::chemical::ChemicalManager::get_instance()->residue_type_set(core::chemical::FA_STANDARD) ); + core::chemical::MutableResidueTypeOP restype( core::chemical::read_topology_file("core/chemical/params/U28.params",residue_set) ); + + protocols::drug_design::ReactionGrow grow; + grow.reaction_file("protocols/drug_design/amide_rxn.txt"); + grow.reaction_file("protocols/drug_design/ester_rxn2.txt", true); // append + grow.reaction_file("protocols/drug_design/ester_rxn.txt" , true); + grow.fragment_database("protocols/drug_design/test_fragments1.sdf"); + + TS_ASSERT_EQUALS( restype->nheavyatoms(), 26); + grow.apply(*restype); + // U28 has hydroxyl, fragment file has a 7 atom aldehyde + TS_ASSERT_EQUALS( restype->nheavyatoms(), 26+7 ); + } + +}; diff --git a/source/test/protocols/drug_design/SAScoreFilter.cxxtest.hh b/source/test/protocols/drug_design/SAScoreFilter.cxxtest.hh new file mode 100644 index 0000000000..cd04b7706d --- /dev/null +++ b/source/test/protocols/drug_design/SAScoreFilter.cxxtest.hh @@ -0,0 +1,84 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/SAScoreFilter.cxxtest.hh +/// @brief test for SAScoreFilter +/// @author Rocco Moretti (rmorettiase@gmail.com) + +// Test headers +#include +#include +#include + +// Project Headers +#include + +#include +#include +#include +#include +#include + +// Utility Headers +#include +#include +#include + +#include +#include + +#include + +#include + +static basic::Tracer TR("protocols.drug_design.SAScoreFilter.cxxtest.hh"); + +// --------------- Test Class --------------- // + +class SAScoreFilterTests : public CxxTest::TestSuite { + +private: +public: + + void setUp() { + core_init(); + } + + void tearDown() { + } + + void test_larger() { + } + + void test_sascores() { + protocols::drug_design::SAScoreFilter sascorer; + + // These examples and values come from the RDKit source + utility::io::izstream testfile("protocols/drug_design/zim.100.sascore.txt"); + + + std::string line; + while ( getline(testfile, line) ) { + std::string smiles, name; + core::Real ref_score; + std::stringstream linestream( line ); + linestream >> smiles >> name >> ref_score; + if ( smiles == "smiles" ) { continue; } + + RDKit::ROMolOP rdmol( RDKit::SmilesToMol( smiles) ); + + // I decided not to bother with testing the back-and-forth through Rosetta + core::Real computed_score( sascorer.calculate_rdkit(*rdmol) ); + TR.Debug << "Testing " << smiles << std::endl; + TSM_ASSERT_DELTA( smiles, computed_score, ref_score, 0.001 ); //listed in file to 3 decimal places + } + } + + +}; diff --git a/source/test/protocols/drug_design/SubstituentReplace.cxxtest.hh b/source/test/protocols/drug_design/SubstituentReplace.cxxtest.hh new file mode 100644 index 0000000000..fa3b0c191d --- /dev/null +++ b/source/test/protocols/drug_design/SubstituentReplace.cxxtest.hh @@ -0,0 +1,150 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/SubstituentReplace.cxxtest.hh +/// @brief test for SubstituentReplace Chemistry +/// @author Rocco Moretti (rmorettiase@gmail.com) + +// Test headers +#include +#include +#include + +// Project Headers +#include + +#include +#include +#include + +// Utility Headers +#include +#include + +static basic::Tracer TR("protocols.drug_design.SubstituentReplace.cxxtest.hh"); + +// --------------- Test Class --------------- // + +class SubstituentReplaceTests : public CxxTest::TestSuite { + +private: + core::chemical::ResidueTypeOP restype_; + +public: + + void setUp() { + core_init(); + } + + void tearDown() { + } + + void test_substitute() { + protocols::drug_design::SubstituentReplace replace; + + replace.template_database( "protocols/drug_design/pyrimidine_stub1.sdf"); + replace.substituents_database( "protocols/drug_design/ethoxypyrimidine.sdf"); + + utility::vector1< core::chemical::MutableResidueTypeOP > restypes( core::chemical::sdf::convert_to_ResidueTypes("protocols/drug_design/carboxypyrimidine.sdf") ); + TS_ASSERT( restypes.size() > 0 ); + core::chemical::MutableResidueTypeOP restype( restypes[1] ); + + core::chemical::NameVDMapping orig_map( *restype ); + + TS_ASSERT_EQUALS( restype->natoms(), 13 ); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 9 ); + + //orig_map.show(TR); + + TS_ASSERT( restype->has("O1") ); + TS_ASSERT( restype->has("O2") ); + TS_ASSERT( restype->has("C5") ); + TS_ASSERT( restype->has("N1") ); + TS_ASSERT( restype->has("N2") ); + + replace.apply(*restype); + TS_ASSERT_EQUALS( replace.get_last_status(), core::chemical::modifications::SUCCESS ); + + TS_ASSERT_EQUALS( restype->natoms(), 17 ); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 10 ); + + // Keeps the nitrogens + TS_ASSERT( restype->has("N1") ); + TS_ASSERT( restype->has("N2") ); + TS_ASSERT( restype->has("O1") ); // Has the one oxygen + TS_ASSERT( ! restype->has("O2") ); // Has the one oxygen + + TS_ASSERT( restype->has("F1") ); // Adds the flurine + TS_ASSERT( ! restype->has("CL1") ); // Does not add the chlorine + + core::chemical::VDVDMapping map( replace.get_mapping() ); + + TS_ASSERT_EQUALS( map[ orig_map[ " O2 " ] ], map.invalid_entry() ); + + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " N1 " ] ] ), " N1 " ); + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " N2 " ] ] ), " N2 " ); + + // Shouldn't map anything on the flourine or oxygen + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("F1") ), map.invalid_key() ); + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("O1") ), map.invalid_key() ); + } + + // Now go the other direction + void test_substitute_rev() { + protocols::drug_design::SubstituentReplace replace; + + replace.template_database( "protocols/drug_design/pyrimidine_stub1.sdf"); + replace.substituents_database( "protocols/drug_design/carboxypyrimidine.sdf"); + + utility::vector1< core::chemical::MutableResidueTypeOP > restypes( core::chemical::sdf::convert_to_ResidueTypes("protocols/drug_design/ethoxypyrimidine.sdf") ); + TS_ASSERT( restypes.size() > 0 ); + core::chemical::MutableResidueTypeOP restype( restypes[1] ); + + core::chemical::NameVDMapping orig_map( *restype ); + + TS_ASSERT_EQUALS( restype->natoms(), 17 ); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 11 ); + + //orig_map.show(TR); + + TS_ASSERT( restype->has("O1") ); + TS_ASSERT( restype->has("N1") ); + TS_ASSERT( restype->has("N2") ); + TS_ASSERT( restype->has("F1") ); + TS_ASSERT( restype->has("CL1") ); + + replace.apply(*restype); + TS_ASSERT_EQUALS( replace.get_last_status(), core::chemical::modifications::SUCCESS ); + + TS_ASSERT_EQUALS( restype->natoms(), 13 ); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 10 ); + + // Keeps the nitrogens + TS_ASSERT( restype->has("N1") ); + TS_ASSERT( restype->has("N2") ); + TS_ASSERT( restype->has("O1") ); // Has the two oxygens + TS_ASSERT( restype->has("O2") ); + TS_ASSERT( restype->has("CL1") ); // keeps the chlorine + TS_ASSERT( ! restype->has("F1") ); // loses the flurine + + core::chemical::VDVDMapping map( replace.get_mapping() ); + + TS_ASSERT_EQUALS( map[ orig_map[ " F1 " ] ], map.invalid_entry() ); + TS_ASSERT_EQUALS( map[ orig_map[ " O1 " ] ], map.invalid_entry() ); + + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " N1 " ] ] ), " N1 " ); + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " N2 " ] ] ), " N2 " ); + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " CL1" ] ] ), " CL1" ); + + // Shouldn't map anything on the oxygens + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("O1") ), map.invalid_key() ); + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("O2") ), map.invalid_key() ); + } + +}; diff --git a/source/test/protocols/drug_design/SubstructureReplace.cxxtest.hh b/source/test/protocols/drug_design/SubstructureReplace.cxxtest.hh new file mode 100644 index 0000000000..8f5db9ba3a --- /dev/null +++ b/source/test/protocols/drug_design/SubstructureReplace.cxxtest.hh @@ -0,0 +1,355 @@ +// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*- +// vi: set ts=2 noet: +// +// (c) Copyright Rosetta Commons Member Institutions. +// (c) This file is part of the Rosetta software suite and is made available under license. +// (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +// (c) For more information, see http://www.rosettacommons.org. Questions about this can be +// (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu. + +/// @file protocols/drug_design/SubstructureReplace.cxxtest.hh +/// @brief test for SubstructureReplace Chemistry +/// @author Rocco Moretti (rmorettiase@gmail.com) + +// Test headers +#include +#include +#include + +// Project Headers +#include + +#include +#include +#include +#include + +// Utility Headers +#include +#include + +static basic::Tracer TR("protocols.drug_design.SubstructureReplace.cxxtest.hh"); + +// --------------- Test Class --------------- // + +class SubstructureReplaceTests : public CxxTest::TestSuite { + +private: + core::chemical::ResidueTypeOP restype_; + +public: + + void setUp() { + core_init(); + } + + void tearDown() { + } + + void test_remove_hydro() { + protocols::drug_design::SubstructureReplace replace; + + replace.substructure_database( "protocols/drug_design/benzene_stub.sdf", /*append=*/ true); + replace.substructure_database( "protocols/drug_design/pyrimidine_stub.sdf", /*append=*/ true); + replace.distance_threshold(1.5); + + utility::vector1< core::chemical::MutableResidueTypeOP > restypes( core::chemical::sdf::convert_to_ResidueTypes("protocols/drug_design/IClBrbenzene.sdf") ); + TS_ASSERT( restypes.size() > 0 ); + core::chemical::MutableResidueTypeOP restype( restypes[1] ); + + core::chemical::NameVDMapping orig_map( *restype ); + + TS_ASSERT_EQUALS( restype->natoms(), 12 ); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 9 ); + + //orig_map.show(TR); + + TS_ASSERT( restype->has("CL1") ); + TS_ASSERT( restype->has("I1") ); + TS_ASSERT( restype->has("BR1") ); + TS_ASSERT( restype->has("C4") ); + + replace.apply(*restype); + TS_ASSERT_EQUALS( replace.get_last_status(), core::chemical::modifications::SUCCESS ); + + TS_ASSERT_EQUALS( restype->natoms(), 10 ); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 9 ); + + // Need to have the new nitrogens + TS_ASSERT( restype->has("N1") ); + TS_ASSERT_EQUALS( restype->atom_type( restype->atom_vertex("N1") ).atom_type_name(), "Nhis" ); // No hydrogen + TS_ASSERT( restype->has("N2") ); + TS_ASSERT_EQUALS( restype->atom_type( restype->atom_vertex("N2") ).atom_type_name(), "Nhis" ); // No hydrogen + + core::chemical::VDVDMapping map( replace.get_mapping() ); + + //core::chemical::VDNameMapping n_map( *restype ); + //map.show(TR); + //TR << "---------------------------------------------------------------" << std::endl; + //n_map.show(TR); + //TR << "---------------------------------------------------------------" << std::endl; + //orig_map.downstream_combine( map ).downstream_combine( n_map ).show( TR ); + + TS_ASSERT_DIFFERS( map[ orig_map[ " CL1" ] ], map.invalid_entry() ); + TS_ASSERT_DIFFERS( map[ orig_map[ " I1 " ] ], map.invalid_entry() ); + TS_ASSERT_DIFFERS( map[ orig_map[ " BR1" ] ], map.invalid_entry() ); + TS_ASSERT_EQUALS( map[ orig_map[ " C4 " ] ], map.invalid_entry() ); // The rings shouldn't be mapped. + + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " CL1" ] ] ), " CL1" ); + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " I1 " ] ] ), " I1 " ); + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " BR1" ] ] ), " BR1" ); + + // Shouldn't map anything on the nitrogens. + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("N1") ), map.invalid_key() ); + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("N2") ), map.invalid_key() ); + + } + + void test_no_stub_match() { + protocols::drug_design::SubstructureReplace replace; + + replace.substructure_database( "protocols/drug_design/benzene_stub.sdf", /*append=*/ true); + replace.substructure_database( "protocols/drug_design/pyridazine_stub.sdf", /*append=*/ true); + replace.distance_threshold(1.5); + + utility::vector1< core::chemical::MutableResidueTypeOP > restypes( core::chemical::sdf::convert_to_ResidueTypes("protocols/drug_design/IClBrbenzene.sdf") ); + TS_ASSERT( restypes.size() > 0 ); + core::chemical::MutableResidueTypeOP restype( restypes[1] ); + + replace.apply(*restype); + + // Shouldn't be able to find a match. + TS_ASSERT_EQUALS( replace.get_last_status(), core::chemical::modifications::FAIL_RETRY ); + } + + void test_add_hydro() { + protocols::drug_design::SubstructureReplace replace; + + replace.substructure_database( "protocols/drug_design/benzene_stub.sdf", /*append=*/ true); + replace.substructure_database( "protocols/drug_design/pyrimidine_stub.sdf", /*append=*/ true); + replace.distance_threshold(1.5); + + utility::vector1< core::chemical::MutableResidueTypeOP > restypes( core::chemical::sdf::convert_to_ResidueTypes("protocols/drug_design/IClBrpyrimidine.sdf") ); + TS_ASSERT( restypes.size() > 0 ); + core::chemical::MutableResidueTypeOP restype( restypes[1] ); + + core::chemical::NameVDMapping orig_map( *restype ); + + TS_ASSERT_EQUALS( restype->natoms(), 10 ); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 9 ); + + TS_ASSERT( restype->has("CL1") ); + TS_ASSERT( restype->has("I1") ); + TS_ASSERT( restype->has("BR1") ); + TS_ASSERT( restype->has("C4") ); + TS_ASSERT( restype->has("H1") ); + + replace.apply(*restype); + TS_ASSERT_EQUALS( replace.get_last_status(), core::chemical::modifications::SUCCESS ); + + TS_ASSERT_EQUALS( restype->natoms(), 12 ); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 9 ); + + // Need to have removed the nitrogens + TS_ASSERT( ! restype->has("N1") ); + TS_ASSERT( ! restype->has("N2") ); + + core::chemical::VDVDMapping map( replace.get_mapping() ); + + //core::chemical::VDNameMapping n_map( *restype ); + //map.show(TR); + //TR << "---------------------------------------------------------------" << std::endl; + //n_map.show(TR); + //TR << "---------------------------------------------------------------" << std::endl; + //orig_map.downstream_combine( map ).downstream_combine( n_map ).show( TR ); + + TS_ASSERT_DIFFERS( map[ orig_map[ " CL1" ] ], map.invalid_entry() ); + TS_ASSERT_DIFFERS( map[ orig_map[ " BR1" ] ], map.invalid_entry() ); + TS_ASSERT_DIFFERS( map[ orig_map[ " I1 " ] ], map.invalid_entry() ); + TS_ASSERT_DIFFERS( map[ orig_map[ " H1 " ] ], map.invalid_entry() ); + TS_ASSERT_EQUALS( map[ orig_map[ " C4 " ] ], map.invalid_entry() ); // The rings shouldn't be mapped. + + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " CL1" ] ] ), " CL1" ); + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " BR1" ] ] ), " BR1" ); + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " I1 " ] ] ), " I1 " ); + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " H1 " ] ] ), " H1 " ); + + // Should have three hydrogens, each connected to a carbon. + TS_ASSERT( restype->has("H1") ); + TS_ASSERT( restype->has("H2") ); + TS_ASSERT( restype->has("H3") ); + + TS_ASSERT_EQUALS(restype->atom( restype->atom_base( restype->atom_vertex("H1") ) ).element(), core::chemical::element::C); + TS_ASSERT_EQUALS(restype->atom( restype->atom_base( restype->atom_vertex("H2") ) ).element(), core::chemical::element::C); + TS_ASSERT_EQUALS(restype->atom( restype->atom_base( restype->atom_vertex("H3") ) ).element(), core::chemical::element::C); + + // Should have six carbons, each connected to a carbon. They shouldn't map backwards + TS_ASSERT( restype->has("C1") ); + TS_ASSERT( restype->has("C2") ); + TS_ASSERT( restype->has("C3") ); + TS_ASSERT( restype->has("C4") ); + TS_ASSERT( restype->has("C5") ); + TS_ASSERT( restype->has("C6") ); + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("C1") ), map.invalid_key() ); + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("C2") ), map.invalid_key() ); + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("C3") ), map.invalid_key() ); + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("C4") ), map.invalid_key() ); + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("C5") ), map.invalid_key() ); + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("C6") ), map.invalid_key() ); + } + + void test_double_bonds() { + protocols::drug_design::SubstructureReplace replace; + replace.distance_threshold(1.5); + + replace.substructure_database( "protocols/drug_design/ethylbenzene_stub.sdf", /*append=*/ true); + replace.substructure_database( "protocols/drug_design/ethylpyrimidine_stub.sdf", /*append=*/ true); + + utility::vector1< core::chemical::MutableResidueTypeOP > restypes( core::chemical::sdf::convert_to_ResidueTypes("protocols/drug_design/methylclstyrene.sdf") ); + TS_ASSERT( restypes.size() > 0 ); + core::chemical::MutableResidueTypeOP restype( restypes[1] ); + + core::chemical::NameVDMapping orig_map( *restype ); + + TS_ASSERT_EQUALS( restype->natoms(), 19 ); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 10 ); + + TS_ASSERT( restype->has("CL1") ); + + replace.apply(*restype); + TS_ASSERT_EQUALS( replace.get_last_status(), core::chemical::modifications::FAIL_DO_NOT_RETRY ); // Can't find template + + replace.substructure_database( "protocols/drug_design/styrene_stub.sdf", /*append=*/ true); + + replace.apply(*restype); + TS_ASSERT_EQUALS( replace.get_last_status(), core::chemical::modifications::FAIL_RETRY ); // Can find template, can't find replacement + + replace.substructure_database( "protocols/drug_design/vinylpyrimidine_stub.sdf", /*append=*/ true); + + replace.apply(*restype); + TS_ASSERT_EQUALS( replace.get_last_status(), core::chemical::modifications::SUCCESS ); + + TS_ASSERT_EQUALS( restype->natoms(), 17 ); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 10 ); + + core::chemical::VDVDMapping map( replace.get_mapping() ); + + TS_ASSERT_DIFFERS( map[ orig_map[ " CL1" ] ], map.invalid_entry() ); + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " CL1" ] ] ), " CL1" ); + + // Need to have the new nitrogens + TS_ASSERT( restype->has("N1") ); + TS_ASSERT_EQUALS( restype->atom_type( restype->atom_vertex("N1")).atom_type_name(), "Nhis" ); // No hydrogen + TS_ASSERT( restype->has("N2") ); + TS_ASSERT_EQUALS( restype->atom_type( restype->atom_vertex("N2")).atom_type_name(), "Nhis" ); // No hydrogen + + // Shouldn't map anything on the nitrogens. + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("N1") ), map.invalid_key() ); + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("N2") ), map.invalid_key() ); + } + + void test_HV_items() { + protocols::drug_design::SubstructureReplace replace; + + replace.substructure_database( "protocols/drug_design/benzene.sdf", /*append=*/ true); + replace.substructure_database( "protocols/drug_design/pyrimidine_Vstub.sdf", /*append=*/ true); + replace.distance_threshold(1.5); + + utility::vector1< core::chemical::MutableResidueTypeOP > restypes( core::chemical::sdf::convert_to_ResidueTypes("protocols/drug_design/IClBrbenzene.sdf") ); + TS_ASSERT( restypes.size() > 0 ); + core::chemical::MutableResidueTypeOP restype( restypes[1] ); + + core::chemical::NameVDMapping orig_map( *restype ); + + TS_ASSERT_EQUALS( restype->natoms(), 12 ); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 9 ); + + //orig_map.show(TR); + + TS_ASSERT( restype->has("CL1") ); + TS_ASSERT( restype->has("I1") ); + TS_ASSERT( restype->has("BR1") ); + TS_ASSERT( restype->has("C4") ); + + replace.apply(*restype); + TS_ASSERT_EQUALS( replace.get_last_status(), core::chemical::modifications::FAIL_DO_NOT_RETRY ); // Template can't be found + + replace.H_as_dummy( true ); + replace.substructure_database( "protocols/drug_design/benzene.sdf", /*append=*/ true); + + replace.apply(*restype); + TS_ASSERT_EQUALS( replace.get_last_status(), core::chemical::modifications::FAIL_RETRY ); // replace can't be found + + replace.V_as_dummy( true ); + replace.substructure_database( "protocols/drug_design/pyrimidine_Vstub.sdf", /*append=*/ true); + + replace.apply(*restype); + TS_ASSERT_EQUALS( replace.get_last_status(), core::chemical::modifications::SUCCESS ); + + TS_ASSERT_EQUALS( restype->natoms(), 10 ); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 9 ); + + // Need to have the new nitrogens + TS_ASSERT( restype->has("N1") ); + TS_ASSERT_EQUALS( restype->atom_type( restype->atom_vertex("N1")).atom_type_name(), "Nhis" ); // No hydrogen + TS_ASSERT( restype->has("N2") ); + TS_ASSERT_EQUALS( restype->atom_type( restype->atom_vertex("N2")).atom_type_name(), "Nhis" ); // No hydrogen + + core::chemical::VDVDMapping map( replace.get_mapping() ); + + //core::chemical::VDNameMapping n_map( *restype ); + //map.show(TR); + //TR << "---------------------------------------------------------------" << std::endl; + //n_map.show(TR); + //TR << "---------------------------------------------------------------" << std::endl; + //orig_map.downstream_combine( map ).downstream_combine( n_map ).show( TR ); + + TS_ASSERT_DIFFERS( map[ orig_map[ " CL1" ] ], map.invalid_entry() ); + TS_ASSERT_DIFFERS( map[ orig_map[ " I1 " ] ], map.invalid_entry() ); + TS_ASSERT_DIFFERS( map[ orig_map[ " BR1" ] ], map.invalid_entry() ); + TS_ASSERT_EQUALS( map[ orig_map[ " C4 " ] ], map.invalid_entry() ); // The rings shouldn't be mapped. + + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " CL1" ] ] ), " CL1" ); + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " I1 " ] ] ), " I1 " ); + TS_ASSERT_EQUALS( restype->atom_name( map[ orig_map[ " BR1" ] ] ), " BR1" ); + + // Shouldn't map anything on the nitrogens. + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("N1") ), map.invalid_key() ); + TS_ASSERT_EQUALS( map.reverse_lookup( restype->atom_vertex("N2") ), map.invalid_key() ); + } + + // @brief Test if a structure which has extra hydrogens can gracefully delete them. + void test_dropped_hydrogens() { + protocols::drug_design::SubstructureReplace replace; + + replace.H_as_dummy( true ); + replace.substructure_database( "protocols/drug_design/benzene.sdf", /*append=*/ true); + replace.substructure_database( "protocols/drug_design/piperazine_stub.sdf", /*append=*/ true); + replace.distance_threshold(1.5); + + utility::vector1< core::chemical::MutableResidueTypeOP > restypes( core::chemical::sdf::convert_to_ResidueTypes("protocols/drug_design/piperazine_prot.sdf") ); + TS_ASSERT( restypes.size() > 0 ); + core::chemical::MutableResidueTypeOP restype( restypes[1] ); + + TS_ASSERT_EQUALS( restype->natoms(), 18 ); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 6 ); + + replace.apply(*restype); + TS_ASSERT_EQUALS( replace.get_last_status(), core::chemical::modifications::SUCCESS ); + + TS_ASSERT_EQUALS( restype->natoms(), 12 ); + TS_ASSERT_EQUALS( restype->nheavyatoms(), 6 ); + + // But it shouldn't work if the items are not hydrogens + utility::vector1< core::chemical::MutableResidueTypeOP > restypes2( core::chemical::sdf::convert_to_ResidueTypes("protocols/drug_design/piperazine_quat.sdf") ); + TS_ASSERT( restypes2.size() > 0 ); + core::chemical::MutableResidueTypeOP restype2( restypes2[1] ); + + TS_ASSERT_EQUALS( restype2->natoms(), 30 ); + TS_ASSERT_EQUALS( restype2->nheavyatoms(), 10 ); + + replace.apply(*restype2); + TS_ASSERT_EQUALS( replace.get_last_status(), core::chemical::modifications::FAIL_DO_NOT_RETRY ); + } + +}; diff --git a/source/test/protocols/drug_design/amide_rxn.txt b/source/test/protocols/drug_design/amide_rxn.txt new file mode 100644 index 0000000000..ff8ca7f016 --- /dev/null +++ b/source/test/protocols/drug_design/amide_rxn.txt @@ -0,0 +1,2 @@ + +[C;h:1](=[O:2])[#6:4].[N;!D3;h:3]>>[#6:4][C:1](=[O:2])-!@[N:3] 1.0 diff --git a/source/test/protocols/drug_design/benzene.sdf b/source/test/protocols/drug_design/benzene.sdf new file mode 100644 index 0000000000..61b931c7e4 --- /dev/null +++ b/source/test/protocols/drug_design/benzene.sdf @@ -0,0 +1,30 @@ + + OpenBabel09031509533D + + 12 12 0 0 0 0 0 0 0 0999 V2000 + -0.7599 1.1696 -0.0016 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6329 1.2451 -0.0005 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3948 0.0765 0.0009 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7640 -1.1674 0.0012 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6288 -1.2431 0.0005 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3906 -0.0746 -0.0009 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3535 2.0797 -0.0030 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1244 2.2142 -0.0010 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4801 0.1357 0.0020 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3576 -2.0779 0.0029 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.1203 -2.2122 0.0011 H 0 0 0 0 0 0 0 0 0 0 0 0 + -2.4758 -0.1336 -0.0018 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 2 2 0 0 0 0 + 1 7 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 8 1 0 0 0 0 + 3 4 2 0 0 0 0 + 3 9 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 10 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 11 1 0 0 0 0 + 6 12 1 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/benzene_stub.sdf b/source/test/protocols/drug_design/benzene_stub.sdf new file mode 100644 index 0000000000..c64b1db21c --- /dev/null +++ b/source/test/protocols/drug_design/benzene_stub.sdf @@ -0,0 +1,30 @@ + + OpenBabel09031509533D + + 12 12 0 0 0 0 0 0 0 0999 V2000 + -0.7599 1.1696 -0.0016 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6329 1.2451 -0.0005 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3948 0.0765 0.0009 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7640 -1.1674 0.0012 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6288 -1.2431 0.0005 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3906 -0.0746 -0.0009 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3535 2.0797 -0.0030 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1244 2.2142 -0.0010 * 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4801 0.1357 0.0020 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3576 -2.0779 0.0029 * 0 0 0 0 0 0 0 0 0 0 0 0 + -1.1203 -2.2122 0.0011 * 0 0 0 0 0 0 0 0 0 0 0 0 + -2.4758 -0.1336 -0.0018 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 2 2 0 0 0 0 + 1 7 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 8 1 0 0 0 0 + 3 4 2 0 0 0 0 + 3 9 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 10 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 11 1 0 0 0 0 + 6 12 1 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/ester_rxn.txt b/source/test/protocols/drug_design/ester_rxn.txt new file mode 100644 index 0000000000..3a652e9720 --- /dev/null +++ b/source/test/protocols/drug_design/ester_rxn.txt @@ -0,0 +1,2 @@ + +[C;h:1](=[O:2])[#6:4].[O;h:3]>>[#6:4][C:1](=[O:2])-!@[O:3] 1.0 diff --git a/source/test/protocols/drug_design/ester_rxn2.txt b/source/test/protocols/drug_design/ester_rxn2.txt new file mode 100644 index 0000000000..1711af4a6c --- /dev/null +++ b/source/test/protocols/drug_design/ester_rxn2.txt @@ -0,0 +1,2 @@ + +[O;h:3].[C;h:1](=[O:2])[#6:4]>>[#6:4][C:1](=[O:2])-!@[O:3] 1.0 diff --git a/source/test/protocols/drug_design/ethoxypyrimidine.sdf b/source/test/protocols/drug_design/ethoxypyrimidine.sdf new file mode 100644 index 0000000000..b35ffde1c8 --- /dev/null +++ b/source/test/protocols/drug_design/ethoxypyrimidine.sdf @@ -0,0 +1,40 @@ + + OpenBabel09181513003D + + 17 17 0 0 1 0 0 0 0 0999 V2000 + 9.7591 5.2780 -2.8845 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8038 4.2253 -2.7025 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1244 3.4070 -1.4503 C 0 0 3 0 0 0 0 0 0 0 0 0 + 8.7891 4.1571 -0.3518 F 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3905 2.0824 -1.4885 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0388 2.0995 -1.5451 N 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4124 0.9004 -1.5486 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0742 -0.3042 -1.5121 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2484 -1.7999 -1.5189 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4453 -0.2214 -1.4743 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1275 0.9479 -1.4662 N 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3957 5.8783 -3.5591 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8352 3.6051 -3.6053 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8112 4.6823 -2.6187 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.2041 3.2318 -1.3837 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3282 0.9492 -1.5808 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0649 -1.1135 -1.4460 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 1 12 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 13 1 0 0 0 0 + 2 14 1 0 0 0 0 + 3 5 1 0 0 0 0 + 3 4 1 0 0 0 0 + 3 15 1 0 0 0 0 + 5 11 1 0 0 0 0 + 5 6 2 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 2 0 0 0 0 + 7 16 1 0 0 0 0 + 8 10 1 0 0 0 0 + 8 9 1 0 0 0 0 + 10 11 2 0 0 0 0 + 10 17 1 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/ethylbenzene_stub.sdf b/source/test/protocols/drug_design/ethylbenzene_stub.sdf new file mode 100644 index 0000000000..7a9ea80331 --- /dev/null +++ b/source/test/protocols/drug_design/ethylbenzene_stub.sdf @@ -0,0 +1,36 @@ + + OpenBabel09081510163D + + 15 15 0 0 0 0 0 0 0 0999 V2000 + -0.6919 1.1551 -0.0244 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7023 1.2307 0.0316 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4645 0.0647 0.0373 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.8336 -1.1761 -0.0190 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.5612 -1.2511 -0.0742 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3406 -0.0864 -0.0649 C 0 0 0 0 0 0 0 0 0 0 0 0 + -2.8067 -0.1182 -0.1543 C 0 0 0 0 0 0 0 0 0 0 0 0 + -3.5418 -1.2359 -0.2218 * 0 0 0 0 0 0 0 0 0 0 0 0 + -1.2682 2.0769 -0.0279 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1916 2.2002 0.0704 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5489 0.1226 0.0796 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4256 -2.0876 -0.0246 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.0294 -2.2304 -0.1371 H 0 0 0 0 0 0 0 0 0 0 0 0 + -3.0527 0.4404 -1.0331 H 0 0 0 0 0 0 0 0 0 0 0 0 + -3.0706 0.2318 0.8218 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 2 2 0 0 0 0 + 1 9 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 10 1 0 0 0 0 + 3 4 2 0 0 0 0 + 3 11 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 12 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 13 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 14 1 0 0 0 0 + 7 15 1 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/ethylpyrimidine_stub.sdf b/source/test/protocols/drug_design/ethylpyrimidine_stub.sdf new file mode 100644 index 0000000000..cb444e0811 --- /dev/null +++ b/source/test/protocols/drug_design/ethylpyrimidine_stub.sdf @@ -0,0 +1,32 @@ + + OpenBabel09081510153D + + 13 13 0 0 0 0 0 0 0 0999 V2000 + -0.6237 1.2284 -0.0867 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7499 1.1472 -0.0719 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4247 -0.0070 0.0640 N 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6679 -1.1066 0.1880 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6718 -1.1480 0.1874 N 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3042 0.0347 0.0484 C 0 0 0 0 0 0 0 0 0 0 0 0 + -2.7754 0.0230 0.0439 C 0 0 0 0 0 0 0 0 0 0 0 0 + -3.5249 -1.0812 0.1685 * 0 0 0 0 0 0 0 0 0 0 0 0 + -1.1265 2.1788 -0.1984 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3655 2.0362 -0.1721 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1879 -2.0516 0.2991 H 0 0 0 0 0 0 0 0 0 0 0 0 + -3.0640 0.4633 -0.8876 H 0 0 0 0 0 0 0 0 0 0 0 0 + -2.9810 0.5089 0.9748 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 2 2 0 0 0 0 + 1 9 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 10 1 0 0 0 0 + 3 4 2 0 0 0 0 + 4 5 1 0 0 0 0 + 4 11 1 0 0 0 0 + 5 6 2 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 12 1 0 0 0 0 + 7 13 1 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/methylclstyrene.sdf b/source/test/protocols/drug_design/methylclstyrene.sdf new file mode 100644 index 0000000000..46abadeb8c --- /dev/null +++ b/source/test/protocols/drug_design/methylclstyrene.sdf @@ -0,0 +1,44 @@ +MethylChloroStyrene + OpenBabel09081509583D + + 19 19 0 0 0 0 0 0 0 0999 V2000 + -0.5923 1.1559 0.1668 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7988 1.1539 0.2915 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.5151 -0.0250 0.0937 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.8433 -1.2021 -0.2317 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.5475 -1.2010 -0.3572 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.2755 -0.0240 -0.1494 C 0 0 0 0 0 0 0 0 0 0 0 0 + -2.7343 0.0093 -0.3045 C 0 0 0 0 0 0 0 0 0 0 0 0 + -3.6339 -0.6634 0.4329 C 0 0 0 0 0 0 0 0 0 0 0 0 + -3.1904 -1.6947 1.7528 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + -5.1111 -0.5670 0.2065 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.1382 2.0827 0.3264 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3219 2.0721 0.5446 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5979 -0.0270 0.1930 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4007 -2.1219 -0.3875 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.0577 -2.1258 -0.6165 H 0 0 0 0 0 0 0 0 0 0 0 0 + -3.0784 0.6684 -1.0983 H 0 0 0 0 0 0 0 0 0 0 0 0 + -5.3543 0.1050 -0.6234 H 0 0 0 0 0 0 0 0 0 0 0 0 + -5.6141 -0.1834 1.1005 H 0 0 0 0 0 0 0 0 0 0 0 0 + -5.5281 -1.5511 -0.0324 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 2 2 0 0 0 0 + 1 11 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 12 1 0 0 0 0 + 3 4 2 0 0 0 0 + 3 13 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 14 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 15 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 2 3 0 0 0 + 7 16 1 0 0 0 0 + 8 10 1 0 0 0 0 + 8 9 1 0 0 0 0 + 10 17 1 0 0 0 0 + 10 18 1 0 0 0 0 + 10 19 1 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/piperazine_prot.sdf b/source/test/protocols/drug_design/piperazine_prot.sdf new file mode 100644 index 0000000000..559bc8d4ce --- /dev/null +++ b/source/test/protocols/drug_design/piperazine_prot.sdf @@ -0,0 +1,43 @@ + + OpenBabel09141517073D + + 18 18 0 0 0 0 0 0 0 0999 V2000 + -0.7614 -1.1949 -0.3379 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.5501 -0.0007 0.0362 N 0 3 0 0 0 0 0 0 0 0 0 0 + -0.7614 1.1936 -0.3367 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6542 1.1933 0.2907 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4429 -0.0007 -0.0836 N 0 3 0 0 0 0 0 0 0 0 0 0 + 0.6541 -1.1953 0.2896 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6728 -1.2771 -1.4300 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.2812 -2.1030 -0.0090 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.2811 2.1016 -0.0070 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6727 1.2770 -1.4289 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5656 1.2755 1.3828 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1741 2.1015 -0.0382 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1739 -2.1031 -0.0402 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5655 -1.2784 1.3815 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3317 -0.0009 0.4168 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6389 -0.0002 -1.0846 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.7259 -0.0012 1.0409 H 0 0 0 0 0 0 0 0 0 0 0 0 + -2.4488 -0.0004 -0.4463 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 1 6 1 0 0 0 0 + 1 7 1 0 0 0 0 + 1 8 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 17 1 0 0 0 0 + 2 18 1 0 0 0 0 + 3 4 1 0 0 0 0 + 3 9 1 0 0 0 0 + 3 10 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 11 1 0 0 0 0 + 4 12 1 0 0 0 0 + 5 6 1 0 0 0 0 + 5 15 1 0 0 0 0 + 5 16 1 0 0 0 0 + 6 13 1 0 0 0 0 + 6 14 1 0 0 0 0 +M CHG 2 2 1 5 1 +M END +$$$$ diff --git a/source/test/protocols/drug_design/piperazine_quat.sdf b/source/test/protocols/drug_design/piperazine_quat.sdf new file mode 100644 index 0000000000..7136dafd40 --- /dev/null +++ b/source/test/protocols/drug_design/piperazine_quat.sdf @@ -0,0 +1,67 @@ + + OpenBabel09141517143D + + 30 30 0 0 0 0 0 0 0 0999 V2000 + 0.7000 1.2797 -0.1717 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7670 1.2685 0.3197 N 0 0 0 0 0 0 0 0 0 0 0 0 + -0.8449 1.4036 1.8469 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.4875 2.5006 -0.2719 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.4990 -0.0039 -0.1693 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7356 -1.3126 0.1664 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7328 -1.3036 -0.3224 N 0 0 0 0 0 0 0 0 0 0 0 0 + 0.8124 -1.4443 -1.8493 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4510 -2.5323 0.2751 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4653 -0.0284 0.1621 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6696 1.4335 -1.2557 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1928 2.1438 0.2889 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.3410 0.5503 2.3038 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.8993 1.4218 2.1366 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.3493 2.3355 2.1346 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.4472 2.4298 -1.3625 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.9676 3.3983 0.0746 H 0 0 0 0 0 0 0 0 0 0 0 0 + -2.5236 2.4895 0.0764 H 0 0 0 0 0 0 0 0 0 0 0 0 + -2.4932 -0.0074 0.2909 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.6177 0.0964 -1.2542 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7072 -1.4656 1.2505 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.2279 -2.1765 -0.2942 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.3169 -0.5882 -2.3099 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.3094 -2.3728 -2.1342 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.8669 -1.4729 -2.1365 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4091 -2.4576 1.3656 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4886 -2.5239 -0.0712 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.9316 -3.4320 -0.0685 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.5889 -0.1276 1.2458 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4576 -0.0252 -0.3026 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 10 1 0 0 0 0 + 1 2 1 0 0 0 0 + 1 11 1 0 0 0 0 + 1 12 1 0 0 0 0 + 2 5 1 0 0 0 0 + 2 4 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 13 1 0 0 0 0 + 3 14 1 0 0 0 0 + 3 15 1 0 0 0 0 + 4 16 1 0 0 0 0 + 4 17 1 0 0 0 0 + 4 18 1 0 0 0 0 + 5 6 1 0 0 0 0 + 5 19 1 0 0 0 0 + 5 20 1 0 0 0 0 + 6 7 1 0 0 0 0 + 6 21 1 0 0 0 0 + 6 22 1 0 0 0 0 + 7 10 1 0 0 0 0 + 7 9 1 0 0 0 0 + 7 8 1 0 0 0 0 + 8 23 1 0 0 0 0 + 8 24 1 0 0 0 0 + 8 25 1 0 0 0 0 + 9 26 1 0 0 0 0 + 9 27 1 0 0 0 0 + 9 28 1 0 0 0 0 + 10 29 1 0 0 0 0 + 10 30 1 0 0 0 0 +M CHG 2 2 1 7 1 +M END +$$$$ diff --git a/source/test/protocols/drug_design/piperazine_stub.sdf b/source/test/protocols/drug_design/piperazine_stub.sdf new file mode 100644 index 0000000000..e42e4d2160 --- /dev/null +++ b/source/test/protocols/drug_design/piperazine_stub.sdf @@ -0,0 +1,38 @@ + + OpenBabel09141517063D + + 16 16 0 0 0 0 0 0 0 0999 V2000 + 0.6744 1.2418 -0.2310 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7308 1.2766 0.2046 N 0 0 0 0 0 0 0 0 0 0 0 0 + -1.4063 0.0426 -0.2298 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6716 -1.2339 0.2285 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7345 -1.2692 -0.2078 N 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4108 -0.0349 0.2263 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7214 1.3228 -1.3241 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1897 2.1175 0.1789 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.1928 2.0770 -0.2238 H 0 0 0 0 0 0 0 0 0 0 0 0 + -2.4229 0.0367 0.1800 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.5006 0.0421 -1.3230 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7174 -1.3144 1.3218 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.1862 -2.1104 -0.1801 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1961 -2.0697 0.2208 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.5056 -0.0350 1.3197 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4264 -0.0296 -0.1842 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 2 1 0 0 0 0 + 1 7 1 0 0 0 0 + 1 8 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 9 1 0 0 0 0 + 3 4 1 0 0 0 0 + 3 10 1 0 0 0 0 + 3 11 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 12 1 0 0 0 0 + 4 13 1 0 0 0 0 + 5 6 1 0 0 0 0 + 5 14 1 0 0 0 0 + 6 15 1 0 0 0 0 + 6 16 1 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/pyridazine_stub.sdf b/source/test/protocols/drug_design/pyridazine_stub.sdf new file mode 100644 index 0000000000..fb83d7d347 --- /dev/null +++ b/source/test/protocols/drug_design/pyridazine_stub.sdf @@ -0,0 +1,26 @@ + + OpenBabel09031514043D + + 10 10 0 0 0 0 0 0 0 0999 V2000 + -0.7524 1.1622 0.0085 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6237 1.1828 0.0253 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3838 0.0724 0.0537 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7125 -1.0900 0.0650 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6223 -1.2320 0.0508 N 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3289 -0.0868 0.0225 N 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3409 2.0681 -0.0143 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1718 2.1206 0.0162 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3034 -1.9989 0.0879 * 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4150 0.1120 0.0660 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 2 2 0 0 0 0 + 1 7 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 8 1 0 0 0 0 + 3 4 2 0 0 0 0 + 3 10 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 9 1 0 0 0 0 + 5 6 2 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/pyrimidine_Vstub.sdf b/source/test/protocols/drug_design/pyrimidine_Vstub.sdf new file mode 100644 index 0000000000..3ad7610791 --- /dev/null +++ b/source/test/protocols/drug_design/pyrimidine_Vstub.sdf @@ -0,0 +1,26 @@ + + OpenBabel09031509533D + + 10 10 0 0 0 0 0 0 0 0999 V2000 + -0.7524 1.1622 0.0085 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6237 1.1828 0.0253 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3838 0.0724 0.0537 N 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7125 -1.0900 0.0650 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6223 -1.2320 0.0508 N 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3289 -0.0868 0.0225 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3409 2.0681 -0.0143 V 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1718 2.1206 0.0162 V 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3034 -1.9989 0.0879 V 0 0 0 0 0 0 0 0 0 0 0 0 + -2.4081 -0.2074 0.0110 V 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 2 2 0 0 0 0 + 1 7 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 8 1 0 0 0 0 + 3 4 2 0 0 0 0 + 4 5 1 0 0 0 0 + 4 9 1 0 0 0 0 + 5 6 2 0 0 0 0 + 6 10 1 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/pyrimidine_stub.sdf b/source/test/protocols/drug_design/pyrimidine_stub.sdf new file mode 100644 index 0000000000..e867f8e07b --- /dev/null +++ b/source/test/protocols/drug_design/pyrimidine_stub.sdf @@ -0,0 +1,26 @@ + + OpenBabel09031509533D + + 10 10 0 0 0 0 0 0 0 0999 V2000 + -0.7524 1.1622 0.0085 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6237 1.1828 0.0253 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3838 0.0724 0.0537 N 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7125 -1.0900 0.0650 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6223 -1.2320 0.0508 N 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3289 -0.0868 0.0225 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3409 2.0681 -0.0143 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1718 2.1206 0.0162 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3034 -1.9989 0.0879 * 0 0 0 0 0 0 0 0 0 0 0 0 + -2.4081 -0.2074 0.0110 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 2 2 0 0 0 0 + 1 7 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 8 1 0 0 0 0 + 3 4 2 0 0 0 0 + 4 5 1 0 0 0 0 + 4 9 1 0 0 0 0 + 5 6 2 0 0 0 0 + 6 10 1 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/pyrimidine_stub1.sdf b/source/test/protocols/drug_design/pyrimidine_stub1.sdf new file mode 100644 index 0000000000..1ddb66bd5f --- /dev/null +++ b/source/test/protocols/drug_design/pyrimidine_stub1.sdf @@ -0,0 +1,20 @@ + + OpenBabel09181513163D + + 7 7 0 0 0 0 0 0 0 0999 V2000 + -0.7103 1.0909 0.0592 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6245 1.2365 0.0464 N 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3339 0.0931 0.0224 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7606 -1.1577 0.0112 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6148 -1.1818 0.0262 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3784 -0.0737 0.0503 N 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3040 1.9987 0.0786 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 2 2 0 0 0 0 + 1 7 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 2 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 2 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/styrene_stub.sdf b/source/test/protocols/drug_design/styrene_stub.sdf new file mode 100644 index 0000000000..cb5ef71ece --- /dev/null +++ b/source/test/protocols/drug_design/styrene_stub.sdf @@ -0,0 +1,34 @@ + + OpenBabel09081510173D + + 14 14 0 0 0 0 0 0 0 0999 V2000 + -0.6919 1.1551 -0.0244 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7023 1.2307 0.0316 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4645 0.0647 0.0373 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.8336 -1.1761 -0.0190 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.5612 -1.2511 -0.0742 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3406 -0.0864 -0.0649 C 0 0 0 0 0 0 0 0 0 0 0 0 + -2.8067 -0.1182 -0.1543 C 0 0 0 0 0 0 0 0 0 0 0 0 + -3.5395 -1.2344 -0.2608 * 0 0 0 0 0 0 0 0 0 0 0 0 + -1.2682 2.0769 -0.0279 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1916 2.2002 0.0704 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5489 0.1226 0.0796 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4256 -2.0876 -0.0246 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.0294 -2.2304 -0.1371 H 0 0 0 0 0 0 0 0 0 0 0 0 + -3.3100 0.8444 -0.1097 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 2 2 0 0 0 0 + 1 9 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 10 1 0 0 0 0 + 3 4 2 0 0 0 0 + 3 11 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 12 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 13 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 2 0 0 0 0 + 7 14 1 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/test_fragments1.sdf b/source/test/protocols/drug_design/test_fragments1.sdf new file mode 100644 index 0000000000..fff192374f --- /dev/null +++ b/source/test/protocols/drug_design/test_fragments1.sdf @@ -0,0 +1,136 @@ + + OpenBabel05061518093D + + 17 16 0 0 1 0 0 0 0 0999 V2000 + 1.4788 0.8789 0.5040 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7980 0.0064 -0.7081 C 0 0 3 0 0 0 0 0 0 0 0 0 + 0.6152 -0.6501 -1.1844 O 0 0 0 0 0 0 0 0 0 0 0 0 + 0.1788 -1.7181 -0.3515 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3237 0.8755 -1.8477 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8192 -0.0060 -2.9608 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9808 0.0058 -3.3588 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3503 1.4649 0.8114 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1694 0.2696 1.3586 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6519 1.5632 0.2855 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5519 -0.7460 -0.4425 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6163 -2.2538 -0.8765 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.2312 -1.3349 0.5860 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.9955 -2.4181 -0.1528 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1530 1.5073 -1.5126 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.5367 1.5148 -2.2621 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0660 -0.6615 -3.4315 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 1 8 1 0 0 0 0 + 1 9 1 0 0 0 0 + 1 10 1 0 0 0 0 + 2 5 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 11 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 12 1 0 0 0 0 + 4 13 1 0 0 0 0 + 4 14 1 0 0 0 0 + 5 6 1 0 0 0 0 + 5 15 1 0 0 0 0 + 5 16 1 0 0 0 0 + 6 7 2 0 0 0 0 + 6 17 1 0 0 0 0 +M END +$$$$ + + OpenBabel05061518093D + + 16 16 0 0 0 0 0 0 0 0999 V2000 + 3.2128 0.0879 -1.0669 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2916 0.0894 0.0115 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7569 -0.8824 1.0579 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6363 -0.4722 2.0695 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0752 -1.3782 3.0376 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6474 -2.7038 2.9982 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7866 -3.1274 1.9876 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3472 -2.2228 1.0194 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8794 0.7233 -1.7207 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3001 -0.1876 -0.3632 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2240 1.1015 0.4249 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9902 0.5547 2.1023 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.7579 -1.0496 3.8179 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9933 -3.4089 3.7487 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4634 -4.1629 1.9478 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6877 -2.5698 0.2260 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 1 9 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 10 1 0 0 0 0 + 2 11 1 0 0 0 0 + 3 4 2 0 0 0 0 + 3 8 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 12 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 13 1 0 0 0 0 + 6 7 1 0 0 0 0 + 6 14 1 0 0 0 0 + 7 8 2 0 0 0 0 + 7 15 1 0 0 0 0 + 8 16 1 0 0 0 0 +M END +$$$$ + + OpenBabel05061518093D + + 27 26 0 0 1 0 0 0 0 0999 V2000 + 4.6433 -4.3168 -1.6569 N 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5665 -4.0321 -2.6042 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5364 -3.0136 -2.0725 C 0 0 3 0 0 0 0 0 0 0 0 0 + 1.7947 -3.5322 -0.8336 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5319 -4.9489 -1.2940 Br 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1655 -1.6387 -1.7361 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8109 -0.9067 -2.9247 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9011 -0.6725 -4.0587 N 0 0 0 0 0 0 0 0 0 0 0 0 + 1.8178 0.2530 -3.7117 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6492 -0.1389 -5.2018 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2126 -5.0806 -2.0227 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2690 -3.5150 -1.5934 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0680 -4.9755 -2.8471 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0054 -3.6768 -3.5438 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7966 -2.8579 -2.8687 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1719 -2.7513 -0.3855 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4608 -3.9322 -0.0646 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9249 -1.7589 -0.9534 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3999 -0.9894 -1.2916 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6672 -1.5011 -3.2644 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2258 0.0453 -2.5675 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1576 -0.1767 -2.9524 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1838 0.4490 -4.5838 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.1975 1.2124 -3.3425 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4249 -0.8460 -5.5180 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.1269 0.8196 -4.9714 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9881 0.0028 -6.0632 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 1 11 1 0 0 0 0 + 1 12 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 13 1 0 0 0 0 + 2 14 1 0 0 0 0 + 3 6 1 0 0 0 0 + 3 4 1 0 0 0 0 + 3 15 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 16 1 0 0 0 0 + 4 17 1 0 0 0 0 + 6 7 1 0 0 0 0 + 6 18 1 0 0 0 0 + 6 19 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 20 1 0 0 0 0 + 7 21 1 0 0 0 0 + 8 10 1 0 0 0 0 + 8 9 1 0 0 0 0 + 9 22 1 0 0 0 0 + 9 23 1 0 0 0 0 + 9 24 1 0 0 0 0 + 10 25 1 0 0 0 0 + 10 26 1 0 0 0 0 + 10 27 1 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/test_fragments2.sdf b/source/test/protocols/drug_design/test_fragments2.sdf new file mode 100644 index 0000000000..181dc6edc2 --- /dev/null +++ b/source/test/protocols/drug_design/test_fragments2.sdf @@ -0,0 +1,178 @@ + + OpenBabel05061518243D + + 23 24 0 0 0 0 0 0 0 0999 V2000 + -0.2967 -0.0875 1.2430 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0963 -0.6235 2.4696 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7251 -0.4788 3.5870 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.9455 0.1879 3.4745 C 0 0 0 0 0 0 0 0 0 0 0 0 + -2.3445 0.7175 2.2451 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.5061 0.6020 1.1332 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.8221 1.0669 -0.1234 O 0 0 0 0 0 0 0 0 0 0 0 0 + -2.5291 2.2431 -0.2412 C 0 0 0 0 0 0 0 0 0 0 0 0 + -2.1721 3.3939 0.4728 C 0 0 0 0 0 0 0 0 0 0 0 0 + -2.8880 4.5795 0.2937 C 0 0 0 0 0 0 0 0 0 0 0 0 + -3.9430 4.6294 -0.6169 C 0 0 0 0 0 0 0 0 0 0 0 0 + -4.2807 3.4946 -1.3559 C 0 0 0 0 0 0 0 0 0 0 0 0 + -3.5714 2.3048 -1.1736 C 0 0 0 0 0 0 0 0 0 0 0 0 + -4.0173 0.9299 -2.1191 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 0.3390 -0.2083 0.3698 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.0388 -1.1582 2.5514 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.4196 -0.8960 4.5432 H 0 0 0 0 0 0 0 0 0 0 0 0 + -2.5917 0.2872 4.3430 H 0 0 0 0 0 0 0 0 0 0 0 0 + -3.3102 1.2088 2.1690 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3360 3.3759 1.1673 H 0 0 0 0 0 0 0 0 0 0 0 0 + -2.6160 5.4676 0.8595 H 0 0 0 0 0 0 0 0 0 0 0 0 + -4.4960 5.5547 -0.7597 H 0 0 0 0 0 0 0 0 0 0 0 0 + -5.0937 3.5439 -2.0754 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 2 2 0 0 0 0 + 1 15 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 16 1 0 0 0 0 + 3 4 2 0 0 0 0 + 3 17 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 18 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 19 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 1 0 0 0 0 + 8 9 2 0 0 0 0 + 8 13 1 0 0 0 0 + 9 10 1 0 0 0 0 + 9 20 1 0 0 0 0 + 10 11 2 0 0 0 0 + 10 21 1 0 0 0 0 + 11 12 1 0 0 0 0 + 11 22 1 0 0 0 0 + 12 13 2 0 0 0 0 + 12 23 1 0 0 0 0 + 13 14 1 0 0 0 0 +M END +$$$$ + + OpenBabel05061518253D + + 21 21 0 0 0 0 0 0 0 0999 V2000 + 0.7375 1.1314 -0.1368 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7621 1.2758 0.1347 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.5341 0.0678 -0.3839 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.9279 -1.2293 0.1392 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5820 -1.2823 -0.1137 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3265 -0.1121 0.3814 N 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4350 -0.0912 1.8306 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2845 -1.1426 2.2374 O 0 0 0 0 0 0 0 0 0 0 0 0 + 0.9056 1.1650 -1.2213 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.2713 2.0016 0.2649 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.1368 2.1849 -0.3498 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.9415 1.3952 1.2102 H 0 0 0 0 0 0 0 0 0 0 0 0 + -2.5863 0.1379 -0.0878 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.5090 0.0643 -1.4805 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.1359 -1.3271 1.2111 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.4112 -2.0819 -0.3518 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.9914 -2.2094 0.3057 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7529 -1.3583 -1.1958 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.4787 -0.1848 2.3540 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.9078 0.8372 2.1698 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9104 -1.2115 1.4963 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 2 1 0 0 0 0 + 1 9 1 0 0 0 0 + 1 10 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 11 1 0 0 0 0 + 2 12 1 0 0 0 0 + 3 4 1 0 0 0 0 + 3 13 1 0 0 0 0 + 3 14 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 15 1 0 0 0 0 + 4 16 1 0 0 0 0 + 5 6 1 0 0 0 0 + 5 17 1 0 0 0 0 + 5 18 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 19 1 0 0 0 0 + 7 20 1 0 0 0 0 + 8 21 1 0 0 0 0 +M END +$$$$ + + OpenBabel05061518253D + + 36 35 0 0 0 0 0 0 0 0999 V2000 + 0.3281 -0.3372 1.3040 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.8408 -0.3730 1.4485 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4060 1.0029 1.8020 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9281 0.9531 1.9402 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5090 2.3246 2.2890 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0318 2.2562 2.4205 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6303 3.6257 2.7526 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1435 3.5628 2.9619 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8570 3.1824 1.7601 N 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2048 4.1024 0.7967 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9717 5.3025 0.8831 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8865 3.4980 -0.4107 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8732 2.8819 -1.3564 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.0515 -1.3325 1.0523 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0260 0.3524 0.5094 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.1479 -0.0180 2.2366 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2827 -0.7267 0.5097 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.1104 -1.0968 2.2264 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.9618 1.3535 2.7415 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.1302 1.7253 1.0241 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3692 0.5973 1.0011 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2017 0.2308 2.7191 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0732 2.6812 3.2301 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2380 3.0486 1.5111 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4588 1.8817 1.4825 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2986 1.5392 3.2064 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1681 4.0090 3.6704 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3915 4.3349 1.9509 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3966 2.8313 3.7364 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5073 4.5422 3.2921 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0080 2.1987 1.5803 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6083 2.7429 -0.0799 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4545 4.2854 -0.9182 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3136 2.0754 -0.8717 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3755 2.4690 -2.2365 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1496 3.6313 -1.6946 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 1 14 1 0 0 0 0 + 1 15 1 0 0 0 0 + 1 16 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 17 1 0 0 0 0 + 2 18 1 0 0 0 0 + 3 4 1 0 0 0 0 + 3 19 1 0 0 0 0 + 3 20 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 21 1 0 0 0 0 + 4 22 1 0 0 0 0 + 5 6 1 0 0 0 0 + 5 23 1 0 0 0 0 + 5 24 1 0 0 0 0 + 6 7 1 0 0 0 0 + 6 25 1 0 0 0 0 + 6 26 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 27 1 0 0 0 0 + 7 28 1 0 0 0 0 + 8 9 1 0 0 0 0 + 8 29 1 0 0 0 0 + 8 30 1 0 0 0 0 + 9 10 1 0 0 0 0 + 9 31 1 0 0 0 0 + 10 12 1 0 0 0 0 + 10 11 2 0 0 0 0 + 12 13 1 0 0 0 0 + 12 32 1 0 0 0 0 + 12 33 1 0 0 0 0 + 13 34 1 0 0 0 0 + 13 35 1 0 0 0 0 + 13 36 1 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/vinylpyrimidine_stub.sdf b/source/test/protocols/drug_design/vinylpyrimidine_stub.sdf new file mode 100644 index 0000000000..5b907e4ef9 --- /dev/null +++ b/source/test/protocols/drug_design/vinylpyrimidine_stub.sdf @@ -0,0 +1,30 @@ + + OpenBabel09081510033D + + 12 12 0 0 0 0 0 0 0 0999 V2000 + -0.6237 1.2284 -0.0867 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7499 1.1472 -0.0719 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4247 -0.0070 0.0640 N 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6679 -1.1066 0.1880 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6718 -1.1480 0.1874 N 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3042 0.0347 0.0484 C 0 0 0 0 0 0 0 0 0 0 0 0 + -2.7754 0.0230 0.0439 C 0 0 0 0 0 0 0 0 0 0 0 0 + -3.5249 -1.0812 0.1685 * 0 0 0 0 0 0 0 0 0 0 0 0 + -1.1265 2.1788 -0.1984 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3655 2.0362 -0.1721 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1879 -2.0516 0.2991 H 0 0 0 0 0 0 0 0 0 0 0 0 + -3.2696 0.9834 -0.0690 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 2 2 0 0 0 0 + 1 9 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 10 1 0 0 0 0 + 3 4 2 0 0 0 0 + 4 5 1 0 0 0 0 + 4 11 1 0 0 0 0 + 5 6 2 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 2 0 0 0 0 + 7 12 1 0 0 0 0 +M END +$$$$ diff --git a/source/test/protocols/drug_design/zim.100.sascore.txt b/source/test/protocols/drug_design/zim.100.sascore.txt new file mode 100644 index 0000000000..f8eb6f4139 --- /dev/null +++ b/source/test/protocols/drug_design/zim.100.sascore.txt @@ -0,0 +1,101 @@ +smiles Name sa_score +Cc1c(C(=O)NCCO)[n+](=O)c2ccccc2n1[O-] ZINC21984717 3.166 +Cn1cc(NC=O)cc1C(=O)Nc1cc(C(=O)Nc2cc(C(=O)NCCC(N)=[NH2+])n(C)c2)n(C)c1 ZINC03872327 3.328 +OC(c1ccncc1)c1ccc(OCC[NH+]2CCCC2)cc1 ZINC34421620 3.822 +CC(C(=O)[O-])c1ccc(-c2ccccc2)cc1 ZINC00000361 2.462 +C[NH+](C)CC(O)Cn1c2ccc(Br)cc2c2cc(Br)ccc21 ZINC00626529 3.577 +NC(=[NH2+])NCC1COc2ccccc2O1 ZINC00000357 3.290 +CCC(C)(C)[NH2+]CC(O)COc1ccccc1C#N ZINC04214111 3.698 +C[NH+](C)CC(O)Cn1c2ccc(Br)cc2c2cc(Br)ccc21 ZINC00626528 3.577 +CC12CCC3C(CCC4CC(=O)CCC43C)C1CCC2=O ZINC04081985 3.912 +COc1ccc(OC(=O)N(CC(=O)[O-])Cc2ccc(OCCc3nc(-c4ccccc4)oc3C)cc2)cc1 ZINC03935839 2.644 +COc1ccccc1OC(=O)c1ccccc1 ZINC00000349 1.342 +CC(C)CC[NH2+]CC1COc2ccccc2O1 ZINC04214115 3.701 +CN1CCN(C(=O)OC2c3nccnc3C(=O)N2c2ccc(Cl)cn2)CC1 ZINC19632834 3.196 +CCC1(c2ccccc2)C(=O)N(COC)C(=O)N(COC)C1=O ZINC02986592 2.759 +Nc1ccc(S(=O)(=O)Nc2ccccc2)cc1 ZINC00141883 1.529 +O=C([O-])CCCNC(=O)NC1CCCCC1 ZINC08754389 2.493 +CCC(C)C(C(=O)OC1CC[N+](C)(C)CC1)c1ccccc1 ZINC00000595 3.399 +CCC(C)SSc1ncc[nH]1 ZINC13209429 3.983 +CC[N+](C)(CC)CCOC(=O)C(O)(c1cccs1)C1CCCC1 ZINC01690860 3.471 +CC12CCC3C(CCC4CC(=O)CCC43C)C1CCC2O ZINC03814360 3.994 +CC12CCC3C4CCC(=O)C=C4CCC3C1CCC2O ZINC03814379 4.056 +OCC1OC(OC2C(CO)OC(O)C(O)C2O)C(O)C(O)C1O ZINC04095762 4.282 +CC(C)CC(CC[NH+](C(C)C)C(C)C)(C(N)=O)c1ccccn1 ZINC02016048 4.092 +C=CC1(C)CC(=O)C2(O)C(C)(O1)C(OC(C)=O)C(OC(=O)CC[NH+](C)C)C1C(C)(C)CCC(O)C12C ZINC38595287 5.519 +C=CC[NH+]1CCCC1CNC(=O)c1cc(S(N)(=O)=O)cc(OC)c1OC ZINC00601278 4.286 +CC(=O)OC1C[NH+]2CCC1CC2 ZINC00492792 5.711 +CC12CCC3C(CCC4CC(=O)CCC43C)C1CCC2O ZINC03814418 3.994 +CC1(O)CCC2C3CCC4=CC(=O)CCC4(C)C3CCC21C ZINC03814422 4.022 +CC(=O)OC1(C(C)=O)CCC2C3C=C(Cl)C4=CC(=O)C5CC5C4(C)C3CCC21C ZINC03814423 4.827 +C#CC1(O)CCC2C3CCc4cc(OC)ccc4C3CCC21C ZINC03815424 3.810 +C=CC1(C)CC(OC(=O)CSCC[NH+](CC)CC)C2(C)C3C(=O)CCC3(CCC2C)C(C)C1O ZINC25757051 6.200 +O=C([O-])C(=O)Nc1nc(-c2ccc3c(c2)OCCO3)cs1 ZINC03623428 2.594 +CC[NH+]1CCCC1CNC(=O)C(O)(c1ccccc1)c1ccccc1 ZINC00900569 3.950 +CC(C)(OCc1nn(Cc2ccccc2)c2ccccc12)C(=O)[O-] ZINC00004594 2.573 +Cc1nnc(C(C)C)n1C1CC2CCC(C1)[NH+]2CCC(NC(=O)C1CCC(F)(F)CC1)c1ccccc1 ZINC03817234 5.316 +Nc1ncnc2c1ncn2C1OC(COP(=O)([O-])OP(=O)([O-])OP(=O)([O-])[O-])C(O)C1O ZINC03871612 5.290 +O=C([O-])CNC(=O)c1ccccc1 ZINC00097685 2.097 +Nc1ncnc2c1ncn2C1OC(COP(=O)([O-])OP(=O)([O-])OP(=O)([O-])[O-])C(O)C1O ZINC03871613 5.290 +Nc1ncnc2c1ncn2C1OC(COP(=O)([O-])OP(=O)([O-])OP(=O)([O-])[O-])C(O)C1O ZINC03871614 5.290 +c1ccc(OCc2ccc(CCCN3CCOCC3)cc2)cc1 ZINC19865692 1.702 +CC=CC1=C(C(=O)[O-])N2C(=O)C(NC(=O)C(N)c3ccc(O)cc3)C2SC1 ZINC20444132 4.042 +C[NH+]1CCCC1COc1cccnc1 ZINC03805141 4.510 +O=C([O-])C(O)CC(O)C(O)CO ZINC04803503 4.398 +O=C([O-])C(O)CC(O)C(O)CO ZINC01696607 4.398 +C[NH+]1CCCC1Cc1c[nH]c2ccc(CCS(=O)(=O)c3ccccc3)cc12 ZINC03823475 3.921 +C(=Cc1ccccc1)C[NH+]1CCN(C(c2ccccc2)c2ccccc2)CC1 ZINC19632891 2.973 +Nc1ncnc2c1ncn2C1OC(COP(=O)([O-])OP(=O)([O-])OP(=O)([O-])[O-])C(O)C1O ZINC03871615 5.290 +CC(c1ccccc1)N(C)C=O ZINC06932229 2.562 +CC(=O)C1CCC2C3CCC4CC(C)(O)CCC4(C)C3CCC12C ZINC03824281 4.279 +O=C([O-])C(O)CC(O)C(O)CO ZINC04803506 4.398 +COc1cc(O)c(C(=O)c2ccccc2)c(O)c1 ZINC00000187 1.868 +O=C([O-])C(O)CC(O)C(O)CO ZINC04803507 4.398 +COc1c2c(cc3c1C(O)N(C)CC3)OCO2 ZINC00000186 3.183 +CCC(C(=O)[O-])c1ccc(CC(C)C)cc1 ZINC00015537 2.827 +O=C([O-])C1[NH+]=C(c2ccccc2)c2cc(Cl)ccc2NC1(O)O ZINC38611850 4.011 +O=C([O-])C1[NH+]=C(c2ccccc2)c2cc(Cl)ccc2NC1(O)O ZINC38611851 4.011 +OCC(O)COc1ccc(Cl)cc1 ZINC00000135 2.102 +NC(=O)NC(=O)C(Cl)c1ccccc1 ZINC00000134 2.455 +OC(c1ccccc1)(c1ccccc1)C1C[NH+]2CCC1CC2 ZINC01298963 4.530 +C[NH2+]CC(C)c1ccccc1 ZINC04298801 3.471 +Clc1cccc(Cl)c1N=C1NCCO1 ZINC13835972 3.267 +[NH3+]C(Cc1ccccc1)C(=O)CCl ZINC02504633 3.251 +CC(C)Cn1cnc2c1c1ccccc1nc2N ZINC19632912 2.230 +CC(O)CN(C)c1ccc(NN)nn1 ZINC00000624 3.193 +CC1(O)CCC2C3CCC4=CC(=O)CCC4=C3C=CC21C ZINC00001727 4.461 +CCC(C(=O)[O-])c1ccc(-c2ccccc2)cc1 ZINC00000111 2.505 +CC(=O)OCC1OC(n2ncc(=O)[nH]c2=O)C(OC(C)=O)C1OC(C)=O ZINC03830255 3.832 +CC(=O)OCC1OC(n2ncc(=O)[nH]c2=O)C(OC(C)=O)C1OC(C)=O ZINC03830256 3.832 +Cn1cc(C(=O)c2cccc3ccccc32)cc1C(=O)[O-] ZINC00001783 2.456 +CC(=O)OCC1OC(n2ncc(=O)[nH]c2=O)C(OC(C)=O)C1OC(C)=O ZINC03830257 3.832 +Cc1cccc(-c2nc3ccccc3c(Nc3ccc4[nH]ncc4c3)n2)n1 ZINC39279791 2.358 +O=C([O-])C1CC2CCCCC2[NH2+]1 ZINC04899687 5.422 +CC(=O)OCC(=O)C1CCC2C3CC=C4CC(O)CCC4(C)C3CCC12C ZINC00538219 4.187 +O=C([O-])C1CC2CCCCC2[NH2+]1 ZINC04899686 5.422 +O=C(OCc1ccccc1)C(O)c1ccccc1 ZINC00000078 2.038 +CC(=O)OCC(=O)C1(O)CCC2C3CCC4=CC(=O)C=CC4(C)C3C(O)CC21C ZINC00608041 4.394 +Cc1ccc(-c2cc(C(F)(F)F)nn2-c2ccc(S(N)(=O)=O)cc2)cc1 ZINC02570895 2.144 +COCc1cccc(CC(O)C=CC2C(O)CC(=O)C2CCSCCCC(=O)OC)c1 ZINC03940680 3.934 +CCC(=O)N(c1ccccc1)C1CC[NH+](C(C)Cc2ccccc2)CC1 ZINC01664586 3.582 +CCC(=O)N(c1ccccc1)C1CC[NH+](C(C)Cc2ccccc2)CC1 ZINC01664587 3.582 +CCOC(=O)Nc1ccc2c(c1)N(C(=O)CCN1CCOCC1)c1ccccc1S2 ZINC19340795 2.446 +O=C([O-])Cc1cc(=O)[nH]c(=O)[nH]1 ZINC00403617 3.258 +NC(=O)C([NH3+])Cc1c[nH]c2ccccc12 ZINC04899521 3.224 +NC(=O)C([NH3+])Cc1ccc(O)cc1 ZINC04899513 3.280 +O=C(c1cc2ccccc2o1)N1CCN(Cc2ccccc2)CC1 ZINC19632922 1.799 +O=C(CO)C(O)C(O)CO ZINC00902219 3.473 +CC(Cc1ccccc1)NC(=O)C([NH3+])CCCC[NH3+] ZINC11680943 3.967 +C[NH+]1CCC(c2c(O)cc(=O)c3c(O)cc(-c4ccccc4Cl)oc2-3)C(O)C1 ZINC05966679 4.616 +CN(C)c1ccc(O)c2c1CC1CC3C([NH+](C)C)C(=O)C(C(N)=O)=C(O)C3(O)C(=O)C1=C2O ZINC04019704 4.713 +Cc1cc2nc3c(=O)[nH]c(=O)nc-3n(CC(O)C(O)C(O)CO)c2cc1C ZINC03650334 3.791 +C[NH+]1C2CCC1CC(OC(=O)c1c[nH]c3ccccc13)C2 ZINC18130447 4.892 +Cc1ccccc1NC(=O)C(C)[NH+]1CCCC1 ZINC00000051 3.809 +O=S(=O)([O-])CCN1CCOCC1 ZINC19419111 2.776 +C[NH+]1CCN(CC(=O)N2c3ccccc3C(=O)Nc3cccnc32)CC1 ZINC19632927 3.379 +CCCCCC=CCC=CCCCCCCCC(=O)[O-] ZINC03802188 2.805 +CC(CC([NH3+])C(=O)[O-])C(=O)[O-] ZINC01747048 5.690 +CC1c2cccc(O)c2C(=O)C2=C(O)C3(O)C(O)=C(C(N)=O)C(=O)C([NH+](C)C)C3C(O)C21 ZINC04019706 5.069 +Cc1cc2nc3nc([O-])[nH]c(=O)c3nc2cc1C ZINC12446789 3.079 +CC1=CC(C)C2(CO)COC(c3ccc(O)cc3)C1C2C ZINC38190856 4.749 +CC[NH+]1CCC(=C2c3ccccc3CCc3ccccc32)C1C ZINC02020004 3.925