Skip to content

Commit

Permalink
Added Structure.swap_indices() method for changing the symbol and geo…
Browse files Browse the repository at this point in the history
…metry indices on a Structure.
  • Loading branch information
coltonbh committed Nov 11, 2024
1 parent 924176a commit cbc2f8f
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [unreleased]

### Added

- `Structure.swap_indices()` method for changing the indices of a structure's symbols and geometry. Helpful for setting up structures for an NEB run or RMSD calculation in which index labels are important.

## [0.11.14] - 2024-10-16

### Fixed
Expand Down
41 changes: 41 additions & 0 deletions qcio/models/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,47 @@ def model_dump(self, **kwargs) -> Dict[str, Any]:
]
return as_dict

def swap_indices(self, indices: List[Tuple[int, int]]) -> None:
"""Swap the indices in the symbols and geometry list.
Args:
indices: A list of tuples containing the indices to swap. E.g.,
[(0, 1), (2, 3)] will swap the first and second atoms and the third
and fourth atoms.
"""
# Validate indices
old_set = set()
new_set = set()
for old, new in indices:
error = False
error_message = ""
if old in old_set:
error_message += (
f"Duplicated old index: {old}. You cannot move an atom twice. "
)
error = True
if new in new_set:
error_message += (
f"Duplicated new index: {new}. You cannot move two atoms to the "
"same index."
)
error = True
if error:
raise ValueError(error_message)

old_set.add(old)
new_set.add(new)

# Perform reordering
new_symbols = [s for s in self.symbols]
new_geometry = np.array([g for g in self.geometry])
for old, new in indices:
new_symbols[new] = self.symbols[old]
new_geometry[new] = self.geometry[old]

object.__setattr__(self, "symbols", new_symbols)
object.__setattr__(self, "geometry", new_geometry)


@renamed_class(Structure)
class Molecule(Structure):
Expand Down
12 changes: 11 additions & 1 deletion tests/test_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def test_smiles_to_structure_rdkit():
[-3.15050459, 1.21801804, 0.2556908],
[-2.22416969, -1.49454485, -1.60187022],
],
atol=1e-4,
atol=1e-1,
)
assert struct.charge == 0
assert struct.multiplicity == 1
Expand Down Expand Up @@ -343,3 +343,13 @@ def test_distance():
struct = Structure(symbols=["H", "H"], geometry=[[0, 0, 0], [0, 1.4, -1.3]])

assert struct.distance(0, 1) == pytest.approx(1.91049731, abs=1e-8)


def test_reorder_indices():
struct = Structure(
symbols=["H", "O", "H"], geometry=[[1, 0, 0], [0, 0, 0], [0, 0, 1]]
)
struct.swap_indices([(0, 1), (1, 2), (2, 0)])

assert struct.symbols == ["H", "H", "O"]
assert np.array_equal(struct.geometry, [[0, 0, 1], [1, 0, 0], [0, 0, 0]])

0 comments on commit cbc2f8f

Please sign in to comment.