Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ public class AtomContactSet implements Serializable, Iterable<AtomContact> {

private static final long serialVersionUID = 1L;

/**
* The default load factor of a {@link HashMap}, needed to size the map from an expected number of
* entries.
*/
private static final float DEFAULT_LOAD_FACTOR = 0.75f;

private HashMap<Pair<AtomIdentifier>, AtomContact> contacts;
Comment thread
aalhossary marked this conversation as resolved.
Resolved
private double cutoff;

Expand All @@ -45,6 +51,20 @@ public AtomContactSet(double cutoff) {
this.contacts = new HashMap<>();
}

/**
* Creates an AtomContactSet sized to hold the given number of contacts, so that the underlying map
* doesn't have to be repeatedly resized and rehashed as contacts are added. Contact calculations
* produce hundreds of thousands of contacts for a large structure, where the repeated rehashing
* that growing from the default capacity entails is a significant part of the cost.
* @param cutoff the distance cutoff
* @param expectedSize the number of contacts expected to be added. Only affects performance: an
* over-estimate merely leaves the map larger than it needs to be.
*/
public AtomContactSet(double cutoff, int expectedSize) {
this.cutoff = cutoff;
this.contacts = new HashMap<>((int) (expectedSize / DEFAULT_LOAD_FACTOR) + 1);
}

public void add(AtomContact contact) {
this.contacts.put(getAtomIdPairFromContact(contact), contact);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public class Grid {
private GridCell[][][] cells;

private double cutoff;
private double cutoffSq;
private int cellSize;

private Point3d[] iAtoms;
Expand All @@ -91,6 +92,7 @@ public class Grid {
*/
public Grid(double cutoff) {
this.cutoff = cutoff;
this.cutoffSq = cutoff * cutoff;
this.cellSize = (int) Math.floor(cutoff*SCALE);
this.noOverlap = false;
}
Expand Down Expand Up @@ -380,10 +382,12 @@ private int[] getIntBounds(BoundingBox coordbounds) {
*/
public AtomContactSet getAtomContacts() {

AtomContactSet contacts = new AtomContactSet(cutoff);

List<Contact> list = getIndicesContacts();

// each contact maps to at most one entry in the set, so the number of index contacts sizes it
// without ever under-allocating
AtomContactSet contacts = new AtomContactSet(cutoff, list.size());

if (jAtomObjects == null) {
for (Contact cont : list) {
contacts.add(new AtomContact(new Pair<Atom>(iAtomObjects[cont.getI()],iAtomObjects[cont.getJ()]),cont.getDistance()));
Expand Down Expand Up @@ -496,6 +500,15 @@ public double getCutoff() {
return cutoff;
}

/**
* Returns the square of the cutoff, precomputed at construction. Used by {@link GridCell} to
* compare squared distances, avoiding a square root per candidate pair.
* @return the squared cutoff
*/
protected double getCutoffSq() {
return cutoffSq;
}

/**
* Tells whether (after having added atoms to grid) the i and j grids are not overlapping.
* Overlap is defined as enclosing bounds of the 2 grids being no more than one cell size apart.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package org.biojava.nbio.structure.contact;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.vecmath.Point3d;
Expand All @@ -35,30 +36,61 @@
public class GridCell {


/**
* Shared empty array so that cells that never receive indices (e.g. the j indices when only one
* set of atoms was added to the grid) don't allocate anything at all.
*/
private static final int[] EMPTY = new int[0];

/**
* Capacity of the index arrays on first insertion. Cell occupancy depends on the cutoff (the cell
* side is the cutoff), ranging from a handful of atoms for small cutoffs to a few tens for large
* ones, so we start small and grow geometrically.
*/
private static final int INITIAL_CAPACITY = 8;

private Grid grid;
private ArrayList<Integer> iIndices;
private ArrayList<Integer> jIndices;

/**
* The indices of the i atoms in this cell, held as a primitive array to avoid the boxing (and the
* pointer chasing it entails) of a Collection of Integers: these are read in the innermost loop of
* the contact calculation. Only the first {@link #numIindices} elements are meaningful.
*/
private int[] iIndices;
private int numIindices;

/**
* The indices of the j atoms in this cell. See {@link #iIndices}.
*/
private int[] jIndices;
private int numJindices;

public GridCell(Grid parent){
iIndices = new ArrayList<>();
jIndices = new ArrayList<>();
iIndices = EMPTY;
jIndices = EMPTY;
this.grid = parent;
}

public void addIindex(int serial){
iIndices.add(serial);
if (numIindices == iIndices.length) {
iIndices = Arrays.copyOf(iIndices, numIindices == 0 ? INITIAL_CAPACITY : numIindices * 2);
}
iIndices[numIindices++] = serial;
}

public void addJindex(int serial){
jIndices.add(serial);
if (numJindices == jIndices.length) {
jIndices = Arrays.copyOf(jIndices, numJindices == 0 ? INITIAL_CAPACITY : numJindices * 2);
}
jIndices[numJindices++] = serial;
}

public int getNumIindices() {
return iIndices.size();
return numIindices;
}

public int getNumJindices() {
return jIndices.size();
return numJindices;
}

/**
Expand All @@ -74,23 +106,31 @@ public List<Contact> getContactsWithinCell(){

Point3d[] iAtoms = grid.getIAtoms();
Point3d[] jAtoms = grid.getJAtoms();
double cutoff = grid.getCutoff();
// we compare squared distances to the squared cutoff, so that the expensive square root is
// only computed for the pairs that are actually in contact (the large majority are not)
double cutoffSq = grid.getCutoffSq();

if (jAtoms==null) {
for (int i:iIndices) {
for (int j:iIndices) {
for (int a=0; a<numIindices; a++) {
int i = iIndices[a];
Point3d atomI = iAtoms[i];
for (int b=0; b<numIindices; b++) {
int j = iIndices[b];
if (j>i) {
double distance = iAtoms[i].distance(iAtoms[j]);
if (distance<cutoff) contacts.add(new Contact(i, j, distance));
double distanceSq = atomI.distanceSquared(iAtoms[j]);
if (distanceSq<cutoffSq) contacts.add(new Contact(i, j, Math.sqrt(distanceSq)));
}
}
Comment thread
aalhossary marked this conversation as resolved.
Resolved
}

} else {
for (int i:iIndices) {
for (int j:jIndices) {
double distance = iAtoms[i].distance(jAtoms[j]);
if (distance<cutoff) contacts.add(new Contact(i, j, distance));
for (int a=0; a<numIindices; a++) {
int i = iIndices[a];
Point3d atomI = iAtoms[i];
for (int b=0; b<numJindices; b++) {
int j = jIndices[b];
double distanceSq = atomI.distanceSquared(jAtoms[j]);
if (distanceSq<cutoffSq) contacts.add(new Contact(i, j, Math.sqrt(distanceSq)));
}
}
}
Expand All @@ -111,26 +151,38 @@ public List<Contact> getContactsToOtherCell(GridCell otherCell){

Point3d[] iAtoms = grid.getIAtoms();
Point3d[] jAtoms = grid.getJAtoms();
double cutoff = grid.getCutoff();
// we compare squared distances to the squared cutoff, so that the expensive square root is
// only computed for the pairs that are actually in contact (the large majority are not)
double cutoffSq = grid.getCutoffSq();


if (jAtoms==null) {

for (int i:iIndices) {
for (int j:otherCell.iIndices) {
int[] otherIndices = otherCell.iIndices;
int otherNum = otherCell.numIindices;
for (int a=0; a<numIindices; a++) {
int i = iIndices[a];
Point3d atomI = iAtoms[i];
for (int b=0; b<otherNum; b++) {
int j = otherIndices[b];
if (j>i) {
double distance = iAtoms[i].distance(iAtoms[j]);
if (distance<cutoff) contacts.add(new Contact(i, j, distance));
double distanceSq = atomI.distanceSquared(iAtoms[j]);
if (distanceSq<cutoffSq) contacts.add(new Contact(i, j, Math.sqrt(distanceSq)));
}
}
}

} else {

for (int i:iIndices) {
for (int j:otherCell.jIndices) {
double distance = iAtoms[i].distance(jAtoms[j]);
if (distance<cutoff) contacts.add(new Contact(i, j, distance));
int[] otherIndices = otherCell.jIndices;
int otherNum = otherCell.numJindices;
for (int a=0; a<numIindices; a++) {
int i = iIndices[a];
Point3d atomI = iAtoms[i];
for (int b=0; b<otherNum; b++) {
int j = otherIndices[b];
double distanceSq = atomI.distanceSquared(jAtoms[j]);
if (distanceSq<cutoffSq) contacts.add(new Contact(i, j, Math.sqrt(distanceSq)));
}
}

Expand All @@ -148,15 +200,17 @@ public List<Contact> getContactsToOtherCell(GridCell otherCell){
* @return
*/
public boolean hasContactToAtom(Point3d[] iAtoms, Point3d[] jAtoms, Point3d query, double cutoff) {
for( int i : iIndices ) {
double distance = iAtoms[i].distance(query);
if( distance<cutoff)
// only the comparison matters here, so we can stay in squared distance space and avoid square roots altogether
double cutoffSq = cutoff * cutoff;
for (int a=0; a<numIindices; a++) {
double distanceSq = iAtoms[iIndices[a]].distanceSquared(query);
if( distanceSq<cutoffSq)
return true;
}
if (jAtoms!=null) {
for( int i : jIndices ) {
double distance = jAtoms[i].distance(query);
if( distance<cutoff)
for (int a=0; a<numJindices; a++) {
double distanceSq = jAtoms[jIndices[a]].distanceSquared(query);
if( distanceSq<cutoffSq)
return true;
}
}
Expand All @@ -168,7 +222,7 @@ public boolean hasContactToAtom(Point3d[] iAtoms, Point3d[] jAtoms, Point3d quer
*/
@Override
public String toString() {
return String.format("GridCell [%d iAtoms,%d jAtoms]",iIndices.size(),jIndices==null?"-":jIndices.size());
return String.format("GridCell [%d iAtoms,%d jAtoms]", numIindices, numJindices);
}


Expand Down
Loading