def MatrixMultiply ( extent, cpuTimer, ndg ): a = Real2DArray ( extent, extent ) ; a.Set ( 0.0 ) b = Real2DArray ( extent, extent ) ; b.Set ( 0.0 ) c = Real2DArray ( extent, extent ) ; c.Set ( 0.0 ) for i in range ( extent ): for j in range ( extent ): a[i,j] = ndg.NextDeviate ( ) b[i,j] = ndg.NextDeviate ( ) tStart = cpuTimer.Current ( ) c.MatrixMultiply ( a, b ) return ( cpuTimer.Current ( ) - tStart )
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 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 Eigenvalues ( extent, cpuTimer, ndg ): s = SymmetricMatrix ( extent ) ; s.Set ( 0.0 ) e = Real1DArray ( extent ) ; e.Set ( 0.0 ) v = Real2DArray ( extent, extent ) ; v.Set ( 0.0 ) for i in range ( extent ): for j in range ( i+1 ): s[i,j] = ndg.NextDeviate ( ) s[i,i] *= 2.0 tStart = cpuTimer.Current ( ) s. Diagonalize ( e, eigenVectors = v ) return ( cpuTimer.Current ( ) - tStart )
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 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)
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 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 ESPChargeFitting(system, aresp=_DEFAULT_ARESP, bresp=_DEFAULT_BRESP, log=logFile, QRESP=False): """Perform an ESP charge fit.""" # . Check for a system with a qcModel. if not (hasattr(system, "energyModel") and hasattr(system.energyModel, "qcModel")): raise ValueError("System does not have a QC energy model.") # . Initialization. natoms = len(system.energyModel.qcAtoms) ndim = natoms + 1 qtotal = 0.0 if hasattr(system, "electronicState"): qtotal = float(system.electronicState.charge) # atomicNumbers = system.atoms.GetItemAttributes ( "atomicNumber" ) # qtotal = float ( sum ( atomicNumbers ) ) # . Get the grid points for the QC atoms only and convert to bohrs. gridPoints = GenerateVanDerWaalsSurface( system, log=log, qcAtomsOnly=True, scalingfactors=[1.4, 1.6, 1.8, 2.0]) gridPoints.Scale(UNITS_LENGTH_ANGSTROMS_TO_BOHRS) # . Allocate space - one larger than necessary for fInteraction. fInteraction = Real2DArray.WithExtents(ndim, gridPoints.rows) fInteraction.Set(0.0) # . Get the observed potentials at the grid points. phi = system.energyModel.qcModel.GridPointPotentials( system.configuration, gridPoints) # . Get the interaction terms for each atom at the grid points. coordinates3 = system.energyModel.qcAtoms.GetCoordinates3( system.coordinates3, toBohrs=True) GetInteractionTerms(coordinates3, gridPoints, fInteraction) # . Get the A matrix and the B vector. A = Real2DArray.WithExtents(ndim, ndim) A.Set(0.0) B = Real1DArray.WithExtent(ndim) B.Set(0.0) A.MatrixMultiply(fInteraction, fInteraction, yTranspose=True) fInteraction.VectorMultiply(phi, B) # . Add the total charge constraint terms. for i in range(natoms): A[i, ndim - 1] = 1.0 A[ndim - 1, i] = 1.0 B[ndim - 1] = qtotal # A.Print ( ) # B.Print ( ) # phi.Print ( ) # . Get |phi^2|. phi2 = phi.Dot(phi) # . Iterative solution. if QRESP: (isConverged, sos, solution, condition, rank) = RESPIterator(natoms, A, B, fInteraction, phi, aresp, bresp, log=log) # . Non-iterative solution. else: # . Solve the equations. (condition, rank) = A.SolveLinearEquationsBySVD(B) solution = B # . Determine the sum of squares. fInteraction.VectorMultiply(solution, phi, alpha=-1.0, beta=1.0, transpose=True) sos = phi.Dot(phi) # . Determine the error measure. error = math.sqrt(sos / phi2) # . Do some printing. if LogFileActive(log): summary = log.GetSummary() summary.Start("ESP Charge Fitting Summary") summary.Entry("Charges", "{:d}".format(natoms)) summary.Entry("Rank", "{:d}".format(rank)) summary.Entry("Error", "{:.5g}".format(error)) summary.Entry("Condition", "{:.5g}".format(condition)) if QRESP: if isConverged: summary.Entry("Converged", "True") else: summary.Entry("Converged", "False") summary.Stop() # . Return the charges. charges = Real1DArray.WithExtent(natoms) for i in range(natoms): charges[i] = solution[i] return charges
def RESPIterator(natoms, A, rhs, fInteraction, phi, aresp, bresp, ftolerance=_DEFAULT_FTOLERANCE, log=logFile, logFrequency=_DEFAULT_LOGFREQUENCY, maximumIterations=_DEFAULT_MAXIMUMITERATIONS, qtolerance=_DEFAULT_QTOLERANCE): """Solve the RESP equations by simple iteration.""" # . Allocate space. n = len(rhs) Atemp = Real2DArray.WithExtents(n, n) Btemp = Real1DArray.WithExtent(n) phitemp = Real1DArray.WithExtent(len(phi)) q = Real1DArray.WithExtent(n, initializer=0.0) # . With initial values. qold = Real1DArray.WithExtent(n) # . Check for printing. QPRINT = (logFrequency > 0) and (logFrequency < maximumIterations) and LogFileActive(log) if QPRINT: table = log.GetTable(columns=[10, 20, 20, 20, 20, 10]) table.Start() table.Title("RESP Solver") table.Heading("Iteration") table.Heading("Function") table.Heading("Change in F") table.Heading("Change in Q") table.Heading("Condition") table.Heading("Rank") # . Determine the sum of squares with initial zero charges. f = phi.Dot(phi) # . Initialization. niterations = 0 isConverged = False # . Perform the iterations. while (niterations < maximumIterations) and (not isConverged): # . Save old data. fold = f q.CopyTo(qold) # . Fill new A and RHS. A.CopyTo(Atemp) rhs.CopyTo(Btemp) # . Add in the constraints. GetRESPConstraints(natoms, aresp, bresp, q, Atemp) # . Solve the equations. (condition, rank) = Atemp.SolveLinearEquationsBySVD(Btemp) Btemp.CopyTo(q) # . Determine the sum of squares. phi.CopyTo(phitemp) fInteraction.VectorMultiply(q, phitemp, alpha=-1.0, beta=1.0, transpose=True) f = phitemp.Dot(phitemp) # . Check for convergence. qold.AddScaledArray(-1.0, q) fdifference = math.fabs(fold - f) qdifference = qold.AbsoluteMaximum() isConverged = (fdifference < ftolerance) and (qdifference < qtolerance) # . Printing. if QPRINT and (niterations % logFrequency == 0): table.Entry("{:d}".format(niterations)) table.Entry("{:.6g}".format(f)) table.Entry("{:.6g}".format(fdifference)) table.Entry("{:.6g}".format(qdifference)) table.Entry("{:.6g}".format(condition)) table.Entry("{:d}".format(rank)) # . End of loop. niterations += 1 # . Stop printing. if QPRINT: table.Stop() if isConverged: log.Paragraph("RESP procedure converged.") else: log.Paragraph("Warning: RESP procedure not converged.") # . Finish up. return (isConverged, f, q, condition, rank)