diff --git a/biojava-aa-prop/pom.xml b/biojava-aa-prop/pom.xml index 5c35a618b8..89ef2154ef 100644 --- a/biojava-aa-prop/pom.xml +++ b/biojava-aa-prop/pom.xml @@ -2,7 +2,7 @@ biojava org.biojava - 4.2.5 + 4.2.6 4.0.0 biojava-aa-prop @@ -70,12 +70,12 @@ org.biojava biojava-core - 4.2.5 + 4.2.6 org.biojava biojava-structure - 4.2.5 + 4.2.6 diff --git a/biojava-alignment/pom.xml b/biojava-alignment/pom.xml index d42f48f857..41389cdd4e 100644 --- a/biojava-alignment/pom.xml +++ b/biojava-alignment/pom.xml @@ -4,7 +4,7 @@ biojava org.biojava - 4.2.5 + 4.2.6 biojava-alignment biojava-alignment @@ -46,7 +46,7 @@ org.biojava biojava-core - 4.2.5 + 4.2.6 compile @@ -74,7 +74,7 @@ org.biojava biojava-phylo - 4.2.5 + 4.2.6 diff --git a/biojava-core/pom.xml b/biojava-core/pom.xml index 80cab2a58a..e3b0e9a7a1 100644 --- a/biojava-core/pom.xml +++ b/biojava-core/pom.xml @@ -3,7 +3,7 @@ biojava org.biojava - 4.2.5 + 4.2.6 4.0.0 biojava-core diff --git a/biojava-genome/pom.xml b/biojava-genome/pom.xml index 93ca9d0423..b382489846 100644 --- a/biojava-genome/pom.xml +++ b/biojava-genome/pom.xml @@ -3,7 +3,7 @@ biojava org.biojava - 4.2.5 + 4.2.6 4.0.0 biojava-genome @@ -85,13 +85,13 @@ org.biojava biojava-core - 4.2.5 + 4.2.6 compile org.biojava biojava-alignment - 4.2.5 + 4.2.6 compile diff --git a/biojava-integrationtest/pom.xml b/biojava-integrationtest/pom.xml index 82703c0da0..dca85357b3 100644 --- a/biojava-integrationtest/pom.xml +++ b/biojava-integrationtest/pom.xml @@ -4,7 +4,7 @@ biojava org.biojava - 4.2.5 + 4.2.6 biojava-integrationtest jar @@ -32,7 +32,7 @@ org.biojava biojava-structure - 4.2.5 + 4.2.6 diff --git a/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/StructureToolsTest.java b/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/StructureToolsTest.java index de234a7159..7faf37e823 100644 --- a/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/StructureToolsTest.java +++ b/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/StructureToolsTest.java @@ -22,8 +22,6 @@ */ package org.biojava.nbio.structure.test; -import junit.framework.TestCase; - import org.biojava.nbio.structure.*; import org.biojava.nbio.structure.align.util.AtomCache; import org.biojava.nbio.structure.io.FileParsingParameters; @@ -31,18 +29,21 @@ import org.biojava.nbio.structure.io.mmcif.ChemCompGroupFactory; import org.biojava.nbio.structure.io.mmcif.ChemCompProvider; import org.biojava.nbio.structure.io.mmcif.DownloadChemCompProvider; +import org.junit.Before; +import org.junit.Test; import java.io.IOException; import java.io.InputStream; import static org.junit.Assume.assumeNoException; +import static org.junit.Assert.*; -public class StructureToolsTest extends TestCase { +public class StructureToolsTest { Structure structure, structure2, structure3, structure4; - @Override - protected void setUp() throws IOException + @Before + public void setUp() throws IOException { InputStream inStream = this.getClass().getResourceAsStream("/5pti.pdb"); assertNotNull(inStream); @@ -85,12 +86,13 @@ protected void setUp() throws IOException inStream.close(); } - + @Test public void testGetCAAtoms(){ Atom[] cas = StructureTools.getRepresentativeAtomArray(structure); assertEquals("did not find the expected number of Atoms (58), but got " + cas.length,58,cas.length); } + @Test public void testGetAtomsConsistency() throws IOException, StructureException{ //Save the existing ChemCompProvider @@ -120,11 +122,13 @@ public void testGetAtomsConsistency() throws IOException, StructureException{ ChemCompGroupFactory.setChemCompProvider(provider); } + @Test public void testGetNrAtoms(){ int length = StructureTools.getNrAtoms(structure); assertEquals("did not find the expected number of Atoms (1087), but got " + length,1087,length); } + @Test @SuppressWarnings("deprecation") public void testGetSubRanges() throws StructureException { String range; @@ -239,6 +243,7 @@ public void testGetSubRanges() throws StructureException { } catch(IllegalArgumentException ex) {} //expected } + @Test public void testRevisedConvention() throws IOException, StructureException{ AtomCache cache = new AtomCache(); @@ -319,6 +324,7 @@ public void testRevisedConvention() throws IOException, StructureException{ * Test some subranges that we used to have problems with * @throws StructureException */ + @Test @SuppressWarnings("deprecation") public void testGetSubRangesExtended() throws StructureException { String range; @@ -380,6 +386,7 @@ public void testGetSubRangesExtended() throws StructureException { * Test insertion codes * @throws StructureException */ + @Test @SuppressWarnings("deprecation") public void testGetSubRangesInsertionCodes() throws StructureException { String range; @@ -437,6 +444,7 @@ public void testGroupsWithinShell() { //TODO } + @Test public void testCAmmCIF() throws StructureException { //Save the existing ChemCompProvider @@ -473,5 +481,36 @@ public void testCAmmCIF() throws StructureException { ChemCompGroupFactory.setChemCompProvider(provider); } + + @Test + public void testGetRepresentativeAtomsProtein() throws StructureException, IOException { + Structure s = StructureIO.getStructure("1smt"); + Chain c = s.getChain(0); + Atom[] atoms = StructureTools.getRepresentativeAtomArray(c); + assertEquals(98,atoms.length); + + Chain clonedChain = (Chain)c.clone(); + atoms = StructureTools.getRepresentativeAtomArray(clonedChain); + assertEquals(98,atoms.length); + } + /** + * See https://github.com/biojava/biojava/issues/631 + * @throws StructureException + * @throws IOException + */ + @Test + public void testGetRepresentativeAtomsDna() throws StructureException, IOException { + + Structure s = StructureIO.getStructure("2pvi"); + Chain c = s.getChainByPDB("C"); + Atom[] atoms = StructureTools.getRepresentativeAtomArray(c); // chain C (1st nucleotide chain) + // actually it should be 13, but at the moment one of the nucleotides is not caught correctly because it's non-standard + assertEquals(12,atoms.length); + + Chain clonedChain = (Chain)c.clone(); + atoms = StructureTools.getRepresentativeAtomArray(clonedChain); // chain C (1st nucleotide chain) + assertEquals(12,atoms.length); + + } } diff --git a/biojava-modfinder/pom.xml b/biojava-modfinder/pom.xml index a1f7b57970..e969b11f09 100644 --- a/biojava-modfinder/pom.xml +++ b/biojava-modfinder/pom.xml @@ -4,7 +4,7 @@ biojava org.biojava - 4.2.5 + 4.2.6 biojava-modfinder biojava-modfinder @@ -31,7 +31,7 @@ org.biojava biojava-structure - 4.2.5 + 4.2.6 jar compile diff --git a/biojava-ontology/pom.xml b/biojava-ontology/pom.xml index 441b3bded0..305039b548 100644 --- a/biojava-ontology/pom.xml +++ b/biojava-ontology/pom.xml @@ -4,7 +4,7 @@ org.biojava biojava - 4.2.5 + 4.2.6 biojava-ontology diff --git a/biojava-phylo/pom.xml b/biojava-phylo/pom.xml index affe865373..bc55ac6307 100644 --- a/biojava-phylo/pom.xml +++ b/biojava-phylo/pom.xml @@ -3,7 +3,7 @@ biojava org.biojava - 4.2.5 + 4.2.6 4.0.0 biojava-phylo @@ -44,7 +44,7 @@ org.biojava biojava-core - 4.2.5 + 4.2.6 compile diff --git a/biojava-protein-disorder/pom.xml b/biojava-protein-disorder/pom.xml index 9e12925676..b95632bc2c 100644 --- a/biojava-protein-disorder/pom.xml +++ b/biojava-protein-disorder/pom.xml @@ -3,7 +3,7 @@ biojava org.biojava - 4.2.5 + 4.2.6 biojava-protein-disorder jar @@ -63,7 +63,7 @@ org.biojava biojava-core - 4.2.5 + 4.2.6 diff --git a/biojava-sequencing/pom.xml b/biojava-sequencing/pom.xml index eb65c46b23..0990a8e9b2 100644 --- a/biojava-sequencing/pom.xml +++ b/biojava-sequencing/pom.xml @@ -3,7 +3,7 @@ biojava org.biojava - 4.2.5 + 4.2.6 4.0.0 biojava-sequencing @@ -47,7 +47,7 @@ org.biojava biojava-core - 4.2.5 + 4.2.6 compile diff --git a/biojava-structure-gui/pom.xml b/biojava-structure-gui/pom.xml index d5b18c9e14..f96aad2a97 100644 --- a/biojava-structure-gui/pom.xml +++ b/biojava-structure-gui/pom.xml @@ -3,7 +3,7 @@ biojava org.biojava - 4.2.5 + 4.2.6 4.0.0 biojava-structure-gui @@ -25,13 +25,13 @@ org.biojava biojava-structure - 4.2.5 + 4.2.6 compile org.biojava biojava-core - 4.2.5 + 4.2.6 compile diff --git a/biojava-structure/pom.xml b/biojava-structure/pom.xml index 0be0265a08..ac6de4757a 100644 --- a/biojava-structure/pom.xml +++ b/biojava-structure/pom.xml @@ -4,7 +4,7 @@ biojava org.biojava - 4.2.5 + 4.2.6 biojava-structure biojava-structure @@ -22,13 +22,13 @@ org.biojava biojava-alignment - 4.2.5 + 4.2.6 compile org.biojava biojava-core - 4.2.5 + 4.2.6 compile diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/Calc.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/Calc.java index dee571a3cb..d09252f16c 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/Calc.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/Calc.java @@ -699,12 +699,17 @@ public static final void shift(Group group , Atom a ){ - /** Returns the center of mass of the set of atoms. + /** + * Returns the center of mass of the set of atoms. * @param atomSet a set of Atoms * @return an Atom representing the Centroid of the set of atoms */ public static final Atom getCentroid(Atom[] atomSet){ + // if we don't catch this case, the centroid returned is (NaN,NaN,NaN), which can cause lots of problems down the line + if (atomSet.length==0) + throw new IllegalArgumentException("Atom array has length 0, can't calculate centroid!"); + double[] coords = new double[3]; coords[0] = 0; diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/NucleotideImpl.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/NucleotideImpl.java index 7cc5a9400b..75fa3b8da6 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/NucleotideImpl.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/NucleotideImpl.java @@ -93,5 +93,36 @@ public Atom getP() { } - // no need to implement clone here, it's already in super class + // note we need to implement a clone here, despite there's one in super class already, + // that's due to issue https://github.com/biojava/biojava/issues/631 - JD 2017-01-21 + @Override + public Object clone() { + + NucleotideImpl n = new NucleotideImpl(); + n.setPDBFlag(has3D()); + n.setResidueNumber(getResidueNumber()); + + n.setPDBName(getPDBName()); + + // copy the atoms + for (Atom atom1 : atoms) { + Atom atom = (Atom) atom1.clone(); + n.addAtom(atom); + atom.setGroup(n); + } + + // copying the alt loc groups if present, otherwise they stay null + if (getAltLocs()!=null && !getAltLocs().isEmpty()) { + for (Group altLocGroup:this.getAltLocs()) { + Group nAltLocGroup = (Group)altLocGroup.clone(); + n.addAltLoc(nAltLocGroup); + } + } + + if (chemComp!=null) + n.setChemComp(chemComp); + + + return n; + } } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/asa/AsaCalculator.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/asa/AsaCalculator.java index c5d39662cd..b5c66e6c59 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/asa/AsaCalculator.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/asa/AsaCalculator.java @@ -411,7 +411,7 @@ else if (atomCode.equals("CA") || atomCode.equals("CB") || else if (atomCode.equals("CG")) return TETRAHEDRAL_CARBON_VDW; default: - logger.warn("Unexpected carbon atom "+atomCode+" for aminoacid "+aa+", assigning its standard vdw radius"); + logger.info("Unexpected carbon atom "+atomCode+" for aminoacid "+aa+", assigning its standard vdw radius"); return Element.C.getVDWRadius(); } } @@ -419,12 +419,8 @@ else if (atomCode.equals("CA") || atomCode.equals("CB") || // not any of the expected atoms } else { // non standard aas, (e.g. MSE, LLP) will always have this problem, - // thus we log at info level for them, at warn for others - if (amino.getChemComp()!=null && amino.getChemComp().isStandard()) { - logger.warn("Unexpected atom "+atomCode+" for aminoacid "+aa+ " ("+amino.getPDBName()+"), assigning its standard vdw radius"); - } else { - logger.info("Unexpected atom "+atomCode+" for aminoacid "+aa+ " ("+amino.getPDBName()+"), assigning its standard vdw radius"); - } + logger.info("Unexpected atom "+atomCode+" for aminoacid "+aa+ " ("+amino.getPDBName()+"), assigning its standard vdw radius"); + return atom.getElement().getVDWRadius(); } @@ -449,7 +445,7 @@ private static double getRadiusForNucl(NucleotideImpl nuc, Atom atom) { if (atom.getElement()==Element.O) return OXIGEN_VDW; - logger.warn("Unexpected atom "+atom.getName()+" for nucleotide "+nuc.getPDBName()+", assigning its standard vdw radius"); + logger.info("Unexpected atom "+atom.getName()+" for nucleotide "+nuc.getPDBName()+", assigning its standard vdw radius"); return atom.getElement().getVDWRadius(); } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/HelixAxisAligner.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/HelixAxisAligner.java index 6685e6a3ce..8e69ea83b8 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/HelixAxisAligner.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/HelixAxisAligner.java @@ -22,11 +22,16 @@ import org.biojava.nbio.structure.symmetry.geometry.MomentsOfInertia; import org.biojava.nbio.structure.symmetry.geometry.SuperPosition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.vecmath.*; import java.util.*; public class HelixAxisAligner extends AxisAligner { + + private static final Logger LOGGER = LoggerFactory.getLogger(HelixAxisAligner.class); + private static final Vector3d Y_AXIS = new Vector3d(0,1,0); private static final Vector3d Z_AXIS = new Vector3d(0,0,1); @@ -480,7 +485,7 @@ private Matrix4d alignAxes(Vector3d[] axisVectors, Vector3d[] referenceVectors) ref[0] = new Point3d(referenceVectors[0]); ref[1] = new Point3d(referenceVectors[1]); if (SuperPosition.rmsd(axes, ref) > 0.1) { - System.out.println("Warning: AxisTransformation: axes alignment is off. RMSD: " + SuperPosition.rmsd(axes, ref)); + LOGGER.info("AxisTransformation: axes alignment is off. RMSD: " + SuperPosition.rmsd(axes, ref)); } return m2; @@ -600,7 +605,7 @@ private void calcReferenceVector() { referenceVector = getReferenceAxisCylic(); if (referenceVector == null) { - System.err.println("Warning: no reference vector found. Using y-axis."); + LOGGER.warn("no reference vector found. Using y-axis."); referenceVector = new Vector3d(Y_AXIS); } // make sure reference vector is perpendicular principal roation vector @@ -616,7 +621,7 @@ private Vector3d orthogonalize(Vector3d vector1, Vector3d vector2) { vector2.negate(); } if (Math.abs(dot) < 0.00001) { - System.out.println("HelixAxisAligner: Warning: reference axis parallel"); + LOGGER.info("HelixAxisAligner: reference axis parallel"); } vector2.cross(vector1, vector2); // System.out.println("Intermed. refVector: " + vector2); diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/PermutationGroup.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/PermutationGroup.java index 675e4facb0..19f10ba72e 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/PermutationGroup.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/PermutationGroup.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -30,7 +31,7 @@ * * @author Peter */ -public class PermutationGroup { +public class PermutationGroup implements Iterable> { List> permutations = new ArrayList>(); public void addPermutation(List permutation) { @@ -139,4 +140,9 @@ public String getGroupTable() { public int hashCode() { return getGroupTable().hashCode(); } + + @Override + public Iterator> iterator() { + return permutations.iterator(); + } } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationAxisAligner.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationAxisAligner.java index 12d5db93b6..1a5bf33f32 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationAxisAligner.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationAxisAligner.java @@ -22,11 +22,16 @@ import org.biojava.nbio.structure.symmetry.geometry.MomentsOfInertia; import org.biojava.nbio.structure.symmetry.geometry.SuperPosition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.vecmath.*; import java.util.*; public class RotationAxisAligner extends AxisAligner{ + + private static final Logger LOGGER = LoggerFactory.getLogger(RotationAxisAligner.class); + private static final Vector3d X_AXIS = new Vector3d(1,0,0); private static final Vector3d Y_AXIS = new Vector3d(0,1,0); private static final Vector3d Z_AXIS = new Vector3d(0,0,1); @@ -320,7 +325,7 @@ private List alignWithReferenceAxis(List orbit) { } Collections.reverse(orbit.subList(1, orbit.size())); if (orbit.get(1) != index2) { - System.err.println("Warning: alignWithReferenceAxis failed"); + LOGGER.warn("alignWithReferenceAxis failed"); } // System.out.println("Orbit2: " + orbit); return orbit; @@ -459,7 +464,7 @@ private Matrix4d alignAxes(Vector3d[] axisVectors, Vector3d[] referenceVectors) ref[0] = new Point3d(referenceVectors[0]); ref[1] = new Point3d(referenceVectors[1]); if (SuperPosition.rmsd(axes, ref) > 0.1) { - System.out.println("Warning: AxisTransformation: axes alignment is off. RMSD: " + SuperPosition.rmsd(axes, ref)); + LOGGER.info("AxisTransformation: axes alignment is off. RMSD: " + SuperPosition.rmsd(axes, ref)); } return m2; @@ -632,7 +637,7 @@ private List deconvolute(List orbit) { int index = p0.indexOf(current); int next = p1.get(index); if (!orbit.contains(next)) { - System.err.println("deconvolute: inconsistency in permuation. Returning original order"); + LOGGER.warn("deconvolute: inconsistency in permuation. Returning original order"); return orbit; } inRotationOrder.add(next); @@ -675,7 +680,7 @@ private void calcReferenceVector() { } if (referenceVector == null) { - System.err.println("Warning: no reference vector found. Using y-axis."); + LOGGER.warn("no reference vector found. Using y-axis."); referenceVector = new Vector3d(Y_AXIS); } // make sure reference vector is perpendicular principal roation vector diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationGroup.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationGroup.java index ead65b5b20..c605aa29e7 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationGroup.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationGroup.java @@ -27,13 +27,14 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.Iterator; import java.util.List; /** * * @author Peter */ -public class RotationGroup { +public class RotationGroup implements Iterable { private List rotations = new ArrayList(); private int principalAxisIndex = 0; private int higherOrderRotationAxis = 0; @@ -369,4 +370,9 @@ public int compare(Rotation o1, Rotation o2) { } }); } + + @Override + public Iterator iterator() { + return rotations.iterator(); + } } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationSolver.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationSolver.java index 8802ef3bbc..bbc0d4cf9e 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationSolver.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationSolver.java @@ -21,19 +21,22 @@ package org.biojava.nbio.structure.symmetry.core; -import org.biojava.nbio.structure.symmetry.geometry.DistanceBox; -import org.biojava.nbio.structure.symmetry.geometry.MomentsOfInertia; -import org.biojava.nbio.structure.symmetry.geometry.SphereSampler; -import org.biojava.nbio.structure.symmetry.geometry.SuperPosition; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import javax.vecmath.AxisAngle4d; import javax.vecmath.Matrix4d; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; + +import org.biojava.nbio.structure.symmetry.geometry.DistanceBox; +import org.biojava.nbio.structure.symmetry.geometry.MomentsOfInertia; +import org.biojava.nbio.structure.symmetry.geometry.SphereSampler; +import org.biojava.nbio.structure.symmetry.geometry.SuperPosition; /** @@ -50,7 +53,8 @@ public class RotationSolver implements QuatSymmetrySolver { private Matrix4d centroidInverse = new Matrix4d(); private Point3d[] originalCoords = null; private Point3d[] transformedCoords = null; - private Set> hashCodes = new HashSet>(); + // Cache whether a permutation is invalid (null) vs has been added to rotations + private Map,Rotation> evaluatedPermutations = new HashMap<>(); private RotationGroup rotations = new RotationGroup(); @@ -95,9 +99,14 @@ private void solve() { List angles = getAngles(); - for (int i = 0; i < sphereCount; i++) { + for (int i = 0; i < sphereCount; i++) { + // Sampled orientation + //TODO The SphereSampler samples 4D orientation space. We really + // only need to sample 3D unit vectors, since we use limited + // angles. -SB SphereSampler.getAxisAngle(i, sphereAngle); + // Each valid rotation angle for (double angle : angles) { // apply rotation sphereAngle.angle = angle; @@ -113,14 +122,14 @@ private void solve() { List permutation = getPermutation(); // System.out.println("Rotation Solver: permutation: " + i + ": " + permutation); - boolean isValidPermuation = isValidPermutation(permutation); - if (! isValidPermuation) { - continue; + // check if novel + if ( evaluatedPermutations.containsKey(permutation)) { + continue; //either invalid or already added } - boolean newPermutation = evaluatePermutation(permutation); - if (newPermutation) { - completeRotationGroup(); + Rotation newPermutation = isValidPermutation(permutation); + if (newPermutation != null) { + completeRotationGroup(newPermutation); } // check if all symmetry operations have been found. @@ -131,33 +140,84 @@ private void solve() { } } - private void completeRotationGroup() { + /** + * Combine current rotations to make all possible permutations. + * If these are all valid, add them to the rotations + * @param additionalRots Additional rotations we are considering adding to this.rotations + * @return whether the rotations were valid and added + */ + private boolean completeRotationGroup(Rotation... additionalRots) { PermutationGroup g = new PermutationGroup(); - for (int i = 0; i < rotations.getOrder(); i++) { - Rotation s = rotations.getRotation(i); + for (Rotation s : rotations) { g.addPermutation(s.getPermutation()); } + for( Rotation s : additionalRots) { + g.addPermutation(s.getPermutation()); + // inputs should not have been added already + assert evaluatedPermutations.get(s.getPermutation()) == null; + } g.completeGroup(); // the group is complete, nothing to do - if (g.getOrder() == rotations.getOrder()) { - return; + if (g.getOrder() == rotations.getOrder()+additionalRots.length) { + for( Rotation s : additionalRots) { + addRotation(s); + } + return true; } // try to complete the group - for (int i = 0; i < g.getOrder(); i++) { - List permutation = g.getPermutation(i); - - boolean isValidPermutation = isValidPermutation(permutation); - if (isValidPermutation) { + List newRots = new ArrayList<>(g.getOrder()); + // First, quick check for whether they're allowed + for (List permutation : g) { + if( evaluatedPermutations.containsKey(permutation)) { + Rotation rot = evaluatedPermutations.get(permutation); + if( rot == null ) { + return false; + } + } else { + if( ! isAllowedPermutation(permutation)) { + return false; + } + } + } + // Slower check including the superpositions + for (List permutation : g) { + Rotation rot; + if( evaluatedPermutations.containsKey(permutation)) { + rot = evaluatedPermutations.get(permutation); + } else { + rot = isValidPermutation(permutation); + } - // perform permutation of subunits - evaluatePermutation(permutation); + if( rot == null ) { + // if any induced rotation is invalid, abort + return false; + } + if(!evaluatedPermutations.containsKey(permutation)){ //novel + newRots.add(rot); } } + // Add rotations + for( Rotation rot : newRots) { + addRotation(rot); + } + return true; } - private boolean evaluatePermutation(List permutation) { + private void addRotation(Rotation rot) { + evaluatedPermutations.put(rot.getPermutation(),rot); + rotations.addRotation(rot); + } + + /** + * Superimpose subunits based on the given permutation. Then check whether + * the superposition passes RMSD thresholds and create a Rotation to + * represent it if so. + * @param permutation A list specifying which subunits should be aligned by the current transformation + * @return A Rotation representing the permutation, or null if the superposition did not meet thresholds. + */ + private Rotation superimposePermutation(List permutation) { // permutate subunits for (int j = 0, n = subunits.getSubunitCount(); j < n; j++) { transformedCoords[j].set(originalCoords[permutation.get(j)]); @@ -176,17 +236,20 @@ private boolean evaluatePermutation(List permutation) { // evaluate superposition of CA traces QuatSymmetryScores scores = QuatSuperpositionScorer.calcScores(subunits, transformation, permutation); if (scores.getRmsd() < 0.0 || scores.getRmsd() > parameters.getRmsdThreshold()) { - return false; + return null; } scores.setRmsdCenters(subunitRmsd); Rotation symmetryOperation = createSymmetryOperation(permutation, transformation, axisAngle, fold, scores); - rotations.addRotation(symmetryOperation); - return true; + return symmetryOperation; } - return false; + return null; } + /** + * Get valid rotation angles given the number of subunits + * @return The rotation angle corresponding to each fold of {@link Subunits#getFolds()} + */ private List getAngles() { int n = subunits.getSubunitCount(); // for spherical symmetric cases, n cannot be higher than 60 @@ -210,44 +273,53 @@ private boolean isSpherical() { return m.getSymmetryClass(0.05) == MomentsOfInertia.SymmetryClass.SYMMETRIC; } - private boolean isValidPermutation(List permutation) { - if (permutation.size() == 0) { - return false; - } + /** + * Checks if a particular permutation is allowed and superimposes well. + * Caches results. + * @param permutation + * @return null if invalid, or a rotation if valid + */ + private Rotation isValidPermutation(List permutation) { + if (permutation.size() == 0) { + return null; + } - // if this permutation is a duplicate, return false - if (hashCodes.contains(permutation)) { - return false; + // cached value + if (evaluatedPermutations.containsKey(permutation)) { + return evaluatedPermutations.get(permutation); } // check if permutation is allowed if (! isAllowedPermutation(permutation)) { - return false; - } - - // get fold and make sure there is only one E (fold=1) permutation - int fold = PermutationGroup.getOrder(permutation); - if (rotations.getOrder() > 1 && fold == 1) { - return false; - } - - if (fold == 0 || subunits.getSubunitCount() % fold != 0) { - return false; + evaluatedPermutations.put(permutation, null); + return null; } - // if this permutation is a duplicate, returns false - return hashCodes.add(permutation); + // check if superimposes + Rotation rot = superimposePermutation(permutation); + return rot; } + /** + * The permutation must map all subunits onto an equivalent subunit + * and no subunit onto itself + * @param permutation + * @return + */ private boolean isAllowedPermutation(List permutation) { List seqClusterId = subunits.getSequenceClusterIds(); + int selfaligned = 0; for (int i = 0; i < permutation.size(); i++) { int j = permutation.get(i); - if (seqClusterId.get(i) != seqClusterId.get(j)) { + if ( seqClusterId.get(i) != seqClusterId.get(j)) { return false; } + if(i == j ) { + selfaligned++; + } } - return true; + // either identity (all self aligned) or all unique + return selfaligned == 0 || selfaligned == permutation.size(); } /** * Adds translational component to rotation matrix @@ -260,7 +332,7 @@ private void combineWithTranslation(Matrix4d rotation) { rotation.mul(rotation, centroidInverse); } - private Rotation createSymmetryOperation(List permutation, Matrix4d transformation, AxisAngle4d axisAngle, int fold, QuatSymmetryScores scores) { + private static Rotation createSymmetryOperation(List permutation, Matrix4d transformation, AxisAngle4d axisAngle, int fold, QuatSymmetryScores scores) { Rotation s = new Rotation(); s.setPermutation(new ArrayList(permutation)); s.setTransformation(new Matrix4d(transformation)); @@ -299,6 +371,11 @@ private double calcDistanceThreshold() { return distanceThreshold; } + /** + * Compare this.transformedCoords with the original coords. For each + * subunit, return the transformed subunit with the closest position. + * @return A list mapping each subunit to the closest transformed subunit + */ private List getPermutation() { List permutation = new ArrayList(transformedCoords.length); double sum = 0.0f; diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/Subunits.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/Subunits.java index 6e980be703..c639cae3bc 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/Subunits.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/Subunits.java @@ -65,6 +65,12 @@ public class Subunits { * @param modelNumbers Model number for the subunit */ public Subunits(List caCoords, List sequenceClusterIds, List pseudoStoichiometry, List minSequenceIdentity, List maxSequenceIdentity, List folds, List chainIds, List modelNumbers) { + + for (int i=0; i triples = new ArrayList(); @@ -38,6 +42,7 @@ class Permute { triples.add(tmp); int n = 1; + // Unpermuted ±x,±y,±z if (t.x != 0) { for (int i = 0; i < n; ++i) { Tuple3i m = triples.get(i); @@ -66,6 +71,7 @@ class Permute { return; } + // At least one differing value for (int i = 0; i < n; ++i) { Point3i m = triples.get(i); triples.add(new Point3i(m.y, m.z, m.x)); @@ -77,6 +83,7 @@ class Permute { return; } + // At least two differing values for (int i = 0; i < n; ++i) { Point3i m = triples.get(i); triples.add(new Point3i(m.y, m.x, m.z)); @@ -94,7 +101,10 @@ public Point3i get(int i) { }; /** - * + * Sample possible orientations. An orientation can be represented as a + * quaternion or as a rotation axis and angle. The orientations are somewhat + * evenly distributed over orientation space, but the current implementation + * is not suited for statistically uniform sampling. * @author Peter */ public final class SphereSampler { @@ -125,16 +135,20 @@ public final class SphereSampler { + // Approximate spacing between grid points private static final double delta = 0.15846; + // Amount of distortion towards grid corners to account for the projection back to a 4-sphere private static final double sigma = 0.00; - private static final int ntot = 7416; - private static final int ncell = 309; - private static final int nent = 18; + private static final int ntot = 7416; // total number of grid points over the 24 cells + private static final int ncell = 309; // number of grid points per cell + private static final int nent = 18; // number of distinct grid points (with k>=l>=m) + // Why not (5,5,3) and (5,5,5)? Would they overlap with other cells? -SB private static final int[] k = { 0, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5}; private static final int[] l = { 0, 1, 0, 2, 2, 1, 3, 3, 0, 2, 2, 4, 4, 4, 1, 3, 3, 5}; private static final int[] m = { 0, 1, 0, 0, 2, 1, 1, 3, 0, 0, 2, 0, 2, 4, 1, 1, 3, 1}; + // number of permutations to expect for each (k,l,m) tuple private static final int[] mult = { 1, 8, 6, 12, 8, 24, 24, 8, 6, 24, 24, 12, 24, 8, 24, 48, 24, 24}; static @@ -142,15 +156,29 @@ public final class SphereSampler { List myorientations = new ArrayList(); + // 60 equally spaced grid points covering all quaternions + // This could be removed, since the 48-cell bcc lattice has lower angle error -SB for (int i = 0; i < IcosahedralSampler.getSphereCount(); i++) { myorientations.add(IcosahedralSampler.getQuat4d(i)); } + + // SB's interpretation of this code: + // Directly form a 4D grid over the 4-sphere of orientations. Start by + // making a 3D body-centered lattice with w=1. Then use the hypercube symmetries + // to extend this grid over the whole surface. + // The permuted (k,l,m) values together make a diagonal grid out to ±(5,5,5). + // The point spacing is distorted by the pind() function so that the + // projection of the points back to the 4-sphere will be more even. + + // This is the c48u309 lattice from Karney 2006, with a max error of 10.07 + // degrees. + List grid = new ArrayList(); int ncell1 = 0; - for (int n = 0; n < nent; ++n) { + for (int n = 0; n < nent; ++n) { // for each tuple (k,l,m) above Permute p = new Permute(new Point3i(k[n], l[n], m[n])); assert (mult[n] == p.size()); - for (int i = 0; i < mult[n]; ++i) { + for (int i = 0; i < mult[n]; ++i) { // for each permutation Point3i t = p.get(i); grid.add(new Quat4d(1.0, pind(0.5 * t.x, delta, sigma), pind( 0.5 * t.y, delta, sigma), pind(0.5 * t.z, delta, sigma))); @@ -158,6 +186,7 @@ public final class SphereSampler { ncell1 += mult[n]; } assert (ncell1 == ncell); + // Apply symmetry operations to distribute grid over all values of w int nc = grid.size(); assert (nc == ncell); for (int n = 1; n < 24; ++n) { @@ -168,6 +197,7 @@ public final class SphereSampler { qs.mul(q, grid.get(i)); grid.add(qs); // s.add(times(q, s.getOrientation(i)), s.getWeight(i)); // this data set has no weights + // Actually, it does have weights but we don't care since we don't need uniform sampling. -SB } } assert (grid.size() == ntot); @@ -196,6 +226,10 @@ public static void getAxisAngle(int index, AxisAngle4f axisAngle) { axisAngle.set(orientations.get(index)); } + /** + * @param index Index between 0 and {@link #getSphereCount()}-1 + * @param axisAngle (Output) modified to contain the index-th sampled orientation + */ public static void getAxisAngle(int index, AxisAngle4d axisAngle) { axisAngle.set(orientations.get(index)); diff --git a/biojava-survival/pom.xml b/biojava-survival/pom.xml index d7c072c558..1f03bad9be 100644 --- a/biojava-survival/pom.xml +++ b/biojava-survival/pom.xml @@ -4,7 +4,7 @@ org.biojava biojava - 4.2.5 + 4.2.6 biojava-survival diff --git a/biojava-ws/pom.xml b/biojava-ws/pom.xml index 490d79c32d..646bd14a0b 100644 --- a/biojava-ws/pom.xml +++ b/biojava-ws/pom.xml @@ -3,7 +3,7 @@ biojava org.biojava - 4.2.5 + 4.2.6 biojava-ws biojava-ws @@ -19,7 +19,7 @@ org.biojava biojava-core - 4.2.5 + 4.2.6 compile diff --git a/pom.xml b/pom.xml index e1daf1fcea..3993929e35 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ org.biojava biojava pom - 4.2.5 + 4.2.6 biojava BioJava is an open-source project dedicated to providing a Java framework for processing biological data. It provides analytical and statistical routines, parsers for common file formats and allows the @@ -44,7 +44,7 @@ scm:git:git@github.com:biojava/biojava.git https://github.com/biojava/biojava - biojava-4.2.5 + biojava-4.2.6