-
Notifications
You must be signed in to change notification settings - Fork 0
/
Peptide.java
355 lines (329 loc) · 13.3 KB
/
Peptide.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
import java.util.*;
import java.io.*;
import com.google.common.collect.*;
import org.apache.commons.math3.geometry.euclidean.threed.*;
import org.jgrapht.*;
import org.jgrapht.graph.*;
import org.jgrapht.alg.*;
import java.util.concurrent.*;
/**
* This class represents a peptide. It is immutable.
*/
public class Peptide extends Molecule implements Immutable, Serializable, Comparable<Peptide>
{
/** For serialization. */
public static final long serialVersionUID = 1L;
/** The residues, from the N-terminus to the C-terminus. */
public final List<Residue> sequence;
/** The energy broken down by residue. */
public final EnergyBreakdown energyBreakdown;
/** Creates a Peptide. */
public Peptide(String name, List<Atom> contents, SimpleWeightedGraph<Atom,DefaultWeightedEdge> connectivity,
List<Residue> sequence, EnergyBreakdown energyBreakdown)
{
super(name,contents,connectivity);
this.sequence = ImmutableList.copyOf(sequence);
this.energyBreakdown = energyBreakdown;
}
/**
* Factory method to create a peptide given a map of old atoms to new atoms. Should be used
* to move atoms.
* @param atomMap a map from old atoms to new atoms (does not have to include all atoms)
* @return the new Peptide
*/
public Peptide moveAtoms2(Map<Atom,Atom> atomMap)
{
Molecule oldMolecule = (Molecule)this;
Molecule newMolecule = oldMolecule.moveAtoms(atomMap);
// create new sequence/residues
List<Residue> newSequence = new ArrayList<>(sequence.size());
for (Residue r : sequence)
newSequence.add(r.moveAtoms(atomMap));
newSequence = ImmutableList.copyOf(newSequence);
// return result
// assumes the new peptide has the same name and won't have an EnergyBreakdown yet
return new Peptide(newMolecule.name, newMolecule.contents, newMolecule.connectivity, newSequence, EnergyBreakdown.BLANK);
}
/**
* Returns a new Peptide that has the same fields as this one, except for the name.
* @param newName the new name
* @return the new Peptide
*/
public Peptide setName(String newName)
{
return new Peptide(newName, contents, connectivity, sequence, energyBreakdown);
}
/**
* Returns a new Peptide that has the same fields as this one, but with a different EnergyBreakdown.
* @param newEnergyBreakdown the new energy breakdown to use
* @return the new Peptide
*/
public Peptide setEnergyBreakdown(EnergyBreakdown newEnergyBreakdown)
{
//if ( energyBreakdown != null && energyBreakdown != EnergyBreakdown.BLANK )
// System.out.println("warning: set energy breakdown when not null");
String newName = name.split("@")[0] + String.format(" @ %.2f kcal", newEnergyBreakdown.totalEnergy);
return new Peptide(newName, contents, connectivity, sequence, newEnergyBreakdown);
}
/**
* Changes the geometry of this Peptide.
* @param newMolecule the new molecule geometry to use
* @return the new Peptide
*/
public Peptide setMolecule(Molecule newMolecule)
{
if ( contents.size() != newMolecule.contents.size() )
throw new IllegalArgumentException("contents size mismatch");
Molecule oldMolecule = (Molecule)this;
Map<Atom,Atom> atomMap = getAtomMap(oldMolecule,newMolecule);
return moveAtoms2(atomMap);
}
/**
* Changes the geometry of this Peptide.
* @param newMolecule the new molecule geometry to use
* @return the new Peptide
*/
public Peptide setPositions(Molecule newMolecule)
{
if ( contents.size() != newMolecule.contents.size() )
throw new IllegalArgumentException("contents size mismatch");
Map<Atom,Atom> atomMap = new HashMap<>();
for (int i=0; i < contents.size(); i++)
{
Atom oldAtom = contents.get(i);
Vector3D newPosition = newMolecule.contents.get(i).position;
Atom newAtom = oldAtom.moveAtom(newPosition);
atomMap.put(oldAtom, newAtom);
}
return moveAtoms2(atomMap);
}
/**
* Changes the geometry of this Peptide.
* @param positions the new geometry to use
* @return the new Peptide
*/
public Peptide setMolecule(List<Vector3D> positions)
{
if ( contents.size() != positions.size() )
throw new IllegalArgumentException("size mismatch");
Map<Atom,Atom> atomMap = new HashMap<>();
for (int i=0; i < contents.size(); i++)
{
Atom oldAtom = contents.get(i);
Atom newAtom = oldAtom.moveAtom(positions.get(i));
atomMap.put(oldAtom,newAtom);
}
return moveAtoms2(atomMap);
}
/**
* Provides an atom map from one molecule to another. This assumes
* molecule1 and molecule2 are different conformations.
* @param molecule1 the from atoms
* @param molecule2 the to atoms
* @return a map from atoms in molecule1 to atoms in molecule2 (won't include atoms that don't move)
*/
public static Map<Atom,Atom> getAtomMap(Molecule molecule1, Molecule molecule2)
{
if ( molecule1.contents.size() != molecule2.contents.size() )
throw new IllegalArgumentException("atom list size mismatch");
Map<Atom,Atom> returnMap = new HashMap<>();
for (int i=0; i < molecule1.contents.size(); i++)
{
Atom a1 = molecule1.contents.get(i);
Atom a2 = molecule2.contents.get(i);
if ( ! a1.equals(a2) )
returnMap.put(a1,a2);
}
return returnMap;
}
/**
* Writes the specified peptides to disk in gjf format.
* @param peptides the peptides to write to disk
* @param prefix the location and prefix to send the peptides to (e.g., "test_peptides/prefix_")
* @param digits how many digits to use in the filename
* @param maxNumber the maximum number of files to write
*/
public static void writeGJFs(List<Peptide> peptides, String prefix, int digits, int maxNumber)
{
if ( peptides == null )
return;
if ( digits < 1 )
throw new IllegalArgumentException("must use at least one digit");
for (int i=0; i < Math.min(maxNumber,peptides.size()); i++)
{
Peptide peptide = peptides.get(i);
String filename = String.format("%s%0" + digits + "d.gjf", prefix, i);
GaussianInputFile f = new GaussianInputFile(peptide);
f.write(filename);
}
}
/**
* Writes the specified peptides to disk in gjf format.
* @param peptides the peptides to write to disk
* @param prefix the location and prefix to send the peptides to (e.g., "test_peptides/prefix_")
* @param digits how many digits to use in the filename
* @param digits how many digits to use in the filename
*/
public static void writeCHKs(List<Peptide> peptides, String prefix, int digits, int maxNumber)
{
if ( peptides == null )
return;
if ( digits < 1 )
throw new IllegalArgumentException("must use at least one digit");
for (int i=0; i < Math.min(maxNumber,peptides.size()); i++)
{
Peptide peptide = peptides.get(i);
String filename = String.format("%s%0" + digits + "d.chk", prefix, i);
peptide.checkpoint(filename);
}
}
/**
* Looks for pairs of backbone atoms that should be checked for clashes.
* Only backbone atoms that are more than two bonds apart are checked.
* Hairpin positions are treated as part of the backbone. All other positions
* are treated as having rotamers, even if it's just the H of glycine.
* HNs are treated as part of the backbone for this method.
* @return pairs of indices of backbone atoms that should be checked for clashes
*/
public List<Pair<Integer,Integer>> getBackbonePairs()
{
// get all rotamer atoms
int numberOfResidues = sequence.size();
Set<Atom> allRotamerAtoms = new HashSet<>();
for (Residue r : sequence)
{
if ( r.isHairpin )
continue;
allRotamerAtoms.addAll( RotamerFactory.getSidechainAtoms(this,r,false) );
}
// get backbone atoms
Set<Atom> backboneAtoms = new HashSet<>();
for (Atom a : contents)
{
if ( allRotamerAtoms.contains(a) )
continue;
backboneAtoms.add(a);
}
if ( backboneAtoms.size() == 0 )
throw new IllegalArgumentException("expected to find some backbone atoms");
// create all pairs
List<Atom> backboneAtomsList = new ArrayList<>(backboneAtoms);
int expectedSize = backboneAtomsList.size() * (backboneAtomsList.size() - 1);
List<Pair<Integer,Integer>> returnList = new ArrayList<>(expectedSize);
for (int i=0; i < backboneAtomsList.size()-1; i++)
{
Atom atomI = contents.get(i);
for (int j=i+1; j < backboneAtomsList.size(); j++)
{
Atom atomJ = contents.get(j);
if ( !areSeparated(atomI,atomJ) )
continue;
Pair<Integer,Integer> pair = new Pair<>(i,j);
returnList.add(pair);
}
}
// return result
return ImmutableList.copyOf(returnList);
}
/**
* Checks if the backbone atoms in the specified peptide clash.
* @param backbonePairs pairs of indices of backbone atoms that should be checked for clashes
* @return true if there is a clash
*/
public boolean hasBackboneClash(List<Pair<Integer,Integer>> backbonePairs)
{
for (Pair<Integer,Integer> pair : backbonePairs)
{
int i = pair.getFirst();
int j = pair.getSecond();
Atom atomI = contents.get(i);
Atom atomJ = contents.get(j);
if ( RotamerSpace.clashes(atomI,atomJ) )
return true;
}
return false;
}
/**
* Writes the peptide to disk. Will not die from exceptions.
* @param filename the filename to write to
*/
public void checkpoint(String filename)
{
if ( filename == null || filename.length() == 0 )
throw new NullPointerException("must specify a filename");
try
{
FileOutputStream fileOut = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(this);
out.close();
fileOut.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* Loads a peptide checkpoint from disk. WIll not die from exceptions.
* @param filename the filename to write to
* @return the deserialized peptide
*/
public static Peptide load(String filename)
{
if ( filename == null || filename.length() == 0 )
throw new NullPointerException("must specify a filename");
try
{
FileInputStream fileIn = new FileInputStream(filename);
ObjectInputStream in = new ObjectInputStream(fileIn);
Peptide p = (Peptide)in.readObject();
in.close();
fileIn.close();
return p;
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
/**
* Compares peptides on the basis of their total energy in
* energy breakdown (allows for sorting lists in ascending order)
* @param p2 the peptide to compare this peptide to
*/
@Override
public int compareTo(Peptide p2)
{
if ( this.energyBreakdown == null || p2.energyBreakdown == null ||
this.energyBreakdown == EnergyBreakdown.BLANK || p2.energyBreakdown == EnergyBreakdown.BLANK )
throw new NullPointerException("null or blank energy breakdown -- cannot compare");
if ( this.energyBreakdown.type != p2.energyBreakdown.type )
throw new IllegalArgumentException("energy type mismatch");
return energyBreakdown.totalEnergy > p2.energyBreakdown.totalEnergy ? 1 : (energyBreakdown.totalEnergy < p2.energyBreakdown.totalEnergy ? -1 : 0);
}
@Override
public int hashCode()
{
return Objects.hash(name, contents, connectivity, sequence, energyBreakdown);
}
@Override
public boolean equals(Object obj)
{
if ( obj == null )
return false;
if ( obj == this )
return true;
if ( !(obj instanceof Peptide) )
return false;
Peptide p = (Peptide)obj;
if ( name.equals(p.name) &&
contents.equals(p.contents) &&
connectivity.equals(p.connectivity) &&
sequence.equals(p.sequence) &&
Objects.equals(energyBreakdown, p.energyBreakdown) )
return true;
return false;
}
}