@@ -128,9 +128,9 @@ def _registerCV(
128
128
"""
129
129
cls = self .__class__
130
130
self .setName (cls .__name__ )
131
- self .setUnit ( unit )
131
+ self ._unit = unit
132
132
self ._mass_unit = mmunit .dalton * (mmunit .nanometers / self .getUnit ()) ** 2
133
- arguments , _ = self .getArguments ()
133
+ arguments , _ = self ._getArguments ()
134
134
self ._args = dict (zip (arguments , args ))
135
135
self ._args .update (kwargs )
136
136
@@ -149,21 +149,22 @@ def _registerPeriod(self, period: float) -> None:
149
149
self ._period = period
150
150
151
151
@classmethod
152
- def getArguments (cls ) -> t .Tuple [collections .OrderedDict , collections .OrderedDict ]:
152
+ def _getArguments (cls ) -> t .Tuple [collections .OrderedDict , collections .OrderedDict ]:
153
153
"""
154
154
Inspect the arguments needed for constructing an instance of this collective
155
155
variable.
156
156
157
157
Returns
158
158
-------
159
+ OrderedDict
159
160
A dictionary with the type annotations of all arguments
160
-
161
+ OrderedDict
161
162
A dictionary with the default values of optional arguments
162
163
163
164
Example
164
165
-------
165
166
>>> import cvpack
166
- >>> args, defaults = cvpack.RadiusOfGyration.getArguments ()
167
+ >>> args, defaults = cvpack.RadiusOfGyration._getArguments ()
167
168
>>> for name, annotation in args.items():
168
169
... print(f"{name}: {annotation}")
169
170
group: typing.Iterable[int]
@@ -180,16 +181,32 @@ def getArguments(cls) -> t.Tuple[collections.OrderedDict, collections.OrderedDic
180
181
defaults [name ] = parameter .default
181
182
return arguments , defaults
182
183
183
- def setUnit (self , unit : mmunit . Unit ) -> None :
184
+ def _setUnusedForceGroup (self , system : openmm . System ) -> None :
184
185
"""
185
- Set the unit of measurement of this collective variable.
186
+ Set the force group of this collective variable to the one at a given position
187
+ in the ascending ordered list of unused force groups in an :OpenMM:`System`.
188
+
189
+ .. note::
190
+
191
+ Evaluating a collective variable (see :meth:`getValue`) or computing its
192
+ effective mass (see :meth:`getEffectiveMass`) is more efficient when the
193
+ collective variable is the only force in its own force group.
186
194
187
195
Parameters
188
196
----------
189
- unit
190
- The unit of measurement of this collective variable
197
+ system
198
+ The system to search for unused force groups
199
+
200
+ Raises
201
+ ------
202
+ RuntimeError
203
+ If all force groups are already in use
191
204
"""
192
- self ._unit = unit
205
+ used_groups = {force .getForceGroup () for force in system .getForces ()}
206
+ new_group = next (filter (lambda i : i not in used_groups , range (32 )), None )
207
+ if new_group is None :
208
+ raise RuntimeError ("All force groups are already in use." )
209
+ self .setForceGroup (new_group )
193
210
194
211
def getUnit (self ) -> mmunit .Unit :
195
212
"""
@@ -209,42 +226,23 @@ def getPeriod(self) -> t.Optional[mmunit.SerializableQuantity]:
209
226
return None
210
227
return mmunit .SerializableQuantity (self ._period , self .getUnit ())
211
228
212
- def setUnusedForceGroup (self , position : int , system : openmm .System ) -> int :
229
+ def addToSystem (
230
+ self , system : openmm .System , setUnusedForceGroup : bool = True
231
+ ) -> None :
213
232
"""
214
- Set the force group of this collective variable to the one at a given position
215
- in the ascending ordered list of unused force groups in an :OpenMM:`System`.
216
-
217
- .. note::
218
-
219
- Evaluating a collective variable (see :meth:`getValue`) or computing its
220
- effective mass (see :meth:`getEffectiveMass`) is more efficient when the
221
- collective variable is the only force in its own force group.
233
+ Add this collective variable to an :OpenMM:`System`.
222
234
223
235
Parameters
224
236
----------
225
- position
226
- The position of the force group in the ascending ordered list of unused
227
- force groups in the system
228
- system
229
- The system to search for unused force groups
230
-
231
- Returns
232
- -------
233
- The index of the force group that was set
234
-
235
- Raises
236
- ------
237
- RuntimeError
238
- If all force groups are already in use
237
+ system
238
+ The system to which this collective variable should be added
239
+ setUnusedForceGroup
240
+ If True, the force group of this collective variable will be set to the
241
+ first available force group in the system
239
242
"""
240
- free_groups = sorted (
241
- set (range (32 )) - {force .getForceGroup () for force in system .getForces ()}
242
- )
243
- if not free_groups :
244
- raise RuntimeError ("All force groups are already in use." )
245
- new_group = free_groups [position ]
246
- self .setForceGroup (new_group )
247
- return new_group
243
+ if setUnusedForceGroup :
244
+ self ._setUnusedForceGroup (system )
245
+ system .addForce (self )
248
246
249
247
def getValue (self , context : openmm .Context ) -> mmunit .Quantity :
250
248
"""
@@ -257,12 +255,43 @@ def getValue(self, context: openmm.Context) -> mmunit.Quantity:
257
255
258
256
Parameters
259
257
----------
260
- context
261
- The context at which this collective variable should be evaluated
258
+ context
259
+ The context at which this collective variable should be evaluated
262
260
263
261
Returns
264
262
-------
263
+ unit.Quantity
265
264
The value of this collective variable at the given context
265
+
266
+
267
+ Example
268
+ -------
269
+ In this example, we compute the values of the backbone dihedral angles and
270
+ the radius of gyration of an alanine dipeptide molecule in water:
271
+
272
+ >>> import cvpack
273
+ >>> import openmm
274
+ >>> from openmmtools import testsystems
275
+ >>> model = testsystems.AlanineDipeptideExplicit()
276
+ >>> top = model.mdtraj_topology
277
+ >>> backbone_atoms = top.select("name N C CA and resid 1 2")
278
+ >>> phi = cvpack.Torsion(*backbone_atoms[0:4])
279
+ >>> psi = cvpack.Torsion(*backbone_atoms[1:5])
280
+ >>> radius_of_gyration = cvpack.RadiusOfGyration(
281
+ ... top.select('not water')
282
+ ... )
283
+ >>> for cv in [phi, psi, radius_of_gyration]:
284
+ ... cv.addToSystem(model.system)
285
+ >>> context = openmm.Context(
286
+ ... model.system, openmm.VerletIntegrator(0)
287
+ ... )
288
+ >>> context.setPositions(model.positions)
289
+ >>> print(phi.getValue(context))
290
+ 3.1415... rad
291
+ >>> print(psi.getValue(context))
292
+ 3.1415... rad
293
+ >>> print(radius_of_gyration.getValue(context))
294
+ 0.29514... nm
266
295
"""
267
296
state = get_single_force_state (self , context , getEnergy = True )
268
297
value = value_in_md_units (state .getPotentialEnergy ())
@@ -291,36 +320,41 @@ def getEffectiveMass(self, context: openmm.Context) -> mmunit.Quantity:
291
320
292
321
Parameters
293
322
----------
294
- context
295
- The context at which this collective variable's effective mass should be
296
- evaluated
323
+ context
324
+ The context at which this collective variable's effective mass should be
325
+ evaluated
297
326
298
327
Returns
299
328
-------
329
+ unit.Quantity
300
330
The effective mass of this collective variable at the given context
301
331
302
332
Example
303
333
-------
334
+ In this example, we compute the effective masses of the backbone dihedral
335
+ angles and the radius of gyration of an alanine dipeptide molecule in water:
336
+
304
337
>>> import cvpack
305
338
>>> import openmm
306
339
>>> from openmmtools import testsystems
307
- >>> model = testsystems.AlanineDipeptideImplicit()
308
- >>> peptide = [
309
- ... a.index
310
- ... for a in model.topology.atoms()
311
- ... if a.residue.name != 'HOH'
312
- ... ]
313
- >>> radius_of_gyration = cvpack.RadiusOfGyration(peptide)
314
- >>> radius_of_gyration.setForceGroup(1)
315
- >>> radius_of_gyration.setUnusedForceGroup(0, model.system)
316
- 1
317
- >>> model.system.addForce(radius_of_gyration)
318
- 6
319
- >>> platform = openmm.Platform.getPlatformByName('Reference')
340
+ >>> model = testsystems.AlanineDipeptideExplicit()
341
+ >>> top = model.mdtraj_topology
342
+ >>> backbone_atoms = top.select("name N C CA and resid 1 2")
343
+ >>> phi = cvpack.Torsion(*backbone_atoms[0:4])
344
+ >>> psi = cvpack.Torsion(*backbone_atoms[1:5])
345
+ >>> radius_of_gyration = cvpack.RadiusOfGyration(
346
+ ... top.select('not water')
347
+ ... )
348
+ >>> for cv in [phi, psi, radius_of_gyration]:
349
+ ... cv.addToSystem(model.system)
320
350
>>> context = openmm.Context(
321
- ... model.system,openmm.VerletIntegrator(0), platform
351
+ ... model.system, openmm.VerletIntegrator(0)
322
352
... )
323
353
>>> context.setPositions(model.positions)
354
+ >>> print(phi.getEffectiveMass(context))
355
+ 0.05119... nm**2 Da/(rad**2)
356
+ >>> print(psi.getEffectiveMass(context))
357
+ 0.05186... nm**2 Da/(rad**2)
324
358
>>> print(radius_of_gyration.getEffectiveMass(context))
325
359
30.946... Da
326
360
"""
0 commit comments