Initial Translation of SetGlobals

Initial work in the tradution of SetGlobals and Logistics in the use of classes for the management of processes and threaded works

Signed-off-by: Vitor Hideyoshi <vitor.h.n.batista@gmail.com>
This commit is contained in:
2021-07-19 16:59:03 -03:00
parent ec08e05614
commit a1ff35eba6
3 changed files with 544 additions and 449 deletions

480
DPpack/Molhandling.py Normal file
View File

@@ -0,0 +1,480 @@
from DPpack.MolHandling import total_mass
import os, sys
import math
import shutil
import textwrap
import sys, math
from copy import deepcopy
import numpy as np
from numpy import linalg
from DPpack.Misc import *
from DPpack.PTable import *
from DPpack.SetGlobals import *
# Usaremos uma nova classe que ira conter toda interação entre moleculas
class System:
def __init__(self):
self.molecule = []
def add_molecule(self, m):
self.molecule.append(m)
# Função que calcula a distância entre dois centros de massa
# e por se tratar de uma função de dois atomos não deve ser
# inserida dentro de Molecule
def center_of_mass_distance(self, a, b):
com1 = self.molecule[a].center_of_mass()
com2 = self.molecule[b].center_of_mass()
dx = com1[0] - com2[0]
dy = com1[1] - com2[1]
dz = com1[2] - com2[2]
distance = math.sqrt(dx**2 + dy**2 + dz**2)
return distance
def minimum_distance(self, index1, index2):
distances = []
for atom1 in self.molecule[index1]:
if atom1.na != ghost_number:
for atom2 in self.molecule[index2]:
if atom2.na != ghost_number:
dx = atom1.rx - atom2.rx
dy = atom1.ry - atom2.ry
dz = atom1.rz - atom2.rz
distances.append(math.sqrt(dx**2 + dy**2 + dz**2))
return min(distances)
def rmsd_fit(self, index_p, index_r):
projecting_mol = self.molecule[index_p]
reference_mol = self.molecule[index_r]
if len(projecting_mol.atom) != len(reference_mol.atom):
sys.exit("Error in RMSD fit procedure: molecules have different number of atoms")
dim = len(projecting_mol.atom)
new_projecting_mol = deepcopy(projecting_mol)
new_reference_mol = deepcopy(reference_mol)
new_projecting_mol.center_of_mass_to_origin()
new_reference_mol.center_of_mass_to_origin()
x = []
y = []
for atom in new_projecting_mol:
x.extend([ atom.rx, atom.ry, atom.rz ])
for atom in new_reference_mol:
y.extend([ atom.rx, atom.ry, atom.rz ])
x = np.array(x).reshape(dim, 3)
y = np.array(y).reshape(dim, 3)
r = np.matmul(y.T, x)
rr = np.matmul(r.T, r)
try:
evals, evecs = linalg.eigh(rr)
except:
sys.exit("Error: diagonalization of RR matrix did not converge")
a1 = evecs[:,2].T
a2 = evecs[:,1].T
a3 = np.cross(a1, a2)
A = np.array([ a1[0], a1[1], a1[2], a2[0], a2[1], a2[2], a3[0], a3[1], a3[2] ])
A = A.reshape(3,3)
b1 = np.matmul(r, a1.T).T # or np.dot(r, a1)
b1 /= linalg.norm(b1)
b2 = np.matmul(r, a2.T).T # or np.dot(r, a2)
b2 /= linalg.norm(b2)
b3 = np.cross(b1, b2)
B = np.array([ b1[0], b1[1], b1[2], b2[0], b2[1], b2[2], b3[0], b3[1], b3[2] ])
B = B.reshape(3,3).T
rot_matrix = np.matmul(B, A)
x = np.matmul(rot_matrix, x.T).T
rmsd = 0
for i in range(dim):
rmsd += (x[i,0] - y[i,0])**2 + (x[i,1] - y[i,1])**2 + (x[i,2] - y[i,2])**2
rmsd = math.sqrt(rmsd/dim)
for i in range(dim):
new_projecting_mol.atom[i].rx = x[i,0]
new_projecting_mol.atom[i].ry = x[i,1]
new_projecting_mol.atom[i].rz = x[i,2]
tr_vector = reference_mol.center_of_mass()
projected_mol = new_projecting_mol.translate(tr_vector)
return rmsd, projected_mol
def update_molecule(self, position, fh):
position_in_ang = (position * bohr2ang).tolist()
self.add_molecule(deepcopy(self.molecule[0]))
for atom in self.molecule[-1].atom:
atom.rx = position_in_ang.pop(0)
atom.ry = position_in_ang.pop(0)
atom.rz = position_in_ang.pop(0)
rmsd, self.molecule[0] = self.rmsd_fit(-1, 0)
self.molecule.pop(-1)
fh.write("\nProjected new conformation of reference molecule with RMSD fit\n")
fh.write("RMSD = {:>8.5f} Angstrom\n".format(rmsd))
def nearest_image(self, index_r, index_m, lx, ly, lz, criterium=None):
if criterium in None:
criterium = "com"
if criterium != "com" and criterium != "min":
sys.exit("Error in value passed to function nearest_image")
min_dist = 1e20
for i in range(-1, 2):
for j in range(-1, 2):
for k in range(-1, 2):
tr_vector = [i * lx, j * ly, k * lz]
self.add_molecule(self.molecule[index_m].translate(tr_vector))
if criterium == "com":
dist = self.center_of_mass_distance(index_r, -1)
else:
dist = self.minimum_distance(index_r, -1)
if dist < min_dist:
min_dist = dist
nearestmol = deepcopy(self.molecule[-1])
self.molecule.pop(-1)
return min_dist, nearestmol
def print_geom(self, cycle, fh):
fh.write("{}\n".format(len(self.molecule[0])))
fh.write("Cycle # {}\n".format(cycle))
for atom in self.molecule[0].atoms:
symbol = atomsymb[atom.na]
fh.write("{:<2s} {:>10.6f} {:>10.6f} {:>10.6f}\n".format(symbol,
atom.rx, atom.ry, atom.rz))
# Classe que conterá toda informação e funções relacionadas a uma unica molecula
class Molecule:
def __init__(self):
self.atom = [] # Lista de instancias de Atom
self.position = None # Array Numpy
self.energy = None # Array Numpy
self.gradient = None # Array Numpy
self.hessian = None # Array Numpy
self.total_mass = 0
def add_atom(self, a):
self.atom.append(a) # Inserção de um novo atomo
self.total_mass += a.mass
def center_of_mass(self):
com = np.zeros(3)
total_mass = 0.0
for atom in self.atom:
total_mass += atom.mass
com += atom.mass * np.array([atom.rx, atom.ry, atom.rz])
com = com / total_mass
return com
def center_of_mass_to_origin(self):
com = self.center_of_mass()
for atom in self.atom:
atom.rx -= com[0]
atom.ry -= com[1]
atom.rz -= com[2]
def charges_and_dipole(self):
eA_to_Debye = 1/0.20819434
charge = 0
dipole = np.zeros(3)
for atom in self.atom:
position = np.array([ atom.rx, atom.ry, atom.rz ])
dipole += atom.chg * position
charge += atom.chg
dipole *= eA_to_Debye
total_dipole = math.sqrt(dipole[0]**2 + dipole[1]**2 + dipole[2]**2)
return [charge, dipole[0], dipole[1], dipole[2], total_dipole]
def distances_between_atoms(self):
distances = []
dim = len(self.atom)
for atom1 in self.atom:
if atom1.na != ghost_number:
for atom2 in self.atom:
if atom2.na != ghost_number:
dx = atom1.rx - atom2.rx
dy = atom1.ry - atom2.ry
dz = atom1.rz - atom2.rz
distances.append(math.sqrt(dx**2 + dy**2 + dz**2))
return np.array(distances).reshape(dim, dim)
def eixos(self):
eixos = np.zeros(3)
if len(self.atom) == 2:
position1 = np.array([ self.atom[0].rx, self.atom[0].ry, self.atom[0].rz ])
position2 = np.array([ self.atom[1].rx, self.atom[1].ry, self.atom[1].rz ])
eixos = position2 - position1
eixos /= linalg.norm(eixos)
elif len(self.atom) > 2:
position1 = np.array([ self.atom[0].rx, self.atom[0].ry, self.atom[0].rz ])
position2 = np.array([ self.atom[1].rx, self.atom[1].ry, self.atom[1].rz ])
position3 = np.array([ self.atom[2].rx, self.atom[2].ry, self.atom[2].rz ])
v1 = position2 - position1
v2 = position3 - position1
v3 = np.cross(v1, v2)
v2 = np.cross(v1, v3)
v1 /= linalg.norm(v1)
v2 /= linalg.norm(v2)
v3 /= linalg.norm(v3)
eixos = np.array([[v1[0], v1[1], v1[2]],
[v2[0], v2[1], v2[2]],
[v3[0], v3[1], v3[2]]])
return eixos
def inertia_tensor(self):
com = self.center_of_mass()
Ixx = Ixy = Ixz = Iyy = Iyz = Izz = 0.0
for atom in self.atom:
#### Obtain the displacement from the center of mass
dx = atom.rx - com[0]
dy = atom.ry - com[1]
dz = atom.rz - com[2]
#### Update the diagonal components of the tensor
Ixx += atom.mass * (dy**2 + dz**2)
Iyy += atom.mass * (dz**2 + dx**2)
Izz += atom.mass * (dx**2 + dy**2)
#### Update the off-diagonal components of the tensor
Ixy += atom.mass * dx * dy * -1
Ixz += atom.mass * dx * dz * -1
Iyz += atom.mass * dy * dz * -1
return np.array([ [Ixx, Ixy, Ixz],
[Ixy, Iyy, Iyz],
[Ixz, Iyz, Izz] ])
def principal_axes(self):
try:
evals, evecs = linalg.eigh(self.inertia_tensor())
except:
sys.exit("Error: diagonalization of inertia tensor did not converge")
return evals, evecs
def read_position(self):
position_list = []
for atom in self.atom:
position_list.extend([ atom.rx, atom.ry, atom.rz ])
position = np.array(position_list)
position *= ang2bohr
return position
def update_hessian(self, step, cur_gradient): ## According to the BFGS
dif_gradient = cur_gradient - self.gradient
mat1 = 1/np.dot(dif_gradient, step) * np.matmul(dif_gradient.T, dif_gradient)
mat2 = 1/np.dot(step, np.matmul(self.hessian, step.T).T)
mat2 *= np.matmul( np.matmul(self.hessian, step.T), np.matmul(step, hessian) )
self.hessian += mat1 - mat2
def sizes_of_molecule(self):
x_list = []
y_list = []
z_list = []
for atom in self.atom:
if atom.na != ghost_number:
x_list.append(atom.rx)
y_list.append(atom.ry)
z_list.append(atom.rz)
x_max = max(x_list)
x_min = min(x_list)
y_max = max(y_list)
y_min = min(y_list)
z_max = max(z_list)
z_min = min(z_list)
sizes = [x_max - x_min, y_max - y_min, z_max - z_min]
return sizes
def standard_orientation(self):
self.center_of_mass_to_origin()
tensor = self.inertia_tensor()
evals, evecs = self.principal_axes()
if round(linalg.det(evecs)) == -1:
evecs[0,2] *= -1
evecs[1,2] *= -1
evecs[2,2] *= -1
if round(linalg.det(evecs)) != 1:
sys.exit("Error: could not make a rotation matrix while adopting the standard orientation")
rot_matrix = evecs.T
for atom in self.atom:
position = np.array([ atom.rx, atom.ry, atom.rz ])
new_position = np.matmul(rot_matrix, position.T).T
atom.rx = new_position[0]
atom.ry = new_position[1]
atom.rz = new_position[2]
def translate(self, vector):
new_molecule = deepcopy(self)
for atom in new_molecule.atom:
atom.rx += vector[0]
atom.ry += vector[1]
atom.rz += vector[2]
return new_molecule
def print_mol_info(self, fh):
com = self.center_of_mass()
fh.write(" Center of mass = ( {:>10.4f} , {:>10.4f} , {:>10.4f} )\n".format(com[0],
com[1], com[2]))
inertia = self.inertia_tensor()
evals, evecs = self.principal_axes()
fh.write(" Moments of inertia = {:>9E} {:>9E} {:>9E}\n".format(evals[0],
evals[1], evals[2]))
fh.write(" Major principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )\n".format(
evecs[0,0], evecs[1,0], evecs[2,0]))
fh.write(" Inter principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )\n".format(
evecs[0,1], evecs[1,1], evecs[2,1]))
fh.write(" Minor principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )\n".format(
evecs[0,2], evecs[1,2], evecs[2,2]))
sizes = self.sizes_of_molecule()
fh.write(" Characteristic lengths = ( {:>6.2f} , {:>6.2f} , {:>6.2f} )\n".format(
sizes[0], sizes[1], sizes[2]))
mol_mass = self.total_mass()
fh.write(" Total mass = {:>8.2f} au\n".format(mol_mass))
chg_dip = self.charges_and_dipole()
fh.write(" Total charge = {:>8.4f} e\n".format(chg_dip[0]))
fh.write(" Dipole moment = ( {:>9.4f} , {:>9.4f} , {:>9.4f} ) Total = {:>9.4f} Debye\n\n".format(
chg_dip[1], chg_dip[2], chg_dip[3], chg_dip[4]))
def calculate_step(self, fh):
invhessian = linalg.inv(self.hessian)
pre_step = -1 * np.matmul(invhessian, self.gradient.T).T
maxstep = np.amax(np.absolute(pre_step))
factor = min(1, player['maxstep']/maxstep)
step = factor * pre_step
fh.write("\nCalculated step:\n")
pre_step_list = pre_step.tolist()
fh.write("-----------------------------------------------------------------------\n"
"Center Atomic Step (Bohr)\n"
"Number Number X Y Z\n"
"-----------------------------------------------------------------------\n")
for i in range(len(molecules[0])):
fh.write(" {:>5d} {:>3d} {:>14.9f} {:>14.9f} {:>14.9f}\n".format(
i + 1, molecules[0][i]['na'],
pre_step_list.pop(0), pre_step_list.pop(0), pre_step_list.pop(0)))
fh.write("-----------------------------------------------------------------------\n")
fh.write("Maximum step is {:>11.6}\n".format(maxstep))
fh.write("Scaling factor = {:>6.4f}\n".format(factor))
fh.write("\nFinal step (Bohr):\n")
step_list = step.tolist()
fh.write("-----------------------------------------------------------------------\n"
"Center Atomic Step (Bohr)\n"
"Number Number X Y Z\n"
"-----------------------------------------------------------------------\n")
for i in range(len(molecules[0])):
fh.write(" {:>5d} {:>3d} {:>14.9f} {:>14.9f} {:>14.9f}\n".format(
i + 1, molecules[0][i]['na'],
step_list.pop(0), step_list.pop(0), step_list.pop(0)))
fh.write("-----------------------------------------------------------------------\n")
step_max = np.amax(np.absolute(step))
step_rms = np.sqrt(np.mean(np.square(step)))
fh.write(" Max Step = {:>14.9f} RMS Step = {:>14.9f}\n\n".format(
step_max, step_rms))
return step
class Atom:
def __init__(self, lbl,na,rx,ry,rz,chg,eps,sig):
self.lbl = lbl # Integer
self.na = na # Integer
self.rx = rx # Double
self.ry = ry # Double
self.rz = rz # Double
self.chg = chg # Double
self.eps = eps # Double
self.sig = sig # Double
self.mass = atommass[self.na] # Double

