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) throws IOException
101 * Calls the Jmol library to parse the PDB/mmCIF file, and then inspects the
102 * resulting object model to generate Jalview-style sequences, with secondary
103 * structure annotation added where available (i.e. where it has been computed
104 * by Jmol using DSSP).
106 * @see jalview.io.AlignFile#parse()
109 public void parse() throws IOException
111 setChains(new Vector<PDBChain>());
112 Viewer jmolModel = getJmolData();
113 jmolModel.openReader(getDataName(), getDataName(), getReader());
114 waitForScript(jmolModel);
117 * Convert one or more Jmol Model objects to Jalview sequences
119 if (jmolModel.ms.mc > 0)
121 // ideally we do this
124 // setStructureFileType(jmolModel.evalString("show _fileType"));
125 // } catch (Exception q)
129 // instead, we distinguish .cif from non-.cif by filename
130 setStructureFileType(
131 getDataName().toLowerCase(Locale.ROOT).endsWith(".cif")
132 ? PDBEntry.Type.MMCIF.toString()
135 transformJmolModelToJalview(jmolModel.ms);
140 * create a headless jmol instance for dataprocessing
144 private Viewer getJmolData()
151 * params -o (output to sysout) -n (nodisplay) -x (exit when finished)
152 * see http://wiki.jmol.org/index.php/Jmol_Application
155 viewer = JalviewJmolBinding.getJmolData(this);
156 // ensure the 'new' (DSSP) not 'old' (Ramachandran) SS method is used
157 viewer.setBooleanProperty("defaultStructureDSSP", true);
158 } catch (ClassCastException x)
160 throw new Error(MessageManager.formatMessage(
161 "error.jmol_version_not_compatible_with_jalview_version",
163 { JmolViewer.getJmolVersion() }), x);
169 public static Regex getNewAlphafoldValidator()
171 Regex validator = new Regex("(AF-[A-Z]+[0-9]+[A-Z0-9]+-F1)");
172 validator.setIgnoreCase(true);
176 PDBEntry.Type jmolFiletype = null;
179 * resolve a jmol filetype string and update the jmolFiletype field
182 * @param jmolIdentifiedFileType
183 * @return true if filetype was identified as MMCIF, PDB
185 public boolean updateFileType(String jmolIdentifiedFileType)
187 if (jmolIdentifiedFileType == null
188 || jmolIdentifiedFileType.trim().equals(""))
192 if ("mmcif".equalsIgnoreCase(jmolIdentifiedFileType))
194 jmolFiletype = PDBEntry.Type.MMCIF;
197 if ("pdb".equalsIgnoreCase(jmolIdentifiedFileType))
199 jmolFiletype = PDBEntry.Type.PDB;
205 public void transformJmolModelToJalview(ModelSet ms) throws IOException
209 Regex alphaFold = getNewAlphafoldValidator();
211 List<SequenceI> rna = new ArrayList<SequenceI>();
212 List<SequenceI> prot = new ArrayList<SequenceI>();
214 String pdbId = (String) ms.getInfo(0, "title");
215 boolean isMMCIF = false;
216 String jmolFileType_String = (String) ms.getInfo(0, "fileType");
217 if (updateFileType(jmolFileType_String))
219 setStructureFileType(jmolFiletype.toString());
222 isMMCIF = PDBEntry.Type.MMCIF.equals(jmolFiletype);
226 setId(safeName(getDataName()));
227 setPDBIdAvailable(false);
232 setPDBIdAvailable(true);
233 Console.debug("##### DATASOURCETYPE=" + getDataSourceType());
234 setAlphafoldModel(alphaFold.search(pdbId) && isMMCIF
235 && getDataSourceType() == DataSourceType.URL);
237 List<Atom> significantAtoms = convertSignificantAtoms(ms);
238 for (Atom tmpatom : significantAtoms)
240 if (tmpatom.resNumIns.trim().equals(lastID))
242 // phosphorylated protein - seen both CA and P..
245 tmpchain = findChain(tmpatom.chain);
246 if (tmpchain != null)
248 tmpchain.atoms.addElement(tmpatom);
252 AnnotationRowBuilder builder = null;
253 if (isAlphafoldModel()
254 || getTemperatureFactorType() == StructureImportSettings.TFType.PLDDT)
256 builder = new AlphaFoldAnnotationRowBuilder();
259 tmpchain = new PDBChain(getId(), tmpatom.chain, builder);
260 getChains().add(tmpchain);
261 tmpchain.atoms.addElement(tmpatom);
263 lastID = tmpatom.resNumIns.trim();
265 if (isParseImmediately())
267 // configure parsing settings from the static singleton
274 for (PDBChain chain : getChains())
276 SequenceI chainseq = postProcessChain(chain);
286 // look at local setting for adding secondary tructure
287 if (predictSecondaryStructure)
289 createAnnotation(chainseq, chain, ms.at);
292 // if Alphafold, fetch the PAE matrix if doesn't already have one
293 if (isAlphafoldModel() && !hasPAEMatrix())
297 Console.info("retrieving pAE for " + pdbId);
298 File paeFile = EBIAlfaFold.fetchAlphaFoldPAE(pdbId, null);
299 this.setPAEMatrix(paeFile.getAbsolutePath());
300 } catch (Throwable t)
302 Console.error("Couldn't get the pAE for " + pdbId, t);
305 // add a PAEMatrix if set (either by above or otherwise)
306 Console.debug("##### hasPAEMatrix()=" + hasPAEMatrix());
307 Console.debug("##### isAlphafoldModel()=" + isAlphafoldModel());
308 Console.debug("##### getPAEMatrix()=" + getPAEMatrix());
311 Alignment al = new Alignment(prot.toArray(new SequenceI[0]));
312 if (isAlphafoldModel())
314 EBIAlfaFold.addAlphaFoldPAE(al, new File(this.getPAEMatrix()), 0,
319 EBIAlfaFold.addPAEToStructure(null, this.getInFile(),
320 new File(this.getPAEMatrix()));
323 Console.debug("##### al.getAlignmentAnnotation()="
324 + al.getAlignmentAnnotation());
325 if (al.getAlignmentAnnotation() != null)
327 for (AlignmentAnnotation alann : al.getAlignmentAnnotation())
329 Console.debug("##### Adding to alann" + alann.annotationId + "("
330 + alann.getCalcId() + ")");
331 annotations.add(alann);
335 } catch (OutOfMemoryError er)
338 "OUT OF MEMORY LOADING TRANSFORMING JMOL MODEL TO JALVIEW MODEL");
339 throw new IOException(MessageManager
340 .getString("exception.outofmemory_loading_mmcif_file"));
344 private List<Atom> convertSignificantAtoms(ModelSet ms)
346 List<Atom> significantAtoms = new ArrayList<Atom>();
347 HashMap<String, org.jmol.modelset.Atom> chainTerMap = new HashMap<String, org.jmol.modelset.Atom>();
348 org.jmol.modelset.Atom prevAtom = null;
349 for (org.jmol.modelset.Atom atom : ms.at)
351 if (atom.getAtomName().equalsIgnoreCase("CA")
352 || atom.getAtomName().equalsIgnoreCase("P"))
354 if (!atomValidated(atom, prevAtom, chainTerMap))
358 Atom curAtom = new Atom(atom.x, atom.y, atom.z);
359 curAtom.atomIndex = atom.getIndex();
360 curAtom.chain = atom.getChainIDStr();
361 curAtom.insCode = atom.group.getInsertionCode() == '\000' ? ' '
362 : atom.group.getInsertionCode();
363 curAtom.name = atom.getAtomName();
364 curAtom.number = atom.getAtomNumber();
365 curAtom.resName = atom.getGroup3(true);
366 curAtom.resNumber = atom.getResno();
367 curAtom.occupancy = ms.occupancies != null
368 ? ms.occupancies[atom.getIndex()]
369 : Float.valueOf(atom.getOccupancy100());
370 String fmt = new Format("%4i").form(curAtom.resNumber);
371 curAtom.resNumIns = (fmt + curAtom.insCode);
372 curAtom.tfactor = atom.getBfactor100() / 100f;
374 // significantAtoms.add(curAtom);
375 // ignore atoms from subsequent models
376 if (!significantAtoms.contains(curAtom))
378 significantAtoms.add(curAtom);
383 return significantAtoms;
386 private boolean atomValidated(org.jmol.modelset.Atom curAtom,
387 org.jmol.modelset.Atom prevAtom,
388 HashMap<String, org.jmol.modelset.Atom> chainTerMap)
390 // System.out.println("Atom: " + curAtom.getAtomNumber()
391 // + " Last atom index " + curAtom.group.lastAtomIndex);
392 if (chainTerMap == null || prevAtom == null)
396 String curAtomChId = curAtom.getChainIDStr();
397 String prevAtomChId = prevAtom.getChainIDStr();
398 // new chain encoutered
399 if (!prevAtomChId.equals(curAtomChId))
401 // On chain switch add previous chain termination to xTerMap if not exists
402 if (!chainTerMap.containsKey(prevAtomChId))
404 chainTerMap.put(prevAtomChId, prevAtom);
406 // if current atom belongs to an already terminated chain and the resNum
407 // diff < 5 then mark as valid and update termination Atom
408 if (chainTerMap.containsKey(curAtomChId))
410 if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno())
414 if ((curAtom.getResno()
415 - chainTerMap.get(curAtomChId).getResno()) < 5)
417 chainTerMap.put(curAtomChId, curAtom);
423 // atom with previously terminated chain encountered
424 else if (chainTerMap.containsKey(curAtomChId))
426 if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno())
430 if ((curAtom.getResno()
431 - chainTerMap.get(curAtomChId).getResno()) < 5)
433 chainTerMap.put(curAtomChId, curAtom);
438 // HETATM with resNum jump > 2
439 return !(curAtom.isHetero()
440 && ((curAtom.getResno() - prevAtom.getResno()) > 2));
443 private void createAnnotation(SequenceI sequence, PDBChain chain,
444 org.jmol.modelset.Atom[] jmolAtoms)
446 char[] secstr = new char[sequence.getLength()];
447 char[] secstrcode = new char[sequence.getLength()];
449 // Ensure Residue size equals Seq size
450 if (chain.residues.size() != sequence.getLength())
455 for (Residue residue : chain.residues)
457 Atom repAtom = residue.getAtoms().get(0);
458 STR proteinStructureSubType = jmolAtoms[repAtom.atomIndex].group
459 .getProteinStructureSubType();
460 setSecondaryStructure(proteinStructureSubType, annotIndex, secstr,
464 addSecondaryStructureAnnotation(chain.pdbid, sequence, secstr,
465 secstrcode, chain.id, sequence.getStart());
469 * Helper method that adds an AlignmentAnnotation for secondary structure to
470 * the sequence, provided at least one secondary structure assignment has been
481 protected void addSecondaryStructureAnnotation(String modelTitle,
482 SequenceI sq, char[] secstr, char[] secstrcode, String chainId,
485 int length = sq.getLength();
486 boolean ssFound = false;
487 Annotation asecstr[] = new Annotation[length + firstResNum - 1];
488 for (int p = 0; p < length; p++)
490 if (secstr[p] >= 'A' && secstr[p] <= 'z')
494 asecstr[p] = new Annotation(null, null, secstrcode[p], Float.NaN);
496 } catch (Exception e)
498 // e.printStackTrace();
505 String mt = modelTitle == null ? getDataName() : modelTitle;
507 AlignmentAnnotation ann = new AlignmentAnnotation(
508 "Secondary Structure", "Secondary Structure for " + mt,
510 ann.belowAlignment = true;
512 ann.autoCalculated = false;
513 ann.setCalcId(getClass().getName());
514 ann.adjustForAlignment();
515 ann.validateRangeAndDisplay();
516 annotations.add(ann);
517 sq.addAlignmentAnnotation(ann);
521 private void waitForScript(Viewer jmd)
523 while (jmd.isScriptExecuting())
529 } catch (InterruptedException x)
536 * Convert Jmol's secondary structure code to Jalview's, and stored it in the
537 * secondary structure arrays at the given sequence position
539 * @param proteinStructureSubType
544 protected void setSecondaryStructure(STR proteinStructureSubType, int pos,
545 char[] secstr, char[] secstrcode)
547 switch (proteinStructureSubType)
566 switch (proteinStructureSubType)
572 secstrcode[pos] = 'H';
575 secstrcode[pos] = 'E';
583 * Convert any non-standard peptide codes to their standard code table
584 * equivalent. (Initial version only does Selenomethionine MSE->MET.)
586 * @param threeLetterCode
590 protected void replaceNonCanonicalResidue(String threeLetterCode,
593 String canonical = ResidueProperties
594 .getCanonicalAminoAcid(threeLetterCode);
595 if (canonical != null && !canonical.equalsIgnoreCase(threeLetterCode))
597 seq[pos] = ResidueProperties.getSingleCharacterCode(canonical);
602 * Not implemented - returns null
605 public String print(SequenceI[] seqs, boolean jvSuffix)
614 public void setCallbackFunction(String callbackType,
615 String callbackFunction)
620 public void notifyCallback(CBK cbType, Object[] data)
622 String strInfo = (data == null || data[1] == null ? null
623 : data[1].toString());
627 sendConsoleEcho(strInfo);
630 notifyScriptTermination((String) data[2],
631 ((Integer) data[3]).intValue());
634 String mystatus = (String) data[3];
635 if (mystatus.indexOf("Picked") >= 0
636 || mystatus.indexOf("Sequence") >= 0)
639 sendConsoleMessage(strInfo);
641 else if (mystatus.indexOf("Completed") >= 0)
643 sendConsoleEcho(strInfo.substring(strInfo.lastIndexOf(",") + 2,
644 strInfo.length() - 1));
648 sendConsoleMessage(data == null ? null : strInfo);
651 sendConsoleMessage(strInfo);
658 String lastConsoleEcho = "";
660 private void sendConsoleEcho(String string)
662 lastConsoleEcho += string;
663 lastConsoleEcho += "\n";
666 String lastConsoleMessage = "";
668 private void sendConsoleMessage(String string)
670 lastConsoleMessage += string;
671 lastConsoleMessage += "\n";
674 int lastScriptTermination = -1;
676 String lastScriptMessage = "";
678 private void notifyScriptTermination(String string, int intValue)
680 lastScriptMessage += string;
681 lastScriptMessage += "\n";
682 lastScriptTermination = intValue;
686 public boolean notifyEnabled(CBK callbackPick)
688 switch (callbackPick)
702 * Not implemented - returns null
705 public String eval(String strEval)
711 * Not implemented - returns null
714 public float[][] functionXY(String functionName, int x, int y)
720 * Not implemented - returns null
723 public float[][][] functionXYZ(String functionName, int nx, int ny,
730 * Not implemented - returns null
733 public String createImage(String fileName, String imageType,
734 Object text_or_bytes, int quality)
740 * Not implemented - returns null
743 public Map<String, Object> getRegistryInfo()
752 public void showUrl(String url)
757 * Not implemented - returns null
760 public int[] resizeInnerPanel(String data)
766 public Map<String, Object> getJSpecViewProperty(String arg0)
771 public boolean isPredictSecondaryStructure()
773 return predictSecondaryStructure;
776 public void setPredictSecondaryStructure(
777 boolean predictSecondaryStructure)
779 this.predictSecondaryStructure = predictSecondaryStructure;
782 public boolean isVisibleChainAnnotation()
784 return visibleChainAnnotation;
787 public void setVisibleChainAnnotation(boolean visibleChainAnnotation)
789 this.visibleChainAnnotation = visibleChainAnnotation;