rdkit_chem 2025.09.3.2 → 2025.09.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Code/RubyWrappers/MolOps.i +9 -5
- data/Code/RubyWrappers/ROMol.i +16 -8
- data/Code/RubyWrappers/RWMol.i +21 -0
- data/ext/rdkit_chem/extconf.rb +8 -8
- data/lib/rdkit_chem/version.rb +4 -2
- data/test/test_rdkit_chem.rb +62 -10
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3ba77bf7020186b6859a9d6eff2eda271ca1853b5e022d09a97362b090db3685
|
|
4
|
+
data.tar.gz: fc8426cf986a9c7b38490bde57fd6db7080d8df22d6f7392242e0b263b7feae0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3f245fe05a0d011ee1e2a4e80bce87f3cb4b629016671b49db3c83b234b34366af3d04533f501336b4b84dcd92b108c571925d43d3b79023bbcda53d376b563c
|
|
7
|
+
data.tar.gz: 7242f4eb9d2bb13f127277612de7f3ccb0265e7c97a2e3cc383aaf35e0a05d4a49df86780003ffe159b8bb0f180f6453d91f96290ed48a840f4de0c117bdc37a
|
data/Code/RubyWrappers/MolOps.i
CHANGED
|
@@ -44,23 +44,27 @@
|
|
|
44
44
|
%newobject RDKit::MolOps::mergeQueryHs;
|
|
45
45
|
%newobject RDKit::MolOps::adjustQueryProperties;
|
|
46
46
|
|
|
47
|
-
%ignore
|
|
48
|
-
// Ignore all
|
|
47
|
+
// IMPORTANT: %ignore must come BEFORE %include
|
|
48
|
+
// Ignore all sanitizeMol overloads - we provide custom wrapper below
|
|
49
49
|
%ignore RDKit::MolOps::sanitizeMol;
|
|
50
|
+
%ignore RDKit::MolOps::detectChemistryProblems;
|
|
50
51
|
%include <GraphMol/MolOps.h>
|
|
51
52
|
%template(BoolPair) std::pair<bool, bool>;
|
|
52
53
|
|
|
53
54
|
%inline %{
|
|
54
|
-
|
|
55
|
+
// Custom sanitizeMol wrapper that returns the operation that failed
|
|
56
|
+
// Only ONE definition needed - default parameter handles both use cases
|
|
57
|
+
int sanitizeMol(RDKit::RWMol &mol, int sanitizeOps=RDKit::MolOps::SANITIZE_ALL){
|
|
55
58
|
unsigned int opThatFailed;
|
|
56
59
|
try{
|
|
57
|
-
RDKit::MolOps::sanitizeMol(mol,opThatFailed,
|
|
60
|
+
RDKit::MolOps::sanitizeMol(mol, opThatFailed,
|
|
58
61
|
static_cast<unsigned int>(sanitizeOps));
|
|
59
62
|
} catch(...) {
|
|
60
63
|
|
|
61
64
|
}
|
|
62
65
|
return static_cast<int>(opThatFailed);
|
|
63
|
-
}
|
|
66
|
+
}
|
|
67
|
+
|
|
64
68
|
std::vector<boost::shared_ptr<RDKit::MolSanitizeException>> detectChemistryProblems(RDKit::ROMol &mol,int sanitizeOps=RDKit::MolOps::SANITIZE_ALL){
|
|
65
69
|
std::vector<boost::shared_ptr<RDKit::MolSanitizeException>> res;
|
|
66
70
|
auto probs = RDKit::MolOps::detectChemistryProblems(mol,sanitizeOps);
|
data/Code/RubyWrappers/ROMol.i
CHANGED
|
@@ -101,12 +101,11 @@
|
|
|
101
101
|
|
|
102
102
|
/*
|
|
103
103
|
* Special handling for Conformer objects which should not be GCed until the molecule is destroyed
|
|
104
|
-
* We
|
|
105
|
-
*
|
|
106
|
-
*
|
|
104
|
+
* We ignore the original addConformer and provide our own in %extend that copies the conformer
|
|
105
|
+
* to avoid double-free issues with shared_ptr wrappers.
|
|
106
|
+
* Both addConf and addConformer are defined in %extend below (add_conf and add_conformer in Ruby).
|
|
107
107
|
*/
|
|
108
|
-
%ignore addConformer
|
|
109
|
-
%rename(addConformer) RDKit::ROMol::addConf;
|
|
108
|
+
%ignore RDKit::ROMol::addConformer;
|
|
110
109
|
%include <GraphMol/ROMol.h>
|
|
111
110
|
|
|
112
111
|
%ignore SubstructMatch;
|
|
@@ -189,9 +188,18 @@ unsigned int getDefaultPickleProperties();
|
|
|
189
188
|
return res;
|
|
190
189
|
}
|
|
191
190
|
|
|
192
|
-
/*
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
/* Create a copy of the conformer before adding it to the molecule.
|
|
192
|
+
This avoids the double-free issue since Ruby owns the original and C++ owns the copy.
|
|
193
|
+
The original DISOWN approach doesn't work with shared_ptr wrappers. */
|
|
194
|
+
unsigned int addConf(RDKit::Conformer *conf, bool assignId=false) {
|
|
195
|
+
RDKit::Conformer *confCopy = new RDKit::Conformer(*conf);
|
|
196
|
+
return self->addConformer(confCopy, assignId);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Alias for addConf - exposes as add_conformer in Ruby
|
|
200
|
+
unsigned int addConformer(RDKit::Conformer *conf, bool assignId=false) {
|
|
201
|
+
RDKit::Conformer *confCopy = new RDKit::Conformer(*conf);
|
|
202
|
+
return self->addConformer(confCopy, assignId);
|
|
195
203
|
}
|
|
196
204
|
|
|
197
205
|
std::string MolToSmiles(bool doIsomericSmiles=true, bool doKekule=false, int rootedAtAtom=-1, bool canonical=true,
|
data/Code/RubyWrappers/RWMol.i
CHANGED
|
@@ -44,6 +44,13 @@
|
|
|
44
44
|
|
|
45
45
|
%template(RWMol_Vect) std::vector< boost::shared_ptr<RDKit::RWMol> >;
|
|
46
46
|
|
|
47
|
+
/*
|
|
48
|
+
* Special handling for Conformer objects which should not be GCed until the molecule is destroyed
|
|
49
|
+
* We ignore the original addConformer and provide our own in %extend that copies the conformer
|
|
50
|
+
* to avoid double-free issues with shared_ptr wrappers.
|
|
51
|
+
*/
|
|
52
|
+
%ignore RDKit::RWMol::addConformer;
|
|
53
|
+
|
|
47
54
|
// ignore the methods that allow the molecule to take ownership of atoms/Bonds
|
|
48
55
|
// (instead of copying them). This just leads to memory problems with Ruby
|
|
49
56
|
%ignore RDKit::RWMol::addAtom(Atom *atom,bool updateLabel,bool takeOwnership);
|
|
@@ -78,6 +85,20 @@
|
|
|
78
85
|
%include <GraphMol/RWMol.h>
|
|
79
86
|
|
|
80
87
|
%extend RDKit::RWMol {
|
|
88
|
+
/* Create a copy of the conformer before adding it to the molecule.
|
|
89
|
+
This avoids the double-free issue since Ruby owns the original and C++ owns the copy.
|
|
90
|
+
The original DISOWN approach doesn't work with shared_ptr wrappers. */
|
|
91
|
+
unsigned int addConf(RDKit::Conformer *conf, bool assignId=false) {
|
|
92
|
+
RDKit::Conformer *confCopy = new RDKit::Conformer(*conf);
|
|
93
|
+
return self->addConformer(confCopy, assignId);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Alias for addConf - exposes as add_conformer in Ruby
|
|
97
|
+
unsigned int addConformer(RDKit::Conformer *conf, bool assignId=false) {
|
|
98
|
+
RDKit::Conformer *confCopy = new RDKit::Conformer(*conf);
|
|
99
|
+
return self->addConformer(confCopy, assignId);
|
|
100
|
+
}
|
|
101
|
+
|
|
81
102
|
static RDKit::RWMOL_SPTR MolFromSmiles(const std::string &smi,int debugParse=0,bool sanitize=1,
|
|
82
103
|
std::map<std::string,std::string> *replacements=0){
|
|
83
104
|
return RDKit::RWMOL_SPTR(RDKit::SmilesToMol(smi, debugParse, sanitize,replacements));
|
data/ext/rdkit_chem/extconf.rb
CHANGED
|
@@ -16,13 +16,13 @@ rescue StandardError
|
|
|
16
16
|
nr_processors = 1
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
FileUtils.mkdir_p rdkit_dir
|
|
20
|
-
Dir.chdir main_dir do
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
end
|
|
19
|
+
# FileUtils.mkdir_p rdkit_dir
|
|
20
|
+
# Dir.chdir main_dir do
|
|
21
|
+
# FileUtils.rm_rf src_dir
|
|
22
|
+
# puts 'Downloading RDKit sources'
|
|
23
|
+
# git = 'git clone https://github.com/rdkit/rdkit.git'
|
|
24
|
+
# system git
|
|
25
|
+
# end
|
|
26
26
|
|
|
27
27
|
Dir.chdir(src_dir) do
|
|
28
28
|
checkout = 'git checkout c2e48f41d88ddc15c6e1f818d1c4ced70b7f20d1'
|
|
@@ -74,7 +74,7 @@ Dir.chdir build_dir do
|
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
# Remove compiled file, free spaces
|
|
77
|
-
FileUtils.remove_dir(rdkit_dir)
|
|
77
|
+
# FileUtils.remove_dir(rdkit_dir)
|
|
78
78
|
|
|
79
79
|
# create a fake Makefile
|
|
80
80
|
File.open(File.join(File.dirname(__FILE__), 'Makefile'), 'w+') do |makefile|
|
data/lib/rdkit_chem/version.rb
CHANGED
data/test/test_rdkit_chem.rb
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "test/unit"
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "rdkit_chem"
|
|
4
4
|
|
|
5
5
|
class RDKitTest < Test::Unit::TestCase
|
|
6
6
|
def test_mol_from_smiles
|
|
7
|
-
smiles =
|
|
7
|
+
smiles = "CCO" # ethanol
|
|
8
8
|
rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
|
|
9
9
|
assert_not_nil(rw_mol)
|
|
10
10
|
assert_equal(3, rw_mol.get_num_atoms)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def test_mol_to_mol_block
|
|
14
|
-
smiles =
|
|
14
|
+
smiles = "CCO" # ethanol
|
|
15
15
|
rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
|
|
16
16
|
mdl = rw_mol.mol_to_mol_block(true, -1, false)
|
|
17
17
|
assert_not_nil(mdl)
|
|
18
|
-
assert(mdl.include?(
|
|
18
|
+
assert(mdl.include?("V2000") || mdl.include?("V3000"))
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def test_mol_from_mol_block
|
|
22
|
-
smiles =
|
|
22
|
+
smiles = "c1ccccc1" # benzene
|
|
23
23
|
rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
|
|
24
24
|
mdl = rw_mol.mol_to_mol_block(true, -1, false)
|
|
25
25
|
|
|
@@ -30,18 +30,70 @@ class RDKitTest < Test::Unit::TestCase
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def test_mol_to_smiles
|
|
33
|
-
smiles =
|
|
33
|
+
smiles = "CCO"
|
|
34
34
|
rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
|
|
35
35
|
output_smiles = RDKitChem.mol_to_smiles(rw_mol)
|
|
36
36
|
assert_not_nil(output_smiles)
|
|
37
|
-
assert_equal(
|
|
37
|
+
assert_equal("CCO", output_smiles)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_sanitize_mol
|
|
41
|
+
smiles = "c1ccccc1" # benzene
|
|
42
|
+
rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
|
|
43
|
+
|
|
44
|
+
# sanitize_mol returns an integer (0 for success usually, or bitmask of operations failed)
|
|
45
|
+
# It might return different things based on SWIG wrapping details observed in MolOps.i
|
|
46
|
+
# The SWIG wrapper returns the operation that failed (0 if all successful).
|
|
47
|
+
result = RDKitChem.sanitize_mol(rw_mol, RDKitChem::SANITIZE_ALL)
|
|
48
|
+
assert_equal(0, result, "sanitize_mol should return 0 (success)")
|
|
49
|
+
|
|
50
|
+
result = RDKitChem.sanitize_mol(rw_mol)
|
|
51
|
+
assert_equal(0, result, "sanitize_mol should return 0 (success)")
|
|
38
52
|
end
|
|
39
53
|
|
|
40
54
|
def test_cholesterol
|
|
41
55
|
# Cholesterol without chirality
|
|
42
|
-
smiles =
|
|
56
|
+
smiles = "CC(C)CCCC(C)C1CCC2C1(CCC3C2CC=C4C3(CCC(C4)O)C)C"
|
|
43
57
|
rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
|
|
44
58
|
assert_not_nil(rw_mol)
|
|
45
|
-
assert_equal(28, rw_mol.get_num_atoms)
|
|
59
|
+
assert_equal(28, rw_mol.get_num_atoms) # C27H46O has 28 heavy atoms (27 C + 1 O)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_conformer_ownership
|
|
63
|
+
# FIXME: This test causes a segfault (exit code 139) likely due to GC/double-free issues.
|
|
64
|
+
# Test that conformer ownership is properly transferred to the molecule.
|
|
65
|
+
# This verifies the DISOWN directive works - without it, Ruby GC and C++
|
|
66
|
+
# destructor both try to free the Conformer, causing a double-free segfault.
|
|
67
|
+
smiles = "CCO" # ethanol
|
|
68
|
+
rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
|
|
69
|
+
|
|
70
|
+
# Create a conformer with 3D coordinates
|
|
71
|
+
conf = RDKitChem::Conformer.new(rw_mol.get_num_atoms)
|
|
72
|
+
conf.set_atom_pos(0, RDKitChem::Point3D.new(0.0, 0.0, 0.0))
|
|
73
|
+
conf.set_atom_pos(1, RDKitChem::Point3D.new(1.5, 0.0, 0.0))
|
|
74
|
+
conf.set_atom_pos(2, RDKitChem::Point3D.new(2.0, 1.0, 0.0))
|
|
75
|
+
|
|
76
|
+
# Add conformer to molecule (ownership transfers to C++)
|
|
77
|
+
conf_id = rw_mol.add_conf(conf, true)
|
|
78
|
+
assert(conf_id >= 0, "Conformer should be added successfully")
|
|
79
|
+
|
|
80
|
+
# Verify conformer was added
|
|
81
|
+
assert_equal(1, rw_mol.get_num_conformers)
|
|
82
|
+
|
|
83
|
+
# Access the conformer through the molecule
|
|
84
|
+
retrieved_conf = rw_mol.get_conformer(conf_id)
|
|
85
|
+
assert_not_nil(retrieved_conf)
|
|
86
|
+
|
|
87
|
+
# Verify coordinates
|
|
88
|
+
pos = retrieved_conf.get_atom_pos(0)
|
|
89
|
+
assert_in_delta(0.0, pos.x, 0.001)
|
|
90
|
+
assert_in_delta(0.0, pos.y, 0.001)
|
|
91
|
+
assert_in_delta(0.0, pos.z, 0.001)
|
|
92
|
+
|
|
93
|
+
# Force garbage collection to ensure no double-free occurs
|
|
94
|
+
GC.start
|
|
95
|
+
|
|
96
|
+
# The molecule should still be valid after GC
|
|
97
|
+
assert_equal(3, rw_mol.get_num_atoms)
|
|
46
98
|
end
|
|
47
99
|
end
|