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

Fix relative tolerance #604

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
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
55 changes: 55 additions & 0 deletions openff/evaluator/_tests/test_full_workflows/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import pytest
from openff.units import unit

from openff.evaluator.datasets import (
MeasurementSource,
PhysicalPropertyDataSet,
PropertyPhase,
)
from openff.evaluator.properties import Density, EnthalpyOfMixing
from openff.evaluator.substances import Substance
from openff.evaluator.thermodynamics import ThermodynamicState


@pytest.fixture
def dummy_enthalpy_of_mixing():
thermodynamic_state = ThermodynamicState(
temperature=298.15 * unit.kelvin,
pressure=101.325 * unit.kilopascal,
)

return EnthalpyOfMixing(
thermodynamic_state=thermodynamic_state,
phase=PropertyPhase.Liquid,
value=1.0 * EnthalpyOfMixing.default_unit(),
uncertainty=1.0 * EnthalpyOfMixing.default_unit(),
source=MeasurementSource(doi=" "),
substance=Substance.from_components("CCCO", "O"),
)


@pytest.fixture
def dummy_density():
thermodynamic_state = ThermodynamicState(
temperature=298.15 * unit.kelvin,
pressure=101.325 * unit.kilopascal,
)

return Density(
thermodynamic_state=thermodynamic_state,
phase=PropertyPhase.Liquid,
value=1.0 * Density.default_unit(),
uncertainty=1.0 * Density.default_unit(),
source=MeasurementSource(doi=" "),
substance=Substance.from_components("CCCO"),
)


@pytest.fixture
def dummy_dataset(dummy_density, dummy_enthalpy_of_mixing):
dataset = PhysicalPropertyDataSet()
dataset.add_properties(dummy_density, dummy_enthalpy_of_mixing)

for i, prop in enumerate(dataset.properties):
prop.id = f"{i}"
return dataset
179 changes: 179 additions & 0 deletions openff/evaluator/_tests/test_full_workflows/test_simulation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import os
import pathlib

import pytest

from openff.evaluator._tests.utils import (
_copy_property_working_data,
_write_force_field,
)
from openff.evaluator.backends.dask import DaskLocalCluster
from openff.evaluator.client import BatchMode, EvaluatorClient, RequestOptions
from openff.evaluator.forcefield import SmirnoffForceFieldSource
from openff.evaluator.layers.simulation import SimulationLayer
from openff.evaluator.properties import Density, EnthalpyOfMixing
from openff.evaluator.server.server import EvaluatorServer
from openff.evaluator.storage.localfile import LocalFileStorage
from openff.evaluator.workflow import Workflow


def _get_simulation_request_options(
n_molecules: int = 256,
):
dhmix_schema = EnthalpyOfMixing.default_simulation_schema(
n_molecules=n_molecules,
relative_tolerance=0.2,
)
density_schema = Density.default_simulation_schema(
n_molecules=n_molecules,
relative_tolerance=0.2,
)

options = RequestOptions()
options.calculation_layers = ["SimulationLayer"]

options.add_schema(
"SimulationLayer",
"Density",
density_schema,
)
options.add_schema(
"SimulationLayer",
"EnthalpyOfMixing",
dhmix_schema,
)
return options


class TestSimulationLayer:

@pytest.fixture
def dhmix_density_CCCO(self, tmp_path_factory):
path = tmp_path_factory.mktemp("dhmix-density-CCCO")
path.mkdir(exist_ok=True, parents=True)

_copy_property_working_data(
"test/workflows/simulation/dhmix-density-CCCO",
uuid_prefix=["0", "1"],
destination_directory=path,
)
return path

def test_simulation(self, dummy_enthalpy_of_mixing, dhmix_density_CCCO):
"""
Test direct execution of an EnthalpyOfMixing protocol
"""
os.chdir(dhmix_density_CCCO)

_write_force_field()
schema = EnthalpyOfMixing.default_simulation_schema(
n_molecules=256,
relative_tolerance=0.2,
)

metadata = Workflow.generate_default_metadata(
dummy_enthalpy_of_mixing, "force-field.json"
)
metadata.update(
SimulationLayer._get_workflow_metadata(
".",
dummy_enthalpy_of_mixing,
"force-field.json",
[],
LocalFileStorage(),
schema,
)
)

workflow_schema = schema.workflow_schema
workflow_schema.replace_protocol_types(
{"BaseBuildSystem": "BuildSmirnoffSystem"}
)
uuid_prefix = "1"
workflow = Workflow.from_schema(
workflow_schema, metadata=metadata, unique_id=uuid_prefix
)
workflow_graph = workflow.to_graph()
protocol_graph = workflow_graph._protocol_graph

previous_output_paths = []

for name, protocol in workflow_graph.protocols.items():
path = name.replace("|", "_")
if "conditional" not in name:
output = protocol_graph._execute_protocol(
path,
protocol,
True,
*previous_output_paths,
available_resources=None,
safe_exceptions=False,
)
previous_output_paths.append(output)

