Skip to main content

Point-group symmetry tools on the PySCF atomic-orbital basis

Project description

pgsym

Point-group symmetry tools on the PySCF atomic-orbital basis.

pgsym builds matrix representations of molecular point-group operations directly on the AO basis of a PySCF Mole, and uses them to:

  • detect the point group from the geometry, or build any named group (Cₙ, Cₙᵥ, Dₙₕ, Dₙd, Sₙ, cubic T/Td/Th/O/Oh, icosahedral I/Ih, linear C∞ᵥ/D∞ₕ, atomic O(3));
  • compute character tables (Burnside–Dixon) with Mulliken labels and diatomic term symbols;
  • build irrep projectors and a symmetry-adapted basis, and block-diagonalise any symmetry-invariant AO operator (Fock, density, …) per irrep;
  • label MOs by irrep and report per-irrep occupations (real or complex coefficients);
  • run symmetry-conforming SCF with optional per-irrep occupation constraints;
  • symmetrise geometries/gradients and run symmetry-constrained geometry optimisation.

It is complementary to PySCF's own symmetry handling: operator-centric, works on any Mole (no symmetry=True required), and interoperates with PySCF's irrep names.

Installation

pip install -e .            # runtime (numpy, scipy, pyscf)
pip install -e .[geomopt]   # + geomeTRIC, for sym_optimize (geometry optimisation)
pip install -e .[all]       # everything, incl. test deps

Requires Python ≥ 3.9.

Quickstart

Detect the group, run a symmetry-conforming RHF, and print an irrep-labelled report:

import pyscf.scf
from pyscf import gto
from pgsym import SymmetryDetection, apply_sym_eig

mol = gto.M(atom="O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469", basis="sto-3g")

pg = SymmetryDetection(mol).point_group     # -> C2v
pg.irrep_names()                            # assign Mulliken labels (A1, B1, ...)

mf = pyscf.scf.RHF(mol)
apply_sym_eig(mf, pg)                        # every eig step now stays symmetry-pure
mf.kernel()
mf.analyze()                                 # irrep-labelled MO table + per-irrep occupations

Guided tour

Point groups: construct or detect

from pgsym import PointGroup, SymmetryDetection, Rotation, Reflection

pg = PointGroup.C2v(mol)                     # named factory (axis/origin/tol optional)
pg = SymmetryDetection(mol).point_group      # auto-detect from geometry

# or from explicit generators (the group is the closure):
c2    = Rotation(mol, [0, 0, 1]).representation(2)
sigma = Reflection(mol, [0, 1, 0]).representation()
pg    = PointGroup(mol, [c2, sigma], name="C2v")

pg.order                                     # 4
pg.is_symmetric(mol.intor("int1e_ovlp"))     # True: the overlap is group-invariant

Character tables, projectors, and symmetry-blocked diagonalisation

pg.character_table(); pg.irrep_names()
pg.print_character_table()
pg.decompose_ao()                            # AO multiplicity of each irrep

P = pg.irrep_projectors()                    # {label: (nao, nao) real projector}, sum = I

S = mol.intor("int1e_ovlp")
H = mol.intor("int1e_kin") + mol.intor("int1e_nuc")
e, C, labels = pg.symmetry_eigh(H, S)        # generalised eigenproblem, solved per irrep

MO labelling and per-irrep occupations

from pgsym import classify_mo_irreps, irrep_occupations

labels = classify_mo_irreps(mo_coeff, S, pg)     # mo_coeff shape (2, nao, nmo); real OR complex
occ    = irrep_occupations(dm, pg, S)            # {irrep: electron count}

Symmetry-conforming SCF and constrained occupations

apply_sym_eig(mf, pg) overrides only the eig step of a mean-field object, so every iteration diagonalises the Fock per irrep and the MOs stay symmetry-pure — works for RHF, UHF, ROHF, and the x2c classes (call it after mf.x2c()); mf.pgs = None restores the original solver exactly.

Passing irrep_nelec additionally constrains the occupations per irrep (PySCF-style): listed irreps are held at their electron count and the remaining electrons fill by aufbau. This lets the SCF hold — or converge to — a specific symmetry root, including an excited configuration ordinary aufbau would never reach:

mf = pyscf.scf.RHF(mol)
apply_sym_eig(mf, pg, irrep_nelec={"A1": 6, "B1": 2, "B2": 2})   # {irrep: n} or {irrep: (na, nb)}
mf.kernel()

A scalar n splits na = n - n//2, nb = n//2 for UHF/ROHF (it is the irrep total for RHF); a (na, nb) tuple is used verbatim. The standalone symmetry_constrained_occupation(H, pg, occupations, ...) does the same filling for a given Fock without running an SCF.

Linear molecules and PySCF irrep names

