Skip to content

Commit b66d271

Browse files
committed
Show ligands by proximity in CE-Symm
- SymmetryTools.divideStructure adds ligands that lie close to the repeats, similar to how SubstructureIdentifier handles them - Refactor some shared code into StructureTools and SymmetryTools - Minor bug fix in StructureImpl when dealing with empty structures
1 parent 931c29b commit b66d271

3 files changed

Lines changed: 147 additions & 10 deletions

File tree

biojava-structure/src/main/java/org/biojava/nbio/structure/StructureImpl.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,9 @@ public Chain getChain(String asymId) {
690690
@Override
691691
public Chain getChainByPDB(String chainId)
692692
throws StructureException{
693+
if(nrModels() < 1 ) {
694+
throw new StructureException("No chains are present.");
695+
}
693696
return getChainByPDB(chainId,0);
694697
}
695698

biojava-structure/src/main/java/org/biojava/nbio/structure/StructureTools.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,77 @@ public static List<Group> getLigandsByProximity(Collection<Group> target, Atom[]
504504
}
505505
return ligands;
506506
}
507+
508+
/**
509+
* Expand a set of atoms into all groups from the same structure.
510+
*
511+
* If the structure is set, only the first atom is used (assuming all
512+
* atoms come from the same original structure).
513+
* If the atoms aren't linked to a structure (for instance, for cloned atoms),
514+
* searches all chains of all atoms for groups.
515+
* @param atoms Sample of atoms
516+
* @return All groups from all chains accessible from the input atoms
517+
*/
518+
public static Set<Group> getAllGroupsFromSubset(Atom[] atoms) {
519+
return getAllGroupsFromSubset(atoms,null);
520+
}
521+
/**
522+
* Expand a set of atoms into all groups from the same structure.
523+
*
524+
* If the structure is set, only the first atom is used (assuming all
525+
* atoms come from the same original structure).
526+
* If the atoms aren't linked to a structure (for instance, for cloned atoms),
527+
* searches all chains of all atoms for groups.
528+
* @param atoms Sample of atoms
529+
* @param types Type of groups to return (useful for getting only ligands, for instance).
530+
* Null gets all groups.
531+
* @return All groups from all chains accessible from the input atoms
532+
*/
533+
public static Set<Group> getAllGroupsFromSubset(Atom[] atoms,GroupType types) {
534+
// Get the full structure
535+
Structure s = null;
536+
if (atoms.length > 0) {
537+
Group g = atoms[0].getGroup();
538+
if (g != null) {
539+
Chain c = g.getChain();
540+
if (c != null) {
541+
s = c.getStructure();
542+
}
543+
}
544+
}
545+
// Collect all groups from the structure
546+
Set<Chain> allChains = new HashSet<>();
547+
if( s != null ) {
548+
allChains.addAll(s.getChains());
549+
}
550+
// In case the structure wasn't set, need to use ca chains too
551+
for(Atom a : atoms) {
552+
Group g = a.getGroup();
553+
if(g != null) {
554+
Chain c = g.getChain();
555+
if( c != null ) {
556+
allChains.add(c);
557+
}
558+
}
559+
}
560+
561+
if(allChains.isEmpty() ) {
562+
return Collections.emptySet();
563+
}
564+
565+
// Extract all ligand groups
566+
Set<Group> full = new HashSet<>();
567+
for(Chain c : allChains) {
568+
if(types == null) {
569+
full.addAll(c.getAtomGroups());
570+
} else {
571+
full.addAll(c.getAtomGroups(types));
572+
}
573+
}
574+
575+
return full;
576+
}
577+
507578

