OpenMPCD
EckartSystem.py
1 # coding=utf-8
2 
3 class EckartSystem:
4  """
5  Represents an Eckart system.
6 
7  The Eckart system is defined as in the article
8  "Eckart vectors, Eckart frames, and polyatomic molecules"
9  by James D. Louck and Harold W. Galbraith,
10  Reviews of Modern Physics, January 1976, Vol. 48, No. 1, pp. 69-106,
11  DOI: 10.1103/RevModPhys.48.69
12  """
13 
14  def __init__(self, referenceConfiguration):
15  """
16  The constructor.
17 
18  @throw TypeError
19  Throws if `referenceConfiguration` is not of type
20  `ParticleCollection`.
21  @throw ValueError
22  Throws if `referenceConfiguration` does not have masse set for
23  all its particles.
24 
25  @param[in] referenceConfiguration
26  The configuration of particles to use as a constant reference
27  for the remainder of this instance's existence.
28  """
29 
30  from .ParticleCollection import ParticleCollection
31 
32  if not isinstance(referenceConfiguration, ParticleCollection):
33  raise TypeError()
34 
35  for i in range(0, referenceConfiguration.getParticleCount()):
36  try:
37  referenceConfiguration.getMass(i)
38  except RuntimeError:
39  raise ValueError
40 
41  import copy
42  self._referenceConfiguration = copy.deepcopy(referenceConfiguration)
43  self._referenceConfiguration.shiftToCenterOfMassFrame()
44 
45 
46  def getParticleCount(self):
47  """
48  Returns the number of particles in the Eckart system.
49  """
50 
52 
53 
54  def getReferencePosition(self, i):
55  """
56  Returns the reference position vector \f$ \vec{a}_i \f$ for the particle
57  with the index `i`.
58 
59  @throw KeyError
60  Throws if `i` is negative, or equal to or greater than the
61  number of `getParticleCount()`.
62  @throw TypeError
63  Throws if `i` is not of type `int`.
64 
65  @param[in] i
66  The particle index to query, which must be an `int` in the
67  range `[0, getParticleCount() - 1]`.
68 
69  @return Returns an instance of `Vector3DReal`.
70  """
71 
72  if not isinstance(i, int):
73  raise TypeError()
74 
75  if i < 0 or i >= self.getParticleCount():
76  raise KeyError()
77 
78  return self._referenceConfiguration.getPosition(i)
79 
80 
81  def getEckartVectors(self, instantaneousConfiguration):
82  """
83  Returns the Eckart vectors \f$ \vec{F}_1, \vec{F}_2, \vec{F}_3 \f$
84  defined by
85  \f[
86  \vec{F}_i = \sum_{\alpha = 1}^N m_\alpha a_i^\alpha \vec{r}^\alpha
87  \f]
88  where \f$ N \f$ is the number of particles, \f$ m_\alpha \f$ is the mass
89  of the particle with index \f$ \alpha \f$, \f$ \vec{r}^\alpha \f$ is its
90  instantaneous position as specified in `instantaneousConfiguration`, and
91  \f$ a_i^\alpha \f$ is the \f$ i \f$-th component of the position vector
92  of the \f$ \alpha \f$-th particle in the center-of-mass-frame of the
93  reference configuration.
94 
95  @throw TypeError
96  Throws if `instantaneousConfiguration` is not an instance of
97  `ParticleCollection`.
98  @throw ValueError
99  Throws if `instantaneousConfiguration` is incompatible with the
100  reference configuration, i.e. if the number of particles, or
101  their masses, mismatch.
102 
103  @param[in] instantaneousConfiguration
104  An instance of `ParticleCollection` containing the current
105  particle positions.
106 
107  @return Returns a list containing
108  \f$ \vec{F}_1, \vec{F}_2, \vec{F}_3 \f$, in that order, as
109  instances of `Vector3DReal`.
110  """
111 
112  from .ParticleCollection import ParticleCollection
113  from .Vector3DReal import Vector3DReal
114 
115  if not isinstance(instantaneousConfiguration, ParticleCollection):
116  raise TypeError()
117 
118  particleCount = instantaneousConfiguration.getParticleCount()
119  if particleCount != self._referenceConfiguration.getParticleCount():
120  raise ValueError()
121 
122 
123  ret = [Vector3DReal(0, 0, 0) for _ in range(0, 3)]
124  for i in range(0, particleCount):
125  mass = instantaneousConfiguration.getMass(i)
126  if mass != self._referenceConfiguration.getMass(i):
127  raise ValueError()
128 
129  instantaneousPosition = instantaneousConfiguration.getPosition(i)
130  referencePosition = self._referenceConfiguration.getPosition(i)
131  for coord in range(0, 3):
132  factor = mass * referencePosition[coord]
133  ret[coord] += instantaneousPosition * factor
134 
135 
136  return ret
137 
138 
139  def getGramMatrix(
140  self, instantaneousConfiguration,
141  _eckartVectors = None):
142  """
143  Returns the Gram matrix \f$ F \f$ with entries
144  \f$ F_{ij} = \vec{F}_i \cdot \vec{F}_j \f$, \f$ \vec{F}_i \f$ being the
145  vectors as returned by `getEckartVectors()`.
146 
147  @throw TypeError
148  Throws if `instantaneousConfiguration` is not an instance of
149  `ParticleCollection`.
150  @throw ValueError
151  Throws if `instantaneousConfiguration` is incompatible with the
152  reference configuration, i.e. if the number of particles, or
153  their masses, mismatch.
154 
155  @param[in] instantaneousConfiguration
156  An instance of `ParticleCollection` containing the current
157  particle positions.
158  @param[in] _eckartVectors
159  If not `None`, the given argument will be used as if returned
160  by `getEckartVectors` with the given
161  `instantaneousConfiguration`, and the latter's validity is
162  not checked.
163  This argument is meant to be used only from other functions
164  of this class.
165 
166  @return Returns a `3 x 3` matrix, in the form of a list of three lists
167  consisting of three `float` instances each.
168  """
169 
170  if _eckartVectors is None:
171  eckartVectors = self.getEckartVectors(instantaneousConfiguration)
172  else:
173  eckartVectors = _eckartVectors
174 
175  ret = [[0, 0, 0] for _ in range(0, 3)]
176  for i in range(0, 3):
177  for j in range(i, 3):
178  ret[i][j] = eckartVectors[i].dot(eckartVectors[j])
179 
180  ret[1][0] = ret[0][1]
181  ret[2][0] = ret[0][2]
182  ret[2][1] = ret[1][2]
183 
184  return ret
185 
186 
187  def getEckartFrame(self, instantaneousConfiguration):
188  """
189  Returns the Eckart frame vectors \f$ \hat{f}_1, \hat{f}_2, \hat{f}_3 \f$
190  defined by
191  \f[
192  \left[ \hat{f}_1, \hat{f}_2, \hat{f}_3 \right]
193  =
194  \left[ \vec{F}_1, \vec{F}_2, \vec{F}_3 \right] F^{-1/2}
195  \f]
196  where the \f$ \vec{F}_i \f$ are the Eckart vectors as returned by
197  `getEckartVectors`, and \f$ F \f$ is the Gram matrix as returned by
198  `getGramMatrix`.
199 
200  @throw TypeError
201  Throws if `instantaneousConfiguration` is not an instance of
202  `ParticleCollection`.
203  @throw ValueError
204  Throws if `instantaneousConfiguration` is incompatible with the
205  reference configuration, i.e. if the number of particles, or
206  their masses, mismatch.
207 
208  @param[in] instantaneousConfiguration
209  An instance of `ParticleCollection` containing the current
210  particle positions.
211 
212  @return Returns a list containing
213  \f$ \hat{f}_1, \hat{f}_2, \hat{f}_3 \f$, in that order, as
214  instances of `Vector3DReal`.
215  """
216 
217  ev = self.getEckartVectors(instantaneousConfiguration)
218 
219  left = []
220  left.append([ev[0][0], ev[1][0], ev[2][0]])
221  left.append([ev[0][1], ev[1][1], ev[2][1]])
222  left.append([ev[0][2], ev[1][2], ev[2][2]])
223 
224  right = \
226  instantaneousConfiguration,
227  _eckartVectors = ev)
228 
229  import numpy
230  result = numpy.dot(left, right)
231 
232  from .Vector3DReal import Vector3DReal
233  f1 = Vector3DReal(result[0][0], result[1][0], result[2][0])
234  f2 = Vector3DReal(result[0][1], result[1][1], result[2][1])
235  f3 = Vector3DReal(result[0][2], result[1][2], result[2][2])
236 
237  return [f1, f2, f3]
238 
239 
240  def getEckartFrameEquilibriumPositions(self, instantaneousConfiguration):
241  """
242  Returns the vectors \f$ \vec{c}^\alpha \f$,
243  \f$ \alpha \in \left[0, N\right] \f$,
244  in the laboratory frame coordinate system,
245  where the \f$ \vec{c} \f$ are defined by
246  \f[
247  \vec{c}^\alpha = \sum_{i = 1}^3 a_i^\alpha \hat{f}_i
248  \f]
249  where \f$ N \f$ is the number of particles, \f$ a_i^\alpha \f$ is the
250  \f$ i \f$-th component of the position vector of the \f$ \alpha \f$-th
251  particle in the center-of-mass-frame of the reference configuration,
252  and the \f$ \hat{f}_i \f$ are the Eckart frame vectors, as returned by
253  `getEckartFrame(instantaneousConfiguration)`.
254 
255  @throw TypeError
256  Throws if `instantaneousConfiguration` is not an instance of
257  `ParticleCollection`.
258  @throw ValueError
259  Throws if `instantaneousConfiguration` is incompatible with the
260  reference configuration, i.e. if the number of particles, or
261  their masses, mismatch.
262 
263  @param[in] instantaneousConfiguration
264  An instance of `ParticleCollection` containing the current
265  particle positions.
266 
267  @return Returns a list containing
268  \f$ \vec{c}^1, \vec{c}^2, \ldots, \vec{c}^N \f$, in that order,
269  as instances of `Vector3DReal`.
270  """
271 
272  result = []
273  eckartFrame = self.getEckartFrame(instantaneousConfiguration)
274 
275  from .Vector3DReal import Vector3DReal
276  for alpha in range(0, self.getParticleCount()):
277  v = Vector3DReal(0, 0, 0)
278  a = self.getReferencePosition(alpha)
279  for i in range(0, 3):
280  v += eckartFrame[i] * a[i]
281  result.append(v)
282 
283  return result
284 
285 
287  self, instantaneousConfiguration,
288  _eckartFrameEquilibriumPositions = None):
289  """
290  Returns the Eckart moment of inertia tensor \f$ J \f$ in the laboratory
291  frame coordinate system, defined as
292  \f[
293  J =
294  \sum_{\alpha = 1}^N
295  \left(
296  m_\alpha
297  \left(
298  \left( \vec{r}_\alpha - \vec{r}_{cm} \right)
299  \cdot \vec{c}_\alpha
300  \right)
301  I
302  -
303  \left( \vec{r}_\alpha - \vec{r}_{cm} \right)
304  \otimes \vec{c}_\alpha
305  \right)
306  \f]
307  where \f$ N \f$ is the number of particles, \f$ I \f$ is the
308  \f$ 3 \times 3 \f$ unit matrix, \f$ \otimes \f$ denotes the outer
309  product, \f$ \vec{r}_\alpha \f$ is the instantaneous position of the
310  \f$ \alpha \f$-th particle, \f$ \vec{r}_{cm} \f$ is the instantaneous
311  position of the center of mass of the particles, \f$ \vec{c}_\alpha \f$
312  are the Eckart frame equilibrium positions as returned by
313  `getEckartFrameEquilibriumPositions(instantaneousConfiguration)`,
314  and \f$ m_\alpha \f$ is the mass of the \f$ \alpha \f$-th particle.
315 
316  This quantity is defined as in equation (15) in the article
317  "Application of the Eckart frame to soft matter: rotation of star
318  polymers under shear flow"
319  by Jurij Sablić, Rafael Delgado-Buscalioni, and Matej Praprotnik,
320  arXiv:1707.09170v1 [cond-mat.soft]
321 
322  @throw TypeError
323  Throws if `instantaneousConfiguration` is not an instance of
324  `ParticleCollection`.
325  @throw ValueError
326  Throws if `instantaneousConfiguration` is incompatible with the
327  reference configuration, i.e. if the number of particles, or
328  their masses, mismatch.
329 
330  @param[in] instantaneousConfiguration
331  An instance of `ParticleCollection` containing the current
332  particle positions.
333  @param[in] _eckartFrameEquilibriumPositions
334  If not `None`, the given argument will be used as if returned
335  by `getEckartFrameEquilibriumPositions` with the given
336  `instantaneousConfiguration`, and the latter's validity is
337  not checked.
338  This argument is meant to be used only from other functions
339  of this class.
340 
341  @return Returns an instance of `numpy.ndarray` with shape `(3, 3)`.
342  """
343 
344  if _eckartFrameEquilibriumPositions is not None:
345  cs = _eckartFrameEquilibriumPositions
346  else:
347  cs = \
349  instantaneousConfiguration)
350 
351  import numpy
352  J = numpy.zeros((3, 3))
353 
354  centerOfMass = instantaneousConfiguration.getCenterOfMass()
355  unitMatrix = numpy.identity(3)
356  for alpha in range(0, self.getParticleCount()):
357  m = instantaneousConfiguration.getMass(alpha)
358  R = instantaneousConfiguration.getPosition(alpha) - centerOfMass
359  c = cs[alpha]
360 
361  outerProduct = numpy.zeros((3, 3))
362  for i in range(0, 3):
363  for j in range(0, 3):
364  outerProduct[i][j] = R[i] * c[j]
365 
366  current = numpy.dot(unitMatrix, R.dot(c))
367  current -= outerProduct
368  J += numpy.dot(current, m)
369 
370  return J
371 
372 
373  def getEckartAngularVelocityVector(self, instantaneousConfiguration):
374  """
375  Returns the Eckart angular velocity vector \f$ \vec{\Omega} \f$ in the
376  laboratory frame coordinate system, defined as
377  \f[
378  \vec{\Omega} =
379  J^{-1}
380  \sum_{\alpha = 1}^N
381  m_\alpha
382  \vec{c}_\alpha
383  \times
384  \left( \vec{v}_\alpha - \vec{v}_{cm} \right)
385  \f]
386  where \f$ N \f$ is the number of particles, \f$ J \f$ is the
387  Eckart moment of inertia tensor, as returned by
388  `getEckartMomentOfInertiaTensor(instantaneousConfiguration)`,
389  \f$ \times \f$ denotes the cross
390  product, \f$ \vec{v}_\alpha \f$ is the instantaneous velocity of the
391  \f$ \alpha \f$-th particle, \f$ \vec{v}_{cm} \f$ is the instantaneous
392  velocity of the center of mass of the particles, \f$ \vec{c}_\alpha \f$
393  are the Eckart frame equilibrium positions as returned by
394  `getEckartFrameEquilibriumPositions(instantaneousConfiguration)`,
395  and \f$ m_\alpha \f$ is the mass of the \f$ \alpha \f$-th particle.
396 
397  This quantity is defined as in equation (14) in the article
398  "Application of the Eckart frame to soft matter: rotation of star
399  polymers under shear flow"
400  by Jurij Sablić, Rafael Delgado-Buscalioni, and Matej Praprotnik,
401  arXiv:1707.09170v1 [cond-mat.soft]
402 
403  @throw TypeError
404  Throws if `instantaneousConfiguration` is not an instance of
405  `ParticleCollection`.
406  @throw ValueError
407  Throws if `instantaneousConfiguration` is incompatible with the
408  reference configuration, i.e. if the number of particles, or
409  their masses, mismatch.
410 
411  @param[in] instantaneousConfiguration
412  An instance of `ParticleCollection` containing the current
413  particle positions.
414 
415  @return Returns an instance of `Vector3DReal`.
416  """
417 
418  cs = self.getEckartFrameEquilibriumPositions(instantaneousConfiguration)
419  J = \
421  instantaneousConfiguration,
422  _eckartFrameEquilibriumPositions = cs)
423  comVelocity = instantaneousConfiguration.getCenterOfMassVelocity()
424 
425  from .Vector3DReal import Vector3DReal
426  rhs = Vector3DReal(0, 0, 0)
427 
428  for alpha in range(0, self.getParticleCount()):
429  relativeVelocity = \
430  instantaneousConfiguration.getVelocity(alpha) - comVelocity
431 
432  current = cs[alpha].cross(relativeVelocity)
433  rhs += current * instantaneousConfiguration.getMass(alpha)
434 
435  rhs = [rhs[i] for i in range(0, 3)]
436 
437  import numpy
438  import scipy.linalg
439  result = numpy.dot(scipy.linalg.inv(J, overwrite_a = True), rhs)
440 
441  return Vector3DReal(result[0], result[1], result[2])
442 
443 
444  def _getGramMatrixInverseSquareRoot(
445  self, instantaneousConfiguration,
446  _eckartVectors = None):
447  """
448  Returns \f$ F^{-1/2} \f$, where \f$ F \f$ is the Gram matrix as returned
449  by `getGramMatrix()`.
450 
451  @throw TypeError
452  Throws if `instantaneousConfiguration` is not an instance of
453  `ParticleCollection`.
454  @throw ValueError
455  Throws if `instantaneousConfiguration` is incompatible with the
456  reference configuration, i.e. if the number of particles, or
457  their masses, mismatch.
458 
459  @param[in] instantaneousConfiguration
460  An instance of `ParticleCollection` containing the current
461  particle positions.
462  @param[in] _eckartVectors
463  If not `None`, the given argument will be used as if returned
464  by `getEckartVectors` with the given
465  `instantaneousConfiguration`, and the latter's validity is
466  not checked.
467  This argument is meant to be used only from other functions
468  of this class.
469 
470  @return Returns an instance of `numpy.ndarray` with shape `(3, 3)`.
471  """
472 
473  gramMatrix = \
474  self.getGramMatrix(
475  instantaneousConfiguration,
476  _eckartVectors = _eckartVectors)
477 
478  import scipy.linalg
479  invertedGramMatrix = scipy.linalg.inv(gramMatrix, overwrite_a = True)
480  return scipy.linalg.sqrtm(invertedGramMatrix)
481 
482 
483 
MPCDAnalysis.EckartSystem.EckartSystem.getEckartAngularVelocityVector
def getEckartAngularVelocityVector(self, instantaneousConfiguration)
Definition: EckartSystem.py:426
MPCDAnalysis.EckartSystem.EckartSystem.__init__
def __init__(self, referenceConfiguration)
Definition: EckartSystem.py:30
MPCDAnalysis.EckartSystem.EckartSystem._referenceConfiguration
_referenceConfiguration
Definition: EckartSystem.py:44
MPCDAnalysis.EckartSystem.EckartSystem.getParticleCount
def getParticleCount(self)
Definition: EckartSystem.py:52
MPCDAnalysis.EckartSystem.EckartSystem.getEckartFrame
def getEckartFrame(self, instantaneousConfiguration)
Definition: EckartSystem.py:222
MPCDAnalysis.EckartSystem.EckartSystem.getEckartFrameEquilibriumPositions
def getEckartFrameEquilibriumPositions(self, instantaneousConfiguration)
Definition: EckartSystem.py:278
MPCDAnalysis.EckartSystem.EckartSystem.getEckartVectors
def getEckartVectors(self, instantaneousConfiguration)
Definition: EckartSystem.py:115
MPCDAnalysis.EckartSystem.EckartSystem.getEckartMomentOfInertiaTensor
def getEckartMomentOfInertiaTensor(self, instantaneousConfiguration, _eckartFrameEquilibriumPositions=None)
Definition: EckartSystem.py:349
MPCDAnalysis.EckartSystem.EckartSystem.getReferencePosition
def getReferencePosition(self, i)
Definition: EckartSystem.py:74
MPCDAnalysis.EckartSystem.EckartSystem.getGramMatrix
def getGramMatrix(self, instantaneousConfiguration, _eckartVectors=None)
Definition: EckartSystem.py:172
MPCDAnalysis.EckartSystem.EckartSystem._getGramMatrixInverseSquareRoot
def _getGramMatrixInverseSquareRoot(self, instantaneousConfiguration, _eckartVectors=None)
Definition: EckartSystem.py:480