Skip to content

Commit 19f64ca

Browse files
authored
Merge pull request #513 from abradle/master
MMTF roundtripping in Biojava
2 parents 0154b0b + 18ff76e commit 19f64ca

File tree

5 files changed

+263
-8
lines changed

5 files changed

+263
-8
lines changed

biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmtf/MmtfStructureReader.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ public void finalizeStructure() {
129129
structure.addChain(modelChain, i);
130130
String sequence = chainSequenceMap.get(modelChain.getId());
131131
MmtfUtils.addSeqRes(modelChain, sequence);
132-
133132
}
134133
}
135134
StructureTools.cleanUpAltLocs(structure);
@@ -199,11 +198,12 @@ public void setGroupInfo(String groupName, int groupNumber,
199198
break;
200199
default:
201200
group = new HetatomImpl();
201+
break;
202202
}
203203
atomsInGroup = new ArrayList<Atom>();
204-
// Set the CC -> empty but not null
205204
ChemComp chemComp = new ChemComp();
206205
chemComp.setOne_letter_code(String.valueOf(singleLetterCode));
206+
chemComp.setType(chemCompType.toUpperCase());
207207
ResidueType residueType = ResidueType.getResidueTypeFromString(chemCompType);
208208
chemComp.setResidueType(residueType);
209209
chemComp.setPolymerType(residueType.polymerType);
@@ -216,7 +216,7 @@ public void setGroupInfo(String groupName, int groupNumber,
216216
groupNumber, insertionCode);
217217
}
218218
group.setAtoms(new ArrayList<Atom>(atomCount));
219-
if (polymerType != 0) {
219+
if (polymerType==1 || polymerType==2) {
220220
MmtfUtils.insertSeqResGroup(chain, group, sequenceIndexId);
221221
}
222222
if (atomCount > 0) {
@@ -257,6 +257,9 @@ public void setAtomInfo(String atomName,
257257
atom.setPDBserial(serialNumber);
258258
atom.setName(atomName.trim());
259259
atom.setElement(Element.valueOfIgnoreCase(element));
260+
if(alternativeLocationId==MmtfStructure.UNAVAILABLE_CHAR_VALUE){
261+
alternativeLocationId = ' ';
262+
}
260263
if (alternativeLocationId != ' ') {
261264
// Get the altGroup
262265
altGroup = getCorrectAltLocGroup(alternativeLocationId);

biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmtf/MmtfStructureWriter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public MmtfStructureWriter(Structure structure, StructureAdapterInterface dataTr
8282
singleLetterCode = chemComp.getOne_letter_code().charAt(0);
8383
}
8484
mmtfDecoderInterface.setGroupInfo(group.getPDBName(), group.getResidueNumber().getSeqNum(), insCode.charValue(),
85-
chemComp.getType(), atomsInGroup.size(), MmtfUtils.getNumBondsInGroup(atomsInGroup), singleLetterCode,
85+
chemComp.getType().toUpperCase(), atomsInGroup.size(), MmtfUtils.getNumBondsInGroup(atomsInGroup), singleLetterCode,
8686
sequenceGroups.indexOf(group), MmtfUtils.getSecStructType(group));
8787
for (Atom atom : atomsInGroup){
8888
char altLoc = MmtfStructure.UNAVAILABLE_CHAR_VALUE;

biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmtf/MmtfUtils.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.biojava.nbio.structure.io.FileParsingParameters;
3232
import org.biojava.nbio.structure.io.mmcif.ChemCompGroupFactory;
3333
import org.biojava.nbio.structure.io.mmcif.DownloadChemCompProvider;
34+
import org.biojava.nbio.structure.io.mmcif.model.ChemComp;
3435
import org.biojava.nbio.structure.quaternary.BioAssemblyInfo;
3536
import org.biojava.nbio.structure.quaternary.BiologicalAssemblyTransformation;
3637
import org.biojava.nbio.structure.secstruc.DSSPParser;
@@ -478,7 +479,7 @@ public static void addSeqRes(Chain modelChain, String sequence) {
478479
List<Group> seqResGroups = modelChain.getSeqResGroups();
479480
GroupType chainType = getChainType(modelChain.getAtomGroups());
480481
for(int i=0; i<sequence.length(); i++){
481-
char aa = sequence.charAt(i);
482+
char singleLetterCode = sequence.charAt(i);
482483
Group group;
483484
if(seqResGroups.size()<=i){
484485
group=null;
@@ -490,7 +491,7 @@ public static void addSeqRes(Chain modelChain, String sequence) {
490491
group=null;
491492
continue;
492493
}
493-
group = getSeqResGroup(modelChain, aa, chainType);
494+
group = getSeqResGroup(modelChain, singleLetterCode, chainType);
494495
addGroupAtId(seqResGroups, group, i);
495496
seqResGroups.set(i, group);
496497
group=null;
@@ -518,15 +519,21 @@ private static <T> void addGroupAtId(List<T> seqResGroups, T group, int sequence
518519
}
519520
}
520521

521-
private static Group getSeqResGroup(Chain modelChain, char aa, GroupType type) {
522+
private static Group getSeqResGroup(Chain modelChain, char singleLetterCode, GroupType type) {
522523
if(type==GroupType.AMINOACID){
523524
AminoAcidImpl a = new AminoAcidImpl();
524525
a.setRecordType(AminoAcid.SEQRESRECORD);
525-
a.setAminoType(aa);
526+
a.setAminoType(singleLetterCode);
527+
ChemComp chemComp = new ChemComp();
528+
chemComp.setOne_letter_code(""+singleLetterCode);
529+
a.setChemComp(chemComp);
526530
return a;
527531

528532
} else if (type==GroupType.NUCLEOTIDE) {
529533
NucleotideImpl n = new NucleotideImpl();
534+
ChemComp chemComp = new ChemComp();
535+
chemComp.setOne_letter_code(""+singleLetterCode);
536+
n.setChemComp(chemComp);
530537
return n;
531538
}
532539
else{

biojava-structure/src/test/java/org/biojava/nbio/structure/io/mmtf/TestBasicMmtf.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.biojava.nbio.structure.ResidueNumber;
2020
import org.biojava.nbio.structure.Structure;
2121
import org.biojava.nbio.structure.StructureImpl;
22+
import org.biojava.nbio.structure.io.mmcif.model.ChemComp;
2223
import org.junit.Rule;
2324
import org.junit.Test;
2425
import org.junit.rules.TemporaryFolder;
@@ -65,6 +66,10 @@ public void testWrite() throws IOException {
6566
chain.setName("A");
6667
Group group = new AminoAcidImpl();
6768
group.setPDBName("FKF");
69+
ChemComp chemComp = new ChemComp();
70+
chemComp.setType("TYPfdl");
71+
chemComp.setOne_letter_code("A");
72+
group.setChemComp(chemComp);
6873
Atom atom = new AtomImpl();
6974
atom.setName("A");
7075
atom.setElement(Element.Ag);
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
package org.biojava.nbio.structure.io.mmtf;
2+
3+
import static org.junit.Assert.assertArrayEquals;
4+
import static org.junit.Assert.assertEquals;
5+
import static org.junit.Assert.assertTrue;
6+
7+
import java.io.IOException;
8+
import java.util.ArrayList;
9+
import java.util.Collections;
10+
import java.util.Comparator;
11+
import java.util.List;
12+
13+
import org.biojava.nbio.structure.Atom;
14+
import org.biojava.nbio.structure.Bond;
15+
import org.biojava.nbio.structure.Chain;
16+
import org.biojava.nbio.structure.Group;
17+
import org.biojava.nbio.structure.Structure;
18+
import org.biojava.nbio.structure.StructureException;
19+
import org.biojava.nbio.structure.StructureIO;
20+
import org.junit.Test;
21+
import org.rcsb.mmtf.decoder.StructureDataToAdapter;
22+
import org.rcsb.mmtf.encoder.AdapterToStructureData;
23+
24+
/**
25+
* Tests to see if roundtripping of MMTF can be done.
26+
* @author Anthony Bradley
27+
*
28+
*/
29+
public class TestMmtfRoundTrip {
30+
31+
/**
32+
* Test that we can round trip a simple structure.
33+
* @throws IOException an error reading the file
34+
* @throws StructureException an error parsing the structure
35+
*/
36+
@Test
37+
public void testRoundTrip() throws IOException, StructureException {
38+
Structure structure = StructureIO.getStructure("4CUP");
39+
AdapterToStructureData writerToEncoder = new AdapterToStructureData();
40+
new MmtfStructureWriter(structure, writerToEncoder);
41+
MmtfStructureReader mmtfStructureReader = new MmtfStructureReader();
42+
new StructureDataToAdapter(writerToEncoder, mmtfStructureReader);
43+
assertTrue(checkIfAtomsSame(structure,mmtfStructureReader.getStructure()));
44+
}
45+
46+
/**
47+
* Broad test of atom similarity
48+
* @param structOne the first input structure
49+
* @param structTwo the second input structure
50+
* @param mmtfParams
51+
* @return
52+
*/
53+
private boolean checkIfAtomsSame(Structure structOne, Structure structTwo) {
54+
int numModels = structOne.nrModels();
55+
if(numModels!=structTwo.nrModels()){
56+
System.out.println("Error - diff number models: "+structOne.getPDBCode());
57+
return false;
58+
}
59+
for(int i=0;i<numModels;i++){
60+
List<Chain> chainsOne = structOne.getChains(i);
61+
List<Chain> chainsTwo = structTwo.getChains(i);
62+
if(chainsOne.size()!=chainsTwo.size()){
63+
System.out.println("Error - diff number chains: "+structOne.getPDBCode());
64+
return false;
65+
}
66+
// Now make sure they're sorted in the right order
67+
sortChains(chainsOne, chainsTwo);
68+
// Check that each one has the same number of poly, non-poly and water chains
69+
checkDiffChains(structOne, structTwo, i);
70+
// Now loop over
71+
for(int j=0; j<chainsOne.size();j++){
72+
Chain chainOne = chainsOne.get(j);
73+
Chain chainTwo = chainsTwo.get(j);
74+
// Check they have the same chain id
75+
assertEquals(chainOne.getId(), chainTwo.getId());
76+
List<Group> groupsOne = chainOne.getAtomGroups();
77+
List<Group> groupsTwo = chainTwo.getAtomGroups();
78+
if(groupsOne.size()!=groupsTwo.size()){
79+
System.out.println("Error - diff number groups: "+structOne.getPDBCode());
80+
System.out.println(chainOne.getId()+":"+groupsOne.size()+" "+groupsTwo.size());
81+
return false;
82+
}
83+
for(int k=0; k<groupsOne.size();k++){
84+
Group groupOne = groupsOne.get(k);
85+
Group groupTwo = groupsTwo.get(k);
86+
// Check if the groups are of the same type
87+
if(groupOne.getType().equals(groupTwo.getType())==false){
88+
System.out.println("Error - diff group type: "+structOne.getPDBCode());
89+
System.out.println(groupOne.getPDBName() + " and type: "+groupOne.getType());
90+
System.out.println(groupTwo.getPDBName() + " and type: "+groupTwo.getType());;
91+
}
92+
// Check the single letter amino aicd is correct
93+
if(groupOne.getChemComp().getOne_letter_code().length()==1 && groupTwo.getChemComp().getOne_letter_code().length()==1){
94+
if(!groupOne.getChemComp().getOne_letter_code().equals(groupTwo.getChemComp().getOne_letter_code())){
95+
System.out.println(groupOne.getPDBName());
96+
}
97+
assertEquals(groupOne.getChemComp().getOne_letter_code(), groupTwo.getChemComp().getOne_letter_code());
98+
}
99+
assertEquals(groupOne.getType(), groupTwo.getType());
100+
assertEquals(groupOne.getResidueNumber().getSeqNum(), groupTwo.getResidueNumber().getSeqNum());
101+
assertEquals(groupOne.getResidueNumber().getInsCode(), groupTwo.getResidueNumber().getInsCode());
102+
assertEquals(groupOne.getResidueNumber().getChainName(), groupTwo.getResidueNumber().getChainName());
103+
if(groupTwo.getAltLocs().size()!=groupOne.getAltLocs().size()){
104+
System.out.println("Error - diff number alt locs: "+structOne.getPDBCode()+" "+groupOne.getPDBName()+" "+groupOne.getResidueNumber().getSeqNum());
105+
System.out.println(groupOne.getAltLocs().size());
106+
System.out.println(groupTwo.getAltLocs().size());
107+
108+
}
109+
// Get the first conf
110+
List<Atom> atomsOne = new ArrayList<>(groupOne.getAtoms());
111+
List<Atom> atomsTwo = new ArrayList<>(groupTwo.getAtoms());
112+
113+
for(Group altLocOne: groupOne.getAltLocs()){
114+
for(Atom atomAltLocOne: altLocOne.getAtoms()){
115+
atomsOne.add(atomAltLocOne);
116+
}
117+
}
118+
for(Group altLocTwo: groupTwo.getAltLocs()){
119+
for(Atom atomAltLocTwo: altLocTwo.getAtoms()){
120+
atomsTwo.add(atomAltLocTwo);
121+
}
122+
}
123+
if(atomsOne.size()!=atomsTwo.size()){
124+
System.out.println("Error - diff number atoms: "+structOne.getPDBCode());
125+
System.out.println(groupOne.getResidueNumber());
126+
System.out.println(groupOne.getPDBName()+" vs "+groupTwo.getPDBName());
127+
System.out.println(atomsOne.size()+" vs "+atomsTwo.size());
128+
return false;
129+
}
130+
// Now sort the atoms
131+
sortAtoms(atomsOne, atomsTwo);
132+
// Now loop through the atoms
133+
for(int l=0;l<atomsOne.size();l++){
134+
Atom atomOne = atomsOne.get(l);
135+
Atom atomTwo = atomsTwo.get(l);
136+
assertTrue(atomOne.getGroup().getPDBName().equals(atomTwo.getGroup().getPDBName()));
137+
assertTrue(atomOne.getCharge()==atomTwo.getCharge());
138+
// Check the coords are the same to three db
139+
assertArrayEquals(atomOne.getCoords(), atomTwo.getCoords(), 0.0009999999);
140+
assertEquals(atomOne.getTempFactor(), atomTwo.getTempFactor(), 0.009999999);
141+
assertEquals(atomOne.getOccupancy(), atomTwo.getOccupancy(), 0.009999999);
142+
assertTrue(atomOne.getElement()==atomTwo.getElement());
143+
assertTrue(atomOne.getName().equals(atomTwo.getName()));
144+
assertTrue(atomOne.getAltLoc()==atomTwo.getAltLoc());
145+
if(i==0){
146+
if(atomOne.getBonds()==null){
147+
if(atomTwo.getBonds()!=null){
148+
System.out.println("Null bonds in one and not the other");
149+
return false;
150+
}
151+
}
152+
else if(atomTwo.getBonds()==null){
153+
System.out.println("Null bonds in one and not the other");
154+
return false;
155+
}
156+
else if(atomOne.getBonds().size()!=atomTwo.getBonds().size()){
157+
System.out.println("Error different number of bonds: "+structOne.getPDBCode());
158+
System.out.println(atomOne.getBonds().size()+" vs. "+atomTwo.getBonds().size());
159+
System.out.println(atomOne);
160+
System.out.println(atomTwo);
161+
for(Bond bond : atomOne.getBonds()) {
162+
System.out.println(bond);
163+
}
164+
for(Bond bond : atomTwo.getBonds()) {
165+
System.out.println(bond);
166+
}
167+
return false;
168+
}
169+
}
170+
}
171+
}
172+
}
173+
}
174+
return true;
175+
}
176+
/**
177+
* Check both structures have the same number of poly,non-poly and water chains
178+
* @param structOne the first structure
179+
* @param structTwo the second structure
180+
* @param i the model index
181+
*/
182+
private void checkDiffChains(Structure structOne, Structure structTwo, int i) {
183+
assertEquals(structOne.getPolyChains(i).size(), structTwo.getPolyChains(i).size());
184+
assertEquals(structOne.getNonPolyChains(i).size(), structTwo.getNonPolyChains(i).size());
185+
assertEquals(structOne.getWaterChains(i).size(), structTwo.getWaterChains(i).size());
186+
}
187+
/**
188+
* Sort the atom based on PDB serial id
189+
* @param atomsOne the first list
190+
* @param atomsTwo the second list
191+
*/
192+
private void sortAtoms(List<Atom> atomsOne, List<Atom> atomsTwo) {
193+
atomsOne.sort(new Comparator<Atom>() {
194+
@Override
195+
public int compare(Atom o1, Atom o2) {
196+
//
197+
if (o1.getPDBserial()<o2.getPDBserial()){
198+
return -1;
199+
}
200+
else{
201+
return 1;
202+
}
203+
}
204+
});
205+
atomsTwo.sort(new Comparator<Atom>() {
206+
@Override
207+
public int compare(Atom o1, Atom o2) {
208+
//
209+
if (o1.getPDBserial()<o2.getPDBserial()){
210+
return -1;
211+
}
212+
else{
213+
return 1;
214+
}
215+
}
216+
});
217+
}
218+
219+
/**
220+
* Sort the chains based on chain id.
221+
* @param chainsOne the first list of chains
222+
* @param chainsTwo the second list of chains
223+
*/
224+
private void sortChains(List<Chain> chainsOne, List<Chain> chainsTwo) {
225+
Collections.sort(chainsOne, new Comparator<Chain>() {
226+
@Override
227+
public int compare(Chain o1, Chain o2) {
228+
return o1.getId().compareTo(o2.getId());
229+
}
230+
});
231+
Collections.sort(chainsTwo, new Comparator<Chain>() {
232+
@Override
233+
public int compare(Chain o1, Chain o2) {
234+
return o1.getId().compareTo(o2.getId());
235+
}
236+
});
237+
238+
}
239+
240+
}

0 commit comments

Comments
 (0)