Skip to content
Merged
Show file tree
Hide file tree
Changes from 66 commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
d901d44
update smirnoff for vsites
lilyminium Feb 25, 2026
38b5d02
add tests
lilyminium Feb 25, 2026
6977614
add tests
lilyminium Feb 25, 2026
9abc5b4
this has to move in lockstep with evaluator
lilyminium Feb 25, 2026
f210213
undo py313
lilyminium Feb 25, 2026
1a8db42
pin pint
lilyminium Feb 25, 2026
f84cf66
oops use right branch
lilyminium Feb 25, 2026
c0fcaa4
add evaluator to env for dependencies
lilyminium Feb 25, 2026
bc464d3
Revert "add evaluator to env for dependencies"
lilyminium Feb 25, 2026
276aa2e
install package higher to trigger error earlier
lilyminium Feb 25, 2026
040c763
add evaluator dependencies
lilyminium Feb 25, 2026
99f616b
widen tolerance of evaluator test and wait for server
lilyminium Feb 25, 2026
1c34afa
undo fancy wait method
lilyminium Feb 25, 2026
b8c2d35
try pinning dask
lilyminium Feb 25, 2026
bdff323
temporarily remove gmx and other tests to iterate faster
lilyminium Feb 25, 2026
7057664
Revert "temporarily remove gmx and other tests to iterate faster"
lilyminium Feb 25, 2026
5b648aa
get stdout output
lilyminium Feb 25, 2026
94a5ab2
don't use context manager
lilyminium Feb 25, 2026
2f1f4ab
Reapply "temporarily remove gmx and other tests to iterate faster"
lilyminium Feb 25, 2026
0d527b4
monitor server and change port
lilyminium Feb 25, 2026
9abf676
fix port
lilyminium Feb 25, 2026
c324a8b
only run this one test
lilyminium Feb 25, 2026
c054c25
give up on evaluator bromine test
lilyminium Feb 25, 2026
8de0b00
Revert "fix port"
lilyminium Feb 25, 2026
c947c34
Revert "monitor server and change port"
lilyminium Feb 25, 2026
3d769c8
Revert "don't use context manager"
lilyminium Feb 25, 2026
ba27332
Revert "get stdout output"
lilyminium Feb 25, 2026
23dc054
expand pythons
lilyminium Feb 26, 2026
c3595f2
ignore files from running tests locally
lilyminium Feb 26, 2026
b794c1b
try making evaluator test more robust
lilyminium Feb 26, 2026
ba45a74
add diagnostic log
lilyminium Feb 27, 2026
5db7e84
more diagnostics
lilyminium Feb 27, 2026
d7c3f42
pin setuptools if py<3.11
lilyminium Feb 27, 2026
b5feb5e
fix evaluator test: bypass _Multiprocessor fork-safety deadlock
lilyminium Feb 27, 2026
000b73f
use spawn
lilyminium Feb 28, 2026
8fe3107
more diagnostics
lilyminium Feb 28, 2026
a582e7a
fix path
lilyminium Feb 28, 2026
ab34285
update evaluator version
lilyminium Feb 28, 2026
f174c38
work around ambertools not being available on python 3.9
lilyminium Mar 1, 2026
ed807d9
oops, use micromamba
lilyminium Mar 1, 2026
689f59d
remove ambertools
lilyminium Mar 1, 2026
f2156b7
add rdkit
lilyminium Mar 1, 2026
9bfd725
pin below 2025.09?
lilyminium Mar 1, 2026
d95bcb7
give up; separate env files
lilyminium Mar 1, 2026
44db401
this has to move in lockstep with evaluator
lilyminium Feb 25, 2026
ed76618
widen tolerance of evaluator test and wait for server
lilyminium Feb 25, 2026
cb95c39
undo fancy wait method
lilyminium Feb 25, 2026
34daa23
monitor server and change port
lilyminium Feb 25, 2026
db21dc9
fix port
lilyminium Feb 25, 2026
45b8e45
Revert "fix port"
lilyminium Feb 25, 2026
471b403
Revert "monitor server and change port"
lilyminium Feb 25, 2026
ed5840b
draft new test
lilyminium Mar 2, 2026
c79515a
fix parameter eval
lilyminium Mar 2, 2026
02ccda0
update import
lilyminium Mar 2, 2026
e70857c
add initial test files
lilyminium Mar 2, 2026
606534c
add more test files
lilyminium Mar 5, 2026
2e57aba
add targets
lilyminium Mar 5, 2026
8c1d0e1
update python env files
lilyminium Mar 5, 2026
b8b6086
undo perturbation experiment
lilyminium Mar 5, 2026
7d56b3e
add pickle fix
lilyminium Mar 5, 2026
b74a0a6
update test with Liquid
lilyminium Mar 5, 2026
751443d
use interchange env with fixed vsites
lilyminium Mar 5, 2026
3ddee47
test with nagl
lilyminium Mar 5, 2026
083d81f
try shorten liquid tests more
lilyminium Mar 6, 2026
167b67d
try to make fit smaller
lilyminium Mar 6, 2026
0e9a701
tidy tests a bit
lilyminium Mar 6, 2026
12309cd
more tidying
lilyminium Mar 6, 2026
36c3ba0
update script to be accurate
lilyminium Mar 6, 2026
a770175
fix teardown
lilyminium Mar 6, 2026
9ac70c8
fix wq blocking
lilyminium Mar 19, 2026
c991c2a
Merge branch 'master' into fix-vsite-fits
lilyminium Mar 19, 2026
8b19797
compress targets
lilyminium Mar 31, 2026
def6686
update branch to released version
lilyminium Mar 31, 2026
0c637fd
update compression and ci
lilyminium Mar 31, 2026
a0937d9
add gmx back
lilyminium Mar 31, 2026
913a63d
no gmx v4 or v5
lilyminium Mar 31, 2026
536146b
make ci steps clearer
lilyminium Mar 31, 2026
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
24 changes: 21 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ jobs:
- macOS-latest
- ubuntu-latest
python-version:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
# pymbar 3 does not support Python 3.13
# - "3.13"