508579
/**
509580
* Returns and array of all non-Hydrogen atoms in the given Structure,

biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/utils/SymmetryTools.java

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.biojava.nbio.structure.Chain;
3737
import org.biojava.nbio.structure.ChainImpl;
3838
import org.biojava.nbio.structure.Group;
39+
import org.biojava.nbio.structure.GroupType;
3940
import org.biojava.nbio.structure.Structure;
4041
import org.biojava.nbio.structure.StructureException;
4142
import org.biojava.nbio.structure.StructureIdentifier;
@@ -510,7 +511,8 @@ public static List<Structure> divideStructure(CeSymmResult symmetry)
510511
+ "is not refined, repeats cannot be defined");
511512

512513
int order = symmetry.getMultipleAlignment().size();
513-
Atom[] atoms = StructureTools.cloneAtomArray(symmetry.getAtoms());
514+
Atom[] atoms = symmetry.getAtoms();
515+
Set<Group> allGroups = StructureTools.getAllGroupsFromSubset(atoms, GroupType.HETATM);
514516
List<StructureIdentifier> repeatsId = symmetry.getRepeatsID();
515517
List<Structure> repeats = new ArrayList<Structure>(order);
516518

@@ -523,24 +525,85 @@ public static List<Structure> divideStructure(CeSymmResult symmetry)
523525
Block align = symmetry.getMultipleAlignment().getBlock(0);
524526

525527
// Get the start and end of the repeat
528+
// Repeats are always sequential blocks
526529
int res1 = align.getStartResidue(i);
527530
int res2 = align.getFinalResidue(i);
531+
532+
// All atoms from the repeat, used for ligand search
533+
// AA have an average of 8.45 atoms, so guess capacity with that
534+
List<Atom> repeat = new ArrayList<>(Math.max(9*(res2-res1+1),9));
535+
// speedy chain lookup
536+
Chain prevChain = null;
537+
for(int k=res1;k<=res2; k++) {
538+
Group g = atoms[k].getGroup();
539+
prevChain = addGroupToStructure(s, g, prevChain,true);
540+
repeat.addAll(g.getAtoms());
541+
}
528542

529-
Atom[] repeat = Arrays.copyOfRange(atoms, res1, res2 + 1);
530-
531-
Chain newCh = new ChainImpl();
532-
newCh.setId(repeat[0].getGroup().getChainId());
533-
534-
for (int k = 0; k < repeat.length; k++) {
535-
Group g = (Group) repeat[k].getGroup().clone();
536-
newCh.addGroup(g);
543+
544+
List<Group> ligands = StructureTools.getLigandsByProximity(
545+
allGroups,
546+
repeat.toArray(new Atom[repeat.size()]),
547+
StructureTools.DEFAULT_LIGAND_PROXIMITY_CUTOFF);
548+
549+
logger.warn("Adding {} ligands to {}",ligands.size(), symmetry.getMultipleAlignment().getStructureIdentifier(i));
550+
for( Group ligand : ligands) {
551+
prevChain = addGroupToStructure(s, ligand, prevChain,true);
537552
}
538-
s.addChain(newCh);
553+
539554
repeats.add(s);
540555
}
541556
return repeats;
542557
}
543558

559+
/**
560+
* Adds a particular group to a structure. A new chain will be created if necessary.
561+
*
562+
* <p>When adding multiple groups, pass the return value of one call as the
563+
* chainGuess parameter of the next call for efficiency.
564+
* <pre>
565+
* Chain guess = null;
566+
* for(Group g : groups) {
567+
* guess = addGroupToStructure(s, g, guess );
568+
* }
569+
* </pre>
570+
* @param s structure to receive the group
571+
* @param g group to add
572+
* @param chainGuess (optional) If not null, should be a chain from s. Used
573+
* to improve performance when adding many groups from the same chain
574+
* @param clone Indicates whether the input group should be cloned before
575+
* being added to the new chain
576+
* @return the chain g was added to
577+
*/
578+
public static Chain addGroupToStructure(Structure s, Group g, Chain chainGuess, boolean clone ) {
579+
// Find or create the chain
580+
String chainId = g.getChainId();
581+
assert !chainId.isEmpty();
582+
Chain chain;
583+
if(chainGuess != null && chainGuess.getId() == chainId) {
584+
// previously guessed chain
585+
chain = chainGuess;
586+
} else {
587+
// Try to guess
588+
chain = s.getChain(chainId);
589+
if(chain == null) {
590+
// no chain found
591+
chain = new ChainImpl();
592+
chain.setId(chainId);
593+
chain.setName(g.getChain().getName());
594+
s.addChain(chain);
595+
}
596+
}
597+
598+
// Add cloned group
599+
if(clone) {
600+
g = (Group)g.clone();
601+
}
602+
chain.addGroup(g);
603+
604+
return chain;
605+
}
606+
544607
/**
545608
* Method that converts a repeats symmetric alignment into an alignment of
546609
* whole structures.

0 commit comments

Comments
 (0)