Skip to content
Merged
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]
### Removed
- `geom.is_duplicate_conformer()` and `irmsd` dependency. (Broken `numpy` reference in solved `irmsd` version).
- `Algorithm.IRMSD` conformer-group identity algorithm.


### Fixed
- Dependencies listed in `pixi.toml` instead of `pyproject.toml`

## [0.0.22] - 2026-08-28
### Added
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Conversions are standalone module-level functions, not methods on the Pydantic m

### Current module map

- `automol.geom` — the `Geometry` core model (`core.py`), plus `properties.py` (center of mass, distance matrix/keys, adjacency matrix), `transform.py`, `comparison.py` (e.g. `is_duplicate_conformer`, via `irmsd`), and `view.py` (`View`, 3D visualization, py3Dmol-based).
- `automol.geom` — the `Geometry` core model (`core.py`), plus `properties.py` (center of mass, distance matrix/keys, adjacency matrix), `transform.py`, `comparison.py`, and `view.py` (`View`, 3D visualization, py3Dmol-based).
- `automol.rd` — RDKit bridge (`rd/mol.py`) for `Geometry` ↔ RDKit mol conversion.
- `automol.ident` — `Algorithm`/`Identity` for InChI/SMILES generation from a `Geometry`.
- `automol.utils` — shared low-level helpers: `constants.py`, `types.py`, `exc.py`, `utils/element/`.
Expand Down
24 changes: 0 additions & 24 deletions docs/source/geometry.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,30 +108,6 @@ subset of atoms by index, leaving the rest untouched:
geom.transform.translate(water, [1.0, 0.0, 0.0], keys=[0]) # move only atom 0
```

## Duplicate conformer detection

`is_duplicate_conformer` checks whether a `Geometry` is geometrically
identical to any geometry in a list, using
[irmsd](https://pypi.org/project/irmsd/)'s interatomic RMSD after optimal
alignment (translation, rotation, and atom matching) — so a rotated or
translated copy still counts as a match, while a genuinely different
conformer doesn't:

```python
rotated_water = geom.transform.rotate(water, Rotation.from_euler("z", 60, degrees=True))
geom.is_duplicate_conformer(water, [rotated_water]) # True

geom.is_duplicate_conformer(water, []) # False — nothing to compare against
```

Candidates with a different atom count are never considered a match.
`rthr` (default `0.125`, matching irmsd's own default) sets the iRMSD
threshold, in Angstroms, below which two geometries count as identical —
lower it for stricter matching:

```python
geom.is_duplicate_conformer(water, [rotated_water], rthr=1e-6) # stricter
```

## Next steps

Expand Down
1,747 changes: 994 additions & 753 deletions pixi.lock

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ preview = ["pixi-build"]
[dependencies]
python = ">=3.12,<3.14"
automol = { path = "." }
ipython = ">=9.15.0,<10"
pynauty = ">=2.8.8.1,<3"

[package]
name = "automol"
Expand Down Expand Up @@ -50,7 +48,6 @@ dev = { features = ["dev", "docs"], solve-group = "default"}

[pypi-dependencies]
stereomolgraph = ">=0.0.22b0"
irmsd = "==0.1.1"
xyzrender = ">=0.3.1, <0.4"

[feature.dev.tasks]
Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ authors = [
]
requires-python = ">= 3.12"
dependencies = [
"ipython>=9.17.1",
"numpy>=2.0",
"pint>=0.24",
"py3dmol>=2.5",
"pydantic>=2.5",
"pynauty>=2.8.8.1",
"pyparsing>=3.3.2",
"rdkit>=2025",
"scipy>=1.13",
"stereomolgraph>=0.0.22b0",
"irmsd>=0.1.1",
"xyzrender>=0.3.8",
]

[build-system]
Expand Down
2 changes: 0 additions & 2 deletions src/automol/geom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
inertia_axes,
inertia_moments,
inertia_tensor,
is_duplicate_conformer,
kabsch_align,
mass_weight_vector,
normal_mode_projection,
Expand Down Expand Up @@ -64,7 +63,6 @@
"inertia_moments",
"inertia_tensor",
"io",
"is_duplicate_conformer",
"kabsch_align",
"mass_weight_vector",
"normal_mode_projection",
Expand Down
46 changes: 0 additions & 46 deletions src/automol/geom/analysis.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
"""Geometric, structural, and vibrational analysis of a geometry."""

from collections.abc import Sequence
from itertools import permutations
from math import factorial
from typing import TYPE_CHECKING, Literal

import irmsd
import numpy as np
import numpy.typing as npt
from pynauty import Graph, autgrp, canon_label
Expand Down Expand Up @@ -746,50 +744,6 @@ def harmonic_zpv(


# Comparison
def is_duplicate_conformer(
geo: "Geometry",
geos: Sequence["Geometry"],
*,
rthr: float = RMSD_THRESHOLD,
) -> list[bool]:
"""Check whether a geometry is an identical conformer to one in a list.

Two geometries are considered identical conformers if, after optimal
alignment (translation, rotation, and atom matching), their interatomic
RMSD is below `rthr`. Candidates with a different atom count are never
considered a match.

Parameters
----------
geo
Geometry to check.
geos
Candidate geometries to compare against.
rthr
iRMSD threshold, in Angstroms, below which two geometries are
considered identical conformers.

Returns
-------
List the same length as `geos`, with `True` at each position where `geo`
matches the corresponding candidate, `False` otherwise.
"""
mol = irmsd.Molecule(symbols=geo.symbols, positions=geo.coordinates)

matches = []
for candidate in geos:
if len(candidate.symbols) != len(geo.symbols):
matches.append(False)
continue
candidate_mol = irmsd.Molecule(
symbols=candidate.symbols, positions=candidate.coordinates
)
value, _, _ = irmsd.get_irmsd_molecule(mol, candidate_mol)
matches.append(value <= rthr)

return matches


def bond_graph(geo: "Geometry") -> Graph:
"""Build a pynauty graph representing geo's bond connectivity.

Expand Down
56 changes: 0 additions & 56 deletions tests/test_comparison.py

This file was deleted.