All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.actelion.research.chem.ExtendedMolecule Maven / Gradle / Ivy

There is a newer version: 2024.12.1
Show newest version
/*
* 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.
*
*/

package com.actelion.research.chem;

import com.actelion.research.util.Angle;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * While the Molecule class covers all primary molecule information as atom and bond properties,
 * the atom connectivity and coordinates, its derived class ExtendedMolecule handles secondary,
 * i.e. calculated molecule information. Most important are the directly connected atoms and bonds
 * of every atom, information about rings and whether they are aromatic, and some atom properties
 * that depend on their near neighbors. This calculated information is cached in helper arrays and
 * stays valid as long as the molecule's primary information is not changed. High level methods,
 * e.g. getPath(), that require valid helper arrays take care of updating the cache themselves.
 * Low level methods, e.g. isAromaticAtom(), which typically are called very often, do not check
 * the validity of or update the helper arrays themselves for performance reasons. If you use low
 * level methods, then you need to make sure that the required helper array information is valid by
 * calling ensureHelperArrays().
 * Typically you will never instantiate an ExtendedMolecule, but rather use a StereoMolecule.
 */
public class ExtendedMolecule extends Molecule implements Serializable {
	static final long serialVersionUID = 0x2006CAFE;

	/**
	 * To interpret a stereo center as fisher projection, all non stereo
	 * bonds must be vertical and all stereo bonds must be horizontal.
	 * FISCHER_PROJECTION_LIMIT is the allowed tolerance (currently 5.0 degrees).
	 */
	public static final float FISCHER_PROJECTION_LIMIT = (float)Math.PI / 36;
	public static final float STEREO_ANGLE_LIMIT = (float)Math.PI / 36;   // 5 degrees

	public static final int cMaxConnAtoms = 16; // ExtendedMolecule is not restricted anymore
											   // However, this is a suggestion for editors and other classes
	transient private int mAtoms,mBonds;
	transient private RingCollection mRingSet;

	transient private int mPi[];
	transient private int mConnAtoms[];
	transient private int mAllConnAtoms[];
	transient private int mConnAtom[][];
	transient private int mConnBond[][];
	transient private int mConnBondOrder[][];

	public ExtendedMolecule() {
		}


	public ExtendedMolecule(int maxAtoms, int maxBonds) {
		super(maxAtoms, maxBonds);
		}

	public ExtendedMolecule(Molecule mol) {
		super(mol.getMaxAtoms(), mol.getMaxBonds());
		mol.copyMolecule(this);
		}


