package org.biojava.nbio.structure.io.mmtf;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.biojava.nbio.structure.Atom;
import org.biojava.nbio.structure.Bond;
import org.biojava.nbio.structure.Chain;
import org.biojava.nbio.structure.Group;
import org.biojava.nbio.structure.Structure;
import org.biojava.nbio.structure.StructureException;
import org.biojava.nbio.structure.StructureIO;
import org.biojava.nbio.structure.align.util.AtomCache;
import org.biojava.nbio.structure.io.mmcif.ChemCompGroupFactory;
import org.biojava.nbio.structure.io.mmcif.DownloadChemCompProvider;
import org.junit.Test;
import org.rcsb.mmtf.decoder.StructureDataToAdapter;
import org.rcsb.mmtf.encoder.AdapterToStructureData;
/**
* Tests to see if roundtripping of MMTF can be done.
* @author Anthony Bradley
*
*/
public class TestMmtfRoundTrip {
/**
* Test that we can round trip a simple structure.
* @throws IOException an error reading the file
* @throws StructureException an error parsing the structure
*/
@Test
public void testRoundTrip() throws IOException, StructureException {
AtomCache cache = new AtomCache();
cache.setUseMmCif(true);
ChemCompGroupFactory.setChemCompProvider(new DownloadChemCompProvider());
StructureIO.setAtomCache(cache);
Structure structure = StructureIO.getStructure("4CUP");
AdapterToStructureData writerToEncoder = new AdapterToStructureData();
new MmtfStructureWriter(structure, writerToEncoder);
MmtfStructureReader mmtfStructureReader = new MmtfStructureReader();
new StructureDataToAdapter(writerToEncoder, mmtfStructureReader);
assertTrue(checkIfAtomsSame(structure,mmtfStructureReader.getStructure()));
}
/**
* Broad test of atom similarity
* @param structOne the first input structure
* @param structTwo the second input structure
* @param mmtfParams
* @return
*/
private boolean checkIfAtomsSame(Structure structOne, Structure structTwo) {
int numModels = structOne.nrModels();
if(numModels!=structTwo.nrModels()){
System.out.println("Error - diff number models: "+structOne.getPDBCode());
return false;
}
for(int i=0;i<numModels;i++){
List<Chain> chainsOne = structOne.getChains(i);
List<Chain> chainsTwo = structTwo.getChains(i);
if(chainsOne.size()!=chainsTwo.size()){
System.out.println("Error - diff number chains: "+structOne.getPDBCode());
return false;
}
// Now make sure they're sorted in the right order
sortChains(chainsOne, chainsTwo);
// Check that each one has the same number of poly, non-poly and water chains
checkDiffChains(structOne, structTwo, i);
// Now loop over
for(int j=0; j<chainsOne.size();j++){
Chain chainOne = chainsOne.get(j);
Chain chainTwo = chainsTwo.get(j);
// Check they have the same chain id
assertEquals(chainOne.getId(), chainTwo.getId());
checkSeqresGroups(chainOne, chainTwo);
List<Group> groupsOne = chainOne.getAtomGroups();
List<Group> groupsTwo = chainTwo.getAtomGroups();
if(groupsOne.size()!=groupsTwo.size()){
System.out.println("Error - diff number groups: "+structOne.getPDBCode());
System.out.println(chainOne.getId()+":"+groupsOne.size()+" "+groupsTwo.size());
return false;
}
for(int k=0; k<groupsOne.size();k++){
Group groupOne = groupsOne.get(k);
Group groupTwo = groupsTwo.get(k);
// Check if the groups are of the same type
if(!groupOne.getType().equals(groupTwo.getType())){
System.out.println("Error - diff group type: "+structOne.getPDBCode());
System.out.println(groupOne.getPDBName() + " and type: "+groupOne.getType());
System.out.println(groupTwo.getPDBName() + " and type: "+groupTwo.getType());;
}
// Check the single letter amino acid is correct
if(groupOne.getChemComp().getOne_letter_code().length()==1 && groupTwo.getChemComp().getOne_letter_code().length()==1){
if(!groupOne.getChemComp().getOne_letter_code().equals(groupTwo.getChemComp().getOne_letter_code())){
System.out.println(groupOne.getPDBName());
}
assertEquals(groupOne.getChemComp().getOne_letter_code(), groupTwo.getChemComp().getOne_letter_code());
}
assertEquals(groupOne.getType(), groupTwo.getType());
assertEquals(groupOne.getPDBName(), groupTwo.getPDBName());
assertEquals(groupOne.getResidueNumber().getSeqNum(), groupTwo.getResidueNumber().getSeqNum());
assertEquals(groupOne.getResidueNumber().getInsCode(), groupTwo.getResidueNumber().getInsCode());
assertEquals(groupOne.getResidueNumber().getChainName(), groupTwo.getResidueNumber().getChainName());
if(groupTwo.getAltLocs().size()!=groupOne.getAltLocs().size()){
System.out.println("Error - diff number alt locs: "+structOne.getPDBCode()+" "+groupOne.getPDBName()+" "+groupOne.getResidueNumber().getSeqNum());
System.out.println(groupOne.getAltLocs().size());
System.out.println(groupTwo.getAltLocs().size());
}
// Get the first conf
List<Atom> atomsOne = new ArrayList<>(groupOne.getAtoms());
List<Atom> atomsTwo = new ArrayList<>(groupTwo.getAtoms());
for(Group altLocOne: groupOne.getAltLocs()){
for(Atom atomAltLocOne: altLocOne.getAtoms()){
atomsOne.add(atomAltLocOne);
}
}
for(Group altLocTwo: groupTwo.getAltLocs()){
for(Atom atomAltLocTwo: altLocTwo.getAtoms()){
atomsTwo.add(atomAltLocTwo);
}
}
if(atomsOne.size()!=atomsTwo.size()){
System.out.println("Error - diff number atoms: "+structOne.getPDBCode());
System.out.println(groupOne.getResidueNumber());
System.out.println(groupOne.getPDBName()+" vs "+groupTwo.getPDBName());
System.out.println(atomsOne.size()+" vs "+atomsTwo.size());
return false;
}
// Now sort the atoms
sortAtoms(atomsOne, atomsTwo);
// Now loop through the atoms
for(int l=0;l<atomsOne.size();l++){
Atom atomOne = atomsOne.get(l);
Atom atomTwo = atomsTwo.get(l);
assertEquals(atomOne.getGroup().getPDBName(), atomTwo.getGroup().getPDBName());
assertEquals(atomOne.getCharge(), atomTwo.getCharge());
// Check the coords are the same to three db
assertArrayEquals(atomOne.getCoords(), atomTwo.getCoords(), 0.0009999999);
assertEquals(atomOne.getTempFactor(), atomTwo.getTempFactor(), 0.009999999);
assertEquals(atomOne.getOccupancy(), atomTwo.getOccupancy(), 0.009999999);
assertEquals(atomOne.getElement(), atomTwo.getElement());
assertEquals(atomOne.getName(),atomTwo.getName());
assertEquals(atomOne.getAltLoc(), atomTwo.getAltLoc());
if(i==0){
if(atomOne.getBonds()==null){
if(atomTwo.getBonds()!=null){
System.out.println("Null bonds in one and not the other");
return false;
}
}
else if(atomTwo.getBonds()==null){
System.out.println("Null bonds in one and not the other");
return false;
}
else if(atomOne.getBonds().size()!=atomTwo.getBonds().size()){
System.out.println("Error different number of bonds: "+structOne.getPDBCode());
System.out.println(atomOne.getBonds().size()+" vs. "+atomTwo.getBonds().size());
System.out.println(atomOne);
System.out.println(atomTwo);
for(Bond bond : atomOne.getBonds()) {
System.out.println(bond);
}
for(Bond bond : atomTwo.getBonds()) {
System.out.println(bond);
}
return false;
}
}
}
}
}
}
return true;
}
/**
* Check both structures have the same number of poly,non-poly and water chains
* @param structOne the first structure
* @param structTwo the second structure
* @param i the model index
*/
private void checkDiffChains(Structure structOne, Structure structTwo, int i) {
assertEquals(structOne.getPolyChains(i).size(), structTwo.getPolyChains(i).size());
assertEquals(structOne.getNonPolyChains(i).size(), structTwo.getNonPolyChains(i).size());
assertEquals(structOne.getWaterChains(i).size(), structTwo.getWaterChains(i).size());
}
/**
* Sort the atom based on PDB serial id
* @param atomsOne the first list
* @param atomsTwo the second list
*/
private void sortAtoms(List<Atom> atomsOne, List<Atom> atomsTwo) {
atomsOne.sort(new Comparator<Atom>() {
@Override
public int compare(Atom o1, Atom o2) {
//
if (o1.getPDBserial()<o2.getPDBserial()){
return -1;
}
else{
return 1;
}
}
});
atomsTwo.sort(new Comparator<Atom>() {
@Override
public int compare(Atom o1, Atom o2) {
//
if (o1.getPDBserial()<o2.getPDBserial()){
return -1;
}
else{
return 1;
}
}
});
}
/**
* Sort the chains based on chain id.
* @param chainsOne the first list of chains
* @param chainsTwo the second list of chains
*/
private void sortChains(List<Chain> chainsOne, List<Chain> chainsTwo) {
Collections.sort(chainsOne, new Comparator<Chain>() {
@Override
public int compare(Chain o1, Chain o2) {
return o1.getId().compareTo(o2.getId());
}
});
Collections.sort(chainsTwo, new Comparator<Chain>() {
@Override
public int compare(Chain o1, Chain o2) {
return o1.getId().compareTo(o2.getId());
}
});
}
private void checkSeqresGroups(Chain chainOne, Chain chainTwo) {
assertEquals(chainOne.getSeqResGroups().size(), chainTwo.getSeqResGroups().size());
for (int i=0; i<chainOne.getSeqResGroups().size(); i++) {
Group gOne = chainOne.getSeqResGroup(i);
Group gTwo = chainTwo.getSeqResGroup(i);
assertNotNull(gOne.getChemComp());
assertNotNull(gTwo.getChemComp());
assertEquals(gOne.getChemComp().getOne_letter_code(), gTwo.getChemComp().getOne_letter_code());
assertEquals(gOne.getResidueNumber(), gTwo.getResidueNumber());
//assertEquals(gOne.getPDBName(), gTwo.getPDBName());
}
// this is to test issue #517: a null pointer happens if the group hasn't been cloned (including the chem comp associated to it)
Chain clonedChain = (Chain)chainTwo.clone();
assertEquals(chainTwo.getSeqResGroups().size(), clonedChain.getSeqResGroups().size());
for (int i=0; i<clonedChain.getSeqResGroups().size(); i++) {
Group g = clonedChain.getSeqResGroup(i);
assertNotNull(g.getChemComp());
}
}
}