Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions core/indigo-core/molecule/src/molfile_saver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <ctime>
#include <map>
#include <sstream>
#include <string>

#include "base_cpp/locale_guard.h"
#include "base_cpp/output.h"
Expand Down Expand Up @@ -787,11 +788,29 @@ void MolfileSaver::_writeCtab(Output& output, BaseMolecule& mol, bool query)

output.writeStringCR("M V30 END BOND");

QS_DEF(Array<int>, sgs_sorted);
_checkSGroupIndices(mol, sgs_sorted);

//[Sapio] [CHEMBUGS-184] S-GROUP needs to be before COLLECTION when S GROUP is used.
const bool has_sgroups = (mol.countSGroups() > 0);
MoleculeStereocenters& stereocenters = mol.stereocenters;
const bool has_collection = (stereocenters.begin() != stereocenters.end() || mol.hasHighlighting() || mol.custom_collections.size() > 0);
const bool sgroup_collection_order_reversed = (has_sgroups && has_collection);

// Default order: COLLECTION before SGROUP.
// When sgroup_collection_order_reversed, write SGROUP then COLLECTION (CT spec) page 22.
std::string collection_str;
StringOutput collection_buf(collection_str);
Output* collection_dest_ptr;
if (sgroup_collection_order_reversed)
collection_dest_ptr = static_cast<Output*>(&collection_buf);
else
collection_dest_ptr = &output;
Output& collection_dest = *collection_dest_ptr;

if (stereocenters.begin() != stereocenters.end() || mol.hasHighlighting())
if (has_collection)
{
output.writeStringCR("M V30 BEGIN COLLECTION");
collection_dest.writeStringCR("M V30 BEGIN COLLECTION");

QS_DEF(Array<int>, processed);

Expand Down Expand Up @@ -832,7 +851,7 @@ void MolfileSaver::_writeCtab(Output& output, BaseMolecule& mol, bool query)
out.printf(" %d", _atom_mapping[list[j]]);
out.writeChar(')');

_writeMultiString(output, buf.ptr(), buf.size());
_writeMultiString(collection_dest, buf.ptr(), buf.size());
}

if (mol.hasHighlighting())
Expand All @@ -848,7 +867,7 @@ void MolfileSaver::_writeCtab(Output& output, BaseMolecule& mol, bool query)
out.printf(" %d", _bond_mapping[i]);
out.writeChar(')');

_writeMultiString(output, buf.ptr(), buf.size());
_writeMultiString(collection_dest, buf.ptr(), buf.size());
}
if (mol.countHighlightedAtoms() > 0)
{
Expand All @@ -859,7 +878,7 @@ void MolfileSaver::_writeCtab(Output& output, BaseMolecule& mol, bool query)
out.printf(" %d", _atom_mapping[i]);
out.writeChar(')');

_writeMultiString(output, buf.ptr(), buf.size());
_writeMultiString(collection_dest, buf.ptr(), buf.size());
}
}
if (mol.custom_collections.size() > 0)
Expand All @@ -868,17 +887,14 @@ void MolfileSaver::_writeCtab(Output& output, BaseMolecule& mol, bool query)
{
ArrayOutput out(buf);
out.printf("%s", mol.custom_collections.at(i));
_writeMultiString(output, buf.ptr(), buf.size());
_writeMultiString(collection_dest, buf.ptr(), buf.size());
}
}

output.writeStringCR("M V30 END COLLECTION");
collection_dest.writeStringCR("M V30 END COLLECTION");
}

QS_DEF(Array<int>, sgs_sorted);
_checkSGroupIndices(mol, sgs_sorted);

if (mol.countSGroups() > 0)
if (has_sgroups)
{
MoleculeSGroups* sgroups = &mol.sgroups;
int idx = 1;
Expand Down Expand Up @@ -1074,6 +1090,9 @@ void MolfileSaver::_writeCtab(Output& output, BaseMolecule& mol, bool query)
_removeImplicitSGroups(mol, implicit_sgroups_indexes);
}

if (sgroup_collection_order_reversed)
output.write(collection_str.data(), static_cast<int>(collection_str.size()));

output.writeStringCR("M V30 END CTAB");

