Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Amber parsers #636

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
4 changes: 2 additions & 2 deletions openff/interchange/components/interchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ def to_openmm_topology(
def to_prmtop(self, file_path: Union[Path, str], writer="internal"):
"""Export this Interchange to an Amber .prmtop file."""
if writer == "internal":
from openff.interchange.interop.internal.amber import to_prmtop
from openff.interchange.interop.amber.export import to_prmtop

to_prmtop(self, file_path)

Expand Down Expand Up @@ -403,7 +403,7 @@ def to_crd(self, file_path: Union[Path, str]):
def to_inpcrd(self, file_path: Union[Path, str], writer="internal"):
"""Export this Interchange to an Amber .inpcrd file."""
if writer == "internal":
from openff.interchange.interop.internal.amber import to_inpcrd
from openff.interchange.interop.amber.export import to_inpcrd

to_inpcrd(self, file_path)

Expand Down
2 changes: 1 addition & 1 deletion openff/interchange/drivers/openmm.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def _get_openmm_energies(
positions: openmm.unit.Quantity,
round_positions: Optional[int],
platform: str,
) -> EnergyReport:
) -> Dict[int, openmm.Force]:
"""Given prepared `openmm` objects, run a single-point energy calculation."""
for index, force in enumerate(system.getForces()):
force.setForceGroup(index)
Expand Down
1 change: 1 addition & 0 deletions openff/interchange/interop/amber/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Interoperability with Amber files."""
1 change: 1 addition & 0 deletions openff/interchange/interop/amber/_import/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Interfaces with Amber."""
91 changes: 91 additions & 0 deletions openff/interchange/interop/amber/_import/_import.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"""Interfaces with Amber."""
from typing import TYPE_CHECKING, Dict, List

if TYPE_CHECKING:
from openff.toolkit import Topology

from openff.interchange import Interchange


def from_prmtop(
file: str,
) -> "Interchange":
"""Import from a prmtop file."""
from openff.interchange import Interchange

interchange = Interchange()

data: Dict[str, List[str]] = dict()

with open(file) as f:
for chunk in f.read().split(r"%FLAG"):
tag, format, *_data = chunk.strip().split()

if tag == "%VERSION":
continue

data[tag] = _data

interchange.topology = _make_topology(data)

return interchange


def _make_topology(data: Dict[str, List[str]]) -> "Topology":
"""Make a topology from the data."""
from openff.toolkit import Topology
from openff.toolkit.topology._mm_molecule import _SimpleMolecule

Topology._add_bond = _add_bond

topology = Topology()

start_index = 0

for molecule_index in range(int(data["POINTERS"][11])):
molecule = _SimpleMolecule()

end_index = start_index + int(data["ATOMS_PER_MOLECULE"][molecule_index])

for atom_index in range(start_index, end_index):
# TODO: Check for isotopes (unsupported) or otherwise botches atomic masses
molecule.add_atom(
atomic_number=int(data["ATOMIC_NUMBER"][atom_index]),
name=data["ATOM_NAME"][atom_index],
)

topology.add_molecule(molecule)

start_index = end_index

bonds: List[str] = data["BONDS_INC_HYDROGEN"] + data["BONDS_WITHOUT_HYDROGEN"]

# third value in each triplet is an index to the bond type
for n1, n2 in zip(bonds[::3], bonds[1::3]):
# See BONDS_INC_HYDROGEN in https://ambermd.org/prmtop.pdf
# For run-time efficiency, the atom indexes are actually indexes into a coordinate array,
# so the actual atom index A is calculated from the coordinate array index N by A = N/3 + 1

a1 = int(int(n1) / 3)
a2 = int(int(n2) / 3)

topology._add_bond(int(a1), int(a2))

return topology


def _add_bond(self, atom1_index: int, atom2_index: int):
atom1 = self.atom(atom1_index)
atom2 = self.atom(atom2_index)

if atom1.molecule is not atom2.molecule:
raise ValueError(
"Cannot add a bond between atoms in different molecules.",
)

molecule = atom1.molecule

molecule.add_bond(
atom1,
atom2,
)
2 changes: 2 additions & 0 deletions openff/interchange/interop/amber/export/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"""Exports to Amber files."""
from openff.interchange.interop.amber.export.export import to_inpcrd, to_prmtop
3 changes: 2 additions & 1 deletion openff/interchange/smirnoff/_gbsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from openff.interchange.components.potentials import Potential
from openff.interchange.constants import kcal_mol_a2
from openff.interchange.exceptions import InvalidParameterHandlerError
from openff.interchange.smirnoff._base import SMIRNOFFCollection

if TYPE_CHECKING:
Expand Down Expand Up @@ -70,7 +71,7 @@ def store_potentials(self, parameter_handler: GBSAHandler) -> None:
self.potentials[potential_key] = potential

@classmethod
def create( # type: ignore[override]
def create(
cls,
parameter_handler: GBSAHandler,
topology: "Topology",
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ per-file-ignores =
openff/interchange/components/*:F821
openff/interchange/__init__.py:F401
openff/interchange/tests/data/*:INP001
openff/interchange/interop/amber/export/__init__.py:F401
plugins/*:INP001

[isort]
Expand Down