Quick Start Guide

This guide will walk you through your first QuasiX calculations in under 5 minutes.

Prerequisites

Ensure QuasiX is installed. See Installation for detailed instructions.

# Verify installation
import quasix
print(f"QuasiX version: {quasix.__version__}")

Example 1: G0W0 Calculation for Water

The simplest GW calculation - one-shot G0W0 starting from Hartree-Fock:

from quasix import G0W0Driver
from pyscf import gto, scf

# Define water molecule
mol = gto.M(
    atom='''
    O  0.000000  0.000000  0.117369
    H  0.000000  0.756950 -0.469476
    H  0.000000 -0.756950 -0.469476
    ''',
    basis='def2-svp',
    unit='angstrom'
)

# Run Hartree-Fock
mf = scf.RHF(mol)
mf.kernel()

# Run G0W0
gw = G0W0Driver(mf)
result = gw.kernel()

# Print results
print(f"HOMO (HF):   {result.homo_dft:.3f} eV")
print(f"HOMO (G0W0): {result.homo_qp:.3f} eV")
print(f"LUMO (G0W0): {result.lumo_qp:.3f} eV")
print(f"Gap (G0W0):  {result.gap_qp:.3f} eV")

Expected output:

HOMO (HF):   -13.62 eV
HOMO (G0W0): -12.57 eV
LUMO (G0W0):  1.23 eV
Gap (G0W0):  13.80 eV

Example 2: evGW for Better Accuracy

Eigenvalue self-consistent GW improves accuracy, especially for band gaps:

from quasix import evGWDriver
from pyscf import gto, scf, dft

# Use DFT (PBE) starting point
mol = gto.M(
    atom='O 0 0 0; H 0 0.757 0.587; H 0 -0.757 0.587',
    basis='def2-tzvp'
)
mf = dft.RKS(mol)
mf.xc = 'pbe'
mf.kernel()

# Run eigenvalue self-consistent GW
gw = evGWDriver(mf, max_iter=10, conv_tol=1e-4)
result = gw.kernel()

print(f"evGW HOMO: {result.homo_qp:.3f} eV")
print(f"Converged in {result.n_iter} iterations")

Tip

evGW@PBE0 typically gives the most accurate ionization potentials, with MAD = 0.29 eV compared to experimental values (CCSD(T)).

Example 3: BSE Optical Spectrum

Calculate optical excitations for benzene using BSE:

from quasix import BSEDriver
from pyscf import gto, scf

# Benzene molecule
mol = gto.M(
    atom='''
    C  1.3970  0.0000  0.0000
    C  0.6985  1.2098  0.0000
    C -0.6985  1.2098  0.0000
    C -1.3970  0.0000  0.0000
    C -0.6985 -1.2098  0.0000
    C  0.6985 -1.2098  0.0000
    H  2.4810  0.0000  0.0000
    H  1.2405  2.1486  0.0000
    H -1.2405  2.1486  0.0000
    H -2.4810  0.0000  0.0000
    H -1.2405 -2.1486  0.0000
    H  1.2405 -2.1486  0.0000
    ''',
    basis='def2-svp'
)
mf = scf.RHF(mol).run()

# Run BSE
bse = BSEDriver(mf, n_states=10, spin='singlet')
result = bse.kernel()

# Print excitation energies
print("Excitation Energies (eV):")
for i, (E, f) in enumerate(zip(result.excitation_energies,
                                result.oscillator_strengths)):
    print(f"  S{i+1}: {E:.3f} eV  (f = {f:.4f})")

# Generate absorption spectrum
energies, spectrum = bse.get_spectrum(
    energy_range=(4.0, 10.0),
    broadening=0.1
)

Example 4: Custom Calculation Parameters

G0W0 with Custom Frequency Grid

from quasix import G0W0Driver

gw = G0W0Driver(
    mf,
    n_freq=64,               # Number of frequency points
    freq_method='minimax',   # 'minimax' or 'gauss-legendre'
    eta=0.01,                # Broadening parameter (Ha)
)
result = gw.kernel()

Choosing the QP Solver

from quasix import G0W0Driver

# Linearized QP equation (fast, usually sufficient)
gw = G0W0Driver(mf, qp_solver='linearized')

