def FromSystem(selfClass, system, electronicState1, electronicState2, method="GP"): """Constructor given a system.""" # . Basic setup. system.electronicState = electronicState1 self = super(SEAMObjectiveFunction, selfClass).FromSystem(system) self.method = method # . Define the first system. self.system1 = self.system # . Define the second system without coordinates. coordinates3 = self.system1.coordinates3 symmetryParameters = self.system1.symmetryParameters self.system1.coordinates3 = None self.system1.symmetryParameters = None self.system2 = Clone(self.system1) self.system2.electronicState = electronicState2 # . Reset the coordinates for both systems so that they are the same. self.system1.coordinates3 = coordinates3 self.system2.coordinates3 = coordinates3 if symmetryParameters is not None: self.system1.symmetryParameters = symmetryParameters self.system2.symmetryParameters = symmetryParameters # . Allocate space. self.g1 = Real1DArray.WithExtent(len(self)) self.g2 = Real1DArray.WithExtent(len(self)) # . Finish up. return self
def AddLinearConstraint ( self, constraint ): """Add a linear constraint.""" if len ( constraint ) != self.nvariables: raise ValueError ( "Invalid linear constraint length." ) # . Orthogonalize to existing constraints. if self.linearVectors is not None: constraint = Clone ( constraint ) self.linearVectors.ProjectOutOfArray ( constraint ) # . Check to see if the constraint is valid. cnorm2 = constraint.Norm2 ( ) if cnorm2 > 1.0e-10: constraint.Scale ( 1.0 / cnorm2 ) # . Allocate space for new constraints. ncolumns = 1 if self.linearVectors is not None: ncolumns += self.linearVectors.columns newconstraints = Real2DArray.WithExtents ( len ( constraint ), ncolumns ) # . Copy over constraints. if self.linearVectors is not None: for r in range ( self.linearVectors.rows ): for c in range ( self.linearVectors.columns ): newconstraints[r,c] = self.linearVectors[r,c] for r in range ( len ( constraint ) ): newconstraints[r,ncolumns-1] = constraint[r] self.linearVectors = newconstraints # . Determine the linear scalars. self.linearScalars = Real1DArray.WithExtent ( self.linearVectors.columns ) reference = Real1DArray.WithExtent ( self.linearVectors.rows ) if self.rtReference is None: self.system.coordinates3.CopyToArray ( reference ) else: self.rtReference.CopyToArray ( reference ) self.linearVectors.VectorMultiply ( reference, self.linearScalars, 1.0, 0.0, transpose = True ) # . Reset the number of degrees of freedom. self.degreesOfFreedom = self.nvariables - len ( self.linearScalars )
def WHAM_Bootstrapping(paths, **keywordArguments): """Solve the WHAM equations and estimate errors using bootstrapping.""" # . Get specific options. options = dict(keywordArguments) log = options.get("log", logFile) minimizer = options.pop("minimizerFunction", WHAM_ConjugateGradientMinimize) resamples = options.pop("resamples", _Default_Resamples) # . Initial minimization - it must succeed to proceed. results = minimizer(paths, **dict(options)) if results["Converged"]: # . Reset options for resampling. options["handler"] = results.pop("Handler") options["histogram"] = Clone(results.pop("Unreduced Histogram")) options["log"] = None # . Resampling minimizations. pmfs = [] for r in range(resamples): localResults = minimizer(paths, **dict(options)) if localResults["Converged"]: pmfs.append(localResults.pop("PMF")) # . Process results. numberOfResamples = len(pmfs) results.update({ "Number Of Resamples": numberOfResamples, "PMFs": pmfs }) if numberOfResamples > 1: # . Gather some counters. lowerIndex = int(math.ceil( 0.5 * _Alpha * float(numberOfResamples))) - 1 upperIndex = int( math.floor( (1.0 - 0.5 * _Alpha) * float(numberOfResamples))) - 1 # . Do statistics. originalPMF = results["PMF"] lowerLimit = Real1DArray.WithExtent(len(originalPMF)) standardError = Real1DArray.WithExtent(len(originalPMF)) upperLimit = Real1DArray.WithExtent(len(originalPMF)) work = Real1DArray.WithExtent(numberOfResamples) for b in range(len(originalPMF)): for (r, pmf) in enumerate(pmfs): work[r] = pmf[b] work.Sort() m = originalPMF[b] lowerLimit[b] = 2.0 * m - work[upperIndex] upperLimit[b] = 2.0 * m - work[lowerIndex] standardError[b] = Statistics(work).standardDeviation # . Printing. data = (("Standard Error", standardError), ("Lower 95% Confidence Interval", lowerLimit), ("Upper 95% Confidence Interval", upperLimit)) _BootstrappingSummary(results["Histogram"], log, numberOfResamples, originalPMF, standardError, lowerLimit, upperLimit) # . Finish up. for (key, value) in data: results[key] = value return results
def Initialize(self, pressure=_Default_Pressure, temperature=_Default_Temperature): """Create the arrays needed for the WHAM equations.""" # . Initialization. handler = self.handler histogram = self.histogram if (handler is not None) and (histogram is not None): p0 = pressure * UNITS_PRESSURE_ATMOSPHERES_TO_KILOJOULES_PER_MOLE t0 = temperature * CONSTANT_MOLAR_GAS * 1.0e-3 # . Gather pressures and temperatures. pressures = [] temperatures = [] for (p, t) in zip(handler.pressures, handler.temperatures): if p is None: p = pressure if t is None: t = temperature pressures.append( p * UNITS_PRESSURE_ATMOSPHERES_TO_KILOJOULES_PER_MOLE) temperatures.append(t * CONSTANT_MOLAR_GAS * 1.0e-3) # . Create c which in the most general case is Exp ( - ( (Bj - B0) * U_i + (Bj*Pj - B0*P0) * V_i + Bj * SC_ji ) ). c = Real2DArray.WithExtents(handler.numberOfWindows, histogram.bins) c.Set(0.0) for (i, midPoint) in enumerate(histogram.BinMidPointIterator()): # . Energy. if handler.hasEnergy: e = midPoint.pop(0) for (j, tj) in enumerate(temperatures): c[j, i] += e * (1.0 / tj - 1.0 / t0) # . Volume. if handler.hasVolume: v = midPoint.pop(0) for (j, pj) in enumerate(pressures): c[j, i] += v * (pj / tj - p0 / t0) # . Soft constraints. for (j, tj) in enumerate(temperatures): for (s, model) in zip(midPoint, handler.energyModels[j]): c[j, i] += model.Energy(s)[0] / tj c.Scale(-1.0) c.Exp() # . Create the points arrays. m = Real1DArray.WithExtent(histogram.bins) m.Set(0.0) n = Real1DArray.WithExtent(handler.numberOfWindows) n.Set(0.0) for (i, v) in enumerate(histogram.counts): m[i] = float(v) for (i, v) in enumerate(handler.windowPoints): n[i] = float(v) # . Save all. self.pointsPerBin = m self.pointsPerWindow = n self.pressure = pressure self.temperature = temperature self.temperatureFactor = t0 self.weightMatrix = c
def Allocate(self): """Allocate arrays.""" if (self.handler is not None) and (self.histogram is not None): self.energies = Real1DArray.WithExtent( self.handler.numberOfWindows) self.energies.Set(1.0) self.probabilities = Real1DArray.WithExtent(self.histogram.bins) self.probabilities.Set(1.0) self.workE = Real1DArray.WithExtent(self.handler.numberOfWindows) self.workE.Set(0.0) self.workP = Real1DArray.WithExtent(self.histogram.bins) self.workP.Set(0.0) self.histogram.Normalize(self.probabilities)
def AtomicCharges(self, qcChargeModel=None, qcAtomsOnly=False, spinDensities=False): """Calculate the partial charges for the atoms.""" charges = None if self.energyModel is not None: em = self.energyModel if (em.mmModel is not None) and (not qcAtomsOnly): if spinDensities: charges = Real1DArray.WithExtent(em.mmAtoms.size) charges.Set(0.0e+00) else: charges = em.mmAtoms.AtomicCharges() if em.qcModel is not None: qccharges = em.qcModel.AtomicCharges( self.configuration, chargeModel=qcChargeModel, spinDensities=spinDensities) if charges is None: charges = qccharges else: for (index, q) in zip(em.qcAtoms, qccharges): charges[index] += q return charges
def VelocitiesAssign ( self, temperature, normalDeviateGenerator = None ): """Set up the velocities for the system at a particular temperature. If |temperature| is None the velocities must already exist. """ # . Check for an existing set of velocities of the correct size. velocities = self.system.configuration.__dict__.get ( "velocities", None ) QASSIGN = ( velocities is None ) or ( ( velocities is not None ) and ( len ( velocities ) != self.nvariables ) ) # . Assign velocities. if QASSIGN: if temperature is None: raise ValueError ( "Velocities need to be assigned for the system but a temperature has not been specified." ) else: # . Get the generator. if normalDeviateGenerator is None: normalDeviateGenerator = NormalDeviateGenerator.WithRandomNumberGenerator ( RandomNumberGenerator.WithRandomSeed ( ) ) # . Get the velocities. sigma = _MS_TO_APS * math.sqrt ( CONSTANT_BOLTZMANN * temperature / CONSTANT_ATOMIC_MASS ) velocities = Real1DArray.WithExtent ( self.NumberOfVariables ( ) ) normalDeviateGenerator.NextStandardDeviates ( velocities ) velocities.Scale ( ( sigma ) ) self.system.configuration.velocities = velocities # . Project out linear constraints. if self.linearVectors is not None: self.linearVectors.ProjectOutOfArray ( velocities ) # . Scale velocities if necessary (even for assigned velocities). if temperature is not None: ( ke, tactual ) = self.Temperature ( velocities ) velocities.Scale ( math.sqrt ( temperature / tactual ) )
def ParseAtomSection(self): """Parse the ATOM section.""" if hasattr(self, "natoms"): atomicCharges = Real1DArray.WithExtent(self.natoms) atomicCharges.Set(0.0) atomicNumbers = [] atomNames = [] xyz = Coordinates3.WithExtent(self.natoms) xyz.Set(0.0) for i in range(self.natoms): items = self.GetTokens(converters=[ int, None, float, float, float, None, None, None, float ]) if len(items) < 6: self.Warning("Invalid ATOM line.", True) else: atomicNumber = PeriodicTable.AtomicNumber(items[1]) if atomicNumber <= 0: atomicNumber = self.AtomicNumberFromAtomType(items[5]) atomicNumbers.append(atomicNumber) atomNames.append(items[1]) xyz[i, 0] = items[2] xyz[i, 1] = items[3] xyz[i, 2] = items[4] if len(items) >= 9: atomicCharges[i] = items[8] self.atomicCharges = atomicCharges self.atomicNumbers = atomicNumbers self.atomNames = atomNames self.xyz = xyz else: self.Warning("Unknown number of atoms in molecule.", True)
def PathSummary ( self, log = logFile ): """Output a path summary.""" if LogFileActive ( log ): # . Calculate all data for the current path. variables = Real1DArray.WithExtent ( self.NumberOfVariables ( ) ) self.VariablesGet ( variables ) self.Function ( variables ) # . Get the energies of the first and last structures. self.energies[0:0] = [ self.objectiveFunction.Function ( self.x0 ) ] self.energies.append ( self.objectiveFunction.Function ( self.xn ) ) # . Modify the distance lists. self.distances1.append ( None ) self.distances2.extend ( [ None, None ] ) # . Output. table = log.GetTable ( columns = [ 10, 20, 20, 20 ] ) table.Start ( ) table.Title ( "Path Summary" ) table.Heading ( "Structure" ) table.Heading ( "Energy" ) table.Heading ( "Dist(i,i+1)" ) table.Heading ( "Dist(i,i+2)" ) for ( i, ( e, d1, d2 ) ) in enumerate ( zip ( self.energies, self.distances1, self.distances2 ) ): table.Entry ( "{:d}" .format ( i ) ) table.Entry ( "{:.3f}".format ( e ) ) if d1 is None: table.Entry ( "" ) else: table.Entry ( "{:.3f}".format ( d1 ) ) if d2 is None: table.Entry ( "" ) else: table.Entry ( "{:.3f}".format ( d2 ) ) table.Stop ( )
def VariablesAllocate ( self ): """Return a variables object of the correct size.""" if self.NumberOfVariables ( ) > 0: variables = Real1DArray.WithExtent ( self.NumberOfVariables ( ) ) variables.Set ( 0.0 ) return variables else: return None
def VariablesAllocate ( self ): """Return an object to hold the variables.""" if self.NumberOfVariables ( ) > 0: variables = Real1DArray.WithExtent ( self.NumberOfVariables ( ) ) variables.Set ( 0.0 ) return variables else: return None
def Setup ( selfClass, qcAtoms, electronicState, nbState, job, scratch, deleteJobFiles = False, useRandomJob = False, useRandomScratch = False ): """Set up a state given the relevant data.""" self = selfClass ( ) # . QC atoms. # . Basic attributes. self.charge = electronicState.charge self.multiplicity = electronicState.multiplicity self.numberOfQCAtoms = len ( qcAtoms ) # . File paths. if useRandomJob: job = RandomString ( ) if useRandomScratch: self.scratchPath = os.path.join ( scratch, RandomString ( ) ) if not os.path.exists ( self.scratchPath ): os.mkdir ( self.scratchPath ) self.jobPath = os.path.join ( self.scratchPath, job ) else: self.scratchPath = None self.jobPath = os.path.join ( scratch, job ) self.engradPath = self.jobPath + ".engrad" self.inputPath = self.jobPath + ".inp" self.outputPath = self.jobPath + ".log" self.pcgradPath = self.jobPath + ".pcgrad" self.pcPath = self.jobPath + ".pc" # . Options. self.deleteJobFiles = deleteJobFiles # . Symbols. atomicNumbers = qcAtoms.GetAtomicNumbers ( ) self.symbols = [] for i in atomicNumbers: self.symbols.append ( PeriodicTable.Symbol ( i ) ) # . Array attributes. self.qcCoordinates3 = Coordinates3.WithExtent ( self.numberOfQCAtoms ) ; self.qcCoordinates3.Set ( 0.0 ) self.dipole = Vector3.Null ( ) self.qcGradients3 = Coordinates3.WithExtent ( self.numberOfQCAtoms ) ; self.qcGradients3.Set ( 0.0 ) self.chelpgCharges = Real1DArray.WithExtent ( self.numberOfQCAtoms ) ; self.chelpgCharges.Set ( 0.0 ) self.lowdinCharges = Real1DArray.WithExtent ( self.numberOfQCAtoms ) ; self.lowdinCharges.Set ( 0.0 ) self.lowdinSpins = Real1DArray.WithExtent ( self.numberOfQCAtoms ) ; self.lowdinSpins.Set ( 0.0 ) self.mullikenCharges = Real1DArray.WithExtent ( self.numberOfQCAtoms ) ; self.mullikenCharges.Set ( 0.0 ) self.mullikenSpins = Real1DArray.WithExtent ( self.numberOfQCAtoms ) ; self.mullikenSpins.Set ( 0.0 ) # . Orbital data. self.H**O = -1 self.LUMO = -1 self.orbitalEnergies = [] # . NB state. self.nbState = nbState return self
def FromOptions(selfClass, alphas, gradientsAlpha, hessianAlpha, minimumCoefficient, numberOfSets=1): """Constructor from options.""" self = selfClass() self.alphas = alphas self.gradientsAlpha = gradientsAlpha self.hessianAlpha = hessianAlpha self.minimumCoefficient = minimumCoefficient self.numberOfSets = numberOfSets self.numberOfVariables = len(self.alphas) self.numberOfVariablesPerSet = self.numberOfVariables // self.numberOfSets self.work = Real1DArray.WithExtent(self.numberOfVariables) self.workA = Real1DArray.WithExtent(self.numberOfVariables) self.workS = Real1DArray.WithExtent(self.numberOfVariables) self.workT = Real1DArray.WithExtent(self.numberOfVariables) return self
def CovarianceMatrix(*arguments, **keywordArguments): """Calculate the covariance matrix for selected particles.""" # . Initialization. covariance = None # . Get the trajectory and associated system data. (system, trajectories) = _GetSystemAndTrajectoriesFromArguments(*arguments) # . Get the selection and the size of the problem. selection = keywordArguments.pop("selection", None) if selection is None: selection = Selection.FromIterable(range(len(system.atoms))) n = 3 * len(selection) # . Get the average positions. averagePositions = keywordArguments.pop("averagePositions", None) if averagePositions is None: averagePositions = AveragePositions(trajectories, selection=selection) _CheckForUnknownOptions(keywordArguments) # . Continue processing. if (n > 0) and (averagePositions is not None): # . Allocate space. covariance = SymmetricMatrix.WithExtent(n) covariance.Set(0.0) displacement = Real1DArray.WithExtent(n) displacement.Set(0.0) # . Loop over trajectory frames. numberFrames = 0 for trajectory in trajectories: trajectory.ReadHeader() while trajectory.RestoreOwnerData(): frame = system.coordinates3 for (p, i) in enumerate(selection): displacement[3 * p] = frame[i, 0] - averagePositions[p, 0] displacement[3 * p + 1] = frame[i, 1] - averagePositions[p, 1] displacement[3 * p + 2] = frame[i, 2] - averagePositions[p, 2] for i in range(n): dI = displacement[i] for j in range(i + 1): covariance[i, j] += (dI * displacement[j]) trajectory.ReadFooter() trajectory.Close() numberFrames += len(trajectory) # . Scale. if numberFrames > 0: covariance.Scale(1.0 / float(numberFrames)) return covariance
def GetPMF(self): """Calculate the PMF.""" if (self.reducedHistogram is not None) and (self.reducedProbabilities is not None): pmf = Real1DArray.WithExtent(self.reducedHistogram.bins) pmf.Set(0.0) self.reducedProbabilities.CopyTo(pmf) pmf.Ln() pmf.Scale(-self.temperatureFactor) v = min(pmf) pmf.AddScalar(-v) self.pmf = pmf
def _GetHardSphereRadii(atoms): """Get the hard-sphere radii for the atoms.""" hsradii = Real1DArray.WithExtent(len(atoms)) hsradii.Set(0.0) atomicNumbers = atoms.GetItemAttributes("atomicNumber") for (i, atomicNumber) in enumerate(atomicNumbers): try: hsradii[i] = _HSRADII[atomicNumber] except: raise KeyError("Hard-sphere radius for element " + PeriodicTable.Symbol(atomicNumber) + " unknown.") return hsradii
def DefineWeights ( self ): """Define atom weights - use masses by default.""" self.atomWeights = self.system.atoms.GetItemAttributes ( "mass" ) if self.nvariables > 0: self.variableWeights = Real1DArray.WithExtent ( self.nvariables ) self.variableWeights.Set ( 0.0 ) if self.freeAtoms is None: indices = range ( len ( self.system.atoms ) ) else: indices = self.freeAtoms n = 0 for iatom in indices: w = math.sqrt ( self.atomWeights[iatom] ) for j in range ( 3 ): self.variableWeights[n] = w n += 1
def FindMaxima(self, computeStructures=False): """Find maxima along the path.""" if self.imageSpline is not None: if self.functionSplineA is None: self.BuildFunctionSplines() self.maximaA = self.functionSplineA.FindMaxima() self.maximaI = self.functionSplineI.FindMaxima() if computeStructures: structures = [] for (s, fs) in self.maximaI: v = Real1DArray.WithExtent( self.imageSpline.NumberOfSplines()) v.Set(0.0) self.imageSpline.Evaluate(s, f=v) structures.append((s, fs, v)) self.maximaIStructures = structures
def WriteBlock(self): """Write a block of data.""" if self.current > 0: # . The following is a fudge until slicing is handled properly. if self.current == self.blockSize: outdata = self.data else: outdata = Real1DArray.WithExtent(self.current * self.rank) for i in range(len(outdata)): outdata[i] = self.data[i] Pickle( os.path.join( self.path, _BlockPrefix + "{:d}".format(self.blocks) + _BlockPostfix), outdata) # . This is what it should be. # Pickle ( os.path.join ( self.path, _BlockPrefix + "{:d}".format ( self.blocks ) + _BlockPostfix, self.data[0:self.current*self.rank] ) self.blocks += 1 self.current = 0
def WriteHeader(self, pressure=None, temperature=None): """Write the trajectory header.""" # . Check for soft constraints. self.hasSoftConstraints = hasattr( self.owner.energyModel, "softConstraints") and (len( self.owner.energyModel.softConstraints) > 0) # . Check the volume option. self.hasVolume = self.hasVolume and hasattr(self.owner, "symmetry") # . Check that there will be data on the trajectory. if not (self.hasPotentialEnergy or self.hasSoftConstraints or self.hasVolume): raise ValueError("A soft-constraint trajectory contains no data.") # . Initialization. labels = [] models = [] # . Potential energy and volume. if self.hasPotentialEnergy: labels.append("Potential Energy") if self.hasVolume: labels.append("Volume") # . Soft constraints. if self.hasSoftConstraints: self.softConstraintLabels = sorted( self.owner.energyModel.softConstraints) for label in self.softConstraintLabels: models.append( self.owner.energyModel.softConstraints[label].energyModel) labels.extend(self.softConstraintLabels) # . Set up the header. header = { "labels": labels, "hasPotentialEnergy": self.hasPotentialEnergy, "hasVolume": self.hasVolume, "softConstraintEnergyModels": models } # . Optional information. if pressure is not None: header["pressure"] = pressure if temperature is not None: header["temperature"] = temperature # . Write out. Pickle(os.path.join(self.path, _HeaderName + _BlockPostfix), header) # . Setup the block for output. self.rank = len(labels) self.data = Real1DArray.WithExtent(self.rank * self.blockSize)
def ParseCharges(self): """Parse charges from a charge analysis.""" if (self.natoms > 0): nall = self.natoms + len(self.dummies) q = Real1DArray.WithExtent(self.natoms) q.Set(0.0) i = -1 n = -1 for iblock in range((nall + 4) // 5): self.GetLine() # Blank line. self.GetLine() # Atom heading. line = self.GetLine() tokens = line[7:].split() for token in tokens: n += 1 if n not in self.dummies: i += 1 q[i] = float(token) return q else: return None
def GetItemAttributes(self, attributeLabel, asDictionary=False, selection=None): """Return a sequence of item attributes.""" # . Initialization. data = None if (attributeLabel in Atom.defaultAttributes) or (attributeLabel in Element.defaultAttributes): if selection is None: indices = range(len(self)) else: indices = selection if indices is not None: # . Use a dictionary for non-default values only. if asDictionary: data = {} if attributeLabel in Atom.defaultAttributes: defaultValue = Atom.defaultAttributes[attributeLabel] else: defaultValue = Element.defaultAttributes[ attributeLabel] for i in indices: value = getattr(self[i], attributeLabel) if value != defaultValue: data[i] = value # . Use a sequence for all values. else: attribute = getattr(self[0], attributeLabel) # . Float attribute. if isinstance(attribute, (float)): data = Real1DArray.WithExtent(len(indices)) for (i, index) in enumerate(indices): data[i] = getattr(self[index], attributeLabel) # . Other attribute. else: data = [] for i in indices: data.append(getattr(self[i], attributeLabel)) return data
def __init__ ( self, system, trajectory, **keywordArguments ): """Constructor.""" # . Set defaults for all options. for ( key, value ) in self.__class__.attributes.iteritems ( ): setattr ( self, key, value ) # . Set values of keyword arguments. for ( key, value ) in keywordArguments.iteritems ( ): setattr ( self, key, value ) # . Define the system geometry object function. self.objectiveFunction = SystemGeometryObjectiveFunction.FromSystem ( system ) # . Define the trajectory - a check should be made here to ensure that the trajectory is a direct access one with coordinates! # . Reading and writing the header/footer is not required but maybe should be included for completeness? self.trajectory = trajectory # . Get some scratch space. self.g = Real1DArray.WithExtent ( self.objectiveFunction.NumberOfVariables ( ) ) self.gd = Real1DArray.WithExtent ( self.objectiveFunction.NumberOfVariables ( ) + self.NumberOfVariables ( ) ) self.x = Real1DArray.WithExtent ( self.objectiveFunction.NumberOfVariables ( ) ) self.x0 = Real1DArray.WithExtent ( self.objectiveFunction.NumberOfVariables ( ) ) self.xn = Real1DArray.WithExtent ( self.objectiveFunction.NumberOfVariables ( ) ) self.y = Real1DArray.WithExtent ( self.objectiveFunction.NumberOfVariables ( ) ) self.g.Set ( 0.0 ) self.gd.Set ( 0.0 ) self.x.Set ( 0.0 ) self.x0.Set ( 0.0 ) self.xn.Set ( 0.0 ) self.y.Set ( 0.0 ) # . Get the first structure on the trajectory. self.trajectory.RestoreOwnerData ( index = 0 ) self.objectiveFunction.VariablesGet ( self.x0 ) # . Make the first structure the reference structure. # . It is assumed that the remaining structures on the trajectory are already # . orientated with respect to this one. This is the case for trajectories # . from linear-interpolation or expansion or previous SAW calculations but # . may not be otherwise. if self.removeRotationTranslation: self.objectiveFunction.RemoveRotationTranslation ( reference = self.objectiveFunction.system.coordinates3 ) # . Get the last structure on the trajectory. self.trajectory.RestoreOwnerData ( index = self.trajectory.frames - 1 ) self.objectiveFunction.VariablesGet ( self.xn ) self.trajectories = []
def CoordinateFluctuations(*arguments, **keywordArguments): """Calculate the coordinate fluctuations for selected particles.""" # . Initialization. fluctuations = None # . Get the trajectory and associated system data. (system, trajectories) = _GetSystemAndTrajectoriesFromArguments(*arguments) # . Get the selection and the size of the problem. selection = keywordArguments.pop("selection", None) if selection is None: selection = Selection.FromIterable(range(len(system.atoms))) n = len(selection) # . Get the average positions. averagePositions = keywordArguments.pop("averagePositions", None) if averagePositions is None: averagePositions = AveragePositions(trajectories, selection=selection) # . Various other options. anisotropic = keywordArguments.pop("anisotropic", False) asBFactors = keywordArguments.pop("asBFactors", False) _CheckForUnknownOptions(keywordArguments) # . Continue processing. if (n > 0) and (averagePositions is not None): # . Allocate space. displacement = Coordinates3.WithExtent(n) if anisotropic: fluctuations = Real2DArray.WithExtents(n, 6) else: fluctuations = Real1DArray.WithExtent(n) displacement.Set(0.0) fluctuations.Set(0.0) # . Loop over trajectory frames. numberFrames = 0 for trajectory in trajectories: trajectory.ReadHeader() while trajectory.RestoreOwnerData(): frame = system.coordinates3 if anisotropic: for (p, i) in enumerate(selection): dx = frame[i, 0] - averagePositions[p, 0] dy = frame[i, 1] - averagePositions[p, 1] dz = frame[i, 2] - averagePositions[p, 2] fluctuations[p, 0] += dx * dx fluctuations[p, 1] += dy * dx fluctuations[p, 2] += dy * dy fluctuations[p, 3] += dz * dx fluctuations[p, 4] += dz * dy fluctuations[p, 5] += dz * dz else: for (p, i) in enumerate(selection): fluctuations[p] += ( ( frame[i,0] - averagePositions[p,0] )**2 + \ ( frame[i,1] - averagePositions[p,1] )**2 + \ ( frame[i,2] - averagePositions[p,2] )**2 ) trajectory.ReadFooter() trajectory.Close() numberFrames += len(trajectory) # . Scale. if numberFrames > 0: fluctuations.Scale(1.0 / float(numberFrames)) # . Convert to B-factors if necessary. if asBFactors: conversionFactor = 8.0 * math.pi**2 if not anisotropic: conversionFactor /= 3.0 fluctuations.Scale(conversionFactor) # . Finish up. return fluctuations
def JaguarBondOrders(infile, outfile, bondOrdertolerance=_DEFAULTBONDORDERTOLERANCE, chargetolerance=_DEFAULTCHARGETOLERANCE, log=logFile, QCROSS=False): """Calculate bond orders given Jaguar input and output files.""" # . Parse the input file. infile = JaguarInputFileReader(infile) infile.Parse() infile.Summary(log=logFile) # . Parse the output file. outfile = JaguarOutputFileReader(outfile) outfile.Parse() outfile.Summary(log=logFile) # . Get data from the files. # . Input. atomicNumbersi = getattr(infile, "atomicNumbers", None) coordinates3 = getattr(infile, "coordinates3", None) orbitalsets = getattr(infile, "orbitalsets", None) # . Output. atomicNumberso = getattr(outfile, "atomicNumbers", None) nfunctions = getattr(outfile, "nfunctions", None) overlap = getattr(outfile, "overlap", None) # . Check for coordinates. if coordinates3 is None: raise ValueError("The coordinate data is missing from the input file.") # . Check the orbital data. QOK = (orbitalsets is not None) and (len(orbitalsets) > 0) if QOK: if (len(orbitalsets) == 1) and ("" in orbitalsets): spinDensitiesRESTRICTED = True elif (len(orbitalsets) == 2) and ("alpha" in orbitalsets) and ("beta" in orbitalsets): spinDensitiesRESTRICTED = False else: QOK = False if not QOK: raise ValueError("Invalid orbital data on input file.") if spinDensitiesRESTRICTED: nbasisi = infile.orbitalsets[""][1] else: nbasisi = infile.orbitalsets["alpha"][1] # . Check the overlap. if (overlap is None): raise ValueError("The overlap matrix is missing from the output file.") nbasiso = overlap.Dimension() # . Check the array giving the number of functions per atom. # print nfunctions, len ( nfunctions ), len ( atomicNumberso ), sum ( nfunctions ), nbasiso if (nfunctions is None) or (len(nfunctions) != len(atomicNumberso) or (sum(nfunctions) != nbasiso)): raise ValueError( "Basis function data on the output file is missing or invalid.") # . Create the function index array. findices = [] first = 0 last = 0 for f in nfunctions: last += f findices.append((first, last)) first += f # . Check for compatibility between the data. QOK = (atomicNumbersi is not None) and (atomicNumberso is not None) and ( len(atomicNumbersi) == len(atomicNumberso)) and (nbasisi == nbasiso) if QOK: for (i, j) in zip(atomicNumbersi, atomicNumberso): if i != j: QOK = False break if not QOK: raise ValueError( "The systems on the input and output files are incompatible.") # . Set the keys for the calculation. if spinDensitiesRESTRICTED: keys = [""] else: keys = ["alpha", "beta"] # . Get the densities multiplied by the overlap. ps = {} for key in keys: p = _MakeDensity(orbitalsets[key]) # p.Print ( title = "**1**" ) result = Real2DArray.WithExtents(nbasisi, nbasisi) result.Set(999.0) p.PostMultiply(overlap, result) # overlap.Print ( title = "**2**" ) # result.Print ( title = "**3**" ) ps[key] = result # f = STOP # . Scale ps correctly for the spin-restricted case. if spinDensitiesRESTRICTED: ps[""].Scale(2.0) # . If cross terms are not required condense the ps matrices. if (not QCROSS) and (not spinDensitiesRESTRICTED): tps = ps.pop(keys[0]) for key in keys[1:]: tps.AddScaledMatrix(1.0, ps[key]) ps = {"": tps} keys = [""] # . Get the bond-orders. bondOrders = {} for key1 in keys: for key2 in keys: bondOrders[(key1, key2)] = _MakeBondOrders(ps[key1], ps[key2], findices) # . Make the total bond-order if necessary. bokeys = bondOrders.keys() if len(bokeys) > 1: bokeys.sort() tbo = Clone(bondOrders[bokeys[0]]) for key in bokeys[1:]: tbo.AddScaledMatrix(1.0, bondOrders[key]) tkey = ("", "") bokeys.append(tkey) bondOrders[tkey] = tbo # . Compute the electronic contribution to the Mulliken charges. qmulliken = Real1DArray.WithExtent(len(atomicNumbersi)) qmulliken.Set(0.0) for key in keys: _MakeElectronicMullikenCharges(ps[key], findices, qmulliken) # . Determine atom valencies. free = Real1DArray.WithExtent(len(atomicNumbersi)) free.Set(0.0) valencies = Real1DArray.WithExtent(len(atomicNumbersi)) valencies.Set(0.0) tbo = bondOrders[("", "")] for i in range(len(atomicNumbersi)): valencies[i] = (-2.0 * qmulliken[i]) - tbo[i, i] totalbo = 0.0 for j in range(len(atomicNumbersi)): totalbo += tbo[i, j] free[i] = (-2.0 * qmulliken[i]) - totalbo # . Add in the core contributions to the Mulliken charges. for (i, q) in enumerate(atomicNumbersi): qmulliken[i] += float(q) if outfile.QECP: for (i, q) in enumerate(outfile.ecpelectrons): qmulliken[i] -= float(q) # . Output the results. if LogFileActive(log): # . Get the spin label. if spinDensitiesRESTRICTED: spinlabel = "Spin Restricted" else: spinlabel = "Spin Unrestricted" # . Create the atom names. atomnames = [] for (i, n) in enumerate(atomicNumbersi): atomnames.append(PeriodicTable.Symbol(n, index=i + 1)) # . Atom data. columns = [10, 20, 20, 20] if not spinDensitiesRESTRICTED: columns.append(20) table = log.GetTable(columns=columns) table.Start() table.Title("Atom Data (" + spinlabel + ")") table.Heading("Atom") table.Heading("Charge") table.Heading("Self Bond Order") table.Heading("Valence") if not spinDensitiesRESTRICTED: table.Heading("Free Valence") for (i, ni) in enumerate(atomnames): table.Entry(ni) table.Entry("{:.3f}".format(qmulliken[i])) table.Entry("{:.3f}".format(tbo[i, i])) table.Entry("{:.3f}".format(valencies[i])) if not spinDensitiesRESTRICTED: table.Entry("{:.3f}".format(free[i])) table.Stop() # . Bond orders. for key in bokeys: orders = bondOrders[key] table = log.GetTable(columns=[10, 10, 20, 20]) table.Start() if key == ("", ""): table.Title("Total Bond Orders") else: table.Title(key[0].title() + "/" + key[1].title() + " Bond Orders") table.Heading("Atom 1") table.Heading("Atom 2") table.Heading("Order") table.Heading("Distance") for (i, ni) in enumerate(atomnames): for (j, nj) in enumerate(atomnames[0:i]): b = orders[i, j] if math.fabs(b) > bondOrdertolerance: table.Entry(ni) table.Entry(nj) table.Entry("{:.3f}".format(b)) table.Entry("{:.3f}".format(coordinates3.Distance( i, j))) table.Stop() # . Checks on the calculation. # . Free valence. if spinDensitiesRESTRICTED: deviation = free.AbsoluteMaximum() if deviation > chargetolerance: log.Paragraph( "Warning: the largest deviation between the free valence values and zero is {:.3f}." .format(deviation)) # . Total charge. deviation = math.fabs(qmulliken.Sum() - float(infile.charge)) if deviation > chargetolerance: log.Paragraph( "Warning: the total charge deviates from the formal charge by {:.3f}." .format(deviation)) # . Check for a valid reference set of Mulliken charges. qreference = getattr(outfile, "qmulliken", None) if (qreference is not None) and (len(qreference) == len(atomicNumbersi)): qmulliken.AddScaledArray(-1.0, qreference) deviation = qmulliken.AbsoluteMaximum() if deviation > chargetolerance: log.Paragraph( "Warning: the largest deviation between calculated and read Mulliken charges is {:.3f}." .format(deviation)) # . Finish up. return bondOrders
def QuasiHarmonic_SystemGeometry(system, log=logFile, modify=None, temperature=300.0, title="Quasi-Harmonic Frequencies (cm^(-1))", trajectories=None): """Determine the quasi-harmonic modes for a system.""" # . Initialization. state = None # . Determine if any atoms are fixed. hc = system.hardConstraints if (hc is not None) and (hc.fixedAtoms is not None) and (len(hc.fixedAtoms) > 0): fixedAtoms = hc.fixedAtoms else: fixedAtoms = None # . Get the covariance matrix. covariance = CovarianceMatrix(trajectories, selection=fixedAtoms) # . Proceed with the analysis. if covariance is not None: # . Get some options. if modify is None: modopt = None else: modopt = modify.upper() # . Mass-weight the covariance matrix. # . Weights are square roots of masses. of = SystemGeometryObjectiveFunction.FromSystem(system) of.DefineWeights() n = of.NumberOfVariables() for i in range(n): wI = of.variableWeights[i] for j in range(i + 1): wJ = of.variableWeights[j] covariance[i, j] *= (wI * wJ) # . Get the mass-weighted rotation-translation vectors and count their number. of.RemoveRotationTranslation() if of.linearScalars is None: nrtmodes = 0 else: nrtmodes = len(of.linearScalars) # . Modify the Hessian. if modopt in _MODIFYOPTIONS: if modopt == "PROJECT": covariance.ProjectOutVectors(of.linearVectors) elif modopt == "RAISE": covariance.Raise(of.linearVectors, 0.0) # . Diagonalization. eigenValues = Real1DArray.WithExtent(n) eigenValues.Set(0.0) eigenVectors = Real2DArray.WithExtents(n, n) eigenVectors.Set(0.0) covariance.Diagonalize(eigenValues, eigenVectors) # . Convert eigenvalues to frequencies. conversionFactor = math.sqrt(_TO_KJMOL * temperature) * _TO_WAVENUMBERS numberZero = 0 for (i, e) in enumerate(eigenValues): eAbs = math.fabs(e) if eAbs <= _QHTolerance: f = 0.0 numberZero += 1 else: f = math.sqrt(1.0 / eAbs) * conversionFactor if e < 0.0: f *= -1.0 eigenValues[i] = f # . Un-mass-weight the modes. for r in range(n): w = 1.0 / of.variableWeights[r] for c in range(n): eigenVectors[r, c] *= w # . Reverse in place (excluding zero modes). temporary = Real1DArray.WithExtent(n) for i in range((n - numberZero) // 2): # . Indices. lower = i + numberZero upper = n - i - 1 # . Eigenvalues. e = eigenValues[upper] eigenValues[upper] = eigenValues[lower] eigenValues[lower] = e # . Eigenvectors. for j in range(n): temporary[j] = eigenVectors[j, upper] for j in range(n): eigenVectors[j, upper] = eigenVectors[j, lower] for j in range(n): eigenVectors[j, lower] = temporary[j] # . Do some printing. if LogFileActive(log): table = log.GetTable(columns=_NFREQUENCYCOLUMNS * [_FREQUENCYWIDTHS]) table.Start() table.Title(title) for f in eigenValues: table.Entry(_FREQUENCYFORMAT.format(f)) table.Stop() # . Save all data. state = NormalModeState(dimension=n, freeAtoms=of.freeAtoms, frequencies=eigenValues, modes=eigenVectors, nrtmodes=nrtmodes, weights=of.variableWeights) system.configuration.SetTemporaryAttribute("qhState", state) # . Finish up. return state
def NormalModes_SystemGeometry(system, hessian=None, log=logFile, modify=None, title="Harmonic Frequencies (cm^(-1))"): """Determine the normal modes for a system.""" # . Get some options. if modify is None: modopt = None else: modopt = modify.upper() # . Get the Hessian with mass-weighting. of = SystemGeometryObjectiveFunction.FromSystem(system) of.DefineWeights() n = of.NumberOfVariables() if hessian is None: x = Real1DArray.WithExtent(n) x.Set(0.0) of.VariablesGet(x) hessian = of.NumericalHessian(x) # . Get the mass-weighted rotation-translation vectors and count their number. of.RemoveRotationTranslation() if of.linearScalars is None: nrtmodes = 0 else: nrtmodes = len(of.linearScalars) # . Modify the Hessian. if modopt in _MODIFYOPTIONS: if modopt == "PROJECT": hessian.ProjectOutVectors(of.linearVectors) elif modopt == "RAISE": hessian.Raise(of.linearVectors, _RAISEEIGENVALUE) # . Diagonalization. # . Maybe should save hessian here as it is destroyed by the diagonalization. eigenvalues = Real1DArray.WithExtent(n) eigenvalues.Set(0.0) eigenvectors = Real2DArray.WithExtents(n, n) eigenvectors.Set(0.0) hessian.Diagonalize(eigenvalues, eigenvectors) # . Convert eigenvalues to frequencies. for (i, e) in enumerate(eigenvalues): f = math.sqrt(math.fabs(e)) * _TO_WAVENUMBERS if e < 0.0: f *= -1.0 eigenvalues[i] = f # . Un-mass-weight the modes. for r in range(n): w = 1.0 / of.variableWeights[r] for c in range(n): eigenvectors[r, c] *= w # . Do some printing. if LogFileActive(log): table = log.GetTable(columns=_NFREQUENCYCOLUMNS * [_FREQUENCYWIDTHS]) table.Start() table.Title(title) for f in eigenvalues: table.Entry(_FREQUENCYFORMAT.format(f)) table.Stop() # . Save all data. state = NormalModeState(dimension=n, freeAtoms=of.freeAtoms, frequencies=eigenvalues, modes=eigenvectors, nrtmodes=nrtmodes, weights=of.variableWeights) system.configuration.SetTemporaryAttribute("nmState", state) # . Finish up. return state
def GrowingStringInitialPath(system, npoints, point0, pointn, path, **keywordArguments): """Use a \"growing string\" method for generating an initial path.""" # . Get some options. log = keywordArguments.pop("log", logFile) sequence = keywordArguments.pop("sequence", "Forward").lower() # . Get the number of points. npoints = max(npoints, 2) # . Check for fixed atoms. QFIXED = (system.hardConstraints is not None) and ( system.hardConstraints.NumberOfFixedAtoms() > 0) # . Save the current system coordinates. temporary = Clone(system.coordinates3) # . Create the trajectory and write the first frame. trajectory = SystemGeometryTrajectory(path, system, mode="w") trajectory.WriteFrame(point0, frame=0) # . Get the first and last energies. energies = {} point0.CopyTo(system.coordinates3) energies[0] = system.Energy(log=log) pointn.CopyTo(system.coordinates3) if not QFIXED: system.coordinates3.Superimpose(point0) energies[npoints - 1] = system.Energy(log=log) # . Save the last point. trajectory.WriteFrame(system.coordinates3, frame=npoints - 1) # . Generate intermediate points. if npoints > 2: # . Set up the optimizer. algorithm = keywordArguments.pop("optimizer", LBFGSMinimizer) options = dict(_MinimizerOptions) options.update(keywordArguments) optimizer = algorithm(**options) optimizer.Summary(log=log) # . Set up the objective function. of = SystemGeometryObjectiveFunction.FromSystem(system) # . Get variables for the first and last points and the tangent constraint. first = Real1DArray.WithExtent(of.nvariables) last = Real1DArray.WithExtent(of.nvariables) tangent = Real1DArray.WithExtent(of.nvariables) # . Save the last point (given it is already in system.coordinates3). of.VariablesGet(last) # . Save the first point. point0.CopyTo(system.coordinates3) of.VariablesGet(first) # . Generate the sequence over which iterations are to occur. sequenceData = [] if sequence == "alternate": for i in range((npoints - 2) // 2): sequenceData.append( (first, last, i + 1, npoints - 2 - (2 * i))) sequenceData.append( (last, first, npoints - 2 - i, npoints - 2 - (2 * i + 1))) if (npoints - 2) % 2 != 0: sequenceData.append((first, last, (npoints - 2) // 2 + 1, 1)) elif sequence == "backward": for i in range(npoints - 2): sequenceData.append( (last, first, npoints - 2 - i, npoints - 2 - i)) elif sequence == "forward": for i in range(npoints - 2): sequenceData.append((first, last, i + 1, npoints - 2 - i)) else: raise ArgumentError("Unknown sequence option - " + sequence + ".") # . Loop over points. for (start, stop, index, remainder) in sequenceData: # . Get the new point to optimize and the tangent constraint. stop.CopyTo(tangent) tangent.AddScaledArray(-1.0, start) start.AddScaledArray(1.0 / float(remainder + 1), tangent) tangent.Normalize() # . Set up the object function. of.RemoveRotationTranslation(reference=point0) of.AddLinearConstraint(tangent) of.VariablesPut(start) # . Minimize and save the point. optimizer.Iterate(of, log=log) of.VariablesGet(start) energies[index] = system.Energy(log=log) trajectory.WriteFrame(system.coordinates3, frame=index) # . Finish up. system.coordinates3 = temporary trajectory.Close() # . Output. if LogFileActive(log): table = log.GetTable(columns=[30, 20]) table.Start() table.Title("Growing String Structure Energies") table.Heading("Structure") table.Heading("Energy") keys = energies.keys() keys.sort() for key in keys: table.Entry(repr(key)) table.Entry("{:.3f}".format(energies[key])) table.Stop()
def VariablesAllocate(self): """Return an object to hold the variables.""" variables = Real1DArray.WithExtent(self.numberOfVariables) variables.Set(0.0) return variables
def ParseGuessSection(self, line): """Parse the guess section.""" # . Get the terminator as the opening character of the current line. terminator = line[0:1] # . Get the guess basis. words = line.split(None, 1) if len(words) > 1: tokens = words[1].split("=", 1) if (len(tokens) > 1) and (tokens[0] == "basgss"): guessbasis = tokens[1].strip() # . Initialization. coefficients = None nbasis = None orbitals = None orbitalsets = {} key = "" nwarnings0 = self.nwarnings # . Loop until an end of section is reached while True: items = self.GetTokens() # . The end found. if (len(items) == 1) and (items[0] == terminator): break # . An orbital set header. elif (len(items) > 2) and (items[-2] == "Molecular") and (items[-1] == "Orbitals"): key = " ".join(items[0:-2]) orbitals = None # . An orbital line. elif ("Orbital" in items): # . Orbital data. if "Energy" in items: energy = float(items[items.index("Energy") + 1]) else: energy = 0.0 if "Occupation" in items: occupancy = float(items[items.index("Occupation") + 1]) else: occupancy = 0.0 if (nbasis is None) and (coefficients is not None): nbasis = len(coefficients) coefficients = [] # . Orbital set data. if orbitals is None: orbitals = [] orbitals.append((energy, occupancy, coefficients)) # . Orbital collection data. if key is not None: if key in orbitalsets: self.Warning("Orbital sets with duplicate names.", False) else: orbitalsets[key] = orbitals key = None # . A coefficient line. elif (len(items) > 0) and (coefficients is not None): for item in items: coefficients.append(float(item)) if (nbasis is not None) and (len(coefficients) > nbasis): self.Warning( "There are orbitals with differing numbers of coefficients.", False) # . Other lines. else: self.Warning("Unable to interpret guess section line.", False) # . Save the data if there have been no warnings. if (nwarnings0 == self.nwarnings) and (nbasis is not None): # . Initialization. self.orbitalsets = {} # . Process each of the orbital sets in turn. for (key, orbitals) in orbitalsets.iteritems(): norbitals = len(orbitals) energies = Real1DArray.WithExtent(norbitals) occupancies = Real1DArray.WithExtent(norbitals) vectors = Real2DArray.WithExtents(nbasis, norbitals) for (i, (energy, occupancy, coefficients)) in enumerate(orbitals): energies[i] = energy occupancies[i] = occupancy for (j, v) in enumerate(coefficients): vectors[j, i] = v self.orbitalsets[key.lower()] = (norbitals, nbasis, energies, occupancies, vectors)