forked from CCQC/optavc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
molecule.py
84 lines (73 loc) · 3.03 KB
/
molecule.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import psi4
import numpy as np
import re
from . import regex
from . import masses
bohr2angstrom = 0.52917721067
class Molecule(object):
def __init__(self, arg):
try:
# if arg is a psi4.Molecule, convert it to a molecule string
molecule_string = arg
if type(arg) is psi4.core.Molecule:
arg.update_geometry()
molecule_string = arg.create_psi4_string_from_molecule()
assert type(molecule_string) is str
# predefine regexes
units_regex = "units" + regex.capture(
regex.lsp_word) + regex.lsp_endline
label_regex = regex.capture(
regex.lsp_atomic_symbol
) + 3 * regex.lsp_signed_double + regex.lsp_endline
xyz_regex = regex.lsp_atomic_symbol + 3 * regex.capture(
regex.lsp_signed_double) + regex.lsp_endline
# define Molecule attributes
self.units = re.search(units_regex,
molecule_string).group(1).strip().lower()
self.labels = [
label_match.group(1).strip().upper()
for label_match in re.finditer(label_regex, molecule_string)
]
self.geom = np.array(
[[float(coord) for coord in xyz_match.groups()]
for xyz_match in re.finditer(xyz_regex, molecule_string)])
self.masses = [masses.get_mass(label) for label in self.labels]
self.natom = len(self.labels)
assert self.units in ("angstrom", "bohr")
except:
raise Exception(
"Invalid argument \n{:s}\n passed to Molecule constructor.".format(molecule_string))
def set_units(self, units):
if units == "angstrom" and self.units == "bohr":
# print(self.geom)
self.units = "angstrom"
self.geom *= bohr2angstrom
elif units == "bohr" and self.units == "angstrom":
self.units = "bohr"
self.geom /= bohr2angstrom
def __len__(self):
return self.natom
def __iter__(self):
geom = self.geom.reshape(-1, 3)
for label, xyz in zip(self.labels, geom):
yield label, xyz
def __str__(self):
ret = "units {:s}\n".format(self.units)
fmt = "{:2s} {: >15.10f} {: >15.10f} {: >15.10f}\n"
for label, coords in self:
ret += fmt.format(label, *coords)
return ret
def __repr__(self):
return str(self)
def copy(self):
return Molecule(str(self))
def set_geometry(self, geom, geom_units=None):
# print(geom)
if geom_units is None: geom_units = self.units
self.units = geom_units
self.geom = geom
def cast_to_psi4_molecule_object(self, fix_com=False, fix_orientation=False):
# psi4.efp_init() # JPM - Maybe the original use case needed this library?
mol = psi4.core.Molecule.from_string(str(self), fix_com=fix_com, fix_orientation=fix_orientation)
mol.update_geometry()
return mol