env:
CI_OS: ${{ matrix.os }}
Expand All @@ -35,12 +38,27 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Set up conda environment
- name: Set up conda environment for Python 3.9 or 3.10
uses: mamba-org/setup-micromamba@v1
if: matrix.python-version == '3.9' || matrix.python-version == '3.10'
with:
# OpenFF Evaluator requires Python >= 3.10, so we use a separate environment file for Python 3.9 and 3.10 builds.
environment-file: devtools/conda-envs/test_env_py310.yaml
create-args: >- # beware the >- instead of |, we don't split on newlines but on spaces
python=${{ matrix.python-version }}

- name: Set up conda environment for Python 3.11 or higher
uses: mamba-org/setup-micromamba@v1
if: matrix.python-version != '3.9' && matrix.python-version != '3.10'
with:
environment-file: devtools/conda-envs/test_env.yaml
create-args: >- # beware the >- instead of |, we don't split on newlines but on spaces
python=${{ matrix.python-version }}

- name: Pin setuptools for Python < 3.10
if: matrix.python-version == '3.9' || matrix.python-version == '3.10'
shell: bash -l {0}
run: pip install "setuptools<82"

- name: Additional info about the build
shell: bash
Expand Down Expand Up @@ -119,7 +137,7 @@ jobs:

- name: Run tests
run: |
pytest -v --cov=forcebalance --cov-config=setup.cfg --durations=0 --cov-report=xml src/tests/
pytest -v --cov=forcebalance --cov-config=setup.cfg --durations=0 --cov-report=xml -k "not TestEvaluatorBromineStudy" src/tests/

- name: Run water study
run: |
Expand Down
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,12 @@ studies/*/targets/
studies/*/*.tmp/
studies/*/*.bak/
sutdies/*/*.err

# test files from running CI locally
src/tests/files/XmlScript_out/TIP3G2w.xml
src/tests/files/targets/dms-liquid/dms.xml
src/tests/files/temp.bak/
src/tests/files/test_liquid/targets/Liquid/TIP3G2w.xml
src/tests/files/test_liquid/targets/Liquid/dms.xml
src/tests/test.job
studies/003d_evaluator_liquid_bromine/smirnoff_parameter_assignments.json
25 changes: 23 additions & 2 deletions devtools/conda-envs/test_env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,28 @@ dependencies:
- ndcctools
- geometric
# - gromacs =2019.1
- openff-toolkit >=0.11.3
- openff-evaluator >= 0.4.1
- openff-toolkit >=0.18.0
- openff-interchange >=0.5.1
# - openff-evaluator-base >= 0.4.5
# - openff-recharge
# - openeye-toolkits (Don't have a license file to use with GH Actions.)
- pint <0.25

# evaluator dependencies
- dask ==2024.2.1
- distributed >=2.7.0
- dask-jobqueue >=0.8.0
- dask-kubernetes
- uncertainties
- openmmtools
- pyyaml
- requests
- python-dateutil
- pydantic =2,<2.12
- taproom
- dataclasses
- pandas =2

- pip:
# install branch of evaluator
- git+https://github.com/openforcefield/openff-evaluator.git@fix-vsite-fits

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We will aim to get this merged and into a release ASAP, but this branch needed to be developed in tandem with the Evaluator one for testing to work (hence the same name)

30 changes: 30 additions & 0 deletions devtools/conda-envs/test_env_py310.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: forcebalance-test
channels:
- conda-forge
- openeye
dependencies:
# Base depends
- python
- pip
# Testing
- pytest
- pytest-cov
- codecov
- numpy
- scipy
- lxml
- networkx
- zlib
- swig
- future
- pymbar =3
- openmm >= 8
# ambertools has no Python 3.9 builds on conda-forge
- ndcctools
- geometric
# - gromacs =2019.1
# openff packages require Python >= 3.11; tests are skipped on 3.9 and 3.10
# - openff-toolkit-base
# - openff-evaluator-base
# - openff-recharge
# - openeye-toolkits (Don't have a license file to use with GH Actions.)
55 changes: 45 additions & 10 deletions src/evaluator_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import numpy as np
from forcebalance.nifty import warn_once, printcool, printcool_dictionary
from forcebalance.output import getLogger
from forcebalance.smirnoffio import select_virtual_site_parameter
from forcebalance.target import Target

