Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
0df511b
initial implementation of NAGLChargesHandler
j-wags Apr 23, 2025
482128c
have testing env use naglcharges toolkit branch
j-wags Apr 23, 2025
d7aa607
Merge branch 'main' into naglcharges-handler
j-wags Jul 8, 2025
d8f7070
adding a bunch of tests, some todos remain
j-wags Jul 9, 2025
b27d68d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 9, 2025
e592850
I guess valueerror is fine
j-wags Jul 9, 2025
da7686f
Merge remote-tracking branch 'origin/fix-1254' into naglcharges-handler
j-wags Jul 9, 2025
6856153
update vsite charge test
j-wags Jul 9, 2025
d0c522d
Apply suggestions from code review
j-wags Jul 11, 2025
272092a
Replace usages of Interchange.from_smirnoff with ForceField.create_in…
j-wags Jul 11, 2025
6655de5
remove repeated nagl FF creation and replace with new fixture
j-wags Jul 11, 2025
d0f52a4
add check that charge_from_molecules takes precedence over NAGLCharges
j-wags Jul 14, 2025
18e7abc
Tighten all total charge tolerances in new tests down to 1e-10
j-wags Jul 14, 2025
2f14578
add test for NAGL charge assignment failure falling back to lower pre…
j-wags Jul 14, 2025
39f61ef
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 14, 2025
8abbd36
Implement correct(er) error handling/reporting behavior for NAGLCharges
j-wags Jul 15, 2025
f869775
test new error handling logic
j-wags Jul 15, 2025
450f851
test with new openff-nagl-models
j-wags Jul 15, 2025
2ecb940
remove fallback behavior test since it's being handled separately in …
j-wags Jul 17, 2025
151e876
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 17, 2025
52d4bbd
use a more recent nagl model in host-gues example in case that helps …
j-wags Jul 17, 2025
3e2982c
Merge branch 'naglcharges-handler' of github.com:openforcefield/openf…
j-wags Jul 17, 2025
3493599
don't monkeypatch get_release_metadata (since it doesn't exist any more)
j-wags Jul 17, 2025
93a4468
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 17, 2025
cf5e60d
add fail fast and strip down test matrix
j-wags Jul 24, 2025
cfa85d2
try getting openff-nagl-models from main branch
j-wags Jul 24, 2025
f1e9aa2
better check for NAGL wrapper in registry
j-wags Aug 1, 2025
953798b
add tests for naglcharges being superseded by charge_from_molecules
j-wags Aug 1, 2025
a76880e
rename existing sage_with_nagl fixture in charge assignment logging t…
j-wags Aug 1, 2025
75632bb
add naglcharges speed tests
j-wags Aug 1, 2025
798c247
add charge assignment logging tests and update strings with NAGL beha…
j-wags Aug 1, 2025
cc97617
improve test documentation and consolidate invalid model name tests
j-wags Aug 1, 2025
f99a10e
implement dangerous logic for charge method fallback handling and err…
j-wags Aug 1, 2025
f8da57e
remove dangerously broad charge handler fallback and mark tests as xfail
j-wags Aug 1, 2025
71cbd11
Lint and various cleanups/import rearrangement
j-wags Aug 1, 2025
d203cf3
restore testing matrix
j-wags Aug 1, 2025
20f7a12
relax performance test runtime
j-wags Aug 1, 2025
c9e7599
add link to charge fallback test skip reasoning
j-wags Aug 1, 2025
48f5521
revert unrelated changes
j-wags Aug 1, 2025
45ae561
update openff-nagl-models branch used for test envs
j-wags Aug 2, 2025
cca4c67
try not installing from branches in docs env
j-wags Aug 2, 2025
b45e135
Merge branch 'main' into naglcharges-handler
j-wags Aug 2, 2025
3625f3e
have docs env fetch dev branches of toolkit and nagl-models
j-wags Aug 4, 2025
f69c249
Increase allowed execution time on nagl runtime tests
j-wags Aug 4, 2025
2af90cf
route all charge assignment through cached _compute_partial_charges m…
j-wags Aug 6, 2025
1a8a4d1
remove toolkitam1bcc from sage_with_nagl_charges test fixture
j-wags Aug 7, 2025
1a1beda
rename sage_with_nagl_charges test fixture to sage_nagl
j-wags Aug 7, 2025
7944479
remove unnecessary ToolkitAM1BCCHandler registration+deregistration f…
j-wags Aug 7, 2025
518b8b0
point to main branch of nagl-models now that PR is merged
j-wags Aug 7, 2025
9ec2ff7
Merge remote-tracking branch 'upstream/main' into naglcharges-handler
mattwthompson Aug 18, 2025
4fca8fd
Drop development versions
mattwthompson Aug 18, 2025
5676133
clean up pre-merge
j-wags Aug 19, 2025
e60656c
update releasehistory
j-wags Aug 19, 2025
d4fb4e9
Merge remote-tracking branch 'upstream/main' into naglcharges-handler
mattwthompson Aug 19, 2025
3c9ff5c
Merge remote-tracking branch 'upstream/naglcharges-handler' into nagl…
mattwthompson Aug 19, 2025
281c1a8
Merge remote-tracking branch 'upstream/main' into naglcharges-handler
mattwthompson Aug 19, 2025
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: 3 additions & 4 deletions devtools/conda-envs/dev_env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,18 @@ dependencies:
- python
- pip
- versioningit
- pip
- numpy <2.3
- pydantic =2
# OpenFF stack
- openff-toolkit-base ~=0.16.6
- openff-toolkit-base =0.17
- openff-units
- ambertools =23
# Optional features
- openmm =8.2
# smirnoff-plugins =2024
# de-forcefields # add back after smirnoff-plugins update
- openff-nagl ~=0.5
- openff-nagl-models ~=0.3
- openff-nagl
- openff-nagl-models
- mbuild ~=0.18
- foyer =1
- gmso ~=0.12
Expand Down
2 changes: 1 addition & 1 deletion devtools/conda-envs/docs_env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ dependencies:
- pip
- numpy =1
- pydantic =2
- openff-toolkit-base ~=0.16.6
- openff-toolkit-base =0.17
- openmm =8.2
- mbuild
- foyer =1
Expand Down
6 changes: 3 additions & 3 deletions devtools/conda-envs/examples_env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ dependencies:
- numpy <2.3
- pydantic =2
# OpenFF stack
- openff-toolkit-base ~=0.16.6
- openff-toolkit-base =0.17
- openff-units
- ambertools =23
# Optional features
- openff-nagl ~=0.5
- openff-nagl-models ~=0.3
- openff-nagl
- openff-nagl-models
- mbuild
- foyer
- nglview
Expand Down
2 changes: 1 addition & 1 deletion devtools/conda-envs/test_env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ dependencies:
- numpy <2.3
- pydantic =2
# OpenFF stack
- openff-toolkit-base ~=0.16.8
- openff-toolkit-base =0.17
- openff-units =0.3
- ambertools =24
# Needs to be explicitly listed to not be dropped when AmberTools is removed
Expand Down
2 changes: 1 addition & 1 deletion devtools/conda-envs/test_not_py313.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ dependencies:
- numpy <2.3
- pydantic =2
# OpenFF stack
- openff-toolkit-base ~=0.16.8
- openff-toolkit-base =0.17
- openff-units =0.3
- ambertools
# Needs to be explicitly listed to not be dropped when AmberTools is removed
Expand Down
6 changes: 6 additions & 0 deletions docs/releasehistory.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ Dates are given in YYYY-MM-DD format.

