Coverage for openxps/couplings/collective_variable_coupling.py: 100%

32 statements  

« prev     ^ index     » next       coverage.py v7.11.3, created at 2025-11-13 22:08 +0000

1""" 

2Collective variable coupling. 

3 

4.. module:: openxps.couplings.collective_variable_coupling 

5 :platform: Linux, MacOS, Windows 

6 :synopsis: Coupling between dynamical variables and physical collective variables 

7 

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

9 

10""" 

11 

12import typing as t 

13 

14import cvpack 

15import openmm as mm 

16from openmm import _openmm as mmswig 

17from openmm import unit as mmunit 

18 

19from openxps.utils import preprocess_args 

20 

21from ..dynamical_variable import DynamicalVariable 

22from .base import Coupling 

23 

24 

25class CollectiveVariableCoupling(Coupling): 

26 """Coupling between dynamical variables and physical collective variables. 

27 

28 This class uses a :CVPack:`MetaCollectiveVariable` to create a coupling defined by 

29 a mathematical expression involving physical collective variables and parameters. 

30 

31 Parameters 

32 ---------- 

33 function 

34 A mathematical expression that defines the coupling energy as a function of 

35 collective variables and parameters. 

36 collective_variables 

37 The physical collective variables used in the coupling function. 

38 dynamical_variables 

39 The extended dynamical variables used in the coupling function. 

40 **parameters 

41 Named parameters that appear in the mathematical expression. 

42 

43 Examples 

44 -------- 

45 >>> from copy import copy 

46 >>> import cvpack 

47 >>> import openxps as xps 

48 >>> from openmm import unit 

49 >>> from math import pi 

50 >>> phi = cvpack.Torsion(6, 8, 14, 16, name="phi") 

51 >>> mass = 3 * unit.dalton * (unit.nanometer / unit.radian)**2 

52 >>> phi0 = xps.DynamicalVariable("phi0", unit.radian, mass, xps.CircularBounds()) 

53 >>> xps.CollectiveVariableCoupling( 

54 ... f"0.5*kappa*min(delta,{2*pi}-delta)^2; delta=abs(phi-phi0)", 

55 ... [phi], 

56 ... [phi0], 

57 ... kappa=1000 * unit.kilojoules_per_mole / unit.radian**2, 

58 ... ) 

59 CollectiveVariableCoupling("0.5*kappa*min(delta,6.28...-delta)^2; ...") 

60 """ 

61 

62 @preprocess_args 

63 def __init__( 

64 self, 

65 function: str, 

66 collective_variables: t.Iterable[cvpack.CollectiveVariable], 

67 dynamical_variables: t.Iterable[DynamicalVariable], 

68 **parameters: mmunit.Quantity, 

69 ) -> None: 

70 dv_names = {dv.name for dv in dynamical_variables} 

71 filtered_params = {k: v for k, v in parameters.items() if k not in dv_names} 

72 

73 force = cvpack.MetaCollectiveVariable( 

74 function, 

75 collective_variables, 

76 unit=mmunit.kilojoule_per_mole, 

77 name="coupling", 

78 **filtered_params, 

79 **{dv.name: 0.0 * dv.unit for dv in dynamical_variables}, 

80 ) 

81 super().__init__([force], dynamical_variables) 

82 

83 def __repr__(self) -> str: 

84 return f'{self.__class__.__name__}("{self._forces[0].getEnergyFunction()}")' 

85 

86 def _createFlippedForce(self) -> mm.CustomCVForce: 

87 force = self._forces[0] 

88 parameters = force.getParameterDefaultValues() 

89 dvs_to_flip = [dv for dv in self._dynamical_variables if dv.name in parameters] 

90 for dv in dvs_to_flip: 

91 parameters.pop(dv.name) 

92 parameters.update( 

93 {cv.getName(): 0.0 * cv.getUnit() for cv in force.getInnerVariables()} 

94 ) 

95 return cvpack.MetaCollectiveVariable( 

96 force.getEnergyFunction(), 

97 [ 

98 dv.createCollectiveVariable(self._dv_indices[dv.name]) 

99 for dv in dvs_to_flip 

100 ], 

101 unit=mmunit.kilojoule_per_mole, 

102 name="coupling", 

103 **parameters, 

104 ) 

105 

106 def updateExtensionContext( 

107 self, 

108 physical_context: mm.Context, 

109 extension_context: mm.Context, 

110 ) -> None: 

111 force = self._forces[0] 

112 collective_variables = mmswig.CustomCVForce_getCollectiveVariableValues( 

113 force, physical_context 

114 ) 

115 for index, value in enumerate(collective_variables): 

116 name = mmswig.CustomCVForce_getCollectiveVariableName(force, index) 

117 mmswig.Context_setParameter(extension_context, name, value) 

118 

119 

120CollectiveVariableCoupling.registerTag("!openxps.CollectiveVariableCoupling")