001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 * Created on Aug 2, 2010
021 * Author: Jianjiong Gao
022 *
023 */
024
025package org.biojava.nbio.protmod.structure;
026
027import org.biojava.nbio.structure.*;
028import org.biojava.nbio.structure.io.mmcif.MetalBondParser;
029import org.biojava.nbio.structure.io.mmcif.chem.MetalBondDistance;
030
031import java.util.ArrayList;
032import java.util.Collections;
033import java.util.List;
034import java.util.Map;
035
036public final class StructureUtil {
037        private StructureUtil() {
038                throw new AssertionError();
039        }
040
041        /**
042         *
043         * @param group a {@link Group} in structure.
044         * @param isAminoAcid true if it is an amino acid.
045         * @return the {@link StructureGroup} of the group.
046         */
047        public static StructureGroup getStructureGroup(Group group, boolean isAminoAcid) {
048                ResidueNumber resNum = group.getResidueNumber();
049                return new StructureGroup(resNum, group.getPDBName(), isAminoAcid);
050        }
051
052        /**
053         *
054         * @param atom a {@link Atom} in structure.
055         * @param isParentAminoAcid true if the containing group is an amino acid.
056         * @return the {@link StructureAtom} of the atom.
057         */
058        public static StructureAtom getStructureAtom(Atom atom, boolean isParentAminoAcid) {
059
060                Group g = atom.getGroup();
061                String chainId = g.getChainId();
062                StructureGroup strucGroup = getStructureGroup(g, isParentAminoAcid);
063                strucGroup.setChainId(chainId);
064                return new StructureAtom(strucGroup, atom.getName());
065        }
066
067        /**
068         *
069         * @param atom1 the first {@link Atom} in structure.
070         * @param isParentAminoAcid1 true if the first containing group is an amino acid..
071         * @param atom2 the second {@link Atom} in structure.
072         * @param isParentAminoAcid2 true if the second containing group is an amino acid..
073         * @return the {@link StructureAtomLinkage} of the two atoms.
074         */
075        public static StructureAtomLinkage getStructureAtomLinkage(Atom atom1,
076                        boolean isParentAminoAcid1, Atom atom2, boolean isParentAminoAcid2) {
077                StructureAtom strucAtom1 = getStructureAtom(atom1, isParentAminoAcid1);
078                StructureAtom strucAtom2 = getStructureAtom(atom2, isParentAminoAcid2);
079                double distance = getAtomDistance(atom1, atom2);
080                return new StructureAtomLinkage(strucAtom1, strucAtom2, distance);
081        }
082
083        /**
084         *
085         * @param atom1 the first {@link Atom} in structure.
086         * @param atom2 the second {@link Atom} in structure.
087         * @return the distance between the two atoms in Angstrom.
088         */
089        public static double getAtomDistance(Atom atom1, Atom atom2) {
090                double distance;
091
092                distance = Calc.getDistance(atom1, atom2);
093
094                return distance;
095        }
096
097        /**
098         * Find a linkage between two groups within tolerance of bond length,
099         * from potential atoms.
100         * @param group1 the first {@link Group}.
101         * @param group2 the second {@link Group}.
102         * @param potentialNamesOfAtomOnGroup1 potential names of the atom on the first group.
103         *                If null, search all atoms on the first group.
104         * @param potentialNamesOfAtomOnGroup2 potential names of the atom on the second group.
105         *                If null, search all atoms on the second group.
106         * @param ignoreNCLinkage true to ignore all N-C linkages
107         * @param bondLengthTolerance bond length error tolerance.
108         * @return an array of two Atoms that form bond between each other
109         *  if found; null, otherwise.
110         */
111        public static Atom[] findNearestAtomLinkage(final Group group1, final Group group2,
112                        List<String> potentialNamesOfAtomOnGroup1, List<String> potentialNamesOfAtomOnGroup2,
113                        final boolean ignoreNCLinkage, double bondLengthTolerance) {
114
115
116                List<Atom[]> linkages = findAtomLinkages(group1, group2,
117                                potentialNamesOfAtomOnGroup1, potentialNamesOfAtomOnGroup2,
118                                ignoreNCLinkage, bondLengthTolerance);
119
120                Atom[] ret = null;
121                double minDistance = Double.POSITIVE_INFINITY;
122
123                for (Atom[] linkage : linkages) {
124                        double distance;
125
126                        distance = Calc.getDistance(linkage[0], linkage[1]);
127
128
129                        if (distance < minDistance) {
130                                minDistance = distance;
131                                ret = linkage;
132                        }
133                }
134
135                return ret;
136        }
137
138        /**
139         * Find linkages between two groups within tolerance of bond length,
140         * from potential atoms.
141         * @param group1 the first {@link Group}.
142         * @param group2 the second {@link Group}.
143         * @param ignoreNCLinkage true to ignore all N-C linkages
144         * @param bondLengthTolerance bond length error tolerance.
145         * @return a list, each element of which is an array of two Atoms that form bond
146         * between each other.
147         */
148        public static List<Atom[]> findAtomLinkages(final Group group1,
149                        final Group group2, final boolean ignoreNCLinkage,
150                        final double bondLengthTolerance) {
151                return findAtomLinkages(group1, group2,
152                                null, null, ignoreNCLinkage, bondLengthTolerance);
153        }
154
155        /**
156         * Find linkages between two groups within tolerance of bond length,
157         * from potential atoms.
158         * @param group1 the first {@link Group}.
159         * @param group2 the second {@link Group}.
160         * @param potentialNamesOfAtomOnGroup1 potential names of the atom on the first group.
161         *                If null, search all atoms on the first group.
162         * @param potentialNamesOfAtomOnGroup2 potential names of the atom on the second group.
163         *                If null, search all atoms on the second group.
164         * @param ignoreNCLinkage true to ignore all N-C linkages
165         * @param bondLengthTolerance bond length error tolerance.
166         * @return a list, each element of which is an array of two Atoms that form bond
167         * between each other.
168         */
169        public static List<Atom[]> findAtomLinkages(final Group group1,
170                        final Group group2,
171                        List<String> potentialNamesOfAtomOnGroup1,
172                        List<String> potentialNamesOfAtomOnGroup2,
173                        final boolean ignoreNCLinkage,
174                        final double bondLengthTolerance) {
175                if (group1==null || group2==null) {
176                        throw new IllegalArgumentException("Null group(s).");
177                }
178
179                if (bondLengthTolerance<0) {
180                        throw new IllegalArgumentException("bondLengthTolerance cannot be negative.");
181                }
182
183                List<Atom[]> ret = new ArrayList<Atom[]>();
184
185                if (potentialNamesOfAtomOnGroup1 == null) {
186                        // if empty name, search for all atoms
187                        potentialNamesOfAtomOnGroup1 = getAtomNames(group1);
188                }
189
190                if (potentialNamesOfAtomOnGroup2 == null) {
191                        // if empty name, search for all atoms
192                        potentialNamesOfAtomOnGroup2 = getAtomNames(group2);
193                }
194
195                for (String namesOfAtomOnGroup1 : potentialNamesOfAtomOnGroup1) {
196                        for (String namesOfAtomOnGroup2 : potentialNamesOfAtomOnGroup2) {
197                                Atom[] atoms = findLinkage(group1, group2, namesOfAtomOnGroup1,
198                                                namesOfAtomOnGroup2, bondLengthTolerance);
199                                if (atoms != null) {
200                                        if (ignoreNCLinkage &&
201                                                        ((atoms[0].getName().equals("N") && atoms[1].getName().equals("C"))
202                                                                        || (atoms[0].getName().equals("C") && atoms[1].getName().equals("N")))
203                                                        ) {
204                                                continue;
205                                        }
206
207                                        ret.add(atoms);
208                                }
209                        }
210                }
211
212                return ret;
213        }
214
215        /**
216         * Find a linkage between two groups within tolerance of bond length.
217         * @param group1 the first {@link Group}.
218         * @param group2 the second {@link Group}.
219         * @param nameOfAtomOnGroup1 atom name of the first group.
220         * @param nameOfAtomOnGroup2 atom name of the second group.
221         * @param bondLengthTolerance bond length error tolerance.
222         * @return an array of two Atoms that form bond between each other
223         *  if found; null, otherwise.
224         */
225        public static Atom[] findLinkage(final Group group1, final Group group2,
226                        String nameOfAtomOnGroup1, String nameOfAtomOnGroup2,
227                        double bondLengthTolerance) {
228
229                Atom[] ret = new Atom[2];
230
231                ret[0] = group1.getAtom(nameOfAtomOnGroup1);
232                ret[1] = group2.getAtom(nameOfAtomOnGroup2);
233
234                if (ret[0]==null || ret[1]==null) {
235                        return null;
236                }
237
238
239                Atom a1 = ret[0];
240                Atom a2 = ret[1];
241
242                boolean hasBond =  a1.hasBond(a2);
243
244                if ( hasBond ) {
245
246                        return ret;
247                }
248
249                // is it a metal ?
250
251                if ( a1.getElement().isMetal() || a2.getElement().isMetal()){
252
253                        MetalBondDistance defined = getMetalDistanceCutoff(a1.getElement().name(),a2.getElement().name());
254
255                        if ( defined != null) {
256
257                                if (hasMetalBond(a1, a2, defined))
258                                        return ret;
259                                else
260                                        return null;
261                        }
262
263                }
264
265                        // not a metal
266
267                        double distance = Calc.getDistance(a1, a2);
268
269                        float radiusOfAtom1 = ret[0].getElement().getCovalentRadius();
270                        float radiusOfAtom2 = ret[1].getElement().getCovalentRadius();
271
272                        if (Math.abs(distance - radiusOfAtom1 - radiusOfAtom2)
273                                        > bondLengthTolerance) {
274                                return null;
275                        }
276
277
278                return ret;
279        }
280
281        private static boolean hasMetalBond(Atom a1, Atom a2, MetalBondDistance definition) {
282
283                double distance = Calc.getDistance(a1,a2);
284
285                Float min = definition.getLowerLimit();
286                Float max = definition.getUpperLimit();
287
288                return ( min < distance && max > distance);
289
290        }
291
292        private static MetalBondDistance getMetalDistanceCutoff(String name1, String name2) {
293                Map<String,List<MetalBondDistance>> defs= MetalBondParser.getMetalBondDefinitions();
294
295                List<MetalBondDistance> distances = defs.get(name1);
296
297                if ( distances == null){
298
299                        distances = defs.get(name2);
300                        String tmp = name1;
301                         name2 = name1;
302                         name1 = tmp;
303                }
304                if ( distances == null){
305                        return null;
306                }
307
308                for  ( MetalBondDistance d : distances){
309                        if ( name1.equalsIgnoreCase(d.getAtomType1()) && name2.equalsIgnoreCase(d.getAtomType2()) )
310                                return d;
311                }
312
313                // no matching atom definitions found
314                return null;
315
316        }
317
318        /**
319         *
320         * @param group a {@link Group}.
321         * @return all atom names of the group.
322         */
323        public static List<String> getAtomNames(Group group) {
324                List<Atom> atoms = group.getAtoms();
325                if (atoms == null) {
326                        return Collections.emptyList();
327                }
328
329                int n = atoms.size();
330                List<String> ret = new ArrayList<String>(n);
331                for (int i=0; i<n; i++) {
332                        ret.add(atoms.get(i).getName());
333                }
334
335                return ret;
336        }
337
338        /**
339         * Get all amino acids in a chain.
340         * @param chain
341         * @return
342         */
343        public static List<Group> getAminoAcids(Chain chain) {
344
345                List<Group> gs = new ArrayList<>();
346
347                for ( Group g : chain.getAtomGroups()){
348
349                        if ( g.isAminoAcid())
350                                gs.add(g);
351
352                }
353
354                return gs;
355
356        }
357
358}