Please note that all releases prior to a version 1.0.0 are considered pre-releases and many API changes will come before a stable release.

## 0.4.5 - 2025-08-19

### New features

* #1206 Support `<NAGLCharges>` tags in SMIRNOFF force fields.

## 0.4.4 - 2025-07-29

### Behavior changes
Expand Down
6 changes: 3 additions & 3 deletions examples/host-guest/host_guest.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
"source": [
"NAGLToolkitWrapper().assign_partial_charges(\n",
" molecule=host,\n",
" partial_charge_method=\"openff-gnn-am1bcc-0.1.0-rc.1.pt\",\n",
" partial_charge_method=\"openff-gnn-am1bcc-0.1.0-rc.3.pt\",\n",
")\n",
"\n",
"host.partial_charges.round(3)"
Expand All @@ -110,7 +110,7 @@
"metadata": {},
"outputs": [],
"source": [
"sage = ForceField(\"openff-2.1.0.offxml\")\n",
"sage = ForceField(\"openff-2.2.1.offxml\")\n",
"\n",
"out = sage.create_interchange(topology=docked_topology, charge_from_molecules=[host])"
]
Expand Down Expand Up @@ -192,7 +192,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
"version": "3.11.13"
}
},
"nbformat": 4,
Expand Down
16 changes: 14 additions & 2 deletions openff/interchange/_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def sage_with_bond_charge(sage):
type="BondCharge",
match="all_permutations",
distance="0.8 * angstrom ** 1",
charge_increment1="0.0 * elementary_charge ** 1",
charge_increment1="0.123 * elementary_charge ** 1",
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needed to be a nonzero value for a nagl test, happy to make a separate fixture or put this in the test directly to avoid possible cross contamination.

