package org.biojava.nbio.structure.io.mmtf;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.biojava.nbio.structure.Atom;
import org.biojava.nbio.structure.Bond;
import org.biojava.nbio.structure.Chain;
import org.biojava.nbio.structure.ChainImpl;
import org.biojava.nbio.structure.EntityInfo;
import org.biojava.nbio.structure.Group;
import org.biojava.nbio.structure.PDBCrystallographicInfo;
import org.biojava.nbio.structure.PDBHeader;
import org.biojava.nbio.structure.Structure;
import org.biojava.nbio.structure.io.mmcif.model.ChemComp;
import org.biojava.nbio.structure.quaternary.BioAssemblyInfo;
import org.rcsb.mmtf.api.StructureAdapterInterface;
import org.rcsb.mmtf.dataholders.MmtfStructure;
/**
* Class to take Biojava structure data and covert to the DataApi for encoding.
* Must implement all the functions in {@link StructureAdapterInterface}.
* @author Anthony Bradley
*
*/
public class MmtfStructureWriter {
private StructureAdapterInterface mmtfDecoderInterface;
/**
* Pass data from Biojava structure to another generic output type. Loops through the data
* structure and calls all the set functions.
* @param structure the input {@link Structure} to write
* @param dataTransferInterface the generic interface that
* implements all the set methods.
*/
public MmtfStructureWriter(Structure structure, StructureAdapterInterface dataTransferInterface) {
this.mmtfDecoderInterface = dataTransferInterface;
// Reset structure to consider altloc groups with the same residue number but different group names as seperate groups
MmtfUtils.fixMicroheterogenity(structure);
// Get the chain name to index map
MmtfSummaryDataBean mmtfSummaryDataBean = MmtfUtils.getStructureInfo(structure);
Map<String, Integer> chainIdToIndexMap = mmtfSummaryDataBean.getChainIdToIndexMap();
List<Atom> allAtoms = mmtfSummaryDataBean.getAllAtoms();
int numBonds = mmtfSummaryDataBean.getNumBonds();
List<Chain> allChains = mmtfSummaryDataBean.getAllChains();
mmtfDecoderInterface.initStructure(numBonds, allAtoms.size(), MmtfUtils.getNumGroups(structure), allChains.size(), structure.nrModels(), structure.getPDBCode());
// Generate the secondary structure
MmtfUtils.calculateDsspSecondaryStructure(structure);
// Get the header and the xtal info.
PDBHeader pdbHeader = structure.getPDBHeader();
PDBCrystallographicInfo xtalInfo = pdbHeader.getCrystallographicInfo();
mmtfDecoderInterface.setHeaderInfo(pdbHeader.getRfree(), pdbHeader.getRwork(), pdbHeader.getResolution(), pdbHeader.getTitle(), MmtfUtils.dateToIsoString(pdbHeader.getDepDate()),
MmtfUtils.dateToIsoString(pdbHeader.getModDate()), MmtfUtils.techniquesToStringArray(pdbHeader.getExperimentalTechniques()));
mmtfDecoderInterface.setXtalInfo(MmtfUtils.getSpaceGroupAsString(xtalInfo.getSpaceGroup()), MmtfUtils.getUnitCellAsArray(xtalInfo), MmtfUtils.getNcsAsArray(xtalInfo.getNcsOperators()));
// Store the bioassembly data
storeBioassemblyInformation(chainIdToIndexMap, pdbHeader.getBioAssemblies());
// Store the entity data
storeEntityInformation(allChains, structure.getEntityInfos());
// Now loop through the data structure
for (int modelIndex=0; modelIndex<structure.nrModels(); modelIndex++) {
List<Chain> modelChains = structure.getChains(modelIndex);
// Set this model
mmtfDecoderInterface.setModelInfo(modelIndex, modelChains.size());
for(int chainInModelIndex=0; chainInModelIndex<modelChains.size(); chainInModelIndex++) {
Chain chain = modelChains.get(chainInModelIndex);
List<Group> groups = chain.getAtomGroups();
List<Group> sequenceGroups = chain.getSeqResGroups();
mmtfDecoderInterface.setChainInfo(chain.getId(), chain.getName(), groups.size());
for(int groupInChainIndex=0; groupInChainIndex<groups.size(); groupInChainIndex++){
Group group = groups.get(groupInChainIndex);
List<Atom> atomsInGroup = MmtfUtils.getAtomsForGroup(group);
ChemComp chemComp = group.getChemComp();
Character insCode = group.getResidueNumber().getInsCode();
if(insCode==null || insCode.equals(' ')){
insCode=MmtfStructure.UNAVAILABLE_CHAR_VALUE;
}
char singleLetterCode = 'X';
if (chemComp.getOne_letter_code().length()==1){
singleLetterCode = chemComp.getOne_letter_code().charAt(0);
}
mmtfDecoderInterface.setGroupInfo(group.getPDBName(), group.getResidueNumber().getSeqNum(), insCode.charValue(),
chemComp.getType().toUpperCase(), atomsInGroup.size(), MmtfUtils.getNumBondsInGroup(atomsInGroup), singleLetterCode,
sequenceGroups.indexOf(group), MmtfUtils.getSecStructType(group));
for (Atom atom : atomsInGroup){
char altLoc = MmtfStructure.UNAVAILABLE_CHAR_VALUE;
if(atom.getAltLoc()!=null){
if(atom.getAltLoc().charValue()!=' '){
altLoc=atom.getAltLoc().charValue();
}
}
mmtfDecoderInterface.setAtomInfo(atom.getName(), atom.getPDBserial(), altLoc, (float) atom.getX(),
(float) atom.getY(), (float) atom.getZ(), atom.getOccupancy(),
atom.getTempFactor(), atom.getElement().toString(), atom.getCharge());
addBonds(atom, atomsInGroup, allAtoms);
}
}
}
}
mmtfDecoderInterface.finalizeStructure();
}
/**
* Add the bonds for a given atom.
* @param atom the atom for which bonds are to be formed
* @param atomsInGroup the list of atoms in the group
* @param allAtoms the list of atoms in the whole structure
*/
private void addBonds(Atom atom, List<Atom> atomsInGroup, List<Atom> allAtoms) {
if(atom.getBonds()==null){
return;
}
for(Bond bond : atom.getBonds()) {
// Now set the bonding information.
Atom other = bond.getOther(atom);
// If both atoms are in the group
if (atomsInGroup.indexOf(other)!=-1){
Integer firstBondIndex = atomsInGroup.indexOf(atom);
Integer secondBondIndex = atomsInGroup.indexOf(other);
// Don't add the same bond twice
if(firstBondIndex>secondBondIndex){
int bondOrder = bond.getBondOrder();
mmtfDecoderInterface.setGroupBond(firstBondIndex, secondBondIndex, bondOrder);
}
}
// Otherwise it's an inter group bond - so add it here
else {
Integer firstBondIndex = allAtoms.indexOf(atom);
Integer secondBondIndex = allAtoms.indexOf(other);
if(firstBondIndex>secondBondIndex){
// Don't add the same bond twice
int bondOrder = bond.getBondOrder();
mmtfDecoderInterface.setInterGroupBond(firstBondIndex, secondBondIndex, bondOrder);
}
}
}
}
/**
* Store the entity information for a given structure.
* @param allChains a list of all the chains in a structure
* @param entityInfos a list of the entity information
*/
private void storeEntityInformation(List<Chain> allChains, List<EntityInfo> entityInfos) {
for (EntityInfo entityInfo : entityInfos) {
String description = entityInfo.getDescription();
String type;
if (entityInfo.getType()==null){
type = null;
}
else{
type = entityInfo.getType().getEntityType();
}
List<Chain> entityChains = entityInfo.getChains();
if (entityChains.isEmpty()){
// Error mapping chain to entity
System.err.println("ERROR MAPPING CHAIN TO ENTITY: "+description);
mmtfDecoderInterface.setEntityInfo(new int[0], "", description, type);
continue;
}
else{
int[] chainIndices = new int[entityChains.size()];
for (int i=0; i<entityChains.size(); i++) {
chainIndices[i] = allChains.indexOf(entityChains.get(i));
}
Chain chain = entityChains.get(0);
ChainImpl chainImpl;
if (chain instanceof ChainImpl){
chainImpl = (ChainImpl) entityChains.get(0);
}
else{
throw new RuntimeException();
}
String sequence = chainImpl.getSeqResOneLetterSeq();
mmtfDecoderInterface.setEntityInfo(chainIndices, sequence, description, type);
}
}
}
/**
* Generate the bioassembly information on in the desired form.
* @param bioJavaStruct the Biojava structure
* @param header the header
*/
private void storeBioassemblyInformation(Map<String, Integer> chainIdToIndexMap, Map<Integer, BioAssemblyInfo> inputBioAss) {
int bioAssemblyIndex = 0;
for (Entry<Integer, BioAssemblyInfo> entry : inputBioAss.entrySet()) {
Map<double[], int[]> transformMap = MmtfUtils.getTransformMap(entry.getValue(), chainIdToIndexMap);
for(Entry<double[], int[]> transformEntry : transformMap.entrySet()) {
mmtfDecoderInterface.setBioAssemblyTrans(bioAssemblyIndex, transformEntry.getValue(), transformEntry.getKey(), entry.getKey().toString());
}
bioAssemblyIndex++;
}
}
}