int n_rgroups = mol.rgroups.getRGroupCount();
Expand Down
2 changes: 2 additions & 0 deletions core/indigo-core/tests/common.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include "common.h"
#include "reaction/reaction.h"
#include "reaction/reaction_auto_loader.h"
#include "reaction/reaction_cml_saver.h"
#include "reaction/reaction_json_saver.h"
#include "reaction/rxnfile_saver.h"
Expand Down
249 changes: 249 additions & 0 deletions core/indigo-core/tests/tests/molecule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@
#include <molecule/hybridization.h>
#include <molecule/lipinski.h>
#include <molecule/molecule_mass.h>
#include <molecule/molfile_loader.h>
#include <molecule/smiles_loader.h>
#include <molecule/smiles_saver.h>
#include <molecule/tpsa.h>

#include "common.h"
#include "molecule/elements.h"

using namespace std;
using namespace indigo;
Expand Down Expand Up @@ -352,3 +354,250 @@ TEST_F(IndigoCoreMoleculeTest, dearomatize_smarts)
// printf("%s", sm.c_str());
EXPECT_STREQ("c1-c=c-c=c-c=1", sm.c_str());
}

// [Sapio] [CHEMBUGS-184] Stereo reaction molecules (from former stereo_reaction.rxn): load as mol blocks to verify parsing.
namespace
{
const char* const stereoReactionReactant1 = R"(
Mrv2305 05232323372D

18 19 0 0 0 0 999 V2000
-6.9420 -0.0902 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-7.6565 -0.5027 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-7.6565 -1.3278 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-6.9420 -1.7402 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0
-6.2275 -1.3278 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-6.2275 -0.5027 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0
-5.5357 0.0000 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0
-4.7511 -0.2549 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
-4.2661 0.4125 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0
-4.7510 1.0800 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0
-5.5356 0.8250 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0
-3.4411 0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-6.2031 1.3100 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0
-4.4961 1.8646 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
-5.4066 1.6399 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-8.3709 -1.7403 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
-5.5130 -1.7403 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
-2.8891 1.0256 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
1 2 2 0 0 0 0
2 3 1 0 0 0 0
9 10 1 0 0 0 0
10 11 1 0 0 0 0
7 11 1 0 0 0 0
9 12 1 1 0 0 0
3 16 2 0 0 0 0
5 17 2 0 0 0 0
8 9 1 0 0 0 0
7 8 1 0 0 0 0
10 14 1 6 0 0 0
12 18 1 0 0 0 0
11 15 1 1 0 0 0
11 13 1 6 0 0 0
1 6 1 0 0 0 0
5 6 1 0 0 0 0
7 6 1 1 0 0 0
3 4 1 0 0 0 0
4 5 1 0 0 0 0
M STY 1 1 SUP
M SAL 1 1 15
M SBL 1 1 13
M SMT 1 Me
M SAP 1 1 15 11 1
M END
)";
const char* const stereoReactionReactant2 = R"(
Mrv2305 05232323372D

24 26 0 0 0 0 999 V2000
-4.7105 -3.1000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-1.1382 -0.2125 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0
-4.7106 -2.2750 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
-3.9961 -1.8625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-3.9961 -1.0375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-3.2816 -0.6250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-2.5671 -1.0375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-2.5671 -1.8625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-3.2816 -2.2750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-1.8527 -0.6250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-1.8527 0.2000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-2.5672 0.6126 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-2.5671 1.4375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-1.8527 1.8500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-1.1382 1.4376 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-1.1382 0.6125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-1.1382 -1.0375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-0.4237 -0.6250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
0.2907 -1.0375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
0.2907 -1.8625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1.0052 -2.2750 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
1.7197 -1.8625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-0.4237 -2.2750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-1.1382 -1.8625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
10 2 1 0 0 0 0
1 3 1 0 0 0 0
3 4 1 0 0 0 0
4 5 2 0 0 0 0
4 9 1 0 0 0 0
5 6 1 0 0 0 0
6 7 2 0 0 0 0
7 8 1 0 0 0 0
7 10 1 0 0 0 0
8 9 2 0 0 0 0
10 11 1 0 0 0 0
10 17 1 0 0 0 0
11 12 2 0 0 0 0
11 16 1 0 0 0 0
12 13 1 0 0 0 0
13 14 2 0 0 0 0
14 15 1 0 0 0 0
15 16 2 0 0 0 0
17 18 2 0 0 0 0
17 24 1 0 0 0 0
18 19 1 0 0 0 0
19 20 2 0 0 0 0
20 21 1 0 0 0 0
20 23 1 0 0 0 0
21 22 1 0 0 0 0
23 24 2 0 0 0 0
M STY 1 1 SUP
M SAL 1 15 1 3 4 5 6 7 8 9 10 11 12 13 14 15 16
M SAL 1 8 17 18 19 20 21 22 23 24
M SBL 1 1 1
M SMT 1 DMTr
M SAP 1 1 10 2 1
M END
)";
const char* const stereoReactionProduct = R"(
Mrv2305 05232323372D

