com.actelion.research.chem.coords.CoordinateInventor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openchemlib Show documentation
Show all versions of openchemlib Show documentation
Open Source Chemistry Library
/*
* Copyright (c) 1997 - 2016
* Actelion Pharmaceuticals Ltd.
* Gewerbestrasse 16
* CH-4123 Allschwil, Switzerland
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @author Thomas Sander
*/
package com.actelion.research.chem.coords;
import com.actelion.research.chem.*;
import java.util.*;
public class CoordinateInventor {
public static final int MODE_SKIP_DEFAULT_TEMPLATES = 1;
public static final int MODE_REMOVE_HYDROGEN = 2;
public static final int MODE_KEEP_MARKED_ATOM_COORDS = 4;
public static final int MODE_PREFER_MARKED_ATOM_COORDS = 8;
private static final int MODE_CONSIDER_MARKED_ATOMS = MODE_KEEP_MARKED_ATOM_COORDS | MODE_PREFER_MARKED_ATOM_COORDS;
public static final int MODE_DEFAULT = MODE_REMOVE_HYDROGEN;
private static final byte FLIP_AS_LAST_RESORT = 1;
private static final byte FLIP_POSSIBLE = 2;
private static final byte FLIP_PREFERRED = 3;
private static final int PREFERRED_FLIPS = 32;
private static final int POSSIBLE_FLIPS = 64;
private static final int LAST_RESORT_FLIPS = 128;
private static final int TOTAL_FLIPS = PREFERRED_FLIPS + POSSIBLE_FLIPS + LAST_RESORT_FLIPS;
private static final float JOIN_DISTANCE_METAL_BONDS = 1.2f;
private static final float JOIN_DISTANCE_CHARGED_ATOMS = 1.4f;
public static final float JOIN_DISTANCE_UNCHARGED_FRAGMENTS = 1.6f;
private static volatile List sDefaultTemplateList;
private StereoMolecule mMol;
private long[] mFFP;
private Random mRandom;
private boolean[] mAtomHandled;
private boolean[] mBondHandled;
private boolean[] mAtomIsPartOfCustomTemplate;
private boolean mAbsoluteOrientationTemplateFound; // we just use the first matching template, which is set to define absolute orientation
private int[] mUnPairedCharge;
private final int mMode;
private List mFragmentList;
private List mCustomTemplateList;
private static synchronized void buildDefaultTemplateList() {
if (sDefaultTemplateList == null)
sDefaultTemplateList = new InventorDefaultTemplateList();
}
/**
* Creates an CoordinateInventor, which removes unneeded hydrogen atoms
* and creates new atom coordinates for all(!) atoms.
* This constructor creates a CoordinateInventor that uses templates
* of the InventorDefaultTemplateList to create 3D-projection derived coordinates for
* polycyclic structures from these templates (adamantanes, cubane, etc.).
*/
public CoordinateInventor () {
this(MODE_DEFAULT);
}
/**
* Creates an CoordinateInventor, which removes unneeded hydrogens, if mode flags include
* MODE_REMOVE_HYDROGEN. If mode includes MODE_KEEP_MARKED_ATOM_COORDS, then marked atoms
* keep their coordinates. If mode includes MODE_PREFER_MARKED_ATOM_COORDS, then coordinates
* of marked atoms are changed only, if perfect coordinates are not possible without.
* Unless mode includes MODE_SKIP_DEFAULT_TEMPLATES, the CoordinateInventor uses templates
* of the InventorDefaultTemplateList to create 3D-projection derived coordinates for
* polycyclic structures from these templates (adamantanes, cubane, etc.).
* @param mode
*/
public CoordinateInventor (int mode) {
mMode = mode;
if ((mode & MODE_SKIP_DEFAULT_TEMPLATES) == 0 && sDefaultTemplateList == null)
buildDefaultTemplateList();
}
public void setRandomSeed(long seed) {
mRandom = new Random(seed);
}
/**
* A custom template list contains substructures with predefined atom coordinates.
* When such a list is provided, and if a molecules contains one of the list's substructures,
* then the matching atoms will receive the relative atom coordinates of the provided template,
* unless the substructure shares two or more atoms with another earlier found template or
* if mode is MODE_????_MARKED_ATOM_COORDS and the substructure match contains two or more
* non-marked (and therefore untouchable) atoms. If a template substructure contains an E- or Z-
* double bound, then the query feature 'match EZ-parity' should be set.
* @param templateList
*/
public void setCustomTemplateList(List templateList) {
mCustomTemplateList = templateList;
for (InventorTemplate template:templateList)
template.normalizeCoordinates();
}
/**
* Creates new atom 2D-coordinates for a molecule or a part of a molecule.
* Typically, the molecule has defined TH- and EZ-parities (even if unknown or none), which were not
* calculated, but taken from a SMILES or from an IDCode. In these cases setParitiesValid() should have
* been called to indicate that a parity calculation is not needed and even would destroy given parities.
* New coordinates will correctly reflect E/Z double bond parities, unless the double bond is in a small ring.
* If atom parities are available, this call is typically followed by calling mol.setStereoBondsFromParity();
* Unneeded explicit hydrogens are removed, if mode includes MODE_REMOVE_HYDROGEN.
* The relative orientation of all marked atoms is retained, if mode includes MODE_KEEP_MARKED_ATOM_COORDS.
* The relative orientation of all marked atoms is changed as last resort only, if mode includes MODE_PREFER_MARKED_ATOM_COORDS.
* If setTemplateList() was called, then any substructures matching any of the templates will be shown
* with the relative atom orientation provided with the template. If many molecule's coordinates are invented
* with templates, then you should also provide the molecules' fragment fingerprint to speed up template search
* using invent(mol, ffp).
* @param mol the molecule that gets new 2D coordinates in place
*/
public void invent(StereoMolecule mol) {
invent(mol, null);
}
/**
* Creates new atom 2D-coordinates for a molecule or a part of a molecule.
* Typically, the molecule has defined TH- and EZ-parities (even if unknown or none), which were not
* calculated, but taken from a SMILES or from an IDCode. In these cases setParitiesValid() should have
* been called to indicate that a parity calculation is not needed and even would destroy given parities.
* New coordinates will correctly reflect E/Z double bond parities, unless the double bond is in a small ring.
* If atom parities are available, this call is typically followed by calling mol.setStereoBondsFromParity();
* Unneeded explicit hydrogens are removed, if mode includes MODE_REMOVE_HYDROGEN.
* The relative orientation of all marked atoms is retained, if mode includes MODE_KEEP_MARKED_ATOM_COORDS.
* The relative orientation of all marked atoms is changed as last resort only, if mode includes MODE_PREFER_MARKED_ATOM_COORDS.
* If setTemplateList() was called, then any substructures matching any of the templates will be shown
* with the relative atom orientation provided with the template. If many molecule's coordinates are invented
* with templates, then you should also provide the molecules' fragment fingerprint to speed up template search.
* @param mol the molecule that gets new 2D coordinates in place
* @parem ffp null or fragment fingerprint of the molecule, which is used (if available) for faster template location
*/
public void invent(StereoMolecule mol, long[] ffp) {
boolean paritiesPresent = (mol.getHelperArrayStatus() & Molecule.cHelperParities) != 0;
int parityState = mol.getHelperArrayStatus() & Molecule.cHelperBitsStereo;
if (mRandom == null)
mRandom = new Random();
if ((mMode & MODE_REMOVE_HYDROGEN) != 0)
mol.removeExplicitHydrogens(false, false);
mMol = mol;
mMol.ensureHelperArrays(Molecule.cHelperRings);
mFFP = ffp;
mFragmentList = new ArrayList() {
@Override
public boolean add(InventorFragment f) {
for (InventorFragment ff:this)
if (ff.equals(f))
return false;
return super.add(f);
}
};
mAtomHandled = new boolean[mMol.getAllAtoms()];
mBondHandled = new boolean[mMol.getAllBonds()];
mUnPairedCharge = new int[mMol.getAllAtoms()];
for (int atom=0; atom templateList, int priority) {
boolean useFFP = (mFFP != null && !templateList.isEmpty() && templateList.get(0).getFFP() != null);
SSSearcher searcher = null;
SSSearcherWithIndex searcherWithIndex = null;
if (useFFP) {
searcherWithIndex = new SSSearcherWithIndex();
searcherWithIndex.setMolecule(mMol, mFFP);
}
else {
searcher = new SSSearcher();
searcher.setMolecule(mMol);
}
boolean[] atomIsPartOfTemplate = new boolean[mMol.getAtoms()];
for (InventorTemplate template: templateList) {
ArrayList matchList = null;
StereoMolecule templateMol = template.getFragment();
if (useFFP) {
searcherWithIndex.setFragment(templateMol, template.getFFP());
if (searcherWithIndex.findFragmentInMolecule(SSSearcher.cCountModeOverlapping, SSSearcher.cDefaultMatchMode) != 0)
matchList = searcherWithIndex.getGraphMatcher().getMatchList();
}
else {
searcher.setFragment(templateMol);
if (searcher.findFragmentInMolecule(SSSearcher.cCountModeOverlapping, SSSearcher.cDefaultMatchMode) != 0)
matchList = searcher.getMatchList();
}
if (matchList != null) {
for (int[] match:matchList) {
int templateAtomCount = 0;
for (int atom:match)
if (atomIsPartOfTemplate[atom])
templateAtomCount++;
if (templateAtomCount <= 1) {
// we just use the first matching template that is supposed to keep its absolute orientation
boolean definesAbsoluteOrientation = template.keepAbsoluteOrientation();
if (mAbsoluteOrientationTemplateFound)
definesAbsoluteOrientation = false;
else
mAbsoluteOrientationTemplateFound = true;
InventorFragment fragment = new InventorFragment(mMol, match.length, definesAbsoluteOrientation);
for (int i=0; i 4) {
InventorFragment f = new InventorFragment(mMol, 1+mMol.getAllConnAtoms(atom), false);
f.mAtomX[mMol.getAllConnAtoms(atom)] = 0.0;
f.mAtomY[mMol.getAllConnAtoms(atom)] = 0.0;
f.mPriority[mMol.getAllConnAtoms(atom)] = 32;
f.mGlobalAtom[mMol.getAllConnAtoms(atom)] = atom;
mAtomHandled[atom] = true;
for (int i=0; i 2) {
InventorFragment f = new InventorFragment(mMol, members, false);
int count = 0;
for (int i=0; i 2)
break;
mAtomHandled[alleneAtom[last+1]] = true;
mBondHandled[mMol.getConnBond(alleneAtom[last], nextIndex)] = true;
last++;
} while (mMol.getAtomPi(alleneAtom[last]) == 2
&& mMol.getAllConnAtoms(alleneAtom[last]) == 2);
int members = mMol.getAllConnAtoms(alleneAtom[0])
+ mMol.getAllConnAtoms(alleneAtom[last])
+ last - 1;
InventorFragment f = new InventorFragment(mMol, members, false);
for (int j=0; j<=last; j++) {
f.mAtomX[j] = j;
f.mAtomY[j] = 0.0;
f.mPriority[j] = 64;
f.mGlobalAtom[j] = alleneAtom[j];
}
int current = last+1;
boolean found = false;
for (int j=0; j connAtom)
secondAtomCounts[i] = true;
f.mAtomX[count] = (i==0) ? -1.0 : 1.0;
f.mAtomY[count] = (neighbours==0) ? -Math.sin(Math.PI/3) : Math.sin(Math.PI/3);
f.mAtom[count++] = connAtom;
mAtomHandled[connAtom] = true;
mBondHandled[mMol.getConnBond(bondAtom[i], j)] = true;
neighbours++;
}
}
}
if ((bondParity == Molecule.cBondParityE) ^ (secondAtomCounts[0] ^ secondAtomCounts[1]))
for (int i=1; i longestChain.getChainLength())
longestChain = theChain;
}
}
if (longestChain == null)
break;
InventorFragment f = new InventorFragment(mMol, longestChain.getChainLength(), false);
for (int i=0; i= FIRST_RING_SIZE)
// printRingStats(f, ringAtom, ringBond);
int maxBit = (1 << f.size());
int bondEConstraint = 0;
int bondZConstraint = 0;
if (f.size() > 7) {
for (int i=0; i>>= 1;
bondZConstraint >>>= 1;
}
}
int ringIndex = f.size()-FIRST_RING_SIZE;
if (f.size() >= FIRST_RING_SIZE && f.size() <= LAST_RING_SIZE && cBondZList[ringIndex] != null) {
for (int zList=0; zList>>= 1;
}
// We choose the starting turn direction such that we end up
// with more right turns and, thus, close the ring in clockwise direction.
boolean wasRightTurn = (rightTurns > f.size() / 2); // create a ring that closes with right turns
for (int i=1; i>>= 1;
}
return;
}
if ((bondZList & 1) != 0)
bondZList |= maxBit;
bondZList >>>= 1;
}
}
}
}
// if not successful so far
createRegularRingFragment(f, bondEConstraint, bondZConstraint);
}
private int getLargeRingBondParity(int[] ringAtom, int[] ringBond, int index) {
int higherIndex = (index == ringAtom.length-1) ? 0 : index+1; // second ringAtom index
int lowerIndex = (index == 0) ? ringAtom.length-1 : index-1;
int highestIndex = (higherIndex == ringAtom.length-1) ? 0 : higherIndex+1;
if (mMol.getBondOrder(ringBond[index]) == 2) {
int bondParity = mMol.getBondParity(ringBond[index]);
if (bondParity == Molecule.cBondParityEor1
|| bondParity == Molecule.cBondParityZor2) {
// translate bond configuration from lowest atom index to ring members
if (isLowestIndexNeighbour(ringAtom[lowerIndex], ringAtom[index], ringAtom[higherIndex])
^ isLowestIndexNeighbour(ringAtom[highestIndex], ringAtom[higherIndex], ringAtom[index]))
bondParity = (bondParity == Molecule.cBondParityEor1) ?
Molecule.cBondParityZor2 : Molecule.cBondParityEor1;
return bondParity;
}
}
// If the bond is also a member of a small ring
if (mMol.isSmallRingBond(ringBond[index])) {
int sharedRing1 = mMol.getRingSet().getSharedRing(ringBond[lowerIndex], ringBond[index]);
int sharedRing2 = mMol.getRingSet().getSharedRing(ringBond[higherIndex], ringBond[index]);
// If more than one bond is shared by the large and the small ring,
// then the first and the last shared bond are E and the ones between Z.
if (sharedRing1 != -1 || sharedRing2 != -1)
return sharedRing1 == sharedRing2 ? Molecule.cBondParityZor2 : Molecule.cBondParityEor1;
// If only this one bond is shared, then we have a Z-configuration
return Molecule.cBondParityZor2;
}
return Molecule.cBondParityNone;
}
/**
* @param atom
* @param rootAtom
* @param excludeAtom
* @return whether atom has the lowest atom index of rootAtom's neighbours not considering excludeAtom
*/
private boolean isLowestIndexNeighbour(int atom, int rootAtom, int excludeAtom) {
for (int i=0; i>>= 1;
bondZConstraint >>>= 1;
}
System.out.println("ringSize:"+f.size()
+" E-list:"+Integer.toBinaryString(bondEConstraint)
+" Z-list:"+Integer.toBinaryString(bondZConstraint));
}*/
/**
* Find the smallest ring of given bond
* @param bond
* @return
*/
private InventorChain getSmallestRingFromBond(int bond) {
int atom1 = mMol.getBondAtom(0, bond);
int atom2 = mMol.getBondAtom(1, bond);
int[] graphAtom = new int[mMol.getAllAtoms()];
int[] graphBond = new int[mMol.getAllAtoms()];
int[] graphLevel = new int[mMol.getAllAtoms()];
int[] graphParent = new int[mMol.getAllAtoms()];
graphAtom[0] = atom1;
graphAtom[1] = atom2;
graphBond[1] = bond;
graphLevel[atom1] = 1;
graphLevel[atom2] = 2;
graphParent[0] = -1;
graphParent[1] = 0;
int current = 1;
int highest = 1;
while (current <= highest) {
for (int i=0; i 1) && candidate == atom1) {
InventorChain theRing = new InventorChain(graphLevel[graphAtom[current]]);
graphBond[0] = mMol.getConnBond(graphAtom[current], i);
int index = current;
for (int j=0; j 0) {
int handlePreferred = (commonAtoms == 1
&& getConnAtoms(f1, commonAtom) == 1
&& getConnAtoms(f2, commonAtom) == 1) ? 0 : 1;
int joinPriority;
if (maxF1Priority > maxF2Priority)
joinPriority = (handlePreferred << 24)
+ (maxF1Priority << 16)
+ (maxF2Priority << 8)
+ commonAtoms;
else
joinPriority = (handlePreferred << 24)
+ (maxF2Priority << 16)
+ (maxF1Priority << 8)
+ commonAtoms;
if (maxJoinPriority < joinPriority) {
maxJoinPriority = joinPriority;
maxCommonAtoms = commonAtoms;
// retain coordinates of fragment with highest priority atom
maxF1Priority = 0;
maxF2Priority = 0;
for (int k=0; k maxF2Priority) {
maxFragment1 = f1;
maxFragment2 = f2;
}
else {
maxFragment1 = f2;
maxFragment2 = f1;
}
}
}
}
}
if (maxJoinPriority == 0)
break;
if (maxCommonAtoms == maxFragment1.size())
mFragmentList.remove(maxFragment1);
else if (maxCommonAtoms == maxFragment2.size())
mFragmentList.remove(maxFragment2);
else
joinFragments(maxFragment1, maxFragment2, maxCommonAtoms);
}
}
private void joinFragments(InventorFragment f1, InventorFragment f2, int commonAtoms) {
int[] commonAtom = new int[commonAtoms];
int count = 0;
for (int i = 0; i Math.abs(getAngleDif(meanNeighbourAngleF1.mAngle, meanNeighbourAngleF2Flip.mAngle))) {
f2.rotate(meanX1, meanY1, meanAngleDif.mAngle);
}
else {
f2.flip(meanX1, meanY1, 0.0);
f2.rotate(meanX1, meanY1, meanAngleDifFlip.mAngle);
}
return getMergedFragment(f1, f2, commonAtoms);
}
private InventorFragment getMergedFragment(InventorFragment f1, InventorFragment f2, int commonAtoms) {
// merges all atoms of two fragments into a new one retaining original coordinates
InventorFragment f = new InventorFragment(mMol, f1.mGlobalAtom.length + f2.mGlobalAtom.length - commonAtoms, f1.mKeepMarkedAtoms | f2.mKeepMarkedAtoms);
int count = 0;
for (int i = 0; i0; i--) { // bubble sort
for (int j=0; j connAngle[j+1]) {
double tempAngle = connAngle[j];
connAngle[j] = connAngle[j+1];
connAngle[j+1] = tempAngle;
int tempAtom = connAtom[j];
connAtom[j] = connAtom[j+1];
connAtom[j+1] = tempAtom;
int tempBond = connBond[j];
connBond[j] = connBond[j+1];
connBond[j+1] = tempBond;
}
}
}
connAngle[connAngles] = connAngle[0] + 2 * Math.PI;
connAtom[connAngles] = connAtom[0];
connBond[connAngles] = connBond[0];
double maxAngleDif = -100.0;
int maxIndex = 0;
for (int i=0; i 2
&& mMol.isRingBond(connBond[i])
&& mMol.isRingBond(connBond[i+1])) {
int ringSize = getSmallestRingSize(connAtom[i], atom, connAtom[i+1]);
if (ringSize != 0)
angleDif -= 100.0 - ringSize;
}
if (maxAngleDif < angleDif) {
maxAngleDif = angleDif;
maxIndex = i;
}
}
return (connAngle[maxIndex] + connAngle[maxIndex+1]) / 2;
}
private double getAngleDif(double angle1, double angle2) {
double angleDif = angle1 - angle2;
while (angleDif < -Math.PI)
angleDif += 2 * Math.PI;
while (angleDif > Math.PI)
angleDif -= 2 * Math.PI;
return angleDif;
}
protected static InventorAngle getMeanAngle(InventorAngle[] angle, int noOfAngles) {
// adds noOfAngles vectors of length=1 with angles angle[i]
// and returns angle of sum-vector
// length of sum-vector is criteria for deviation
double sinSum = 0;
double cosSum = 0;
for (int i=0; i 0) ? Math.PI/2 : -Math.PI/2;
else {
meanAngle = Math.atan(sinSum/cosSum);
if (cosSum < 0)
meanAngle += Math.PI;
}
double length = Math.sqrt(sinSum * sinSum + cosSum * cosSum) / noOfAngles;
return new InventorAngle(meanAngle, length);
}
private int getConnAtoms(InventorFragment f, int atom) {
int connAtoms = 0;
for (int i=0; i1)
&& (mMol.getConnAtoms(mMol.getBondAtom(1, bond))>1)
&& (mMol.getBondParity(bond) == Molecule.cBondParityEor1
|| mMol.getBondParity(bond) == Molecule.cBondParityZor2)) {
int[] minConnAtom = new int[2];
int[] bondAtom = new int[2];
for (int j = 0; j<2; j++) {
minConnAtom[j] = mMol.getMaxAtoms();
bondAtom[j] = mMol.getBondAtom(j, bond);
for (int k = 0; kconnAtom)
minConnAtom[j] = connAtom;
}
}
double dbAngle = InventorAngle.getAngle(f.mAtomX[f.mGlobalToLocalAtom[bondAtom[0]]],
f.mAtomY[f.mGlobalToLocalAtom[bondAtom[0]]],
f.mAtomX[f.mGlobalToLocalAtom[bondAtom[1]]],
f.mAtomY[f.mGlobalToLocalAtom[bondAtom[1]]]);
double angle1 = InventorAngle.getAngle(f.mAtomX[f.mGlobalToLocalAtom[minConnAtom[0]]],
f.mAtomY[f.mGlobalToLocalAtom[minConnAtom[0]]],
f.mAtomX[f.mGlobalToLocalAtom[bondAtom[0]]],
f.mAtomY[f.mGlobalToLocalAtom[bondAtom[0]]]);
double angle2 = InventorAngle.getAngle(f.mAtomX[f.mGlobalToLocalAtom[bondAtom[1]]],
f.mAtomY[f.mGlobalToLocalAtom[bondAtom[1]]],
f.mAtomX[f.mGlobalToLocalAtom[minConnAtom[1]]],
f.mAtomY[f.mGlobalToLocalAtom[minConnAtom[1]]]);
if (((getAngleDif(dbAngle, angle1)<0)
^ (getAngleDif(dbAngle, angle2)<0))
^ (mMol.getBondParity(bond) == Molecule.cBondParityZor2)) {
f.flipOneSide(bond);
}
}
}
}
}
}
private void optimizeFragments() {
int[] atomSymRank = calculateAtomSymmetries();
byte[] bondFlipPriority = new byte[mMol.getAllBonds()];
locateFlipBonds(bondFlipPriority, atomSymRank);
for (int bond=0; bond collisionList = f.getCollisionList();
double minCollisionPanalty = f.getCollisionPanalty();
InventorFragment minCollisionFragment = new InventorFragment(f);
int lastBond = -1;
for (int flip = 0; flip= FLIP_POSSIBLE)
availableBond[availableBonds++] = bondSequence[i];
}
else {
for (int i=1; i= FLIP_AS_LAST_RESORT)
availableBond[availableBonds++] = bondSequence[i];
}
if (availableBonds != 0) {
int theBond = availableBond[0];
if (availableBonds > 1) {
do { // don't rotate 2 times around same bond
theBond = availableBond[mRandom.nextInt(availableBonds)];
} while (theBond == lastBond);
}
if (theBond != lastBond) {
lastBond = theBond;
f.flipOneSide(theBond);
// getCollisionList() is necessary to update collision panalty
collisionList = f.getCollisionList();
if (minCollisionPanalty > f.getCollisionPanalty()) {
minCollisionPanalty = f.getCollisionPanalty();
minCollisionFragment = new InventorFragment(f);
}
}
}
}
mFragmentList.set(fragmentNo, minCollisionFragment);
// finished optimization by rotating around single bonds
// starting optimization by moving individual atoms
/*
double avbl = mMol.getAverageBondLength();
for (int i=0; i currentRank && theRank < nextAvailableRank)
nextAvailableRank = theRank;
}
currentRank = nextAvailableRank;
} while (nextAvailableRank != 9999);
}
}
private int[] getShortestConnection(int atom1, int atom2) {
int[] graphAtom = new int[mMol.getAllAtoms()];
int[] graphBond = new int[mMol.getAllAtoms()];
int[] graphLevel = new int[mMol.getAllAtoms()];
int[] graphParent = new int[mMol.getAllAtoms()];
graphAtom[0] = atom2;
graphLevel[atom2] = 1;
graphParent[0] = -1;
int current = 0;
int highest = 0;
while (current <= highest) {
for (int i=0; i 2) {
boolean symmetricEndFound = true;
int connSymRank = -1;
for (int j=0; jj; k--)
connRank[k] = connRank[k-1];
connRank[j] = rank;
}
int neighbours = mMol.getAllConnAtoms(atom);
baseValue[atom].init(atom);
baseValue[atom].add(atomBits, symRank[atom]);
baseValue[atom].add((maxConnAtoms - neighbours)*(atomBits + 1), 0);
for (int i=0; i associationList = createMetalBondAssociations();
while (associationList != null) {
FragmentAssociation association = getMaxPriorityAssociation(associationList);
joinAssociatedFragments(association, JOIN_DISTANCE_METAL_BONDS);
associationList = createMetalBondAssociations();
}
}
private void joinChargedFragments() {
FragmentAssociation association = createChargeAssociation();
while (association != null) {
joinAssociatedFragments(association, JOIN_DISTANCE_CHARGED_ATOMS);
association = createChargeAssociation();
}
}
private void joinRemainingFragments() {
FragmentAssociation association = createDisconnectedAssociation();
while (association != null) {
joinAssociatedFragments(association, JOIN_DISTANCE_UNCHARGED_FRAGMENTS);
association = createDisconnectedAssociation();
}
}
private void joinAssociatedFragments(FragmentAssociation association, double minDistance) {
association.arrange(minDistance, (mMode & MODE_CONSIDER_MARKED_ATOMS) != 0);
InventorFragment mergedFragment = getMergedFragment(association.getFragment(0), association.getFragment(1), 0);
updateFragmentList(association.getFragment(0), association.getFragment(1), mergedFragment);
}
private FragmentAssociation getMaxPriorityAssociation(ArrayList associationList) {
int maxPriority = 0;
FragmentAssociation maxFA = null;
for (FragmentAssociation association:associationList) {
if (maxPriority < association.getPriority()) {
maxPriority = association.getPriority();
maxFA = association;
}
}
return maxFA;
}
private ArrayList createMetalBondAssociations() {
ArrayList associationList = null;
FragmentAssociation[][] fa = null;
for (int bond=0; bond f2) { // f1 must be the smaller one (and potentially the core fragment with fixed coords)
int temp = f1;
f1 = f2;
f2 = temp;
temp = atomIndex1;
atomIndex1 = atomIndex2;
atomIndex2 = temp;
}
if (fa == null)
fa = new FragmentAssociation[mFragmentList.size()][];
if (fa[f2] == null)
fa[f2] = new FragmentAssociation[f2];
if (fa[f2][f1] != null)
fa[f2][f1].add(atomIndex1, atomIndex2);
else {
fa[f2][f1] = new FragmentAssociation(mFragmentList.get(f1), mFragmentList.get(f2), atomIndex1, atomIndex2);
if (associationList == null)
associationList = new ArrayList();
associationList.add(fa[f2][f1]);
}
}
}
}
return associationList;
}
private FragmentAssociation createChargeAssociation() {
ArrayList negChargeList = new ArrayList();
ArrayList posChargeList = new ArrayList();
ArrayList chargeList = new ArrayList();
for (InventorFragment f:mFragmentList) {
int fragmentCharge = 0;
chargeList.clear();
for (int i=0; i {
int c1 = Math.abs(o1.charge);
int c2 = Math.abs(o2.charge);
return Integer.compare(c1, c2);
});
for (InventorCharge ic:chargeList) {
if (fragmentCharge * ic.charge > 0) { // charges have same sign
int charge = (Math.abs(fragmentCharge) >= Math.abs(ic.charge)) ? ic.charge : fragmentCharge;
fragmentCharge -= charge;
(charge < 0 ? negChargeList : posChargeList).add(new InventorCharge(f, ic.atom, charge));
if (fragmentCharge == 0)
break;
}
}
}
}
if (negChargeList.isEmpty() || posChargeList.isEmpty())
return null;
// with positive charges we have the large fragments first
posChargeList.sort(new Comparator() {
//@Override Annotation incompatible with 1.5
public int compare(InventorCharge o1, InventorCharge o2) {
int c1 = o1.fragment.size();
int c2 = o1.fragment.size();
return Integer.compare(c2, c1);
}
});
// with negative charges we have the small o1.fragments first
negChargeList.sort(new Comparator() {
//@Override Annotation incompatible with 1.5
public int compare(InventorCharge o1, InventorCharge o2) {
int c1 = o1.fragment.size();
int c2 = o1.fragment.size();
return Integer.compare(c1, c2);
}
});
// we combine preferably small with large
for (InventorCharge pc:posChargeList) {
for (InventorCharge nc : negChargeList) {
if (pc.charge == -nc.charge) {
mUnPairedCharge[pc.fragment.getGlobalAtom(pc.atom)] -= pc.charge;
mUnPairedCharge[nc.fragment.getGlobalAtom(nc.atom)] -= nc.charge;
return new FragmentAssociation(pc.fragment, nc.fragment, pc.atom, nc.atom);
}
}
}
for (InventorCharge pc:posChargeList) {
for (InventorCharge nc : negChargeList) {
if (pc.charge > -nc.charge) {
mUnPairedCharge[pc.fragment.getGlobalAtom(pc.atom)] += nc.charge;
mUnPairedCharge[nc.fragment.getGlobalAtom(nc.atom)] -= nc.charge;
return new FragmentAssociation(pc.fragment, nc.fragment, pc.atom, nc.atom);
}
}
}
for (InventorCharge pc:posChargeList) {
for (InventorCharge nc : negChargeList) {
if (pc.charge < -nc.charge) {
mUnPairedCharge[pc.fragment.getGlobalAtom(pc.atom)] -= pc.charge;
mUnPairedCharge[nc.fragment.getGlobalAtom(nc.atom)] += pc.charge;
return new FragmentAssociation(pc.fragment, nc.fragment, pc.atom, nc.atom);
}
}
}
return null;
}
private FragmentAssociation createDisconnectedAssociation() {
if (mFragmentList.size() < 2)
return null;
return new FragmentAssociation(mFragmentList.get(0), mFragmentList.get(1));
}
/* ingenious solution to whether we have a cyclic relationship between fragments and metal bond associations
private boolean isCyclic(FragmentAssociation[][] fa) {
for (int i=1; i