# Newton solver (more accurate for difficult cases)
gw = G0W0Driver(mf, qp_solver='newton', newton_tol=1e-6)

Example 5: Parallel Execution

Configure parallelism for large calculations:

import os
# Set BEFORE importing quasix
os.environ['RAYON_NUM_THREADS'] = '8'

from quasix import G0W0Driver

gw = G0W0Driver(mf, n_workers=8)
result = gw.kernel()

Output Analysis

Accessing Quasiparticle Energies

# Access all QP energies (in eV)
print("All QP energies (eV):")
for i, e in enumerate(result.qp_energies):
    occ = "occ" if i < result.homo_idx + 1 else "vir"
    print(f"  Orbital {i:3d} ({occ}): {e:10.4f} eV")

# Frontier orbitals
print(f"\nHOMO index: {result.homo_idx}")
print(f"HOMO: {result.homo_qp:.4f} eV")
print(f"LUMO: {result.lumo_qp:.4f} eV")
print(f"Gap:  {result.gap_qp:.4f} eV")

Self-Energy Analysis

# Exchange self-energy (eV)
homo = result.homo_idx
print(f"Sigma_x(HOMO): {result.sigma_x[homo]:.4f} eV")

# Correlation self-energy (eV)
print(f"Sigma_c(HOMO): {result.sigma_c[homo]:.4f} eV")

# Renormalization factor
print(f"Z(HOMO): {result.z_factor[homo]:.4f}")

# QP correction breakdown
dft_homo = result.dft_energies[homo]
sigma_total = result.sigma_x[homo] + result.sigma_c[homo]
print(f"\nQP correction breakdown for HOMO:")
print(f"  DFT energy:    {dft_homo:.4f} eV")
print(f"  Sigma_x:       {result.sigma_x[homo]:.4f} eV")
print(f"  Sigma_c:       {result.sigma_c[homo]:.4f} eV")
print(f"  Z factor:      {result.z_factor[homo]:.4f}")
print(f"  QP energy:     {result.homo_qp:.4f} eV")

Comparing Starting Points

Different DFT functionals give different G0W0 results:

from quasix import G0W0Driver
from pyscf import gto, scf, dft

mol = gto.M(atom='O 0 0 0; H 0 0.757 0.587; H 0 -0.757 0.587',
            basis='def2-tzvp')

# G0W0@HF
mf_hf = scf.RHF(mol).run()
gw_hf = G0W0Driver(mf_hf).kernel()
print(f"G0W0@HF HOMO: {gw_hf.homo_qp:.3f} eV")

# G0W0@PBE
mf_pbe = dft.RKS(mol)
mf_pbe.xc = 'pbe'
mf_pbe.run()
gw_pbe = G0W0Driver(mf_pbe).kernel()
print(f"G0W0@PBE HOMO: {gw_pbe.homo_qp:.3f} eV")

# G0W0@PBE0
mf_pbe0 = dft.RKS(mol)
mf_pbe0.xc = 'pbe0'
mf_pbe0.run()
gw_pbe0 = G0W0Driver(mf_pbe0).kernel()
print(f"G0W0@PBE0 HOMO: {gw_pbe0.homo_qp:.3f} eV")

Common Workflow: GW + BSE

A typical workflow for optical properties:

from quasix import G0W0Driver, BSEDriver
from pyscf import gto, dft

# 1. Build molecule and run DFT
mol = gto.M(atom='...', basis='def2-tzvp')
mf = dft.RKS(mol)
mf.xc = 'pbe0'
mf.run()

# 2. Run GW for quasiparticle energies
gw = G0W0Driver(mf)
gw_result = gw.kernel()
print(f"QP gap: {gw_result.gap_qp:.3f} eV")

# 3. Run BSE for optical excitations
bse = BSEDriver(mf, gw_result=gw_result, n_states=10)
bse_result = bse.kernel()
print(f"Optical gap: {bse_result.excitation_energies[0]:.3f} eV")

# 4. Compute exciton binding energy
E_bind = gw_result.gap_qp - bse_result.excitation_energies[0]
print(f"Exciton binding energy: {E_bind:.3f} eV")

Next Steps