/// <summary> /// Outputs a structure that is a fusion of the chains indicated by the sequence alignments. In the case of a cycle (wherein the last structure /// is a copy of the first at a different position), only one set of chains for the two endpoint structures is included. /// </summary> /// <param name="structures">structures to fuse</param> /// <param name="alignments">the sequence positions at which to fuse</param> /// <param name="ncDirections">the directionality of the alignment: true -> the first structure is N-terminal, false -> C-terminal</param> /// <param name="cycle">whether the structure forms a cycle, in which case the first and last fusions affect both ends</param> /// <param name="partialChain">a partial chain has been created due to cyclization whose entirety will be restored only through asu pattering</param> /// <returns></returns> public static IStructure GetStructure(IStructure[] structures, SequenceAlignment[] alignments, bool[] ncDirections, bool cycle, out IChain partialChain) { partialChain = null; // Note: The fusion product's middle residue from the alignment range comes from the N-terminal chain, i.e the N-term range is [0, Middle] and C-term is [Middle + 1, Len-1] Trace.Assert(structures != null && alignments != null && ncDirections != null); Trace.Assert(structures.Length > 0); Trace.Assert(structures.Length == alignments.Length + 1); Trace.Assert(alignments.Length == ncDirections.Length); // Data for the cyclization case - the chain that the final fusion should be joined with fused back onto fusion products of the first chain should be tracked so that the last chain can be fused back onto that chain IChain cycleDoubleFusedChain = null; // Track what chain the first fusion chain ends up in bool isCycleDoubleFused = cycle && alignments.First().ChainIndex1 == alignments.Last().ChainIndex2; // Whether the first chain is fused both to the next chain and to the last/prior (wrap-around) chain Matrix cycleAlignment = isCycleDoubleFused? Rmsd.GetRmsdTransform(structures.First()[0][0], structures.Last()[0][0]) : Matrix.Identity; // Mark which aas to remove without actually removing them, so that their indices remain unchanged while // those to be removed are being computed Selection remove = new Selection(); for (int alignmentIndex = 0; alignmentIndex < alignments.Length; alignmentIndex++) { bool ncDirection = ncDirections[alignmentIndex]; SequenceAlignment alignment = alignments[alignmentIndex]; SequenceAlignment ncAlignment = ncDirection? alignment : SequenceAlignment.Reversed(alignment); // N as chain1 IStructure structureN = ncDirection ? structures[alignmentIndex] : structures[alignmentIndex + 1]; IStructure structureC = ncDirection ? structures[alignmentIndex + 1] : structures[alignmentIndex]; IChain chainN = structureN[ncAlignment.ChainIndex1]; IChain chainC = structureC[ncAlignment.ChainIndex2]; remove.Aas.UnionWith(chainN[ncAlignment.Range1.Middle + 1, chainN.Count - 1]); remove.Aas.UnionWith(chainC[0, ncAlignment.Range2.Middle]); // If a cycle exists, then for each chain in the first and last structure (which are identical, modulo a transform), only one copy should be // preserved. If that copy is fused on both sides, then it must be trimmed according to both the first and final sequence alignments. In such a // case, remove the entire last structure. // Cycle and fusion is to the same chain on both sides: // -> remove all of the second copy // -> transform the second copy neighbor chain onto the first // Cycle and fusion is to separate chains on either side: // -> remove all of the second copy except for the fused chain // -> remove from the first copy the chain that was fused on the second copy if (cycle && alignmentIndex == alignments.Length - 1) { if (isCycleDoubleFused) { // Mark all chains from second copy for deletion foreach (IChain chain in structures[structures.Length - 1]) { remove.Aas.UnionWith(chain); } // Mark the first structure residues for deletion on one side of the fusion point IChain chain0 = structures[0][alignment.ChainIndex2]; if (ncDirection) { remove.Aas.UnionWith(chain0[0, alignment.Range2.Middle]); } else { remove.Aas.UnionWith(chain0[alignment.Range2.Middle + 1, chain0.Count - 1]); } } else { // Mark all chains from the second copy for deletion, except the one being fused IChain keep = structures[alignmentIndex + 1][alignment.ChainIndex2]; foreach (IChain chain in structures[alignmentIndex + 1]) { if (chain != keep) { remove.Aas.UnionWith(chain); } } // For the one being fused, remove that index from the first structure remove.Aas.UnionWith(structures[0][alignment.ChainIndex2]); } } } // Remove them for (int alignmentIndex = 0; alignmentIndex < alignments.Length; alignmentIndex++) { SequenceAlignment alignment = alignments[alignmentIndex]; IStructure structure1 = structures[alignmentIndex]; IStructure structure2 = structures[alignmentIndex + 1]; IChain chain1 = structure1[alignment.ChainIndex1]; IChain chain2 = structure2[alignment.ChainIndex2]; for (int i = chain1.Count - 1; i >= 0; i--) { IAa aa = chain1[i]; if (remove.Aas.Contains(aa)) { Matrix desired = aa.TotalTransform; chain1.RemoveAt(i); aa.Transform(desired * Matrix.Invert(aa.TotalTransform)); #if DEBUG_TRANSFORMS Debug.Assert((desired - aa.TotalTransform).Translation.Length() < 0.1); #endif } } for (int i = chain2.Count - 1; i >= 0; i--) { IAa aa = chain2[i]; if (remove.Aas.Contains(aa)) { Matrix desired = aa.TotalTransform; chain2.RemoveAt(i); aa.Transform(desired * Matrix.Invert(aa.TotalTransform)); #if DEBUG_TRANSFORMS Debug.Assert((desired - aa.TotalTransform).Translation.Length() < 0.1); #endif } } if (cycle && alignmentIndex == 0) { foreach (IChain chain in structure1) { for (int i = chain.Count - 1; i >= 0; i--) { IAa aa = chain[i]; if (remove.Aas.Contains(aa)) { Matrix desired = aa.TotalTransform; chain.RemoveAt(i); aa.Transform(desired * Matrix.Invert(aa.TotalTransform)); #if DEBUG_TRANSFORMS Debug.Assert((desired - aa.TotalTransform).Translation.Length() < 0.1); #endif } } } } if (cycle && alignmentIndex == alignments.Length - 1) { foreach (IChain chain in structure2) { for (int i = chain.Count - 1; i >= 0; i--) { IAa aa = chain[i]; if (remove.Aas.Contains(aa)) { Matrix desired = aa.TotalTransform; chain.RemoveAt(i); aa.Transform(desired * Matrix.Invert(aa.TotalTransform)); #if DEBUG_TRANSFORMS Debug.Assert((desired - aa.TotalTransform).Translation.Length() < 0.1); #endif } } } } } // Join the chains and allocate the result to the second structure so as to preserve the indexes for the next fusion step for (int alignmentIndex = 0; alignmentIndex < alignments.Length; alignmentIndex++) { bool ncDirection = ncDirections[alignmentIndex]; SequenceAlignment alignment = alignments[alignmentIndex]; IStructure structure1 = structures[alignmentIndex]; IStructure structure2 = structures[alignmentIndex + 1]; IChain chain1 = structure1[alignment.ChainIndex1]; IChain chain2 = structure2[alignment.ChainIndex2]; if (ncDirection) { #if DEBUG_MIRRORS foreach (IAa aa2 in chain2.ToArray()) { chain1.AddInPlace(aa2); } #else chain1.AddArraySourceInPlace(chain2); #endif structure2[alignment.ChainIndex2, true] = chain1; } else { #if DEBUG_MIRRORS foreach (IAa aa1 in chain1.ToArray()) { chain2.AddInPlace(aa1); } #else chain2.AddArraySourceInPlace(chain1); #endif } // Track which chain contains the first chain in the cycle so that the final chain in the cycle can fuse to it if (isCycleDoubleFused && (alignmentIndex == 0 || chain1 == cycleDoubleFusedChain || chain2 == cycleDoubleFusedChain)) { cycleDoubleFusedChain = ncDirection? chain1 : chain2; } if (isCycleDoubleFused && alignmentIndex == alignments.Length - 1) { // If it's a cycle on the same chain, move the chain back to where it fuses to the first structure IChain combined = ncDirection ? chain1 : chain2; combined.Transform(cycleAlignment); if (combined == cycleDoubleFusedChain) { // If the structure[0] fusion chain has been fused all the way through, there is no need to move the // current chain to meet the first, since they're already joined partialChain = cycleDoubleFusedChain; } else { if (ncDirection) { combined.AddArraySourceInPlace(cycleDoubleFusedChain); IStructure cycleDoubleFusedParent = (IStructure)cycleDoubleFusedChain.Parent; } else { cycleDoubleFusedChain.AddArraySourceInPlace(combined); } } } } // Add all unique chains to a new structure Structure total = new Structure(); structures.SelectMany(s => s).Distinct().Where(c => c.Count > 0).ToList().ForEach(c => total.AddInPlace(c)); return(total); }