Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
784ab7e
remove UBE df option
lweisburn May 14, 2025
d85a8a8
remove UBE df option
lweisburn May 14, 2025
c84bb88
adding equal_bath option
lweisburn May 16, 2025
2ad2cc8
remove unused doc options
lweisburn May 16, 2025
882fd14
update UBE test
lweisburn May 16, 2025
45903b5
update (but not fix) tests and fix equal_bath
lweisburn Jun 17, 2025
7ce2e31
fix uhf energy change in misc
lweisburn Jun 17, 2025
9bb980a
get rid of redundant uhf_full_e and remove prints
lweisburn Jun 17, 2025
560e681
more standardized hf_etot
lweisburn Jun 17, 2025
eb168de
modify test targets
lweisburn Jun 25, 2025
ad440fc
add schmidt_decomposition description
lweisburn Jul 23, 2025
efa0394
fix bath augmentation out-of-bounds for open-shell systems
May 26, 2026
6888abb
add urdm1_fullbasis for spin density assembly from BE-UCCSD fragments
May 27, 2026
fabaf6d
save mo_coeff_uccsd on fragment after UCCSD for RDM assembly
Jun 4, 2026
62410d7
UBE: Parallel RDM propagation, DF support, bug fixes
Jun 16, 2026
5e155e2
scripts: Add ube_hfcc as console entry point
Jun 16, 2026
d4407f1
fix: convert zip to list in oneshot parallel path so rdm1__ is stored…
Jun 22, 2026
77ca4fb
Fix no-op os.system() call in be_func_parallel and be_func_parallel_u…
Aug 14, 2026
62107b4
Apply ruff format to pass formatting check
Aug 14, 2026
6677d2b
Fix ruff check errors: line length, local import, import sort, f-strings
Aug 14, 2026
977967e
Fix mypy S_ type assignment errors inherited from main (#191)
Aug 14, 2026
5aca29b
Use fobj.mo_coeffs consistently with BE instead of redundant mo_coeff…
Aug 20, 2026
fe78296
Assert custom PySCF ERI support before opposite-spin transform can fa…
Aug 20, 2026
917b129
Replace use_df bool with int_transform from UBE
Aug 20, 2026
45be38c
Fix equal_bath assert checks, activate new test set (Test B) and add …
Aug 21, 2026
6489d08
Fix Sphinx doc build: use :python: role instead of broken :func: refe…
Aug 21, 2026
13717d9
Activate Test B for the three passing hexene tests, cation frozen is …
Aug 22, 2026
d2b6447
Gate anion_frz BE2 as known to fail
Aug 25, 2026
1f6c6ed
ruff formatting
Aug 25, 2026
c6a54ea
Fix urdm1_fullbasis(): Apply fragment projector in local embedding sp…
Aug 28, 2026
95961df
Fix formatting
Aug 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions example/molbe_oneshot_ube_qmmm.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
unrestricted=True, # specify unrestricted calculation
from_chk=False, # can save the UHF as PySCF checkpoint.
# Set to true if running from converged UHF chk
checkfile=None,
) # if not None, will save UHF calculation to a checkfile.
checkfile=None, # if not None, will save UHF calculation to checkfile
opt="SOSCF", # SOSCF and DAMP options: note that damping settings hard-coded
)
# if rerunning from chk (from_chk=True), name the checkfile here
# ecp = ecp) # can add ECP for heavy atoms as: {'Ru': 'def2-SVP'}
# ecp = ecp) # can add ECP for heavy atoms as: {'Ru': 'def2-SVP'}
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,6 @@ where = ["./src"]
include = ["quemb*"]
exclude = []
namespaces = false

[project.scripts]
ube-hfcc = "quemb.scripts.ube_hfcc:main"
25 changes: 16 additions & 9 deletions src/quemb/molbe/be_parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ def run_solver_u(
gcores=full_uhf.full_gcore,
frozen=frozen,
)
return e_f
return e_f, rdm1_tmp, fobj_a._mf.mo_coeff.copy(), fobj_b._mf.mo_coeff.copy()
Comment thread
mscho527 marked this conversation as resolved.