Linear molecules (C∞v/D∞h) are labelled with diatomic term symbols (Sigmag+, Pig, Deltau, …), which map losslessly to PySCF's linear-group irrep names (A1g, E1gx, …):

from pgsym import pyscf_to_term, term_to_pyscf

pyscf_to_term("Dooh", "A1u")     # -> "Sigmau+"
term_to_pyscf("Dooh", "Piu")     # -> ["E1ux", "E1uy"]

Set pg.pyscf_names = True (or pass pyscf_names=True to classify_mo_irreps, naming="pyscf" to irrep_occupations) to render output in PySCF naming. symmetry_constrained_occupation and irrep_nelec accept occupation targets keyed by internal labels, term symbols, or PySCF names interchangeably.

For PySCF's finite abelian groups (D2h and its subgroups: C2h, C2v, D2, Cs, Ci, C2, C1) the Mulliken labels already coincide with PySCF's (including Cs = A'/A"), so PySCF irrep names work directly; pyscf_to_mulliken / mulliken_to_pyscf provide the (identity) conversion. Note the C₂ᵥ B1/B2 plane convention can differ from PySCF's — when cross-checking a symmetry-breaking state against PySCF, compare the physical state (read its per-irrep counts in this package's labels) rather than matching by irrep name.

Geometry symmetrisation and constrained optimisation

from pgsym import symmetrize_molecule, sym_optimize

new_mol, report = symmetrize_molecule(mol, tol=1e-1)   # snap a distorted geometry onto exact symmetry

mf     = pyscf.scf.RHF(mol)
mol_eq = sym_optimize(mf)                               # relax while staying in the point group

sym_optimize auto-detects the group from the starting geometry and needs the geomopt extra; apply_sym_grad(mf, pg) exposes the symmetry-projected gradient scanner for your own optimiser call, and symmetrize_gradient(grad, mol, pg) projects a single (natm, 3) gradient.

Examples

Runnable, self-contained scripts live in examples/ — run any directly, e.g. python examples/01_point_groups.py. See examples/README.md for the full index.

# Script Shows
01 01_point_groups.py building a PointGroup (factory / generators); group invariance
02 02_symmetry_detection.py auto-detecting the point group of several molecules
03 03_character_tables.py character table, Mulliken names, AO decomposition
04 04_projectors_and_blocked_diag.py projectors and per-irrep symmetry_eigh
05 05_mo_irreps_and_occupations.py MO irrep labels and per-irrep occupations
06 06_constrained_occupation.py symmetry_constrained_occupation (degenerate split; capacity guard)
07 07_symmetry_conforming_scf.py apply_sym_eig; analyze(); the pgs=None fallback
08 08_constrained_occupation_scf.py irrep_nelec; an excited root cross-checked against PySCF
09 09_linear_molecules.py linear-group term symbols ↔ PySCF names
10 10_symmetrize_geometry.py symmetrize_molecule
11 11_symmetry_constrained_optimization.py sym_optimize / apply_sym_grad (needs [geomopt])

Documentation

The full reference manual is docs/manual.pdf (build with make in docs/), covering:

  • Overview & installation — capabilities, package layout, and how it relates to PySCF.
  • Theory — the group-theoretic background and algorithms (Burnside–Dixon character tables, isotypic projectors, symmetry-adapted bases, Reynolds averaging).
  • API reference — every public class and function, with signatures and source references.
  • Worked examples — annotated versions of the scripts above, with expected output.

Testing

pytest

The suite runs the standalone verification scripts under tests/ as subprocesses. Real-SCF tests are skipped automatically if the BLAS backend can't be loaded.

License

GPL-3.0-or-later. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pgsym-0.2.1.tar.gz (73.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pgsym-0.2.1-py3-none-any.whl (70.8 kB view details)

Uploaded Python 3

File details

Details for the file pgsym-0.2.1.tar.gz.

File metadata

  • Download URL: pgsym-0.2.1.tar.gz
  • Upload date:
  • Size: 73.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for pgsym-0.2.1.tar.gz
Algorithm Hash digest
SHA256 39f0ef481bbd75ca9a8776cce52f9f751929349599c67469e8b48fcbbc58a882
MD5 e6cd3e6137913a03232cd7f69fde38de
BLAKE2b-256 7e46ce3750078a31a51724d4fcf70dbbf869fa4cfc722ab92df3c79d2267b206

See more details on using hashes here.

File details

Details for the file pgsym-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: pgsym-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 70.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for pgsym-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8f89e2fa92e808df72c45bc446f9c3330cc4cf6e9a11e3d3d261ede1f64353ec
MD5 927b0c66ca6bbd7cc7a3c84c20c7ab82
BLAKE2b-256 cf7db7ed38e37c62a54a98e0c51e291c52e10576cab655bf203ed6845a95bdc4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page