41 45 0 0 0 0 999 V2000
2.9291 -0.1300 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.2146 -0.5425 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.2146 -1.3676 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.9291 -1.7800 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0
3.6436 -1.3676 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
3.6436 -0.5425 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0
4.3354 -0.0398 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0
5.1200 -0.2947 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
5.6049 0.3727 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0
5.1200 1.0402 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0
4.3354 0.7852 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0
6.4299 0.3727 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
3.6680 1.2701 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0
5.3749 1.8248 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
4.4645 1.6000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1.5002 -1.7801 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
4.3581 -1.7801 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
6.9820 0.9858 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
11.5195 1.7003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
11.1070 0.9858 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
10.2820 0.9858 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
9.8695 0.2713 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
9.0445 0.2713 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
8.6320 0.9858 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
9.0445 1.7003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
9.8695 1.7003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
7.8070 0.9858 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
7.3945 0.2713 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
7.8070 -0.4432 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
7.3945 -1.1576 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
6.5695 -1.1576 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
6.1570 -0.4432 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
6.5695 0.2713 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
7.3945 1.7003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
6.5695 1.7003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
6.1570 2.4147 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
6.5695 3.1292 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
6.1570 3.8437 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
5.3320 3.8437 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
7.3945 3.1292 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
7.8070 2.4147 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1 2 2 0 0 0 0
2 3 1 0 0 0 0
9 10 1 0 0 0 0
10 11 1 0 0 0 0
7 11 1 0 0 0 0
9 12 1 1 0 0 0
3 16 2 0 0 0 0
5 17 2 0 0 0 0
8 9 1 0 0 0 0
7 8 1 0 0 0 0
10 14 1 6 0 0 0
12 18 1 0 0 0 0
11 15 1 1 0 0 0
11 13 1 6 0 0 0
1 6 1 0 0 0 0
5 6 1 0 0 0 0
7 6 1 1 0 0 0
3 4 1 0 0 0 0
4 5 1 0 0 0 0
18 27 1 0 0 0 0
19 20 1 0 0 0 0
20 21 1 0 0 0 0
21 22 2 0 0 0 0
21 26 1 0 0 0 0
22 23 1 0 0 0 0
23 24 2 0 0 0 0
24 25 1 0 0 0 0
24 27 1 0 0 0 0
25 26 2 0 0 0 0
27 28 1 0 0 0 0
27 34 1 0 0 0 0
28 29 2 0 0 0 0
28 33 1 0 0 0 0
29 30 1 0 0 0 0
30 31 2 0 0 0 0
31 32 1 0 0 0 0
32 33 2 0 0 0 0
34 35 2 0 0 0 0
34 41 1 0 0 0 0
35 36 1 0 0 0 0
36 37 2 0 0 0 0
37 38 1 0 0 0 0
37 40 1 0 0 0 0
38 39 1 0 0 0 0
40 41 2 0 0 0 0
M STY 2 1 SUP 2 SUP
M SAL 1 1 15
M SBL 1 1 13
M SMT 1 Me
M SAP 1 1 15 11 1
M SAL 2 15 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
M SAL 2 8 34 35 36 37 38 39 40 41
M SBL 2 1 20
M SMT 2 DMTr
M SAP 2 1 27 18 1
M END
)";
}

TEST_F(IndigoCoreMoleculeTest, stereoReactionReactant1)
{
Molecule mol;
BufferScanner scanner(stereoReactionReactant1);
MolfileLoader loader(scanner);
loader.loadMolecule(mol);
EXPECT_EQ(18, mol.vertexCount());
EXPECT_EQ(19, mol.edgeCount());
}

TEST_F(IndigoCoreMoleculeTest, stereoReactionReactant2)
{
Molecule mol;
BufferScanner scanner(stereoReactionReactant2);
MolfileLoader loader(scanner);
loader.loadMolecule(mol);
EXPECT_EQ(24, mol.vertexCount());
EXPECT_EQ(26, mol.edgeCount());
}

TEST_F(IndigoCoreMoleculeTest, stereoReactionProduct)
{
Molecule mol;
BufferScanner scanner(stereoReactionProduct);
MolfileLoader loader(scanner);
loader.loadMolecule(mol);
EXPECT_EQ(41, mol.vertexCount());
EXPECT_EQ(45, mol.edgeCount());
}
Loading