Skip to content

Commit

Permalink
Round trip positional variation in Ctab V3000.
Browse files Browse the repository at this point in the history
  • Loading branch information
johnmay committed Nov 10, 2015
1 parent 378d50b commit e336783
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 10 deletions.
Expand Up @@ -24,8 +24,11 @@
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
Expand All @@ -46,6 +49,8 @@
import org.openscience.cdk.interfaces.IPseudoAtom;
import org.openscience.cdk.io.formats.IResourceFormat;
import org.openscience.cdk.io.formats.MDLV3000Format;
import org.openscience.cdk.sgroup.Sgroup;
import org.openscience.cdk.sgroup.SgroupType;
import org.openscience.cdk.tools.ILoggingTool;
import org.openscience.cdk.tools.LoggingToolFactory;
import org.openscience.cdk.tools.manipulator.AtomContainerManipulator;
Expand Down Expand Up @@ -142,7 +147,7 @@ public <T extends IChemObject> T read(T object) throws CDKException {
}

public IAtomContainer readMolecule(IChemObjectBuilder builder) throws CDKException {
return builder.newInstance(IAtomContainer.class, readConnectionTable(builder));
return readConnectionTable(builder);
}

public IAtomContainer readConnectionTable(IChemObjectBuilder builder) throws CDKException {
Expand Down Expand Up @@ -393,12 +398,14 @@ public void readBondBlock(IAtomContainer readData) throws CDKException {
logger.debug(exception);
throw new CDKException(error, exception);
}

List<IAtom> endpts = new ArrayList<>();
String attach = null;

// the rest are key=value fields
if (command.indexOf('=') != -1) {
Map<String, String> options = parseOptions(exhaustStringTokenizer(tokenizer));
Iterator<String> keys = options.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next();
for (String key : options.keySet()) {
String value = options.get(key);
try {
if (key.equals("CFG")) {
Expand All @@ -412,12 +419,20 @@ public void readBondBlock(IAtomContainer readData) throws CDKException {
} else if (configuration == 3) {
bond.setStereo(IBond.Stereo.DOWN);
}
} else if (key.equals("ENDPTS")) {
String[] endptStr = value.split(" ");
// skip first value that is count
for (int i = 1; i < endptStr.length; i++) {
endpts.add(readData.getAtom(Integer.parseInt(endptStr[i]) - 1));
}
} else if (key.equals("ATTACH")) {
attach = value;
} else {
logger.warn("Not parsing key: " + key);
}
} catch (Exception exception) {
String error = "Error while parsing key/value " + key + "=" + value + ": "
+ exception.getMessage();
+ exception.getMessage();
logger.error(error);
logger.debug(exception);
throw new CDKException(error, exception);
Expand All @@ -427,6 +442,22 @@ public void readBondBlock(IAtomContainer readData) throws CDKException {

// storing bond
readData.addBond(bond);

// storing positional variation
if ("ANY".equals(attach)) {
Sgroup sgroup = new Sgroup();
sgroup.setType(SgroupType.ExtMulticenter);
sgroup.addAtom(bond.getAtom(0)); // could be other end?
sgroup.addBond(bond);
for (IAtom endpt : endpts)
sgroup.addAtom(endpt);

List<Sgroup> sgroups = readData.getProperty(CDKConstants.CTAB_SGROUPS);
if (sgroups == null)
readData.setProperty(CDKConstants.CTAB_SGROUPS, sgroups = new ArrayList<>(4));
sgroups.add(sgroup);
}

logger.debug("Added bond: " + bond);
}
}
Expand Down
Expand Up @@ -316,6 +316,19 @@ private void writeBondBlock(IAtomContainer mol,
Map<IChemObject, Integer> idxs) throws IOException, CDKException {
if (mol.getBondCount() == 0)
return;

// collect multicenter Sgroups before output
List<Sgroup> sgroups = mol.getProperty(CDKConstants.CTAB_SGROUPS);
Map<IBond,Sgroup> multicenterSgroups = new HashMap<>();
if (sgroups != null) {
for (Sgroup sgroup : sgroups) {
if (sgroup.getType() != SgroupType.ExtMulticenter)
continue;
for (IBond bond : sgroup.getBonds())
multicenterSgroups.put(bond, sgroup);
}
}

writer.write("BEGIN BOND\n");
int bondIdx = 0;
for (IBond bond : mol.bonds()) {
Expand Down Expand Up @@ -373,6 +386,14 @@ private void writeBondBlock(IAtomContainer mol,
break;
}

Sgroup sgroup = multicenterSgroups.get(bond);
if (sgroup != null) {
List<IAtom> atoms = new ArrayList<>(sgroup.getAtoms());
atoms.remove(bond.getAtom(0));
atoms.remove(bond.getAtom(1));
writer.write(" ATTACH=ANY ENDPTS=(").write(atoms, idxs).write(')');
}

writer.write('\n');
}
writer.write("END BOND\n");
Expand Down Expand Up @@ -435,11 +456,6 @@ private List<Sgroup> getSgroups(IAtomContainer mol) {
*/
private void writeSgroupBlock(List<Sgroup> sgroups, Map<IChemObject, Integer> idxs) throws IOException, CDKException {

if (sgroups.isEmpty())
return;

writer.write("BEGIN SGROUP\n");

// going to reorder but keep the originals untouched
sgroups = new ArrayList<>(sgroups);

Expand All @@ -450,6 +466,11 @@ private void writeSgroupBlock(List<Sgroup> sgroups, Map<IChemObject, Integer> id
iter.remove();
}

if (sgroups.isEmpty())
return;

writer.write("BEGIN SGROUP\n");

// Short of building a full dependency graph we write the parents
// first, this sort is good for three levels of nesting. Not perfect
// but really tools should be able to handle output of order parents
Expand Down
Expand Up @@ -25,18 +25,26 @@
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.DefaultChemObjectBuilder;
import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.graph.GraphUtil;
import org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.cdk.sgroup.Sgroup;
import org.openscience.cdk.sgroup.SgroupType;
import org.openscience.cdk.silent.AtomContainer;
import org.openscience.cdk.interfaces.IPseudoAtom;
import org.openscience.cdk.tools.ILoggingTool;
import org.openscience.cdk.tools.LoggingToolFactory;

import java.io.InputStream;
import java.io.StringReader;
import java.util.List;

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

/**
* TestCase for the reading MDL V3000 mol files using one test file.
Expand Down Expand Up @@ -117,4 +125,13 @@ public void testPseudoAtomLabels() throws Exception {
}
}

@Test public void positionalVariation() throws Exception {
MDLV3000Reader reader = new MDLV3000Reader(getClass().getResourceAsStream("multicenterBond.mol"));
IAtomContainer container = reader.read(new org.openscience.cdk.AtomContainer(0,0,0,0));
assertThat(container.getBondCount(), is(8));
List<Sgroup> sgroups = container.getProperty(CDKConstants.CTAB_SGROUPS);
assertNotNull(sgroups);
assertThat(sgroups.size(), is(1));
assertThat(sgroups.get(0).getType(), is(SgroupType.ExtMulticenter));
}
}
Expand Up @@ -392,6 +392,14 @@ public void roundTripOrderMixtures() throws IOException, CDKException {
}
}

@Test public void positionalVariationRoundTrip() throws Exception {
try (MDLV3000Reader mdlr = new MDLV3000Reader(getClass().getResourceAsStream("multicenterBond.mol"))) {
IAtomContainer mol = mdlr.read(new AtomContainer(0, 0, 0, 0));
String res = writeToStr(mol);
assertThat(res, CoreMatchers.containsString("M V30 8 1 8 9 ATTACH=ANY ENDPTS=(5 2 3 4 5 6)\n"));
}
}

private String writeToStr(IAtomContainer mol) throws IOException, CDKException {
StringWriter sw = new StringWriter();
try (MDLV3000Writer mdlw = new MDLV3000Writer(sw)) {
Expand Down
@@ -0,0 +1,29 @@



0 0 0 0 0 999 V3000
M V30 BEGIN CTAB
M V30 COUNTS 9 8 0 0 0
M V30 BEGIN ATOM
M V30 1 C -2.7222 1.4289 0 0
M V30 2 C -4.0559 0.6589 0 0
M V30 3 C -4.0559 -0.8811 0 0
M V30 4 C -2.7222 -1.6511 0 0
M V30 5 C -1.3886 -0.8811 0 0
M V30 6 C -1.3886 0.6589 0 0
M V30 7 C -2.7222 2.9689 0 0
M V30 8 * -2.7222 -0.4191 0 0
M V30 9 O -2.7222 -2.7291 0 0
M V30 END ATOM
M V30 BEGIN BOND
M V30 1 1 1 2
M V30 2 2 2 3
M V30 3 1 3 4
M V30 4 2 4 5
M V30 5 1 5 6
M V30 6 2 1 6
M V30 7 1 1 7
M V30 8 1 8 9 ENDPTS=(5 2 3 4 5 6) ATTACH=ANY
M V30 END BOND
M V30 END CTAB
M END

0 comments on commit e336783

Please sign in to comment.