Coverage for cvpack/openmm_force_wrapper.py: 100%

24 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-16 00:10 +0000

1""" 

2.. class:: OpenMMForceWrapper 

3 :platform: Linux, MacOS, Windows 

4 :synopsis: A collective variable built from the potential energy of an OpenMM force 

5 

6.. classauthor:: Charlles Abreu <craabreu@gmail.com> 

7 

8""" 

9 

10from __future__ import annotations 

11 

12import typing as t 

13 

14import openmm 

15from openmm import unit as mmunit 

16 

17from .collective_variable import CollectiveVariable 

18from .units import Unit, VectorQuantity 

19 

20 

21class OpenMMForceWrapper(CollectiveVariable, openmm.Force): 

22 r""" 

23 A collective variable whose numerical value is computed from the potential energy, 

24 in kJ/mol, of an OpenMM force object. 

25 

26 Parameters 

27 ---------- 

28 openmmForce 

29 The OpenMM force whose potential energy will be used to define the collective 

30 variable. It can be passed as an instance of :OpenMM:`Force` or as a string 

31 containing the XML serialization of the force. 

32 unit 

33 The unit of measurement of the collective variable. It must be compatible 

34 with the MD unit system (mass in `daltons`, distance in `nanometers`, time 

35 in `picoseconds`, temperature in `kelvin`, energy in `kilojoules_per_mol`, 

36 angle in `radians`). If the collective variable does not have a unit, use 

37 `dimensionless`. 

38 periodicBounds 

39 The periodic bounds of the collective variable if it is periodic, or `None` if 

40 it is not. 

41 name 

42 The name of the collective variable. 

43 

44 Example 

45 ------- 

46 >>> import cvpack 

47 >>> import numpy as np 

48 >>> import openmm 

49 >>> from openmm import unit 

50 >>> from openmmtools import testsystems 

51 >>> model = testsystems.AlanineDipeptideVacuum() 

52 >>> torsion = openmm.CustomTorsionForce("theta") 

53 >>> _ = torsion.addTorsion(4, 6, 8, 14) 

54 >>> cv = cvpack.OpenMMForceWrapper( 

55 ... torsion, 

56 ... unit.radian, 

57 ... periodicBounds=[-np.pi, np.pi] * unit.radian, 

58 ... ) 

59 >>> cv.addToSystem(model.system) 

60 >>> integrator = openmm.VerletIntegrator(0) 

61 >>> platform = openmm.Platform.getPlatformByName("Reference") 

62 >>> context = openmm.Context(model.system, integrator, platform) 

63 >>> context.setPositions(model.positions) 

64 >>> cv.getValue(context) 

65 -3.1415... rad 

66 >>> cv.getEffectiveMass(context) 

67 0.04795... nm**2 Da/(rad**2) 

68 """ 

69 

70 def __init__( 

71 self, 

72 openmmForce: openmm.Force | str, 

73 unit: mmunit.Unit, 

74 periodicBounds: VectorQuantity | None = None, 

75 name: str = "openmm_force_wrapper", 

76 ) -> None: 

77 if isinstance(openmmForce, openmm.Force): 

78 openmmForce = openmm.XmlSerializer.serialize(openmmForce) 

79 unit = Unit(unit) 

80 self._wrapped_force = openmm.XmlSerializer.deserialize(openmmForce) 

81 self.this = self._wrapped_force.this 

82 self._registerCV( 

83 name, 

84 unit, 

85 openmmForce=openmmForce, 

86 unit=unit, 

87 periodicBounds=periodicBounds, 

88 ) 

89 if periodicBounds is not None: 

90 self._registerPeriodicBounds(*periodicBounds) 

91 

92 def __getattr__(self, name: str) -> t.Any: 

93 attr = getattr(self._wrapped_force, name) 

94 if callable(attr): 

95 

96 def _wrapped_method(*args, **kwargs): 

97 return attr(*args, **kwargs) 

98 

99 return _wrapped_method 

100 

101 return attr 

102 

103 

104OpenMMForceWrapper.registerTag("!cvpack.OpenMMForceWrapper")