Skip to content

Commit

Permalink
Add Topology.clear_positions (#1827)
Browse files Browse the repository at this point in the history
* Add `Topology.clear_positions`

* Update release history
  • Loading branch information
mattwthompson authored Mar 11, 2024
1 parent 118918c commit bba866c
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/releasehistory.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Releases follow the `major.minor.micro` scheme recommended by [PEP440](https://w

### New features

- [PR #1827](https://github.com/openforcefield/openff-toolkit/pull/1827): Adds `Topology.clear_positions`

### Improved documentation and warnings

- [PR #1795](https://github.com/openforcefield/openff-toolkit/pull/1795): Add `NAGLToolkitWrapper` to API reference
Expand Down
32 changes: 32 additions & 0 deletions openff/toolkit/_tests/test_topology.py
Original file line number Diff line number Diff line change
Expand Up @@ -2243,6 +2243,14 @@ def topology(self) -> Topology:

return topology

@pytest.fixture()
def topology_with_positions(self, topology) -> Topology:
# These conformers will obviously overlap, but they'll be the right data type
for molecule in topology.molecules:
molecule.generate_conformers(n_conformers=1)

return topology

def test_set_positions_fails_without_units(self, topology):
# Generate positions without units deterministically
positions_no_units = np.arange(topology.n_atoms * 3).reshape(-1, 3)
Expand Down Expand Up @@ -2326,6 +2334,30 @@ def test_get_positions_none(self, topology):
topology._molecules[0]._conformers = None
assert topology.get_positions() is None

def test_clear_and_re_set_positions(self, topology_with_positions):
initial_positions = topology_with_positions.get_positions()
assert initial_positions is not None
assert initial_positions.shape == (topology_with_positions.n_atoms, 3)

with pytest.raises(
ValueError,
match="cannot be None.*use clear_positions",
):
topology_with_positions.set_positions(None)

topology_with_positions.clear_positions()

assert topology_with_positions.get_positions() is None

for molecule in topology_with_positions.molecules:
assert molecule.conformers is None

# re-set positions back to their original values
topology_with_positions.set_positions(initial_positions)

assert initial_positions is not None
assert initial_positions.shape == (topology_with_positions.n_atoms, 3)


@requires_rdkit
class TestTopologyFromPdbCustomSubstructures:
Expand Down
22 changes: 22 additions & 0 deletions openff/toolkit/topology/topology.py
Original file line number Diff line number Diff line change
Expand Up @@ -2066,6 +2066,7 @@ def get_positions(self) -> Optional[Quantity]:
See Also
========
set_positions
clear_positions
"""
conformers = []
for molecule in self.molecules:
Expand All @@ -2081,6 +2082,21 @@ def get_positions(self) -> Optional[Quantity]:

return Quantity(positions, unit.nanometer)

def clear_positions(self):
"""
Clear the positions of this topology by removing all conformers from its consituent molecules.
Note that all conformers will be deleted (in-place) from all molecules. Use `Topology.get_positions()`
if you wish to save them before clearing.
See Also
--------
get_positions
set_positions
"""
for molecule in self.molecules:
molecule._conformers = None

def set_positions(self, array: Quantity):
"""
Set the positions in a topology by copying from a single n×3 array.
Expand All @@ -2098,7 +2114,13 @@ def set_positions(self, array: Quantity):
See Also
========
get_positions
clear_positions
"""
if array is None:
raise ValueError(
"array argument cannot be None, use clear_positions instead."
)

if not isinstance(array, Quantity):
raise IncompatibleUnitError(
"array should be an OpenFF Quantity with dimensions of length"
Expand Down

0 comments on commit bba866c

Please sign in to comment.