1 from .ParticleCollection
import ParticleCollection
5 Representation of a collection of star polymers, as described in
6 `OpenMPCD::CUDA::MPCSolute::StarPolymers`.
13 @throw NotImplementedError
14 Throws if more than one star is configured.
17 An instance of `Configuration` that contains the star polymer
18 configuration as its root element. A copy of this instance is
19 stored, rather than a reference to the given instance.
22 from .Configuration
import Configuration
23 if not isinstance(config, Configuration):
27 self.
_config = copy.deepcopy(config)
33 "getParticleType": {},
34 "getParticleStructureIndices": {},
35 "particlesAreBonded": {},
36 "getWCAPotential": {},
37 "getFENEPotential": {},
38 "getMagneticPotential":
None,
42 raise NotImplementedError(
"Unimplemented")
47 Returns the number of stars configured.
50 if "getStarCount" not in self.
_caches:
53 return self.
_caches[
"getStarCount"]
58 Returns the number of arms configured per star.
61 if "getArmCountPerStar" not in self.
_caches:
62 self.
_caches[
"getArmCountPerStar"] = \
65 return self.
_caches[
"getArmCountPerStar"]
70 Returns the number of arm particles configured per arm.
73 if "getArmParticlesPerArm" not in self.
_caches:
74 self.
_caches[
"getArmParticlesPerArm"] = \
75 self.
_config[
"structure.armParticlesPerArm"]
77 return self.
_caches[
"getArmParticlesPerArm"]
82 Returns the total number of particles per arm.
85 if "getTotalParticleCountPerArm" in self.
_caches:
86 return self.
_caches[
"getTotalParticleCountPerArm"]
92 self.
_caches[
"getTotalParticleCountPerArm"] = ret
98 Returns whether arms end in a magnetic particle.
101 if "hasMagneticParticles" not in self.
_caches:
102 self.
_caches[
"hasMagneticParticles"] = \
103 self.
_config[
"structure.hasMagneticParticles"]
105 return self.
_caches[
"hasMagneticParticles"]
110 Returns the mass of a particle.
113 if "getParticleMass" not in self.
_caches:
114 self.
_caches[
"getParticleMass"] = \
115 self.
_config[
"structure.particleMass"]
117 return self.
_caches[
"getParticleMass"]
122 Returns the total number of particles per star.
131 Returns the total number of particles in all stars.
134 if "getTotalParticleCount" in self.
_caches:
135 return self.
_caches[
"getTotalParticleCount"]
138 self.
_caches[
"getTotalParticleCount"] = ret
144 Returns the type of particle for the given particle index.
147 Throws if `index` is out of range.
149 Throws if `index` is not an integer.
152 The particle index, as an integer in the range
153 [0, `getTotalParticleCount()` - 1].
155 @return Returns "Core" for a core particle, "Arm" for an arm particle,
156 and "Magnetic" for a magnetic end particle.
159 if not isinstance(index, int):
163 if index
in self.
_caches[
"getParticleType"]:
164 return self.
_caches[
"getParticleType"][index]
170 originalIndex = index
174 self.
_caches[
"getParticleType"][originalIndex] =
"Core"
182 self.
_caches[
"getParticleType"][originalIndex] =
"Magnetic"
185 self.
_caches[
"getParticleType"][originalIndex] =
"Arm"
191 Returns the type of particle for the given particle index.
193 @warning The returned value is a reference to an internally cached
194 object. Do not modify!
197 Throws if `particleID` is out of range.
199 Throws if `particleID` is not an integer.
201 @param[in] particleID
202 The particle index, as an integer in the range
203 [0, `getTotalParticleCount()` - 1].
205 @return Returns a list of integers. The first integer corresponds to the
206 number of the star the given particle belongs to (ranging from
207 `0` to `getStarCount() - 1`). If the particle is a `Core`
208 particle, the following indices will be `None`; otherwise, the
209 next index corresponds to the arm the particle corresponds to
210 (in the range `0` to `getArmCountPerStar() - 1`). The last index
211 is `None` if the particle is a `Magnetic` particle, and
212 otherwise (i.e. if it is an `Arm` particle) corresponds to the
213 position in the arm, in the range form `0` (for particle closest
214 to the `Core`) to `getArmParticlesPerArm() - 1`.
217 if not isinstance(particleID, int):
220 if particleID
in self.
_caches[
"getParticleStructureIndices"]:
221 return self.
_caches[
"getParticleStructureIndices"][particleID]
226 originalParticleID = particleID
232 ret = [star,
None,
None]
233 self.
_caches[
"getParticleStructureIndices"][originalParticleID] = \
244 return [star, arm,
None]
246 ret = [star, arm, particleID]
247 self.
_caches[
"getParticleStructureIndices"][originalParticleID] = ret
253 Returns whether the two particles given are bonded.
255 If `pID1 == pID2`, `False` is returned.
258 Throws if `pID1` or `pID2` are out of range.
260 Throws if `pID1` or `pID2` are not an integer.
263 The first particle index, as an integer in the range
264 [0, `getTotalParticleCount()` - 1].
266 The second particle index, as an integer in the range
267 [0, `getTotalParticleCount()` - 1].
270 if not isinstance(pID1, int):
273 if not isinstance(pID2, int):
277 if (pID1, pID2)
in self.
_caches[
"particlesAreBonded"]:
278 return self.
_caches[
"particlesAreBonded"][(pID1, pID2)]
289 self.
_caches[
"particlesAreBonded"][(pID1, pID2)] =
False
295 if indices1[0] != indices2[0]:
296 self.
_caches[
"particlesAreBonded"][(pID1, pID2)] =
False
299 if indices1[1]
is None or indices2[1]
is None:
300 if indices1[1]
is None:
307 self.
_caches[
"particlesAreBonded"][(pID1, pID2)] =
True
310 return nonCore[2] == 0
312 if indices1[1] != indices2[1]:
313 self.
_caches[
"particlesAreBonded"][(pID1, pID2)] =
False
316 if indices1[2]
is None or indices2[2]
is None:
317 if indices1[2]
is None:
318 if indices2[2]
is None:
319 self.
_caches[
"particlesAreBonded"][(pID1, pID2)] =
False
321 nonMagnetic = indices2
323 nonMagnetic = indices1
326 self.
_caches[
"particlesAreBonded"][(pID1, pID2)] = ret
330 ret = abs(indices1[2] - indices2[2]) == 1
331 self.
_caches[
"particlesAreBonded"][(pID1, pID2)] = ret
337 Sets the collection of particles to use for dynamic calculations.
340 Throws if `particles` is not an instance of `ParticleCollection`.
342 Throws if `particles` does not conatin the right number of
346 An instance of `ParticleCollection` containing
347 `getTotalParticleCount` particles.
348 Only a reference to this object is saved!
351 if not isinstance(particles, ParticleCollection):
362 Returns the magnetic clusters, i.e. the largest groups of magnetic
363 particles that have the property that from any one member of a magnetic
364 cluster to any other member of that same cluster, there is a sequence of
365 cluster members between the two magnetic particles such that the
366 distance between consecutive members is at most
367 `magneticClusterMaxDistance`.
370 Throws if `magneticClusterMaxDistance` is neither `int` nor
373 Throws if no magnetic particles have been configured.
375 Throws if `setParticles` has not been called previously.
377 Throws if `magneticClusterMaxDistance` is negative.
379 @param[in] magneticClusterMaxDistance
380 The maximum distance between magnetic particles that defines
381 a cluster. Must be a non-negative `int` or `float`.
383 @return Returns a list of clusters, where each cluster is represented as
384 a list containing the positions (as instances of `Vector3DReal`)
385 of the cluster members.
388 if not isinstance(magneticClusterMaxDistance, (int, float)):
391 if magneticClusterMaxDistance < 0:
403 magnetPositions.append(self.
_particles.getPosition(index))
405 clusters = [[pos]
for pos
in magnetPositions]
412 Returns the number of magnetic clusters.
414 See `getMagneticClusters` for further documentation.
417 Throws if `magneticClusterMaxDistance` is neither `int` nor
420 Throws if no magnetic particles have been configured.
422 Throws if `setParticles` has not been called previously.
424 Throws if `magneticClusterMaxDistance` is negative.
432 Returns the WCA potential parameter \f$ \epsilon \f$ for the interaction
433 of particles of types `type1` and `type2`.
436 Throws if either `type1` or `type2` are not of type `str`.
438 Throws if either `type1` or `type2` have illegal values.
441 The type of one of the particles, which must be one of
442 `"Core"`, `"Arm"`, or `"Magnetic"` (the latter being allowed
443 only if `hasMagneticParticles()`).
445 The type of the other particle; see `type1` for further
449 for t
in [type1, type2]:
450 if not isinstance(t, str):
452 allowedValues = [
"Core",
"Arm"]
454 allowedValues.append(
"Magnetic")
455 if t
not in allowedValues:
459 self.
_config[
"interactionParameters.epsilon_" + type1.lower()]
461 self.
_config[
"interactionParameters.epsilon_" + type2.lower()]
464 return math.sqrt(epsilon1 * epsilon2)
469 Returns the WCA potential parameter \f$ \sigma \f$ for the interaction
470 of particles of types `type1` and `type2`.
473 Throws if either `type1` or `type2` are not of type `str`.
475 Throws if either `type1` or `type2` have illegal values.
478 The type of one of the particles, which must be one of
479 `"Core"`, `"Arm"`, or `"Magnetic"` (the latter being allowed
480 only if `hasMagneticParticles()`).
482 The type of the other particle; see `type1` for further
486 for t
in [type1, type2]:
487 if not isinstance(t, str):
489 allowedValues = [
"Core",
"Arm"]
491 allowedValues.append(
"Magnetic")
492 if t
not in allowedValues:
495 sigma1 = self.
_config[
"interactionParameters.sigma_" + type1.lower()]
496 sigma2 = self.
_config[
"interactionParameters.sigma_" + type2.lower()]
498 return (sigma1 + sigma2) / 2.0
503 Returns the WCA potential parameter \f$ D \f$ for the interaction
504 of particles of types `type1` and `type2`.
507 Throws if either `type1` or `type2` are not of type `str`.
509 Throws if either `type1` or `type2` have illegal values.
512 The type of one of the particles, which must be one of
513 `"Core"`, `"Arm"`, or `"Magnetic"` (the latter being allowed
514 only if `hasMagneticParticles()`).
516 The type of the other particle; see `type1` for further
520 for t
in [type1, type2]:
521 if not isinstance(t, str):
523 allowedValues = [
"Core",
"Arm"]
525 allowedValues.append(
"Magnetic")
526 if t
not in allowedValues:
529 d1 = self.
_config[
"interactionParameters.D_" + type1.lower()]
530 d2 = self.
_config[
"interactionParameters.D_" + type2.lower()]
532 return (d1 + d2) / 2.0
537 Returns the WCA potential for the interaction of particles of types
540 @warning The returned value is a reference to an internally cached
541 object. Do not modify!
544 Throws if either `type1` or `type2` are not of type `str`.
546 Throws if either `type1` or `type2` have illegal values.
549 The type of one of the particles, which must be one of
550 `"Core"`, `"Arm"`, or `"Magnetic"` (the latter being allowed
551 only if `hasMagneticParticles()`).
553 The type of the other particle; see `type1` for further
557 if (type1, type2)
in self.
_caches[
"getWCAPotential"]:
558 return self.
_caches[
"getWCAPotential"][(type1, type2)]
564 from .PairPotentials.WeeksChandlerAndersen_DistanceOffset\
565 import WeeksChandlerAndersen_DistanceOffset
as WCA
567 ret = WCA(epsilon, sigma, d)
568 self.
_caches[
"getWCAPotential"][(type1, type2)] = ret
574 Returns the WCA potential for the interaction of particles of types
577 @warning The returned value is a reference to an internally cached
578 object. Do not modify!
581 Throws if either `type1` or `type2` are not of type `str`.
583 Throws if either `type1` or `type2` have illegal values.
586 The type of one of the particles, which must be one of
587 `"Core"`, `"Arm"`, or `"Magnetic"` (the latter being allowed
588 only if `hasMagneticParticles()`).
590 The type of the other particle; see `type1` for further
594 if (type1, type2)
in self.
_caches[
"getFENEPotential"]:
595 return self.
_caches[
"getFENEPotential"][(type1, type2)]
601 from .PairPotentials.FENE
import FENE
603 K = 30 * epsilon / (sigma * sigma)
607 ret = FENE(K, l_0, R)
608 self.
_caches[
"getFENEPotential"][(type1, type2)] = ret
614 Returns the magnetic dipole-dipole interaction potential.
616 @warning The returned value is a reference to an internally cached
617 object. Do not modify!
620 Throws if `not hasMagneticParticles()`.
626 if self.
_caches[
"getMagneticPotential"]
is not None:
627 return self.
_caches[
"getMagneticPotential"]
629 from .PairPotentials.MagneticDipoleDipoleInteraction_ConstantIdenticalDipoles \
630 import MagneticDipoleDipoleInteraction_ConstantIdenticalDipoles \
632 from .Vector3DReal
import Vector3DReal
634 prefactor = self.
_config[
"interactionParameters.magneticPrefactor"]
635 orientationX = self.
_config[
"interactionParameters.dipoleOrientation.[0]"]
636 orientationY = self.
_config[
"interactionParameters.dipoleOrientation.[1]"]
637 orientationZ = self.
_config[
"interactionParameters.dipoleOrientation.[2]"]
638 orientation = Vector3DReal(orientationX, orientationY, orientationZ)
640 ret = Potential(prefactor, orientation)
641 self.
_caches[
"getMagneticPotential"] = ret
647 Computes the potential energy of the current system.
650 Throws if `setParticles` has not been called previously.
657 raise NotImplementedError()
673 ret += wca.getPotential(r)
677 ret += fene.getPotential(r)
679 if type1 ==
"Magnetic" and type2 ==
"Magnetic":
680 ret += magnetic.getPotential(r)
685 def _mergeClusters(self, clusters, magneticClusterMaxDistance):
686 for cluster
in clusters:
687 for otherCluster
in clusters:
688 if otherCluster == cluster:
691 for position
in cluster:
692 for otherPosition
in otherCluster:
693 delta = position - otherPosition
694 distance = delta.getLength()
695 if distance <= magneticClusterMaxDistance:
697 for newCluster
in clusters:
698 if newCluster == cluster:
700 if newCluster == otherCluster:
702 newClusters.append(newCluster)
703 newClusters.append(cluster + otherCluster)
707 newClusters, magneticClusterMaxDistance)