def __call__(self, gradients = None, force_constants = None, small_change=0): self.checkUniverseVersion() args = [self.configuration.array] if ParticleProperties.isParticleProperty(gradients): args.append(gradients.array) elif type(gradients) == Numeric.arraytype: gradients = \ ParticleProperties.ParticleVector(self.universe, gradients) args.append(gradients.array) elif gradients: gradients = ParticleProperties.ParticleVector(self.universe, None) args.append(gradients.array) else: args.append(None) if ParticleProperties.isParticleProperty(force_constants): args.append(force_constants.array) elif type(force_constants) == Numeric.arraytype: force_constants = \ ParticleProperties.SymmetricPairTensor(self.universe, force_constants) args.append(force_constants.array) else: sparse_type = None try: from MMTK_forcefield import SparseForceConstants sparse_type = type(SparseForceConstants(2, 2)) except ImportError: pass if type(force_constants) == sparse_type: args.append(force_constants) elif force_constants: force_constants = \ ParticleProperties.SymmetricPairTensor(self.universe, None) args.append(force_constants.array) else: args.append(None) args.append(small_change) self.universe.acquireReadStateLock() try: energy = apply(self.evaluator, tuple(args)) finally: self.universe.releaseReadStateLock() if force_constants: return energy, gradients, force_constants elif gradients: return energy, gradients else: return energy
def __init__(self, universe, rigid_bodies): """ :param universe: the universe for which the subspace is created :type universe: :class:~MMTK.Universe.Universe :param rigid_bodies: a list or set of rigid bodies with some common atoms """ ex_ey_ez = [Vector(1.,0.,0.), Vector(0.,1.,0.), Vector(0.,0.,1.)] # Constructs # 1) a list of vectors describing the rigid-body motions of each # rigid body as if it were independent. # 2) a list of pair-distance constraint vectors for all pairs of # atoms inside a rigid body. # The LRB subspace is constructed from the projections of the # first set of vectors onto the orthogonal complement of the # subspace generated by the second set of vectors. vectors = [] c_vectors = [] for rb in rigid_bodies: atoms = rb.atomList() for d in ex_ey_ez: v = ParticleProperties.ParticleVector(universe) for a in atoms: v[a] = d vectors.append(v) if len(atoms) > 1: center = rb.centerOfMass() iv = len(vectors)-3 for d in ex_ey_ez: v = ParticleProperties.ParticleVector(universe) for a in atoms: v[a] = d.cross(a.position()-center) for vt in vectors[iv:]: v -= v.dotProduct(vt)*vt if v.dotProduct(v) > 0.: vectors.append(v) for a1, a2 in Utility.pairs(atoms): distance = universe.distanceVector(a1.position(), a2.position()) v = ParticleProperties.ParticleVector(universe) v[a1] = distance v[a2] = -distance c_vectors.append(v) if c_vectors: constraints = Subspace(universe, c_vectors) vectors = [constraints.projectionComplementOf(v) for v in vectors] Subspace.__init__(self, universe, vectors)
def EISF(self, q_range=(0., 15.), subset=None, weights=None, random_vectors=15, first_mode=6): if subset is None: subset = self.universe if weights is None: weights = self.universe.getParticleScalar('b_incoherent') weights = weights * weights weights = weights * subset.booleanMask() total = weights.sumOverParticles() weights = weights / total first, last, step = (q_range + (None, ))[:3] if step is None: step = (last - first) / 50. q = N.arange(first, last, step) f = ParticleProperties.ParticleTensor(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) f = f + (1. / mode.inv_relaxation_time) * mode.dyadicProduct(mode) f = Units.k_B * self.temperature * f / self.friction eisf = N.zeros(q.shape, N.Float) random_vectors = Random.randomDirections(random_vectors) for v in random_vectors: for a in subset.atomList(): exp = N.exp(-v * (f[a] * v)) N.add(eisf, weights[a] * exp**(q * q), eisf) return InterpolatingFunction((q, ), eisf / len(random_vectors))
def __call__(self, vector): conf = self.universe.configuration() r = ParticleProperties.ParticleScalar(self.universe) l = deformation(conf.array, vector.array, self.pairs, None, r.array, self.cutoff, self.fc_length, self.factor, self.normalize, 0, self.version) return r
def particleValues(self): """ :returns: the values of the field at the positions of the atoms :rtype: :class:~MMTK.ParticleProperties.ParticleProperty """ universe = self.system.universe() rank = self.field.rank if rank == 0: v = ParticleProperties.ParticleScalar(universe) elif rank == 1: v = ParticleProperties.ParticleVector(universe) else: raise ValueError("no appropriate return type") for a in self.system.atomList(): v[a] = self.field(a.position()) return v
def charges(self, universe): q = ParticleProperties.ParticleScalar(universe) for object in universe: for atom in object.atomList(): q[atom] = object.getAtomProperty(atom, self.dataset.charge_property) return q
def fluctuations(self, first_mode=6): f = ParticleProperties.ParticleScalar(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) f += (mode * mode) / mode.inv_relaxation_time f = Units.k_B * self.temperature * f / self.friction return f
def fluctuations(self, first_mode=6): f = ParticleProperties.ParticleScalar(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) f += (mode * mode) / mode.force_constant if self.temperature is not None: f.array *= Units.k_B * self.temperature return f
def fluctuations(self, first_mode=6): """Returns a Class:MMTK.ParticleScalar containing the thermal fluctuations for each atom in the universe.""" f = ParticleProperties.ParticleScalar(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) f += (mode * mode) / mode.force_constant f.array *= Units.k_B * self.temperature return f
def fluctuations(self, first_mode=6): """Returns a Class:MMTK.ParticleScalar containing the thermal fluctuations for each atom in the universe.""" f = ParticleProperties.ParticleScalar(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) f += (mode * mode) / mode.inv_relaxation_time f = Units.k_B * self.temperature * f / self.friction return f
def anisotropicFluctuations(self, first_mode=6): f = ParticleProperties.ParticleTensor(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) array = mode.array f.array += (array[:, :, N.NewAxis]*array[:, N.NewAxis, :]) \ / mode.force_constant if self.temperature is not None: f.array *= Units.k_B * self.temperature return f
def fluctuations(self, first_mode=6): """Returns a Class:MMTK.ParticleScalar containing the thermal fluctuations for each atom in the universe.""" f = ParticleProperties.ParticleScalar(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) f += (mode * mode) / mode.frequency**2 f.array /= self.weights[:, 0]**2 f.array *= Units.k_B * self.temperature / (2. * N.pi)**2 return f
def anisotropicFluctuations(self, first_mode=6): """Returns a Class:MMTK.ParticleTensor containing the thermal fluctuations for each atom in the universe.""" f = ParticleProperties.ParticleTensor(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) array = mode.array f.array += (array[:, :, N.NewAxis]*array[:, N.NewAxis, :]) \ / mode.force_constant f.array *= Units.k_B * self.temperature return f
def fluctuations(self, first_mode=6): f = ParticleProperties.ParticleScalar(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) f += (mode * mode) / mode.frequency**2 f.array /= self.weights[:, 0]**2 s = 1. / (2. * N.pi)**2 if self.temperature is not None: s *= Units.k_B * self.temperature f.array *= s return f
def complement(self): """ :returns: the orthogonal complement subspace :rtype: :class:~MMTK.Subspace.Subspace """ basis = [] for i in range(self.universe.numberOfAtoms()): for e in [ex, ey, ez]: p = ParticleProperties.ParticleVector(self.universe) p[i] = e basis.append(self.projectionComplementOf(p)) return Subspace(self.universe, basis)
def projectionOf(self, vector): """ :param vector: a particle vector :type vector: :class:~MMTK.ParticleProperties.ParticleVector :returns: the projection of the vector onto the subspace. """ vector = vector.array basis = self.getBasis().array p = N.zeros(vector.shape, N.Float) for bv in basis: N.add(N.add.reduce(N.ravel(bv*vector))*bv, p, p) return ParticleProperties.ParticleVector(self.universe, p)
def EISF(self, q_range=(0., 15.), subset=None, weights=None, random_vectors=15, first_mode=6): """ :param q_range: the range of angular wavenumber values :type q_range: tuple :param subset: the subset of the universe used in the calculation (default: the whole universe) :type subset: :class:~MMTK.Collections.GroupOfAtoms :param weights: the weight to be given to each atom in the average (default: incoherent scattering lengths) :type weights: :class:~MMTK.ParticleProperties.ParticleScalar :param random_vectors: the number of random direction vectors used in the orientational average :type random_vectors: int :param first_mode: the first mode to be taken into account for the fluctuation calculation. The default value of 6 is right for molecules in vacuum. :type first_mode: int :returns: the Elastic Incoherent Structure Factor (EISF) as a function of angular wavenumber :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if subset is None: subset = self.universe if weights is None: weights = self.universe.getParticleScalar('b_incoherent') weights = weights * weights weights = weights * subset.booleanMask() total = weights.sumOverParticles() weights = weights / total first, last, step = (q_range + (None, ))[:3] if step is None: step = (last - first) / 50. q = N.arange(first, last, step) f = ParticleProperties.ParticleTensor(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) f = f + (1. / mode.inv_relaxation_time) * mode.dyadicProduct(mode) f = Units.k_B * self.temperature * f / self.friction eisf = N.zeros(q.shape, N.Float) random_vectors = Random.randomDirections(random_vectors) for v in random_vectors: for a in subset.atomList(): exp = N.exp(-v * (f[a] * v)) N.add(eisf, weights[a] * exp**(q * q), eisf) return InterpolatingFunction((q, ), eisf / len(random_vectors))
def randomParticleVector(universe, width): """ :param universe: a universe :type universe: :class:`~MMTK.Universe.Universe` :param width: the width (standard deviation) of a Gaussian distribution :type width: float :returns: a set of vectors drawn from a Gaussian distribution with a given width centered around zero. :rtype: :class:`~MMTK.ParticleProperties.ParticleVector` """ data = gaussian(0., 0.577350269189 * width, (universe.numberOfPoints(), 3)) return ParticleProperties.ParticleVector(universe, data)
def __call__(self, vector, gradients = None): conf = self.universe.configuration() g = None if gradients is not None: if ParticleProperties.isParticleProperty(gradients): g = gradients elif isinstance(gradients, N.array_type): g = ParticleProperties.ParticleVector(self.universe, gradients) elif gradients: g = ParticleProperties.ParticleVector(self.universe) if g is None: g_array = None else: g_array = g.array l = deformation(conf.array, vector.array, self.pairs, g_array, None, self.cutoff, self.fc_length, self.factor, 0, 1, self.version) if g is None: return l else: return l, g
def anisotropicFluctuations(self, first_mode=6): f = ParticleProperties.ParticleTensor(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) array = mode.array f.array += (array[:, :, N.NewAxis]*array[:, N.NewAxis, :]) \ / mode.frequency**2 s = (2. * N.pi)**2 if self.temperature is not None: s /= Units.k_B * self.temperature f.array /= s * self.universe.masses().array[:, N.NewAxis, N.NewAxis] return f
def __call__(self, **options): # Process the keyword arguments self.setCallOptions(options) # Check if the universe has features not supported by the integrator Features.checkFeatures(self, self.universe) # Get the universe variables needed by the integrator configuration = self.universe.configuration() masses = self.universe.masses() velocities = self.universe.velocities() if velocities is None: raise ValueError("no velocities") # Get the friction coefficients. First check if a keyword argument # 'friction' was given to the integrator. Its value can be a # ParticleScalar or a plain number (used for all atoms). If no # such argument is given, collect the values of the attribute # 'friction' from all atoms (default is zero). try: friction = self.getOption('friction') except KeyError: friction = self.universe.getParticleScalar('friction') if not ParticleProperties.isParticleProperty(friction): var = ParticleProperties.ParticleScalar(self.universe) var.array[:] = friction friction = var # Construct a C evaluator object for the force field, using # the specified number of threads or the default value nt = self.getOption('threads') evaluator = self.universe.energyEvaluator(threads=nt).CEvaluator() # Run the C integrator MMTK_langevin.integrateLD(self.universe, configuration.array, velocities.array, masses.array, friction.array, evaluator, self.getOption('temperature'), self.getOption('delta_t'), self.getOption('steps'), self.getActions())
def displacementUnderTransformation(self, t): """ :param t: the transformation to be applied :type t: Scientific.Geometry.Transformation :returns: the displacement vectors for the atoms in the object that correspond to the transformation t. :rtype: :class:~MMTK.ParticleProperties.ParticleVector """ d = ParticleProperties.ParticleVector(self.universe()) for a in self.atomIterator(): r = a.position() d[a] = t(r) - r return d
def __init__(self, universe, atom_pairs): """ :param universe: the universe for which the subspace is created :type universe: :class:~MMTK.Universe.Universe :param atom_pairs: a sequence of atom pairs whose distance-vector motion is included in the subspace """ vectors = [] for atom1, atom2 in atom_pairs: v = ParticleProperties.ParticleVector(universe) distance = atom1.position()-atom2.position() v[atom1] = distance v[atom2] = -distance vectors.append(v) Subspace.__init__(self, universe, vectors)
def booleanMask(self): """ :returns: a ParticleScalar object that contains a value of 1 for each atom that is in the object and a value of 0 for all other atoms in the universe :rtype: :class:~MMTK.ParticleProperties.ParticleScalar """ universe = self.universe() if universe is None: raise ValueError("object not in a universe") array = N.zeros((universe.numberOfAtoms(), ), N.Int) mask = ParticleProperties.ParticleScalar(universe, array) for a in self.atomIterator(): mask[a] = 1 return mask
def __call__(self, gradients=None, force_constants=None, small_change=False): self.checkUniverseVersion() args = [self.configuration.array] if ParticleProperties.isParticleProperty(gradients): args.append(gradients.array) elif isinstance(gradients, N.array_type): gradients = \ ParticleProperties.ParticleVector(self.universe, gradients) args.append(gradients.array) elif gradients: gradients = ParticleProperties.ParticleVector(self.universe, None) args.append(gradients.array) else: args.append(None) if ParticleProperties.isParticleProperty(force_constants): args.append(force_constants.array) elif isinstance(force_constants, N.array_type): force_constants = \ ParticleProperties.SymmetricPairTensor(self.universe, force_constants) args.append(force_constants.array) else: sparse_type = None try: from MMTK_forcefield import SparseForceConstants sparse_type = type(SparseForceConstants(2, 2)) except ImportError: pass if isinstance(force_constants, sparse_type): args.append(force_constants) elif force_constants: force_constants = \ ParticleProperties.SymmetricPairTensor(self.universe, None) args.append(force_constants.array) else: args.append(None) args.append(small_change) self.universe.acquireReadStateLock() try: energy = apply(self.evaluator, tuple(args)) finally: self.universe.releaseReadStateLock() if force_constants: return energy, gradients, force_constants elif gradients: return energy, gradients else: return energy
def __init__(self, universe, cutoff): """ :param universe: the universe for which the basis will be used :type universe: :class:~MMTK.Universe.Universe :param cutoff: the wavelength cutoff. A smaller value yields a larger basis. :type cutoff: float """ p1, p2 = universe.boundingBox() p2 = p2 + Vector(cutoff, cutoff, cutoff) l = (p2 - p1).array n_max = (0.5 * l / cutoff + 0.5).astype(N.Int) wave_numbers = [(nx, ny, nz) for nx in range(-n_max[0], n_max[0]+1) for ny in range(-n_max[1], n_max[1]+1) for nz in range(-n_max[2], n_max[2]+1) if (nx/l[0])**2 + (ny/l[1])**2 + (nz/l[2])**2 \ < 0.25/cutoff**2] atoms = universe.atomList() natoms = len(atoms) basis = N.zeros((3 * len(wave_numbers) + 3, natoms, 3), N.Float) cm = universe.centerOfMass() i = 0 for rotation in [ Vector(1., 0., 0.), Vector(0., 1., 0.), Vector(0., 0., 1.) ]: v = ParticleProperties.ParticleVector(universe, basis[i]) for a in atoms: v[a] = rotation.cross(a.position() - cm) i += i conf = universe.configuration().array - p1.array for n in wave_numbers: k = 2. * N.pi * N.array(n) / l w = self._w(conf[:, 0], k[0]) * self._w(conf[:, 1], k[1]) * \ self._w(conf[:, 2], k[2]) basis[i, :, 0] = w basis[i + 1, :, 1] = w basis[i + 2, :, 2] = w i += 3 self.array = basis self.universe = universe
def __getitem__(self, item): return ParticleProperties.ParticleVector(self.universe, self.array[item])
def __init__(self, universe, objects): """ :param universe: the universe for which the subspace is created :type universe: :class:~MMTK.Universe.Universe :param objects: a sequence of objects whose rigid-body motion is included in the subspace """ if not Utility.isSequenceObject(objects): objects = [objects] else: objects = copy.copy(objects) # Identify connected sets of linked rigid bodies and remove # them from the plain rigid body list. atom_map = {} for o in objects: for a in o.atomIterator(): am = atom_map.get(a, []) am.append(o) atom_map[a] = am rb_map = {} for rbs in atom_map.values(): if len(rbs) > 1: for rb in rbs: rb_map[rb] = rb_map.get(rb, frozenset()) \ .union(frozenset(rbs)) for rb in rb_map.keys(): objects.remove(rb) while True: changed = False for rbs in rb_map.values(): for rb in rbs: s = rb_map[rb] rb_map[rb] = s.union(rbs) if s != rb_map[rb]: changed = True if not changed: break lrbs = frozenset(rb_map.values()) # Generate the subspace vectors for the isolated rigid bodies. ex_ey_ez = [Vector(1.,0.,0.), Vector(0.,1.,0.), Vector(0.,0.,1.)] vectors = [] for o in objects: rb_atoms = o.atomList() for d in ex_ey_ez: v = ParticleProperties.ParticleVector(universe) for a in rb_atoms: v[a] = d vectors.append(v/N.sqrt(len(rb_atoms))) if len(rb_atoms) > 1: center = o.centerOfMass() iv = len(vectors)-3 for d in ex_ey_ez: v = ParticleProperties.ParticleVector(universe) for a in rb_atoms: v[a] = d.cross(a.position()-center) for vt in vectors[iv:]: v -= v.dotProduct(vt)*vt norm_sq = N.sqrt(v.dotProduct(v)) if norm_sq > 0.: vectors.append(v/norm_sq) # Generate the subspace vectors for the linked rigid bodies. for lrb in lrbs: lrb_ss = LinkedRigidBodyMotionSubspace(universe, lrb) for v in lrb_ss.getBasis(): vectors.append(v) Subspace.__init__(self, universe, vectors) # The vector set is already orthonormal by construction, # so we can skip the lengthy SVD procedure. self._basis = ParticleVectorSet(universe, len(vectors)) for i in range(len(vectors)): self._basis.array[i] = vectors[i].array
def __getitem__(self, i): if i >= self.n: raise IndexError return ParticleProperties.ParticleVector(self.universe, self.array[i])