2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.ext.jmol;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Locale;
30 import java.util.Vector;
32 import org.jmol.api.JmolStatusListener;
33 import org.jmol.api.JmolViewer;
34 import org.jmol.c.CBK;
35 import org.jmol.c.STR;
36 import org.jmol.modelset.ModelSet;
37 import org.jmol.viewer.Viewer;
39 import com.stevesoft.pat.Regex;
41 import jalview.bin.Console;
42 import jalview.datamodel.Alignment;
43 import jalview.datamodel.AlignmentAnnotation;
44 import jalview.datamodel.Annotation;
45 import jalview.datamodel.PDBEntry;
46 import jalview.datamodel.SequenceI;
47 import jalview.datamodel.annotations.AlphaFoldAnnotationRowBuilder;
48 import jalview.datamodel.annotations.AnnotationRowBuilder;
49 import jalview.io.DataSourceType;
50 import jalview.io.FileParse;
51 import jalview.io.StructureFile;
52 import jalview.schemes.ResidueProperties;
53 import jalview.structure.StructureImportSettings;
54 import jalview.util.Format;
55 import jalview.util.MessageManager;
56 import jalview.ws.dbsources.EBIAlfaFold;
58 import mc_view.PDBChain;
59 import mc_view.Residue;
62 * Import and process files with Jmol for file like PDB, mmCIF
67 public class JmolParser extends StructureFile implements JmolStatusListener
71 public JmolParser(boolean immediate, Object inFile,
72 DataSourceType sourceType) throws IOException
74 // BH 2018 File or String for filename
75 super(immediate, inFile, sourceType);
79 public JmolParser(Object inFile, DataSourceType sourceType)
82 this(inFile, sourceType, null);
85 public JmolParser(Object inFile, DataSourceType sourceType,
86 StructureImportSettings.TFType tempfacType) throws IOException
88 super(inFile, sourceType, tempfacType);
91 public JmolParser(FileParse fp, boolean doXferSettings) throws IOException
93 super(fp, doXferSettings);
96 public JmolParser(FileParse fp) throws IOException
106 * Calls the Jmol library to parse the PDB/mmCIF file, and then inspects the
107 * resulting object model to generate Jalview-style sequences, with secondary
108 * structure annotation added where available (i.e. where it has been computed
109 * by Jmol using DSSP).
111 * @see jalview.io.AlignFile#parse()
114 public void parse() throws IOException
120 public void parse(boolean doXferSettings) throws IOException
122 setChains(new Vector<PDBChain>());
123 Viewer jmolModel = getJmolData();
124 jmolModel.openReader(getDataName(), getDataName(), getReader());
125 waitForScript(jmolModel);
128 * Convert one or more Jmol Model objects to Jalview sequences
130 if (jmolModel.ms.mc > 0)
132 // ideally we do this
135 // setStructureFileType(jmolModel.evalString("show _fileType"));
136 // } catch (Exception q)
140 // instead, we distinguish .cif from non-.cif by filename
141 setStructureFileType(
142 getDataName().toLowerCase(Locale.ROOT).endsWith(".cif")
143 ? PDBEntry.Type.MMCIF.toString()
146 transformJmolModelToJalview(jmolModel.ms, doXferSettings);
151 * create a headless jmol instance for dataprocessing
155 private Viewer getJmolData()
162 * params -o (output to sysout) -n (nodisplay) -x (exit when finished)
163 * see http://wiki.jmol.org/index.php/Jmol_Application
166 viewer = JalviewJmolBinding.getJmolData(this);
167 // ensure the 'new' (DSSP) not 'old' (Ramachandran) SS method is used
168 viewer.setBooleanProperty("defaultStructureDSSP", true);
169 } catch (ClassCastException x)
171 throw new Error(MessageManager.formatMessage(
172 "error.jmol_version_not_compatible_with_jalview_version",
174 { JmolViewer.getJmolVersion() }), x);
180 public static Regex getNewAlphafoldValidator()
182 Regex validator = new Regex("(AF-[A-Z]+[0-9]+[A-Z0-9]+-F1)");
183 validator.setIgnoreCase(true);
187 PDBEntry.Type jmolFiletype = null;
190 * resolve a jmol filetype string and update the jmolFiletype field
193 * @param jmolIdentifiedFileType
194 * @return true if filetype was identified as MMCIF, PDB
196 public boolean updateFileType(String jmolIdentifiedFileType)
198 if (jmolIdentifiedFileType == null
199 || jmolIdentifiedFileType.trim().equals(""))
203 if ("mmcif".equalsIgnoreCase(jmolIdentifiedFileType))
205 jmolFiletype = PDBEntry.Type.MMCIF;
208 if ("pdb".equalsIgnoreCase(jmolIdentifiedFileType))
210 jmolFiletype = PDBEntry.Type.PDB;
216 public void transformJmolModelToJalview(ModelSet ms,
217 boolean localDoXferSettings) throws IOException
221 Regex alphaFold = getNewAlphafoldValidator();
223 List<SequenceI> rna = new ArrayList<SequenceI>();
224 List<SequenceI> prot = new ArrayList<SequenceI>();
226 String pdbId = (String) ms.getInfo(0, "title");
227 boolean isMMCIF = false;
228 String jmolFileType_String = (String) ms.getInfo(0, "fileType");
229 if (updateFileType(jmolFileType_String))
231 setStructureFileType(jmolFiletype.toString());
234 isMMCIF = PDBEntry.Type.MMCIF.equals(jmolFiletype);
238 setId(safeName(getDataName()));
239 setPDBIdAvailable(false);
244 setPDBIdAvailable(true);
245 setAlphafoldModel(alphaFold.search(pdbId) && isMMCIF);
247 List<Atom> significantAtoms = convertSignificantAtoms(ms);
248 for (Atom tmpatom : significantAtoms)
250 if (tmpatom.resNumIns.trim().equals(lastID))
252 // phosphorylated protein - seen both CA and P..
255 tmpchain = findChain(tmpatom.chain);
256 if (tmpchain != null)
258 tmpchain.atoms.addElement(tmpatom);
262 AnnotationRowBuilder builder = null;
263 if (isAlphafoldModel()
264 || getTemperatureFactorType() == StructureImportSettings.TFType.PLDDT)
266 builder = new AlphaFoldAnnotationRowBuilder();
269 tmpchain = new PDBChain(getId(), tmpatom.chain, builder);
270 getChains().add(tmpchain);
271 tmpchain.atoms.addElement(tmpatom);
273 lastID = tmpatom.resNumIns.trim();
275 if (isParseImmediately() && localDoXferSettings)
277 // configure parsing settings from the static singleton
284 for (PDBChain chain : getChains())
286 SequenceI chainseq = postProcessChain(chain);
296 // look at local setting for adding secondary tructure
297 if (predictSecondaryStructure)
299 createAnnotation(chainseq, chain, ms.at);
302 // if Alphafold, fetch the PAE matrix if doesn't already have one
303 if (isAlphafoldModel() && !hasPAEMatrix())
307 Console.info("Retrieving PAE for " + pdbId);
308 File paeFile = EBIAlfaFold.fetchAlphaFoldPAE(pdbId, null);
309 this.setPAEMatrix(paeFile.getAbsolutePath());
310 } catch (Throwable t)
312 Console.error("Couldn't get the pAE for " + pdbId, t);
315 // add a PAEMatrix if set (either by above or otherwise)
320 Alignment al = new Alignment(prot.toArray(new SequenceI[0]));
321 EBIAlfaFold.addAlphaFoldPAE(al, new File(this.getPAEMatrix()), 0,
322 null, false, false, null);
324 if (al.getAlignmentAnnotation() != null)
326 for (AlignmentAnnotation alann : al.getAlignmentAnnotation())
328 annotations.add(alann);
331 } catch (Throwable ff)
333 Console.error("Couldn't import PAE Matrix from " + getPAEMatrix(),
335 warningMessage += "Couldn't import PAE Matrix"
336 + getNewlineString() + ff.getLocalizedMessage()
337 + getNewlineString();
340 } catch (OutOfMemoryError er)
342 jalview.bin.Console.outPrintln(
343 "OUT OF MEMORY LOADING TRANSFORMING JMOL MODEL TO JALVIEW MODEL");
344 throw new IOException(MessageManager
345 .getString("exception.outofmemory_loading_mmcif_file"));
349 private List<Atom> convertSignificantAtoms(ModelSet ms)
351 List<Atom> significantAtoms = new ArrayList<Atom>();
352 HashMap<String, org.jmol.modelset.Atom> chainTerMap = new HashMap<String, org.jmol.modelset.Atom>();
353 org.jmol.modelset.Atom prevAtom = null;
354 for (org.jmol.modelset.Atom atom : ms.at)
356 if (atom.getAtomName().equalsIgnoreCase("CA")
357 || atom.getAtomName().equalsIgnoreCase("P"))
359 if (!atomValidated(atom, prevAtom, chainTerMap))
363 Atom curAtom = new Atom(atom.x, atom.y, atom.z);
364 curAtom.atomIndex = atom.getIndex();
365 curAtom.chain = atom.getChainIDStr();
366 curAtom.insCode = atom.group.getInsertionCode() == '\000' ? ' '
367 : atom.group.getInsertionCode();
368 curAtom.name = atom.getAtomName();
369 curAtom.number = atom.getAtomNumber();
370 curAtom.resName = atom.getGroup3(true);
371 curAtom.resNumber = atom.getResno();
372 curAtom.occupancy = ms.occupancies != null
373 ? ms.occupancies[atom.getIndex()]
374 : Float.valueOf(atom.getOccupancy100());
375 String fmt = new Format("%4i").form(curAtom.resNumber);
376 curAtom.resNumIns = (fmt + curAtom.insCode);
377 curAtom.tfactor = atom.getBfactor100() / 100f;
379 // significantAtoms.add(curAtom);
380 // ignore atoms from subsequent models
381 if (!significantAtoms.contains(curAtom))
383 significantAtoms.add(curAtom);
388 return significantAtoms;
391 private boolean atomValidated(org.jmol.modelset.Atom curAtom,
392 org.jmol.modelset.Atom prevAtom,
393 HashMap<String, org.jmol.modelset.Atom> chainTerMap)
395 // jalview.bin.Console.outPrintln("Atom: " + curAtom.getAtomNumber()
396 // + " Last atom index " + curAtom.group.lastAtomIndex);
397 if (chainTerMap == null || prevAtom == null)
401 String curAtomChId = curAtom.getChainIDStr();
402 String prevAtomChId = prevAtom.getChainIDStr();
403 // new chain encoutered
404 if (!prevAtomChId.equals(curAtomChId))
406 // On chain switch add previous chain termination to xTerMap if not exists
407 if (!chainTerMap.containsKey(prevAtomChId))
409 chainTerMap.put(prevAtomChId, prevAtom);
411 // if current atom belongs to an already terminated chain and the resNum
412 // diff < 5 then mark as valid and update termination Atom
413 if (chainTerMap.containsKey(curAtomChId))
415 if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno())
419 if ((curAtom.getResno()
420 - chainTerMap.get(curAtomChId).getResno()) < 5)
422 chainTerMap.put(curAtomChId, curAtom);
428 // atom with previously terminated chain encountered
429 else if (chainTerMap.containsKey(curAtomChId))
431 if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno())
435 if ((curAtom.getResno()
436 - chainTerMap.get(curAtomChId).getResno()) < 5)
438 chainTerMap.put(curAtomChId, curAtom);
443 // HETATM with resNum jump > 2
444 return !(curAtom.isHetero()
445 && ((curAtom.getResno() - prevAtom.getResno()) > 2));
448 private void createAnnotation(SequenceI sequence, PDBChain chain,
449 org.jmol.modelset.Atom[] jmolAtoms)
451 char[] secstr = new char[sequence.getLength()];
452 char[] secstrcode = new char[sequence.getLength()];
454 // Ensure Residue size equals Seq size
455 if (chain.residues.size() != sequence.getLength())
460 for (Residue residue : chain.residues)
462 Atom repAtom = residue.getAtoms().get(0);
463 STR proteinStructureSubType = jmolAtoms[repAtom.atomIndex].group
464 .getProteinStructureSubType();
465 setSecondaryStructure(proteinStructureSubType, annotIndex, secstr,
469 addSecondaryStructureAnnotation(chain.pdbid, sequence, secstr,
470 secstrcode, chain.id, sequence.getStart());
474 * Helper method that adds an AlignmentAnnotation for secondary structure to
475 * the sequence, provided at least one secondary structure assignment has been
486 protected void addSecondaryStructureAnnotation(String modelTitle,
487 SequenceI sq, char[] secstr, char[] secstrcode, String chainId,
490 int length = sq.getLength();
491 boolean ssFound = false;
492 Annotation asecstr[] = new Annotation[length
493 + (firstResNum - sq.getStart())];
494 for (int p = 0; p < length; p++)
496 if (secstr[p] >= 'A' && secstr[p] <= 'z')
500 asecstr[p] = new Annotation(null, null, secstrcode[p], Float.NaN);
502 } catch (Exception e)
504 // e.printStackTrace();
511 String mt = modelTitle == null ? getDataName() : modelTitle;
513 AlignmentAnnotation ann = new AlignmentAnnotation(
514 "Secondary Structure", "Secondary Structure for " + mt,
516 ann.belowAlignment = true;
518 ann.autoCalculated = false;
519 ann.setCalcId(getClass().getName());
520 ann.adjustForAlignment();
521 ann.validateRangeAndDisplay();
522 annotations.add(ann);
523 sq.addAlignmentAnnotation(ann);
527 private void waitForScript(Viewer jmd)
529 while (jmd.isScriptExecuting())
535 } catch (InterruptedException x)
542 * Convert Jmol's secondary structure code to Jalview's, and stored it in the
543 * secondary structure arrays at the given sequence position
545 * @param proteinStructureSubType
550 protected void setSecondaryStructure(STR proteinStructureSubType, int pos,
551 char[] secstr, char[] secstrcode)
553 switch (proteinStructureSubType)
572 switch (proteinStructureSubType)
578 secstrcode[pos] = 'H';
581 secstrcode[pos] = 'E';
589 * Convert any non-standard peptide codes to their standard code table
590 * equivalent. (Initial version only does Selenomethionine MSE->MET.)
592 * @param threeLetterCode
596 protected void replaceNonCanonicalResidue(String threeLetterCode,
599 String canonical = ResidueProperties
600 .getCanonicalAminoAcid(threeLetterCode);
601 if (canonical != null && !canonical.equalsIgnoreCase(threeLetterCode))
603 seq[pos] = ResidueProperties.getSingleCharacterCode(canonical);
608 * Not implemented - returns null
611 public String print(SequenceI[] seqs, boolean jvSuffix)
620 public void setCallbackFunction(String callbackType,
621 String callbackFunction)
626 public void notifyCallback(CBK cbType, Object[] data)
628 String strInfo = (data == null || data[1] == null ? null
629 : data[1].toString());
633 sendConsoleEcho(strInfo);
636 notifyScriptTermination((String) data[2],
637 ((Integer) data[3]).intValue());
640 String mystatus = (String) data[3];
641 if (mystatus.indexOf("Picked") >= 0
642 || mystatus.indexOf("Sequence") >= 0)
645 sendConsoleMessage(strInfo);
647 else if (mystatus.indexOf("Completed") >= 0)
649 sendConsoleEcho(strInfo.substring(strInfo.lastIndexOf(",") + 2,
650 strInfo.length() - 1));
654 sendConsoleMessage(data == null ? null : strInfo);
657 sendConsoleMessage(strInfo);
664 String lastConsoleEcho = "";
666 private void sendConsoleEcho(String string)
668 lastConsoleEcho += string;
669 lastConsoleEcho += "\n";
672 String lastConsoleMessage = "";
674 private void sendConsoleMessage(String string)
676 lastConsoleMessage += string;
677 lastConsoleMessage += "\n";
680 int lastScriptTermination = -1;
682 String lastScriptMessage = "";
684 private void notifyScriptTermination(String string, int intValue)
686 lastScriptMessage += string;
687 lastScriptMessage += "\n";
688 lastScriptTermination = intValue;
692 public boolean notifyEnabled(CBK callbackPick)
694 switch (callbackPick)
708 * Not implemented - returns null
711 public String eval(String strEval)
717 * Not implemented - returns null
720 public float[][] functionXY(String functionName, int x, int y)
726 * Not implemented - returns null
729 public float[][][] functionXYZ(String functionName, int nx, int ny,
736 * Not implemented - returns null
739 public String createImage(String fileName, String imageType,
740 Object text_or_bytes, int quality)
746 * Not implemented - returns null
749 public Map<String, Object> getRegistryInfo()
758 public void showUrl(String url)
763 * Not implemented - returns null
766 public int[] resizeInnerPanel(String data)
772 public Map<String, Object> getJSpecViewProperty(String arg0)
777 public boolean isPredictSecondaryStructure()
779 return predictSecondaryStructure;
782 public void setPredictSecondaryStructure(
783 boolean predictSecondaryStructure)
785 this.predictSecondaryStructure = predictSecondaryStructure;
788 public boolean isVisibleChainAnnotation()
790 return visibleChainAnnotation;
793 public void setVisibleChainAnnotation(boolean visibleChainAnnotation)
795 this.visibleChainAnnotation = visibleChainAnnotation;