JAL-2694 StructureSelectionManager.getMapping takes a list of Chain IDs per sequence...
[jalview.git] / test / jalview / structure / Mapping.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
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.
11  *  
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.
16  * 
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.
20  */
21 package jalview.structure;
22
23 import static org.testng.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertTrue;
25
26 import jalview.datamodel.AlignmentAnnotation;
27 import jalview.datamodel.Annotation;
28 import jalview.datamodel.Sequence;
29 import jalview.datamodel.SequenceI;
30 import jalview.gui.AlignFrame;
31 import jalview.gui.JvOptionPane;
32 import jalview.io.DataSourceType;
33 import jalview.io.FileFormat;
34 import jalview.io.FileLoader;
35 import jalview.io.StructureFile;
36
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.List;
40
41 import org.testng.Assert;
42 import org.testng.AssertJUnit;
43 import org.testng.annotations.BeforeClass;
44 import org.testng.annotations.Test;
45
46 public class Mapping
47 {
48
49   @BeforeClass(alwaysRun = true)
50   public void setUpJvOptionPane()
51   {
52     JvOptionPane.setInteractiveMode(false);
53     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
54   }
55
56   /*
57    * more test data
58    * 
59    * 1QCF|A/101-121 SFQKGDQMVVLEESGEWWKAR Ser 114 jumps to Gly 116 at position
60    * 115 in PDB Res Numbering secondary structure numbers in jmol seem to be in
61    * msd numbering, not pdb res numbering.
62    */
63   @Test(groups = { "Functional" }, enabled = false)
64   public void pdbEntryPositionMap() throws Exception
65   {
66     Assert.fail("This test intentionally left to fail");
67     for (int offset = 0; offset < 20; offset += 6)
68     {
69       // check we put the secondary structure in the right position
70       Sequence uprot = new Sequence("TheProtSeq",
71               "DAWEIPRESLKLEKKLGAGQFGEVWMATYNKHTKVAVKTMKPGSMSVEAFLAEANVMKTL");
72       uprot.setStart(offset + 258); // make it harder - create a fake
73                                     // relocation problem for jalview to
74                                     // deal with
75       uprot.setEnd(uprot.getStart() + uprot.getLength() - 1);
76       // original numbers taken from
77       // http://www.ebi.ac.uk/pdbe-srv/view/entry/1qcf/secondary.html
78       // these are in numbering relative to the subsequence above
79       int coils[] = { 266, 275, 278, 287, 289, 298, 302, 316 }, helices[] = new int[]
80       { 303, 315 }, sheets[] = new int[] { 267, 268, 269, 270 };
81
82       StructureSelectionManager ssm = new jalview.structure.StructureSelectionManager();
83       StructureFile pmap = ssm.setMapping(true, new SequenceI[] { uprot },
84               new List[]
85               { Arrays.asList(new String[] { "A" }) },
86               "test/jalview/ext/jmol/1QCF.pdb",
87               DataSourceType.FILE);
88       assertTrue(pmap != null);
89       SequenceI protseq = pmap.getSeqsAsArray()[0];
90       AlignmentAnnotation pstra = protseq
91               .getAnnotation("Secondary Structure")[0];
92       int pinds, pinde;
93       pstra.restrict((pinds = protseq.findIndex(258) - 1),
94               pinde = (protseq.findIndex(317) - 1));
95       int op;
96       System.out.println("PDB Annot");
97       for (char c : protseq.getSubSequence(pinds, pinde).getSequence())
98       {
99         System.out.print(c + ", ");
100       }
101       System.out.println("\n" + pstra + "\n\nsubsequence\n");
102       for (char c : uprot.getSequence())
103       {
104         System.out.print(c + ", ");
105       }
106       System.out.println("");
107       for (AlignmentAnnotation ss : uprot
108               .getAnnotation("Secondary Structure"))
109       {
110         ss.adjustForAlignment();
111         System.out.println("Uniprot Annot\n" + ss);
112         assertTrue(ss.hasIcons);
113         char expected = 'H';
114         for (int p : helices)
115         {
116           Annotation a = ss.annotations[op = (uprot.findIndex(offset + p) - 1)];
117           assertTrue(
118                   "Expected a helix at position " + p + uprot.getCharAt(op)
119                           + " but got coil", a != null);
120           assertEquals("Expected a helix at position " + p,
121                   a.secondaryStructure, expected);
122         }
123         expected = 'E';
124         for (int p : sheets)
125         {
126           Annotation a = ss.annotations[uprot.findIndex(offset + p) - 1];
127           assertTrue(
128                   "Expected a strand at position " + p + " but got coil",
129                   a != null);
130           assertEquals("Expected a strand at position " + p,
131                   a.secondaryStructure, expected);
132         }
133         expected = ' ';
134         for (int p : coils)
135         {
136           Annotation a = ss.annotations[uprot.findIndex(offset + p) - 1];
137           assertTrue("Expected coil at position " + p + " but got "
138                   + a.secondaryStructure, a == null);
139         }
140       }
141     }
142   }
143
144   @Test(groups = { "Functional" }, enabled = false)
145   public void testPDBentryMapping() throws Exception
146   {
147     Assert.fail("This test intentionally left to fail");
148     Sequence sq = new Sequence(
149             "1GAQ A subseq 126 to 219",
150             "EIVKGVCSNFLCDLQPGDNVQITGPVGKEMLMPKDPNATIIMLATGTGIAPFRSFLWKMFFEKHDDYKFNGLGWLFLGVPTSSSLLYKEEFGKM");
151     Sequence sq1 = new Sequence(sq);
152     String inFile;
153     StructureSelectionManager ssm = new jalview.structure.StructureSelectionManager();
154     // Associate the 1GAQ pdb file with the subsequence 'imported' from another
155     // source
156     StructureFile pde = ssm.setMapping(true, new SequenceI[] { sq },
157             new List[]
158             { Arrays.asList(new String[] { "A" }) },
159             inFile = "examples/1gaq.txt", DataSourceType.FILE);
160     assertTrue("PDB File couldn't be found", pde != null);
161     StructureMapping[] mp = ssm.getMapping(inFile);
162     assertTrue("No mappings made.", mp != null && mp.length > 0);
163     int nsecStr = 0, nsTemp = 0;
164     // test for presence of transferred annotation on sequence
165     for (AlignmentAnnotation alan : sq.getAnnotation())
166     {
167       if (alan.hasIcons)
168       {
169         nsecStr++;
170       }
171       if (alan.graph == alan.LINE_GRAPH)
172       {
173         nsTemp++;
174       }
175     }
176     assertEquals(
177             "Only one secondary structure should be transferred to associated sequence.",
178             1, nsecStr);
179     assertEquals(
180             "Only two line graphs should be transferred to associated sequence.",
181             2, nsTemp);
182     // Now test the transfer function and compare annotated positions
183     for (StructureMapping origMap : mp)
184     {
185       if (origMap.getSequence() == sq)
186       {
187         assertEquals("Mapping was incomplete.", sq.getLength() - 1,
188                 (origMap.getPDBResNum(sq.getEnd()) - origMap
189                         .getPDBResNum(sq.getStart())));
190         // sanity check - if this fails, mapping from first position in sequence
191         // we want to transfer to is not where we expect
192         assertEquals(1, origMap.getSeqPos(126));
193         SequenceI firstChain = pde.getSeqs().get(0);
194         // Compare the annotated positions on the PDB chain sequence with the
195         // annotation on the associated sequence
196         for (AlignmentAnnotation alan : firstChain.getAnnotation())
197         {
198           AlignmentAnnotation transfer = origMap.transfer(alan);
199           System.out.println("pdb:" + firstChain.getSequenceAsString());
200           System.out.println("ann:" + alan.toString());
201           System.out.println("pdb:" + sq.getSequenceAsString());
202           System.out.println("ann:" + transfer.toString());
203
204           for (int p = 0, pSize = firstChain.getLength(); p < pSize; p++)
205           {
206             // walk along the pdb chain's jalview sequence
207             int rseqpos;
208             int fpos = origMap.getSeqPos(rseqpos = firstChain
209                     .findPosition(p));
210             // only look at positions where there is a corresponding position in
211             // mapping
212             if (fpos < 1)
213             {
214               continue;
215             }
216             // p is index into PDB residue entries
217             // rseqpos is pdb sequence position for position p
218             // fpos is sequence position for associated position for rseqpos
219             // tanpos is the column for the mapped sequence position
220             int tanpos = sq.findIndex(fpos) - 1;
221             if (tanpos < 0 || transfer.annotations.length <= tanpos)
222             {
223               // gone beyond mapping to the sequence
224               break;
225             }
226
227             Annotation a = transfer.annotations[tanpos], b = alan.annotations[p];
228             assertEquals("Non-equivalent annotation element at " + p + "("
229                     + rseqpos + ")" + " expected at " + fpos + " (alIndex "
230                     + tanpos + ")", a == null ? a : a.toString(),
231                     b == null ? b : b.toString());
232             System.out.print("(" + a + "|" + b + ")");
233           }
234
235         }
236       }
237     }
238   }
239
240   /**
241    * corner case for pdb mapping - revealed a problem with the AlignSeq->Mapping
242    * transform
243    * 
244    */
245   @Test(groups = { "Functional" })
246   public void mapFer1From3W5V() throws Exception
247   {
248     AlignFrame seqf = new FileLoader(false)
249             .LoadFileWaitTillLoaded(
250                     ">FER1_MAIZE/1-150 Ferredoxin-1, chloroplast precursor\nMATVLGSPRAPAFFFSSSSLRAAPAPTAVALPAAKVGIMGRSASSRRRLRAQATYNVKLITPEGEVELQVPD\nDVYILDQAEEDGIDLPYSCRAGSCSSCAGKVVSGSVDQSDQSYLDDGQIADGWVLTCHAYPTSDVVIETHKE\nEELTGA",
251                     DataSourceType.PASTE, FileFormat.Fasta);
252     SequenceI newseq = seqf.getViewport().getAlignment().getSequenceAt(0);
253     StructureSelectionManager ssm = new jalview.structure.StructureSelectionManager();
254     StructureFile pmap = ssm.setMapping(true, new SequenceI[] { newseq },
255             new List[]
256             { Arrays.asList(new String[0]) }, "examples/3W5V.pdb",
257             DataSourceType.FILE);
258     if (pmap == null)
259     {
260       AssertJUnit.fail("Couldn't make a mapping for 3W5V to FER1_MAIZE");
261     }
262   }
263
264   /**
265    * compare reference annotation for imported pdb sequence to identical
266    * seuqence with transferred annotation from mapped pdb file
267    */
268   @Test(groups = { "Functional" })
269   public void compareTransferredToRefPDBAnnot() throws Exception
270   {
271     StructureImportSettings.setShowSeqFeatures(true);
272     AlignFrame ref = new FileLoader(false)
273             .LoadFileWaitTillLoaded("test/jalview/ext/jmol/1QCF.pdb",
274                     DataSourceType.FILE);
275     SequenceI refseq = ref.getViewport().getAlignment().getSequenceAt(0);
276     SequenceI newseq = new Sequence(refseq.getName() + "Copy",
277             refseq.getSequenceAsString());
278     // make it harder by shifting the copy vs the reference
279     newseq.setStart(refseq.getStart() + 25);
280     newseq.setEnd(refseq.getLength() + 25 + refseq.getStart());
281     StructureSelectionManager ssm = new jalview.structure.StructureSelectionManager();
282     ssm.setProcessSecondaryStructure(true);
283     ssm.setAddTempFacAnnot(true);
284     StructureFile pmap = ssm.setMapping(true, new SequenceI[] { newseq },
285             new List[]
286             { new ArrayList() }, "test/jalview/ext/jmol/1QCF.pdb",
287             DataSourceType.FILE);
288     assertTrue(pmap != null);
289     assertEquals("Original and copied sequence of different lengths.",
290             refseq.getLength(), newseq.getLength());
291     assertTrue(refseq.getAnnotation() != null
292             && refseq.getAnnotation().length > 0);
293     assertTrue(newseq.getAnnotation() != null
294             && newseq.getAnnotation().length > 0);
295     for (AlignmentAnnotation oannot : refseq.getAnnotation())
296     {
297       for (AlignmentAnnotation tannot : newseq.getAnnotation(oannot.label))
298       {
299         for (int p = 0, pSize = refseq.getLength(); p < pSize; p++)
300         {
301           Annotation orig = oannot.annotations[p], tran = tannot.annotations[p];
302           assertTrue("Mismatch: coil and non coil site " + p, orig == tran
303                   || orig != null && tran != null);
304           if (tran != null)
305           {
306             assertEquals("Mismatch in secondary structure at site " + p,
307                     tran.secondaryStructure, orig.secondaryStructure);
308           }
309         }
310       }
311     }
312   }
313 }