// $Id: StructureMap.java,v 1.40 2007/05/23 05:04:10 Sasha Buzko Exp $ // // Copyright (c) 2000-2004 San Diego Supercomputer Center (SDSC), // a facility operated by the University of California, // San Diego (UCSD), San Diego, California, USA. // // Users and possessors of this source code are hereby granted a // nonexclusive, royalty-free copyright and design patent license to // use this code in individual software. License is not granted for // commercial resale, in whole or in part, without prior written // permission from SDSC. This source is provided "AS IS" without express // or implied warranty of any kind. // // For further information, please see: http://mbt.sdsc.edu // // History: // $Log: StructureMap.java,v $ // Revision 1.40 2007/05/23 05:04:10 Sasha Buzko // *** empty log message *** // // Revision 1.39 2007/05/22 20:55:42 Sasha Buzko // *** empty log message *** // // Revision 1.38 2007/05/14 02:35:46 Sasha Buzko // *** empty log message *** // // Revision 1.37 2007/05/10 04:27:15 Sasha Buzko // *** empty log message *** // // Revision 1.36 2007/03/13 04:34:00 Sasha Buzko // *** empty log message *** // // Revision 1.35 2007/03/01 20:56:28 Sasha Buzko // Added MD video // // Revision 1.34 2007/02/25 00:07:15 Sasha Buzko // *** empty log message *** // // Revision 1.33 2007/02/24 23:44:02 Sasha Buzko // *** empty log message *** // // Revision 1.32 2007/02/22 23:43:55 Sasha Buzko // *** empty log message *** // // Revision 1.31 2007/02/22 19:56:33 Sasha Buzko // *** empty log message *** // // Revision 1.30 2007/02/19 07:31:04 Sasha Buzko // *** empty log message *** // // Revision 1.29 2007/02/18 05:08:05 Sasha Buzko // *** empty log message *** // // Revision 1.28 2007/02/13 19:08:41 Sasha Buzko // *** empty log message *** // // Revision 1.27 2007/02/09 18:18:17 Sasha Buzko // *** empty log message *** // // Revision 1.26 2007/02/09 05:08:53 Sasha Buzko // *** empty log message *** // // Revision 1.25 2007/02/08 01:47:06 Sasha Buzko // *** empty log message *** // // Revision 1.24 2007/02/07 02:45:49 Sasha Buzko // *** empty log message *** // // Revision 1.23 2007/02/06 05:42:00 Sasha Buzko // *** empty log message *** // // Revision 1.22 2006/12/23 06:18:56 Sasha Buzko // md // // Revision 1.21 2006/11/22 04:29:07 Sasha Buzko // *** empty log message *** // // Revision 1.20 2006/11/22 01:58:40 Sasha Buzko // *** empty log message *** // // Revision 1.19 2006/11/21 01:02:05 Sasha Buzko // *** empty log message *** // // Revision 1.18 2006/11/18 00:41:24 Sasha Buzko // *** empty log message *** // // Revision 1.17 2006/10/23 22:23:06 Sasha Buzko // *** empty log message *** // // Revision 1.16 2006/10/21 18:41:26 Sasha Buzko // *** empty log message *** // // Revision 1.15 2006/10/10 20:46:51 Sasha Buzko // *** empty log message *** // // Revision 1.14 2006/09/30 04:51:30 Sasha Buzko // *** empty log message *** // // Revision 1.13 2006/09/01 01:54:09 Sasha Buzko // *** empty log message *** // // Revision 1.12 2006/08/31 03:48:30 Sasha Buzko // *** empty log message *** // // Revision 1.11 2006/08/24 21:10:11 Sasha Buzko // *** empty log message *** // // Revision 1.10 2006/08/21 00:55:32 Sasha Buzko // *** empty log message *** // // Revision 1.9 2006/08/16 21:14:00 Sasha Buzko // *** empty log message *** // // Revision 1.8 2006/08/11 16:44:50 Sasha Buzko // *** empty log message *** // // Revision 1.7 2006/08/08 05:45:51 Sasha Buzko // *** empty log message *** // // Revision 1.6 2006/08/03 06:21:23 Sasha Buzko // *** empty log message *** // // Revision 1.5 2006/07/25 04:08:04 Sasha Buzko // *** empty log message *** // // Revision 1.4 2006/07/06 16:56:08 Sasha Buzko // *** empty log message *** // // Revision 1.3 2006/07/02 17:53:31 Sasha Buzko // *** empty log message *** // // Revision 1.2 2006/05/22 06:36:05 Sasha Buzko // *** empty log message *** // // Revision 1.1 2006/05/20 17:02:06 Sasha Buzko // Updated version // // Revision 1.3 2006/05/20 04:19:46 Sasha Buzko // *** empty log message *** // // Revision 1.2 2006/05/17 07:06:17 Sasha Buzko // *** empty log message *** // // Revision 1.1 2006/04/30 20:14:05 Sasha Buzko // New version of the app // // Revision 1.1 2006/04/15 19:42:28 Sasha Buzko // Initial commit // // Revision 1.15 2006/03/23 03:06:21 Administrator // *** empty log message *** // // Revision 1.14 2006/03/21 06:00:00 Administrator // *** empty log message *** // // Revision 1.13 2006/03/07 19:15:18 Administrator // *** empty log message *** // // Revision 1.12 2006/02/24 22:26:53 Administrator // *** empty log message *** // // Revision 1.11 2006/02/20 18:03:24 Administrator // *** empty log message *** // // Revision 1.10 2005/12/31 00:55:25 Administrator // *** empty log message *** // // Revision 1.9 2005/12/26 03:53:48 Administrator // *** empty log message *** // // Revision 1.8 2005/12/25 04:44:49 Administrator // *** empty log message *** // // Revision 1.7 2005/12/12 05:05:56 Administrator // *** empty log message *** // // Revision 1.6 2005/11/28 06:40:38 Administrator // *** empty log message *** // // Revision 1.5 2005/11/26 18:21:03 Administrator // *** empty log message *** // // Revision 1.4 2005/11/26 04:36:10 Administrator // *** empty log message *** // // Revision 1.3 2005/11/22 07:11:32 Administrator // *** empty log message *** // // Revision 1.2 2005/11/13 23:44:31 Administrator // *** empty log message *** // // Revision 1.1 2005/11/13 04:35:24 Administrator // *** empty log message *** // // Revision 1.42 2004/02/12 17:55:27 moreland // Corrected typo in loadFragments method which was preventing coil growth. // // Revision 1.41 2004/02/05 18:36:35 moreland // Now computes distances using the Algebra class methods. // // Revision 1.40 2004/01/30 22:47:56 moreland // Added more detail descriptions for the class block comment. // // Revision 1.39 2004/01/30 21:23:56 moreland // Added new diagrams. // // Revision 1.38 2004/01/29 17:08:16 moreland // Updated copyright and class block comments. // // Revision 1.37 2004/01/17 00:45:34 moreland // After loading conformations, coalesce contiguous runs of coil fragments. // // Revision 1.36 2004/01/16 23:04:21 moreland // Added code to warn-about/skip reversed conformation records. // Added code to warn-about/skip oversized conformation records. // // Revision 1.35 2004/01/16 18:13:24 moreland // Corrected index bug when single-residue fragments were replaced with coil. // // Revision 1.34 2004/01/15 17:13:11 moreland // Removed debug print statement. // // Revision 1.33 2004/01/15 00:52:37 moreland // Moved code that requests that each chain regenerate Fragment objects // to generateFragments method to make loaded and derived methods consistent. // // Revision 1.32 2003/12/20 01:03:44 moreland // Cleaned up formatting a bit. // // Revision 1.31 2003/12/09 21:19:50 moreland // Now throws an IllegalArgumentException if the Structure argument to the contructor is null. // // Revision 1.30 2003/11/20 21:33:52 moreland // Added code to fill a new fragments Vector. // Added getFragmentCount, getFragment, and getFragmentIndex access methods. // // Revision 1.29 2003/10/23 22:05:38 moreland // Changed initialize and print method from public to private methods. // // Revision 1.28 2003/10/17 18:19:56 moreland // Fixed a javadoc comment. // // Revision 1.27 2003/10/06 23:12:44 moreland // Cleaned up code to generate Fragments in StructureMap so that Fragments are set // as complete ranges (instead of individual residues - which didn't work well). // // Revision 1.26 2003/10/01 21:19:24 agramada // Added code to create fragments according to the output from the Kabsch-Sander // algorithm. // // Revision 1.25 2003/09/16 17:18:22 moreland // Added code to enable secondary structure generation from data VS derivation. // // Revision 1.24 2003/09/11 19:41:41 moreland // Added a getChain method variant that takes a chainId argument. // // Revision 1.23 2003/07/17 23:14:45 moreland // The getBonds(atomVector) now returns a Vector of UNIQUE Bond objects. // // Revision 1.22 2003/07/17 22:54:39 moreland // Fixed trivial bug in getBondIndex method which always returned a -1 index value. Oops! // // Revision 1.21 2003/07/11 23:06:26 moreland // Covalent Bonds are now generated and added at construction time. // Added bonds are kept sorted by both Atom's numbers for retrieval performance. // Two "getBonds" methods return a Bond Vector given one or multiple Atom objects. // // Revision 1.20 2003/04/30 17:53:31 moreland // Added addChain method to add chains in sorted order. // The getAtomIndex method now does a binary search. // The getResidueIndex method now does a binary search. // // Revision 1.19 2003/04/24 17:14:34 moreland // Enabled the processConformations to call resetFragments for each chain. // // Revision 1.18 2003/04/23 17:56:04 moreland // Completely rewrote this class from scratch to use an direct object hierarchy // rather than a index/table based model. The excentricities of data and relations // were too problematic to maintain a complex cross-referencing index based model. // // Revision 1.17 2003/04/03 18:18:51 moreland // Changed Atom field "type" to "element" due to naming and meaning conflict. // Changed ATOM_MAP_ and TYPES[] fields from public to private, // and removed the "getAtomMapFlags" method. // // Revision 1.16 2003/03/14 21:08:18 moreland // Divided state initialization code into separate methods in prepration to // eventually add computated secondary structure code. // Also added support for extracting ligands. // // Revision 1.15 2003/03/10 23:25:54 moreland // Moved "chain classification" comment to appear after the overview diagrams. // // Revision 1.14 2003/03/10 22:52:08 moreland // Changed getLeadingCaAtomIndex and getTrailingCaAtomIndex method names // to getLeadingAlphaIndex and getTrailingAlphaIndex in order to reflect // support for C-Alpha (amino acid) and P-alpha (nucleic acid) chain // primary backbone atoms. // The "residues" array's "C-alpha" index is now just the "alpha" index. // The "residues" array is now fully intialized with -1 values. // Added the "ligands" array to support mapping of ligands (het groups). // Added methods to return ligandCount and ligand index values. // When a residue has one atom (eg: O=water), the end index is now set properly. // // Revision 1.13 2003/03/07 20:25:19 moreland // Added support to provide an atom coordinate average. // // Revision 1.12 2003/02/21 21:55:30 moreland // Added bounding box computation for a Structure's atom coordinates. // Added start_atom and end_atom elements to "chains" array. // Added methods to get atom start/stop indexes for a residue or a chain. // Added support for generating a bond list. // // Revision 1.11 2003/02/07 17:33:29 moreland // Fixed inter-chain fragment split bug. // // Revision 1.10 2003/02/03 22:49:51 moreland // Added support for Status message output. // // Revision 1.9 2003/01/22 18:18:57 moreland // Commented out debug call to printTables method. // // Revision 1.8 2003/01/17 22:16:29 moreland // Corrected getTrailingAlphaAtomIndex and getLeadingAlphaAtomIndex method // index calculations. // // Revision 1.7 2003/01/17 02:28:01 moreland // Fixed the "chains" and "fragments" index calculations in the constructor. // Still need to re-test the operation of the other methods in the class... // // Revision 1.6 2003/01/07 19:35:45 moreland // Changed assignment algorithm for conformation records to use the residues // table for finding gaps instead of atomMap flags. // // Revision 1.5 2002/12/20 22:27:53 moreland // Fixed bug in computing end atom index for gap fragments. // // Revision 1.4 2002/12/19 21:12:25 moreland // Oops. Fixed a cut and paste error when calling getType method during // the Conformation pass. // // Revision 1.3 2002/12/17 19:19:14 moreland // Added public and private overview diagrams. // // Revision 1.2 2002/12/16 18:28:10 moreland // Updated getLeadingAlphaIndex and getTrailingAlphaIndex methods (still need testing!) // // Revision 1.1 2002/12/16 06:31:06 moreland // Added new class to enable vital derived data to be built from Structure objects. // This class, in fact, forms the basis for many new/upcomming StructureDocument // and Viewer features and capabilities. // // Revision 1.0 2002/11/14 18:47:33 moreland // Corrected "see" document reference. // package edu.sdsc.mbt; // MBT import edu.sdsc.mbt.util.*; import edu.sdsc.mbt.viewables.*; import edu.sdsc.sirius.util.*; // Core import java.util.*; /** * This class implements a derived data map for a Structure object. It * generates a number of hierarchical links, indexes, and generally provides * access to the numerous relationships that exists between chains, * fragments (secondary structure conformations), residues, atoms, and bonds * for a Structure. The map enables one to "walk" a Structure's * backbone, and finds "gaps" in the map (ie: segments of chains which are not * spanned by Conformation objects). The set of map relationships that are * managed by this class are suitable for applications and viewers to construct * more spacially/biologically meaningful representations and displays. *
*
* This class provides a number of different "entry points" to traverse * the underlying Structure data. The one an application should choose * depeonds mostly on what the application wishes to accomplish. For * example, while a basic sequence viewer might simply walk the raw list * of residues (ie: by calling getResidueCount and getResidue in a loop) * another sequence viewer may want to obtain residues by walking each * chain (ie: by calling getChainCount, plus getChain and chain.getResidue * in a nested loop) so that it knows where the residues of one chain ends * and another begins. Again, its entirely up to the application. *
*
* @author John L. Moreland * @see edu.sdsc.mbt.StructureComponent * @see edu.sdsc.mbt.StructureComponentRegistry */ public class StructureMap { // PROGRAMMER NOTE #1: // Eventually, if/when the Structure class adds "set/add/remove" methods, // this class could be made to listen for StructureComponentEvent messages // and (in response) automatically update the internal map values. // PROGRAMMER NOTE #2: // Conformation interpretation - // a) Although Conformation records contain both start and end chain // identifiers, Conformations never actually cross chain boundaries. // b) Since chain idtentifiers are STRING values, there really is // no practical way to compute and iteratively walk intermediate values. // c) Start and end residue numbers are always constrained within one chain. // d) Residue numbers may be reused (restart at 1) for every chain, // or, may be continuous over the entire structure. So, don't count on // residue numbers for indexing purposes. // // Private state // // Stores a reference to the Structure. private Structure structure; // Primary StructureMap containers. private Vector atoms; // All Atoms in the Stucture. private Vector residues; // All Residues in the Stucture. private Vector fragments; // All Fragments in the Stucture. private Vector chains; // All Chains in the Stucture. private Vector ligands; // Only Ligand Residues. private Vector bonds; // All Bond objects added to this StructureMap. private Hashtable bondUniqueness; // Make sure Bond objects are unique. private Hashtable atomToBonds; // Find all Bonds connected to each Atom. private Hashtable atomToAtoms; //find all Atoms connected to each Atom private Hashtable bondToBonds; //find all Bonds connected to each Bond private Object value = new Object(); // Stores Chain object references by atom.chain_id value. private Hashtable chainById = null; // Stores Residue object references by atom.chain_id+atom_residue_id value. private Hashtable residueByChainAndResidueId = null; private double[] minCoordinate; private double[] maxCoordinate; private double[] center;//geometric center of the structure private HashMap phi = new HashMap();//Residue -> phi angle private HashMap psi = new HashMap();//Residue -> psi angle private Vector endResidues = new Vector();// private HashMap cMap = new HashMap(); private HashMap nMap = new HashMap(); private static final String defaultChainId = "_"; private boolean fillDisorderedGaps = false; // private boolean generateBondsByDistance = false; public boolean bondOrderImported = false;//indicates whether the source had bond order information public boolean bondOrderUserAssigned = false;//indicates whether the user has added bond order information public boolean chargesUserAssigned = false; public int MIN_ATOMS_FOR_FRAGMENTS = 20;//min number of atoms in the structure to run fragment detection private double bondCutoff = 1.9;//structure-specific bond cutoff distance (may be different for salt fragments) public static double hBondCutoff = 1.7;//cutoff for any bond including H /** * Used in the force field calculations */ private double[] gradient;//an array with three elements per atom // // Constructors // /** * Constructs a StructureMap object for a given Structure. */ public StructureMap( Structure structure ) { if ( structure == null ) throw new IllegalArgumentException( "null Structure" ); this.structure = structure; initialize( ); if ( Status.getOutputLevel() >= Status.LEVEL_DUMP ) print( ); // System.exit(1); } /** * Initialize all of the internal StructureMap state. * @param computeSecondaryStructure Should secondary structure be computed * (or loaded as it is defined from the source data)? */ private void initialize( ) { int atomCount = structure.getStructureComponentCount( StructureComponentRegistry.TYPE_ATOM ); int residueCount = structure.getStructureComponentCount( StructureComponentRegistry.TYPE_RESIDUE ); if ( atomCount > 0 ) atoms = new Vector( atomCount ); // All Atoms in the Stucture. else atoms = new Vector( ); // All Atoms in the Stucture. if ( residueCount > 0 ) residues = new Vector( residueCount ); // All Residues in the Stucture. else residues = new Vector( ); // All Residues in the Stucture. bonds = new Vector( ); // All Bond objects added to this StructureMap. bondUniqueness = new Hashtable( ); // Make sure Bond objects are unique. atomToBonds = new Hashtable( ); // Find all Bonds connected to each Atom. atomToAtoms = new Hashtable( ); // Find all Bonds connected to each Atom. bondToBonds = new Hashtable( ); // Find all Bonds connected to each Atom. chains = new Vector( ); // All Chains in the Stucture. ligands = new Vector( ); // Only Ligand Residues. fragments = new Vector( ); // All Fragments in the Stucture. // Stores Chain object references by atom.chain_id value. chainById = new Hashtable( ); // Stores Residue object references by atom.chain_id+atom_residue_id value. // Makes it possible to take an Atom record and find the appropriate residue // in which the Atom should be stored. residueByChainAndResidueId = new Hashtable( ); if ( atomCount > 0 ){ // Its molecule data processAtomRecords( ); //check whether Structure already has Bond records if (structure.getStructureComponentCount(StructureComponentRegistry.TYPE_BOND) > 0){ for (int i = 0; i < structure.getStructureComponentCount(StructureComponentRegistry.TYPE_BOND); i++){ Bond b = (Bond)structure.getStructureComponentByIndex(StructureComponentRegistry.TYPE_BOND, i); b.structure = structure; addBond(b); } bondOrderImported = true; } else{ // System.out.println("generating bonds"); generateBonds( ); } if (bonds.size() < atoms.size()/1.2){ detectBonds(); } else{ detectLigandBonds(); } checkResidueClassification(); //check if it makes sense to derive fragments if (atomCount > MIN_ATOMS_FOR_FRAGMENTS){ generateFragments( ); } extractLigands( ); } else if ( residueCount > 0 ){ // Its sequence data processResidueRecords( ); } else { // Its useless data } // System.out.println("bond count = " + bonds.size()); //set up the gradient array int size = getAtomCount()*3; gradient = new double[size]; //while passing through the residues, determine each atom's backbone assignment for (int i = 0; i < getResidueCount(); i++){ Residue r = getResidue(i); double[] sum = new double[3]; for (int j = 0; j < r.getAtomCount(); j++){ Atom a = r.getAtom(j); sum[0] += a.coordinate[0]; sum[1] += a.coordinate[1]; sum[2] += a.coordinate[2]; } sum[0] /= r.getAtomCount(); sum[1] /= r.getAtomCount(); sum[2] /= r.getAtomCount(); //another pass to determine which atom is the closest to the center double min = 100000; Atom target = null; for (int j = 0; j < r.getAtomCount(); j++){ Atom a = r.getAtom(j); double d = Algebra.distance(a.coordinate, sum); if (d < min){ min = d; target = a; } } if (target == null) target = r.getAtom(0); r.centerAtom = target; if (r.getClassification().equals(Residue.COMPOUND_AMINO_ACID) || r.getClassification().equals(Residue.COMPOUND_NUCLEIC_ACID)){ for (int j = 0; j < r.getAtomCount(); j++){ Atom a = r.getAtom(j); if (ProteinProperties.isProteinBackbone(a) || DNAProperties.isDNABackbone(a)){ a.backbone = true; } } } } } private void checkResidueClassification(){ //at the end, run a check for unnatural aminoacids, which should really still be marked as amino acids, rather than ligands // System.out.println("count = " + residues.size()); for (int i = 0; i < residues.size(); i++){ Residue rr = (Residue)residues.get(i); // System.out.println(i + ": rr = " + rr.getCompoundCode() + ", " + rr.getClassification()); if (rr.getClassification().equals(Residue.COMPOUND_LIGAND)){ // System.out.println(i + ": rr = " + rr.getCompoundCode() + ", " + rr.getClassification()); boolean nBond = false;//there is a N-C bond with a known amino acid boolean nCABond = false;//there is a N-CA bond within this residue boolean cBond = false;//there is a C-N bond with a known amino acid boolean cCABond = false;//there is a C-CA bond Atom ca = null; //check whether it's bonded to other amino acids and has typical backbone atom names for (int j = 0; j < rr.getAtomCount(); j++){ Atom a = rr.getAtom(j); if (a.name.equals("N")){ // System.out.println("Found N"); //check its bonds and find out whether one of them is connected to a CA and the other to a true amino acid Vector bonds = this.getBonds(a); // System.out.println("bonds = " + bonds); if (bonds != null){ for (int k = 0; k < bonds.size(); k++){ Bond b = (Bond)bonds.get(k); if (b.getAtom(0) == a){ Atom other = b.getAtom(1); if (other.residue == rr){ if (other.name.equals("CA")){ nCABond = true; } } else{ // System.out.println("other = " + other.name + ", classification = " + other.residue.getClassification()); if (other.name.equals("C") && other.residue.getClassification().equals(Residue.COMPOUND_AMINO_ACID)){ nBond = true; } } } if (b.getAtom(1) == a){ Atom other = b.getAtom(0); if (other.residue == rr){ if (other.name.equals("CA")){ nCABond = true; } } else{ // System.out.println("other = " + other.name + ", classification = " + other.residue.getClassification()); if (other.name.equals("C") && other.residue.getClassification().equals(Residue.COMPOUND_AMINO_ACID)){ nBond = true; } } } } } } else if (a.name.equals("C")){ // System.out.println("Found C"); Vector bonds = this.getBonds(a); if (bonds != null){ for (int k = 0; k < bonds.size(); k++){ Bond b = (Bond)bonds.get(k); if (b.getAtom(0) == a){ Atom other = b.getAtom(1); if (other.residue == rr){ if (other.name.equals("CA")){ cCABond = true; } } else{ if (other.name.equals("N") && other.residue.getClassification().equals(Residue.COMPOUND_AMINO_ACID)){ cBond = true; } } } if (b.getAtom(1) == a){ Atom other = b.getAtom(0); if (other.residue == rr){ if (other.name.equals("CA")){ cCABond = true; } } else{ if (other.name.equals("N") && other.residue.getClassification().equals(Residue.COMPOUND_AMINO_ACID)){ cBond = true; } } } } } } else if (a.name.equals("CA")){ ca = a; } } // System.out.println("nBond = " + nBond + ", cBond = " + cBond + ", cCABond = " + cCABond + ", ca = " + ca); if (nBond && cBond && nCABond && cCABond && ca != null){ rr.setClassification(Residue.COMPOUND_AMINO_ACID); rr.setAlphaAtom(ca); } } } } // // Initialization helper methods (private) // /** * Processes the atom records for the Structure and builds the hierarchy. *
*
*
* coordinateBounds[0][0] = min x
* coordinateBounds[0][1] = min y
* coordinateBounds[0][2] = min z
* coordinateBounds[1][0] = max x
* coordinateBounds[1][1] = max y
* coordinateBounds[1][2] = max z
*
*/ public double[][] getAtomCoordinateBounds( ) { return AtomStats.getAtomCoordinateBounds( structure ); } /** * Return the coordinate average for a Structure's atom coordinates. *
* double[0] = x
* double[1] = y
* double[2] = z
*
*/ public double[] getAtomCoordinateAverage( ) { return AtomStats.getAtomCoordinateAverage( structure ); } public Vector getAtoms( Atom atom ){ return (Vector)atomToAtoms.get(atom); } /** * Return the total Atom count extracted from the Structure. */ public int getAtomCount( ) { if ( atoms == null ) return 0; return atoms.size( ); } /** * Get the Atom at the specified index. *
*/ public Atom getAtom( int atomIndex ) { // System.out.println("atoms = " + atoms); if ( atoms == null ) return null; return (Atom) atoms.elementAt( atomIndex ); } /** * Get the index of the specified Atom. *
*/
public int getAtomIndex( Atom atom )
{
if ( atom == null )
throw new IllegalArgumentException( "null atom" );
if ( atoms == null )
throw new IllegalArgumentException( "no atoms!" );
// Do a binary search of the atoms vector.
int low = 0;
int high = atoms.size() - 1;
while ( low <= high )
{
int mid = (low + high) / 2;
Atom atom2 = (Atom) atoms.elementAt( mid );
// If the atom matches, return the index.
if ( atom == atom2 )
{
return mid;
}
else
{
int chainCompare = atom.chain_id.compareTo( atom2.chain_id );
if ( chainCompare == 0 )
{
if ( atom.residue_id == atom2.residue_id )
{
// Since we currently don't have an atom number,
// do a linear search of the residue
for ( int a=low; a<=high; a++ )
{
atom2 = (Atom) atoms.elementAt( a );
if ( atom == atom2 ) return a;
}
// Give up and do the exhastive linear search
break;
// return mid;
}
else if ( atom.residue_id < atom2.residue_id )
{
high = mid - 1;
}
else // ( atom.residue_id > atom2.residue_id )
{
low = mid + 1;
}
}
else if ( chainCompare < 0 )
{
high = mid - 1;
}
else // ( chainCompare > 0 )
{
low = mid + 1;
}
}
}
// Try a much more expensive linear search in case the atom is out of order.
int atomCount = atoms.size();
for ( int a=0; a
*/
public Chain getChain( Atom atom )
{
if ( atom == null ) return null;
if ( residueByChainAndResidueId == null ) return null;
return (Chain) chainById.get( atom.chain_id );
}
/**
* Get the Chain object given its ID.
*
*/
public Chain getChain( String chainId )
{
if ( chainId == null ) return null;
if ( chainById == null ) return null;
return (Chain) chainById.get( chainId );
}
/**
* Get a count of bonds contained in this StructureMap.
*/
public int getBondCount( )
{
return bonds.size( );
}
/**
* Get the Bond at the specified index.
*
*/
public Bond getBond( int bondIndex )
{
return (Bond) bonds.elementAt( bondIndex );
}
/**
* Get the index of the specified Bond.
*
*/
public int getBondIndex( Bond bond )
{
if ( bond == null ) return -1;
int bondCount = bonds.size( );
if ( bondCount < 1 ) return -1;
Atom atom0 = bond.getAtom( 0 );
if ( atom0 == null ) return -1;
Atom atom1 = bond.getAtom( 1 );
if ( atom1 == null ) return -1;
// Do a binary search to find the Bond index.
int low = 0;
int high = bondCount - 1;
while ( low <= high )
{
int mid = (low + high) / 2;
Bond bond2 = getBond( mid );
if ( bond == bond2 ) return mid;
Atom atom2 = bond2.getAtom( 0 );
Atom atom3 = bond2.getAtom( 1 );
if ( atom0.number < atom2.number )
{
high = mid - 1;
}
else if ( atom0.number == atom2.number )
{
if ( atom1.number < atom3.number )
high = mid - 1;
else
low = mid + 1;
}
else // if ( atom0.number > atom2.number )
{
low = mid + 1;
}
}
// Just in case we didn't find the Bond using the binary search,
// do a linear search of the bonds vector. This would only happen
// if the bond did not exist, or the bonds were not sorted.
for ( int i=0; i
*/
public void addBonds( Vector bondVector )
{
if ( bondVector == null ) return;
int bvCount = bondVector.size( );
for ( int i=0; i
*/
public void removeBond( int bondIndex )
{
removeBond( getBond( bondIndex ) );
}
/**
* Remove all Bond objects from the StructureMap.
*
*/
public void removeAllBonds( )
{
// Since we are taking a short cut below by blowing away the data directly,
// we will eventually have to generate events for Bond in "bonds" here...
bonds.clear( );
atomToBonds.clear( );
bondToBonds.clear();
atomToAtoms.clear();
bondUniqueness.clear( );
}
/**
* Return a Vector of all Bond objects connected to the given Atom object.
* NOTE: The Vector returned here is the internal copy (for speed purposes),
* so DO NOT MODIFY THE VECTOR!
*
*/
public Vector getBonds( Atom atom )
{
return (Vector) atomToBonds.get( atom );
}
/**
* Return a Vector of bonds that are neighbors of the argument
* @param bond
* @return
*/
public Vector getBonds (Bond bond){
return (Vector)bondToBonds.get(bond);
}
/**
* Return a Vector of all Bond objects connected to the given Atom objects.
*
*/
public Vector getBonds( Vector atomVector )
{
Hashtable uniqueBonds = new Hashtable( );
int atomCount = atomVector.size( );
for ( int a=0; a
*/
public Residue getLigandResidue( int ligandIndex )
{
if ( ligands == null ) return null;
return (Residue) ligands.elementAt( ligandIndex );
}
/**
* Return the residue count extracted from the Structure.
*/
public int getResidueCount( )
{
if ( residues == null ) return 0;
return residues.size( );
}
/**
* Return the start Atom index for a given residue index.
*
*/
public Residue getResidue( int residueIndex )
{
if ( residues == null ) return null;
return (Residue) residues.elementAt( residueIndex );
}
/**
* Get the Residue object by its chain and residue id.
*
*/
public Residue getResidue (String chainAndResidue){
return (Residue)residueByChainAndResidueId.get(chainAndResidue);
}
/**
* Get the index of the specified Residue.
*
*/
public int getResidueIndex( Residue residue )
throws IllegalArgumentException
{
if ( residue == null )
throw new IllegalArgumentException( "null residue" );
if ( residues == null )
throw new IllegalArgumentException( "no residues" );
// Do a binary search of the residues vector.
int low = 0;
int high = residues.size() - 1;
while ( low <= high )
{
int mid = (low + high) / 2;
Residue residue2 = (Residue) residues.elementAt( mid );
// If the residue matches, return the index.
if ( residue == residue2 )
{
return mid;
}
else
{
String chainId = residue.getChainId( );
String chainId2 = residue2.getChainId( );
int chainCompare = chainId.compareTo( chainId2 );
if ( chainCompare == 0 )
{
int residueId = residue.getResidueId( );
int residueId2 = residue2.getResidueId( );
if ( residueId == residueId2 )
{
if ( residue == residue2 )
return mid;
else
break; // Give up and do an exhastive linear search
}
else if ( residueId < residueId2 )
{
high = mid - 1;
}
else // ( residueId > residueId2 )
{
low = mid + 1;
}
}
else if ( chainCompare < 0 )
{
high = mid - 1;
}
else // ( chainCompare > 0 )
{
low = mid + 1;
}
}
}
// Try a much more expensive linear search in case the residue
// is out of order.
int residueCount = residues.size();
for ( int i=0; i
*/
public int getFragmentIndex( Fragment fragment )
{
if ( fragment == null )
throw new IllegalArgumentException( "null fragment" );
if ( fragments == null )
throw new IllegalArgumentException( "no fragments!" );
int fragmentCount = fragments.size( );
for ( int f=0; f
*/
public Vector getChildren( Object object )
{
if ( object == null ) return null;
if ( object instanceof Structure )
return getChains( );
if ( ! (object instanceof StructureComponent) )
return null;
StructureComponent sc = (StructureComponent) object;
String type = sc.getStructureComponentType( );
if ( type == StructureComponentRegistry.TYPE_CHAIN )
{
return ((Chain) sc).getFragments( );
}
else if ( type == StructureComponentRegistry.TYPE_FRAGMENT )
{
return ((Fragment) sc).getResidues( );
}
else if ( type == StructureComponentRegistry.TYPE_RESIDUE )
{
return ((Residue) sc).getAtoms();
}
else if ( type == StructureComponentRegistry.TYPE_ATOM )
{
return null;
}
else if ( type == StructureComponentRegistry.TYPE_BOND )
{
// Even though physically Atoms are children, they
// are not concidered children logically.
// Note that Atoms ARE parents to Bonds.
return null;
}
else
{
return null;
}
}
/**
* Return the chains from the Structure.
*/
public Vector getChains( )
{
return chains;
}
/**
* @return Returns the atoms.
*/
public final Vector getAtoms() {
return atoms;
}
/**
* @return Returns the atomToAtoms.
*/
public final Hashtable getAtomToAtoms() {
return atomToAtoms;
}
/**
* @return Returns the atomToBonds.
*/
public final Hashtable getAtomToBonds() {
return atomToBonds;
}
/**
* @return Returns the bondOrderImported.
*/
public final boolean isBondOrderImported() {
return bondOrderImported;
}
/**
* @return Returns the bondOrderUserAssigned.
*/
public final boolean isBondOrderUserAssigned() {
return bondOrderUserAssigned;
}
/**
* @return Returns the bonds.
*/
public final Vector getBonds() {
return bonds;
}
/**
* @return Returns the bondToBonds.
*/
public final Hashtable getBondToBonds() {
return bondToBonds;
}
/**
* @return Returns the bondUniqueness.
*/
public final Hashtable getBondUniqueness() {
return bondUniqueness;
}
/**
* @return Returns the chainById.
*/
public final Hashtable getChainById() {
return chainById;
}
/**
* @return Returns the chargesUserAssigned.
*/
public final boolean isChargesUserAssigned() {
return chargesUserAssigned;
}
/**
* @return Returns the fragments.
*/
public final Vector getFragments() {
return fragments;
}
/**
* @return Returns the ligands.
*/
public final Vector getLigands() {
return ligands;
}
/**
* @return Returns the residueByChainAndResidueId.
*/
public final Hashtable getResidueByChainAndResidueId() {
return residueByChainAndResidueId;
}
/**
* @return Returns the residues.
*/
public final Vector getResidues() {
return residues;
}
/**
* Return the StructureComponent parent objects.
* Atom->Residue->Fragment->Chain->Structure->null, or,
* Bond->{Atom,Atom}->null, or,
* null.
*
*/
public Vector getParents( Object object )
{
if ( object == null )
return null;
if ( object instanceof Structure )
return null;
if ( ! (object instanceof StructureComponent) )
return null;
StructureComponent sc = (StructureComponent) object;
String type = sc.getStructureComponentType( );
Vector parents = null;
if ( type == StructureComponentRegistry.TYPE_ATOM )
{
parents = new Vector( );
parents.add( getResidue( (Atom) sc ) );
}
else if ( type == StructureComponentRegistry.TYPE_BOND )
{
parents = new Vector( );
Bond bond = (Bond) sc;
parents.add( bond.getAtom(0) );
parents.add( bond.getAtom(1) );
}
else if ( type == StructureComponentRegistry.TYPE_RESIDUE )
{
parents = new Vector( );
parents.add( ((Residue)sc).getFragment() );
}
else if ( type == StructureComponentRegistry.TYPE_FRAGMENT )
{
parents = new Vector( );
parents.add( ((Fragment)sc).getChain() );
}
else if ( type == StructureComponentRegistry.TYPE_CHAIN )
{
parents = new Vector( );
parents.add( structure );
}
return parents;
}
/**
* Should we "fill in" the disordered residue gaps with random coil
* when we load fragments?
*
* @see #getFillDisorderedGaps( )
*
*/
public void setFillDisorderedGaps( boolean state )
{
fillDisorderedGaps = state;
}
/**
* Should we "fill in" the disordered residue gaps with random coil
* when we load fragments?
*
* @see #setFillDisorderedGaps( boolean state )
*
*/
public boolean getFillDisorderedGaps( )
{
return fillDisorderedGaps;
}
/**
* This method walks over all bonds in the structure and calculates orders
* based on their geometry
*
*/
public void deriveBondOrders(){
for (int i = 0; i < bonds.size(); i++){
Bond bond = (Bond)bonds.get(i);
Atom a0 = bond.getAtom(0);
Atom a1 = bond.getAtom(1);
if (a0.element == 6 && a1.element == 6){//carbons
//both are carbons
}
else if (a0.element == 6){
double[] center = a0.coordinate;
double[] tip1 = a1.coordinate;
Atom third = null;
Vector atoms = getAtoms(a0);
for (int j = 0; j < atoms.size(); j++){
if ((Atom)atoms.get(j) != a0 && (Atom)atoms.get(j) != a1){
third = (Atom)atoms.get(j);
break;
}
}
if (third == null){
//nothing else is connected to this carbon
//this could be in files with no hydrogens for a methyl group
//if so, go to the other carbon to get the bond order
}
else{
//get the angle
double[] tip2 = third.coordinate;
double angle = Algebra.angle(tip1, center, tip2);
if (angle > 105 && angle <= 112){
//sp3 carbon
bond.setOrder(1.0f);
}
else if (angle > 112 && angle <= 118){
/**
* TODO check here for a possible aromatic case
*/
bond.setOrder(2.0f);
}
else {
bond.setOrder(3.0f);
}
}
}
else if (a1.element == 6){
}
else if (a0.element == 1 || a1.element == 1){
bond.setOrder(1.0f);
}
else{
bond.setOrder(1.0f);
}
}
}
public boolean inRing(Atom atom, int ringSize){
Vector atoms = getAtoms(atom);
visitedAtoms.put(atom, "");
boolean root = false;//root atom was seen as a neighbor
cycles = 1;
for (int i = 0; i < atoms.size(); i++){
Atom a = (Atom)atoms.get(i);
hasNeighbor(a, atom);
visitedAtoms.put(a, "");
}
return false;
}
private int cycles = 0;
private HashMap visitedAtoms = new HashMap();
private boolean hasNeighbor(Atom atom, Atom root){
Vector atoms = getAtoms(atom);
for (int i = 0; i < atoms.size(); i++){
Atom a = (Atom)atoms.get(0);
if (visitedAtoms.containsKey(a))continue;
if (a == root){
return true;
}
else{
return hasNeighbor(a, root);
}
}
return false;
}
/**
* This method removes an atom from the structure
*
*/
public synchronized void removeAtom(Atom atom){
if (!atoms.contains(atom)) return;
Chain chain = getChain( atom );
//get its bonds and remove them as well
Vector bonds = getBonds(atom);
if (bonds != null){
for (int i = bonds.size()-1; i >= 0; i--){
Bond b = (Bond)bonds.get(i);
removeBond(b);
}
}
atomToBonds.remove(atom);
atoms.remove(atom);
atomToAtoms.remove(atom);
//check all values in atomToAtoms to remove references to this Atom
/* Set keys = atomToAtoms.keySet();
Iterator it = keys.iterator();
while (it.hasNext()){
Atom a = (Atom)it.next();
Vector v = (Vector)atomToAtoms.get(a);
if (v.contains(atom)){
v.remove(atom);
}
}
*/
//remove the atom from its residue as well
Residue r = getResidue(atom);
r.getAtoms().remove(atom);
/**
* TODO handling of structure components in the Structure object
*/
//check if there is a residue now with no atoms
if (r.getAtomCount() == 0){
//this residue should be deleted and removed from the chain record
residues.remove(r);
chain.removeResidue(r);
getStructureStyles().registerStructureComponentRemoval(r);
//further check to see if the chain itself is still there
if (chain.getResidueCount() == 0){
//delete the chain
chains.remove(chain);
}
}
}
/**
* This method incorporates a new structure (fragment) into this structure
* by importing all data structures and updating names
* @param map
*/
public void addStructure(StructureMap map){
// System.out.println("this = " + this + ", map = " + map);
Structure s = this.getStructure();
for (int i = 0; i < map.getAtomCount(); i++){
Atom a = map.getAtom(i);
a.number = getNextAtomNumber();
a.structure = s;
}
for (int i = 0; i < map.getBondCount(); i++){
Bond b = map.getBond(i);
b.structure = s;
}
atoms.addAll(map.getAtoms());
bonds.addAll(map.getBonds());
//check the chains
//if a chain has a lettered name, add it as is. otherwise, merge the content with the corresponding chain
//of the parent structure. no need to have multiple chains for merged fragments of small molecules
//no two chains can have the same name. If it's a letter, reassign the name to be the next in alphabet after the last one existing
//in the parent structure. If it's a "_" and there is no "_" in the parent, add it as is. If there are "_" in both
//structures, merge them into one chain and renumber residues
//first, get a list of existing chains in both parts
Vector chains1 = new Vector();
Chain dashChain1 = null;
for (int i = 0; i < this.getChainCount(); i++){
Chain chain = this.getChain(i);
if (chain.getChainId().equals("_")) dashChain1 = chain;
chains1.add(chain.getChainId());
}
for (int i = 0; i < map.getChainCount(); i++){
Chain chain = map.getChain(i);
if (chain.getChainId().equals("_")){
if (dashChain1 == null){
//there is no dash chain in the parent structure, so just add this one
chains.add(chain);
chain.structure = s;
for (int j = 0; j < chain.getResidueCount(); j++){
chain.getResidue(j).structure = s;
residues.add(chain.getResidue(j));
}
chainById.put("_", chain);
residueByChainAndResidueId.putAll(map.getResidueByChainAndResidueId());
}
else{
//content of the new chain must be merged with the parent chain
//the only data that gets added is residue by chain and residue id hash entries
//in addition, the residues get renumbered
chain.structure = s;
Hashtable labels = new Hashtable();
for (int k = 0; k < chain.getResidueCount(); k++){
Residue r = chain.getResidue(k);
int n = getNextResidueNumber();
r.setResidueId(n);
for (int nn = 0; nn < r.getAtomCount(); nn++){
r.getAtom(nn).residue_id = n;
}
r.structure = s;
String label = "_" + n;
labels.put(label, r);
dashChain1.addResidue(r);
residues.add(r);
}
residueByChainAndResidueId.putAll(labels);
}
}
else{
//check whether the new chain has a name that is already taken
String chainId = chain.getChainId();
boolean pass = true;
for (int j = 0; j < this.getChainCount(); j++){
Chain c = this.getChain(j);
if (c.getChainId().equals(chainId)){
pass = false;
break;
}
}
if (pass){
//add the chain as is, with no changes to its name
chains.add(chain);
chain.structure = s;
for (int j = 0; j < chain.getResidueCount(); j++){
chain.getResidue(j).structure = s;
residues.add(chain.getResidue(j));
}
chainById.put(chainId, chain);
residueByChainAndResidueId.putAll(map.getResidueByChainAndResidueId());
}
else{
String id = getNextChainId();
chain.structure = s;
for (int j = 0; j < chain.getResidueCount(); j++){
Residue r = chain.getResidue(j);
r.structure = s;
int ri = r.getResidueId();
residueByChainAndResidueId.put(id+ri, r);
for (int k = 0; k < r.getAtomCount(); k++){
Atom a = r.getAtom(k);
a.chain_id = id;
a.residue_id = ri;
}
residues.add(r);
}
chainById.put(id, chain);
chains.add(chain);
}
}
}
fragments.addAll(map.getFragments());
atomToAtoms.putAll(map.getAtomToAtoms());
atomToBonds.putAll(map.getAtomToBonds());
bondToBonds.putAll(map.getBondToBonds());
bondUniqueness.putAll(map.getBondUniqueness());
ligands.addAll(map.getLigands());
for (int i = 0; i < fragments.size(); i++){
Fragment f = (Fragment)fragments.get(i);
f.structure = s;
}
//recalculate new structure bounds
calculateBounds();
}
public void calculateBounds(){
double maxX=0.0, minX=0.0, maxY=0.0, minY=0.0, maxZ=0.0, minZ=0.0;
for ( int i = 0; i < getAtomCount(); i++ )
{
Atom atom = getAtom(i);
if ( i == 0 )
{
maxX = atom.coordinate[0];
minX = atom.coordinate[0];
maxY = atom.coordinate[1];
minY = atom.coordinate[1];
maxZ = atom.coordinate[2];
minZ = atom.coordinate[2];
}
if ( atom.coordinate[0] >= maxX ) maxX = atom.coordinate[0];
if ( atom.coordinate[0] <= minX ) minX = atom.coordinate[0];
if ( atom.coordinate[1] >= maxY ) maxY = atom.coordinate[1];
if ( atom.coordinate[1] <= minY ) minY = atom.coordinate[1];
if ( atom.coordinate[2] >= maxZ ) maxZ = atom.coordinate[2];
if ( atom.coordinate[2] <= minZ ) minZ = atom.coordinate[2];
}
//calculate the bounds of the structure
maxCoordinate = new double[] {maxX, maxY, maxZ};
minCoordinate = new double[] {minX, minY, minZ};
}
/**
* Get all combination of three atoms connected through the central atom and forming an angle
* Each item is an array of three Atoms
* @return
*/
public Vector getAngleConnections(){
Vector out = new Vector();
//walk through atoms and pick those that have at least two bonds
//for those with more, record all combinations
//IMPORTANT: the central atom should be in the middle of the set, since iteration starts
//at the periphery
for (int i = 0; i < getAtomCount(); i++){
Atom a = getAtom(i);
Vector atoms = getAtoms(a);
int number = atoms.size();
if (number <= 1) continue;
else if (number == 2){
Atom[] set = new Atom[3];
set[0] = (Atom)atoms.get(0);
set[1] = a;
set[2] = (Atom)atoms.get(1);
out.add(set);
}
else{
//there are more than two atoms - record all possible pairwise combinations
//of the peripheral atoms
for (int j = 0; j < number; j++){
for (int k = j+1; k < number; k++){
Atom[] set = new Atom[3];
set[0] = (Atom)atoms.get(j);
set[1] = a;
set[2] = (Atom)atoms.get(k);
out.add(set);
}
}
}
}
return out;
}
public Vector getTorsionConnections(){
Vector out = new Vector();
//loop through all bonds and find those with terminal bonds attached
//record the atoms
for (int i = 0; i < getBondCount(); i++){
Bond b = getBond(i);
Atom a1 = b.getAtom(0);
Atom a2 = b.getAtom(1);
Vector atoms1 = getAtoms(a1);
Vector atoms2 = getAtoms(a2);
if (atoms1.size() <= 1 || atoms2.size() <= 1) continue;//this is a terminal atom, so no torsion at this bond
//walk through the atoms and record them
Atom[] indices1 = new Atom[atoms1.size() - 1];//minus the atom in the parent bond
int counter = 0;
for (int j = 0; j < atoms1.size(); j++){
if (atoms1.get(j) == a2){
continue;
}
indices1[counter++] = (Atom)atoms1.get(j);
}
//walk through the atoms and record their indices
Atom[] indices2 = new Atom[atoms2.size() - 1];//minus the atom in the parent bond
counter = 0;
for (int j = 0; j < atoms2.size(); j++){
if (atoms2.get(j) == a1){
continue;
}
indices2[counter++] = (Atom)atoms2.get(j);
}
//now walk through the collected indices and
for (int n = 0; n < indices1.length; n++){
for (int m = 0; m < indices2.length; m++){
Atom[] set = new Atom[4];
set[0] = indices1[n];
set[1] = a1;
set[2] = a2;
set[3] = indices2[m];
out.add(set);
}
}
}
return out;
}
/**
* Returns a Vector of arrays with pairs of atoms that don't form bonds
* @return
*/
public Vector getNonBondedConnections(){
Vector out = new Vector();
for (int i = 0; i < getAtomCount(); i++){
for (int j = i+1; j < getAtomCount(); j++){
Atom a1 = getAtom(i);
Atom a2 = getAtom(j);
Vector atoms = getAtoms(a1);
if (!atoms.contains(a2)){
Atom[] set = new Atom[2];
set[0] = a1;
set[1] = a2;
out.add(set);
}
}
}
return out;
}
public boolean forceFieldTypesSet = false;
/**
* This method calculates phi-psi angles for the protein structure and saves the values in the
* hashmaps
* If the structure contains no amino acids, false is returned. Otherwise, the result is true
*
*/
public boolean computePhiPsi(){
//first pass: check for amino acid content and save out C and N atoms for each Residue
cMap = new HashMap();
nMap = new HashMap();
boolean peptide = false;
for (int i = 0; i < getResidueCount(); i++){
Residue r = getResidue(i);
if (ProteinProperties.isAminoAcid(r.getCompoundCode())){
peptide = true;
//get the necessary atoms by looping through the list
for (int j = 0; j < r.getAtomCount(); j++){
Atom a = r.getAtom(j);
if (a.name.equals("C")){
cMap.put(r, a);
}
else if (a.name.equals("N")){
nMap.put(r, a);
}
}
}
}
if (!peptide) return false;
//go over all residues, this time calculating the angles
for (int i = 0; i < getResidueCount(); i++){
Residue r = getResidue(i);
if (ProteinProperties.isAminoAcid(r.getCompoundCode())){
Atom CA = null;
Atom localC = null;
Atom localN = null;
for (int j = 0; j < r.getAtomCount(); j++){
Atom a = r.getAtom(j);
if (a.name.equals("CA")){
CA = a;
}
else if (a.name.equals("C")){
localC = a;
}
else if (a.name.equals("N")){
localN = a;
}
}
try{
Atom C = (Atom)cMap.get(getResidue(i-1));
double angle = Algebra.dihedralAngle(C.coordinate, localN.coordinate, CA.coordinate, localC.coordinate);
phi.put(r, new Double(angle));
}
catch (Exception ex){
if (!endResidues.contains(r)) endResidues.add(r);
phi.put(r, new Double(-180));
}
try{
Atom N = (Atom)nMap.get(getResidue(i+1));
double angle = Algebra.dihedralAngle(localN.coordinate, CA.coordinate, localC.coordinate, N.coordinate);
psi.put(r, new Double(angle));
}
catch (Exception ex){
if (!endResidues.contains(r)) endResidues.add(r);
psi.put(r, new Double(180));
}
}
}
return true;
}
public void computePhiPsi(Residue r, HashMap transformed){
//if transformed coordinates are null, use atom records, otherwise, use the hash
int index = getResidueIndex(r);
if (index < 0) return;
if (ProteinProperties.isAminoAcid(r.getCompoundCode())){
//get the necessary atoms by looping through the list
Atom C = null;
Atom N = null;
Atom CA = null;
for (int j = 0; j < r.getAtomCount(); j++){
Atom a = r.getAtom(j);
if (a.name.equals("C")){
C = a;
}
else if (a.name.equals("N")){
N = a;
}
else if (a.name.equals("CA")){
CA = a;
}
}
//get the other atoms from the neighboring residues
try{
Residue last = getResidue(index-1);
double[] coordinateLastC = null;
double[] coordinateN = null;
double[] coordinateCA = null;
double[] coordinateC = null;
if (transformed != null && transformed.containsKey(cMap.get(last))){
coordinateLastC = (double[])transformed.get(cMap.get(last));
}
else{
coordinateLastC = ((Atom)cMap.get(last)).coordinate;
}
if (transformed != null && transformed.containsKey(N)){
coordinateN = (double[])transformed.get(N);
}
else{
coordinateN = N.coordinate;
}
if (transformed != null && transformed.containsKey(CA)){
coordinateCA = (double[])transformed.get(CA);
}
else{
coordinateCA = CA.coordinate;
}
if (transformed != null && transformed.containsKey(C)){
coordinateC = (double[])transformed.get(C);
}
else{
coordinateC = C.coordinate;
}
double angle = Algebra.dihedralAngle(coordinateLastC, coordinateN, coordinateCA, coordinateC);
phi.put(r, new Double(angle));
}
catch (Exception e){
phi.put(r, new Double(-180));
}
try{
Residue next = getResidue(index+1);
double[] coordinateNextN = null;
double[] coordinateN = null;
double[] coordinateCA = null;
double[] coordinateC = null;
if (transformed != null && transformed.containsKey(nMap.get(next))){
coordinateNextN = (double[])transformed.get(nMap.get(next));
}
else{
coordinateNextN = ((Atom)nMap.get(next)).coordinate;
}
if (transformed != null && transformed.containsKey(N)){
coordinateN = (double[])transformed.get(N);
}
else{
coordinateN = N.coordinate;
}
if (transformed != null && transformed.containsKey(CA)){
coordinateCA = (double[])transformed.get(CA);
}
else{
coordinateCA = CA.coordinate;
}
if (transformed != null && transformed.containsKey(C)){
coordinateC = (double[])transformed.get(C);
}
else{
coordinateC = C.coordinate;
}
double angle = Algebra.dihedralAngle(coordinateN, coordinateCA, coordinateC, coordinateNextN);
psi.put(r, new Double(angle));
}
catch (Exception e){
psi.put(r, new Double(180));
}
}
}
public HashMap getPhiMap(){
return phi;
}
public HashMap getPsiMap(){
return psi;
}
public Vector getEndResidues(){
return endResidues;
}
public double[] getPsiPhi(Residue r){
if (phi.containsKey(r) && psi.containsKey(r)){
return new double[]{ ((Double)phi.get(r)).doubleValue(),((Double)psi.get(r)).doubleValue() };
}
return null;
}
}