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

com.actelion.research.chem.IsomericSmilesCreator 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.chem.reaction.Reaction;

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

public class IsomericSmilesCreator {
	private StereoMolecule mMol;
	private Canonizer mCanonizer;
	private String mSmiles;
	private boolean mIncludeMapping;
	private int[] mAtomRank;
	private int[] mClosureNumber;
	private int[] mSmilesIndex;
	private int[] mClosureBuffer;
	private int[][] mKnownTHCountInESRGroup;
	private List mGraphAtomList;
	private boolean[] mAtomUsed;
	private boolean[] mBondUsed;
	private boolean[] mClosureOpened;
	private boolean[] mPseudoStereoGroupInversion;
	private boolean[] mPseudoStereoGroupInitialized;

	public static String createReactionSmiles(Reaction rxn) {
		StringBuilder sb = new StringBuilder();
		for (int i=0; i');
		for (int i=0; i');
		for (int i=0; i();

		int atom = findUnusedStartAtom();
		while (atom != -1) {
			int graphIndex = mGraphAtomList.size();
			addToGraph(new SmilesAtom(atom, -1, false, false), graphIndex);
			if (mMol.getConnAtoms(atom) != 0) {
				addHighestRankingChain(graphIndex, false);

				while (graphIndex < mGraphAtomList.size() - 1) {
					while (hasUnusedNeighborAtom(mGraphAtomList.get(graphIndex).atom))
						addHighestRankingChain(graphIndex, true);
					graphIndex++;
				}
			}

			// we may have multiple unconnected fragments
			atom = findUnusedStartAtom();
		}

		// assign a smiles position index to every atom (needed for stereo assignment)
		mSmilesIndex = new int[mMol.getAtoms()];
		int index = 0;
		for (SmilesAtom smilesAtom:mGraphAtomList)
			mSmilesIndex[smilesAtom.atom] = index++;
	}

	private void findRingClosures() {
		boolean[] closureNumberUsed = new boolean[mMol.getBonds()];
		mClosureNumber = new int[mMol.getBonds()];

		for (SmilesAtom smilesAtom:mGraphAtomList) {
			for (int i=0; i rank) {
					startRank = rank;
					startAtom = atom;
				}
			}
		}

		return startAtom;
	}

	private boolean hasUnusedNeighborAtom(int atom) {
		for (int i=0; i 1)
					builder.append(Integer.toString(hCount));
			}
		}

		if (charge != 0) {
			builder.append(charge > 0 ? '+' : '-');
			if (Math.abs(charge) > 1)
				builder.append(Integer.toString(Math.abs(charge)));
		}

		if (mapNo != 0) {
			builder.append(':');
			builder.append(Integer.toString(mapNo));
		}

		if (useBrackets)
			builder.append(']');

		appendClosureBonds(smilesAtom, builder);

		if (smilesAtom.isSideChainEnd)
			builder.append(')');
	}

	/**
	 * Don't store nitrogen parities in SMILES unless we have a quarternary nitrogen.
	 * Some software packages seem to have promblems when decoding parities on nitrogen atoms.
	 * @param atom
	 * @return
	 */
	private boolean qualifiesForAtomParity(int atom) {
		return (mMol.getAtomParity(atom) == Molecule.cAtomParity1
			 || mMol.getAtomParity(atom) == Molecule.cAtomParity2)
			&& !isSingleKnownStereoCenterInESRGroup(atom)
			&& (mMol.getAtomicNo(atom) != 7 || mMol.getAtomCharge(atom) > 0);
	}

	private boolean isSingleKnownStereoCenterInESRGroup(int atom) {
		int type = mMol.getAtomESRType(atom) - 1;
		return type == -1 ? false : mKnownTHCountInESRGroup[type][mMol.getAtomESRGroup(atom)] <= 1;
	}