try:
Expand Down Expand Up @@ -308,20 +309,40 @@ def _parameter_value_from_gradient_key(self, gradient_key):
bool
Returns True if the parameter is a cosmetic one.
"""
# try:
# import openmm.unit as simtk_unit
# except ImportError:
# import simtk.unit as simtk_unit
from openff.units import unit as openff_unit


parameter_handler = self.FF.openff_forcefield.get_parameter_handler(
gradient_key.tag
)
parameter = (
parameter_handler if gradient_key.smirks is None
else parameter_handler.parameters[gradient_key.smirks]
)

if gradient_key.smirks is None:
parameter = parameter_handler
elif gradient_key.tag != "VirtualSites":
parameter = parameter_handler.parameters[gradient_key.smirks]
else:
# VirtualSite parameters are not uniquely identifiable by SMIRKS alone.
# Require explicit type/name/match metadata in every VirtualSites key.
if gradient_key.virtual_site_type is None:
raise KeyError(
f"Gradient key {gradient_key} is missing required virtual_site_type"
)
if gradient_key.virtual_site_name is None:
raise KeyError(
f"Gradient key {gradient_key} is missing required virtual_site_name"
)
if gradient_key.virtual_site_match is None:
raise KeyError(
f"Gradient key {gradient_key} is missing required virtual_site_match"
)

parameter = select_virtual_site_parameter(
parameters=parameter_handler.parameters,
smirks=gradient_key.smirks,
virtual_site_type=gradient_key.virtual_site_type,
virtual_site_name=gradient_key.virtual_site_name,
virtual_site_match=gradient_key.virtual_site_match,
error_context=f"gradient key {gradient_key}",
)

attribute_split = re.split(r"(\d+)", gradient_key.attribute)
attribute_split = list(filter(None, attribute_split))
Expand Down Expand Up @@ -474,14 +495,27 @@ def submit_jobs(self, mvals, AGrad=True, AHess=True):
string_key = field_list[0]
key_split = string_key.split("/")

virtual_site_kwargs = {}

if len(key_split) == 3 and key_split[0] == "":
parameter_tag = key_split[1].strip()
parameter_smirks = None
parameter_attribute = key_split[2].strip()
elif len(key_split) == 4:
elif len(key_split) >= 4:
parameter_tag = key_split[0].strip()
parameter_smirks = key_split[3].strip()
parameter_attribute = key_split[2].strip()

if parameter_tag == "VirtualSites":
# VirtualSites keys must include positional identity metadata:
# VirtualSites/<tag>/<attribute>/<smirks>/<type>/<name>/<match>
if len(key_split) != 7:
raise KeyError(
f"VirtualSites parameter key must include type/name/match: {string_key}"
)
virtual_site_kwargs["virtual_site_type"] = key_split[4].strip()
virtual_site_kwargs["virtual_site_name"] = key_split[5].strip()
virtual_site_kwargs["virtual_site_match"] = key_split[6].strip()
else:
raise NotImplementedError()

Expand All @@ -490,6 +524,7 @@ def submit_jobs(self, mvals, AGrad=True, AHess=True):
tag=parameter_tag,
smirks=parameter_smirks,
attribute=parameter_attribute,
**virtual_site_kwargs,
)

# Find the unit of the gradient parameter.
Expand Down
2 changes: 1 addition & 1 deletion src/forcefield.py
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ def addff_xml(self, ffname):
res = re.search(r'^[-+]?[0-9]*\.?[0-9]*([eEdD][-+]?[0-9]+)?', quantity_str)
value_str, unit_str = quantity_str[:res.end()], quantity_str[res.end():]
# LPW 2023-01-23: Behavior of parameter unit string for "evaluated" parameter is undefined.
unit_str = ""
# unit_str = ""

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixes parameter_eval

quantity_str = e.get(parameter_name)
self.offxml_unit_strs[dest] = unit_str

Expand Down
5 changes: 5 additions & 0 deletions src/openmmio.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,11 @@ def GetVirtualSiteParameters(system):
vsprm.append(_openmm.OutOfPlaneSite_getWeight12(vs))
vsprm.append(_openmm.OutOfPlaneSite_getWeight13(vs))
vsprm.append(_openmm.OutOfPlaneSite_getWeightCross(vs))
elif vstype == 'LocalCoordinatesSite':

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

OpenFF vsites are convereted to localcoordinatesites

vsprm.extend(_openmm.LocalCoordinatesSite_getOriginWeights(vs))
vsprm.extend(_openmm.LocalCoordinatesSite_getXWeights(vs))
vsprm.extend(_openmm.LocalCoordinatesSite_getYWeights(vs))
vsprm.extend(_openmm.LocalCoordinatesSite_getLocalPosition(vs))
return np.array(vsprm)

def GetDrudeParameters(system):
Expand Down
Loading
Loading