Designing the AOBasisSet Component
Following from Quantum electrons., the Atom
, Molecule
, and
ChemicalSystem
classes store their electrons implicitly. This usually
entails storing the number of electrons and the total spin. When it comes time
to model the actual electrons one needs to build wavefunctions (or densities).
The first step in this process is defining a basis set. For the purpose of this
page we assume that the basis set will be comprised of atom-centered atomic
orbitals (AOs).
What is an AOBasisSet?
AOs are the one-electron orbitals describing electrons within an atom. By an AO
basis set we mean a set of AOs used as a basis set for forming molecular
orbitals (MOs). The AO basis set is limited to representing the parameters
necessary to construct the set of AOs, i.e., representing the space spanned
by the AOs is outside the scope of the AOBasisSet
class (as detailed in
Architecture of Wavefunction Component representing this space falls to the
AOSpace
class).
Why do we need an AOBasisSet?
To compute integrals involving AOs we must be able to construct the AOs. This
requires us to know the parameters defining those AOs. The AOBasisSet
class
is a one-stop shop for those parameters. Most users will construct an
AOBasisSet
(usually by specifying its name, e.g., 6-31G*) and never need
to otherwise manipulate the AOBasisSet.
AOBasisSet Considerations
Topics in this section were considered in designing the AOBasisSet component of Chemist and ultimately were addressed by the design.
- Performance
Integrals involving AOs are some of the lowest-lying operations. The
AOBasisSet
needs to be interoperable with existing highly-optimized integral libraries. In particular:Exponents, coefficients stored in a manner that is vectorizable.
float vs. double. Integrals are a performance critical piece of the code, being able to use floats, instead of doubles, leads to significant speed ups (when doing so does not compromise accuracy).
views also allow others to use existing basis sets seamlessly with our infrastructure, without copying!
- Spherical vs. Cartesian
Terming the \(e^{-\alpha r^2}\) part of a Gaussian basis function the “radial” part, the angular part can take on one of two forms “spherical” (in which case the radial/angular labels actually work) or “Cartesian” (in which case the radial/angular labels are sloppy). We need to know this information to properly compute the integrals.
As a corollary, the distinction between spherical and Cartesian Gaussians primarily impacts the integrals library. Consumers of the integrals only care about the number of Gaussians. Point being, the design should adhere to established norms that spherical/Cartesian Gaussians can be used largely interchangeable.
- Tracking Order
For tensors represented in an AO basis the order of the AOs will determine the ordering of the tensor elements. For tensors to be compatible they must have the same order. The AO basis set should be able to work out if this is the case or not.
This point is important to note because most other codes do NOT track this information.
This information can be stored implicitly and only generated when needed.
- AO to Center Mapping
We usually center multiple AOs on the same point. Since the overlap of AOs decays with the distance between them, the sparsity pattern tends to depend on where AOs are centered. It is thus helpful to collect AOs on the same center together.
- Atomic Basis Name
Most of the time users use pre-tabulated AO basis set parameters. Convention has established names for the various parameter sets, e.g., STO-3G, cc-pVDZ, and 6-31G*. While the parameters often suffice, there are circumstances where we may need the name. Examples include: mapping to auxillary basis sets, and interfacing with codes which do not expose parameter-based APIs.
We note that mixing basis sets, say using cc-pVDZ on hydrogens and aug-cc-pVDZ on non-hydrogens, is not unheard of. Thus we may need to specify the basis name per center.
- Atomic number tracking
In practice one usually specifies an atomic basis set on a per atom basis. This requires the name of the basis set and the atomic number of the atom.
- Hierarchical
While we think of atom-centered basis sets as simple sets of basis functions, the reality is those functions have a somewhat complicated hierarchical form, i.e., primitives often combine to form contracted Gaussian functions which then are paired with either a Cartesian or spherical piece to form atomic orbitals which are then grouped into shells which are then grouped into atomic basis sets which finally are combined to form molecular basis sets. Our classes should also be able to represent this hierarchy.
- Class flattening
For better or worse much of the infrastructure written to support AO integrals is function-based and not object-oriented. Point being to use an object-oriented basis set with existing infrastructure we often need to operate with the primitives directly. For convenience each level of the hierarchy should be able to flatten itself, i.e., we should be able to iterate over primitives in an object, regardless of the nesting.
Out of Scope
Topics in this section were considered, but ultimately do not play a role in
the current implementation of the AOBasisSet
class. Many of these
considerations may need to
- Segmented vs general
Contracted Gaussians can come in two forms: segmented and general. Both are linear combinations of primitive Gaussian functions. The difference is that each segmented contracted Gaussian is comprised of different primitive Gaussians, whereas with general contracted Gaussians multiple contractions share the same primitives.
The vast majority of existing infrastructure targets segmented contractions so we have prioritized segmented contractions. While very inefficient, one can use segmented basis infrastructure for general contractions by copying the primitives.
An efficient solution would likely require writing a class parallel to
Shell
which acts like a container ofContractedGaussians
, but is also keyed into the underlying general nature of the basis.
- Effective core potentials
For heavy atoms it is common to replace the core electrons with a potential. In practice ECPs are basis set specific and thus have repercussions for the basis set.
ECPs will be needed down the road. A potential place for storing the ECP is the
AtomicBasisSet
class.
- Not Gaussian-Based
AOs can be comprised of many types of functions. Atom-centered AOS are overwhelming created from Gaussian functions and our current design prioritizes Gaussian-based AOs.
With some modifications the current design could be adapted to other AO types, such as Slater orbitals. This would require introducing new classes for the parameters of the other functions. It also may void some naming conventions and would thus be API breaking.
AOBasisSet Design
Fig. 7 pictorially describes the AOBasisSet
component of Chemist. The top-level class is the AOBasisSet
class (located
in the top-right corner of the diagram). The
remaining classes in the diagram form the internal, hierarchical structure of
the AOBasisSet
class. The AOBasisSet
class itself is little more than a
container of AtomicBasisSet
objects.
The AtomicBasisSet
class groups AOs by center. This is
in response to consideration AO to Center Mapping. More specifically,
all members of the same AtomicBasisSet
object are centered on the same
point.
The Shell
class represents all the AOs with a given total angular momentum
which can be formed from a ContractedGaussian
object. At present, the AOs
in the Shell
class are implicit and can not be individually accessed.
Allowing the user to directly access the AO
objects would satisfy
consideration Tracking Order. In practice, the Shell
class adds
two pieces of state: the total angular momentum and whether the AOs are
Cartesian or spherical (in turn satisfying Spherical vs. Cartesian).
The ContractedGaussian
class is a container around a set of Primitive
objects. The Primitive
class holds the exponent, where the Primitive
is centered, and coefficient.
Not shown in Fig. 7 are the respective view classes.
Associated with Primitive
, ContractedGaussian
, and Shell
are
the classes PrimitiveView
, ContractedGaussianView
, and ShellView
.
Additional views could be added for the AO
class and the AtomicBasisSet
classes. Each view has the same API as its namesake class (e.g.,
PrimitiveView
has the same API as Primitive
), but different memory
ownership semantics. In particular, the view versions alias their state whereas
the non-views own it. The view versions are necessary to maintain an array-of-
structure API, while actually implementing a structure-of-arrays to be
consistent with Performance.
Summary
Briefly, the design of the AOBasisSet component addresses the considerations raised in AOBasisSet Considerations by:
- Performance
Our design addresses performance primarily by a separation of the API from the implementation. More specifically, the containers behave like arrays of structures, but under the hood are implemented as structures of arrays. The trick
- Spherical vs. Cartesian
The design contains two provisions for spherical AOs vs. Cartesian AOs. The first is in the
CartesianAO
andSphericalAO
classes which are envisioned as providing direct access to the parameters defining a particular AO. The second is theShell
class which knows if the AOs forming the shell are spherical or Cartesian.- Tracking Order
The design calls for the
AO
,CartesianAO
, andSphericalAO
classes, which can be used to iterate over the AOs in aShell
.- AO to Center Mapping
The
AtomicBasisSet
class stores all of the AOs (grouped into shells) which reside on the same center.- Atomic Basis Name
The
AtomicBasisSet
class is capable of storing the name of the basis set.