Molecule
General purpose ‘Molecule’ class where the ‘Molecule’ need not be a molecule at all
Properties and Methods
from_zmat: method
from_pybel: method
from_file: method
__init__(self, atoms, coords, bonds=None, obmol=None, charge=None, name=None, zmatrix=None, dipole_surface=None, potential_surface=None, potential_derivatives=None, source_file=None, guess_bonds=True, **kw):
atoms
:Iterable[str]
atoms specified by name, either full name or short
coords
:np.ndarray
coordinates for the molecule, assumed to be in Bohr by default
bonds
:Iterable[Iterable[int]] | None
bond specification for the molecule
obmol
:Any
OpenBabel molecule for doing conversions
charge
:int | None
Net charge on the molecule
name
:str | None
Name for the molecule
dipole_surface
:DipoleSurface | None
The dipole surface for the system
potential_surface
:PotentialSurface | None
The potential surface for the system
potential_derivatives
:Iterable[np.ndarray] | None
Derivatives of the potential surface
guess_bonds
:bool
Whether or not to guess the bonding arrangement when that would be used
source_file
:str
The data file the molecule was loaded from
kw
:Any
Other bound parameters that might be useful
__repr__(self):
@property
num_atoms(self):
@property
atoms(self):
@property
masses(self):
@property
bonds(self):
@property
coords(self):
@property
sys(self):
@property
formula(self):
@property
multiconfig(self):
@property
name(self):
@property
force_constants(self):
@property
potential_derivatives(self):
@property
potential_surface(self):
@property
dipole_surface(self):
@property
center_of_mass(self):
:returns
:CoordinateSet
No description…
@property
inertial_axes(self):
:returns
:CoordinateSet
No description…
@property
internal_coordinates(self):
@property
normal_modes(self):
:returns
:VibrationalModes
No description…
take_submolecule(self, spec):
Takes a ‘slice’ of a molecule if working with Cartesian coords. If not, need to do some corner case handling for that.
spec
:Any
No description…
:returns
:_
No description…
@property
shape(self):
__len__(self):
__iter__(self):
__getitem__(self, item):
copy(self):
prop(self, name, *args, **kwargs):
load_force_constants(self, file=None):
Loads force constants from a file (or from source_file
if set)
file
:Any
No description…
:returns
:_
No description…
load_potential_derivatives(self, file=None):
Loads potential derivatives from a file (or from source_file
if set)
file
:Any
No description…
:returns
:_
No description…
load_normal_modes(self, file=None):
Loads potential derivatives from a file (or from source_file
if set)
file
:Any
No description…
:returns
:_
No description…
load_potential_surface(self):
load_dipole_surface(self):
principle_axis_frame(self, sel=None, inverse=False):
Gets the principle axis frame(s) for the molecule
mol
:Any
No description…
sel
:Any
selection of atoms to use when getting the Eckart frame
inverse
:bool
whether to return the inverse of the rotations or not
:returns
:MolecularTransformation | List[MolecularTransformation]
No description…
eckart_frame(self, mol, sel=None, inverse=False):
Gets the Eckart frame(s) for the molecule
mol
:Any
No description…
sel
:Any
selection of atoms to use when getting the Eckart frame
inverse
:bool
whether to return the inverse of the rotations or not
:returns
:_
No description…
get_embedded_molecule(self, ref=None):
Returns a Molecule embedded in an Eckart frame if ref is not None, otherwise returns a principle-axis embedded Molecule
:returns
:Molecule
No description…
plot(self, *geometries, figure=None, bond_radius=0.1, atom_radius_scaling=0.25, atom_style=None, bond_style=None, mode='fast', objects=False, **plot_ops):
get_normal_modes(self, **kwargs):
Examples
We’re working on getting actual examples in place, here. For now, here are the unit tests we use, which will help give some flavor of what’s possible
test_log_water = TestManager.test_data("water_OH_scan.log")
test_log_freq = TestManager.test_data("water_freq.log")
test_HOD = TestManager.test_data("HOD_freq.fchk")
test_fchk = TestManager.test_data("water_freq.fchk")
test_log_h2 = TestManager.test_data("outer_H2_scan_new.log")
def test_ImportMolecule(self):
n = 3 # water
m = Molecule.from_file(self.test_fchk)
self.assertEquals(m.atoms, ("O", "H", "H"))
def test_Eckart(self):
scan_file = TestManager.test_data("tbhp_030.log")
ref_file = TestManager.test_data("tbhp_180.fchk")
scan = Molecule.from_file(scan_file)
ref = Molecule.from_file(ref_file)
sel = np.where(ref.masses > 3)[0]
pax_rot = ref.principle_axis_frame(sel=sel) #type: MolecularTransformation
rot_ref = pax_rot.apply(ref)
#transf = scan.principle_axis_frame(sel=sel)
transf = scan.eckart_frame(ref, sel=sel)
tf_test = transf[0].transformation_function
tf_mat = tf_test.transform
self.assertTrue(np.allclose(tf_mat@tf_mat.T - np.eye(3), 0.))
self.assertEquals(tf_test.transf.shape, (4, 4))
for t, m in zip(transf, scan):
new_mol = t(m)
# rot_ref.guess_bonds = False
# ref.guess_bonds = False
# m.guess_bonds = False
# new_mol.guess_bonds = False
# m = m #type: Molecule
# g1, a, b = ref.plot()
# # ref.plot(figure=g1)
# rot_ref.plot(figure=g1)
# g, a, b = new_mol.plot()
# rot_ref.plot(figure=g, atom_style=dict(color='black'))
# g.show()
fuckup = np.linalg.norm(new_mol.coords[sel] - rot_ref.coords[sel])
self.assertLess(fuckup/len(sel), .1)
def test_EckartEmbedDipoles(self):
scan_file = TestManager.test_data("tbhp_030.log")
ref_file = TestManager.test_data("tbhp_180.fchk")
scan = Molecule.from_file(scan_file)
ref = Molecule.from_file(ref_file)
sel = np.where(ref.masses>3)[0]
pax_rot = ref.principle_axis_frame(sel=sel, inverse=True) # type: MolecularTransformation
rot_ref = pax_rot.apply(ref)
transf = scan.eckart_frame(rot_ref, sel=sel)
carts, dips = DipoleSurface.get_log_values(scan_file, keys=("StandardCartesianCoordinates", "OptimizedDipoleMoments"))
rot_dips = np.array([ np.dot(t.transformation_function.transform, d) for t,d in zip(transf, dips) ])
self.assertTrue(np.allclose(np.linalg.norm(dips, axis=1)-np.linalg.norm(rot_dips, axis=1), 0.))
### Visualize dipole surface
dists = np.linalg.norm(carts[1:, 5] - carts[1:, 6], axis=1)
Graphics.default_style['image_size'] = 575
g = GraphicsGrid(nrows=1, ncols=2, padding=((.075, 0), (0, .45)))
p = Plot(dists, rot_dips[:, 0], figure=g[0, 0])
Plot(dists, rot_dips[:, 1], figure=p)
Plot(dists, rot_dips[:, 2], figure=p)
p2= Plot(dists, dips[:, 0], figure=g[0, 1])
Plot(dists, dips[:, 1], figure=p2)
Plot(dists, dips[:, 2], figure=p2)
g.show()
def test_NormalModes(self):
n = 3 # water
with GaussianFChkReader(self.test_fchk) as reader:
parse = reader.parse(("ForceConstants", "AtomicMasses"))
fcs = parse["ForceConstants"].array
masses = parse["AtomicMasses"]
nms = MolecularNormalModes.from_force_constants(fcs, masses)
# ArrayPlot(nms.matrix).show()
self.assertEquals(nms.matrix.shape, (3*n, 3*n))
def test_Plotting(self):
g = Graphics3D(
image_size=[1500, 1500],
plot_range=[[-10, 10]]*3,
backend="VTK"
)
h5 = Molecule.from_file(
self.test_log_h2,
# self.test_fchk,
# bonds = [
# [0, 1, 1],
# [0, 2, 1]
# ]
)
h5.plot(
figure=g
# mode='3D',
# bond_style= { "circle_points": 24 },
# atom_style= { "sphere_points": 24 }
)
m = Molecule.from_file(
self.test_fchk,
bonds = [
[0, 1, 1],
[0, 2, 1]
]
)
m.plot(
figure=g
# mode='3D',
# bond_style= { "circle_points": 24 },
# atom_style= { "sphere_points": 24 }
)
# g.show()
def test_BondGuessing(self):
m = Molecule.from_file(self.test_fchk)
self.assertEquals(m.bonds, [[0, 1, 1], [0, 2, 1]])
def test_Frags(self):
m = Molecule.from_file(self.test_fchk)
self.assertEquals(len(m.prop("fragments")), 1)
def test_HODModes(self):
# oops fucked up getting D out
m = Molecule.from_file(self.test_HOD, bonds=[[0, 1, 1], [0, 2, 1]])
modes = m.normal_modes
self.assertEquals(m.atoms, ("O", "H", "D"))
self.assertEquals(tuple(np.round(modes.freqs)), (1422.0, 2810.0, 3874.0))
def test_H2OModes(self):
m = Molecule.from_file(self.test_fchk, bonds=[[0, 1, 1], [0, 2, 1]])
modes = m.normal_modes
self.assertEquals(m.atoms, ("O", "H", "H"))
self.assertEquals(tuple(np.round(modes.freqs)), (1622.0, 3803.0, 3938.0))
def test_RenormalizeGaussianModes(self):
with GaussianFChkReader(self.test_HOD) as gr:
parse = gr.parse(["Coordinates", "Gradient", "AtomicMasses",
"ForceConstants", "ForceDerivatives", "VibrationalModes", "VibrationalData"])
coords = UnitsData.convert("Angstroms", "AtomicUnitOfLength") * parse["Coordinates"]
masses = UnitsData.convert("AtomicMassUnits", "AtomicUnitOfMass") * parse["AtomicMasses"]
modes = parse["VibrationalModes"].T
freqs = parse["VibrationalData"]["Frequencies"]
fcs = parse["ForceConstants"].array
sad = UnitsData.convert("Hartrees", "Wavenumbers") * np.sqrt(np.diag(np.dot(np.dot(modes.T, fcs), modes)))
modes = modes * freqs/sad
print( UnitsData.convert("Hartrees", "Wavenumbers") * np.sqrt(np.diag(np.dot(np.dot(modes.T, fcs), modes))))
masses = np.broadcast_to(masses, (len(masses), 3)).T.flatten()
# print(modes-np.linalg.pinv(modes).T)
print(np.dot(np.dot(modes.T, np.diag(masses)), modes))
modes_2 = Molecule.from_file(self.test_HOD).get_normal_modes(normalize=False)
mm = modes_2._basis.matrix
print(np.dot(np.dot(mm.T, np.diag(masses)), mm))
print(UnitsData.convert("Hartrees", "Wavenumbers") * np.sqrt(np.diag(np.dot(np.dot(mm.T, fcs), mm))))
# print(modes._basis.matrix.T.dot(m.force_constants).shape)
def test_VisualizeNormalModes(self):
from Psience.Molecools.Vibrations import MolecularVibrations, MolecularNormalModes
from McUtils.Plots import GraphicsGrid, Graphics3D
m = Molecule.from_file(self.test_fchk, bonds = [[0, 1, 1], [0, 2, 1]])
with GaussianFChkReader(self.test_fchk) as reader:
parse = reader.parse(("VibrationalModes", "VibrationalData"))
modes = parse["VibrationalModes"].T
test_freqs = parse["VibrationalData"]["Frequencies"]
nms = m.normal_modes
realvibs = MolecularVibrations(m, basis=MolecularNormalModes(modes, freqs=test_freqs))
plot_vibrations = False
if plot_vibrations:
nmodes = 1
mode_start = 0
g = GraphicsGrid(nrows=2, ncols=nmodes,
graphics_class=Graphics3D,
plot_range = [[-2, 2], [-2, 2], [-2, 2]],
fig_kw = dict(figsize = (17, 5)),
tighten = True
)
for i in range(nmodes):
nms.visualize(step_size=.1, figure = g[0, i], which=mode_start + i,
anim_opts= dict(interval = 10)
)
for i in range(nmodes):
realvibs.visualize(step_size=.1, figure = g[1, i], which= mode_start+i,
anim_opts= dict(interval = 10)
)
g.show()
self.assertEquals(tuple(round(a, 4) for a in nms.freqs), tuple(round(a, 4) for a in test_freqs))
Edit Examples or
Create New Examples
Edit Template or
Create New Template
Edit Docstrings