Skip to content

Commit 9dedf32

Browse files
committed
task 'add atoms' implemented and tested
1 parent 3833066 commit 9dedf32

7 files changed

Lines changed: 150 additions & 17 deletions

File tree

src/main/java/autocompchem/molecule/AtomEditor.java

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -342,46 +342,71 @@ public static void addAtom(IAtomContainer iac, AnnotatedAtomTuple tuple)
342342
String newSymbol = words[0];
343343

344344
Map<IAtom,Object[]> bondedAtoms = new HashMap<IAtom,Object[]>();
345-
switch (words[1]) {
345+
switch (words[1].toUpperCase()) {
346346
case KEYATOMATIC:
347347
InternalCoord[] internalCoords = new InternalCoord[referenceAtoms.length];
348-
if (words.length < 8)
348+
if (words.length != 4 && words.length != 6 && words.length != 8)
349349
{
350350
throw new IllegalArgumentException("Value of '" + KEYATOMATIC
351-
+ "' keyword must be followed by 3 space-separated pairs 'index value'."
351+
+ "' keyword must be followed by up to 3 pairs of index and value'."
352352
+ ". Found only " + (words.length - 2) + " words instead of 6.");
353353
}
354-
int[] indexes = new int[3];
355-
double[] values = new double[3];
356-
for (int i=2; i<words.length; i+=2)
354+
int numPairs = (words.length - 2) / 2;
355+
int[] indexes = new int[numPairs];
356+
double[] values = new double[numPairs];
357+
for (int iPair=0; iPair<numPairs; iPair++)
357358
{
359+
int i = 2 + iPair * 2;
358360
if (!NumberUtils.isParsableToInt(words[i]))
359361
{
360362
throw new IllegalArgumentException("Value of '" + KEYATOMATIC
361-
+ "' keyword must be followed by 3 space-separated pairs 'index value'."
363+
+ "' keyword must be followed by space-separated pairs 'index value'."
362364
+ ". Could not parse '" + words[i] + "' as index.");
363365
}
364-
indexes[i/2] = Integer.parseInt(words[i]);
366+
indexes[iPair] = Integer.parseInt(words[i]);
365367
if (!NumberUtils.isParsableToDouble(words[i+1]))
366368
{
367369
throw new IllegalArgumentException("Value of '" + KEYATOMATIC
368-
+ "' keyword must be followed by 3 space-separated pairs 'index value'."
370+
+ "' keyword must be followed by space-separated pairs 'index value'."
369371
+ ". Could not parse '" + words[i+1] + "' as value.");
370372
}
371-
values[i/2] = Double.parseDouble(words[i+1]);
373+
values[iPair] = Double.parseDouble(words[i+1]);
372374
}
373375
internalCoords[0] = new InternalCoord("distance", values[0],
374376
new ArrayList<Integer>(Arrays.asList(-1, indexes[0])));
375-
internalCoords[1] = new InternalCoord("angle", values[1],
376-
new ArrayList<Integer>(Arrays.asList(-1, indexes[0], indexes[1])));
377-
internalCoords[2] = new InternalCoord("torsion", values[2],
378-
new ArrayList<Integer>(Arrays.asList(-1, indexes[0], indexes[1], indexes[2])));
377+
if (numPairs > 1)
378+
{
379+
internalCoords[1] = new InternalCoord("angle", values[1],
380+
new ArrayList<Integer>(Arrays.asList(-1, indexes[0], indexes[1])));
381+
if (numPairs > 2)
382+
{
383+
internalCoords[2] = new InternalCoord("torsion", values[2],
384+
new ArrayList<Integer>(Arrays.asList(-1, indexes[0], indexes[1], indexes[2])));
385+
}
386+
}
379387

380388
addAtom(iac, referenceAtoms, internalCoords, newSymbol, bondedAtoms);
381389
break;
382390

383391
case KEYATOMATCENTROID:
384-
Point3d centroid = MolecularUtils.calculateCentroid(referenceAtoms);
392+
IAtom[] centroidAtoms = new IAtom[4];
393+
if (words.length > 2)
394+
{
395+
centroidAtoms = new IAtom[words.length - 2];
396+
for (int i=2; i<words.length; i++)
397+
{
398+
if (!NumberUtils.isParsableToInt(words[i]))
399+
{
400+
throw new IllegalArgumentException("Value of '" + KEYATOMATCENTROID
401+
+ "' keyword must be followed by a space-separated list of atom indexes."
402+
+ ". Could not parse '" + words[i] + "' as index.");
403+
}
404+
centroidAtoms[i-2] = referenceAtoms[Integer.parseInt(words[i])];
405+
}
406+
} else {
407+
centroidAtoms = referenceAtoms;
408+
}
409+
Point3d centroid = MolecularUtils.calculateCentroid(centroidAtoms);
385410
addAtom(iac, newSymbol, centroid, bondedAtoms);
386411
break;
387412
default:

src/main/java/autocompchem/molecule/MolecularUtils.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1010,7 +1010,8 @@ public static Point3d calculateAtomPosition(IAtom[] referenceAtoms,
10101010
double phiRad = 0.0;
10111011
if (internalCoords.length >= 3)
10121012
{
1013-
phiRad = internalCoords[2].getValue() * Math.PI / 180.0;
1013+
// NB: the -1 reflect the levo/destrogiro convention in chemistry.
1014+
phiRad = internalCoords[2].getValue() * Math.PI / 180.0 * -1.0;
10141015
ArrayList<Integer> idsDih = internalCoords[2].getIDs();
10151016
if (idsDih.size() < 4)
10161017
{

src/main/resources/inputdefinition/AtomEditor.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"key": "ATOMIDS",
1010
"casedKey": "atomIDs",
1111
"type": "<String>",
12-
"doc": "Defines one or more (0-based) index-based rules for identifying atoms to edit or use to add new atoms. Rules are separated by newline characters (i.e., each rule is defined in a single line). In each line, each space-separated string (i.e., a word) is interpreted as following: each word is considered a single index until a word is found that corresponds to any keyword (see below). Once the first keyword is found no more words will be interpreted as an index. The order of the indexes is retained. Recognized keywords are (case-insensitive):\n--> 'remove': to request the removal of the matched atoms (mutually exclusive with other behavior-controlling keywords, like 'addAtom' and 'element').\n 'element' <newElementalSymbol>: to request the mutation of the elemental symbol of the matched atoms (mutually exclusive with other behavior-controlling keywords, like 'addAtom' and 'remove').\n --> 'addAtom': to request the addition od a single atom (mutually exclusive with other behavior-controlling keywords, like 'element' and 'remove'). The value of this keyword must start with the elemental symbol of the new atom following by details about the relative position and bonding of such atom: '<el> <string_details>'. Where <string_details> may have the following syntax:\n -----> 'CENTROID' to add an atom in the position defined by the centroid of the matched atoms.\n -----> 'INTERNALCOORDS' to specify the position of the new atom relative to the atoms in the matched atom tuple. To this end this keyword should be followed by a string adhering to this syntax: '<idA> <valueA> [<idB> <valueB> [<idC> <valueC>]]' where <id...> are indexes in the matched tuple of atoms and <value...> are numerical values used together with the indexes to define internal coordinates in the order 'distance', 'angle', 'dihedral' (No second angle!).\n --> 'OnlyBonded': limits to tuples of atoms to those that are connected to form a path visiting the tuple in the order given by the order of the SMARTS. For example, a SMARTS-based rule for tuple '[#1] [#6] [#7]' may be limited to match only substructures where the H atom is connected with any bond to the C atom, which is connected with any bond to the N atom."
12+
"doc": "Defines one or more (0-based) index-based rules for identifying atoms to edit or use to add new atoms. Rules are separated by newline characters (i.e., each rule is defined in a single line). In each line, each space-separated string (i.e., a word) is interpreted as following: each word is considered a single index until a word is found that corresponds to any keyword (see below). Once the first keyword is found no more words will be interpreted as an index. The order of the indexes is retained. Recognized keywords are (case-insensitive):\n--> 'remove': to request the removal of the matched atoms (mutually exclusive with other behavior-controlling keywords, like 'addAtom' and 'element').\n 'element' <newElementalSymbol>: to request the mutation of the elemental symbol of the matched atoms (mutually exclusive with other behavior-controlling keywords, like 'addAtom' and 'remove').\n --> 'addAtom': to request the addition od a single atom (mutually exclusive with other behavior-controlling keywords, like 'element' and 'remove'). The value of this keyword must start with the elemental symbol of the new atom following by details about the relative position and bonding of such atom: '<el> <string_details>'. Where <string_details> may have the following syntax:\n -----> 'CENTROID' to add an atom in the position defined by the centroid of the matched atoms. Without value, this keyword makes the centroid be computed on all the atom tuple, but is a space-separated list of atoms is given, then only the atoms of the tuple which correspond to the given indexes will be used to compute the centroid (e.g., 'centroid 0 3' places the new atom in between the first and fourth atom of the matched tuple)\n -----> 'INTERNALCOORDS' to specify the position of the new atom relative to the atoms in the matched atom tuple. To this end this keyword should be followed by a string adhering to this syntax: '<idA> <valueA> [<idB> <valueB> [<idC> <valueC>]]' where <id...> are indexes in the matched tuple of atoms and <value...> are numerical values used together with the indexes to define internal coordinates in the order 'distance', 'angle', 'dihedral' (No second angle!).\n --> 'OnlyBonded': limits to tuples of atoms to those that are connected to form a path visiting the tuple in the order given by the order of the SMARTS. For example, a SMARTS-based rule for tuple '[#1] [#6] [#7]' may be limited to match only substructures where the H atom is connected with any bond to the C atom, which is connected with any bond to the N atom."
1313
},
1414
{
1515
"embeddedWorker": "autocompchem.molecule.AtomContainerInputProcessor",

test/cli39-mol.sdf

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
sq
2+
OpenBabel03172615283D
3+
4+
4 4 0 0 0 0 0 0 0 0999 V2000
5+
0.0000 0.0000 0.0000 C 0 0 0 0 0 15 0 0 0 0 0 0
6+
2.0000 0.0000 0.0000 N 0 0 0 0 0 15 0 0 0 0 0 0
7+
2.0000 2.0000 0.0000 C 0 0 0 0 0 15 0 0 0 0 0 0
8+
0.0000 2.0000 0.0000 O 0 0 0 0 0 15 0 0 0 0 0 0
9+
1 2 1 0 0 0 0
10+
2 3 1 0 0 0 0
11+
3 4 1 0 0 0 0
12+
4 1 1 0 0 0 0
13+
M END
14+
$$$$

test/cli39.check

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/bin/bash
2+
3+
function not_passed() {
4+
echo "NOT Passed: check condition leading to line '$1' of '$0'"
5+
exit -1
6+
}
7+
8+
if [ ! -f cli39.log ] ; then not_passed $LINENO ; fi
9+
if [ ! -f cli39-edited.sdf ] ; then not_passed $LINENO ; fi
10+
11+
# Symmetry makes two distinct tuples match the query, so all atoms are added twice
12+
# but in some cases in two different positions, due to the centroid being
13+
# computes over a/symmetric centers
14+
n=0; n=$(grep -c -i " H " cli39-edited.sdf)
15+
if [ 2 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
16+
17+
n=0; n=$(grep -c -i " 1\.00.* 1\.00.* .0\.0.* H " cli39-edited.sdf)
18+
if [ 2 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
19+
20+
n=0; n=$(grep -c -i " He " cli39-edited.sdf)
21+
if [ 2 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
22+
23+
n=0; n=$(grep -c -i " 2\.00.* 1\.00.* .0\.0.* He " cli39-edited.sdf)
24+
if [ 1 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
25+
26+
n=0; n=$(grep -c -i " 1\.00.* .0\.00.* .0\.0.* He " cli39-edited.sdf)
27+
if [ 1 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
28+
29+
n=0; n=$(grep -c -i " Cl " cli39-edited.sdf)
30+
if [ 2 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
31+
32+
n=0; n=$(grep -c -i " 1\.00.* .0\.00.* .0\.0.* Cl " cli39-edited.sdf)
33+
if [ 1 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
34+
35+
n=0; n=$(grep -c -i " 2\.00.* 1\.00.* .0\.0.* Cl " cli39-edited.sdf)
36+
if [ 1 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
37+
38+
n=0; n=$(grep -c -i " B " cli39-edited.sdf)
39+
if [ 2 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
40+
41+
n=0; n=$(grep -c -i " 1\.33.* 1\.33.* .0\.0.* B " cli39-edited.sdf)
42+
if [ 1 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
43+
44+
n=0; n=$(grep -c -i " 0\.66.* 0\.66.* .0\.0.* B " cli39-edited.sdf)
45+
if [ 1 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
46+
47+
n=0; n=$(grep -c -i " W " cli39-edited.sdf)
48+
if [ 1 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
49+
50+
n=0; n=$(grep -c -i " 3\.414.* \-1\.414.* .0\.0.* W " cli39-edited.sdf)
51+
if [ 1 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
52+
53+
n=0; n=$(grep -c -i " Rh " cli39-edited.sdf)
54+
if [ 2 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
55+
56+
n=0; n=$(grep -c -i " 1\.25.* .0\.00.* \-2\.165.* Rh " cli39-edited.sdf)
57+
if [ 1 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
58+
59+
n=0; n=$(grep -c -i " 2\.00.* 0\.75.* 2\.165.* Rh " cli39-edited.sdf)
60+
if [ 1 != "$n" ] ; then not_passed $LINENO ; exit 0 ; fi
61+
62+
n=0; n=$(grep -c -i "Termination status: 0" cli39.log)
63+
if [ 1 == "$n" ] ; then echo Passed ; exit 0 ; fi
64+
not_passed $LINENO
65+
exit -1

test/cli39.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"jobType": "ACCJob",
3+
"params": [
4+
{
5+
"reference": "TASK",
6+
"value": "editAtoms"
7+
},
8+
{
9+
"reference": "SMARTS",
10+
"value": "[#6][#7][#6][#8] addAtom H centroid\n[#6][#7][#6][#8] addAtom Cl centroid 0 1\n[#6][#7][#6][#8] addAtom He centroid 1 2\n[#6][#7][#6][#8] addAtom B centroid 3 2 1\n[#7] [#8] addAtom W internalCoords 0 2.0 1 180.0\n[#7][#6][#8] addAtom Rh internalCoords 1 2.5 0 60.0 2 90"
11+
},
12+
{
13+
"reference": "VERBOSITY",
14+
"value": "5"
15+
},
16+
{
17+
"reference": "INFILE",
18+
"value": "../cli39-mol.sdf"
19+
},
20+
{
21+
"reference": "outFile",
22+
"value": "cli39-edited.sdf"
23+
}
24+
]
25+
}

test/cli39.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"$javaDir/java" -jar "$ACCHome/target/autocompchem-$accVersion-jar-with-dependencies.jar" -j ../cli39.json > cli39.log
2+
3+

0 commit comments

Comments
 (0)