Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
27 changes: 19 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ jobs:
- "3.10"
- "3.11"
- "3.12"
# pymbar 3 does not support Python 3.13
# - "3.13"
gmx-version:
- "5"
- "2019"
Expand Down Expand Up @@ -64,21 +66,29 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Set up conda environment with gromacs
if: matrix.gmx-version != '5' && matrix.gmx-version != '4' && matrix.gmx-version != '2022' && matrix.gmx-version != '2023'
- name: Set up conda environment with gromacs for Python 3.9 or 3.10
if: (matrix.gmx-version != '5' && matrix.gmx-version != '4' && matrix.gmx-version != '2022' && matrix.gmx-version != '2023') && (matrix.python-version == '3.9' || matrix.python-version == '3.10')
uses: mamba-org/setup-micromamba@v1
with:
# ambertools has no Python 3.9 builds; use a separate env file for 3.9
environment-file: ${{ matrix.python-version == '3.9' && 'devtools/conda-envs/test_env_py39.yaml' || 'devtools/conda-envs/test_env.yaml' }}
# Use a reduced legacy environment for Python 3.9/3.10 builds.
environment-file: devtools/conda-envs/test_env_py39.yaml
create-args: >- # beware the >- instead of |, we don't split on newlines but on spaces
python=${{ matrix.python-version }} gromacs=${{ matrix.gmx-version }}.*=nompi_dblprec*

- name: Set up conda environment with gromacs for Python 3.11 or higher
uses: mamba-org/setup-micromamba@v1
if: (matrix.python-version != '3.9' && matrix.python-version != '3.10') && (matrix.gmx-version != '5' && matrix.gmx-version != '4' && matrix.gmx-version != '2022' && matrix.gmx-version != '2023')
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 }} gromacs=${{ matrix.gmx-version }}.*=nompi_dblprec*

- name: Set up conda environment without gromacs
if: matrix.gmx-version == '5' || matrix.gmx-version == '4'
uses: mamba-org/setup-micromamba@v1
with:
# ambertools has no Python 3.9 builds; use a separate env file for 3.9
environment-file: ${{ matrix.python-version == '3.9' && 'devtools/conda-envs/test_env_py39.yaml' || 'devtools/conda-envs/test_env.yaml' }}
# Use the legacy env for Python 3.9/3.10, full env otherwise.
environment-file: ${{ (matrix.python-version == '3.9' || matrix.python-version == '3.10') && 'devtools/conda-envs/test_env_py39.yaml' || '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 }}

Expand All @@ -88,11 +98,12 @@ jobs:
with:
# dblprec conda-forge builds don't exist for 2022/2023; single-precision is sufficient
# for the version-rejection test.
environment-file: ${{ matrix.python-version == '3.9' && 'devtools/conda-envs/test_env_py39.yaml' || 'devtools/conda-envs/test_env.yaml' }}
# Use the legacy env for Python 3.9/3.10, full env otherwise.
environment-file: ${{ (matrix.python-version == '3.9' || matrix.python-version == '3.10') && 'devtools/conda-envs/test_env_py39.yaml' || 'devtools/conda-envs/test_env.yaml' }}
create-args: >-
python=${{ matrix.python-version }} gromacs=${{ matrix.gmx-version }}

- name: Pin setuptools for Python < 3.10
- 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"
Expand Down
7 changes: 5 additions & 2 deletions devtools/conda-envs/test_env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ dependencies:
- ambertools
- ndcctools
- geometric
- openff-toolkit >=0.11.3
- openff-evaluator-base >= 0.4.5
# - gromacs =2019.1
- openff-toolkit >=0.18.0
- openff-interchange >=0.5.1
- openff-evaluator-base >= 0.5.3
# - openff-recharge
# - openeye-toolkits (Don't have a license file to use with GH Actions.)
- pint <0.25
2 changes: 1 addition & 1 deletion devtools/conda-envs/test_env_py39.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ dependencies:
- ndcctools
- geometric
# - gromacs =2019.1
# openff packages require Python >= 3.10; tests are skipped on 3.9
# OpenFF stack is covered by test_env.yaml in newer-Python jobs.
# - openff-toolkit-base
# - openff-evaluator-base
# - openff-recharge
Expand Down
72 changes: 59 additions & 13 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 Expand Up @@ -525,9 +560,7 @@ def submit_jobs(self, mvals, AGrad=True, AHess=True):
)

if (
self._pending_estimate_request.results(
True, polling_interval=self._options.polling_interval
)[0] is None
self._pending_estimate_request.results(False)[0] is None

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.

Having this blocking request here originally meant that other targets had to wait until Evaluator was done computing to even start -- which can be a while ...

):

raise RuntimeError(
Expand Down Expand Up @@ -738,6 +771,19 @@ def get(self, mvals, AGrad=True, AHess=True):
AGrad = bool(AGrad)
AHess = bool(AHess)

# Block until the Evaluator computation is finished. This is intentionally
# placed here (not in submit_jobs) so that Work Queue tasks submitted by other
# targets can run concurrently while we wait.
estimation_results, _ = self._pending_estimate_request.results(
True, polling_interval=self._options.polling_interval
)
if estimation_results is None:
raise RuntimeError(
"No `EvaluatorServer` could be found to retrieve results from. "
"Please double check that a server is running, and that the connection "
"settings specified in the input script are correct."
)

Comment on lines +774 to +786

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.

I moved (and added the same runtimeerror check) this from the submit_jobs function above.

# Extract the properties estimated using the unperturbed parameters.
estimated_data_set, estimated_gradients = self._extract_property_data(
self._pending_estimate_request, mvals, AGrad
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