charge_increment2="0.0 * elementary_charge ** 1",
),
)
Expand Down Expand Up @@ -187,6 +187,19 @@ def sage_with_off_center_hydrogen(sage):
return sage


@pytest.fixture
def sage_nagl(sage):
sage.get_parameter_handler(
"NAGLCharges",
{
"model_file": "openff-gnn-am1bcc-0.1.0-rc.3.pt",
"version": "0.3",
},
)
sage.deregister_parameter_handler("ToolkitAM1BCC")
return sage


@pytest.fixture
def _simple_force_field():
# TODO: Create a minimal force field for faster tests
Expand Down Expand Up @@ -590,7 +603,6 @@ def hydrogen_cyanide_reversed():
def hexane_diol():
molecule = Molecule.from_smiles("OCCCCCCO")
molecule.assign_partial_charges(partial_charge_method="gasteiger")
molecule.partial_charges.m
return molecule


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@

"""
Hierarchy is
1. Match molceules with preset charges
1. Match molecules with preset charges
2. Match chemical environment against library charges
3. Match chemical environment against charge increments
4. Run AM1-BCC (or a variant) on remaining molecules
3. Run NAGLCharges (if present in the FF)
3. Run charge method in ChargeIncrementModel section (if present in the FF) and then
match chemical environment against charge increments
4. Run AM1-BCC (or a variant) on remaining molecules (if present in the FF)

Test cases
----------
Expand Down Expand Up @@ -86,6 +88,19 @@
* Ions get library charges
* Ligand gets charge increments

11. Sage with NAGL and ligand in vacuum
* Ligand gets NAGL charges

12. Sage with NAGL and water molecules (library precedence)
* Water gets library charges (precedence over NAGL)

13. Sage with NAGL mixed topology (ligand and water)
* Ligand gets NAGL charges
* Water gets library charges

14. Sage with NAGL and preset charges on ligand
* Ligand gets preset charges (precedence over NAGL)

Other details
* Specifics of charge method (AM1-BCC, AM1-BCC-ELF10, AM1-BCC via NAGL)
* Molecules with preset charges can be similar but not exact enough
Expand Down Expand Up @@ -118,6 +133,9 @@ def map_methods_to_atom_indices(caplog: pytest.LogCaptureFixture) -> dict[str, l
elif message.startswith("Charge section ToolkitAM1BCC"):
info[message.split(", ")[1].split(" ")[-1]].append(int(message.split("atom index ")[-1]))

elif message.startswith("Charge section NAGLCharges"):
info["NAGLChargesHandler"].append(int(message.split("atom index ")[-1]))

# without also pulling the virtual site - particle mapping (which is different for each engine)
# it's hard to store more information than the orientation atoms that are affected by each
# virtual site's charges
Expand Down Expand Up @@ -149,7 +167,7 @@ def map_methods_to_atom_indices(caplog: pytest.LogCaptureFixture) -> dict[str, l


@pytest.fixture
def sage_with_nagl(sage):
def sage_with_nagl_chargeincrements(sage):
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviewer: I changed this to disambiguate between NAGL called via ChargeIncrementModelHandler (where it tests some SMIRKS-based chargeincrement stuff as well) vs NAGLChargesHandler

from openff.toolkit.typing.engines.smirnoff.parameters import ChargeIncrementModelHandler, ChargeIncrementType

sage.register_parameter_handler(
Expand Down Expand Up @@ -241,6 +259,10 @@ def ligand_and_water_and_ions(ligand, water_and_ions) -> Topology:
8.xSage with preset charges on water
9.xSage with (ligand) virtual site parameters
10.xAM1-with-custom-BCCs Sage with ligand and ions water
11.xSage with NAGL and ligand in vacuum
12.xSage with NAGL and water molecules (library precedence)
13.xSage with NAGL mixed topology (ligand and water)
14.xSage with NAGL and preset charges on ligand
"""