def be_func_parallel(
Expand Down Expand Up @@ -473,7 +473,7 @@ def be_func_parallel(
the error norm, error vector, and the computed energy.
"""
# Set the number of OpenMP threads
os.system("export OMP_NUM_THREADS=" + str(ompnum))
os.environ["OMP_NUM_THREADS"] = str(ompnum)
nprocs = nproc // ompnum

# Update the effective Hamiltonian with potentials
Expand Down Expand Up @@ -603,7 +603,7 @@ def be_func_parallel_u(
Returns the computed energy
"""
# Set the number of OpenMP threads
os.system("export OMP_NUM_THREADS=" + str(ompnum))
os.environ["OMP_NUM_THREADS"] = str(ompnum)
nprocs = nproc // ompnum

with ProcessPool(nprocs) as pool_:
Expand All @@ -623,14 +623,21 @@ def be_func_parallel_u(
)
results.append(result)

energy_list = [result.get() for result in results]

results_list = [result.get() for result in results]
# Store RDMs back into fragment objects
for i, (fobj_a, fobj_b) in enumerate(Fobjs):
e_f, rdm1_tmp, mo_a, mo_b = results_list[i]
fobj_a.rdm1__ = rdm1_tmp[0].copy()
fobj_b.rdm1__ = rdm1_tmp[1].copy()
fobj_a.mo_coeffs = mo_a
fobj_b.mo_coeffs = mo_b
# Compute and return fragment energy
e_1 = 0.0
e_2 = 0.0
e_c = 0.0
for i in range(len(energy_list)):
e_1 += energy_list[i][0]
e_2 += energy_list[i][1]
e_c += energy_list[i][2]
for i in range(len(results_list)):
e_f = results_list[i][0]
e_1 += e_f[0]
e_2 += e_f[1]
e_c += e_f[2]
return (e_1 + e_2 + e_c, (e_1, e_2, e_c))
4 changes: 2 additions & 2 deletions src/quemb/molbe/mbe.py
Comment thread
mscho527 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -1422,7 +1422,7 @@ def localize(
Cpop = multi_dot((C_.T, self.S, C_))
no_core_idx = where(diag(Cpop) > 0.7)[0]
C_ = C_[:, no_core_idx]
S_ = multi_dot((C_.T, self.S, C_))
S_ = multi_dot((C_.T, self.S, C_)) # type: ignore[assignment]
es_, vs_ = eigh(S_)
s_ = sqrt(es_)
s_ = diag(1.0 / s_)
Expand Down Expand Up @@ -1459,7 +1459,7 @@ def localize(
Cpop = diag(Cpop)
no_core_idx = where(Cpop > 0.55)[0]
C_ = C_[:, no_core_idx]
S_ = multi_dot((C_.T, self.S, C_))
S_ = multi_dot((C_.T, self.S, C_)) # type: ignore[assignment]
es_, vs_ = eigh(S_)
s_ = sqrt(es_)
s_ = diag(1.0 / s_)
Expand Down
82 changes: 58 additions & 24 deletions src/quemb/molbe/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ def be2puffin(
checkfile=None,
ecp=None,
frag_type="chemgen",
opt="soscf",
):
"""Front-facing API bridge tailored for SCINE Puffin

Expand Down Expand Up @@ -369,36 +370,56 @@ def be2puffin(
"Using QM/MM Point Charges: Assuming QM structure in Angstrom "
"and MM Coordinates in Bohr !!!"
)
mf1 = scf.UHF(mol).set(
max_cycle=200
) # using SOSCF is more reliable
# mf1 = scf.UHF(mol).set(max_cycle = 200, level_shift = (0.3, 0.2))
# using level shift helps, but not always. level_shift and
# scf.addons.dynamic_level_shift do not seem to work with QM/MM
# note: from the SCINE database, the structure is in Angstrom but
# the MM point charges are in Bohr !!
mf = qmmm.mm_charge(
mf1, pts_and_charges[0], pts_and_charges[1], unit="bohr"
).newton() # mf object, coordinates, charges
mf1 = scf.UHF(mol).set(max_cycle=200)
if opt.upper() == "SOSCF":
# using SOSCF is more reliable
# note: from the SCINE database, the structure is in Angstrom
# but the MM point charges are in Bohr !!
mf = qmmm.mm_charge(
mf1, pts_and_charges[0], pts_and_charges[1], unit="bohr"
).newton() # mf object, coordinates, charges
elif opt.upper() == "DAMP":
mf = qmmm.mm_charge(
mf1, pts_and_charges[0], pts_and_charges[1], unit="bohr"
)
mf.damp = 0.5
mf.diis_start_cycle = 5
else:
mf = scf.UHF(mol).set(max_cycle=200, level_shift=(0.3, 0.2))
if opt.upper() == "SOSCF":
mf = scf.UHF(mol).set(max_cycle=200).newton()
elif opt.upper() == "DAMP":
mf = scf.UHF(mol).set(max_cycle=200)
mf.damp = 0.5
mf.diis_start_cycle = 5
else:
mf = scf.UHF(mol).set(max_cycle=200).newton()
if opt.upper() == "SOSCF":
mf = scf.UHF(mol).set(max_cycle=200).newton()
elif opt.upper() == "DAMP":
mf = scf.UHF(mol).set(max_cycle=200)
mf.damp = 0.5
mf.diis_start_cycle = 5
else: # restricted
if pts_and_charges: # running QM/MM
print(
"Using QM/MM Point Charges: Assuming QM structure in Angstrom and "
"MM Coordinates in Bohr !!!"
)
mf1 = scf.RHF(mol).set(max_cycle=200)
mf = qmmm.mm_charge(
mf1, pts_and_charges[0], pts_and_charges[1], unit="bohr"
).newton()
if use_df or jk is not None:
raise ValueError(
"Setting use_df to false and jk to none: have not tested DF "
"and QM/MM from point charges at the same time"
)
mf1 = scf.RHF(mol).set(max_cycle=200)
if opt.upper() == "SOSCF":
mf = qmmm.mm_charge(
mf1, pts_and_charges[0], pts_and_charges[1], unit="bohr"
).newton()
elif opt.upper() == "DAMP":
mf = qmmm.mm_charge(
mf1, pts_and_charges[0], pts_and_charges[1], unit="bohr"
)
mf.damp = 0.5
mf.diis_start_cycle = 5
elif use_df and jk is None:
mf = scf.RHF(mol).density_fit(auxbasis=df_aux_basis)
else:
Expand Down Expand Up @@ -464,12 +485,22 @@ def be2puffin(
"Using QM/MM Point Charges: Assuming QM structure in Angstrom and "
"MM Coordinates in Bohr !!!"
)
mf = qmmm.mm_charge(
mf,
pts_and_charges[0],
pts_and_charges[1],
unit="bohr",
).newton()
if opt.upper() == "SOSCF":
mf = qmmm.mm_charge(
mf,
pts_and_charges[0],
pts_and_charges[1],
unit="bohr",
).newton()
elif opt.upper() == "DAMP":
mf = qmmm.mm_charge(
mf,
pts_and_charges[0],
pts_and_charges[1],
unit="bohr",
)
mf.damp = 0.5
mf.diis_start_cycle = 5
time_post_mf = time.time()
print("Chkfile electronic energy:", mf.energy_elec(), flush=True)
print("Chkfile e_tot:", mf.e_tot, flush=True)
Expand All @@ -496,7 +527,10 @@ def be2puffin(
# Run oneshot embedding and return system energy

mybe.oneshot(solver=solver, nproc=nproc, ompnum=ompnum)
return mybe.ebe_tot - mybe.ebe_hf
if unrestricted:
return mybe.ebe_tot - mybe.hf_etot
else:
return mybe.ebe_tot - mybe.ebe_hf


def print_energy_cumulant(ecorr, e_V_Kapprox, e_F_dg, e_hf):
Expand Down
35 changes: 21 additions & 14 deletions src/quemb/molbe/pfrag.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import numpy as np
import scipy.linalg
from numpy import (
argsort,
array,
diag_indices,
einsum,
Expand Down Expand Up @@ -469,28 +468,36 @@ def schmidt_decomposition(

# Identify significant environment orbitals based on eigenvalue threshold
Bidx = []

for i in range(len(Eval)):
if thr_bath < np.abs(Eval[i]) < 1.0 - thr_bath:
Bidx.append(i)
# Set the number of orbitals to be taken from the environment orbitals
# Based on an eigenvalue threshold ordering
if norb is not None:
n_frag_ind = len(Frag_sites1)
n_bath_ind = norb - n_frag_ind
ind_sort = argsort(np.abs(Eval))
first_el = [x for x in ind_sort if x < 1.0 - thr_bath][-1 * n_bath_ind]
for i in range(len(Eval)):
if np.abs(Eval[i]) >= first_el:
Bidx.append(i)
else:
for i in range(len(Eval)):
if thr_bath < np.abs(Eval[i]) < 1.0 - thr_bath:
Bidx.append(i)
# add extra orbital(s) from the environment; these will likely have
# Eval close to 1. note: there are normally very few orbitals with a
# Eval[i] <= thr_bath, so adding Bidx from the "front of the list"
# doesn't work. Instead, we add the excluded orbitals closest to the
# thr_bath/1-thr_bath boundary (this is analagous to tightening up
# the bath threshold for the alpha or beta orbitals until they are
# the same size)
excluded = [i for i in range(len(Eval)) if i not in set(Bidx)]
excluded_sorted = sorted(
excluded,
key=lambda i: min(abs(Eval[i] - (1.0 - thr_bath)), abs(Eval[i] - thr_bath)),
)
# Bidx corresponds to sorted Eval and Evec, so this adds indices
# closest to the bath threshold until the bath size reaches norb
for idx in excluded_sorted:
if len(Bidx) >= norb:
break
Bidx.append(idx)

# Initialize the transformation matrix (TA)
TA = zeros([Tot_sites, len(AO_in_frag) + len(Bidx)])
TA[AO_in_frag, : len(AO_in_frag)] = eye(len(AO_in_frag)) # Fragment part
TA[Env_sites1, len(AO_in_frag) :] = Evec[:, Bidx] # Environment part

# return TA, norbs_frag, norbs_bath
return TA, Frag_sites1.shape[0], len(Bidx)


Expand Down
7 changes: 1 addition & 6 deletions src/quemb/molbe/solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,10 +596,6 @@ def be_func_u(
Whether to evaluate the energy. Defaults to False.
relax_density : bool, optional
Whether to relax the density. Defaults to False.
return_vec : bool, optional
Whether to return the error vector. Defaults to False.
ebe_hf : float, optional
Hartree-Fock energy. Defaults to 0.
use_cumulant : bool, optional
Whether to use the cumulant-based energy expression. Defaults to True.
frozen : bool, optional
Expand Down Expand Up @@ -635,10 +631,9 @@ def be_func_u(

assert fobj_a._mf is not None and fobj_b._mf is not None
fobj_a.rdm1__ = rdm1_tmp[0].copy()
fobj_b._rdm1 = (
fobj_a._rdm1 = (
multi_dot((fobj_a._mf.mo_coeff, rdm1_tmp[0], fobj_a._mf.mo_coeff.T)) * 0.5
)

fobj_b.rdm1__ = rdm1_tmp[1].copy()
fobj_b._rdm1 = (
multi_dot((fobj_b._mf.mo_coeff, rdm1_tmp[1], fobj_b._mf.mo_coeff.T)) * 0.5
Expand Down
Loading
Loading