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

Feature choices #118

Merged
merged 4 commits into from
Oct 30, 2023
Merged
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
8 changes: 8 additions & 0 deletions corelib/src/libs/SireSystem/forcefieldinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,14 @@ void ForceFieldInfo::setCutoffType(QString cutoff_type)
void ForceFieldInfo::setCutoffType(QString s_cutoff_type,
const PropertyMap &map)
{
if (s_cutoff_type == "auto")
{
if (this->space().isPeriodic())
s_cutoff_type = "PME";
else
s_cutoff_type = "RF";
}

auto cutoff_type = string_to_cutoff_type(s_cutoff_type);

if (cutoff_type == NO_CUTOFF)
Expand Down
15 changes: 13 additions & 2 deletions doc/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ organisation on `GitHub <https://github.com/openbiosim/sire>`__.
`2023.5.0 <https://github.com/openbiosim/sire/compare/2023.4.0...2023.5.0>`__ - December 2023
---------------------------------------------------------------------------------------------

* Added a new :mod:`sire.options` module that contains new
:cls:`sire.options.Option` objects to represent configurable options.
These include documentation, and make it easier to validate and expose
possible values of configurable options. The API docs for
:cls:`~sire.options.Option` shows how to create your own Option type.
The unit test in ``tests/options/test_options.py`` show how to use
the options. This is integrated into the sire/OpenMM layer.

* Please add an item to this changelog when you create your PR

`2023.4.1 <https://github.com/openbiosim/sire/compare/2023.4.0...2023.4.1>`__ - October 2023
---------------------------------------------------------------------------------------------

* Fixed regression introduced in 2023.4.0 that meant that removed the constraints
from water molecules that had no internal bonds. These waters would blow up
as there was nothing holding them together. The need for these constraints is
Expand All @@ -35,8 +48,6 @@ organisation on `GitHub <https://github.com/openbiosim/sire>`__.
* Fixed an issue where the vacuum dynamics and minimisation simulations still
had a spurious periodic box added when ``.commit()`` was called.

* Please add an item to this changelog when you create your PR

`2023.4.0 <https://github.com/openbiosim/sire/compare/2023.3.0...2023.4.0>`__ - October 2023
--------------------------------------------------------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion recipes/sire/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

package:
name: {{ name }}
version: {{ environ.get('GIT_DESCRIBE_TAG', 'PR') }}
version: {{ environ.get('GIT_DESCRIBE_TAG', 'PR').replace('-','') }}

source:
git_url: SIRE_REMOTE
Expand Down
1 change: 1 addition & 0 deletions src/sire/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ add_subdirectory (mm)
add_subdirectory (mol)
add_subdirectory (morph)
add_subdirectory (move)
add_subdirectory (options)
add_subdirectory (restraints)
add_subdirectory (search)
add_subdirectory (stream)
Expand Down
1 change: 1 addition & 0 deletions src/sire/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,7 @@ def _convert(id):
mol = _lazy_import.lazy_module("sire.mol")
morph = _lazy_import.lazy_module("sire.morph")
move = _lazy_import.lazy_module("sire.move")
options = _lazy_import.lazy_module("sire.options")
qt = _lazy_import.lazy_module("sire.qt")
restraints = _lazy_import.lazy_module("sire.restraints")
search = _lazy_import.lazy_module("sire.search")
Expand Down
19 changes: 19 additions & 0 deletions src/sire/base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,25 @@ def __propertymap_set(obj, key, value):
PropertyMap.__orig__set = PropertyMap.set
PropertyMap.set = __propertymap_set

def __propertymap_get_string(obj, key: str):
"""
Return the string value associated with the passed 'key'

This returns 'key' if there is no value associated
"""
key = str(key)
if obj.specified(key):
val = obj[key]

if val.has_value():
return val.value().as_string()
else:
return val.source()
else:
return key

PropertyMap.get_string = __propertymap_get_string


def create_map(values, extras=None):
"""Construct a PropertyMap from the
Expand Down
51 changes: 14 additions & 37 deletions src/sire/mol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1411,6 +1411,7 @@ def _dynamics(
constraint=None,
perturbable_constraint=None,
include_constrained_energies: bool = True,
integrator=None,
schedule=None,
lambda_value=None,
swap_end_states=None,
Expand Down Expand Up @@ -1486,6 +1487,13 @@ def _dynamics(
energy of the constrained degrees of freedom are not included
in the total energy, and their forces are not evaluated.

integrator: str
The type of integrator to use, e.g. `langevin`, `verlet` etc.
See https://sire.openbiosim.org/cheatsheet/openmm.html#choosing-options
for the full list of options. This will be automatically
set to `langevin_middle` (NVT/NPT) or `verlet` (NVE) depending
on the ensemble if this is not set (or is set to `auto`)

schedule: sire.cas.LambdaSchedule
The schedule used to control how perturbable forcefield parameters
should be morphed as a function of lambda. If this is not set
Expand Down Expand Up @@ -1613,14 +1621,7 @@ def _dynamics(
cutoff = 7.5 * angstrom

if cutoff_type is None and not map.specified("cutoff_type"):
try:
if view.property(map["space"]).is_periodic():
cutoff_type = "PME"
else:
cutoff_type = "RF"
except Exception:
# no space, use RF
cutoff_type = "RF"
cutoff_type = "auto"

if timestep is None and not map.specified("timestep"):
from ..units import femtosecond
Expand All @@ -1646,27 +1647,7 @@ def _dynamics(
map.set("save_velocities", save_velocities)

if constraint is None and not map.specified("constraint"):
from ..units import femtosecond

if timestep is None:
# it must be in the map
timestep = map["timestep"].value()

if timestep > 4 * femtosecond:
# need constraint on everything
constraint = "bonds-h-angles"

elif timestep > 2 * femtosecond:
# need constraint on everything
constraint = "h-bonds-h-angles"

elif timestep > 1 * femtosecond:
# need it just on H bonds and angles
constraint = "h-bonds"

else:
# can get away with no constraints
constraint = "none"
constraint = "auto"

if perturbable_constraint is not None:
perturbable_constraint = str(perturbable_constraint).lower()
Expand All @@ -1691,6 +1672,9 @@ def _dynamics(
if platform is not None:
map.set("platform", str(platform))

if integrator is not None:
map.set("integrator", str(integrator))

return Dynamics(
view,
cutoff=cutoff,
Expand Down Expand Up @@ -1878,14 +1862,7 @@ def _minimisation(
cutoff = 7.5 * angstrom

if cutoff_type is None and not map.specified("cutoff_type"):
try:
if view.property(map["space"]).is_periodic():
cutoff_type = "PME"
else:
cutoff_type = "RF"
except Exception:
# no space, use RF
cutoff_type = "RF"
cutoff_type = "auto"

if device is not None:
map.set("device", str(device))
Expand Down
22 changes: 14 additions & 8 deletions src/sire/mol/_dynamics.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,19 +381,13 @@ def constraint(self):
if self.is_null():
return None
else:
if self._map.specified("constraint"):
return self._map["constraint"].source()
else:
return "none"
return self._omm_mols.get_constraint()

def perturbable_constraint(self):
if self.is_null():
return None
else:
if self._map.specified("perturbable_constraint"):
return self._map["perturbable_constraint"].source()
else:
return self.constraint()
return self._omm_mols.get_perturbable_constraint()

def get_schedule(self):
if self.is_null():
Expand Down Expand Up @@ -427,6 +421,12 @@ def set_lambda(self, lambda_value: float):
self._omm_mols.set_lambda(lambda_value)
self._clear_state()

def integrator(self):
if self.is_null():
return None
else:
return self._omm_mols.getIntegrator()

def info(self):
if self.is_null():
return None
Expand Down Expand Up @@ -1198,6 +1198,12 @@ def perturbable_constraint(self):
"""
return self._d.perturbable_constraint()

def integrator(self):
"""
Return the integrator that is used to run dynamics
"""
return self._d.integrator()

def info(self):
"""
Return the information that describes the forcefield that will
Expand Down
15 changes: 15 additions & 0 deletions src/sire/options/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
########################################
#
# sire.options
#
########################################

# Add your script to this list
set ( SCRIPTS
__init__.py
_option.py
_dynamics_options.py
)

# installation
install( FILES ${SCRIPTS} DESTINATION ${SIRE_PYTHON}/sire/options )
18 changes: 18 additions & 0 deletions src/sire/options/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
__all__ = [
"Option",
"Integrator",
"Constraint",
"PerturbableConstraint",
"Cutoff",
"Platform",
]

from ._option import Option

from ._dynamics_options import (
Integrator,
Constraint,
Cutoff,
PerturbableConstraint,
Platform,
)
128 changes: 128 additions & 0 deletions src/sire/options/_dynamics_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
__all__ = [
"Integrator",
"Constraint",
"PerturbableConstraint",
"Cutoff",
"Platform",
]

from ._option import Option as _Option


class Integrator(_Option):
"""
All of the supported options for the integrator
"""

AUTO = "auto", "Choose the integrator automatically"
VERLET = "verlet", "Use the Verlet integrator"
LEAPFROG = "leapfrog", "Use the Leapfrog integrator"
LANGEVIN = "langevin", "Use the Langevin integrator"
LANGEVIN_MIDDLE = (
"langevin_middle",
"Use the middle scheme Langevin integrator",
)
NOSE_HOOVER = "nose_hoover", "Use the Nose-Hoover integrator"
BROWNIAN = "brownian", "Use the Brownian integrator"
ANDERSEN = (
"andersen",
"Use the Verlet integrator with an Andersen thermostat",
)

@staticmethod
def create(option: str):
return _Option._create(Integrator, option)

@staticmethod
def options(include_docs: bool = False):
return _Option._options(Integrator, include_docs=include_docs)


class Constraint(_Option):
"""
All of the supported constraint options
"""

NONE = "none", "Do not use constraints"
AUTO = "auto", "Choose the constraints automatically"
HBONDS = "h_bonds", "Constrain bonds involving hydrogens"
BONDS = "bonds", "Constrain all bonds"
HBONDS_HANGLES = (
"h_bonds_h_angles",
"Constrain bonds and angles involving hydrogens",
)
BOND_HANGLES = (
"bonds_h_angles",
"Constrain all bonds, and angles involving hydrogens",
)

@staticmethod
def create(option: str):
return _Option._create(Constraint, option)

@staticmethod
def options(include_docs: bool = False):
return _Option._options(Constraint, include_docs=include_docs)


PerturbableConstraint = Constraint


class Cutoff(_Option):
"""
All of the support cutoff options
"""

NONE = "none", "Do not use a cutoff"
AUTO = "auto", "Choose the cutoff automatically"
RF = "rf", "Use a reaction field cutoff"
PME = "pme", "Use a Particle Mesh Ewald cutoff"
EWALD = "ewald", "Use an Ewald cutoff"

@staticmethod
def canonicalise(option: str):
"""
Convert the passed option string to the canonical form
"""
option = _Option.canonicalise(option)

if option == "reaction_field" or option == "reaction field":
return "rf"
elif (
option == "particle_mesh_ewald" or option == "particle mesh ewald"
):
return "pme"
elif option == "no_cutoff" or option == "no cutoff":
return "none"
else:
return option

@staticmethod
def create(option: str):
return _Option._create(Cutoff, option)

@staticmethod
def options(include_docs: bool = False):
return _Option._options(Cutoff, include_docs=include_docs)


class Platform(_Option):
"""
All of the supported platforms
"""

AUTO = "auto", "Choose the platform automatically"
CPU = "cpu", "Run on the CPU"
CUDA = "cuda", "Run on the GPU using CUDA (nVidia)"
OPENCL = "opencl", "Run on the GPU using OpenCL (all GPUs)"
METAL = "metal", "Run on the GPU using Metal (Apple)"
HIP = "hip", "Run on the GPU using HIP (AMD)"
REFERENCE = "reference", "Run on CPU using the reference implementation"

@staticmethod
def create(option: str):
return _Option._create(Platform, option)

@staticmethod
def options(include_docs: bool = False):
return _Option._options(Platform, include_docs=include_docs)
Loading