Expand Down Expand Up @@ -416,7 +438,7 @@ def test_case9(caplog, sage_with_bond_charge):
assert info["orientation"] == [0, 1]


def test_case10(caplog, sage_with_nagl, ligand):
def test_case10(caplog, sage_with_nagl_chargeincrements, ligand):
from openff.toolkit.utils.nagl_wrapper import NAGLToolkitWrapper
from openff.toolkit.utils.rdkit_wrapper import RDKitToolkitWrapper
from openff.toolkit.utils.toolkit_registry import ToolkitRegistry, toolkit_registry_manager
Expand All @@ -430,7 +452,7 @@ def test_case10(caplog, sage_with_nagl, ligand):
toolkit_precedence=[NAGLToolkitWrapper, RDKitToolkitWrapper],
),
):
sage_with_nagl.create_interchange(ligand.to_topology())
sage_with_nagl_chargeincrements.create_interchange(ligand.to_topology())

info = map_methods_to_atom_indices(caplog)

Expand All @@ -445,3 +467,70 @@ def test_case10(caplog, sage_with_nagl, ligand):

# the standard AM1-BCC should not have ran
assert AM1BCC_KEY not in info


def test_case11(caplog, sage_nagl, ligand):
"""Test that NAGL charge assignment is properly logged."""
pytest.importorskip("openff.nagl")

with caplog.at_level(logging.INFO):
sage_nagl.create_interchange(ligand.to_topology())

info = map_methods_to_atom_indices(caplog)

# Should log NAGL charges for all atoms
assert "NAGLChargesHandler" in info
assert info["NAGLChargesHandler"] == [*range(0, ligand.n_atoms)]


def test_case12(caplog, sage_nagl, water):
"""Test logging when LibraryCharges takes precedence over NAGL."""
pytest.importorskip("openff.nagl")

topology = Topology.from_molecules([water, water])

with caplog.at_level(logging.INFO):
sage_nagl.create_interchange(topology)

info = map_methods_to_atom_indices(caplog)

# Water should get library charges, not NAGL
assert info["library"] == [*range(0, 6)] # 2 water molecules, 3 atoms each
assert "NAGLChargesHandler" not in info


def test_case13(caplog, sage_nagl, ligand, water):
"""Test logging with mixed molecule types (some library, some NAGL)."""
pytest.importorskip("openff.nagl")

topology = Topology.from_molecules([ligand, water])

with caplog.at_level(logging.INFO):
sage_nagl.create_interchange(topology)

info = map_methods_to_atom_indices(caplog)

# Ligand should get NAGL charges
assert info["NAGLChargesHandler"] == [*range(0, ligand.n_atoms)]

# Water should get library charges
assert info["library"] == [*range(ligand.n_atoms, ligand.n_atoms + water.n_atoms)]


def test_case14(caplog, sage_nagl, ligand):
"""Test logging when preset charges are used instead of NAGL."""
pytest.importorskip("openff.nagl")

ligand.assign_partial_charges("gasteiger")

with caplog.at_level(logging.INFO):
sage_nagl.create_interchange(
ligand.to_topology(),
charge_from_molecules=[ligand],
)

info = map_methods_to_atom_indices(caplog)

# Should use preset charges, not NAGL
assert info["preset"] == [*range(0, ligand.n_atoms)]
assert "NAGLChargesHandler" not in info
Loading
Loading