// $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. *

*

*/ private void processAtomRecords( ){ int atomCount = structure.getStructureComponentCount( StructureComponentRegistry.TYPE_ATOM ); double maxX=0.0, minX=0.0, maxY=0.0, minY=0.0, maxZ=0.0, minZ=0.0; double[] total = new double[3]; for ( int i=0; i */ private void loadFragments( ) { // // Clear the fragment map for each chain. // int chainCount = getChainCount( ); for ( int c=0; c= chain.getResidueCount() ) { Status.output( Status.LEVEL_WARNING, "Skipping oversized conformation record in chain " + chain_id + " at residue " + conformation.start_residue ); continue; } chain.setFragment( rIndex, rIndex+range, StructureComponentRegistry.TYPE_COIL ); } int helixCount = structure.getStructureComponentCount( StructureComponentRegistry.TYPE_HELIX ); for ( int i=0; i= chain.getResidueCount() ) { Status.output( Status.LEVEL_WARNING, "Skipping oversized conformation record in chain " + chain_id + " at residue " + conformation.start_residue ); // System.out.println("oversized"); continue; } // System.out.println("Setting fragment as helix"); chain.setFragment( rIndex, rIndex+range, StructureComponentRegistry.TYPE_HELIX ); } int strandCount = structure.getStructureComponentCount( StructureComponentRegistry.TYPE_STRAND ); for ( int i=0; i= chain.getResidueCount() ) { Status.output( Status.LEVEL_WARNING, "Skipping oversized conformation record in chain " + chain_id + " at residue " + conformation.start_residue ); continue; } chain.setFragment( rIndex, rIndex+range, StructureComponentRegistry.TYPE_STRAND ); } int turnCount = structure.getStructureComponentCount( StructureComponentRegistry.TYPE_TURN ); for ( int i=0; i= chain.getResidueCount() ) { Status.output( Status.LEVEL_WARNING, "Skipping oversized conformation record in chain " + chain_id + " at residue " + conformation.start_residue ); continue; } chain.setFragment( rIndex, rIndex+range, StructureComponentRegistry.TYPE_TURN ); } // // Examine the residues of each chain to see if any "random coil" // fragments need to be added. // for ( int c=0; c= 0 ) { // The residue has a valid alpha atom/index, but it has // no fragment assignment, so assign coil. String conformationType = residue.getConformationType( ); if ( conformationType == Conformation.TYPE_UNDEFINED ) chain.setFragment( r, r, StructureComponentRegistry.TYPE_COIL ); // Ths residue needs no further attention. continue; } // The residue does not have a valid alpha atom/index, if ( fillDisorderedGaps ) { // Assign a "reasonable" alpha atom/index, // and fill in the disordered residue gap. int reasonableAtom = residue.getAtomCount() / 2; residue.setAlphaAtomIndex( reasonableAtom ); chain.setFragment( r, r, StructureComponentRegistry.TYPE_COIL ); } else { // Leave a gap for the disordered residue. chain.setFragment( r, r, Conformation.TYPE_UNDEFINED ); } } // // Walk the fragments of the current chain, // coalescing contiguous runs of coil (where needed). // int range[] = new int[2]; int fragmentCount = chain.getFragmentCount( ); if ( fragmentCount <= 0 ) continue; String savedType = chain.getFragmentType( 0 ); int savedRange[] = new int[2]; savedRange[0] = chain.getFragmentStartResidue( 0 ); savedRange[1] = chain.getFragmentEndResidue( 0 ); for ( int i=1; i */ private void deriveFragments( ) { DerivedInformation derivInfo = new DerivedInformation( structure, this ); derivInfo.setConformationType( residues ); } /** * Processes residue records (ie: for a sequence). */ private void processResidueRecords( ) { int residueCount = structure.getStructureComponentCount( StructureComponentRegistry.TYPE_RESIDUE ); if ( residueCount <= 0 ) return; // System.out.println("chain processing"); Chain chain = new Chain( ); chain.structure = structure; addChain( chain ); String chain_id = chain.getChainId( ); chainById.put( chain_id, chain ); for ( int r=0; r 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 Residue getResidue( Atom atom ) { if ( atom == null ) return null; // if ( residueByChainAndResidueId == null ) return null; // String chainAndResidue = atom.chain_id + atom.residue_id; return atom.residue; } /** * Get the Chain object to which this Atom belongs. *

*/ 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 addBond( Bond bond ) { if ( bond == null ) return; Atom atom0 = bond.getAtom( 0 ); if ( atom0 == null ) return; Atom atom1 = bond.getAtom( 1 ); if ( atom1 == null ) return; // Make sure the Bond is unique (note: the Bond has a custom hashCode method). if ( bondUniqueness.put( bond, value ) != null ) { return; } // Keep track of the atom-to-bonds relationship. for ( int a=0; a<=1; a++ ) // Examine both atoms of the bond { Atom atom = bond.getAtom( a ); Vector atomBonds = (Vector) atomToBonds.get( atom ); if ( atomBonds == null ) { atomBonds = new Vector( ); atomToBonds.put( atom, atomBonds ); } atomBonds.add( bond ); //add bond to bond reference Vector bondBonds = (Vector)bondToBonds.get(bond); if (bondBonds == null){ bondBonds = new Vector(); bondToBonds.put(bond, bondBonds); } //check all bonds currently recorded for the atom in question Vector bbb = getBonds(atom); for (int i = 0; i < bbb.size(); i++){ if (bondBonds.contains(bbb.get(i)) || bbb.get(i) == bond)continue; bondBonds.add(bbb.get(i)); //also, add the current bond to the list of the listed bond Vector otherBondList = getBonds((Bond)bbb.get(i)); if (otherBondList.contains(bond))continue; otherBondList.add(bond); } Vector bondAtoms = (Vector) atomToAtoms.get( atom ); if ( bondAtoms == null){ bondAtoms = new Vector(); } if (a == 0){ if (!bondAtoms.contains(bond.getAtom(1))) bondAtoms.add(bond.getAtom(1)); } else{ if (!bondAtoms.contains(bond.getAtom(0))) bondAtoms.add(bond.getAtom(0)); } atomToAtoms.put(atom, bondAtoms); } // System.err.println( "StructureMap.addBond: " + atom0.number + " - " + atom1.number ); int bondCount = bonds.size( ); if ( bondCount < 1 ) { bonds.add( bond ); return; } // Do a binary search to determine where this new Bond should be added. int low = 0; int high = bondCount - 1; int mid = 0; while ( low <= high ) { mid = (low + high) / 2; Bond bond2 = getBond( mid ); if ( bond == bond2 ) return; // Bond already added! 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; } } mid = (low + high) / 2; // Add the bond. bonds.add( mid+1, bond ); bond.structure = this.structure; } public void addAtom(Atom added, Residue target, boolean assignName){ atoms.add(added); added.residue_id = target.getResidueId();//assign it to the same residue added.residue = target; added.chain_id = target.getChainId(); added.compound = target.getCompoundCode(); added.structure = target.structure; //assign it a number. before adding a number on top of the total count, check //for gaps in the current numbering int[] numbers = new int[getAtomCount()]; for (int i = 0; i < getAtomCount(); i++){ numbers[i] = getAtom(i).number; } Arrays.sort(numbers); boolean gap = false; int last = -1; for (int i = 0; i < numbers.length; i++){ if (last == -1){ last = numbers[i]; continue; } int dif = numbers[i] - last; if (dif > 1){ //this is a gap added.number = numbers[i] - 1; gap = true; break; } last = numbers[i]; } if (!gap){ //append the number at the end of the current list added.number = getAtomCount(); } if (assignName) added.name = added.getElement() + added.number; target.addAtom(added); } /** * Returns the next available atom number for this structure. If there are no gaps in the existing * numbering, a new number is appended at the end. Otherwise, a number is provided to fill the gap. * @return */ private int getNextAtomNumber(){ int[] numbers = new int[getAtomCount()]; for (int i = 0; i < getAtomCount(); i++){ numbers[i] = getAtom(i).number; } Arrays.sort(numbers); boolean gap = false; int last = -1; for (int i = 0; i < numbers.length; i++){ if (last == -1){ last = numbers[i]; continue; } int dif = numbers[i] - last; if (dif > 1){ //this is a gap return (numbers[i] - 1); } last = numbers[i]; } //append the number at the end of the current list return getAtomCount()+1; } /** * Returns the next available residue number for this residue. If there are no gaps in the existing * numbering, a new number is appended at the end. Otherwise, a number is provided to fill the gap. * @return */ private int getNextResidueNumber(){ int[] numbers = new int[getResidueCount()]; for (int i = 0; i < getResidueCount(); i++){ numbers[i] = getResidue(i).getResidueId(); } Arrays.sort(numbers); boolean gap = false; int last = -1; for (int i = 0; i < numbers.length; i++){ if (last == -1){ last = numbers[i]; continue; } int dif = numbers[i] - last; if (dif > 1){ //this is a gap return (numbers[i] - 1); } last = numbers[i]; } //append the number at the end of the current list return getResidueCount()+1; } private String getNextChainId(){ Vector alphabet = new Vector(); alphabet.add("A"); alphabet.add("B"); alphabet.add("C"); alphabet.add("D"); alphabet.add("E"); alphabet.add("F"); alphabet.add("G"); alphabet.add("H"); alphabet.add("I"); alphabet.add("J"); alphabet.add("K"); alphabet.add("L"); alphabet.add("M"); alphabet.add("N"); alphabet.add("O"); alphabet.add("P"); alphabet.add("Q"); alphabet.add("R"); alphabet.add("S"); alphabet.add("T"); alphabet.add("U"); alphabet.add("V"); alphabet.add("W"); alphabet.add("X"); alphabet.add("Y"); alphabet.add("Z"); for (int j = 0; j < this.getChainCount(); j++){ Chain c = this.getChain(j); String label = c.getChainId(); for (int k = 0; k < alphabet.size(); k++){ if (((String)alphabet.get(k)).equals(label)){ alphabet.remove(k); break; } } } return (String)alphabet.get(0); } public void addAtom(Atom added, Atom bonded, Bond b, boolean assignName){ atoms.add(added); added.residue_id = bonded.residue_id;//assign it to the same residue added.residue = bonded.residue; added.chain_id = bonded.chain_id; added.compound = bonded.compound; //populate the atomToAtoms hash Vector aaa = (Vector)atomToAtoms.get(bonded); if (!aaa.contains(added)) aaa.add(added); // System.out.println("added = " + added.element); added.number = getNextAtomNumber(); // System.out.println("name of " + added + " = " + added.name); if (assignName || added.name == null) added.name = added.getElement() + added.number; this.getResidue(bonded).addAtom(added); addBond(b); } /** * Simply add an atom to the records don't assign bonds (they may be added later), and assign number based on the current numbering in the structure residue is a new Residue object that will be added to the structure (subsequent atoms may be added to the same residue from the calling method) * @param atom * @param residue */ public void addAtom(Atom atom, Residue residue, String chainId){ //add the atom to the residue residue.addAtom(atom); //check a couple of variables, since residue needs to have at least one atom to //work correctly in the bookkeeping issues if (!residues.contains(residue)){ Chain chain = null; for (int i = 0; i < getChainCount(); i++){ Chain c = getChain(i); if (c.getChainId().equals(chainId)){ chain = c; break; } } if (chain == null){ chain = (Chain)chains.get(chains.size()-1); } residue.structure = this.structure; residue.setChainId(chain.getChainId()); int next = getNextResidueNumber(); residue.setNumber(next); residue.setResidueId(next); residues.add(residue); chain.addResidue(residue); if (residue.getCompoundCode() == null){ //create a compound code for the new residue residue.setCompoundCode("X" + residue.getNumber()); } // for (int i = 0; i < residues.size(); i++){ // Residue r = (Residue)residues.get(i); // System.out.println("After adding residue: " + r.getCompoundCode() + r.getResidueId()); // } } atom.residue = residue; atom.residue_id = residue.getResidueId(); atom.structure = this.structure; atom.chain_id = residue.getChainId(); atom.compound = residue.getCompoundCode(); atom.number = getNextAtomNumber(); atoms.add(atom); } /** * Adds a new atom with the specified position of the residue (if new) * @param atom * @param residue * @param position 1-based location of the residue in the chain */ public void addAtom(Atom atom, Residue residue, int position, String chainId){ //add the atom to the residue residue.addAtom(atom); //check a couple of variables, since residue needs to have at least one atom to //work correctly in the bookkeeping issues if (!residues.contains(residue)){ //create a record for the residue and assign it to the last chain in the record Chain chain = null; for (int i = 0; i < getChainCount(); i++){ Chain c = getChain(i); if (c.getChainId().equals(chainId)){ chain = c; break; } } if (chain == null){ chain = (Chain)chains.get(chains.size()-1); } residue.structure = this.structure; residue.setChainId(chain.getChainId()); int id = position; boolean started = false; boolean inserted = false; int counter = 0; for (int i = 0; i < residues.size(); i++){ Residue r = (Residue)residues.get(i); if (r.getChainId().equals(chain.getChainId())){ started = true; counter++; } if (started && counter == position){ if (!inserted){ //insert the new residue before the current one residues.insertElementAt(residue, i);//insert instead of the current residue and shift everything up residue.setResidueId(position); residue.setNumber(position); inserted = true; continue; } } if (inserted){ position++; // System.out.println("numbering " + r.getCompoundCode() + " to " + position); r.setResidueId(position); r.setNumber(position); for (int j = 0; j < r.getAtomCount(); j++){ ((Atom)r.getAtom(j)).residue_id = position; } } } if (residue.getCompoundCode() == null){ //create a compound code for the new residue residue.setCompoundCode("X" + residue.getNumber()); } chain.addResidue(residue, id); // for (int i = 0; i < residues.size(); i++){ // Residue rr = (Residue)residues.get(i); // System.out.println("rr = " + rr.getCompoundCode() + rr.getResidueId()); // } } atom.residue = residue; atom.residue_id = residue.getResidueId(); atom.structure = this.structure; atom.chain_id = residue.getChainId(); atom.compound = residue.getCompoundCode(); atom.number = getNextAtomNumber(); atoms.add(atom); } public void addAtom(Atom added, Atom bonded, Bond b, int number){ atoms.add(added); added.residue_id = bonded.residue_id;//assign it to the same residue added.residue = bonded.residue; added.chain_id = bonded.chain_id; added.compound = bonded.compound; added.number = number; if (added.name == null){ added.name = added.getElement() + added.number; } //populate the atomToAtoms hash Vector aaa = (Vector)atomToAtoms.get(bonded); if (!aaa.contains(added)) aaa.add(added); this.getResidue(bonded).addAtom(added); addBond(b); } /** * Add a vector of Bond objects to the StructureMap. *

*/ public void addBonds( Vector bondVector ) { if ( bondVector == null ) return; int bvCount = bondVector.size( ); for ( int i=0; i */ public void removeBond( Bond bond ) { if ( bond == null ) return; // Remove the Bond from the master Vector. bonds.remove(bond); // Make sure the Bond is removed from the unique hash (note: the Bond has a custom hashCode method). bondUniqueness.remove( bond ); // Keep track of the atom-to-bonds relationship. for ( int a=0; a<=1; a++ ) // Examine both atoms of the bond { Atom atom = bond.getAtom( a ); Vector atomBonds = (Vector) atomToBonds.get( atom ); // Does the atom have any Bond objects connected to it? if ( atomBonds != null ) { atomBonds.remove( bond ); if ( atomBonds.size() <= 0 ) atomToBonds.remove( atom ); } //same for atom to atom Vector atomAtoms = (Vector)atomToAtoms.get(atom); if (atomAtoms != null){ if (a == 0){ atomAtoms.remove(bond.getAtom(1)); } else{ atomAtoms.remove(bond.getAtom(0)); } } if (atomAtoms == null || atomAtoms.size() == 0){ atomToAtoms.remove(atom); } } //remove the bond from any lookup hashes Set keys = atomToBonds.keySet(); Iterator it = keys.iterator(); while (it.hasNext()){ Atom a = (Atom)it.next(); Vector v = (Vector)atomToBonds.get(a); if (v.contains(bond)){ v.remove(bond); } } //remove the bond from any lookup hashes keys = bondToBonds.keySet(); it = keys.iterator(); while (it.hasNext()){ Bond a = (Bond)it.next(); Vector v = (Vector)bondToBonds.get(a); if (v.contains(bond)){ v.remove(bond); } } bondToBonds.remove(bond); } /** * Remove a Bond from the StructureMap. *

*/ 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 void generateBonds( ) { removeAllBonds( ); Vector newBonds = BondFactory.generateCovalentBonds( atoms, bondCutoff, true ); if (newBonds == null) return; //create a temporary atom to bonds hash HashMap map = new HashMap(); for (int i = 0; i < newBonds.size(); i++){ Bond b = (Bond)newBonds.get(i); Atom a1 = b.getAtom(0); Atom a2 = b.getAtom(1); if (map.containsKey(a1)){ Vector a = (Vector)map.get(a1); if (!a.contains(b)) a.add(b); } else{ Vector a = new Vector(); a.add(b); map.put(a1, a); } if (map.containsKey(a2)){ Vector a = (Vector)map.get(a2); if (!a.contains(b)) a.add(b); } else{ Vector a = new Vector(); a.add(b); map.put(a2, a); } } //remove redundant bonds for H's for (int i = 0; i < getAtomCount(); i++){ Atom a = getAtom(i); if (a.element == 1 && map.get(a) != null && ((Vector)map.get(a)).size() > 1){ //remove the longer of the two bonds Vector bonds = (Vector)map.get(a); Bond stays = null;//the shortest double min = 1000000; for (int j = 0; j < bonds.size(); j++){ Bond b = (Bond)bonds.get(j); double dist = Algebra.distance(b.getAtom(0).coordinate, b.getAtom(1).coordinate); if (dist < min){ min = dist; stays = b; } } for (int j = bonds.size()-1; j >= 0; j--){ Bond b = (Bond)bonds.get(j); if (b == stays) continue; //this bonds has to leave. // System.out.println("removing redundant bond"); newBonds.remove(b); } } } addBonds( newBonds ); } /** * This method detects missing bonds without removing any existing ones. The new * bonds are added. This feature is useful for loading data that have mixed * protein/ligand data, and ligands don't have conect records. */ public Vector detectBonds(){ Vector bonds = BondFactory.generateCovalentBonds( atoms, bondCutoff, true ); //scan these bonds and remove those that already exist if (bonds == null) return null; for (int i = bonds.size()-1; i >= 0; i--){ Bond b = (Bond)bonds.get(i); Atom a0 = b.getAtom(0); Atom a1 = b.getAtom(1); Vector bb = this.getBonds(a0); if (bb == null) continue; for (int j = 0; j < bb.size(); j++){ Bond bbb = (Bond)bb.get(j); if (bbb.getAtom(0) == a0){ if (bbb.getAtom(1) == a1){ // System.out.println("removing existing bond = " + bbb.getAtom(0).name + " - " + bbb.getAtom(1).name); //this bond already exists bonds.remove(b); continue; } } else if (bbb.getAtom(1) == a0){ if (bbb.getAtom(0) == a1){ // System.out.println("removing existing bond = " + bbb.getAtom(0).name + " - " + bbb.getAtom(1).name); bonds.remove(b); continue; } } } } /** * TODO remove bridging cases, as well as bonds that violate valency rules */ for (int i = bonds.size()-1; i >= 0; i--){ Bond b = (Bond)bonds.get(i); Atom a0 = b.getAtom(0); Atom a1 = b.getAtom(1); if (a0.element == 1){ //check if this atom already is bonded //first, check if this bond is too long if (Algebra.distance(a0.coordinate, a1.coordinate) > 1.6){ bonds.remove(b); continue; } Vector bbb = this.getBonds(b.getAtom(0)); if (bbb != null && bbb.size() > 0){ // System.out.println("H already bonded to " + bbb.get(0)); //delete this bond, since the H is already bonded bonds.remove(b); continue; } } else{ //check whether this atom already has satisfied valency float sum = 0; Vector local = getBonds(a0); if (local != null){ for (int j = 0; j < local.size(); j++){ sum += ((Bond)local.get(j)).order; } if (sum >= (float)ElementProperties.getElementValency(a0.element)[0]){ bonds.remove(b); continue; } } } if (a1.element == 1){ if (Algebra.distance(a0.coordinate, a1.coordinate) > 1.6){ bonds.remove(b); continue; } //check if this atom already is bonded Vector bbb = this.getBonds(b.getAtom(1)); if (bbb != null && bbb.size() > 0){ //delete this bond, since the H is already bonded // System.out.println("H already bonded"); bonds.remove(b); continue; } } else{ //check whether this atom already has satisfied valency float sum = 0; Vector local = getBonds(a0); if (local != null){ for (int j = 0; j < local.size(); j++){ sum += ((Bond)local.get(j)).order; } if (sum >= (float)ElementProperties.getElementValency(a0.element)[0]){ bonds.remove(b); continue; } } } } //create a temporary atom to bonds hash HashMap map = new HashMap(); for (int i = 0; i < bonds.size(); i++){ Bond b = (Bond)bonds.get(i); Atom a1 = b.getAtom(0); Atom a2 = b.getAtom(1); if (map.containsKey(a1)){ Vector a = (Vector)map.get(a1); if (!a.contains(b)) a.add(b); } else{ Vector a = new Vector(); a.add(b); map.put(a1, a); } if (map.containsKey(a2)){ Vector a = (Vector)map.get(a2); if (!a.contains(b)) a.add(b); } else{ Vector a = new Vector(); a.add(b); map.put(a2, a); } } //remove redundant bonds for H's for (int i = 0; i < getAtomCount(); i++){ Atom a = getAtom(i); if (a.element == 1 && map.get(a) != null && ((Vector)map.get(a)).size() > 1){ //remove the longer of the two bonds Vector bbb = (Vector)map.get(a); Bond stays = null;//the shortest double min = 1000000; for (int j = 0; j < bbb.size(); j++){ Bond b = (Bond)bbb.get(j); double dist = Algebra.distance(b.getAtom(0).coordinate, b.getAtom(1).coordinate); if (dist < min){ min = dist; stays = b; } } for (int j = bbb.size()-1; j >= 0; j--){ Bond b = (Bond)bbb.get(j); if (b == stays) continue; //this bonds has to leave bonds.remove(b); } } } addBonds(bonds); return bonds; } public void detectLigandBonds(){ Vector atoms = new Vector(); for (int i = 0; i < residues.size(); i++){ Residue r = (Residue)residues.get(i); if (r.getClassification().equals(Residue.COMPOUND_LIGAND)){ for (int j = 0; j < r.getAtomCount(); j++){ atoms.add(r.getAtom(j)); } } } Vector bonds = BondFactory.generateCovalentBonds( atoms, bondCutoff, true ); //get the list of bonds that already exist for the atoms in question Vector existing = new Vector(); for (int i = 0; i < atoms.size(); i++){ Atom a = (Atom)atoms.get(i); Vector bb = (Vector)atomToBonds.get(a); if (bb == null) continue; for (int j = 0; j < bb.size(); j++){ if (!existing.contains(bb.get(j))) existing.add(bb.get(j)); } } //scan these bonds and remove those that already exist for (int i = bonds.size()-1; i >= 0; i--){ Bond b = (Bond)bonds.get(i); Atom a0 = b.getAtom(0); Atom a1 = b.getAtom(1); Vector bb = this.getBonds(a0); if (bb == null) continue; for (int j = 0; j < bb.size(); j++){ Bond bbb = (Bond)bb.get(j); if (bbb.getAtom(0) == a0){ if (bbb.getAtom(1) == a1){ // System.out.println("removing existing bond = " + bbb.getAtom(0).name + " - " + bbb.getAtom(1).name); //this bond already exists bonds.remove(b); continue; } } else if (bbb.getAtom(1) == a0){ if (bbb.getAtom(0) == a1){ // System.out.println("removing existing bond = " + bbb.getAtom(0).name + " - " + bbb.getAtom(1).name); bonds.remove(b); continue; } } } } /** * TODO remove bridging cases, as well as bonds that violate valency rules */ for (int i = bonds.size()-1; i >= 0; i--){ Bond b = (Bond)bonds.get(i); Atom a0 = b.getAtom(0); Atom a1 = b.getAtom(1); //first, check if this bond is too long if (Algebra.distance(a0.coordinate, a1.coordinate) > 1.6){ bonds.remove(b); continue; } if (a0.element == 1 || a0.element == 0){ //check if this atom already is bonded Vector bbb = this.getBonds(b.getAtom(0)); if (bbb != null && bbb.size() > 0){ // System.out.println("H already bonded to " + bbb.get(0)); //delete this bond, since the H is already bonded bonds.remove(b); continue; } } else{ //check whether this atom already has satisfied valency float sum = 0; Vector local = getBonds(a0); if (local != null){ for (int j = 0; j < local.size(); j++){ sum += ((Bond)local.get(j)).order; } if (sum >= (float)ElementProperties.getElementValency(a0.element)[0]){ bonds.remove(b); continue; } } } if (a1.element == 1 || a1.element == 0){ //check if this atom already is bonded Vector bbb = this.getBonds(b.getAtom(1)); if (bbb != null && bbb.size() > 0){ //delete this bond, since the H is already bonded // System.out.println("H already bonded"); bonds.remove(b); continue; } } else { //check whether this atom already has satisfied valency float sum = 0; Vector local = getBonds(a0); if (local != null){ for (int j = 0; j < local.size(); j++){ sum += ((Bond)local.get(j)).order; } if (sum >= (float)ElementProperties.getElementValency(a0.element)[0]){ bonds.remove(b); continue; } } } } //create a temporary atom to bonds hash HashMap map = new HashMap(); for (int i = 0; i < bonds.size(); i++){ Bond b = (Bond)bonds.get(i); Atom a1 = b.getAtom(0); Atom a2 = b.getAtom(1); if (map.containsKey(a1)){ Vector a = (Vector)map.get(a1); if (!a.contains(b)) a.add(b); } else{ Vector a = new Vector(); a.add(b); map.put(a1, a); } if (map.containsKey(a2)){ Vector a = (Vector)map.get(a2); if (!a.contains(b)) a.add(b); } else{ Vector a = new Vector(); a.add(b); map.put(a2, a); } } //remove redundant bonds for H's for (int i = 0; i < getAtomCount(); i++){ Atom a = getAtom(i); if (a.element == 1 && map.get(a) != null && ((Vector)map.get(a)).size() > 1){ //remove the longer of the two bonds Vector bbb = (Vector)map.get(a); Bond stays = null;//the shortest double min = 1000000; for (int j = 0; j < bbb.size(); j++){ Bond b = (Bond)bbb.get(j); double dist = Algebra.distance(b.getAtom(0).coordinate, b.getAtom(1).coordinate); if (dist < min){ min = dist; stays = b; } } for (int j = bbb.size()-1; j >= 0; j--){ Bond b = (Bond)bbb.get(j); if (b == stays) continue; //this bonds has to leave. bonds.remove(b); } } } addBonds(bonds); } /** * Return the ligand (het group) count. */ public int getLigandCount( ) { if ( ligands == null ) return 0; return ligands.size( ); } /** * Return the Residue for a given ligand index. *

*/ 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 Fragment getFragment( int fragmentIndex ) { if ( fragments == null ) return null; return (Fragment) fragments.elementAt( fragmentIndex ); } /** * Get the index of the specified Fragment. *

*/ 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 int getChainIndex( Chain chain ) { if ( chains == null ) return -1; int chainCount = chains.size( ); // Do a linear search of the chains vector. for ( int i=0; i */ public StructureStyles getStructureStyles( ) { if ( structureStyles == null ) structureStyles = new StructureStyles( this ); return structureStyles; } // // StructureMap Debug Methods. // /* * Walk and print the primary StructureMap tree. */ private void print( ) { System.err.println( "StructureMap.print: BEGIN" ); int chainCount = getChainCount( ); System.err.println( "chainCount = " + chainCount ); for ( int c=0; cChain->Fragment->Residue->Atom->null, or, * Bond->null. *

*/ 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; } }