	/**
	 * Copies a part of this Molecule into destMol, being defined by a mask of atoms to be included.
	 * If not all atoms are copied, then destMol is set to be a substructure fragment.
	 * @param destMol receives the part of this Molecule
	 * @param includeAtom defines atoms to be copied; its size may be this.getAtoms() or this.getAllAtoms()
	 * @param recognizeDelocalizedBonds defines whether disconnected delocalized bonds will keep their
	 * single/double bond status or whether the query feature 'delocalized bond' will be set
	 * @param atomMap null or int[] not smaller than includeAtom.length; receives atom indices of dest molecule
	 */
	public void copyMoleculeByAtoms(ExtendedMolecule destMol, boolean[] includeAtom, boolean recognizeDelocalizedBonds, int[] atomMap) {
		if (recognizeDelocalizedBonds)
			ensureHelperArrays(cHelperRings);

		destMol.mAtomList = null;
		if (mIsFragment)
			destMol.setFragment(true);

		int atomCount = includeAtom.length;

		if (atomMap == null)
			atomMap = new int[atomCount];

		destMol.mAllAtoms = 0;
		for (int atom=0; atom
	 * standard P valence is 3, used valence is 4, implicit abnormal valence is 5.
	 * The molecule is interpreted as O=PH2-OMe. Requires cHelperNeighbours!
	 * @param atom
	 * @param neglectExplicitHydrogen
	 * @return abnormal valence or -1 if valence doesn't exceed standard valence
	 */
	public int getImplicitHigherValence(int atom, boolean neglectExplicitHydrogen) {
		int occupiedValence = getOccupiedValence(atom) - getElectronValenceCorrection(atom);
		if (neglectExplicitHydrogen)
			occupiedValence -= mAllConnAtoms[atom] - mConnAtoms[atom];

		byte[] valenceList = (mAtomicNo[atom] < cAtomValence.length) ?
				cAtomValence[mAtomicNo[atom]] : null;

		int valence = (valenceList == null) ? cDefaultAtomValence : valenceList[0];
		if (occupiedValence <= valence)
			return -1;

/*	The error may not be the additional hydrogens but an omitted charge.
 *  Therefore, don't correct and just explain what we have.
 *  This old handling caused problems with implicit hydrogens in 3D id-coordinates
 *  because the number og implicit hydrogens was not correctly reproduced during idcode parsing.
 *  TLS 20-Jun-2015
 *
		// If we don't neglect hydrogen and don't have allowed higher valences
		// then consider explicit hydrogens as errors.
		if (!neglectExplicitHydrogen
		 && (valenceList == null || valenceList.length == 1)) {
			occupiedValence -= mAllConnAtoms[atom] - mConnAtoms[atom];
			return (occupiedValence <= valence) ? -1 : occupiedValence;
			}

		if (valenceList != null)
			for (int i=1; (valence 0) {
							pathAtom[index-1] = parentAtom[pathAtom[index]];
							index--;
							}
						return graphLevel[parent];
						}
	
					if (graphLevel[candidate] == 0) {
						graphAtom[++highest] = candidate;
						graphLevel[candidate] = graphLevel[parent]+1;
						parentAtom[candidate] = parent;
						}
					}
				}
			current++;
			}
		return -1;
		}


	/**
	 * Finds bonds of a path that is defined by an atom sequence.
	 * @param pathAtom pathAtom[0]...[pathLength] -> list of atoms on path 
	 * @param pathBond int array not smaller than pathLength
	 * @param pathLength no of path bonds == no of path atoms - 1
	 */
	public void getPathBonds(int[] pathAtom, int[] pathBond, int pathLength) {
		ensureHelperArrays(cHelperNeighbours);
		for (int i=0; i> 1;
			}

		occupiedValence -= getElectronValenceCorrection(atom);
		int maxValence = getAtomAbnormalValence(atom);
		if (maxValence == -1) {
			if (mAtomicNo[atom] >= 171 && mAtomicNo[atom] <= 190) {
				maxValence = 2;
				}
			else {
				byte[] valenceList = (mAtomicNo[atom] < cAtomValence.length) ?
						cAtomValence[mAtomicNo[atom]] : null;
				if (valenceList == null) {
					maxValence = cDefaultAtomValence;
					}
				else {
					maxValence = valenceList[0];
					for (int i=1; (maxValence= 171 && mAtomicNo[atom] <= 190
			 && mAllConnAtoms[atom] > 2)	// to accomodate for bisulfide bridges
				molweight -= (mAllConnAtoms[atom] - 2) * cRoundedMass[1];
			}

		return molweight;
		}

	/**
	 * Simple method to calculate rotatable bonds. This method counts all single
	 * bonds provided that they
* - are not a terminal bond
* - are not part of a ring
* - are not an amide bond
* - are not the second of two equivalent bonds next to the same triple bond
* @return */ public int getRotatableBondCount() { int rCount = 0; ensureHelperArrays(Molecule.cHelperRings); for (int bond=0; bond 1 && !isAromaticAtom(connAtom) && isElectronegative(connAtom)) { isRotatable = false; break; // amid bond } } } } if (isRotatable && !isPseudoRotatableBond(bond)) rCount++; } } return rCount; } /** * In a consecutive sequence of sp-hybridized atoms multiple single bonds * cause redundant torsions. Only that single bond with the smallest bond index * is considered really rotatable; all other single bonds are pseudo rotatable. * If one/both end(s) of the sp-atom sequence doesn't carry atoms * outside of the straight line then no bond is considered rotatable. * A simple terminal single bond * @param bond * @return true, if this bond is not considered rotatable because of a redundancy */ public boolean isPseudoRotatableBond(int bond) { if (getBondOrder(bond) != 1) return false; for (int i=0; i<2; i++) { int atom = mBondAtom[i][bond]; int rearAtom = mBondAtom[1-i][bond]; while (mPi[atom] == 2 && mConnAtoms[atom] == 2 && mAtomicNo[atom] < 10) { for (int j=0; j<2; j++) { int connAtom = mConnAtom[atom][j]; if (connAtom != rearAtom) { if (mConnAtoms[connAtom] == 1) return true; int connBond = mConnBond[atom][j]; if (getBondOrder(connBond) == 1 && connBond < bond) return true; rearAtom = atom; atom = connAtom; break; } } } if (mConnAtoms[atom] == 1) return true; } return false; } public int getAromaticRingCount() { ensureHelperArrays(cHelperRings); int count = 0; for (int i=0; i * This method tells the molecule that current atom/bond parities are valid, even if the * stereo perception not has been performed. In addition to the stereo parities on may * declare CIP parities and/or symmetry ranks also to be valid (helperStereoBits != 0). * This method should be called if no coordinates are available but the parities are valid * nevertheless, e.g. when the IDCodeParser parses an idcode without coordinates. * (Note: After idcode parsing unknown stereo centers have parities cAtomParityNone * instead of cAtomParityUnknown. Thus, calling isStereoCenter(atom) returns false!!!) * Declaring parities valid prevents the Canonizer to run the stereo recognition again when * ensureHelperArrays(cHelperParities or higher) is called.
* May also be called after filling valences with explicit hydrogen atoms, which have no * coordinates, to tell the molecule that the earlier created stereo flags are still valid. * @param helperStereoBits 0 or combinations of cHelperBitCIP,cHelperBitSymmetry...,cHelperBitIncludeNitrogenParities */ public void setParitiesValid(int helperStereoBits) { mValidHelperArrays |= (cHelperBitsStereo & (cHelperBitParities | helperStereoBits)); } /** * This converts one single bond per parity into a stereo up/down bond to * correctly reflect the given parity. This works for tetrahedral and * allene atom parities as well as for BINAP type of bond parities. * Should only be called with valid TH and EZ parities and valid coordinates, * e.g. after idcode parsing with coordinates or after coordinate generation. */ public void setStereoBondsFromParity() { ensureHelperArrays(cHelperRings); // in case we miss ring and neighbour information for (int atom=0; atom 4) { setAtomParity(atom, cAtomParityNone, false); return; } int[] sortedConnMap = getSortedConnMap(atom); float angle[] = new float[mAllConnAtoms[atom]]; for (int i=0; i sortedConn[i-1]) && lowestConnAtom > mConnAtom[atom][j]) { lowestConnAtom = mConnAtom[atom][j]; lowestConnIndex = j; } } sortedConn[i] = lowestConnAtom; if (mConnBond[atom][lowestConnIndex] == preferredBond) preferredBondIndex = i; } */ for (int i=1; i angle[2]) && (angle[1] - angle[2] > Math.PI))); break; case 1: inverted = (angle[2] - angle[0] > Math.PI); break; case 2: inverted = (angle[1] - angle[0] < Math.PI); break; } bondType = ((getAtomParity(atom) == cAtomParity1) ^ inverted) ? cBondTypeUp : cBondTypeDown; /* // original handling where parity depended solely on the clockwise/anti-clockwise of three angles bondType = ((getAtomParity(atom) == cAtomParity1) ^ (angle[1] > angle[2])) ? cBondTypeDown : cBondTypeUp; */ } else { int order = 0; if (angle[1] <= angle[2] && angle[2] <= angle[3]) order = 0; else if (angle[1] <= angle[3] && angle[3] <= angle[2]) order = 1; else if (angle[2] <= angle[1] && angle[1] <= angle[3]) order = 2; else if (angle[2] <= angle[3] && angle[3] <= angle[1]) order = 3; else if (angle[3] <= angle[1] && angle[1] <= angle[2]) order = 4; else if (angle[3] <= angle[2] && angle[2] <= angle[1]) order = 5; bondType = ((getAtomParity(atom) == cAtomParity1) ^ (up_down[order][preferredBondIndex] == 1)) ? cBondTypeDown : cBondTypeUp; } mBondType[preferredBond] = bondType; } private boolean setFisherProjectionStereoBondsFromParity(int atom, int[] sortedConnMap, float[] angle) { int[] direction = new int[mAllConnAtoms[atom]]; int parity = getFisherProjectionParity(atom, sortedConnMap, angle, direction); if (parity == cAtomParityUnknown) return false; int bondType = (getAtomParity(atom) == parity) ? cBondTypeUp : cBondTypeDown; for (int i=0; i 4) return false; boolean[] isUsed = new boolean[4]; for (int i=0; i FISCHER_PROJECTION_LIMIT) return false; direction[i] = 3 & (int)(a / (Math.PI/2)); if (isUsed[direction[i]]) return false; isUsed[direction[i]] = true; if ((direction[i] & 1) == 0) { // vertical bond if (mBondType[mConnBond[atom][sortedConnMap[i]]] != cBondTypeSingle) return false; } else { if (!isStereoBond(mConnBond[atom][sortedConnMap[i]], atom)) return false; } } return isUsed[0] && isUsed[2]; } private void setAlleneStereoBondFromParity(int atom) { // find preferred bond to serve as stereobond if (mConnAtoms[atom] != 2 || mConnBondOrder[atom][0] != 2 || mConnBondOrder[atom][1] != 2 || mConnAtoms[mConnAtom[atom][0]] < 2 || mConnAtoms[mConnAtom[atom][1]] < 2 || mPi[mConnAtom[atom][0]] != 1 || mPi[mConnAtom[atom][1]] != 1) { setAtomParity(atom, cAtomParityNone, false); return; } int preferredBond = -1; int preferredAtom = -1; int preferredAlleneAtom = -1; int oppositeAlleneAtom = -1; int bestScore = 0; for (int i=0; i<2; i++) { int alleneAtom = mConnAtom[atom][i]; for (int j=0; j connAtom)) highPriorityAtom = connAtom; } int[] oppositeAtom = new int[2]; int oppositeAtoms = 0; for (int i=0; i oppositeAtom[1]) { int temp = oppositeAtom[0]; oppositeAtom[0] = oppositeAtom[1]; oppositeAtom[1] = temp; } float hpAngleDif = getAngleDif(alleneAngle, getBondAngle(oppositeAlleneAtom, oppositeAtom[0])); float lpAngleDif = getAngleDif(alleneAngle, getBondAngle(oppositeAlleneAtom, oppositeAtom[1])); angleDif = hpAngleDif - lpAngleDif; } else { angleDif = getAngleDif(alleneAngle, getBondAngle(oppositeAlleneAtom, oppositeAtom[0])); } if ((angleDif < 0.0) ^ (getAtomParity(atom) == cAtomParity1) ^ (highPriorityAtom == preferredAtom)) mBondType[preferredBond] = cBondTypeUp; else mBondType[preferredBond] = cBondTypeDown; } /** * In case bond is a BINAP kind of chiral bond with defined parity, * then the preferred neighbour single bond is converted into a * stereo bond to correctly reflect its defined parity. * @param bond */ public void setStereoBondFromBondParity(int bond) { // set an optimal bond to up/down to reflect the atom parity if (getBondParity(bond) == Molecule.cBondParityNone || getBondParity(bond) == Molecule.cBondParityUnknown || !isBINAPChiralityBond(bond)) return; int preferredBond = -1; int preferredAtom = -1; int preferredBINAPAtom = -1; int oppositeBINAPAtom = -1; int bestScore = 0; for (int i=0; i<2; i++) { int atom = mBondAtom[i][bond]; for (int j=0; j connAtom)) highPriorityAtom = connAtom; } int[] oppositeAtom = new int[2]; int oppositeAtoms = 0; for (int i=0; i oppositeAtom[1]) { int temp = oppositeAtom[0]; oppositeAtom[0] = oppositeAtom[1]; oppositeAtom[1] = temp; } float hpAngleDif = getAngleDif(binapAngle, getBondAngle(oppositeBINAPAtom, oppositeAtom[0])); float lpAngleDif = getAngleDif(binapAngle, getBondAngle(oppositeBINAPAtom, oppositeAtom[1])); angleDif = hpAngleDif - lpAngleDif; } else { angleDif = getAngleDif(binapAngle, getBondAngle(oppositeBINAPAtom, oppositeAtom[0])); } if ((angleDif < 0.0) ^ (getBondParity(bond) == cBondParityZor2) ^ (highPriorityAtom == preferredAtom)) mBondType[preferredBond] = cBondTypeUp; else mBondType[preferredBond] = cBondTypeDown; } protected boolean bondsAreParallel(float angle1, float angle2) { float angleDif = Math.abs(getAngleDif(angle1, angle2)); return (angleDif < 0.08 || angleDif > Math.PI - 0.08); } private int preferredTHStereoBond(int atom) { // If we have two (anti-)parallel bonds, the we need to select // that one of those that is closest to the other bonds. float[] angle = new float[mAllConnAtoms[atom]]; for (int i=0; i= 6) for (int i=0; i= 6) for (int i=0; i= 5) { int orthoSubstituentCount = 0; for (int j=0; j= 3) orthoSubstituentCount++; } if (orthoSubstituentCount == 2 || (orthoSubstituentCount == 1 && mConnAtoms[atom] == 3)) continue; // the nitrogen is rotated out of PI-plane } return true; } // vinyloge amides, etc. for (int j=0; j 2) orthoSubstituentCount++; } for (int j=0; j 2) orthoSubstituentCount++; } return (orthoSubstituentCount > 2); } protected boolean validateBondType(int bond, int type) { boolean ok = super.validateBondType(bond, type); if (ok && type == cBondTypeCross) { ensureHelperArrays(Molecule.cHelperRings); ok &= !isSmallRingBond(bond); } return ok; } public void validate() throws Exception { float avbl = getAverageBondLength(); float minDistanceSquare = avbl * avbl / 16.0f; for (int atom1=1; atom1 getMaxValence(atom)) throw new Exception("atom valence exceeded"); allCharge += mAtomCharge[atom]; } if (allCharge != 0) throw new Exception("unbalanced atom charge"); } /** * Normalizes different forms of functional groups (e.g. nitro) * to a preferred one. This step should precede any canonicalization. * @return true if the molecule was changed */ public boolean normalizeAmbiguousBonds() { ensureHelperArrays(cHelperNeighbours); boolean found = false; for (int atom=0; atom 0 && mAtomCharge[mBondAtom[1][bond]] < 0) { atom1 = mBondAtom[0][bond]; atom2 = mBondAtom[1][bond]; } else if (mAtomCharge[mBondAtom[0][bond]] < 0 && mAtomCharge[mBondAtom[1][bond]] > 0) { atom1 = mBondAtom[1][bond]; atom2 = mBondAtom[0][bond]; } else continue; if (mAtomicNo[atom1] < 9) if (getOccupiedValence(atom1) > 3) continue; mAtomCharge[atom1] -= 1; mAtomCharge[atom2] += 1; if (getBondOrder(bond) == 1) mBondType[bond] = cBondTypeDouble; else mBondType[bond] = cBondTypeTriple; mValidHelperArrays = cHelperNone; } } int overallCharge = 0; int negativeAtomCount = 0; int negativeAdjustableCharge = 0; for (int atom=0; atom 0) { if (!hasNegativeNeighbour(atom) && isElectronegative(atom)) { int chargeReduction = Math.min(getImplicitHydrogens(atom), mAtomCharge[atom]); if (chargeReduction != 0 && negativeAdjustableCharge >= chargeReduction) { overallChargeChange -= chargeReduction; negativeAdjustableCharge += chargeReduction; mAtomCharge[atom] -= chargeReduction; mValidHelperArrays &= cHelperNeighbours; } } } } if (overallChargeChange < 0) { int[] negativeAtom = new int[negativeAtomCount]; negativeAtomCount = 0; for (int atom=0; atom=negativeAtom.length-negativeAtomCount); i--) { int atom = negativeAtom[i] & 0x0000FFFF; if (isElectronegative(atom)) { int chargeReduction = Math.min(-overallChargeChange, -mAtomCharge[atom]); overallChargeChange += chargeReduction; mAtomCharge[atom] += chargeReduction; mValidHelperArrays &= cHelperNeighbours; } } } return overallCharge; } /** * @param atom * @return true if atom has a neighbour with a negative charge */ private boolean hasNegativeNeighbour(int atom) { for (int i=0; i 0) return true; return false; } /** * While the Molecule class covers all primary molecule information, its derived class * ExtendedMolecule handles secondary, i.e. calculated molecule information, which is cached * in helper arrays and stays valid as long as the molecule's primary information is not changed. * Most methods of ExtendedMolecule require some of the helper array's information. High level * methods, e.g. getPath(), take care of updating an outdated cache themselves. Low level methods, * e.g. isAromaticAtom(), which typically are called very often, do not check for validity * nor update the helper arrays themselves. If you use low level methods, then you need to make * sure that the needed helper array information is valid by this method.
* For performance reasons there are distinct levels of helper information. (A higher * level always includes all properties of the previous level):
* cHelperNeighbours: explicit hydrogen atoms are moved to the end of the atom table and * bonds leading to them are moved to the end of the bond table. This way algorithms can skip * hydrogen atoms easily. For every atom directly connected atoms and bonds (with and without * hydrogens) are determined. The number of pi electrons is counted.
* cHelperRings: Aromatic and non-aromatic rings are detected. Atom and bond ring * properties are set and a ring collection provides a total set of small rings (7 or less atoms). * Atoms being in allylic/benzylic or stabilized (neighbor of a carbonyl or similar group) position * are flagged as such.
* cHelperParities: Atom (tetrahedral or axial) and bond (E/Z or atrop) parities are calculated * from the stereo configurations.
* cHelperCIP: Cahn-Ingold-Prelog stereo information for atoms and bonds.
*
cHelperParities and cHelperCIP require a StereoMolecule!!!
* @param required one of cHelperNeighbours,cHelperRings,cHelperParities,cHelperCIP * @return true if the molecule was changed */ public void ensureHelperArrays(int required) { // cHelperNeighbours: mConnAtoms,mConnBonds,mPi for all atoms // cHelperRings: rings,aromaticity/allylic/stabilized for non-H-atoms only // cHelperParities: stereo parities for non-H-atoms/bonds only // cHelperCIP: mCanonizer, stereo parities for non-H-atoms/bonds only if ((required & ~mValidHelperArrays) == 0) return; if ((mValidHelperArrays & cHelperBitNeighbours) == 0) { handleHydrogens(); calculateNeighbours(); mValidHelperArrays |= cHelperBitNeighbours; if (validateQueryFeatures()) { handleHydrogens(); calculateNeighbours(); } } if ((required & ~mValidHelperArrays) == 0) return; if ((mValidHelperArrays & cHelperBitRings) == 0) { for (int atom=0; atom 1) { if (mAtomicNo[mConnAtom[connAtom][j]] == 6) mAtomFlags[atom] |= cAtomFlagAllylic; else { if (!isAromaticBond(mConnBond[connAtom][j]) && isElectronegative(mConnAtom[connAtom][j])) mAtomFlags[atom] |= cAtomFlagStabilized; } } } } } // propagate stabilized flags to vinylic positions while (true) { boolean found = false; for (int atom=0; atom 0 && ((cAtomFlagStabilized | cAtomFlagAromatic) & mAtomFlags[atom]) == cAtomFlagStabilized) { for (int i=0; i 1) { int connAtom = mConnAtom[atom][i]; int connBond = mConnBond[atom][i]; for (int j=0; j * This method moves all simple hydrogen atoms and associated bonds to the end of the atom/bond tables. * It sets mAtoms to exclude simple hydrogen atoms and mBonds to exclude bonds leading to them. * Simple hydrogens are not deleted, though. They are always displayed and the stereo perception * still considers up/down bonds leading to hydrogen atoms. Most other functions, however, can * happily neglect them.
* mConnAtoms/mConnBonds/mConnBondOrder are neither used nor updated.
* Note: This method changes the order among the non-hydrogen atoms. To translate to the original * order use getHandleHydrogenMap() before calling ensureHelperArrays() if the original atom order is relevant. */ private void handleHydrogens() { // find all hydrogens that are connected to a non-H atom and therefore can be implicit boolean[] isSimpleHydrogen = new boolean[mAllAtoms]; for (int bond=0; bond= 0) && isSimpleHydrogen[lastNonHAtom]); for (int atom=0; atom= 0) && isHydrogenBond[lastNonHBond]); for (int bond=0; bond= 0 && isSimpleHydrogen[lastNonHAtom]) { map[lastNonHAtom] = lastNonHAtom; lastNonHAtom--; } for (int atom=0; atom<=lastNonHAtom; atom++) { if (isSimpleHydrogen[atom]) { map[atom] = lastNonHAtom; map[lastNonHAtom] = atom; lastNonHAtom--; while (lastNonHAtom >= 0 && isSimpleHydrogen[lastNonHAtom]) { map[lastNonHAtom] = lastNonHAtom; lastNonHAtom--; } } else { map[atom] = atom; } } return map; } public boolean isSimpleHydrogen(int atom) { return mAtomicNo[atom] == 1 && mAtomMass[atom] == 0 && (mAtomCustomLabel == null || mAtomCustomLabel[atom] == null); } /** * Removes all plain explicit hydrogens atoms from the molecule, converting them * effectively to implicit ones. If an associated bond is a stereo bond indicating * a specific configuration, then another bond is converted to a stereo bond to reflect * the correct stereo geometry. If the removal of a hydrogen atom would change an atom's * implicit valance, the atom's abnormal valence is set accordingly. */ public void removeExplicitHydrogens() { ensureHelperArrays(cHelperParities); // to calculate stereo center parities mAllAtoms = mAtoms; mAllBonds = mBonds; for (int atom=0; atom 1) mPi[atom] += order + order - 2; else if (mBondType[bnd] == cBondTypeDelocalized) mPi[atom] = 2; } } } for(int atom=0; atom 3) mAtomFlags[atom] |= cAtomFlags4RingBonds; } for (int ringNo=0; ringNo= getMaxValence(atom)) mAtomQueryFeatures[atom] &= ~(cAtomQFNoMoreNeighbours | cAtomQFMoreNeighbours); // approximate explicit hydrogens by query features // and remove explicit hydrogens except those with stereo bonds boolean deleteHydrogens = false; for (int atom=0; atom 0) { if ((mAtomQueryFeatures[atom] & cAtomQFNoMoreNeighbours) == 0) { if (getFreeValence(atom) == 0) mAtomQueryFeatures[atom] |= cAtomQFNoMoreNeighbours; else { // add query feature hydrogen to explicit hydrogens int queryFeatureHydrogens = 0; if ((mAtomQueryFeatures[atom] & cAtomQFNot0Hydrogen) == cAtomQFNot0Hydrogen) queryFeatureHydrogens++; if ((mAtomQueryFeatures[atom] & cAtomQFHydrogen) == (cAtomQFNot0Hydrogen | cAtomQFNot1Hydrogen)) queryFeatureHydrogens++; mAtomQueryFeatures[atom] &= ~cAtomQFHydrogen; if (getFreeValence(atom) <= queryFeatureHydrogens) mAtomQueryFeatures[atom] |= cAtomQFNoMoreNeighbours; else if (queryFeatureHydrogens == 0) mAtomQueryFeatures[atom] |= cAtomQFNot0Hydrogen; else // queryFeatureHydrogens > 1 mAtomQueryFeatures[atom] |= (cAtomQFNot0Hydrogen | cAtomQFNot1Hydrogen); } } for (int i=mConnAtoms[atom]; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy