Skip to content

Commit f2f999e

Browse files
authored
Patch v1.3.1 (#68)
* Adding box highlighting functionality to MatrixPlotter and argument to disable energies in ContourPlotter * Refactoring TEI code and adding benchmark * Expanding matrix visualization and updating manual * Fixing bug in BlenderRender
1 parent 41fc9b9 commit f2f999e

File tree

15 files changed

+318
-56
lines changed

15 files changed

+318
-56
lines changed
144 KB
Loading

docs/basis_functions.rst

Lines changed: 81 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,43 +6,100 @@ Basis functions
66
.. contents:: Table of Contents
77
:depth: 3
88

9-
Gaussian type orbitals (GTO)
10-
============================
9+
Gaussian Type Orbitals (GTOs)
10+
=============================
1111

12-
:program:`PyQInt` uses cartesian Gaussian type orbitals as given by
12+
:program:`PyQInt` employs *Cartesian Gaussian Type Orbitals* (GTOs), defined as
1313

1414
.. math::
1515
16-
\Phi(\alpha,l,m,n,\vec{R}) = N (x - X)^{l} (y - Y)^{m} (z - Z)^{n} \exp \left(- \alpha |\vec{r} - \vec{R}|^{2} \right)
16+
\Phi(\alpha, l, m, n, \vec{R}) =
17+
N (x - X)^{l} (y - Y)^{m} (z - Z)^{n}
18+
\exp \left(- \alpha \lvert \vec{r} - \vec{R} \rvert^{2} \right)
1719
18-
wherein :math:`\alpha` is the exponent, :math:`\vec{R} = \left(X,Y,Z\right)` the
19-
position of the orbital, :math:`(l,m,n)` the orders of the pre-exponential
20-
polynomial, and :math:`N` a normalization constant such that
20+
Here, :math:`\alpha` denotes the orbital exponent,
21+
:math:`\vec{R} = (X, Y, Z)` the position of the orbital center,
22+
:math:`(l, m, n)` the orders of the Cartesian polynomial prefactor,
23+
and :math:`N` a normalization constant chosen such that
2124

2225
.. math::
2326
24-
\left< \Phi | \Phi \right> = 1
27+
\langle \Phi \mid \Phi \rangle = 1
2528
26-
GTOs are a fundamental building block of CGF (see below) and typically a user
27-
would not directly work with them (a notable exception is provided below).
28-
Nevertheless, GTO objects can be constructed as follows::
29+
GTO Construction
30+
****************
2931

30-
from pyqint import PyQInt, CGF, GTO
32+
Gaussian Type Orbitals form the fundamental building blocks of *Contracted
33+
Gaussian Functions* (CGFs; see below). In typical workflows, users interact
34+
with CGFs rather than individual GTOs. Nevertheless, GTOs can be constructed
35+
explicitly when needed via::
3136

32-
coeff = 1.0 # coefficients only have meaning for GTOs within a CGF
33-
alpha = 0.5
34-
l,m,n = 0,0,0
35-
p = (0,0,0)
36-
G = GTO(coeff, p, alpha, l, m, n)
37+
from pyqint import GTO
38+
39+
coeff = 1.0 # linear expansion coefficient
40+
alpha = 0.5 # exponent
41+
l, m, n = 0, 0, 0
42+
position = (0.0, 0.0, 0.0)
43+
44+
gto = GTO(coeff, position, alpha, l, m, n)
3745

3846
.. note::
39-
* The normalization constant is automatically calculated by `PyQInt` based
40-
on the value of :math:`\alpha` and :math:`(l,m,n)` and does not have
41-
to be supplied by the user.
42-
* If you work with individual GTOs, the first parameter to construct the GTO
43-
should have a value of 1.0. This first parameter corresponds to the linear
44-
expansion coefficient used in the formulation of Contracted Gaussian Functions
45-
(see below).
47+
48+
- The normalization constant :math:`N` is computed automatically by
49+
:program:`PyQInt` based on the values of :math:`\alpha` and
50+
:math:`(l, m, n)` and must not be supplied explicitly.
51+
- When working with individual GTOs (i.e. not as part of a CGF), the
52+
coefficient should be set to ``1.0``. This parameter represents the
53+
linear expansion coefficient used internally by CGFs.
54+
55+
Retrieving GTO Data Members
56+
***************************
57+
58+
The :code:`GTO` class is intentionally designed as a *flat and accessible*
59+
Python object. All defining parameters of the orbital are stored as public
60+
data members and can be accessed directly.
61+
62+
The following attributes are available:
63+
64+
- ``c`` — linear expansion coefficient
65+
- ``p`` — orbital center :math:`(X, Y, Z)`
66+
- ``alpha`` — Gaussian exponent
67+
- ``l``, ``m``, ``n`` — Cartesian polynomial orders
68+
69+
For example::
70+
71+
coeff = gto.c
72+
position = gto.p
73+
alpha = gto.alpha
74+
l = gto.l
75+
m = gto.m
76+
n = gto.n
77+
78+
print("Coefficient:", coeff)
79+
print("Position:", position)
80+
print("Exponent:", alpha)
81+
print("Orders:", (l, m, n))
82+
83+
These attributes fully define the mathematical form of the primitive Gaussian
84+
orbital and may be inspected or reused when constructing higher-level objects,
85+
such as CGFs.
86+
87+
Evaluating a GTO
88+
****************
89+
90+
In addition to direct data access, a GTO provides methods for numerical
91+
evaluation. For example, the orbital amplitude at a point
92+
:math:`(x, y, z)` can be obtained using::
93+
94+
value = gto.get_amp(x, y, z)
95+
96+
The normalization constant used internally may be retrieved via::
97+
98+
norm = gto.get_norm()
99+
100+
This separation between *data members* and *evaluation routines* allows users
101+
to both inspect the orbital parameters and efficiently compute values when
102+
needed.
46103

47104
Contracted Gaussian Functions (CGF)
48105
===================================

docs/matrix_visualization.rst

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,66 @@ In this example:
108108

109109
.. figure:: _static/img/coefficient-co.png
110110

111-
Coefficient matrix of CO, visualized using the :code:`MatrixPlotter` class.
111+
Coefficient matrix of CO, visualized using the :code:`MatrixPlotter` class.
112+
113+
Highlighting elements
114+
---------------------
115+
116+
In some cases, it is useful not only to visualize a matrix, but also to
117+
emphasize specific elements or regions within it. This can be achieved using
118+
the ``boxes`` argument.
119+
120+
The ``boxes`` argument expects a list of 5-tuples. Each tuple defines a
121+
rectangular region and consists of the following elements, in order:
122+
123+
- the starting x-coordinate
124+
- the starting y-coordinate
125+
- the width of the rectangle
126+
- the height of the rectangle
127+
- the color of the rectangle
128+
129+
The example below demonstrates how to draw red, green, and blue rectangles on
130+
top of a matrix visualization.
131+
132+
.. code-block:: python
133+
134+
from pyqint import MoleculeBuilder, HF, MatrixPlotter
135+
136+
def main():
137+
mol = MoleculeBuilder().from_name('CO')
138+
res = HF(mol, 'sto3g').rhf(verbose=False)
139+
140+
labels = MatrixPlotter.generate_ao_labels([
141+
('O', ('1s', '2s', '2p')),
142+
('C', ('1s', '2s', '2p')),
143+
])
144+
145+
# highlighting boxes
146+
boxes = [
147+
(0,0,1,1,'#BB0000'),
148+
(1,2,3,1,'#00BB00'),
149+
(3,4,1,3,'#0000BB'),
150+
]
151+
152+
MatrixPlotter.plot_matrix(
153+
mat=res['overlap'],
154+
filename='overlap-co.png',
155+
xlabels=labels,
156+
ylabels=labels,
157+
xlabelrot=0,
158+
title='Overlap',
159+
boxes=boxes
160+
)
161+
162+
if __name__ == '__main__':
163+
main()
164+
165+
.. figure:: _static/img/overlap-co-boxes.png
166+
167+
Coefficient matrix of CO, visualized using the :code:`MatrixPlotter` class,
168+
where a number of elements have been highlighted.
169+
170+
.. note::
171+
172+
The ``(x, y)`` coordinates use **zero-based indexing**. Consequently, the
173+
top-left corner of the first box is specified as ``(0, 0)``.

examples/benzene_tei.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# for this basis set
66
integrator = PyQInt()
77
st = time.perf_counter()
8-
mol = MoleculeBuilder.from_name('cubane')
8+
mol = MoleculeBuilder.from_name('benzene')
99
cgfs, nuclei = mol.build_basis('sto3g')
1010
S, T, V, tetensor = integrator.build_integrals_openmp(cgfs, nuclei)
1111
end = time.perf_counter()

examples/cubane.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from pyqint import MoleculeBuilder, PyQInt
2+
import time
3+
4+
# simple test to see how long it takes to build the two-electron integrals
5+
# for this basis set
6+
integrator = PyQInt()
7+
st = time.perf_counter()
8+
mol = MoleculeBuilder.from_name('cubane')
9+
cgfs, nuclei = mol.build_basis('sto3g')
10+
S, T, V, tetensor = integrator.build_integrals_openmp(cgfs, nuclei)
11+
end = time.perf_counter()
12+
print('Elapsed time: %.2f s' % (end - st))

examples/matrixplotter.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from pyqint import MoleculeBuilder, HF, MatrixPlotter
2+
3+
def main():
4+
mol = MoleculeBuilder().from_name('CO')
5+
res = HF(mol, 'sto3g').rhf(verbose=False)
6+
7+
labels = MatrixPlotter.generate_ao_labels([
8+
('O', ('1s', '2s', '2p')),
9+
('C', ('1s', '2s', '2p')),
10+
])
11+
12+
# highlighting boxes
13+
boxes = [
14+
(0,0,1,1,'#BB0000'),
15+
(1,2,3,1,'#00BB00'),
16+
(3,4,1,3,'#0000BB'),
17+
]
18+
19+
MatrixPlotter.plot_matrix(
20+
mat=res['overlap'],
21+
filename='overlap-co.png',
22+
xlabels=labels,
23+
ylabels=labels,
24+
xlabelrot=0,
25+
title='Overlap',
26+
boxes=boxes
27+
)
28+
29+
if __name__ == '__main__':
30+
main()

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ module-path = "src"
4343
# ---------------------------------------------------------------------------
4444
[project]
4545
name = "pyqint"
46-
version = "1.3.0"
46+
version = "1.3.1"
4747
description = "Python package for evaluating integrals of Gaussian type orbitals"
4848
readme = "README.md"
4949
license = { text = "GPL-3.0-only" }

scripts/bench.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from pyqint import PyQInt, MoleculeBuilder
2+
import timeit
3+
import statistics
4+
5+
mol = MoleculeBuilder.from_name('benzene')
6+
cgfs, nuclei = mol.build_basis('sto3g')
7+
integrator = PyQInt()
8+
9+
def tei():
10+
tetensor = integrator.build_tei_openmp(cgfs)
11+
12+
runs = timeit.repeat(
13+
tei,
14+
repeat=5, # number of independent runs
15+
number=1 # iterations per run
16+
)
17+
18+
print(f"Mean: {statistics.mean(runs):.6f}s")
19+
print(f"Std dev: {statistics.stdev(runs):.6f}s")
20+
print(f"Min: {min(runs):.6f}s")

src/pyqint/blenderrender.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,11 +237,13 @@ def __build_isosurface(self, filename, scalarfield, isovalue, sz=5):
237237
isovalue = np.abs(isovalue)
238238
unitcell = np.diag(np.ones(3) * 2 * sz)
239239

240-
# try to import PyTessel but do not throw an error if it cannot be loaded
241240
try:
242241
from pytessel import PyTessel
243-
except ModuleNotFoundError:
244-
print('Cannot find module PyTessel')
242+
except ModuleNotFoundError as exc:
243+
raise ImportError(
244+
"Isosurface rendering requires the optional dependency 'pytessel'. "
245+
"Please install it (e.g. `pip install pytessel`)."
246+
) from exc
245247

246248
pytessel = PyTessel()
247249
vertices, normals, indices = pytessel.marching_cubes(scalarfield.flatten(), scalarfield.shape, unitcell.flatten(), isovalue)

src/pyqint/contour.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def build_contourplot(
2424
dpi: int = 144,
2525
ngrid: int = 5,
2626
labels=None,
27+
plot_energies = True,
2728
):
2829
"""
2930
Generate a grid of contour plots for molecular orbitals.
@@ -41,6 +42,7 @@ def build_contourplot(
4142
plane : str or tuple
4243
Plane specification:
4344
- str: one of {'xy', 'xz', 'yz'}
45+
- list with str
4446
- tuple: (atom_i, atom_j, atom_k, up_direction)
4547
sz : float
4648
Half-width of the plotted plane.
@@ -80,6 +82,10 @@ def build_contourplot(
8082
grid = ContourPlotter.__create_cartesian_grid(sz, npts, plane)
8183
xlabel = '$%s$ [a.u.]' % plane[0]
8284
ylabel = '$%s$ [a.u.]' % plane[1]
85+
elif isinstance(plane, list):
86+
grid = ContourPlotter.__create_cartesian_grid(sz, npts, plane[orb_idx])
87+
xlabel = '$%s$ [a.u.]' % plane[orb_idx][0]
88+
ylabel = '$%s$ [a.u.]' % plane[orb_idx][1]
8389
else:
8490
# Arbitrary plane defined by three atoms
8591
grid = ContourPlotter.__build_plane_from_atoms(
@@ -140,14 +146,15 @@ def build_contourplot(
140146
if ylabel is not None:
141147
ax[i,j].set_ylabel(ylabel)
142148

143-
# Title handling
144149
if labels is not None:
145-
ax[i, j].set_title(
146-
r"%s (%.4f Ht)"
147-
% (labels[orb_idx], res["orbe"][orb_idx])
148-
)
150+
orblabel = labels[orb_idx]
149151
else:
150-
ax[i, j].set_title(r"$\psi_{%i}$ (%.4f Ht)" % (orb_idx + 1, res["orbe"][orb_idx]))
152+
orblabel = r'$\psi_{%i}$' % (orb_idx + 1)
153+
154+
if plot_energies:
155+
orblabel += r' (%.4f Ht)' % res["orbe"][orb_idx]
156+
157+
ax[i, j].set_title(orblabel)
151158

152159
plt.tight_layout()
153160
plt.savefig(filename)

0 commit comments

Comments
 (0)