def meanSquareDisplacement(self, subset=None, weights=None, time_range = (0., None, None), first_mode = 6): """Returns the averaged mean-square displacement of the atoms in |subset| (default: all atoms) at time points defined by |time_range| using |weights| in the average (default: masses). |time_range| is a three element tuple (first, last, step). The defaults are first=0., last= three times the longest relaxation time, and step defined such that 300 points are used in total. """ if subset is None: subset = self.universe if weights is None: weights = self.universe.masses() weights = weights*subset.booleanMask() total = weights.sumOverParticles() weights = weights/(total*self.friction) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3./self.rawMode(first_mode).inv_relaxation_time if step is None: step = (last-first)/300. time = N.arange(first, last, step) msd = N.zeros(time.shape, N.Float) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) rt = mode.inv_relaxation_time d = (weights*(mode*mode)).sumOverParticles() N.add(msd, d*(1.-N.exp(-rt*time))/rt, msd) N.multiply(msd, 2.*Units.k_B*self.temperature, msd) return InterpolatingFunction((time,), msd)
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 EISF(self, q_range = (0., 15.), subset=None, weights = None, random_vectors = 15, first_mode = 6): if self.temperature is None: raise ValueError("no temperature available") 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 = MMTK.ParticleTensor(self.universe) for i in range(first_mode, self.nmodes): mode = self[i] f = f + mode.dyadicProduct(mode) eisf = N.zeros(q.shape, N.Float) for i in range(random_vectors): v = MMTK.Random.randomDirection() 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/random_vectors)
def EISF(self, q_range=(0., 15.), subset=None, weights=None, random_vectors=15, first_mode=6): if self.temperature is None: raise ValueError("no temperature available") 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 = MMTK.ParticleTensor(self.universe) for i in range(first_mode, self.nmodes): mode = self[i] f = f + mode.dyadicProduct(mode) eisf = N.zeros(q.shape, N.Float) for i in range(random_vectors): v = MMTK.Random.randomDirection() 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 / random_vectors)
def meanSquareDisplacement(self, subset=None, weights=None, time_range = (0., None, None), tau=None, first_mode = 6): """Returns the averaged mean-square displacement of the atoms in |subset| (default: all atoms) at time points defined by |time_range| using |weights| in the average (default: masses). |time_range| is a three element tuple (first, last, step). The defaults are first=0., last= 20 times the longest vibration perdio, and step defined such that 300 points are used in total. """ if self.temperature is None: raise ValueError("no temperature available") if subset is None: subset = self.universe if weights is None: weights = self.universe.masses() weights = weights*subset.booleanMask() total = weights.sumOverParticles() weights = weights/total first, last, step = (time_range + (None, None))[:3] if last is None: last = 20./self[first_mode].frequency if step is None: step = (last-first)/300. time = N.arange(first, last, step) if tau is None: damping = 1. else: damping = N.exp(-(time/tau)**2) msd = N.zeros(time.shape, N.Float) for i in range(first_mode, self.nmodes): mode = self[i] omega = 2.*N.pi*mode.frequency d = (weights*(mode*mode)).sumOverParticles() N.add(msd, d*(1.-damping*N.cos(omega*time)), msd) return InterpolatingFunction((time,), msd)
def meanSquareDisplacement(self, subset=None, weights=None, time_range=(0., None, None), first_mode=6): """Returns the averaged mean-square displacement of the atoms in |subset| (default: all atoms) at time points defined by |time_range| using |weights| in the average (default: masses). |time_range| is a three element tuple (first, last, step). The defaults are first=0., last= three times the longest relaxation time, and step defined such that 300 points are used in total. """ if subset is None: subset = self.universe if weights is None: weights = self.universe.masses() weights = weights * subset.booleanMask() total = weights.sumOverParticles() weights = weights / (total * self.friction) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3. / self.rawMode(first_mode).inv_relaxation_time if step is None: step = (last - first) / 300. time = N.arange(first, last, step) msd = N.zeros(time.shape, N.Float) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) rt = mode.inv_relaxation_time d = (weights * (mode * mode)).sumOverParticles() N.add(msd, d * (1. - N.exp(-rt * time)) / rt, msd) N.multiply(msd, 2. * Units.k_B * self.temperature, msd) return InterpolatingFunction((time, ), msd)
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 finalize(self): """Finalizes the calculations (e.g. averaging the total term, output files creations ...). """ # The NetCDF output file is opened for writing. outputFile = NetCDFFile(self.output, 'w') outputFile.title = self.__class__.__name__ outputFile.jobinfo = self.information + '\nOutput file written on: %s\n\n' % asctime( ) # Dictionnary whose keys are of the form Gi where i is the group number # and the entries are the list of the index of the atoms building the group. comp = 1 for g in self.group: outputFile.jobinfo += 'Group %d: %s\n' % (comp, [index for index in g]) comp += 1 # Some dimensions are created. outputFile.createDimension('NFRAMES', self.nFrames) # Creation of the NetCDF output variables. # The time. TIMES = outputFile.createVariable('time', N.Float, ('NFRAMES', )) TIMES[:] = self.times[:] TIMES.units = 'ps' avacfTotal = N.zeros((self.nFrames), typecode=N.Float) for k in self.AVACF.keys(): AVACF = outputFile.createVariable('avacf-group%s' % k, N.Float, ('NFRAMES', )) AVACF[:] = self.AVACF[k][:] AVACF.units = 'rad^2*ps^-2' N.add(avacfTotal, self.AVACF[k], avacfTotal) avacfTotal /= self.nGroups AVACF = outputFile.createVariable('avacf-total', N.Float, ('NFRAMES', )) AVACF[:] = avacfTotal[:] AVACF.units = 'rad^2*ps^-2' asciiVar = sorted(outputFile.variables.keys()) outputFile.close() self.toPlot = { 'netcdf': self.output, 'xVar': 'time', 'yVar': 'avacf-total' } # Create an ASCII version of the NetCDF output file. convertNetCDFToASCII(inputFile = self.output,\ outputFile = os.path.splitext(self.output)[0] + '.cdl',\ variables = asciiVar)
def translateBy(self, vector): """ Adds a vector to the values at all steps. This does B{not} change the data in the trajectory file. @param vector: the vector to be added @type vector: C{Scientific.Geometry.Vector} """ N.add(self.array, vector.array[N.NewAxis, :], self.array)
def projectionOf(self, vector): """Returns the projection of |vector| (a Class:MMTK.ParticleVector object) 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 meanSquareDisplacement(self, subset=None, weights=None, time_range=(0., None, None), tau=None, first_mode=6): """ :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: atomic masses) :type weights: :class:~MMTK.ParticleProperties.ParticleScalar :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_range: tuple :param tau: the relaxation time of an exponential damping factor (default: no damping) :type tau: float :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 averaged mean-square displacement of the atoms in subset as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if self.temperature is None: raise ValueError("no temperature available") if subset is None: subset = self.universe if weights is None: weights = self.universe.masses() weights = weights * subset.booleanMask() total = weights.sumOverParticles() weights = weights / total first, last, step = (time_range + (None, None))[:3] if last is None: last = 20. / self[first_mode].frequency if step is None: step = (last - first) / 300. time = N.arange(first, last, step) if tau is None: damping = 1. else: damping = N.exp(-(time / tau)**2) msd = N.zeros(time.shape, N.Float) for i in range(first_mode, self.nmodes): mode = self[i] omega = 2. * N.pi * mode.frequency d = (weights * (mode * mode)).sumOverParticles() N.add(msd, d * (1. - damping * N.cos(omega * time)), msd) return InterpolatingFunction((time, ), msd)
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 self.temperature is None: raise ValueError("no temperature available") 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 = MMTK.ParticleTensor(self.universe) for i in range(first_mode, self.nmodes): mode = self[i] f = f + mode.dyadicProduct(mode) eisf = N.zeros(q.shape, N.Float) for i in range(random_vectors): v = MMTK.Random.randomDirection() 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 / random_vectors)
def meanSquareDisplacement(self, subset=None, weights=None, time_range = (0., None, None), tau=None, first_mode = 6): """ :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: atomic masses) :type weights: :class:`~MMTK.ParticleProperties.ParticleScalar` :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_range: tuple :param tau: the relaxation time of an exponential damping factor (default: no damping) :type tau: float :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 averaged mean-square displacement of the atoms in subset as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if self.temperature is None: raise ValueError("no temperature available") if subset is None: subset = self.universe if weights is None: weights = self.universe.masses() weights = weights*subset.booleanMask() total = weights.sumOverParticles() weights = weights/total first, last, step = (time_range + (None, None))[:3] if last is None: last = 20./self[first_mode].frequency if step is None: step = (last-first)/300. time = N.arange(first, last, step) if tau is None: damping = 1. else: damping = N.exp(-(time/tau)**2) msd = N.zeros(time.shape, N.Float) for i in range(first_mode, self.nmodes): mode = self[i] omega = 2.*N.pi*mode.frequency d = (weights*(mode*mode)).sumOverParticles() N.add(msd, d*(1.-damping*N.cos(omega*time)), msd) return InterpolatingFunction((time,), msd)
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 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 self.temperature is None: raise ValueError("no temperature available") 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 = MMTK.ParticleTensor(self.universe) for i in range(first_mode, self.nmodes): mode = self[i] f = f + mode.dyadicProduct(mode) eisf = N.zeros(q.shape, N.Float) for i in range(random_vectors): v = MMTK.Random.randomDirection() 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/random_vectors)
def meanSquareDisplacement(self, subset=None, weights=None, time_range=(0., None, None), first_mode=6): """ :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: atomic masses) :type weights: :class:~MMTK.ParticleProperties.ParticleScalar :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_range: tuple :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 averaged mean-square displacement of the atoms in subset as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if subset is None: subset = self.universe if weights is None: weights = self.universe.masses() weights = weights * subset.booleanMask() total = weights.sumOverParticles() weights = weights / (total * self.friction) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3. / self.rawMode(first_mode).inv_relaxation_time if step is None: step = (last - first) / 300. time = N.arange(first, last, step) msd = N.zeros(time.shape, N.Float) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) rt = mode.inv_relaxation_time d = (weights * (mode * mode)).sumOverParticles() N.add(msd, d * (1. - N.exp(-rt * time)) / rt, msd) N.multiply(msd, 2. * Units.k_B * self.temperature, msd) return InterpolatingFunction((time, ), msd)
def finalize(self): """Finalizes the calculations (e.g. averaging the total term, output files creations ...). """ outputFile = NetCDFFile(self.output, 'w') outputFile.title = self.__class__.__name__ outputFile.jobinfo = self.information + '\nOutput file written on: %s\n\n' % asctime( ) outputFile.createDimension('NGROUPS', self.nGroups) outputFile.createDimension('NFRAMES', self.nFrames) TIMES = outputFile.createVariable('time', N.Float, ('NFRAMES', )) TIMES[:] = self.times TIMES.units = 'ps' GROUPNUMBER = outputFile.createVariable('group_number', N.Int32, ('NGROUPS', )) P2 = outputFile.createVariable('p2', N.Float, ('NGROUPS', 'NFRAMES')) P2AVG = outputFile.createVariable('p2-groupavg', N.Float, ('NFRAMES', )) S2 = outputFile.createVariable('s2', N.Float, ('NGROUPS', )) p2Avg = N.zeros((self.nFrames), typecode=N.Float) comp = 0 for bKey in sorted(self.bondNames.keys()): bName = self.bondNames[bKey] GROUPNUMBER[comp] = bName S2[comp] = self.S2[bName] P2[comp, :] = self.P2[bName] N.add(p2Avg, self.P2[bName], p2Avg) comp += 1 P2AVG[:] = p2Avg / float(self.nGroups) asciiVar = sorted(outputFile.variables.keys()) outputFile.close() self.toPlot = {'netcdf': self.output, 'xVar': 'pair', 'yVar': 'S2'} # Creates an ASCII version of the NetCDF output file. convertNetCDFToASCII(inputFile = self.output,\ outputFile = os.path.splitext(self.output)[0] + '.cdl',\ variables = asciiVar)
def meanSquareDisplacement(self, subset=None, weights=None, time_range = (0., None, None), first_mode = 6): """ :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: atomic masses) :type weights: :class:`~MMTK.ParticleProperties.ParticleScalar` :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_range: tuple :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 averaged mean-square displacement of the atoms in subset as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if subset is None: subset = self.universe if weights is None: weights = self.universe.masses() weights = weights*subset.booleanMask() total = weights.sumOverParticles() weights = weights/(total*self.friction) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3./self.rawMode(first_mode).inv_relaxation_time if step is None: step = (last-first)/300. time = N.arange(first, last, step) msd = N.zeros(time.shape, N.Float) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) rt = mode.inv_relaxation_time d = (weights*(mode*mode)).sumOverParticles() N.add(msd, d*(1.-N.exp(-rt*time))/rt, msd) N.multiply(msd, 2.*Units.k_B*self.temperature, msd) return InterpolatingFunction((time,), msd)
def incoherentScatteringFunction(self, q, time_range=(0., None, None), subset=None, weights=None, tau=None, random_vectors=15, first_mode=6): if self.temperature is None: raise ValueError("no temperature available") 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 = (time_range + (None, None))[:3] if last is None: last = 20. / self[first_mode].frequency if step is None: step = (last - first) / 400. time = N.arange(first, last, step) if tau is None: damping = 1. else: damping = N.exp(-(time / tau)**2) finc = N.zeros(time.shape, N.Float) random_vectors = MMTK.Random.randomDirections(random_vectors) for v in random_vectors: for a in subset.atomList(): msd = 0. for i in range(first_mode, self.nmodes): mode = self[i] d = (v * mode[a])**2 omega = 2. * N.pi * mode.frequency msd = msd + d * (1. - damping * N.cos(omega * time)) N.add(finc, weights[a] * N.exp(-q * q * msd), finc) return InterpolatingFunction((time, ), finc / len(random_vectors))
def meanSquareDisplacement(self, subset=None, weights=None, time_range=(0., None, None), tau=None, first_mode=6): """Returns the averaged mean-square displacement of the atoms in |subset| (default: all atoms) at time points defined by |time_range| using |weights| in the average (default: masses). |time_range| is a three element tuple (first, last, step). The defaults are first=0., last= 20 times the longest vibration perdio, and step defined such that 300 points are used in total. """ if self.temperature is None: raise ValueError("no temperature available") if subset is None: subset = self.universe if weights is None: weights = self.universe.masses() weights = weights * subset.booleanMask() total = weights.sumOverParticles() weights = weights / total first, last, step = (time_range + (None, None))[:3] if last is None: last = 20. / self[first_mode].frequency if step is None: step = (last - first) / 300. time = N.arange(first, last, step) if tau is None: damping = 1. else: damping = N.exp(-(time / tau)**2) msd = N.zeros(time.shape, N.Float) for i in range(first_mode, self.nmodes): mode = self[i] omega = 2. * N.pi * mode.frequency d = (weights * (mode * mode)).sumOverParticles() N.add(msd, d * (1. - damping * N.cos(omega * time)), msd) return InterpolatingFunction((time, ), msd)
def incoherentScatteringFunction(self, q, time_range = (0., None, None), subset=None, weights=None, tau=None, random_vectors=15, first_mode = 6): if self.temperature is None: raise ValueError("no temperature available") 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 = (time_range + (None, None))[:3] if last is None: last = 20./self[first_mode].frequency if step is None: step = (last-first)/400. time = N.arange(first, last, step) if tau is None: damping = 1. else: damping = N.exp(-(time/tau)**2) finc = N.zeros(time.shape, N.Float) random_vectors = MMTK.Random.randomDirections(random_vectors) for v in random_vectors: for a in subset.atomList(): msd = 0. for i in range(first_mode, self.nmodes): mode = self[i] d = (v*mode[a])**2 omega = 2.*N.pi*mode.frequency msd = msd+d*(1.-damping*N.cos(omega*time)) N.add(finc, weights[a]*N.exp(-q*q*msd), finc) return InterpolatingFunction((time,), finc/len(random_vectors))
def coherentScatteringFunction(self, q, time_range = (0., None, None), 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_coherent') mask = subset.booleanMask() weights = N.repeat(weights.array, mask.array) weights = weights/N.sqrt(N.add.reduce(weights*weights)) friction = N.repeat(self.friction.array, mask.array) r = N.repeat(self.universe.configuration().array, mask.array) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3./self.rawMode(first_mode).inv_relaxation_time if step is None: step = (last-first)/300. time = N.arange(first, last, step) natoms = subset.numberOfAtoms() kT = Units.k_B*self.temperature fcoh = N.zeros((len(time),), N.Complex) random_vectors = Random.randomDirections(random_vectors) for v in random_vectors: phase = N.exp(-1.j*q*N.dot(r, v.array)) for ai in range(natoms): fbt = N.zeros((natoms, len(time)), N.Float) for i in range(first_mode, self.nmodes): irt = self.rawMode(i).inv_relaxation_time d = q * N.repeat((self.rawMode(i)*v).array, mask.array) \ / N.sqrt(friction) ft = N.exp(-irt*time)/irt N.add(fbt, d[ai] * d[:, N.NewAxis] * ft[N.NewAxis, :], fbt) N.add(fbt, (-0.5/irt) * (d[ai]**2 + d[:, N.NewAxis]**2), fbt) N.add(fcoh, weights[ai]*phase[ai] * N.dot(weights*N.conjugate(phase), N.exp(kT*fbt)), fcoh) return InterpolatingFunction((time,), fcoh.real/len(random_vectors))
def incoherentScatteringFunction(self, q, time_range = (0., None, None), subset=None, random_vectors=15, first_mode = 6): if subset is None: subset = self.universe mask = subset.booleanMask() weights_inc = self.universe.getParticleScalar('b_incoherent') weights_inc = N.repeat(weights_inc.array**2, mask.array) weights_inc = weights_inc/N.add.reduce(weights_inc) friction = N.repeat(self.friction.array, mask.array) mass = N.repeat(self.universe.masses().array, mask.array) r = N.repeat(self.universe.configuration().array, mask.array) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3./self.weighedMode(first_mode).inv_relaxation_time if step is None: step = (last-first)/300. time = N.arange(first, last, step) natoms = subset.numberOfAtoms() kT = Units.k_B*self.temperature finc = N.zeros((len(time),), N.Float) eisf = 0. random_vectors = Random.randomDirections(random_vectors) for v in random_vectors: phase = N.exp(-1.j*q*N.dot(r, v.array)) faat = N.zeros((natoms, len(time)), N.Float) eisf_sum = N.zeros((natoms,), N.Float) for i in range(first_mode, self.nmodes): irt = self.rawMode(i).inv_relaxation_time d = q * N.repeat((self.rawMode(i)*v).array, mask.array) \ / N.sqrt(friction) ft = (N.exp(-irt*time)-1.)/irt N.add(faat, d[:, N.NewAxis]**2 * ft[N.NewAxis, :], faat) N.add(eisf_sum, -d**2/irt, eisf_sum) N.add(finc, N.sum(weights_inc[:, N.NewAxis] * N.exp(kT*faat), 0), finc) eisf = eisf + N.sum(weights_inc*N.exp(kT*eisf_sum)) return InterpolatingFunction((time,), finc/len(random_vectors))
def coherentScatteringFunction(self, q, time_range=(0., None, None), 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_coherent') mask = subset.booleanMask() weights = N.repeat(weights.array, mask.array) weights = weights / N.sqrt(N.add.reduce(weights * weights)) friction = N.repeat(self.friction.array, mask.array) r = N.repeat(self.universe.configuration().array, mask.array) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3. / self.rawMode(first_mode).inv_relaxation_time if step is None: step = (last - first) / 300. time = N.arange(first, last, step) natoms = subset.numberOfAtoms() kT = Units.k_B * self.temperature fcoh = N.zeros((len(time), ), N.Complex) random_vectors = Random.randomDirections(random_vectors) for v in random_vectors: phase = N.exp(-1.j * q * N.dot(r, v.array)) for ai in range(natoms): fbt = N.zeros((natoms, len(time)), N.Float) for i in range(first_mode, self.nmodes): irt = self.rawMode(i).inv_relaxation_time d = q * N.repeat((self.rawMode(i)*v).array, mask.array) \ / N.sqrt(friction) ft = N.exp(-irt * time) / irt N.add(fbt, d[ai] * d[:, N.NewAxis] * ft[N.NewAxis, :], fbt) N.add(fbt, (-0.5 / irt) * (d[ai]**2 + d[:, N.NewAxis]**2), fbt) N.add( fcoh, weights[ai] * phase[ai] * N.dot(weights * N.conjugate(phase), N.exp(kT * fbt)), fcoh) return InterpolatingFunction((time, ), fcoh.real / len(random_vectors))
def incoherentScatteringFunction(self, q, time_range=(0., None, None), subset=None, random_vectors=15, first_mode=6): if subset is None: subset = self.universe mask = subset.booleanMask() weights_inc = self.universe.getParticleScalar('b_incoherent') weights_inc = N.repeat(weights_inc.array**2, mask.array) weights_inc = weights_inc / N.add.reduce(weights_inc) friction = N.repeat(self.friction.array, mask.array) mass = N.repeat(self.universe.masses().array, mask.array) r = N.repeat(self.universe.configuration().array, mask.array) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3. / self.weighedMode(first_mode).inv_relaxation_time if step is None: step = (last - first) / 300. time = N.arange(first, last, step) natoms = subset.numberOfAtoms() kT = Units.k_B * self.temperature finc = N.zeros((len(time), ), N.Float) eisf = 0. random_vectors = Random.randomDirections(random_vectors) for v in random_vectors: phase = N.exp(-1.j * q * N.dot(r, v.array)) faat = N.zeros((natoms, len(time)), N.Float) eisf_sum = N.zeros((natoms, ), N.Float) for i in range(first_mode, self.nmodes): irt = self.rawMode(i).inv_relaxation_time d = q * N.repeat((self.rawMode(i)*v).array, mask.array) \ / N.sqrt(friction) ft = (N.exp(-irt * time) - 1.) / irt N.add(faat, d[:, N.NewAxis]**2 * ft[N.NewAxis, :], faat) N.add(eisf_sum, -d**2 / irt, eisf_sum) N.add(finc, N.sum(weights_inc[:, N.NewAxis] * N.exp(kT * faat), 0), finc) eisf = eisf + N.sum(weights_inc * N.exp(kT * eisf_sum)) return InterpolatingFunction((time, ), finc / len(random_vectors))
def incoherentScatteringFunction(self, q, time_range = (0., None, None), subset=None, random_vectors=15, first_mode = 6): """ :param q: the angular wavenumber :type q: float :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_range: tuple :param subset: the subset of the universe used in the calculation (default: the whole universe) :type subset: :class:`~MMTK.Collections.GroupOfAtoms` :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 Incoherent Scattering Function as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if subset is None: subset = self.universe mask = subset.booleanMask() weights_inc = self.universe.getParticleScalar('b_incoherent') weights_inc = N.repeat(weights_inc.array**2, mask.array) weights_inc = weights_inc/N.add.reduce(weights_inc) friction = N.repeat(self.friction.array, mask.array) mass = N.repeat(self.universe.masses().array, mask.array) r = N.repeat(self.universe.configuration().array, mask.array) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3./self.weighedMode(first_mode).inv_relaxation_time if step is None: step = (last-first)/300. time = N.arange(first, last, step) natoms = subset.numberOfAtoms() kT = Units.k_B*self.temperature finc = N.zeros((len(time),), N.Float) eisf = 0. random_vectors = Random.randomDirections(random_vectors) for v in random_vectors: phase = N.exp(-1.j*q*N.dot(r, v.array)) faat = N.zeros((natoms, len(time)), N.Float) eisf_sum = N.zeros((natoms,), N.Float) for i in range(first_mode, self.nmodes): irt = self.rawMode(i).inv_relaxation_time d = q * N.repeat((self.rawMode(i)*v).array, mask.array) \ / N.sqrt(friction) ft = (N.exp(-irt*time)-1.)/irt N.add(faat, d[:, N.NewAxis]**2 * ft[N.NewAxis, :], faat) N.add(eisf_sum, -d**2/irt, eisf_sum) N.add(finc, N.sum(weights_inc[:, N.NewAxis] * N.exp(kT*faat), 0), finc) eisf = eisf + N.sum(weights_inc*N.exp(kT*eisf_sum)) return InterpolatingFunction((time,), finc/len(random_vectors))
def incoherentScatteringFunction(self, q, time_range = (0., None, None), subset=None, weights=None, tau=None, random_vectors=15, first_mode = 6): """ :param q: the angular wavenumber :type q: float :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_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 tau: the relaxation time of an exponential damping factor (default: no damping) :type tau: float :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 Incoherent Scattering Function as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if self.temperature is None: raise ValueError("no temperature available") 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 = (time_range + (None, None))[:3] if last is None: last = 20./self[first_mode].frequency if step is None: step = (last-first)/400. time = N.arange(first, last, step) if tau is None: damping = 1. else: damping = N.exp(-(time/tau)**2) finc = N.zeros(time.shape, N.Float) random_vectors = MMTK.Random.randomDirections(random_vectors) for v in random_vectors: for a in subset.atomList(): msd = 0. for i in range(first_mode, self.nmodes): mode = self[i] d = (v*mode[a])**2 omega = 2.*N.pi*mode.frequency msd = msd+d*(1.-damping*N.cos(omega*time)) N.add(finc, weights[a]*N.exp(-q*q*msd), finc) return InterpolatingFunction((time,), finc/len(random_vectors))
def finalize(self): """Finalizes the calculations (e.g. averaging the total term, output files creations ...). """ if self.architecture == 'monoprocessor': t = self.trajectory else: # Load the whole trajectory set. t = Trajectory(None, self.trajectoryFilename, 'r') orderedAtoms = sorted(t.universe.atomList(), key=operator.attrgetter('index')) groups = [ Collection([orderedAtoms[ind] for ind in g]) for g in self.group ] # 'freqencies' = 1D Numeric array. Frequencies at which the DOS was computed frequencies = N.arange(self.nFrames) / (2.0 * self.nFrames * self.dt) # The NetCDF output file is opened for writing. outputFile = NetCDFFile(self.output, 'w') outputFile.title = self.__class__.__name__ outputFile.jobinfo = self.information + '\nOutput file written on: %s\n\n' % asctime( ) # Dictionnary whose keys are of the form Gi where i is the group number # and the entries are the list of the index of the atoms building the group. comp = 1 for g in self.group: outputFile.jobinfo += 'Group %d: %s\n' % (comp, [index for index in g]) comp += 1 # Some dimensions are created. outputFile.createDimension('NFRAMES', self.nFrames) # Creation of the NetCDF output variables. # The time. TIMES = outputFile.createVariable('time', N.Float, ('NFRAMES', )) TIMES[:] = self.times[:] TIMES.units = 'ps' # The resolution function. RESOLUTIONFUNCTION = outputFile.createVariable('resolution_function', N.Float, ('NFRAMES', )) RESOLUTIONFUNCTION[:] = self.resolutionFunction[:] RESOLUTIONFUNCTION.units = 'unitless' # Creation of the NetCDF output variables. # The frequencies. FREQUENCIES = outputFile.createVariable('frequency', N.Float, ('NFRAMES', )) FREQUENCIES[:] = frequencies[:] FREQUENCIES.units = 'THz' OMEGAS = outputFile.createVariable('angular_frequency', N.Float, ('NFRAMES', )) OMEGAS[:] = 2.0 * N.pi * frequencies[:] OMEGAS.units = 'rad ps-1' avacfTotal = N.zeros((self.nFrames), typecode=N.Float) adosTotal = N.zeros((self.nFrames), typecode=N.Float) comp = 1 totalMass = 0.0 for g in groups: AVACF = outputFile.createVariable('avacf-group%s' % comp, N.Float, ('NFRAMES', )) AVACF[:] = self.AVACF[comp][:] AVACF.units = 'rad^2*ps^-2' N.add(avacfTotal, self.AVACF[comp], avacfTotal) ADOS = outputFile.createVariable('ados-group%s' % comp, N.Float, ('NFRAMES', )) ADOS[:] = self.ADOS[comp][:] ADOS.units = 'rad^2*ps^-1' N.add(adosTotal, g.mass() * self.ADOS[comp], adosTotal) comp += 1 totalMass += g.mass() adosTotal *= 0.5 * self.dt / (self.nGroups * totalMass) AVACF = outputFile.createVariable('avacf-total', N.Float, ('NFRAMES', )) AVACF[:] = avacfTotal AVACF.units = 'rad^2*ps^-2' ADOS = outputFile.createVariable('ados-total', N.Float, ('NFRAMES', )) ADOS[:] = adosTotal ADOS.units = 'rad^2*ps^-1' asciiVar = sorted(outputFile.variables.keys()) outputFile.close() self.toPlot = { 'netcdf': self.output, 'xVar': 'angular_frequency', 'yVar': 'ados-total' } # Create an ASCII version of the NetCDF output file. convertNetCDFToASCII(inputFile = self.output,\ outputFile = os.path.splitext(self.output)[0] + '.cdl',\ variables = asciiVar)
def __init__(self, trajectory, object, first=0, last=None, skip=1, reference = None): self.trajectory = trajectory universe = trajectory.universe if last is None: last = len(trajectory) first_conf = trajectory.configuration[first] offset = universe.contiguousObjectOffset([object], first_conf, True) if reference is None: reference = first_conf reference = universe.contiguousObjectConfiguration([object], reference) steps = (last-first+skip-1)/skip mass = object.mass() ref_cms = object.centerOfMass(reference) atoms = object.atomList() possq = N.zeros((steps,), N.Float) cross = N.zeros((steps, 3, 3), N.Float) rcms = N.zeros((steps, 3), N.Float) # cms of the CONTIGUOUS object made of CONTINUOUS atom trajectories for a in atoms: r = trajectory.readParticleTrajectory(a, first, last, skip, "box_coordinates").array w = a._mass/mass N.add(rcms, w*r, rcms) if offset is not None: N.add(rcms, w*offset[a].array, rcms) # relative coords of the CONTIGUOUS reference r_ref = N.zeros((len(atoms), 3), N.Float) for a in range(len(atoms)): r_ref[a] = atoms[a].position(reference).array - ref_cms.array # main loop: storing data needed to fill M matrix for a in range(len(atoms)): r = trajectory.readParticleTrajectory(atoms[a], first, last, skip, "box_coordinates").array r = r - rcms # (a-b)**2 != a**2 - b**2 if offset is not None: N.add(r, offset[atoms[a]].array,r) trajectory._boxTransformation(r, r) w = atoms[a]._mass/mass N.add(possq, w*N.add.reduce(r*r, -1), possq) N.add(possq, w*N.add.reduce(r_ref[a]*r_ref[a],-1), possq) N.add(cross, w*r[:,:,N.NewAxis]*r_ref[N.NewAxis, a,:],cross) self.trajectory._boxTransformation(rcms, rcms) # filling matrix M (formula no 40) k = N.zeros((steps, 4, 4), N.Float) k[:, 0, 0] = -cross[:, 0, 0]-cross[:, 1, 1]-cross[:, 2, 2] k[:, 0, 1] = cross[:, 1, 2]-cross[:, 2, 1] k[:, 0, 2] = cross[:, 2, 0]-cross[:, 0, 2] k[:, 0, 3] = cross[:, 0, 1]-cross[:, 1, 0] k[:, 1, 1] = -cross[:, 0, 0]+cross[:, 1, 1]+cross[:, 2, 2] k[:, 1, 2] = -cross[:, 0, 1]-cross[:, 1, 0] k[:, 1, 3] = -cross[:, 0, 2]-cross[:, 2, 0] k[:, 2, 2] = cross[:, 0, 0]-cross[:, 1, 1]+cross[:, 2, 2] k[:, 2, 3] = -cross[:, 1, 2]-cross[:, 2, 1] k[:, 3, 3] = cross[:, 0, 0]+cross[:, 1, 1]-cross[:, 2, 2] del cross for i in range(1, 4): for j in range(i): k[:, i, j] = k[:, j, i] N.multiply(k, 2., k) for i in range(4): N.add(k[:,i,i], possq, k[:,i,i]) del possq quaternions = N.zeros((steps, 4), N.Float) fit = N.zeros((steps,), N.Float) from Scientific.LA import eigenvectors for i in range(steps): e, v = eigenvectors(k[i]) j = N.argmin(e) if e[j] < 0.: fit[i] = 0. else: fit[i] = N.sqrt(e[j]) if v[j,0] < 0.: quaternions[i] = -v[j] # eliminate jumps else: quaternions[i] = v[j] self.fit = fit self.cms = rcms self.quaternions = quaternions
def incoherentScatteringFunction(self, q, time_range=(0., None, None), subset=None, weights=None, tau=None, random_vectors=15, first_mode=6): """ :param q: the angular wavenumber :type q: float :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_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 tau: the relaxation time of an exponential damping factor (default: no damping) :type tau: float :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 Incoherent Scattering Function as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if self.temperature is None: raise ValueError("no temperature available") 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 = (time_range + (None, None))[:3] if last is None: last = 20. / self[first_mode].frequency if step is None: step = (last - first) / 400. time = N.arange(first, last, step) if tau is None: damping = 1. else: damping = N.exp(-(time / tau)**2) finc = N.zeros(time.shape, N.Float) random_vectors = MMTK.Random.randomDirections(random_vectors) for v in random_vectors: for a in subset.atomList(): msd = 0. for i in range(first_mode, self.nmodes): mode = self[i] d = (v * mode[a])**2 omega = 2. * N.pi * mode.frequency msd = msd + d * (1. - damping * N.cos(omega * time)) N.add(finc, weights[a] * N.exp(-q * q * msd), finc) return InterpolatingFunction((time, ), finc / len(random_vectors))
def coherentScatteringFunction(self, q, time_range=(0., None, None), subset=None, weights=None, random_vectors=15, first_mode=6): """ :param q: the angular wavenumber :type q: float :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_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: coherent 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 Coherent Scattering Function as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if subset is None: subset = self.universe if weights is None: weights = self.universe.getParticleScalar('b_coherent') mask = subset.booleanMask() weights = N.repeat(weights.array, mask.array) weights = weights / N.sqrt(N.add.reduce(weights * weights)) friction = N.repeat(self.friction.array, mask.array) r = N.repeat(self.universe.configuration().array, mask.array) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3. / self.rawMode(first_mode).inv_relaxation_time if step is None: step = (last - first) / 300. time = N.arange(first, last, step) natoms = subset.numberOfAtoms() kT = Units.k_B * self.temperature fcoh = N.zeros((len(time), ), N.Complex) random_vectors = Random.randomDirections(random_vectors) for v in random_vectors: phase = N.exp(-1.j * q * N.dot(r, v.array)) for ai in range(natoms): fbt = N.zeros((natoms, len(time)), N.Float) for i in range(first_mode, self.nmodes): irt = self.rawMode(i).inv_relaxation_time d = q * N.repeat((self.rawMode(i)*v).array, mask.array) \ / N.sqrt(friction) ft = N.exp(-irt * time) / irt N.add(fbt, d[ai] * d[:, N.NewAxis] * ft[N.NewAxis, :], fbt) N.add(fbt, (-0.5 / irt) * (d[ai]**2 + d[:, N.NewAxis]**2), fbt) N.add( fcoh, weights[ai] * phase[ai] * N.dot(weights * N.conjugate(phase), N.exp(kT * fbt)), fcoh) return InterpolatingFunction((time, ), fcoh.real / len(random_vectors))
def incoherentScatteringFunction(self, q, time_range=(0., None, None), subset=None, random_vectors=15, first_mode=6): """ :param q: the angular wavenumber :type q: float :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_range: tuple :param subset: the subset of the universe used in the calculation (default: the whole universe) :type subset: :class:~MMTK.Collections.GroupOfAtoms :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 Incoherent Scattering Function as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if subset is None: subset = self.universe mask = subset.booleanMask() weights_inc = self.universe.getParticleScalar('b_incoherent') weights_inc = N.repeat(weights_inc.array**2, mask.array) weights_inc = weights_inc / N.add.reduce(weights_inc) friction = N.repeat(self.friction.array, mask.array) mass = N.repeat(self.universe.masses().array, mask.array) r = N.repeat(self.universe.configuration().array, mask.array) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3. / self.weighedMode(first_mode).inv_relaxation_time if step is None: step = (last - first) / 300. time = N.arange(first, last, step) natoms = subset.numberOfAtoms() kT = Units.k_B * self.temperature finc = N.zeros((len(time), ), N.Float) eisf = 0. random_vectors = Random.randomDirections(random_vectors) for v in random_vectors: phase = N.exp(-1.j * q * N.dot(r, v.array)) faat = N.zeros((natoms, len(time)), N.Float) eisf_sum = N.zeros((natoms, ), N.Float) for i in range(first_mode, self.nmodes): irt = self.rawMode(i).inv_relaxation_time d = q * N.repeat((self.rawMode(i)*v).array, mask.array) \ / N.sqrt(friction) ft = (N.exp(-irt * time) - 1.) / irt N.add(faat, d[:, N.NewAxis]**2 * ft[N.NewAxis, :], faat) N.add(eisf_sum, -d**2 / irt, eisf_sum) N.add(finc, N.sum(weights_inc[:, N.NewAxis] * N.exp(kT * faat), 0), finc) eisf = eisf + N.sum(weights_inc * N.exp(kT * eisf_sum)) return InterpolatingFunction((time, ), finc / len(random_vectors))
def coherentScatteringFunction(self, q, time_range = (0., None, None), subset=None, weights=None, random_vectors=15, first_mode = 6): """ :param q: the angular wavenumber :type q: float :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_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: coherent 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 Coherent Scattering Function as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if subset is None: subset = self.universe if weights is None: weights = self.universe.getParticleScalar('b_coherent') mask = subset.booleanMask() weights = N.repeat(weights.array, mask.array) weights = weights/N.sqrt(N.add.reduce(weights*weights)) friction = N.repeat(self.friction.array, mask.array) r = N.repeat(self.universe.configuration().array, mask.array) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3./self.rawMode(first_mode).inv_relaxation_time if step is None: step = (last-first)/300. time = N.arange(first, last, step) natoms = subset.numberOfAtoms() kT = Units.k_B*self.temperature fcoh = N.zeros((len(time),), N.Complex) random_vectors = Random.randomDirections(random_vectors) for v in random_vectors: phase = N.exp(-1.j*q*N.dot(r, v.array)) for ai in range(natoms): fbt = N.zeros((natoms, len(time)), N.Float) for i in range(first_mode, self.nmodes): irt = self.rawMode(i).inv_relaxation_time d = q * N.repeat((self.rawMode(i)*v).array, mask.array) \ / N.sqrt(friction) ft = N.exp(-irt*time)/irt N.add(fbt, d[ai] * d[:, N.NewAxis] * ft[N.NewAxis, :], fbt) N.add(fbt, (-0.5/irt) * (d[ai]**2 + d[:, N.NewAxis]**2), fbt) N.add(fcoh, weights[ai]*phase[ai] * N.dot(weights*N.conjugate(phase), N.exp(kT*fbt)), fcoh) return InterpolatingFunction((time,), fcoh.real/len(random_vectors))