View File

@@ -1,479 +1,94 @@
from DPpack.MolHandling import total_mass
import os, sys import os, sys
import math
import shutil import shutil
import textwrap import textwrap
import sys, math
from copy import deepcopy
import numpy as np
from numpy import linalg
from DPpack.Misc import *
from DPpack.PTable import * from DPpack.PTable import *
from DPpack.SetGlobals import * from DPpack.Misc import *
# Usaremos uma nova classe que ira conter toda interação entre moleculas class Internal:
class System:
def __init__(self): def __init__(self):
self.molecule = [] self.player = self.Player()
self.dice = self.Dice()
self.gaussian = self.Gaussian()
self.molca = self.Molca()
def add_molecule(self, m): ## Constanst that shall be set for global use
self.molecule.append(m) self.tol_rms_force = 3e-4 # Hartree/Bohr
self.tol_max_force = 4.5e-4 # Hartree/Bohr
self.tol_rms_step = 1.2e-3 # Bohr
self.tol_max_step = 1.8e-3 # Bohr
self.trust_radius = None
# Função que calcula a distância entre dois centros de massa ## Dice:
# e por se tratar de uma função de dois atomos não deve ser self.combrule = None
# inserida dentro de Molecule self.randominit = None
def center_of_mass_distance(self, a, b):
com1 = self.molecule[a].center_of_mass() class Player:
com2 = self.molecule[b].center_of_mass()
dx = com1[0] - com2[0]
dy = com1[1] - com2[1]
dz = com1[2] - com2[2]
distance = math.sqrt(dx**2 + dy**2 + dz**2)
return distance
def minimum_distance(self, index1, index2):
distances = []
for atom1 in self.molecule[index1]:
if atom1.na != ghost_number:
for atom2 in self.molecule[index2]:
if atom2.na != ghost_number:
dx = atom1.rx - atom2.rx
dy = atom1.ry - atom2.ry
dz = atom1.rz - atom2.rz
distances.append(math.sqrt(dx**2 + dy**2 + dz**2))
return min(distances)
def rmsd_fit(self, index_p, index_r):
projecting_mol = self.molecule[index_p]
reference_mol = self.molecule[index_r]
if len(projecting_mol.atom) != len(reference_mol.atom):
sys.exit("Error in RMSD fit procedure: molecules have different number of atoms")
dim = len(projecting_mol.atom)
new_projecting_mol = deepcopy(projecting_mol)
new_reference_mol = deepcopy(reference_mol)
new_projecting_mol.center_of_mass_to_origin()
new_reference_mol.center_of_mass_to_origin()
x = []
y = []
for atom in new_projecting_mol:
x.extend([ atom.rx, atom.ry, atom.rz ])
for atom in new_reference_mol:
y.extend([ atom.rx, atom.ry, atom.rz ])
x = np.array(x).reshape(dim, 3)
y = np.array(y).reshape(dim, 3)
r = np.matmul(y.T, x)
rr = np.matmul(r.T, r)
try:
evals, evecs = linalg.eigh(rr)
except:
sys.exit("Error: diagonalization of RR matrix did not converge")
a1 = evecs[:,2].T
a2 = evecs[:,1].T
a3 = np.cross(a1, a2)
A = np.array([ a1[0], a1[1], a1[2], a2[0], a2[1], a2[2], a3[0], a3[1], a3[2] ])
A = A.reshape(3,3)
b1 = np.matmul(r, a1.T).T # or np.dot(r, a1)
b1 /= linalg.norm(b1)
b2 = np.matmul(r, a2.T).T # or np.dot(r, a2)
b2 /= linalg.norm(b2)
b3 = np.cross(b1, b2)
B = np.array([ b1[0], b1[1], b1[2], b2[0], b2[1], b2[2], b3[0], b3[1], b3[2] ])
B = B.reshape(3,3).T
rot_matrix = np.matmul(B, A)
x = np.matmul(rot_matrix, x.T).T
rmsd = 0
for i in range(dim):
rmsd += (x[i,0] - y[i,0])**2 + (x[i,1] - y[i,1])**2 + (x[i,2] - y[i,2])**2
rmsd = math.sqrt(rmsd/dim)
for i in range(dim):
new_projecting_mol.atom[i].rx = x[i,0]
new_projecting_mol.atom[i].ry = x[i,1]
new_projecting_mol.atom[i].rz = x[i,2]
tr_vector = reference_mol.center_of_mass()
projected_mol = new_projecting_mol.translate(tr_vector)
return rmsd, projected_mol
def update_molecule(self, position, fh):
position_in_ang = (position * bohr2ang).tolist()
self.add_molecule(deepcopy(self.molecule[0]))
for atom in self.molecule[-1].atom:
atom.rx = position_in_ang.pop(0)
atom.ry = position_in_ang.pop(0)
atom.rz = position_in_ang.pop(0)
rmsd, self.molecule[0] = self.rmsd_fit(-1, 0)
self.molecule.pop(-1)
fh.write("\nProjected new conformation of reference molecule with RMSD fit\n")
fh.write("RMSD = {:>8.5f} Angstrom\n".format(rmsd))
def nearest_image(self, index_r, index_m, lx, ly, lz, criterium=None):
if criterium in None:
criterium = "com"
if criterium != "com" and criterium != "min":
sys.exit("Error in value passed to function nearest_image")
min_dist = 1e20
for i in range(-1, 2):
for j in range(-1, 2):
for k in range(-1, 2):
tr_vector = [i * lx, j * ly, k * lz]
self.add_molecule(self.molecule[index_m].translate(tr_vector))
if criterium == "com":
dist = self.center_of_mass_distance(index_r, -1)
else:
dist = self.minimum_distance(index_r, -1)
if dist < min_dist:
min_dist = dist
nearestmol = deepcopy(self.molecule[-1])
self.molecule.pop(-1)
return min_dist, nearestmol
def print_geom(self, cycle, fh):
fh.write("{}\n".format(len(self.molecule[0])))
fh.write("Cycle # {}\n".format(cycle))
for atom in self.molecule[0].atoms:
symbol = atomsymb[atom.na]
fh.write("{:<2s} {:>10.6f} {:>10.6f} {:>10.6f}\n".format(symbol,
atom.rx, atom.ry, atom.rz))
# Classe que conterá toda informação e funções relacionadas a uma unica molecula
class Molecule:
def __init__(self): def __init__(self):
self.atom = [] # Lista de instancias de Atom self.maxcyc = None
self.position = None # Array Numpy self.initcyc = 1
self.energy = None # Array Numpy self.nprocs = 1
self.gradient = None # Array Numpy self.switchcyc = 3
self.hessian = None # Array Numpy self.altsteps = 20000
self.total_mass = 0 self.maxstep = .3
self.qmprog = "g09"
self.opt = "yes"
self.freq = "no"
self.readhessian = "no"
self.lps = "no"
self.ghosts = "no"
self.vdwforces = "no"
self.tol_factor = 1.2
def add_atom(self, a):
self.atom.append(a) # Inserção de um novo atomo
self.total_mass += a.mass
def center_of_mass(self): class Dice:
com = np.zeros(3) def __init__(self):
total_mass = 0.0
for atom in self.atom: self.title = "Diceplayer run"
self.progname = "dice"
self.temp = 300.0
self.press = 1.0
self.isave = 1000 # ASEC construction will take this into account
self.ncores = 1
total_mass += atom.mass self.dens = None # Investigate the possibility of using 'box = Lx Ly Lz' instead.
com += atom.mass * np.array([atom.rx, atom.ry, atom.rz]) #dice['box'] = None # So 'geom' would be set by diceplayer and 'cutoff' would be
# switched off. One of them must be given.
self.ljname = None
self.outname = None
self.nmol = [] # Up to 4 integer values related to up to 4 molecule types
self.nstep = [] # 2 or 3 integer values related to 2 or 3 simulations
# (NVT th + NVT eq) or (NVT th + NPT th + NPT eq).
# This will control the 'nstep' keyword of Dice
com = com / total_mass
return com class Gaussian:
def center_of_mass_to_origin(self): def __init__(self):
com = self.center_of_mass() self.mem = None
self.keywords = None
self.chgmult = [0, 1]
self.gmiddle = None # In each case, if a filename is given, its content will be
self.gbottom = None # inserted in the gaussian input
self.pop = "chelpg"
self.chglevel = None
for atom in self.atom: self.level = None
atom.rx -= com[0] class Molcas:
atom.ry -= com[1]
atom.rz -= com[2]
def charges_and_dipole(self): def __init(self):
eA_to_Debye = 1/0.20819434 self.orbfile = "input.exporb"
charge = 0 self.root = 1
dipole = np.zeros(3)
for atom in self.atom:
position = np.array([ atom.rx, atom.ry, atom.rz ])
dipole += atom.chg * position
charge += atom.chg
dipole *= eA_to_Debye self.mbottom = None
total_dipole = math.sqrt(dipole[0]**2 + dipole[1]**2 + dipole[2]**2) self.basis = None
return [charge, dipole[0], dipole[1], dipole[2], total_dipole]
def distances_between_atoms(self):
distances = []
dim = len(self.atom)
for atom1 in self.atom:
if atom1.na != ghost_number:
for atom2 in self.atom:
if atom2.na != ghost_number:
dx = atom1.rx - atom2.rx
dy = atom1.ry - atom2.ry
dz = atom1.rz - atom2.rz
distances.append(math.sqrt(dx**2 + dy**2 + dz**2))
return np.array(distances).reshape(dim, dim)
def eixos(self):
eixos = np.zeros(3)
if len(self.atom) == 2:
position1 = np.array([ self.atom[0].rx, self.atom[0].ry, self.atom[0].rz ])
position2 = np.array([ self.atom[1].rx, self.atom[1].ry, self.atom[1].rz ])
eixos = position2 - position1
eixos /= linalg.norm(eixos)
elif len(self.atom) > 2:
position1 = np.array([ self.atom[0].rx, self.atom[0].ry, self.atom[0].rz ])
position2 = np.array([ self.atom[1].rx, self.atom[1].ry, self.atom[1].rz ])
position3 = np.array([ self.atom[2].rx, self.atom[2].ry, self.atom[2].rz ])
v1 = position2 - position1
v2 = position3 - position1
v3 = np.cross(v1, v2)
v2 = np.cross(v1, v3)
v1 /= linalg.norm(v1)
v2 /= linalg.norm(v2)
v3 /= linalg.norm(v3)
eixos = np.array([[v1[0], v1[1], v1[2]],
[v2[0], v2[1], v2[2]],
[v3[0], v3[1], v3[2]]])
return eixos
def inertia_tensor(self):
com = self.center_of_mass()
Ixx = Ixy = Ixz = Iyy = Iyz = Izz = 0.0
for atom in self.atom:
#### Obtain the displacement from the center of mass
dx = atom.rx - com[0]
dy = atom.ry - com[1]
dz = atom.rz - com[2]
#### Update the diagonal components of the tensor
Ixx += atom.mass * (dy**2 + dz**2)
Iyy += atom.mass * (dz**2 + dx**2)
Izz += atom.mass * (dx**2 + dy**2)
#### Update the off-diagonal components of the tensor
Ixy += atom.mass * dx * dy * -1
Ixz += atom.mass * dx * dz * -1
Iyz += atom.mass * dy * dz * -1
return np.array([ [Ixx, Ixy, Ixz],
[Ixy, Iyy, Iyz],
[Ixz, Iyz, Izz] ])
def principal_axes(self):
try:
evals, evecs = linalg.eigh(self.inertia_tensor())
except:
sys.exit("Error: diagonalization of inertia tensor did not converge")
return evals, evecs
def read_position(self):
position_list = []
for atom in self.atom:
position_list.extend([ atom.rx, atom.ry, atom.rz ])
position = np.array(position_list)
position *= ang2bohr
return position
def update_hessian(self, step, cur_gradient): ## According to the BFGS
dif_gradient = cur_gradient - self.gradient
mat1 = 1/np.dot(dif_gradient, step) * np.matmul(dif_gradient.T, dif_gradient)
mat2 = 1/np.dot(step, np.matmul(self.hessian, step.T).T)
mat2 *= np.matmul( np.matmul(self.hessian, step.T), np.matmul(step, hessian) )
self.hessian += mat1 - mat2
def sizes_of_molecule(self):
x_list = []
y_list = []
z_list = []
for atom in self.atom:
if atom.na != ghost_number:
x_list.append(atom.rx)
y_list.append(atom.ry)
z_list.append(atom.rz)
x_max = max(x_list)
x_min = min(x_list)
y_max = max(y_list)
y_min = min(y_list)
z_max = max(z_list)
z_min = min(z_list)
sizes = [x_max - x_min, y_max - y_min, z_max - z_min]
return sizes
def standard_orientation(self):
self.center_of_mass_to_origin()
tensor = self.inertia_tensor()
evals, evecs = self.principal_axes()
if round(linalg.det(evecs)) == -1:
evecs[0,2] *= -1
evecs[1,2] *= -1
evecs[2,2] *= -1
if round(linalg.det(evecs)) != 1:
sys.exit("Error: could not make a rotation matrix while adopting the standard orientation")
rot_matrix = evecs.T
for atom in self.atom:
position = np.array([ atom.rx, atom.ry, atom.rz ])
new_position = np.matmul(rot_matrix, position.T).T
atom.rx = new_position[0]
atom.ry = new_position[1]
atom.rz = new_position[2]
def translate(self, vector):
new_molecule = deepcopy(self)
for atom in new_molecule.atom:
atom.rx += vector[0]
atom.ry += vector[1]
atom.rz += vector[2]
return new_molecule
def print_mol_info(self, fh):
com = self.center_of_mass()
fh.write(" Center of mass = ( {:>10.4f} , {:>10.4f} , {:>10.4f} )\n".format(com[0],
com[1], com[2]))
inertia = self.inertia_tensor()
evals, evecs = self.principal_axes()
fh.write(" Moments of inertia = {:>9E} {:>9E} {:>9E}\n".format(evals[0],
evals[1], evals[2]))
fh.write(" Major principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )\n".format(
evecs[0,0], evecs[1,0], evecs[2,0]))
fh.write(" Inter principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )\n".format(
evecs[0,1], evecs[1,1], evecs[2,1]))
fh.write(" Minor principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )\n".format(
evecs[0,2], evecs[1,2], evecs[2,2]))
sizes = self.sizes_of_molecule()
fh.write(" Characteristic lengths = ( {:>6.2f} , {:>6.2f} , {:>6.2f} )\n".format(
sizes[0], sizes[1], sizes[2]))
mol_mass = self.total_mass()
fh.write(" Total mass = {:>8.2f} au\n".format(mol_mass))
chg_dip = self.charges_and_dipole()
fh.write(" Total charge = {:>8.4f} e\n".format(chg_dip[0]))
fh.write(" Dipole moment = ( {:>9.4f} , {:>9.4f} , {:>9.4f} ) Total = {:>9.4f} Debye\n\n".format(
chg_dip[1], chg_dip[2], chg_dip[3], chg_dip[4]))
def calculate_step(self, fh):
invhessian = linalg.inv(self.hessian)
pre_step = -1 * np.matmul(invhessian, self.gradient.T).T
maxstep = np.amax(np.absolute(pre_step))
factor = min(1, player['maxstep']/maxstep)
step = factor * pre_step
fh.write("\nCalculated step:\n")
pre_step_list = pre_step.tolist()
fh.write("-----------------------------------------------------------------------\n"
"Center Atomic Step (Bohr)\n"
"Number Number X Y Z\n"
"-----------------------------------------------------------------------\n")
for i in range(len(molecules[0])):
fh.write(" {:>5d} {:>3d} {:>14.9f} {:>14.9f} {:>14.9f}\n".format(
i + 1, molecules[0][i]['na'],
pre_step_list.pop(0), pre_step_list.pop(0), pre_step_list.pop(0)))
fh.write("-----------------------------------------------------------------------\n")
fh.write("Maximum step is {:>11.6}\n".format(maxstep))
fh.write("Scaling factor = {:>6.4f}\n".format(factor))
fh.write("\nFinal step (Bohr):\n")
step_list = step.tolist()
fh.write("-----------------------------------------------------------------------\n"
"Center Atomic Step (Bohr)\n"
"Number Number X Y Z\n"
"-----------------------------------------------------------------------\n")
for i in range(len(molecules[0])):
fh.write(" {:>5d} {:>3d} {:>14.9f} {:>14.9f} {:>14.9f}\n".format(
i + 1, molecules[0][i]['na'],
step_list.pop(0), step_list.pop(0), step_list.pop(0)))
fh.write("-----------------------------------------------------------------------\n")
step_max = np.amax(np.absolute(step))
step_rms = np.sqrt(np.mean(np.square(step)))
fh.write(" Max Step = {:>14.9f} RMS Step = {:>14.9f}\n\n".format(
step_max, step_rms))
return step
class Atom:
def __init__(self, lbl,na,rx,ry,rz,chg,eps,sig):
self.lbl = lbl # Integer
self.na = na # Integer
self.rx = rx # Double
self.ry = ry # Double
self.rz = rz # Double
self.chg = chg # Double
self.eps = eps # Double
self.sig = sig # Double
self.mass = atommass[self.na] # Double