diff --git a/ebcc/dump.py b/ebcc/dump.py index da076c74..2f65628c 100644 --- a/ebcc/dump.py +++ b/ebcc/dump.py @@ -219,19 +219,23 @@ def read(self, cls, log=None): amplitudes = load(self.name, "amplitudes") lambdas = load(self.name, "lambdas") if spin_type == "U": - amplitudes = { - key: (util.Namespace(**val) if isinstance(val, dict) else val) - for key, val in amplitudes.items() - } - lambdas = { - key: (util.Namespace(**val) if isinstance(val, dict) else val) - for key, val in lambdas.items() - } - amplitudes = util.Namespace(**amplitudes) - lambdas = util.Namespace(**lambdas) + if amplitudes is not None: + amplitudes = { + key: (util.Namespace(**val) if isinstance(val, dict) else val) + for key, val in amplitudes.items() + } + amplitudes = util.Namespace(**amplitudes) + if lambdas is not None: + lambdas = { + key: (util.Namespace(**val) if isinstance(val, dict) else val) + for key, val in lambdas.items() + } + lambdas = util.Namespace(**lambdas) else: - amplitudes = util.Namespace(**amplitudes) - lambdas = util.Namespace(**lambdas) + if amplitudes is not None: + amplitudes = util.Namespace(**amplitudes) + if lambdas is not None: + lambdas = util.Namespace(**lambdas) # Initialise the EBCC object cc = cls( diff --git a/examples/00-ccsd.py b/examples/00-ccsd.py index 965dfaeb..20459bc5 100644 --- a/examples/00-ccsd.py +++ b/examples/00-ccsd.py @@ -1,15 +1,22 @@ +""" +Example of a simple CCSD calculation. +""" + import numpy as np from pyscf import gto, scf from ebcc import EBCC +# Define the molecule using PySCF mol = gto.Mole() mol.atom = "H 0 0 0; F 0 0 1.1" mol.basis = "cc-pvdz" mol.build() +# Run a Hartree-Fock calculation using PySCF mf = scf.RHF(mol) mf.kernel() +# Run a CCSD calculation using EBCC ccsd = EBCC(mf) ccsd.kernel() diff --git a/examples/01-ebccsd.py b/examples/01-ebccsd.py index 0b07d53f..e6d01620 100644 --- a/examples/01-ebccsd.py +++ b/examples/01-ebccsd.py @@ -1,3 +1,7 @@ +""" +Example of some electron-boson CCSD calculations. +""" + import numpy as np from pyscf import gto, scf @@ -5,11 +9,13 @@ np.random.seed(123) +# Define the molecule using PySCF mol = gto.Mole() mol.atom = "H 0 0 0; F 0 0 1.1" mol.basis = "cc-pvdz" mol.build() +# Run a Hartree-Fock calculation using PySCF mf = scf.RHF(mol) mf.kernel() @@ -26,13 +32,13 @@ # v v v v # ____ _ _ _ # CCSD-S-1-1: One-boson amplitudes and one-boson-one-fermion coupling -ccsd = EBCC( +ccsd_s_1_1 = EBCC( mf, ansatz="CCSD-S-1-1", omega=omega, g=g, ) -ccsd.kernel() +ccsd_s_1_1.kernel() # ,--------- Fermionic ansatz # | ,------ Bosonic excitation amplitudes @@ -41,13 +47,13 @@ # v v v v # ____ __ _ _ # CCSD-SD-1-1: Two-boson amplitudes and one-boson-one-fermion coupling -ccsd = EBCC( +ccsd_sd_1_1 = EBCC( mf, ansatz="CCSD-SD-1-1", omega=omega, g=g, ) -ccsd.kernel() +ccsd_sd_1_1.kernel() # ,--------- Fermionic ansatz # | ,------ Bosonic excitation amplitudes @@ -56,10 +62,10 @@ # v v v v # ____ __ _ _ # CCSD-SD-1-2: Two-boson amplitudes and two-boson-one-fermion coupling -ccsd = EBCC( +ccsd_sd_1_2 = EBCC( mf, ansatz="CCSD-SD-1-2", omega=omega, g=g, ) -ccsd.kernel() +ccsd_sd_1_2.kernel() diff --git a/examples/02-eom_uccsd.py b/examples/02-eom_uccsd.py index 16a50529..54d43055 100644 --- a/examples/02-eom_uccsd.py +++ b/examples/02-eom_uccsd.py @@ -1,18 +1,27 @@ +""" +Example of a CCSD calculation using a UHF reference and a subsequent +EOM-CCSD calculation for the ionization potential. +""" + import numpy as np from pyscf import gto, scf from ebcc import UEBCC +# Define the molecule using PySCF mol = gto.Mole() mol.atom = "H 0 0 0; F 0 0 1.1" mol.basis = "cc-pvdz" mol.build() +# Run a UHF calculation using PySCF mf = scf.UHF(mol) mf.kernel() -ccsd = UEBCC(mf) +# Run a UCCSD calculation +ccsd = UEBCC(mf, ansatz="CCSD") ccsd.kernel() +# Run an EOM-CCSD calculation eom = ccsd.ip_eom() eom.kernel() diff --git a/examples/03-cc2.py b/examples/03-cc2.py index 9b381b27..a92f1cf9 100644 --- a/examples/03-cc2.py +++ b/examples/03-cc2.py @@ -1,16 +1,22 @@ +""" +Example of a CC2 calculation. +""" + import numpy as np from pyscf import gto, scf from ebcc import EBCC +# Define the molecule using PySCF mol = gto.Mole() mol.atom = "H 0 0 0; F 0 0 1.1" mol.basis = "cc-pvdz" mol.build() +# Run a RHF calculation using PySCF mf = scf.RHF(mol) mf.kernel() -ccsd = EBCC(mf, ansatz="CC2") -ccsd.kernel() - +# Run a CC2 calculation +cc2 = EBCC(mf, ansatz="CC2") +cc2.kernel() diff --git a/examples/04-ccsdt_active_space.py b/examples/04-ccsdt_active_space.py index d42c3734..bbe5b943 100644 --- a/examples/04-ccsdt_active_space.py +++ b/examples/04-ccsdt_active_space.py @@ -1,25 +1,35 @@ +""" +Example of a CCSDt' calculation with T3 amplitudes in an active +space. +""" + import numpy as np from pyscf import gto, scf from ebcc import REBCC, Space +# Define the molecule using PySCF mol = gto.Mole() mol.atom = "H 0 0 0; F 0 0 1.1" mol.basis = "cc-pvdz" mol.build() +# Run a RHF calculation using PySCF mf = scf.RHF(mol) mf.kernel() -frozen = np.zeros_like(mf.mo_occ, dtype=bool) -active = np.zeros_like(mf.mo_occ, dtype=bool) +# Define the occupied, frozen, and active spaces +occupied = mf.mo_occ > 0 +frozen = np.zeros_like(occupied) +active = np.zeros_like(occupied) active[mol.nelectron // 2 - 1] = True # HOMO active[mol.nelectron // 2] = True # LUMO space = Space( - mf.mo_occ > 0, + occupied, frozen, active, ) +# Run a CCSDt' calculation ccsdt = REBCC(mf, ansatz="CCSDt'", space=space) ccsdt.kernel() diff --git a/examples/05-bccd.py b/examples/05-bccd.py new file mode 100644 index 00000000..f0254f93 --- /dev/null +++ b/examples/05-bccd.py @@ -0,0 +1,25 @@ +""" +Example of a Brueckner orbital calculation using a CCSD reference. +""" + +import numpy as np +from pyscf import gto, scf + +from ebcc import EBCC + +# Define the molecule using PySCF +mol = gto.Mole() +mol.atom = "H 0 0 0; F 0 0 1.1" +mol.basis = "cc-pvdz" +mol.build() + +# Run a RHF calculation using PySCF +mf = scf.RHF(mol) +mf.kernel() + +# Run a CCSD calculation +ccsd = EBCC(mf, ansatz="CCSD") +ccsd.kernel() + +# Run a Brueckner orbital calculation using the CCSD reference +ccsd.brueckner(e_tol=1e-6, t_tol=1e-5) diff --git a/examples/06-restart.py b/examples/06-restart.py new file mode 100644 index 00000000..592ae4d5 --- /dev/null +++ b/examples/06-restart.py @@ -0,0 +1,37 @@ +""" +Example of saving and restarting an EBCC calculation. +""" + +import os +import numpy as np +from pyscf import gto, scf + +from ebcc import REBCC + +# Define the molecule using PySCF +mol = gto.Mole() +mol.atom = "H 0 0 0; F 0 0 1.1" +mol.basis = "cc-pvdz" +mol.build() + +# Run a RHF calculation using PySCF +mf = scf.RHF(mol) +mf.kernel() + +# Run a CC3 calculation that does not converge +cc3 = REBCC(mf, ansatz="CC3") +cc3.options.max_iter = 5 +cc3.kernel() + +# Save the calculation to a file +cc3.write("restart.h5") + +# Load the calculation from the file +cc3 = REBCC.read("restart.h5") + +# Run the calculation again, but this time with a higher max_iter +cc3.options.max_iter = 20 +cc3.kernel() + +# Delete the file +os.remove("restart.h5") diff --git a/examples/07-rdms.py b/examples/07-rdms.py new file mode 100644 index 00000000..ddb40898 --- /dev/null +++ b/examples/07-rdms.py @@ -0,0 +1,42 @@ +""" +Example obtaining RDMs from EBCC. +""" + +import numpy as np +from pyscf import gto, scf, lib + +from ebcc import GEBCC + +# Define the molecule using PySCF +mol = gto.Mole() +mol.atom = "H 0 0 0; F 0 0 1.1" +mol.basis = "cc-pvdz" +mol.build() + +# Run a RHF calculation using PySCF +mf = scf.RHF(mol) +mf.kernel() + +# Convert the RHF object to a GHF object (not necessary for density +# matrices, just simplifies the Hamiltonian) +mf = mf.to_ghf() + +# Run a CCSD calculation +ccsd = GEBCC(mf, ansatz="CCSD") +ccsd.kernel() + +# If the Λ amplitudes are not solved, EBCC will use the approximation +# Λ = T* and warn the user. +ccsd.solve_lambda() + +# Fermionic RDMs +dm1 = ccsd.make_rdm1_f() +dm2 = ccsd.make_rdm2_f() + +# Compare the energies +h1 = np.linalg.multi_dot((mf.mo_coeff.T, mf.get_hcore(), mf.mo_coeff)) +h2 = ccsd.get_eris().array +e_rdm = lib.einsum("pq,qp->", h1, dm1) +e_rdm += lib.einsum("pqrs,pqrs->", h2, dm2) * 0.5 +e_rdm += mol.energy_nuc() +assert np.allclose(e_rdm, ccsd.e_tot) diff --git a/examples/08-mp2.py b/examples/08-mp2.py new file mode 100644 index 00000000..e9c07621 --- /dev/null +++ b/examples/08-mp2.py @@ -0,0 +1,27 @@ +""" +Example of a simple MP2 calculation. +""" + +import numpy as np +from pyscf import gto, scf + +from ebcc import REBCC + +# Define the molecule using PySCF +mol = gto.Mole() +mol.atom = "H 0 0 0; F 0 0 1.1" +mol.basis = "cc-pvdz" +mol.build() + +# Run a Hartree-Fock calculation using PySCF +mf = scf.RHF(mol) +mf.kernel() + +# The CC solver can be used for Moller-Plesset perturbation theory, +# and EBCC will detect that convergence isn't necessary via the +# `ansatz.is_one_shot` attribute. +mp2 = REBCC(mf, ansatz="MP2") +mp2.kernel() + +# Note that this is not the most efficient way to compute MP energies, +# as EBCC will still generate bare amplitudes stored in memory. diff --git a/examples/09-spin_conversion.py b/examples/09-spin_conversion.py new file mode 100644 index 00000000..0540b70e --- /dev/null +++ b/examples/09-spin_conversion.py @@ -0,0 +1,36 @@ +""" +Example of converting restricted and unrestricted EBCC calculations +to spin-orbital (GHF) EBCC calculations. +""" + +import numpy as np +from pyscf import gto, scf + +from ebcc import REBCC, UEBCC, GEBCC + +# Define the molecule using PySCF +mol = gto.Mole() +mol.atom = "H 0 0 0; F 0 0 1.1" +mol.basis = "cc-pvdz" +mol.build() + +# Run a RHF calculation using PySCF +rhf = scf.RHF(mol) +rhf.kernel() + +# Run a REBCC calculation +rcc = REBCC(rhf, ansatz="QCISD") +rcc.kernel() + +# Convert to unrestricted and run kernel - unless the UEBCC solution +# breaks some symmetry this should converge immediately to the same +# solution as the REBCC calculation. +uebcc_from_rebcc = UEBCC.from_rebcc(rcc) +uebcc_from_rebcc.kernel() + +# Conversion of REBCC to GEBCC goes via a UEBCC intermediate, here +# we just convert the UEBCC object we just created. Once again, in +# the absence of symmetry breaking this should converge immediately +# to the same solution as the REBCC calculation. +gebcc_from_uebcc = GEBCC.from_uebcc(uebcc_from_rebcc) +gebcc_from_uebcc.kernel() diff --git a/examples/10-frozen_core.py b/examples/10-frozen_core.py new file mode 100644 index 00000000..b2ee46c4 --- /dev/null +++ b/examples/10-frozen_core.py @@ -0,0 +1,38 @@ +""" +Example of running a CCSD calculation with frozen core orbitals. +""" + +import numpy as np +from pyscf import gto, scf, lib + +from ebcc import REBCC, Space + +# Define the molecule using PySCF +mol = gto.Mole() +mol.atom = "H 0 0 0; F 0 0 1.1" +mol.basis = "cc-pvdz" +mol.build() + +# Run a RHF calculation using PySCF +mf = scf.RHF(mol) +mf.kernel() + +# Define the occupied, frozen, and active spaces. In `ebcc`, the active +# space refers to orbitals that are active within a correlated regime, +# frozen refers to orbitals that are not correlated, and any orbitals +# that are neither frozen nor active are considered correlated. The +# active space is reserved for methods such as CCSDt' (see example 4). +# For more details see `ebcc.space`. +occupied = mf.mo_occ > 0 +frozen = np.zeros_like(occupied) +active = np.zeros_like(occupied) +frozen[:2] = True # Freeze the two lowest energy orbitals +space = Space(occupied, frozen, active) + +# Run a CCSD calculation, first without the frozen core +ccsd = REBCC(mf, ansatz="CCSD") +ccsd.kernel() + +# Now run a CCSD calculation with the frozen core +ccsd_froz = REBCC(mf, ansatz="CCSD", space=space) +ccsd_froz.kernel()