# delete existing output so we can re-create it
pattern = "*conditional*/*conditional*output.json"
for file in pathlib.Path(".").rglob(pattern):
file.unlink()

for name, protocol in workflow_graph.protocols.items():
path = name.replace("|", "_")
if "conditional" in name:
output = protocol_graph._execute_protocol(
path,
protocol,
True,
*previous_output_paths,
available_resources=None,
safe_exceptions=False,
)
previous_output_paths.append(output)

def test_simulation_with_server(self, dummy_dataset, dhmix_density_CCCO):
"""
Test the full workflow with a server
"""
force_field_path = "openff-2.1.0.offxml"
force_field_source = SmirnoffForceFieldSource.from_path(force_field_path)

os.chdir(dhmix_density_CCCO)
options = _get_simulation_request_options(
n_molecules=256,
)
options.batch_mode = BatchMode.NoBatch

batch_path = pathlib.Path("SimulationLayer") / "batch_0000"
batch_path.mkdir(parents=True, exist_ok=True)
_copy_property_working_data(
"test/workflows/simulation/dhmix-density-CCCO",
uuid_prefix="0",
destination_directory=batch_path,
)
_copy_property_working_data(
"test/workflows/simulation/dhmix-density-CCCO",
uuid_prefix="1",
destination_directory=batch_path,
)

with DaskLocalCluster(number_of_workers=1) as calculation_backend:
server = EvaluatorServer(
calculation_backend=calculation_backend,
working_directory=".",
delete_working_files=False,
)
with server:
client = EvaluatorClient()
request, error = client.request_estimate(
dummy_dataset, force_field_source, options
)

assert error is None
results, exception = request.results(
synchronous=True, polling_interval=30
)

assert exception is None
assert len(results.queued_properties) == 0
assert len(results.estimated_properties) == 2
assert len(results.unsuccessful_properties) == 0
assert len(results.exceptions) == 0
57 changes: 57 additions & 0 deletions openff/evaluator/_tests/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import os
import pathlib
import shutil
import uuid
from contextlib import contextmanager

from openff.toolkit import ForceField
from openff.units import unit
from openff.utilities.utilities import get_data_dir_path

from openff.evaluator.datasets import (
CalculationSource,
Expand Down Expand Up @@ -238,3 +242,56 @@ def build_tip3p_smirnoff_force_field():
)

return SmirnoffForceFieldSource.from_object(smirnoff_force_field_with_tip3p)


def _write_force_field(force_field: str = "openff-2.0.0.offxml"):
"""
Write a force field file to disk.
"""
ff = ForceField(force_field)
with open("force-field.json", "w") as file:
file.write(SmirnoffForceFieldSource.from_object(ff).json())


def _copy_property_working_data(
source_directory: str,
uuid_prefix: str | list[str],
destination_directory: str = ".",
include_data_files: bool = False,
):
"""
Copy test data from one directory to another.
This is typically working data in a *Layer directory.

Parameters
----------
source_directory : str
The directory to copy data from.
This should be relative to to the data directory.
For tests, the path will typically start with `test`.
uuid_prefix : str or list[str]
The prefix of the UUID/s to copy.
destination_directory : str
The directory to copy data to.
This should be relative to the current working directory.
Default is the current working directory.
"""
if isinstance(uuid_prefix, str):
uuid_prefix = [uuid_prefix]

# locate our saved test data
data_directory = pathlib.Path(
get_data_dir_path(source_directory, "openff.evaluator")
)
abs_path = data_directory.resolve()
destination_directory = pathlib.Path(destination_directory)

# copy data files over from data_directory
for path in abs_path.iterdir():
if path.name.startswith(tuple(uuid_prefix)) and path.is_dir():
dest_dir = destination_directory / path.name
shutil.copytree(path, dest_dir)

if include_data_files:
for path in abs_path.glob("data*.json"):
shutil.copy(path, destination_directory)

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{".output_trajectory_path": "0_decorrelate_trajectory/uncorrelated_trajectory.dcd"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{".result": {"value": {"value": {"value": -4.5574468423925065, "unit": "kilojoule / mole", "@type": "openff.evaluator.unit.Quantity"}, "error": {"value": 0.029844082370627523, "unit": "kilojoule / mole", "@type": "openff.evaluator.unit.Quantity"}, "@type": "openff.evaluator.unit.Measurement"}, "gradients": [], "@type": "openff.evaluator.utils.observables.Observable"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{".output_number_of_molecules": 256, ".output_substance": {"components": [{"smiles": "CCCO", "role": {"value": "solv", "@type": "openff.evaluator.substances.components.Component.Role"}, "@type": "openff.evaluator.substances.components.Component"}], "amounts": {"CCCO{solv}": [{"value": 1.0, "@type": "openff.evaluator.substances.amounts.MoleFraction"}]}, "@type": "openff.evaluator.substances.substances.Substance"}, ".assigned_residue_names": {"CCCO{solv}": "UFX"}, ".coordinate_file_path": "1_build_coordinates_component_0/output.pdb"}
Loading