mirror of
https://github.com/rdkit/rdkit.git
synced 2026-06-03 21:44:30 +08:00
Enable templating for macrocycles (#9203)
* parse templates as smarts * accept ring templates in SMARTS format * undo CLAUDE mistake * rename files * enable templating for macrocycles * enable macrocycle templating * Add test for macrocycle templating Tests that ring system templates are used only for macrocycles (rings with size > 8). The test verifies the exact threshold by generating coordinates with and without templates for rings of size 4-14. Addresses review feedback on PR #9203. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -629,7 +629,10 @@ void EmbeddedFrag::embedFusedRings(const RDKit::VECT_INT_VECT &fusedRings,
|
||||
|
||||
RDKit::INT_VECT funion;
|
||||
// look for a template that matches the entire fused ring system
|
||||
if (useRingTemplates && fusedRings.size() > 1) {
|
||||
// For single rings, only use templates for macrocycles (size > 8)
|
||||
if (useRingTemplates &&
|
||||
(fusedRings.size() > 1 ||
|
||||
(fusedRings.size() == 1 && fusedRings[0].size() > 8))) {
|
||||
RDKit::Union(fusedRings, funion);
|
||||
bool found_template = matchToTemplate(funion, fusedRings.size());
|
||||
if (found_template) {
|
||||
|
||||
@@ -2472,4 +2472,55 @@ TEST_CASE("canonical ordering") {
|
||||
INFO("i " << i << " " << j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("macrocycle templating") {
|
||||
// Helper function to test if templates are used for a ring of size n.
|
||||
// We generate a ring of that size, generate 2D coordinates with and without
|
||||
// templates enabled, and compare the results. If the coordinates are the
|
||||
// same, we assume no template was used. If they differ, a template was used.
|
||||
auto templates_are_used_for_ring_size_n = [](int ringSize) -> bool {
|
||||
// Build SMILES for n-membered ring: C1 + (n-2) C's + C1
|
||||
std::string smiles = "C1";
|
||||
for (int i = 0; i < ringSize - 2; ++i) {
|
||||
smiles += "C";
|
||||
}
|
||||
smiles += "C1";
|
||||
|
||||
auto mol = SmilesToMol(smiles);
|
||||
if (!mol) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate coordinates WITHOUT templates
|
||||
RDDepict::Compute2DCoordParameters params;
|
||||
params.useRingTemplates = false;
|
||||
RDDepict::compute2DCoords(*mol, params);
|
||||
|
||||
auto withoutTemplates =
|
||||
mol->getConformer().getAtomPos(0) -
|
||||
mol->getConformer().getAtomPos(ringSize / 2);
|
||||
|
||||
// Generate coordinates WITH templates
|
||||
params.useRingTemplates = true;
|
||||
RDDepict::compute2DCoords(*mol, params);
|
||||
|
||||
auto withTemplates = mol->getConformer().getAtomPos(0) -
|
||||
mol->getConformer().getAtomPos(ringSize / 2);
|
||||
|
||||
delete mol;
|
||||
|
||||
// Return true if coordinates differ (templates were used)
|
||||
return !RDKit::feq(withoutTemplates.length(), withTemplates.length(), 0.01);
|
||||
};
|
||||
|
||||
SECTION("template usage threshold at ring size 8") {
|
||||
// Test that templates are used only for rings with size > 8
|
||||
for (int i = 4; i <= 14; ++i) {
|
||||
CAPTURE(i);
|
||||
bool templatesUsed = templates_are_used_for_ring_size_n(i);
|
||||
bool expectedTemplatesUsed = (i > 8);
|
||||
CHECK(templatesUsed == expectedTemplatesUsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user