Skip to content

Commit

Permalink
Conversion to/from inline reaction (molecules) that will allow us to …
Browse files Browse the repository at this point in the history
…easily implement matching.
  • Loading branch information
johnmay committed Jun 3, 2016
1 parent de5fbcc commit 729f5db
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 0 deletions.
16 changes: 16 additions & 0 deletions base/core/src/main/java/org/openscience/cdk/CDKConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,22 @@ public class CDKConstants {
*/
public static final String RELEVANT_RINGS = "cdk:RelevantRings";

/**
* Property used for reactions when converted to/from molecules. It defines what role and atom
* has an a reaction.
*
* Used in. ReactionManipulator.toMolecule and ReactionManipulator.toReaction.
*/
public static final String REACTION_ROLE = "cdk:ReactionRole";

/**
* Property used for reactions when converted to/from molecules. It defines fragment grouping, for example
* when handling ionic components.
*
* Used in. ReactionManipulator.toMolecule and ReactionManipulator.toReaction.
*/
public static final String REACTION_GROUP = "cdk:ReactionGroup";

/* **************************************
* Some predefined property names for * Atoms *
* **************************************
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,26 @@
* */
package org.openscience.cdk.tools.manipulator;

import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.ReactionRole;
import org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.cdk.interfaces.IAtomContainerSet;
import org.openscience.cdk.interfaces.IBond;
import org.openscience.cdk.interfaces.IChemObject;
import org.openscience.cdk.interfaces.IChemObjectBuilder;
import org.openscience.cdk.interfaces.IDoubleBondStereochemistry;
import org.openscience.cdk.interfaces.IElectronContainer;
import org.openscience.cdk.interfaces.IMapping;
import org.openscience.cdk.interfaces.IReaction;
import org.openscience.cdk.interfaces.IStereoElement;
import org.openscience.cdk.interfaces.ITetrahedralChirality;
import org.openscience.cdk.stereo.ExtendedTetrahedral;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* @cdk.module standard
Expand Down Expand Up @@ -257,4 +266,135 @@ public static IChemObject getMappedChemObject(IReaction reaction, IChemObject ch
return null;
}

/**
* Assigns a reaction role and group id to all atoms in a molecule.
*
* @param mol molecule
* @param role role to assign
* @param grpId group id
*/
private static void assignRoleAndGrp(IAtomContainer mol, ReactionRole role, int grpId) {
for (IAtom atom : mol.atoms()) {
atom.setProperty(CDKConstants.REACTION_ROLE, role);
atom.setProperty(CDKConstants.REACTION_GROUP, grpId);
}
}

/**
* <p>Converts a reaction to an 'inlined' reaction stored as a molecule. All
* reactants, agents, products are added to the molecule as disconnected
* components with atoms flagged as to their role {@link ReactionRole} and
* component group.</p>
* <p>
* The inlined reaction, stored in a molecule can be converted back to an explicit
* reaction with {@link #toReaction}. Data stored on the individual components (e.g.
* titles is lost in the conversion).
* </p>
*
* @param rxn reaction to convert
* @return inlined reaction stored in a molecule
* @see #toReaction
*/
public static IAtomContainer toMolecule(IReaction rxn) {
if (rxn == null)
throw new IllegalArgumentException("Null reaction provided");
final IChemObjectBuilder bldr = rxn.getBuilder();
final IAtomContainer mol = bldr.newInstance(IAtomContainer.class);
mol.setProperties(rxn.getProperties());
mol.setID(rxn.getID());
int grpId = 0;
for (IAtomContainer comp : rxn.getReactants().atomContainers()) {
assignRoleAndGrp(comp, ReactionRole.Reactant, ++grpId);
mol.add(comp);
}
for (IAtomContainer comp : rxn.getAgents().atomContainers()) {
assignRoleAndGrp(comp, ReactionRole.Agent, ++grpId);
mol.add(comp);
}
for (IAtomContainer comp : rxn.getProducts().atomContainers()) {
assignRoleAndGrp(comp, ReactionRole.Product, ++grpId);
mol.add(comp);
}
return mol;
}

/**
* <p>Converts an 'inlined' reaction stored in a molecule back to a reaction.</p>
*
* @param mol molecule to convert
* @return reaction
* @see #toMolecule(IReaction)
*/
public static IReaction toReaction(IAtomContainer mol) {
if (mol == null)
throw new IllegalArgumentException("Null molecule provided");
final IChemObjectBuilder bldr = mol.getBuilder();
final IReaction rxn = bldr.newInstance(IReaction.class);
rxn.setProperties(mol.getProperties());
rxn.setID(mol.getID());

Map<Integer,IAtomContainer> components = new HashMap<>();

// split atoms
for (IAtom atom : mol.atoms()) {
ReactionRole role = atom.getProperty(CDKConstants.REACTION_ROLE);
Integer grpIdx = atom.getProperty(CDKConstants.REACTION_GROUP);

if (role == null || role == ReactionRole.None)
throw new IllegalArgumentException("Atom " + mol.getAtomNumber(atom) + " had undefined role");
if (grpIdx == null)
throw new IllegalArgumentException("Atom " + mol.getAtomNumber(atom) + " had no reaction group id");

IAtomContainer comp = components.get(grpIdx);

// new component, and add to appropriate role
if (comp == null) {
components.put(grpIdx, comp = bldr.newInstance(IAtomContainer.class, 20, 20, 0, 0));
switch (role) {
case Reactant:
rxn.addReactant(comp);
break;
case Product:
rxn.addProduct(comp);
break;
case Agent:
rxn.addAgent(comp);
break;
}
}

comp.addAtom(atom);
}

// split bonds
for (IBond bond : mol.bonds()) {
IAtom beg = bond.getAtom(0);
IAtom end = bond.getAtom(1);
Integer begIdx = beg.getProperty(CDKConstants.REACTION_GROUP);
Integer endIdx = end.getProperty(CDKConstants.REACTION_GROUP);
if (begIdx == null || endIdx == null)
throw new IllegalArgumentException("Bond " + mol.getBondNumber(bond) + " had atoms with no reaction group id");
if (!begIdx.equals(endIdx))
throw new IllegalArgumentException("Bond " + mol.getBondNumber(bond) + " had atoms with different reaction group id");
components.get(begIdx).addBond(bond);
}

// split stereochemistry
for (IStereoElement se : mol.stereoElements()) {
IAtom focus = null;
if (se instanceof ITetrahedralChirality) {
focus = ((ITetrahedralChirality) se).getChiralAtom();
} else if (se instanceof IDoubleBondStereochemistry) {
focus = ((IDoubleBondStereochemistry) se).getStereoBond().getAtom(0);
} else if (se instanceof ExtendedTetrahedral) {
focus = ((ExtendedTetrahedral) se).focus();
}
if (focus == null)
throw new IllegalArgumentException("Stereochemistry had no focus");
Integer grpIdx = focus.getProperty(CDKConstants.REACTION_GROUP);
components.get(grpIdx).addStereoElement(se);
}

return rxn;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.openscience.cdk.Reaction;
import org.openscience.cdk.ReactionSet;
import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.exception.InvalidSmilesException;
import org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.cdk.interfaces.IBond;
Expand All @@ -41,8 +42,13 @@
import org.openscience.cdk.interfaces.IMapping;
import org.openscience.cdk.interfaces.IReaction;
import org.openscience.cdk.io.MDLRXNReader;
import org.openscience.cdk.silent.SilentChemObjectBuilder;
import org.openscience.cdk.smiles.SmilesGenerator;
import org.openscience.cdk.smiles.SmilesParser;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

/**
* @cdk.module test-standard
*
Expand Down Expand Up @@ -291,4 +297,17 @@ public void testGetAllReactants_IReaction() {
Assert.assertEquals(2, ReactionManipulator.getAllProducts(reaction).getAtomContainerCount());
}

@Test public void inliningReactions() throws CDKException {
IChemObjectBuilder bldr = SilentChemObjectBuilder.getInstance();
SmilesParser smipar = new SmilesParser(bldr);
IReaction reaction = smipar.parseReactionSmiles("CCO.CC(=O)O>[H+]>CCOC(=O)C.O ethyl esterification");
SmilesGenerator smigen = SmilesGenerator.isomeric();
// convert to molecule
IAtomContainer mol = ReactionManipulator.toMolecule(reaction);
assertThat(smigen.create(mol),
is("CCO.CC(=O)O.[H+].CCOC(=O)C.O"));
assertThat(smigen.createReactionSMILES(ReactionManipulator.toReaction(mol)),
is("CCO.CC(=O)O>[H+]>CCOC(=O)C.O"));
}

}

0 comments on commit 729f5db

Please sign in to comment.