Skip to content

Commit 5fe1ae1

Browse files
authored
Merge pull request #35 from lamalab-org/fix/qm9-normalization-roundtrip
fix: H-assigment
2 parents 9dfdffd + 0ab9d7c commit 5fe1ae1

3 files changed

Lines changed: 46 additions & 9 deletions

File tree

src/openclatura/additive.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ def add_indicated_hydrogens(mol: Molecule, parts: AssemblyParts, numbered_path:
5353
and len(ring_bonds) == 3
5454
and atom.explicit_h_count + atom.total_h_count > 0
5555
)
56-
if sum(b.order for b in ring_bonds) == 2 or fusion_carbon_h:
56+
indicated_h_site = sum(b.order for b in ring_bonds) == 2 and (
57+
not atom.is_carbon or atom.explicit_h_count + atom.total_h_count > 0
58+
)
59+
if indicated_h_site or fusion_carbon_h:
5760
candidates.append((locant, idx))
5861

5962
# A hydrogenated fusion carbon changes the retained-parent hydride state.

src/openclatura/retained_fused_templates.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,16 +109,15 @@ def retained_fused_graph_templates(*, include_disabled: bool = False) -> tuple[R
109109

110110
@cache
111111
def retained_parent_metadata(parent_name: str) -> RetainedParentMetadata | None:
112-
"""Return graph-template metadata for a retained parent spelling."""
112+
"""Return metadata only when the spelling identifies one exact hydride.
113+
114+
Bare aliases such as ``isoindole`` do not identify the indicated-hydrogen
115+
tautomer. Those sites must be derived from the selected molecular graph;
116+
production graph-template matches pass their metadata directly.
117+
"""
113118

114119
for template in retained_fused_graph_templates(include_disabled=True):
115-
spellings = {template.name, *template.aliases}
116-
spellings.update(
117-
f"{locant}H-{template.name}"
118-
for locant in template.default_indicated_h
119-
if not template.name.startswith(f"{locant}H-")
120-
)
121-
if parent_name in spellings:
120+
if parent_name == template.name:
122121
return RetainedParentMetadata(
123122
default_indicated_h=template.default_indicated_h,
124123
fusion_locants=template.fusion_atoms,

src/openclatura/tests/test_analysis.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@
142142
retained_fused_base_templates,
143143
retained_fused_graph_templates,
144144
retained_fused_template_from_data,
145+
retained_parent_metadata,
145146
template_molecule,
146147
)
147148
from openclatura.retained_specs import retained_parent_spec
@@ -2968,6 +2969,40 @@ def test_retained_fused_base_skeletons_are_loaded_from_the_graph_template_table(
29682969
assert base_templates["purinoid_9"]["mancude_double_bonds"] == 4
29692970

29702971

2972+
def test_no_bare_retained_alias_infers_a_canonical_hydride_tautomer():
2973+
templates = retained_fused_graph_templates(include_disabled=True)
2974+
bare_hydride_aliases = {
2975+
alias
2976+
for template in templates
2977+
if template.default_indicated_h
2978+
for alias in template.aliases
2979+
if "H-" not in alias
2980+
}
2981+
2982+
# This registry-wide invariant automatically covers future retained
2983+
# hydrides whose bare alias omits the tautomer-defining H locant.
2984+
assert {"phenalene", "perimidine", "isoindole", "indole"} <= bare_hydride_aliases
2985+
assert all(retained_parent_metadata(alias) is None for alias in bare_hydride_aliases)
2986+
2987+
exact_hydride_templates = [template for template in templates if "H-" in template.name]
2988+
assert exact_hydride_templates
2989+
for template in exact_hydride_templates:
2990+
metadata = retained_parent_metadata(template.name)
2991+
assert metadata is not None
2992+
assert metadata.default_indicated_h == template.default_indicated_h
2993+
2994+
2995+
def test_indicated_hydrogen_follows_graph_tautomer_but_not_hydrogen_free_spiro_carbon():
2996+
cases = {
2997+
"c1ccc2c(c1)CN=C2C1=NCc2ccccc21": "3-(3H-isoindol-1-yl)-1H-isoindole",
2998+
"O=C(OCCNC1=NCc2ccccc21)c1ccccc1": "2-((3H-isoindol-1-yl)amino)ethyl benzoate",
2999+
"FN1CCC2(C=Nc3ccccc32)CC1": "1'-fluorospiro[indole-3,4'-piperidine]",
3000+
}
3001+
3002+
for smiles, expected in cases.items():
3003+
assert name_smiles(smiles) == expected
3004+
3005+
29713006
def test_retained_fused_graph_template_schema_validates_locant_graphs():
29723007
template = retained_fused_template_from_data(_naphthalene_graph_template_row())
29733008

0 commit comments

Comments
 (0)