	private void appendClosureBonds(SmilesAtom smilesAtom, StringBuilder builder) {
		int closureCount = 0;
		for (int i=0; i> 20);
				if (!mClosureOpened[bond]) {
					mClosureOpened[bond] = true;
					appendBondOrderSymbol(bond, builder);
				}
				if (closureNumber > 9)
					builder.append('%');
				builder.append(Integer.toString(closureNumber));
			}
		}
	}

	private void appendBondOrderSymbol(SmilesAtom smilesAtom, StringBuilder builder) {
		if (smilesAtom.ezHalfParity != 0) {
			builder.append(smilesAtom.ezHalfParity == 1 ? '/' : '\\');
			return;
		}
		appendBondOrderSymbol(mMol.getBond(smilesAtom.atom, smilesAtom.parent), builder);
	}

	private void appendBondOrderSymbol(int bond, StringBuilder builder) {
		if (!mMol.isAromaticBond(bond)) {
			int order = mMol.getBondType(bond) & Molecule.cBondTypeMaskSimple;
			if (order == Molecule.cBondTypeSingle) {
				if (mMol.isAromaticAtom(mMol.getBondAtom(0, bond))
				 && mMol.isAromaticAtom(mMol.getBondAtom(1, bond)))
					builder.append('-');
			} else if (order == Molecule.cBondTypeDouble)
				builder.append('=');
			else if (order == Molecule.cBondTypeTriple)
				builder.append('#');
//				else if (order == Molecule.cBondTypeQuadruple)	// not supported by Molecule
//					builder.append('$');
		}
	}

	private String getAtomParitySymbol(int atom, int parent) {
		boolean inversion = false;
		if (mMol.getAtomPi(atom) != 0
		 && mMol.getConnAtoms(atom) == 2
		 && mMol.getConnBondOrder(atom,0) == 2
		 && mMol.getConnBondOrder(atom,1) == 2) {   // allene parities
			for (int i=0; i neighborRank[1])
				inversion = !inversion;
			if (neighborRank[0] > neighborRank[2])
				inversion = !inversion;
			if (neighborRank[1] > neighborRank[2])
				inversion = !inversion;

			if (neighborAtom[0] > neighborAtom[1])
				inversion = !inversion;
			if (neighborAtom[0] > neighborAtom[2])
				inversion = !inversion;
			if (neighborAtom[1] > neighborAtom[2])
				inversion = !inversion;

			for (int i=0; i<3; i++)
				if (parent > neighborAtom[i])
					inversion = !inversion;
		}

		boolean isClockwise = ((mMol.getAtomParity(atom) == Molecule.cAtomParity1) ^ inversion);

		if (mMol.isAtomParityPseudo(atom)) {
			int group = mCanonizer.getPseudoTHGroup(atom);
			if (!mPseudoStereoGroupInitialized[group]) {
				mPseudoStereoGroupInitialized[group] = true;
				mPseudoStereoGroupInversion[group] = isClockwise;
			}
			if (mPseudoStereoGroupInversion[group])
				isClockwise = !isClockwise;
		}

		return isClockwise ? "@@" : "@";
	}

	/**
	 * @param atom for which to return a neighbor's smiles rank
	 * @param neighborIndex index for getConnAtoms() to get neighbor atom and bond
	 * @return neighbor's position rank in smiles from a perspective of atom using closure digit positions for closure neighbors
	 */
	private int getSmilesRank(int atom, int neighborIndex) {
		int bond = mMol.getConnBond(atom, neighborIndex);
		if (mClosureNumber[bond] != 0) {
			// if neighbor is attached via a closure digit, then the rank is based primarily on atom's position
			// in the smiles and secondary on the count of other closures at atom that precede this closure
			int rank = 8 * mSmilesIndex[atom] + 1;
			for (int i=0; i= 5 && atomicNo <= 9)     // B,C,N,O,F
			|| (atomicNo >= 15 && atomicNo <= 17)   // P,S,Cl
			|| atomicNo == 35                       // Br
			|| atomicNo == 53;                      // I
	}
}

class SmilesAtom {
	public int atom,parent,ezHalfParity;
	public boolean isSideChainStart,isSideChainEnd;

	public SmilesAtom(int atom, int parent, boolean isSideChainStart, boolean isSideChainEnd) {
		this.atom = atom;
		this.parent = parent;
		this.isSideChainStart = isSideChainStart;
		this.isSideChainEnd = isSideChainEnd;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy