From f497564214436fd1396cdd06d83758d60881c992 Mon Sep 17 00:00:00 2001 From: Ollie Backhouse Date: Thu, 22 Jun 2023 13:19:48 +0100 Subject: [PATCH 1/5] Adds black to pyproject.toml --- pyproject.toml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 49ce6f193..05a40ac92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,6 +78,7 @@ dev = [ "mpi4py>=3.0.0", "dyson @ git+https://github.com/BoothGroup/dyson@master", "ebcc", + "black>=22.6.0", "pytest", "pytest-cov", ] @@ -117,3 +118,18 @@ markers = [ "slow", "veryslow", ] + +[tool.black] +line-length = 120 +target-version = [ + "py38", + "py39", + "py310", +] +include = "vayesta" +exclude = """ +/( + | *__pycache__* + | .git +)/ +""" From 4de3baf6e026cfd18574808436fbcd8d05d4477b Mon Sep 17 00:00:00 2001 From: Charles Scott Date: Wed, 23 Aug 2023 11:59:57 +0100 Subject: [PATCH 2/5] Small tweak to black configuration. --- pyproject.toml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 05a40ac92..811ee52db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -126,10 +126,9 @@ target-version = [ "py39", "py310", ] -include = "vayesta" -exclude = """ +include="\\.py" +extend-exclude = """ /( - | *__pycache__* - | .git -)/ + __pycache__* +/) """ From 3f80f568262b20fb82fbaa3d780d8a0d5cfebc3f Mon Sep 17 00:00:00 2001 From: Charles Scott Date: Fri, 25 Aug 2023 10:54:16 +0100 Subject: [PATCH 3/5] Reformat all code with black. --- docs/source/conf.py | 29 +- docs/source/quickstart/1d_hubbard.py | 16 +- docs/source/quickstart/edmetfinite.py | 23 +- examples/dmet/01-simple-dmet.py | 26 +- examples/dmet/02-rotational-symmetry.py | 22 +- examples/dmet/03-density-matrices.py | 16 +- examples/dmet/04-h-ring-diis.py | 16 +- examples/dmet/63-hubbard-1d.py | 10 +- examples/dmet/64-hubbard-2d.py | 20 +- examples/dmet/65-hubbard-2d-charge-sc.py | 18 +- examples/dmet/70-h-ring-benchmark.py | 27 +- examples/edmet/01-1d-hubbard-comparison.py | 30 +- examples/edmet/10-bosonic-Hamiltonians.py | 30 +- examples/edmet/30-start-from-dft.py | 20 +- examples/edmet/70-h-ring-benchmark-df.py | 45 +- examples/edmet/72-h-chain-benchmark.py | 25 +- examples/ewf/molecules/01-simple-ccsd.py | 4 +- examples/ewf/molecules/02-simple-uccsd.py | 4 +- examples/ewf/molecules/03-bno-threshold.py | 4 +- examples/ewf/molecules/04-bath-options.py | 53 +- examples/ewf/molecules/05-ccsd-amplitudes.py | 4 +- examples/ewf/molecules/07-corrections.py | 17 +- examples/ewf/molecules/11-fragmentation.py | 4 +- examples/ewf/molecules/12-custom-fragments.py | 14 +- .../ewf/molecules/15-chemical-potential.py | 8 +- examples/ewf/molecules/20-dump-clusters.py | 73 +- examples/ewf/molecules/21-fci-solver.py | 8 +- examples/ewf/molecules/23-tccsd-solver.py | 12 +- examples/ewf/molecules/24-delta-tailor.py | 23 +- .../ewf/molecules/24-tailor-with-fragments.py | 25 +- .../ewf/molecules/25-externally-correct.py | 36 +- examples/ewf/molecules/26-ebcc-solvers.py | 25 +- .../ewf/molecules/31-population-analysis.py | 27 +- examples/ewf/molecules/32-static-corrfuncs.py | 19 +- .../molecules/35-screening-rpa-corrections.py | 28 +- .../ewf/molecules/36-crpa-cas-screening.py | 23 +- examples/ewf/molecules/37-bosonic-bath.py | 21 +- examples/ewf/molecules/40-spectral-moments.py | 28 +- .../molecules/41-ccsd-incluster-moments.py | 6 +- .../ewf/molecules/42-fci-incluster-moments.py | 6 +- examples/ewf/molecules/51-intercluster-mp2.py | 17 +- examples/ewf/molecules/55-dm-energy.py | 15 +- examples/ewf/molecules/56-dm-energy-uccsd.py | 15 +- examples/ewf/molecules/61-lda.py | 6 +- examples/ewf/molecules/71-modify-hcore.py | 24 +- examples/ewf/molecules/72-orbital-plotting.py | 22 +- .../ewf/molecules/73-fragment-symmetry.py | 18 +- .../ewf/molecules/74-multiple-rotations.py | 24 +- examples/ewf/molecules/90-mpi.py | 4 +- examples/ewf/other/35-self-consistency.py | 16 +- examples/ewf/other/51-hubbard-1D.py | 8 +- examples/ewf/other/52-hubbard-2D.py | 16 +- examples/ewf/other/61-ext-corr-hubbard-1D.py | 101 ++- examples/ewf/other/71-sc-h-ring.py | 21 +- examples/ewf/solids/01-simple-sym.py | 10 +- examples/ewf/solids/01-simple.py | 10 +- examples/ewf/solids/02-exact-limit-uhf.py | 16 +- examples/ewf/solids/02-exact-limit.py | 10 +- examples/ewf/solids/03-cubic-BN.py | 17 +- examples/ewf/solids/04-kpts-vs-supercell.py | 25 +- examples/ewf/solids/14-density-matrix.py | 14 +- examples/ewf/solids/20-mRPA-interactions.py | 15 +- examples/ewf/solids/55-dm-energy.py | 23 +- examples/ewf/solids/56-dm-energy-uccsd.py | 23 +- examples/ewf/solids/61-start-from-lda.py | 10 +- examples/ewf/solids/73-rotational-symmetry.py | 20 +- examples/run_examples.py | 13 +- examples/scmf/01-scmf.py | 2 +- .../01-simple-vembedding.py | 32 +- setup.py | 30 +- vayesta/__init__.py | 55 +- vayesta/core/ao2mo/helper.py | 341 ++++---- vayesta/core/ao2mo/kao2gmo.py | 151 ++-- vayesta/core/ao2mo/postscf_ao2mo.py | 142 ++-- vayesta/core/ao2mo/pyscf_eris.py | 84 +- vayesta/core/bath/__init__.py | 6 + vayesta/core/bath/bath.py | 7 +- vayesta/core/bath/bno.py | 385 +++++---- vayesta/core/bath/dmet.py | 123 +-- vayesta/core/bath/ewdmet.py | 13 +- vayesta/core/bath/full.py | 10 +- vayesta/core/bath/helper.py | 73 +- vayesta/core/bath/r2bath.py | 61 +- vayesta/core/bath/rpa.py | 47 +- vayesta/core/bosonic_bath/bbath.py | 28 +- .../bosonic_bath/projected_interactions.py | 61 +- vayesta/core/bosonic_bath/qba_rpa.py | 28 +- vayesta/core/cmdargs.py | 53 +- vayesta/core/eris.py | 44 +- vayesta/core/foldscf.py | 122 +-- vayesta/core/fragmentation/__init__.py | 5 + vayesta/core/fragmentation/cas.py | 36 +- vayesta/core/fragmentation/fragmentation.py | 120 +-- vayesta/core/fragmentation/helper.py | 8 +- vayesta/core/fragmentation/iao.py | 110 ++- vayesta/core/fragmentation/iaopao.py | 38 +- vayesta/core/fragmentation/sao.py | 9 +- vayesta/core/fragmentation/site.py | 4 +- vayesta/core/fragmentation/ufragmentation.py | 15 +- vayesta/core/helper.py | 34 +- vayesta/core/linalg.py | 60 +- vayesta/core/qemb/corrfunc.py | 172 ++-- vayesta/core/qemb/fragment.py | 476 +++++++---- vayesta/core/qemb/qemb.py | 569 +++++++------ vayesta/core/qemb/rdm.py | 154 ++-- vayesta/core/qemb/register.py | 1 - vayesta/core/qemb/ufragment.py | 120 +-- vayesta/core/qemb/uqemb.py | 107 +-- vayesta/core/scmf/__init__.py | 1 + vayesta/core/scmf/brueckner.py | 92 +- vayesta/core/scmf/pdmet.py | 28 +- vayesta/core/scmf/scmf.py | 35 +- vayesta/core/screening/screening_crpa.py | 38 +- vayesta/core/screening/screening_moment.py | 53 +- vayesta/core/spinalg.py | 32 +- vayesta/core/symmetry/group.py | 19 +- vayesta/core/symmetry/operation.py | 101 +-- vayesta/core/symmetry/symmetry.py | 100 ++- vayesta/core/symmetry/tsymmetry.py | 120 +-- vayesta/core/types/bosonic_orbitals.py | 37 +- vayesta/core/types/cluster.py | 87 +- vayesta/core/types/ebwf/__init__.py | 7 +- vayesta/core/types/ebwf/ebcc.py | 71 +- vayesta/core/types/ebwf/ebwf.py | 10 +- vayesta/core/types/orbitals.py | 75 +- vayesta/core/types/wf/__init__.py | 33 +- vayesta/core/types/wf/ccsd.py | 105 ++- vayesta/core/types/wf/ccsdtq.py | 4 +- vayesta/core/types/wf/cisd.py | 52 +- vayesta/core/types/wf/cisdtq.py | 5 +- vayesta/core/types/wf/fci.py | 233 +++--- vayesta/core/types/wf/hf.py | 26 +- vayesta/core/types/wf/mp2.py | 58 +- vayesta/core/types/wf/project.py | 75 +- vayesta/core/types/wf/t_to_c.py | 10 +- vayesta/core/types/wf/wf.py | 12 +- vayesta/core/util.py | 251 ++++-- vayesta/core/vlog.py | 81 +- vayesta/core/vpyscf/ccsd_rdm.py | 525 ++++++------ vayesta/core/vpyscf/uccsd_rdm.py | 785 +++++++++--------- vayesta/dmet/__init__.py | 1 + vayesta/dmet/dmet.py | 61 +- vayesta/dmet/fragment.py | 16 +- vayesta/dmet/pdmet.py | 21 +- vayesta/dmet/sdp_sc.py | 6 +- vayesta/dmet/udmet.py | 20 +- vayesta/dmet/ufragment.py | 38 +- vayesta/dmet/updates.py | 5 +- vayesta/edmet/__init__.py | 1 + vayesta/edmet/edmet.py | 55 +- vayesta/edmet/fragment.py | 467 ++++++----- vayesta/edmet/uedmet.py | 1 - vayesta/edmet/ufragment.py | 119 ++- vayesta/ewf/__init__.py | 5 +- vayesta/ewf/amplitudes.py | 44 +- vayesta/ewf/ewf.py | 195 +++-- vayesta/ewf/fragment.py | 245 +++--- vayesta/ewf/helper.py | 6 +- vayesta/ewf/icmp2.py | 285 ++++--- vayesta/ewf/rdm.py | 250 +++--- vayesta/ewf/uewf.py | 28 +- vayesta/ewf/ufragment.py | 149 ++-- vayesta/ewf/urdm.py | 642 +++++++------- vayesta/lattmod/__init__.py | 1 + vayesta/lattmod/bethe.py | 44 +- vayesta/lattmod/latt.py | 204 ++--- vayesta/libs/__init__.py | 8 +- vayesta/misc/brueckner.py | 28 +- vayesta/misc/corrfunc.py | 127 +-- vayesta/misc/counterpoise.py | 68 +- vayesta/misc/cptbisect.py | 28 +- vayesta/misc/cubefile.py | 173 ++-- vayesta/misc/gmtkn55/__init__.py | 29 +- vayesta/misc/gto_helper.py | 23 +- vayesta/misc/molecules/molecules.py | 185 +++-- vayesta/misc/pcdiis.py | 7 +- vayesta/misc/solids/solids.py | 197 ++--- .../calc_nonorth_couplings.py | 1 - .../variational_params.py | 24 +- vayesta/mpi/__init__.py | 3 +- vayesta/mpi/interface.py | 26 +- vayesta/mpi/rma.py | 63 +- vayesta/mpi/scf.py | 9 +- vayesta/rpa/rirpa/NI_eval.py | 58 +- vayesta/rpa/rirpa/RIRPA.py | 163 +--- vayesta/rpa/rirpa/RIURPA.py | 13 +- vayesta/rpa/rirpa/RIdRRPA.py | 62 +- vayesta/rpa/rirpa/energy_NI.py | 14 +- vayesta/rpa/rirpa/momzero_NI.py | 22 +- vayesta/rpa/rpa.py | 22 +- vayesta/rpa/ssrpa.py | 95 +-- vayesta/rpa/ssurpa.py | 9 +- vayesta/solver/__init__.py | 7 +- vayesta/solver/_uccsd_eris.py | 19 +- vayesta/solver/ccsd.py | 32 +- vayesta/solver/ccsdtq.py | 38 +- vayesta/solver/coupling.py | 235 +++--- vayesta/solver/dump.py | 64 +- vayesta/solver/eb_fci/ebfci.py | 33 +- vayesta/solver/eb_fci/ebfci_slow.py | 210 +++-- vayesta/solver/eb_fci/uebfci_slow.py | 213 +++-- vayesta/solver/ebcc.py | 80 +- vayesta/solver/ebfci.py | 16 +- vayesta/solver/ext_ccsd.py | 4 +- vayesta/solver/fci.py | 17 +- vayesta/solver/hamiltonian.py | 219 +++-- vayesta/solver/mp2.py | 56 +- vayesta/solver/solver.py | 45 +- vayesta/solver/tccsd.py | 16 +- vayesta/tests/common.py | 12 +- vayesta/tests/core/ao2mo/test_helper.py | 28 +- vayesta/tests/core/ao2mo/test_kao2gmo.py | 59 +- .../tests/core/ao2mo/test_postscf_ao2mo.py | 74 +- .../tests/core/ao2mo/test_postscf_kao2gmo.py | 25 +- vayesta/tests/core/ao2mo/test_pyscf_eris.py | 18 +- vayesta/tests/core/bath/test_bath.py | 47 +- vayesta/tests/core/bath/test_bosonic_bath.py | 14 +- vayesta/tests/core/qemb/test_fragment.py | 206 +++-- vayesta/tests/core/qemb/test_integrals.py | 42 +- vayesta/tests/core/qemb/test_rdm.py | 31 +- vayesta/tests/core/qemb/test_symmetry.py | 29 +- vayesta/tests/core/scmf/test_scmf.py | 23 +- vayesta/tests/core/test_foldscf.py | 6 +- vayesta/tests/core/test_linalg.py | 13 +- vayesta/tests/core/test_util.py | 98 +-- vayesta/tests/core/wf/test_ebcc_wf.py | 24 +- vayesta/tests/core/wf/test_wf.py | 104 ++- vayesta/tests/dmet/test_hubbard.py | 76 +- vayesta/tests/dmet/test_molecule.py | 87 +- vayesta/tests/edmet/test_dfedmet_hubbard.py | 25 +- vayesta/tests/edmet/test_dfedmet_molecule.py | 86 +- vayesta/tests/edmet/test_edmet_hubbard.py | 76 +- vayesta/tests/edmet/test_edmet_molecule.py | 124 ++- vayesta/tests/ewf/test_bosonic_bath.py | 13 +- vayesta/tests/ewf/test_corrfunc.py | 96 ++- vayesta/tests/ewf/test_diamond.py | 39 +- vayesta/tests/ewf/test_extcorr.py | 491 +++++++---- vayesta/tests/ewf/test_fbc.py | 6 +- vayesta/tests/ewf/test_h2.py | 28 +- vayesta/tests/ewf/test_h2_pbc.py | 180 ++-- vayesta/tests/ewf/test_hubbard.py | 117 ++- vayesta/tests/ewf/test_icmp2.py | 29 +- vayesta/tests/ewf/test_molecules.py | 448 +++++----- vayesta/tests/ewf/test_moments.py | 24 +- vayesta/tests/ewf/test_nelectron_target.py | 38 +- vayesta/tests/ewf/test_rdm_energy.py | 25 +- vayesta/tests/ewf/test_rpa_bath.py | 17 +- vayesta/tests/ewf/test_rpa_correction.py | 9 +- vayesta/tests/ewf/test_screening.py | 36 +- vayesta/tests/ewf/test_secfrag.py | 22 +- vayesta/tests/ewf/test_tailoring.py | 63 +- vayesta/tests/ewf/test_water.py | 12 +- vayesta/tests/ewf/test_water_sao.py | 35 +- vayesta/tests/latt/test_bethe.py | 37 +- vayesta/tests/latt/test_hubbard1d.py | 6 +- vayesta/tests/latt/test_hubbard2d.py | 14 +- vayesta/tests/misc/test_molstructs.py | 103 +-- vayesta/tests/misc/test_varembedding.py | 21 +- vayesta/tests/rpa/test_rirpa_hubbard.py | 19 +- vayesta/tests/rpa/test_rirpa_molecule.py | 21 +- vayesta/tests/rpa/test_rirpa_solids.py | 15 +- vayesta/tests/rpa/test_rpa_hubbard.py | 62 +- vayesta/tests/rpa/test_rpa_molecule.py | 21 +- vayesta/tests/solver/test_ccsd.py | 15 +- vayesta/tests/solver/test_cisd.py | 11 +- vayesta/tests/solver/test_ebcc.py | 43 +- vayesta/tests/solver/test_fci.py | 17 +- vayesta/tests/solver/test_mp2.py | 15 +- vayesta/tests/testsets/test_testset.py | 34 +- vayesta/tests/testsystems.py | 176 ++-- vayesta/tools/eos.py | 88 +- vayesta/tools/plotting/colors.py | 225 ++--- vayesta/tools/plotting/mpl_mol.py | 49 +- vayesta/tools/plotting/plotly_mol.py | 100 ++- 274 files changed, 9879 insertions(+), 8393 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 78c12c0f3..093979ffe 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -14,17 +14,17 @@ import sys import vayesta -sys.path.insert(0, os.path.abspath('../vayesta/')) +sys.path.insert(0, os.path.abspath("../vayesta/")) # -- Project information ----------------------------------------------------- -project = 'Vayesta' -copyright = '2022' -author = 'All contributors' +project = "Vayesta" +copyright = "2022" +author = "All contributors" # The full version, including alpha/beta/rc tags -release = '1.0.0' +release = "1.0.0" # -- General configuration --------------------------------------------------- @@ -32,13 +32,10 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['sphinx.ext.todo', - 'sphinx.ext.viewcode', - 'sphinx.ext.autodoc', - 'sphinx.ext.napoleon'] +extensions = ["sphinx.ext.todo", "sphinx.ext.viewcode", "sphinx.ext.autodoc", "sphinx.ext.napoleon"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -51,24 +48,24 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] +# html_static_path = ['_static'] html_static_path = [] # Numbering for Figures, Tables, etc numfig = True numfig_format = { - 'figure': 'Fig. %s', - 'table': 'Table %s', + "figure": "Fig. %s", + "table": "Table %s", } # -- Autodoc defaults -------------------------------------------------------- autodoc_default_options = { - 'member-order': 'bysource', - 'inherited-members': True, + "member-order": "bysource", + "inherited-members": True, } diff --git a/docs/source/quickstart/1d_hubbard.py b/docs/source/quickstart/1d_hubbard.py index e088349f1..be8c1e080 100644 --- a/docs/source/quickstart/1d_hubbard.py +++ b/docs/source/quickstart/1d_hubbard.py @@ -5,24 +5,24 @@ nsite = 16 nelectron = nsite hubbard_u = 2.0 -mol = vayesta.lattmod.Hubbard1D(nsite, nelectron=nelectron, hubbard_u=hubbard_u, output='pyscf.out') +mol = vayesta.lattmod.Hubbard1D(nsite, nelectron=nelectron, hubbard_u=hubbard_u, output="pyscf.out") mf = vayesta.lattmod.LatticeMF(mol) mf.kernel() # Single site embedding: -ewf = vayesta.ewf.EWF(mf, bno_threshold=1e-8, fragment_type='Site') +ewf = vayesta.ewf.EWF(mf, bno_threshold=1e-8, fragment_type="Site") ewf.site_fragmentation() ewf.add_atomic_fragment(0, sym_factor=nsite) ewf.kernel() -print("E(MF)= %+16.8f Ha" % (mf.e_tot/nelectron)) -print("E(EWF-CCSD)= %+16.8f Ha" % (ewf.e_tot/nelectron)) +print("E(MF)= %+16.8f Ha" % (mf.e_tot / nelectron)) +print("E(EWF-CCSD)= %+16.8f Ha" % (ewf.e_tot / nelectron)) # Double site embedding: -ewf = vayesta.ewf.EWF(mf, bno_threshold=1e-8, fragment_type='Site') +ewf = vayesta.ewf.EWF(mf, bno_threshold=1e-8, fragment_type="Site") ewf.site_fragmentation() -ewf.add_atomic_fragment([0,1], sym_factor=nsite/2) +ewf.add_atomic_fragment([0, 1], sym_factor=nsite / 2) ewf.kernel() -print("E(MF)= %+16.8f Ha" % (mf.e_tot/nelectron)) -print("E(EWF-CCSD)= %+16.8f Ha" % (ewf.e_tot/nelectron)) +print("E(MF)= %+16.8f Ha" % (mf.e_tot / nelectron)) +print("E(EWF-CCSD)= %+16.8f Ha" % (ewf.e_tot / nelectron)) diff --git a/docs/source/quickstart/edmetfinite.py b/docs/source/quickstart/edmetfinite.py index 180e7d7de..87c730d88 100644 --- a/docs/source/quickstart/edmetfinite.py +++ b/docs/source/quickstart/edmetfinite.py @@ -13,21 +13,22 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() # LDA lda = pyscf.dft.RKS(mol) -lda.xc = 'svwn' +lda.xc = "svwn" lda = lda.density_fit() lda.kernel() # The KS object needs to be converted to a HF object: lda = lda.to_rhf() -emb_lda = vayesta.edmet.EDMET(lda, bath_options=dict(dmet_threshold=1e-12), solver="CCSD-S-1-1", - oneshot=True, make_dd_moments=False) +emb_lda = vayesta.edmet.EDMET( + lda, bath_options=dict(dmet_threshold=1e-12), solver="CCSD-S-1-1", oneshot=True, make_dd_moments=False +) with emb_lda.iao_fragmentation() as f: f.add_all_atomic_fragments() @@ -35,14 +36,16 @@ # b3lyp b3lyp = pyscf.dft.RKS(mol) -b3lyp.xc = 'b3lyp' +b3lyp.xc = "b3lyp" b3lyp = b3lyp.density_fit() b3lyp.kernel() # The KS opbject needs to be converted to a HF object: b3lyp = b3lyp.to_rhf() -emb_b3lyp = vayesta.edmet.EDMET(b3lyp, bath_options=dict(dmet_threshold=1e-12), solver="CCSD-S-1-1", oneshot=True, make_dd_moments=False) +emb_b3lyp = vayesta.edmet.EDMET( + b3lyp, bath_options=dict(dmet_threshold=1e-12), solver="CCSD-S-1-1", oneshot=True, make_dd_moments=False +) with emb_b3lyp.iao_fragmentation() as f: f.add_all_atomic_fragments() emb_b3lyp.kernel() @@ -51,8 +54,9 @@ hf = pyscf.scf.RHF(mol).density_fit() hf.kernel() -emb_hf = vayesta.edmet.EDMET(hf, bath_options=dict(dmet_threshold=1e-12), solver="CCSD-S-1-1", - oneshot=True, make_dd_moments=False) +emb_hf = vayesta.edmet.EDMET( + hf, bath_options=dict(dmet_threshold=1e-12), solver="CCSD-S-1-1", oneshot=True, make_dd_moments=False +) with emb_hf.iao_fragmentation() as f: f.add_all_atomic_fragments() @@ -71,4 +75,3 @@ print("E(HF @B3LYP)= %+16.8f Ha" % emb_b3lyp.e_mf) print("E(Emb. CCSD @B3LYP)= %+16.8f Ha" % emb_b3lyp.e_tot) print("E(Emb. CCSD @HF)= %+16.8f Ha" % emb_hf.e_tot) - diff --git a/examples/dmet/01-simple-dmet.py b/examples/dmet/01-simple-dmet.py index af69ba9d3..5f4f7c223 100644 --- a/examples/dmet/01-simple-dmet.py +++ b/examples/dmet/01-simple-dmet.py @@ -8,9 +8,9 @@ # H6 ring mol = pyscf.gto.Mole() -mol.atom = ring(atom='H', natom=6, bond_length=2.0) -mol.basis = 'sto-6g' -mol.output = 'pyscf.out' +mol.atom = ring(atom="H", natom=6, bond_length=2.0) +mol.basis = "sto-6g" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -22,24 +22,24 @@ fci.kernel() # One-shot DMET -dmet = vayesta.dmet.DMET(mf, solver='FCI', maxiter=1) +dmet = vayesta.dmet.DMET(mf, solver="FCI", maxiter=1) with dmet.sao_fragmentation() as f: - f.add_atomic_fragment([0,1]) - f.add_atomic_fragment([2,3]) - f.add_atomic_fragment([4,5]) + f.add_atomic_fragment([0, 1]) + f.add_atomic_fragment([2, 3]) + f.add_atomic_fragment([4, 5]) dmet.kernel() # Self-consistent DMET -dmet_sc = vayesta.dmet.DMET(mf, solver='FCI') +dmet_sc = vayesta.dmet.DMET(mf, solver="FCI") with dmet_sc.sao_fragmentation() as f: - f.add_atomic_fragment([0,1]) - f.add_atomic_fragment([2,3]) - f.add_atomic_fragment([4,5]) + f.add_atomic_fragment([0, 1]) + f.add_atomic_fragment([2, 3]) + f.add_atomic_fragment([4, 5]) dmet_sc.kernel() print("Energies") print("========") print(" HF: %+16.8f Ha" % mf.e_tot) print(" FCI: %+16.8f Ha" % fci.e_tot) -print(" DMET(1 iteration): %+16.8f Ha (error= %.1f mHa)" % (dmet.e_tot, 1000*(dmet.e_tot-fci.e_tot))) -print(" DMET(self-consistent): %+16.8f Ha (error= %.1f mHa)" % (dmet_sc.e_tot, 1000*(dmet_sc.e_tot-fci.e_tot))) +print(" DMET(1 iteration): %+16.8f Ha (error= %.1f mHa)" % (dmet.e_tot, 1000 * (dmet.e_tot - fci.e_tot))) +print(" DMET(self-consistent): %+16.8f Ha (error= %.1f mHa)" % (dmet_sc.e_tot, 1000 * (dmet_sc.e_tot - fci.e_tot))) diff --git a/examples/dmet/02-rotational-symmetry.py b/examples/dmet/02-rotational-symmetry.py index cf6388d3a..4e7b393fd 100644 --- a/examples/dmet/02-rotational-symmetry.py +++ b/examples/dmet/02-rotational-symmetry.py @@ -11,9 +11,9 @@ # H6 ring mol = pyscf.gto.Mole() -mol.atom = ring(atom='H', natom=6, bond_length=2.0) -mol.basis = 'sto-6g' -mol.output = 'pyscf.out' +mol.atom = ring(atom="H", natom=6, bond_length=2.0) +mol.basis = "sto-6g" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -25,22 +25,22 @@ fci.kernel() # One-shot DMET -dmet = vayesta.dmet.DMET(mf, solver='FCI', maxiter=1) +dmet = vayesta.dmet.DMET(mf, solver="FCI", maxiter=1) with dmet.sao_fragmentation() as f: - with f.rotational_symmetry(3, axis=[0,0,1]): - f.add_atomic_fragment([0,1]) + with f.rotational_symmetry(3, axis=[0, 0, 1]): + f.add_atomic_fragment([0, 1]) dmet.kernel() # Self-consistent DMET -dmet_sc = vayesta.dmet.DMET(mf, solver='FCI') +dmet_sc = vayesta.dmet.DMET(mf, solver="FCI") with dmet_sc.sao_fragmentation() as f: - with f.rotational_symmetry(3, axis=(0,0,1), center=(0,0,0)): - f.add_atomic_fragment([0,1]) + with f.rotational_symmetry(3, axis=(0, 0, 1), center=(0, 0, 0)): + f.add_atomic_fragment([0, 1]) dmet_sc.kernel() print("Energies") print("========") print(" HF: %+16.8f Ha" % mf.e_tot) print(" FCI: %+16.8f Ha" % fci.e_tot) -print(" DMET(1 iteration): %+16.8f Ha (error= %.1f mHa)" % (dmet.e_tot, 1000*(dmet.e_tot-fci.e_tot))) -print(" DMET(self-consistent): %+16.8f Ha (error= %.1f mHa)" % (dmet_sc.e_tot, 1000*(dmet_sc.e_tot-fci.e_tot))) +print(" DMET(1 iteration): %+16.8f Ha (error= %.1f mHa)" % (dmet.e_tot, 1000 * (dmet.e_tot - fci.e_tot))) +print(" DMET(self-consistent): %+16.8f Ha (error= %.1f mHa)" % (dmet_sc.e_tot, 1000 * (dmet_sc.e_tot - fci.e_tot))) diff --git a/examples/dmet/03-density-matrices.py b/examples/dmet/03-density-matrices.py index 9b512bf23..c935db763 100644 --- a/examples/dmet/03-density-matrices.py +++ b/examples/dmet/03-density-matrices.py @@ -7,9 +7,9 @@ # H6 ring mol = pyscf.gto.Mole() -mol.atom = ring(atom='H', natom=6, bond_length=2.0) -mol.basis = 'sto-6g' -mol.output = 'pyscf.out' +mol.atom = ring(atom="H", natom=6, bond_length=2.0) +mol.basis = "sto-6g" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -17,11 +17,11 @@ mf.kernel() # One-shot DMET -dmet = vayesta.dmet.DMET(mf, solver='FCI', maxiter=1) +dmet = vayesta.dmet.DMET(mf, solver="FCI", maxiter=1) with dmet.sao_fragmentation() as f: - f.add_atomic_fragment([0,1]) - f.add_atomic_fragment([2,3]) - f.add_atomic_fragment([4,5]) + f.add_atomic_fragment([0, 1]) + f.add_atomic_fragment([2, 3]) + f.add_atomic_fragment([4, 5]) dmet.kernel() # Calculate energy from democratically-partitioned density matrices: @@ -30,7 +30,7 @@ # One and two-electron integrals in AO basis: h1e = dmet.get_hcore() eris = dmet.get_eris_array(np.eye(mol.nao)) -e_dmet = dmet.e_nuc + np.einsum('ij,ij->', h1e, dm1) + np.einsum('ijkl,ijkl->', eris, dm2)/2 +e_dmet = dmet.e_nuc + np.einsum("ij,ij->", h1e, dm1) + np.einsum("ijkl,ijkl->", eris, dm2) / 2 print("Energies") print("========") diff --git a/examples/dmet/04-h-ring-diis.py b/examples/dmet/04-h-ring-diis.py index e32b088a6..36778e82a 100644 --- a/examples/dmet/04-h-ring-diis.py +++ b/examples/dmet/04-h-ring-diis.py @@ -1,7 +1,7 @@ import numpy as np import pyscf -import pyscf.scf +import pyscf.scf import vayesta import vayesta.dmet @@ -9,9 +9,9 @@ mol = pyscf.gto.Mole() -mol.atom = ring('H', 6, 2.0) -mol.basis = 'sto-3g' -mol.output = 'pyscf.out' +mol.atom = ring("H", 6, 2.0) +mol.basis = "sto-3g" +mol.output = "pyscf.out" mol.verbose = 4 mol.build() @@ -20,8 +20,7 @@ mf.kernel() # DMET calculation with DIIS extrapolation of the high-level correlation potential. -dmet_diis = vayesta.dmet.DMET(mf, solver='FCI', charge_consistent=False, diis=True, - max_elec_err=1e-6) +dmet_diis = vayesta.dmet.DMET(mf, solver="FCI", charge_consistent=False, diis=True, max_elec_err=1e-6) with dmet_diis.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) f.add_atomic_fragment([2, 3]) @@ -29,15 +28,14 @@ dmet_diis.kernel() # DMET calculation without DIIS, using -dmet_mix = vayesta.dmet.DMET(mf, solver='FCI', charge_consistent=False, diis=False, - max_elec_err=1e-6) +dmet_mix = vayesta.dmet.DMET(mf, solver="FCI", charge_consistent=False, diis=False, max_elec_err=1e-6) with dmet_mix.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) f.add_atomic_fragment([2, 3]) f.add_atomic_fragment([4, 5]) dmet_mix.kernel() -ediff = (dmet_diis.e_dmet - dmet_mix.e_dmet) +ediff = dmet_diis.e_dmet - dmet_mix.e_dmet vdiff = np.linalg.norm(dmet_diis.vcorr - dmet_mix.vcorr) print("Difference between DIIS and mixing solution:") diff --git a/examples/dmet/63-hubbard-1d.py b/examples/dmet/63-hubbard-1d.py index 4f68d3818..4a123ad66 100644 --- a/examples/dmet/63-hubbard-1d.py +++ b/examples/dmet/63-hubbard-1d.py @@ -7,25 +7,25 @@ nsite = 10 nimp = 2 hubbard_u = 6.0 -boundary = 'PBC' +boundary = "PBC" nelectron = nsite mol = vayesta.lattmod.Hubbard1D(nsite, nelectron=nelectron, hubbard_u=hubbard_u, boundary=boundary) mf = vayesta.lattmod.LatticeMF(mol) mf.kernel() # Calculate each 2-sites fragment: -dmet = vayesta.dmet.DMET(mf, solver='FCI') +dmet = vayesta.dmet.DMET(mf, solver="FCI") with dmet.site_fragmentation() as f: for site in range(0, nsite, nimp): - f.add_atomic_fragment(list(range(site, site+nimp))) + f.add_atomic_fragment(list(range(site, site + nimp))) dmet.kernel() print(dmet.fragments) # Calculate a single fragment and use translational symmetry: -dmet_sym = vayesta.dmet.DMET(mf, solver='FCI') +dmet_sym = vayesta.dmet.DMET(mf, solver="FCI") # Specify the number of translational copies in direction of the three lattice vectors by passing a list with three # integers, [n0, n1, n2]. 1D or 2D systems have their periodic dimension along the first one or two axes. -nimages = [nsite//nimp, 1, 1] +nimages = [nsite // nimp, 1, 1] dmet_sym.symmetry.set_translations(nimages) # Add only a single 2-sites fragment: with dmet_sym.site_fragmentation() as f: diff --git a/examples/dmet/64-hubbard-2d.py b/examples/dmet/64-hubbard-2d.py index c6dfe6969..6c7a8fa39 100644 --- a/examples/dmet/64-hubbard-2d.py +++ b/examples/dmet/64-hubbard-2d.py @@ -4,14 +4,14 @@ import vayesta.lattmod -nsites = [6,6] -nimps = [2,2] +nsites = [6, 6] +nimps = [2, 2] hubbard_u = 6.0 # Anti-PBC and mixed boundaries do not work with translational symmetry! -boundary = 'PBC' -nsite = nsites[0]*nsites[1] -nelectron = nsite-10 -nimp = nimps[0]*nimps[1] +boundary = "PBC" +nsite = nsites[0] * nsites[1] +nelectron = nsite - 10 +nimp = nimps[0] * nimps[1] mol = vayesta.lattmod.Hubbard2D(nsites, nelectron=nelectron, hubbard_u=hubbard_u, boundary=boundary, tiles=nimps) mf = vayesta.lattmod.LatticeMF(mol) mf.kernel() @@ -19,17 +19,17 @@ # Calculate each (2 x 2)-sites fragment: # (Note that the Hubbard 2D class will reorder the sites, such that all 2 x 2 tiles contain continuous # site indices) -dmet = vayesta.dmet.DMET(mf, solver='FCI', maxiter=1) +dmet = vayesta.dmet.DMET(mf, solver="FCI", maxiter=1) with dmet.site_fragmentation() as f: for site in range(0, nsite, nimp): - f.add_atomic_fragment(list(range(site, site+nimp))) + f.add_atomic_fragment(list(range(site, site + nimp))) dmet.kernel() # Calculate a single fragment and use translational symmetry: -dmet_sym = vayesta.dmet.DMET(mf, solver='FCI', maxiter=1) +dmet_sym = vayesta.dmet.DMET(mf, solver="FCI", maxiter=1) # Specify the number of translational copies in direction of the three lattice vectors by passing a list with three # integers, [n0, n1, n2]. 1D or 2D systems have their periodic dimension along the first one or two axes. -nimages = [nsites[0]//nimps[0], nsites[1]//nimps[1], 1] +nimages = [nsites[0] // nimps[0], nsites[1] // nimps[1], 1] dmet_sym.symmetry.set_translations(nimages) # Add only a single (2 x 2)-sites fragment: with dmet_sym.site_fragmentation() as f: diff --git a/examples/dmet/65-hubbard-2d-charge-sc.py b/examples/dmet/65-hubbard-2d-charge-sc.py index 3f8cdbf9e..a01c07ff5 100644 --- a/examples/dmet/65-hubbard-2d-charge-sc.py +++ b/examples/dmet/65-hubbard-2d-charge-sc.py @@ -5,28 +5,28 @@ # In the Hubbard model charge consistency should not change obtained results. -nsites = [6,6] -nimps = [2,2] +nsites = [6, 6] +nimps = [2, 2] hubbard_u = 6.0 # Anti-PBC and mixed boundaries do not work with translational symmetry! -boundary = 'PBC' -nsite = nsites[0]*nsites[1] -nelectron = nsite-10 -nimp = nimps[0]*nimps[1] -nimages = [nsites[0]//nimps[0], nsites[1]//nimps[1], 1] +boundary = "PBC" +nsite = nsites[0] * nsites[1] +nelectron = nsite - 10 +nimp = nimps[0] * nimps[1] +nimages = [nsites[0] // nimps[0], nsites[1] // nimps[1], 1] mol = vayesta.lattmod.Hubbard2D(nsites, nelectron=nelectron, hubbard_u=hubbard_u, boundary=boundary, tiles=nimps) mf = vayesta.lattmod.LatticeMF(mol) mf.kernel() # DMET with charge conistency (default): -dmet = vayesta.dmet.DMET(mf, solver='FCI') +dmet = vayesta.dmet.DMET(mf, solver="FCI") dmet.symmetry.set_translations(nimages) with dmet.site_fragmentation() as f: f.add_atomic_fragment(list(range(nimp))) dmet.kernel() # DMET without charge conistency: -dmet_nocc = vayesta.dmet.DMET(mf, solver='FCI', charge_consistent=False) +dmet_nocc = vayesta.dmet.DMET(mf, solver="FCI", charge_consistent=False) dmet_nocc.symmetry.set_translations(nimages) with dmet_nocc.site_fragmentation() as f: f_nocc = f.add_atomic_fragment(list(range(nimp))) diff --git a/examples/dmet/70-h-ring-benchmark.py b/examples/dmet/70-h-ring-benchmark.py index 115825d62..6155b5820 100644 --- a/examples/dmet/70-h-ring-benchmark.py +++ b/examples/dmet/70-h-ring-benchmark.py @@ -9,14 +9,13 @@ natom = 6 for d in np.arange(0.5, 3.0001, 0.25): - ring = pyscf.tools.ring.make(natom, d) - atom = [('H %f %f %f' % xyz) for xyz in ring] + atom = [("H %f %f %f" % xyz) for xyz in ring] mol = pyscf.gto.Mole() mol.atom = atom - mol.basis = '6-31G' - mol.output = 'pyscf.out' + mol.basis = "6-31G" + mol.output = "pyscf.out" mol.verbose = 4 mol.build() @@ -33,26 +32,28 @@ fci.kernel() # Single-shot - dmet_oneshot = vayesta.dmet.DMET(mf, solver='FCI', max_elec_err=1e-6, maxiter=1) + dmet_oneshot = vayesta.dmet.DMET(mf, solver="FCI", max_elec_err=1e-6, maxiter=1) with dmet_oneshot.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) f.add_atomic_fragment([2, 3]) f.add_atomic_fragment([4, 5]) dmet_oneshot.kernel() # Full DMET - dmet_diis = vayesta.dmet.DMET(mf, solver='FCI', max_elec_err=1e-6, charge_consistent=True, diis=True) + dmet_diis = vayesta.dmet.DMET(mf, solver="FCI", max_elec_err=1e-6, charge_consistent=True, diis=True) with dmet_diis.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) f.add_atomic_fragment([2, 3]) f.add_atomic_fragment([4, 5]) dmet_diis.kernel() - print("E%-14s %+16.8f Ha" % ('(HF)=', mf.e_tot)) - print("E%-14s %+16.8f Ha" % ('(CCSD)=', cc.e_tot)) - print("E%-14s %+16.8f Ha" % ('(FCI)=', fci.e_tot)) - print("E%-14s %+16.8f Ha" % ('(Oneshot DMET-FCI)=', dmet_oneshot.e_tot)) - print("E%-14s %+16.8f Ha" % ('(DMET-FCI)=', dmet_diis.e_tot)) + print("E%-14s %+16.8f Ha" % ("(HF)=", mf.e_tot)) + print("E%-14s %+16.8f Ha" % ("(CCSD)=", cc.e_tot)) + print("E%-14s %+16.8f Ha" % ("(FCI)=", fci.e_tot)) + print("E%-14s %+16.8f Ha" % ("(Oneshot DMET-FCI)=", dmet_oneshot.e_tot)) + print("E%-14s %+16.8f Ha" % ("(DMET-FCI)=", dmet_diis.e_tot)) with open("energies.txt", "a") as f: - f.write("%.2f % 16.8f % 16.8f % 16.8f %16.8f %16.8f\n" % (d, mf.e_tot, cc.e_tot, fci.e_tot, dmet_oneshot.e_tot, - dmet_diis.e_tot)) + f.write( + "%.2f % 16.8f % 16.8f % 16.8f %16.8f %16.8f\n" + % (d, mf.e_tot, cc.e_tot, fci.e_tot, dmet_oneshot.e_tot, dmet_diis.e_tot) + ) diff --git a/examples/edmet/01-1d-hubbard-comparison.py b/examples/edmet/01-1d-hubbard-comparison.py index 59504a99a..cddc26a30 100644 --- a/examples/edmet/01-1d-hubbard-comparison.py +++ b/examples/edmet/01-1d-hubbard-comparison.py @@ -5,49 +5,59 @@ import vayesta.lattmod.bethe import matplotlib.pyplot as plt + # Function to get mean-field for hubbard of given size and onsite repulsion. def gen_hub(nsite, hubbard_u): nelectron = nsite mol = vayesta.lattmod.Hubbard1D(nsite, nelectron=nelectron, hubbard_u=hubbard_u) - mf = vayesta.lattmod.LatticeMF(mol)#, allocate_eri = False) + mf = vayesta.lattmod.LatticeMF(mol) # , allocate_eri = False) mf.kernel() return mf + sites_to_try = list(range(10, 100, 10)) -def gen_comparison(hubbard_u, nimp=2): +def gen_comparison(hubbard_u, nimp=2): res_dmet = [] res_edmet = [] for nsite in sites_to_try: mf = gen_hub(nsite, hubbard_u) - dmet = vayesta.dmet.DMET(mf, solver='FCI', charge_consistent=True, maxiter=1, - bath_options=dict(bathtype='dmet')) + dmet = vayesta.dmet.DMET( + mf, solver="FCI", charge_consistent=True, maxiter=1, bath_options=dict(bathtype="dmet") + ) with dmet.site_fragmentation() as f: frag = f.add_atomic_fragment(list(range(nimp))) # Add fragments which are translationally symmetric to f - the results of the fragment f # fill be automatically copied. dmet.kernel() symfrags = frag.make_tsymmetric_fragments(tvecs=[nsite // nimp, 1, 1]) - res_dmet += [sum(dmet.fragments[0].get_dmet_energy_contrib())/nimp] + res_dmet += [sum(dmet.fragments[0].get_dmet_energy_contrib()) / nimp] - edmet = vayesta.edmet.EDMET(mf, solver="FCI", charge_consistent = True,maxiter=1, bath_options=dict(bathtype='dmet')) + edmet = vayesta.edmet.EDMET( + mf, solver="FCI", charge_consistent=True, maxiter=1, bath_options=dict(bathtype="dmet") + ) with edmet.site_fragmentation() as f: frag = f.add_atomic_fragment(list(range(nimp))) # Add fragments which are translationally symmetric to f - the results of the fragment f # fill be automatically copied. - symfrags = frag.make_tsymmetric_fragments(tvecs=[nsite//nimp, 1, 1]) + symfrags = frag.make_tsymmetric_fragments(tvecs=[nsite // nimp, 1, 1]) edmet.kernel() - res_edmet += [sum(edmet.fragments[0].get_edmet_energy_contrib())/nimp] - + res_edmet += [sum(edmet.fragments[0].get_edmet_energy_contrib()) / nimp] plt.plot(sites_to_try, res_dmet, label="DMET") plt.plot(sites_to_try, res_edmet, label="Original EDMET") ax = plt.gca() - ax.hlines(vayesta.lattmod.bethe.hubbard1d_bethe_energy(1.0, hubbard_u), sites_to_try[0], sites_to_try[-1], label="Bethe Ansatz") + ax.hlines( + vayesta.lattmod.bethe.hubbard1d_bethe_energy(1.0, hubbard_u), + sites_to_try[0], + sites_to_try[-1], + label="Bethe Ansatz", + ) leg = plt.legend() plt.show() + gen_comparison(10.0, 2) diff --git a/examples/edmet/10-bosonic-Hamiltonians.py b/examples/edmet/10-bosonic-Hamiltonians.py index 4c5adbbf5..c3a10f4ef 100644 --- a/examples/edmet/10-bosonic-Hamiltonians.py +++ b/examples/edmet/10-bosonic-Hamiltonians.py @@ -6,7 +6,7 @@ # Use ethane as a simple trial system. mol = gto.Mole() mol.atom = molecules.alkane(2) -mol.basis = 'sto-3g' +mol.basis = "sto-3g" mol.build() mf = scf.RHF(mol).density_fit() @@ -15,18 +15,38 @@ # Can generate bosons using either RPA couplings or direct projection of the Hamiltonian into the bosonic space. -rdfedmet_drpa_bos = vayesta.edmet.EDMET(mf, solver="CCSD-S-1-1", bath_options=dict(dmet_threshold=1e-12), bosonic_interaction="direct", oneshot=True, make_dd_moments=False) +rdfedmet_drpa_bos = vayesta.edmet.EDMET( + mf, + solver="CCSD-S-1-1", + bath_options=dict(dmet_threshold=1e-12), + bosonic_interaction="direct", + oneshot=True, + make_dd_moments=False, +) with rdfedmet_drpa_bos.iao_fragmentation() as f: f.add_all_atomic_fragments() rdfedmet_drpa_bos.kernel() -rdfedmet_qba_directbos = vayesta.edmet.EDMET(mf, solver="CCSD-S-1-1", bath_options=dict(dmet_threshold=1e-12), bosonic_interaction="qba", oneshot=True, make_dd_moments=False) +rdfedmet_qba_directbos = vayesta.edmet.EDMET( + mf, + solver="CCSD-S-1-1", + bath_options=dict(dmet_threshold=1e-12), + bosonic_interaction="qba", + oneshot=True, + make_dd_moments=False, +) with rdfedmet_qba_directbos.iao_fragmentation() as f: f.add_all_atomic_fragments() rdfedmet_qba_directbos.kernel() -rdfedmet_qba = vayesta.edmet.EDMET(mf, solver="CCSD-S-1-1", bath_options=dict(dmet_threshold=1e-12), bosonic_interaction="qba_bos_ex", - oneshot=True, make_dd_moments=False) +rdfedmet_qba = vayesta.edmet.EDMET( + mf, + solver="CCSD-S-1-1", + bath_options=dict(dmet_threshold=1e-12), + bosonic_interaction="qba_bos_ex", + oneshot=True, + make_dd_moments=False, +) with rdfedmet_qba.iao_fragmentation() as f: f.add_all_atomic_fragments() rdfedmet_qba.kernel() diff --git a/examples/edmet/30-start-from-dft.py b/examples/edmet/30-start-from-dft.py index 1eae5ec17..4a4d4fd93 100644 --- a/examples/edmet/30-start-from-dft.py +++ b/examples/edmet/30-start-from-dft.py @@ -13,32 +13,36 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() # LDA lda = pyscf.dft.RKS(mol) -lda.xc = 'svwn' +lda.xc = "svwn" lda = lda.density_fit() lda.kernel() # The KS opbject needs to be converted to a HF object: lda = lda.to_rhf() -emb_lda = vayesta.edmet.EDMET(lda, solver="CCSD-S-1-1", bath_options=dict(dmet_threshold=1e-12), oneshot=True, make_dd_moments=False) +emb_lda = vayesta.edmet.EDMET( + lda, solver="CCSD-S-1-1", bath_options=dict(dmet_threshold=1e-12), oneshot=True, make_dd_moments=False +) with emb_lda.iao_fragmentation() as f: f.add_all_atomic_fragments() emb_lda.kernel() # b3lyp b3lyp = pyscf.dft.RKS(mol) -b3lyp.xc = 'b3lyp' +b3lyp.xc = "b3lyp" b3lyp = b3lyp.density_fit() b3lyp.kernel() # The KS opbject needs to be converted to a HF object: b3lyp = b3lyp.to_rhf() -emb_b3lyp = vayesta.edmet.EDMET(b3lyp, solver="CCSD-S-1-1", bath_options=dict(dmet_threshold=1e-12), oneshot=True, make_dd_moments=False) +emb_b3lyp = vayesta.edmet.EDMET( + b3lyp, solver="CCSD-S-1-1", bath_options=dict(dmet_threshold=1e-12), oneshot=True, make_dd_moments=False +) with emb_b3lyp.iao_fragmentation() as f: f.add_all_atomic_fragments() emb_b3lyp.kernel() @@ -47,7 +51,9 @@ hf = pyscf.scf.RHF(mol).density_fit() hf.kernel() -emb_hf = vayesta.edmet.EDMET(hf, solver="CCSD-S-1-1", bath_options=dict(dmet_threshold=1e-12), oneshot=True, make_dd_moments=False) +emb_hf = vayesta.edmet.EDMET( + hf, solver="CCSD-S-1-1", bath_options=dict(dmet_threshold=1e-12), oneshot=True, make_dd_moments=False +) with emb_hf.iao_fragmentation() as f: f.add_all_atomic_fragments() emb_hf.kernel() diff --git a/examples/edmet/70-h-ring-benchmark-df.py b/examples/edmet/70-h-ring-benchmark-df.py index df018400f..361edc797 100644 --- a/examples/edmet/70-h-ring-benchmark-df.py +++ b/examples/edmet/70-h-ring-benchmark-df.py @@ -11,8 +11,10 @@ filename = "energies_scEDMET_h{:d}_compare_df.txt".format(natom) with open(filename, "a") as f: - f.write(("%6s" + " %16s " * 8) % ( - "d", "HF", "CCSD", "FCI", "DMET (Oneshot)", "DMET", "EDMET (Oneshot)", "EDMET (old)", "EDMET (new)")) + f.write( + ("%6s" + " %16s " * 8) + % ("d", "HF", "CCSD", "FCI", "DMET (Oneshot)", "DMET", "EDMET (Oneshot)", "EDMET (old)", "EDMET (new)") + ) import numpy as np import pyscf.cc @@ -27,17 +29,15 @@ filename = "energies_h{:d}_compare_df.txt".format(natom) with open(filename, "a") as f: - f.write(("%6s" + " %16s " * 6 + "\n") % ( - "d", "HF", "CCSD", "FCI", "DMET (Oneshot)", "DMET", "EDMET (Oneshot)")) + f.write(("%6s" + " %16s " * 6 + "\n") % ("d", "HF", "CCSD", "FCI", "DMET (Oneshot)", "DMET", "EDMET (Oneshot)")) for d in np.arange(0.5, 3.0001, 0.25): - ring = pyscf.tools.ring.make(natom, d) - atom = [('H %f %f %f' % xyz) for xyz in ring] + atom = [("H %f %f %f" % xyz) for xyz in ring] mol = pyscf.gto.Mole() mol.atom = atom - mol.basis = 'STO-6G' + mol.basis = "STO-6G" # mol.verbose = 10 # mol.output = 'pyscf_out.txt' mol.build() @@ -57,21 +57,21 @@ myfci.kernel() # Single-shot - dmet_oneshot = vayesta.dmet.DMET(mf, solver='FCI', max_elec_err=1e-4, maxiter=1) + dmet_oneshot = vayesta.dmet.DMET(mf, solver="FCI", max_elec_err=1e-4, maxiter=1) with dmet_oneshot.iao_fragmentation() as f: for i in range(0, natom, 2): f.add_atomic_fragment([i, i + 1]) dmet_oneshot.kernel() # Full DMET - dmet_diis = vayesta.dmet.DMET(mf, solver='FCI', charge_consistent=True, diis=True, - max_elec_err=1e-4) + dmet_diis = vayesta.dmet.DMET(mf, solver="FCI", charge_consistent=True, diis=True, max_elec_err=1e-4) with dmet_diis.iao_fragmentation() as f: for i in range(0, natom, 2): f.add_atomic_fragment([i, i + 1]) dmet_diis.kernel() # Single-shot EDMET - edmet_oneshot = vayesta.edmet.EDMET(mf, solver='FCI', max_elec_err=1e-4, maxiter=1, - solver_options=dict(max_boson_occ=2), oneshot=True) + edmet_oneshot = vayesta.edmet.EDMET( + mf, solver="FCI", max_elec_err=1e-4, maxiter=1, solver_options=dict(max_boson_occ=2), oneshot=True + ) with edmet_oneshot.iao_fragmentation() as f: for i in range(0, natom, 2): f.add_atomic_fragment([i, i + 1]) @@ -79,17 +79,14 @@ e_cc = mycc.e_tot if mycc.converged else np.NaN e_dmet = dmet_diis.e_tot if dmet_diis.converged else np.NaN - print("E%-14s %+16.8f Ha" % ('(HF)=', mf.e_tot)) - print("E%-14s %+16.8f Ha" % ('(CCSD)=', e_cc)) - print("E%-14s %+16.8f Ha" % ('(FCI)=', myfci.e_tot)) - print("E%-14s %+16.8f Ha" % ('(DMET-FCI)=', dmet_oneshot.e_tot)) - print("E%-14s %+16.8f Ha" % ('(EDMET-FCI-Oneshot)=', edmet_oneshot.e_tot)) - + print("E%-14s %+16.8f Ha" % ("(HF)=", mf.e_tot)) + print("E%-14s %+16.8f Ha" % ("(CCSD)=", e_cc)) + print("E%-14s %+16.8f Ha" % ("(FCI)=", myfci.e_tot)) + print("E%-14s %+16.8f Ha" % ("(DMET-FCI)=", dmet_oneshot.e_tot)) + print("E%-14s %+16.8f Ha" % ("(EDMET-FCI-Oneshot)=", edmet_oneshot.e_tot)) with open(filename, "a") as f: - f.write("%.2f % 16.8f % 16.8f % 16.8f %16.8f %16.8f %16.8f\n" % (d, mf.e_tot, e_cc, - myfci.e_tot, - dmet_oneshot.e_tot, - e_dmet, - edmet_oneshot.e_tot - )) + f.write( + "%.2f % 16.8f % 16.8f % 16.8f %16.8f %16.8f %16.8f\n" + % (d, mf.e_tot, e_cc, myfci.e_tot, dmet_oneshot.e_tot, e_dmet, edmet_oneshot.e_tot) + ) diff --git a/examples/edmet/72-h-chain-benchmark.py b/examples/edmet/72-h-chain-benchmark.py index 8793fee0b..92b5f3463 100644 --- a/examples/edmet/72-h-chain-benchmark.py +++ b/examples/edmet/72-h-chain-benchmark.py @@ -7,13 +7,12 @@ natom = 10 for d in np.arange(1.0, 3.7, 0.4): - pos = [(d * x, 0, 0) for x in range(natom)] - atom = [('H %f %f %f' % xyz) for xyz in pos] + atom = [("H %f %f %f" % xyz) for xyz in pos] mol = pyscf.gto.Mole() mol.atom = atom - mol.basis = 'cc-pvdz' + mol.basis = "cc-pvdz" mol.unit = "Bohr" mol.build() @@ -26,23 +25,25 @@ mycc.kernel() # Single-shot - dmet_oneshot = vayesta.dmet.DMET(mf, solver='FCI', max_elec_err=1e-6, maxiter=1) + dmet_oneshot = vayesta.dmet.DMET(mf, solver="FCI", max_elec_err=1e-6, maxiter=1) with dmet_oneshot.iao_fragmentation() as f: f.add_all_atomic_fragments() dmet_oneshot.kernel() # Single-shot EDMET - edmet_oneshot = vayesta.edmet.EDMET(mf, solver='FCI', max_elec_err=1e-6, oneshot=True, - solver_options=dict(max_boson_occ=4)) + edmet_oneshot = vayesta.edmet.EDMET( + mf, solver="FCI", max_elec_err=1e-6, oneshot=True, solver_options=dict(max_boson_occ=4) + ) with edmet_oneshot.iao_fragmentation() as f: f.add_all_atomic_fragments() edmet_oneshot.kernel() e_cc = mycc.e_tot if mycc.converged else np.NaN - print("E%-14s %+16.8f Ha" % ('(HF)=', mf.e_tot)) - print("E%-14s %+16.8f Ha" % ('(CCSD)=', e_cc)) - print("E%-14s %+16.8f Ha" % ('(DMET-FCI-Oneshot)=', dmet_oneshot.e_tot)) - print("E%-14s %+16.8f Ha" % ('(EDMET-FCI-Oneshot)=', edmet_oneshot.e_tot)) + print("E%-14s %+16.8f Ha" % ("(HF)=", mf.e_tot)) + print("E%-14s %+16.8f Ha" % ("(CCSD)=", e_cc)) + print("E%-14s %+16.8f Ha" % ("(DMET-FCI-Oneshot)=", dmet_oneshot.e_tot)) + print("E%-14s %+16.8f Ha" % ("(EDMET-FCI-Oneshot)=", edmet_oneshot.e_tot)) with open("energies_h10_ccpvdz.txt", "a") as f: - f.write("%.2f % 16.8f % 16.8f %16.8f %16.8f\n" % (d, mf.e_tot, e_cc, dmet_oneshot.e_tot, - edmet_oneshot.e_tot)) + f.write( + "%.2f % 16.8f % 16.8f %16.8f %16.8f\n" % (d, mf.e_tot, e_cc, dmet_oneshot.e_tot, edmet_oneshot.e_tot) + ) diff --git a/examples/ewf/molecules/01-simple-ccsd.py b/examples/ewf/molecules/01-simple-ccsd.py index 404053d20..5a709a091 100644 --- a/examples/ewf/molecules/01-simple-ccsd.py +++ b/examples/ewf/molecules/01-simple-ccsd.py @@ -12,8 +12,8 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.txt' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.txt" mol.build() # Hartree-Fock diff --git a/examples/ewf/molecules/02-simple-uccsd.py b/examples/ewf/molecules/02-simple-uccsd.py index c02035204..7d78161ea 100644 --- a/examples/ewf/molecules/02-simple-uccsd.py +++ b/examples/ewf/molecules/02-simple-uccsd.py @@ -12,8 +12,8 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.charge = 1 mol.spin = 1 mol.build() diff --git a/examples/ewf/molecules/03-bno-threshold.py b/examples/ewf/molecules/03-bno-threshold.py index f96061541..b74b06641 100644 --- a/examples/ewf/molecules/03-bno-threshold.py +++ b/examples/ewf/molecules/03-bno-threshold.py @@ -12,8 +12,8 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'aug-cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "aug-cc-pVDZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock diff --git a/examples/ewf/molecules/04-bath-options.py b/examples/ewf/molecules/04-bath-options.py index f73610871..0dfc3c9cb 100644 --- a/examples/ewf/molecules/04-bath-options.py +++ b/examples/ewf/molecules/04-bath-options.py @@ -13,8 +13,8 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.txt' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.txt" mol.build() # Hartree-Fock @@ -26,14 +26,14 @@ cc.kernel() # Test the exact (full system CCSD) limit is reproduced by using bathtype='full': -emb = vayesta.ewf.EWF(mf, bath_options=dict(bathtype='full')) +emb = vayesta.ewf.EWF(mf, bath_options=dict(bathtype="full")) emb.kernel() -assert abs(cc.e_tot-emb.e_tot) < 1e-8 +assert abs(cc.e_tot - emb.e_tot) < 1e-8 # By default, the EWF class will use MP2 bath natural orbitals (BNOs), to efficently truncate # the environment. The truncation level can be controlled via the `threshold` parameter, # which corresponds to the natural occupation or deoccupation number of the BNOs: -bath = dict(bathtype='mp2', threshold=1e-4) +bath = dict(bathtype="mp2", threshold=1e-4) emb_mp2 = vayesta.ewf.EWF(mf, bath_options=bath) emb_mp2.kernel() @@ -47,36 +47,47 @@ # Other options (not shown) include projecting out all couplings to the DMET bath space, or just projecting # one index. # The options below are default -emb_mp2_projdmet = vayesta.ewf.EWF(mf, bath_options=dict(bathtype='mp2', threshold=1e-4, - project_dmet_order=2, project_dmet_mode='squared-entropy')) +emb_mp2_projdmet = vayesta.ewf.EWF( + mf, bath_options=dict(bathtype="mp2", threshold=1e-4, project_dmet_order=2, project_dmet_mode="squared-entropy") +) emb_mp2_projdmet.kernel() -assert(np.allclose(emb_mp2_projdmet.e_tot, emb_mp2.e_tot)) +assert np.allclose(emb_mp2_projdmet.e_tot, emb_mp2.e_tot) # To turn off this projection (which was not used in 10.1103/PhysRevX.12.011046), # use `project_dmet_order = 0`: -bath = dict(bathtype='mp2', threshold=1e-4, project_dmet_order=0) +bath = dict(bathtype="mp2", threshold=1e-4, project_dmet_order=0) emb_noproj = vayesta.ewf.EWF(mf, bath_options=bath) emb_noproj.kernel() # Use maximally R^2-localized bath orbitals: # rcut is the cutoff distance in Angstrom -bath = dict(bathtype='r2', rcut=1.3) +bath = dict(bathtype="r2", rcut=1.3) emb_r2 = vayesta.ewf.EWF(mf, bath_options=bath) emb_r2.kernel() # Occupied and virtual bath can be different: -bath = dict(bathtype_occ='r2', rcut_occ=1.3, bathtype_vir='mp2', threshold_vir=1e-4) +bath = dict(bathtype_occ="r2", rcut_occ=1.3, bathtype_vir="mp2", threshold_vir=1e-4) emb_mix = vayesta.ewf.EWF(mf, bath_options=bath) emb_mix.kernel() print("CCSD: E(tot)= %+16.8f Ha" % cc.e_tot) -print("Embedded CCSD with full bath: mean cluster size= %.3f E(tot)= %+16.8f Ha" % ( - emb.get_mean_cluster_size(), emb.e_tot)) -print("Embedded CCSD with MP2 bath: mean cluster size= %.3f E(tot)= %+16.8f Ha" % ( - emb_mp2.get_mean_cluster_size(), emb_mp2.e_tot)) -print("Embedded CCSD with MP2 bath (no DMET projector): mean cluster size= %.3f E(tot)= %+16.8f Ha" % ( - emb_noproj.get_mean_cluster_size(), emb_noproj.e_tot)) -print("Embedded CCSD with R2 bath: mean cluster size= %.3f E(tot)= %+16.8f Ha" % ( - emb_r2.get_mean_cluster_size(), emb_r2.e_tot)) -print("Embedded CCSD with mixed R2/MP2 bath: mean cluster size= %.3f E(tot)= %+16.8f Ha" % ( - emb_mix.get_mean_cluster_size(), emb_mix.e_tot)) +print( + "Embedded CCSD with full bath: mean cluster size= %.3f E(tot)= %+16.8f Ha" + % (emb.get_mean_cluster_size(), emb.e_tot) +) +print( + "Embedded CCSD with MP2 bath: mean cluster size= %.3f E(tot)= %+16.8f Ha" + % (emb_mp2.get_mean_cluster_size(), emb_mp2.e_tot) +) +print( + "Embedded CCSD with MP2 bath (no DMET projector): mean cluster size= %.3f E(tot)= %+16.8f Ha" + % (emb_noproj.get_mean_cluster_size(), emb_noproj.e_tot) +) +print( + "Embedded CCSD with R2 bath: mean cluster size= %.3f E(tot)= %+16.8f Ha" + % (emb_r2.get_mean_cluster_size(), emb_r2.e_tot) +) +print( + "Embedded CCSD with mixed R2/MP2 bath: mean cluster size= %.3f E(tot)= %+16.8f Ha" + % (emb_mix.get_mean_cluster_size(), emb_mix.e_tot) +) diff --git a/examples/ewf/molecules/05-ccsd-amplitudes.py b/examples/ewf/molecules/05-ccsd-amplitudes.py index 97077ae60..a1fcdd995 100644 --- a/examples/ewf/molecules/05-ccsd-amplitudes.py +++ b/examples/ewf/molecules/05-ccsd-amplitudes.py @@ -13,8 +13,8 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock diff --git a/examples/ewf/molecules/07-corrections.py b/examples/ewf/molecules/07-corrections.py index 0a4dd9763..93b7fb76d 100644 --- a/examples/ewf/molecules/07-corrections.py +++ b/examples/ewf/molecules/07-corrections.py @@ -12,8 +12,8 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVTZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVTZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -39,13 +39,16 @@ with emb_sec.iao_fragmentation() as f: # The BNO threshold of the secondary fragments can be set directly, using `bno_threshold`, # or as a fraction of the CCSD threshold, using `bno_threshold_factor`: - with f.secondary_fragments(solver='MP2', bno_threshold=1e-6): + with f.secondary_fragments(solver="MP2", bno_threshold=1e-6): f.add_all_atomic_fragments() emb_sec.kernel() # The FBC can be combined with the secondary-fragment approach: e_fbc_sec = emb_sec.get_fbc_energy() -print("E(Emb. CCSD)= %+16.8f Ha (error= %+.8f Ha)" % (emb.e_tot, emb.e_tot-cc.e_tot)) -print("E(Emb. CCSD + FBC)= %+16.8f Ha (error= %+.8f Ha)" % (emb.e_tot+e_fbc, emb.e_tot+e_fbc-cc.e_tot)) -print("E(Emb. CCSD/MP2)= %+16.8f Ha (error= %+.8f Ha)" % (emb_sec.e_tot, emb_sec.e_tot-cc.e_tot)) -print("E(Emb. CCSD/MP2 + FBC)= %+16.8f Ha (error= %+.8f Ha)" % (emb_sec.e_tot+e_fbc_sec, emb_sec.e_tot+e_fbc_sec-cc.e_tot)) +print("E(Emb. CCSD)= %+16.8f Ha (error= %+.8f Ha)" % (emb.e_tot, emb.e_tot - cc.e_tot)) +print("E(Emb. CCSD + FBC)= %+16.8f Ha (error= %+.8f Ha)" % (emb.e_tot + e_fbc, emb.e_tot + e_fbc - cc.e_tot)) +print("E(Emb. CCSD/MP2)= %+16.8f Ha (error= %+.8f Ha)" % (emb_sec.e_tot, emb_sec.e_tot - cc.e_tot)) +print( + "E(Emb. CCSD/MP2 + FBC)= %+16.8f Ha (error= %+.8f Ha)" + % (emb_sec.e_tot + e_fbc_sec, emb_sec.e_tot + e_fbc_sec - cc.e_tot) +) diff --git a/examples/ewf/molecules/11-fragmentation.py b/examples/ewf/molecules/11-fragmentation.py index cef26754a..19356d9ff 100644 --- a/examples/ewf/molecules/11-fragmentation.py +++ b/examples/ewf/molecules/11-fragmentation.py @@ -12,8 +12,8 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock diff --git a/examples/ewf/molecules/12-custom-fragments.py b/examples/ewf/molecules/12-custom-fragments.py index a2eb7a5f2..5ca7f3e5c 100644 --- a/examples/ewf/molecules/12-custom-fragments.py +++ b/examples/ewf/molecules/12-custom-fragments.py @@ -12,8 +12,8 @@ O 0.0000 1.3464 -0.5965 O 0.0000 -1.3464 -0.5965 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -28,17 +28,17 @@ emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-6)) with emb.iao_fragmentation() as f: # Fragment containing the 1s state of O and 1s and 2s states of Se - f.add_atomic_fragment(['Se', 'O'], orbital_filter=['1s', 'Se 2s']) + f.add_atomic_fragment(["Se", "O"], orbital_filter=["1s", "Se 2s"]) # Atoms can be specified by labels or indices # Fragment containing the 2s state at O and 3s and 4s states of Se - f.add_atomic_fragment([0, 1, 2], orbital_filter=['O 2s', '3s', '4s']) + f.add_atomic_fragment([0, 1, 2], orbital_filter=["O 2s", "3s", "4s"]) # Fragment containing the 2p x- and y-states on the oxygen and the 2p, 3p y- and z- states on selenium # Note that the oxygen does not have 3p IAO states, such that it is not necessary to specify the element for these states - f.add_atomic_fragment([0, 1, 2], orbital_filter=['O 2py', 'O 2pz', 'Se 2p', '3py', '3pz']) + f.add_atomic_fragment([0, 1, 2], orbital_filter=["O 2py", "O 2pz", "Se 2p", "3py", "3pz"]) # All 4p states on Se and all px states (2px on O, 2-4px on Se) - f.add_atomic_fragment(['Se', 'O'], orbital_filter=['4p', 'px']) + f.add_atomic_fragment(["Se", "O"], orbital_filter=["4p", "px"]) # 3d states on Se - f.add_atomic_fragment(0, orbital_filter=['3d']) + f.add_atomic_fragment(0, orbital_filter=["3d"]) emb.kernel() print("E(HF)= %+16.8f Ha" % mf.e_tot) diff --git a/examples/ewf/molecules/15-chemical-potential.py b/examples/ewf/molecules/15-chemical-potential.py index 11c9e4178..a3d8a44bf 100644 --- a/examples/ewf/molecules/15-chemical-potential.py +++ b/examples/ewf/molecules/15-chemical-potential.py @@ -10,7 +10,7 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' +mol.basis = "cc-pVDZ" mol.build() # Hartree-Fock @@ -25,7 +25,7 @@ emb.kernel() # Population analyis: dm1 = emb.make_rdm1(ao_basis=True) -emb.pop_analysis(dm1, filename='pop.txt') +emb.pop_analysis(dm1, filename="pop.txt") # --- Embedded CCSD with target number of electrons per fragment: emb_nelec = vayesta.ewf.EWF(mf, bath_options=dict(threshold=eta)) @@ -36,7 +36,7 @@ emb_nelec.kernel() # Population analyis: dm1 = emb_nelec.make_rdm1(ao_basis=True) -emb_nelec.pop_analysis(dm1, filename='pop-nelec.txt') +emb_nelec.pop_analysis(dm1, filename="pop-nelec.txt") # --- Embedded CCSD with chemical potential, to ensure that democratically partitioned 1-DM has correct trace: emb_cpt = vayesta.ewf.EWF(mf, bath_options=dict(threshold=eta)) @@ -46,7 +46,7 @@ emb_cpt.kernel() # Population analyis: dm1 = emb_cpt.make_rdm1(ao_basis=True) -emb_cpt.pop_analysis(dm1, filename='pop-cpt.txt') +emb_cpt.pop_analysis(dm1, filename="pop-cpt.txt") print("E(HF)= %+16.8f Ha" % mf.e_tot) print("E(emb. CCSD)= %+16.8f Ha" % emb.e_tot) diff --git a/examples/ewf/molecules/20-dump-clusters.py b/examples/ewf/molecules/20-dump-clusters.py index a676849ff..8e6de86f5 100644 --- a/examples/ewf/molecules/20-dump-clusters.py +++ b/examples/ewf/molecules/20-dump-clusters.py @@ -13,8 +13,8 @@ H2 0.0000 0.7572 -0.4692 H3 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -22,8 +22,9 @@ mf.kernel() # Embedding class to form fragment, bath, and cluster spaces -emb = vayesta.ewf.EWF(mf, solver='Dump', bath_options=dict(threshold=1e-6), - solver_options=dict(dumpfile='clusters-rhf.h5')) +emb = vayesta.ewf.EWF( + mf, solver="Dump", bath_options=dict(threshold=1e-6), solver_options=dict(dumpfile="clusters-rhf.h5") +) emb.kernel() # ---- Unrestricted spin symmetry @@ -35,8 +36,8 @@ H3 0.0000 -0.7572 -0.4692 """ mol.charge = mol.spin = 3 -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -44,8 +45,9 @@ mf.kernel() # Embedding class to form fragment, bath, and cluster spaces -emb = vayesta.ewf.EWF(mf, solver='Dump', bath_options=dict(threshold=1e-6), - solver_options=dict(dumpfile='clusters-uhf.h5')) +emb = vayesta.ewf.EWF( + mf, solver="Dump", bath_options=dict(threshold=1e-6), solver_options=dict(dumpfile="clusters-uhf.h5") +) emb.kernel() @@ -53,66 +55,67 @@ print("\nOpening restricted dump file:") import h5py -with h5py.File('clusters-rhf.h5', 'r') as f: + +with h5py.File("clusters-rhf.h5", "r") as f: # The HDF5 file contains a separate group for each fragment in the system: for key, frag in f.items(): # The HDF5-group key for each fragment is constructed as 'fragment_%d' % id print("\nKey= %s" % key) # Name and ID of fragment: - print("name= %s, id= %d" % (frag.attrs['name'], frag.attrs['id'])) + print("name= %s, id= %d" % (frag.attrs["name"], frag.attrs["id"])) # Number of all/occupied/virtual orbitals: print("Full cluster:") - norb, nocc, nvir = frag.attrs['norb'], frag.attrs['nocc'], frag.attrs['nvir'] + norb, nocc, nvir = frag.attrs["norb"], frag.attrs["nocc"], frag.attrs["nvir"] print("norb= %d, nocc= %d, nvir= %d" % (norb, nocc, nvir)) # Orbital coefficients: # The first dimension corresponds to the atomic orbitals, # the second dimension to the cluster or fragment orbitals, respectively. - print("c_cluster.shape= (%d, %d)" % frag['c_cluster'].shape) - print("c_frag.shape= (%d, %d)" % frag['c_frag'].shape) + print("c_cluster.shape= (%d, %d)" % frag["c_cluster"].shape) + print("c_frag.shape= (%d, %d)" % frag["c_frag"].shape) # Integral arrays: # fock, and eris are the Fock, and 2-electron Hamiltonian # matrix elements in the cluster space. # heff is the 1-electron Hamiltonian, plus an additional potential due to the Coulomb and exchange # interaction with the occupied environment orbitals outside the cluster. - print("heff.shape= (%d, %d)" % frag['heff'].shape) - print("fock.shape= (%d, %d)" % frag['fock'].shape) + print("heff.shape= (%d, %d)" % frag["heff"].shape) + print("fock.shape= (%d, %d)" % frag["fock"].shape) # The 2-electron integrals are in chemical ordering: eris[i,j,k,l] = (ij|kl) - print("eris.shape= (%d, %d, %d, %d)" % frag['eris'].shape) + print("eris.shape= (%d, %d, %d, %d)" % frag["eris"].shape) # DMET cluster: print("DMET cluster:") - norb, nocc, nvir = [frag.attrs['%s_dmet_cluster' % x] for x in ('norb', 'nocc', 'nvir')] + norb, nocc, nvir = [frag.attrs["%s_dmet_cluster" % x] for x in ("norb", "nocc", "nvir")] print("norb= %d, nocc= %d, nvir= %d" % (norb, nocc, nvir)) - print("c_dmet_cluster.shape= (%d, %d)" % frag['c_dmet_cluster'].shape) + print("c_dmet_cluster.shape= (%d, %d)" % frag["c_dmet_cluster"].shape) # --- Open unrestricted dump file: print("\nOpening unrestricted dump file:") -with h5py.File('clusters-uhf.h5', 'r') as f: +with h5py.File("clusters-uhf.h5", "r") as f: for key, frag in f.items(): print("\nKey= %s" % key) # Name and ID: - print("name= %s, id= %d" % (frag.attrs['name'], frag.attrs['id'])) + print("name= %s, id= %d" % (frag.attrs["name"], frag.attrs["id"])) # The orbital sizes are now arrays of length 2, representing alpha and beta spin dimension - norb, nocc, nvir = frag.attrs['norb'], frag.attrs['nocc'], frag.attrs['nvir'] + norb, nocc, nvir = frag.attrs["norb"], frag.attrs["nocc"], frag.attrs["nvir"] print("norb= (%d, %d), nocc= (%d, %d), nvir= (%d, %d)" % (*norb, *nocc, *nvir)) # The suffixes _a and _b correspond to alpha and beta spin: - print("c_cluster_a.shape= (%d, %d)" % frag['c_cluster_a'].shape) - print("c_cluster_b.shape= (%d, %d)" % frag['c_cluster_b'].shape) - print("c_frag_a.shape= (%d, %d)" % frag['c_frag_a'].shape) - print("c_frag_b.shape= (%d, %d)" % frag['c_frag_b'].shape) + print("c_cluster_a.shape= (%d, %d)" % frag["c_cluster_a"].shape) + print("c_cluster_b.shape= (%d, %d)" % frag["c_cluster_b"].shape) + print("c_frag_a.shape= (%d, %d)" % frag["c_frag_a"].shape) + print("c_frag_b.shape= (%d, %d)" % frag["c_frag_b"].shape) # Integral arrays: - print("heff_a.shape= (%d, %d)" % frag['heff_a'].shape) - print("heff_b.shape= (%d, %d)" % frag['heff_b'].shape) - print("fock_a.shape= (%d, %d)" % frag['fock_a'].shape) - print("fock_b.shape= (%d, %d)" % frag['fock_b'].shape) + print("heff_a.shape= (%d, %d)" % frag["heff_a"].shape) + print("heff_b.shape= (%d, %d)" % frag["heff_b"].shape) + print("fock_a.shape= (%d, %d)" % frag["fock_a"].shape) + print("fock_b.shape= (%d, %d)" % frag["fock_b"].shape) # The spin blocks correspond to (aa|aa), (aa|bb), and (bb|bb) # (Note that (bb|aa) = (aa|bb).transpose(2,3,0,1) for real orbitals): - print("eris_aa.shape= (%d, %d, %d, %d)" % frag['eris_aa'].shape) - print("eris_ab.shape= (%d, %d, %d, %d)" % frag['eris_ab'].shape) - print("eris_bb.shape= (%d, %d, %d, %d)" % frag['eris_bb'].shape) + print("eris_aa.shape= (%d, %d, %d, %d)" % frag["eris_aa"].shape) + print("eris_ab.shape= (%d, %d, %d, %d)" % frag["eris_ab"].shape) + print("eris_bb.shape= (%d, %d, %d, %d)" % frag["eris_bb"].shape) # DMET cluster: print("DMET cluster:") - norb, nocc, nvir = [frag.attrs['%s_dmet_cluster' % x] for x in ('norb', 'nocc', 'nvir')] + norb, nocc, nvir = [frag.attrs["%s_dmet_cluster" % x] for x in ("norb", "nocc", "nvir")] print("norb= (%d, %d), nocc= (%d, %d), nvir= (%d, %d)" % (*norb, *nocc, *nvir)) - print("c_dmet_cluster_a.shape= (%d, %d)" % frag['c_dmet_cluster_a'].shape) - print("c_dmet_cluster_b.shape= (%d, %d)" % frag['c_dmet_cluster_b'].shape) + print("c_dmet_cluster_a.shape= (%d, %d)" % frag["c_dmet_cluster_a"].shape) + print("c_dmet_cluster_b.shape= (%d, %d)" % frag["c_dmet_cluster_b"].shape) diff --git a/examples/ewf/molecules/21-fci-solver.py b/examples/ewf/molecules/21-fci-solver.py index 189de3611..68a9175d5 100644 --- a/examples/ewf/molecules/21-fci-solver.py +++ b/examples/ewf/molecules/21-fci-solver.py @@ -7,9 +7,9 @@ import vayesta.ewf mol = pyscf.gto.Mole() -mol.atom = ['N 0 0 0', 'N 0 0 2'] -mol.basis = 'aug-cc-pvdz' -mol.output = 'pyscf.out' +mol.atom = ["N 0 0 0", "N 0 0 2"] +mol.basis = "aug-cc-pvdz" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -25,7 +25,7 @@ casscf.kernel() # FCI with DMET bath orbitals only -emb = vayesta.ewf.EWF(mf, solver='FCI', bath_options=dict(bathtype='dmet')) +emb = vayesta.ewf.EWF(mf, solver="FCI", bath_options=dict(bathtype="dmet")) with emb.iao_fragmentation() as f: f.add_atomic_fragment(0, sym_factor=2) emb.kernel() diff --git a/examples/ewf/molecules/23-tccsd-solver.py b/examples/ewf/molecules/23-tccsd-solver.py index 115caf20d..4f229990d 100644 --- a/examples/ewf/molecules/23-tccsd-solver.py +++ b/examples/ewf/molecules/23-tccsd-solver.py @@ -6,8 +6,8 @@ import vayesta.ewf mol = pyscf.gto.Mole() -mol.atom = 'N 0 0 0 ; N 0 0 1.4' -mol.basis = 'cc-pVDZ' +mol.atom = "N 0 0 0 ; N 0 0 1.4" +mol.basis = "cc-pVDZ" mol.build() # Hartree-Fock @@ -23,15 +23,15 @@ emb.kernel() # Embedded Tailored CCSD (Embedded CCSD tailored with FCI in DMET cluster) -emb_tcc = vayesta.ewf.EWF(mf, solver='TCCSD', bath_options=dict(threshold=1e-4)) +emb_tcc = vayesta.ewf.EWF(mf, solver="TCCSD", bath_options=dict(threshold=1e-4)) emb_tcc.kernel() # Embedded Tailored CCSD with custom CAS -emb_tcc2 = vayesta.ewf.EWF(mf, solver='TCCSD', bath_options=dict(threshold=1e-4)) +emb_tcc2 = vayesta.ewf.EWF(mf, solver="TCCSD", bath_options=dict(threshold=1e-4)) with emb_tcc2.fragmentation() as frag: frag.add_all_atomic_fragments() -emb_tcc2.fragments[0].set_cas(iaos='0 N 2p') -emb_tcc2.fragments[1].set_cas(iaos='1 N 2p') +emb_tcc2.fragments[0].set_cas(iaos="0 N 2p") +emb_tcc2.fragments[1].set_cas(iaos="1 N 2p") emb_tcc2.kernel() diff --git a/examples/ewf/molecules/24-delta-tailor.py b/examples/ewf/molecules/24-delta-tailor.py index 03d07e9ec..d24151a55 100644 --- a/examples/ewf/molecules/24-delta-tailor.py +++ b/examples/ewf/molecules/24-delta-tailor.py @@ -10,11 +10,10 @@ dm1 = None for d in np.arange(1.0, 1.5, 0.2): - mol = pyscf.gto.Mole() - mol.atom = 'N 0 0 0 ; N 0 0 %f' % d - cassize = (6,6) - mol.basis = 'STO-6G' + mol.atom = "N 0 0 0 ; N 0 0 %f" % d + cassize = (6, 6) + mol.basis = "STO-6G" mol.build() # Hartree-Fock @@ -36,9 +35,13 @@ fci.kernel() def make_tcc(correction_type): - tcc = vayesta.ewf.EWF(mf, solver='CCSD', bath_options=dict(bathtype='full'), solver_options=dict(solve_lambda=True)) + tcc = vayesta.ewf.EWF( + mf, solver="CCSD", bath_options=dict(bathtype="full"), solver_options=dict(solve_lambda=True) + ) with tcc.cas_fragmentation() as f: - cas = f.add_cas_fragment(*cassize, solver='FCI', store_wf_type='CCSD', bath_options=dict(bathtype='dmet'), auxiliary=True) + cas = f.add_cas_fragment( + *cassize, solver="FCI", store_wf_type="CCSD", bath_options=dict(bathtype="dmet"), auxiliary=True + ) with tcc.sao_fragmentation() as f: ccsd = f.add_atomic_fragment([0, 1]) ccsd.add_external_corrections([cas], correction_type=correction_type, projectors=0) @@ -46,9 +49,9 @@ def make_tcc(correction_type): return tcc # Conventional Tailored CCSD - tcc = make_tcc('tailor') + tcc = make_tcc("tailor") # "delta" TCCSD - dtcc = make_tcc('delta-tailor') + dtcc = make_tcc("delta-tailor") def energy(obj): if obj.converged: @@ -56,6 +59,6 @@ def energy(obj): return np.nan energies = [mf.e_tot, energy(cc), energy(fci), energy(tcc), energy(dtcc)] - with open('energies.txt', 'a') as f: - fmt = '%.2f' + (len(energies)*' %+16.8f') + '\n' + with open("energies.txt", "a") as f: + fmt = "%.2f" + (len(energies) * " %+16.8f") + "\n" f.write(fmt % (d, *energies)) diff --git a/examples/ewf/molecules/24-tailor-with-fragments.py b/examples/ewf/molecules/24-tailor-with-fragments.py index 54d577034..a06142707 100644 --- a/examples/ewf/molecules/24-tailor-with-fragments.py +++ b/examples/ewf/molecules/24-tailor-with-fragments.py @@ -12,7 +12,7 @@ Fe 0 0 0 O 0 0 1.616 """ -mol.spin= 4 # 3d6 Fe(II) +mol.spin = 4 # 3d6 Fe(II) mol.basis = "6-31g" mol.build() @@ -27,12 +27,12 @@ if stable: break mf.kernel(dm1) -assert(mf.converged) +assert mf.converged # Reference full system CCSD: cc = CCSD(mf) cc.kernel() -output = [f'Hartree Fock Energy={mf.e_tot}, CCSD Energy={cc.e_tot if cc.converged else np.nan}'] +output = [f"Hartree Fock Energy={mf.e_tot}, CCSD Energy={cc.e_tot if cc.converged else np.nan}"] for projectors in (0, 1, 2): # Tailor CCSD with an atomic FCI fragment (projected onto fragment space) # T1 and T2 amplitudes of the FCI fragment are used to tailor the CCSD amplitudes. setting auxilary to true, @@ -42,13 +42,20 @@ emb = EWF(mf) fci_frags = [] with emb.iao_fragmentation() as f: - fci_frags.append(f.add_atomic_fragment(['Fe'], orbital_filter=['Fe 3d'], solver='FCI', - store_wf_type='CCSDTQ', - bath_options=dict(bathtype='dmet'), auxiliary=True)) - ccsd = f.add_full_system(solver='extCCSD', bath_options=dict(bathtype='full')) - ccsd.add_external_corrections(fci_frags, correction_type='tailor', projectors=projectors) + fci_frags.append( + f.add_atomic_fragment( + ["Fe"], + orbital_filter=["Fe 3d"], + solver="FCI", + store_wf_type="CCSDTQ", + bath_options=dict(bathtype="dmet"), + auxiliary=True, + ) + ) + ccsd = f.add_full_system(solver="extCCSD", bath_options=dict(bathtype="full")) + ccsd.add_external_corrections(fci_frags, correction_type="tailor", projectors=projectors) emb.kernel() - output.append(f'Projectors={projectors}, Tailored CC Energy={emb.e_tot if emb.converged else np.nan} ') + output.append(f"Projectors={projectors}, Tailored CC Energy={emb.e_tot if emb.converged else np.nan} ") for line in output: print(line) diff --git a/examples/ewf/molecules/25-externally-correct.py b/examples/ewf/molecules/25-externally-correct.py index 124b0bd0b..05d73b99c 100644 --- a/examples/ewf/molecules/25-externally-correct.py +++ b/examples/ewf/molecules/25-externally-correct.py @@ -11,8 +11,8 @@ O 0.0000 1.3464 -0.5965 O 0.0000 -1.3464 -0.5965 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -28,32 +28,44 @@ with emb.iao_fragmentation() as f: # Add all atomic FCI fragments with DMET bath # Store the FCI wave functions as CCSDTQ types, so they can be used for correction later. - # These want to be flagged as 'auxiliary', as they are solved first, and then used as constraints for the + # These want to be flagged as 'auxiliary', as they are solved first, and then used as constraints for the # non-auxiliary fragments. - fci_frags = f.add_all_atomic_fragments(solver='FCI', bath_options=dict(bathtype='dmet'), store_wf_type='CCSDTQ', auxiliary=True) + fci_frags = f.add_all_atomic_fragments( + solver="FCI", bath_options=dict(bathtype="dmet"), store_wf_type="CCSDTQ", auxiliary=True + ) # Add single 'complete' CCSD fragment covering all IAOs - ccsd_frag = f.add_full_system(solver='CCSD', bath_options=dict(bathtype='full')) + ccsd_frag = f.add_full_system(solver="CCSD", bath_options=dict(bathtype="full")) # Setup the external correction from the CCSD fragment. # Main option is 'projectors', which should be an integer between 0 and 2 (inclusive). # The larger the number, the more fragment projectors are applied to the correcting T2 contributions, and less # 'bath' correlation from the FCI clusters is used as a constraint in the external correction of the CCSD clusters. -# For multiple constraining fragments, proj=0 will double-count the correction due to overlapping bath spaces, and +# For multiple constraining fragments, proj=0 will double-count the correction due to overlapping bath spaces, and # in this case, only proj=1 will be exact in the limit of enlarging (FCI) bath spaces. # Note that there is also the option 'low_level_coul' (default True). For the important T3 * V contribution to the # T2 amplitudes, this determines whether the V is expressed in the FCI or CCSD cluster space. The CCSD cluster is # larger, and hence this is likely to be better (and is default), as the correction is longer-ranged (though slightly more expensive). -ccsd_frag.add_external_corrections(fci_frags, correction_type='external', projectors=1) +ccsd_frag.add_external_corrections(fci_frags, correction_type="external", projectors=1) emb.kernel() -print('Total energy from full system CCSD tailored (CCSD Coulomb interaction) by atomic FCI fragments (projectors=1): {}'.format(emb.e_tot)) +print( + "Total energy from full system CCSD tailored (CCSD Coulomb interaction) by atomic FCI fragments (projectors=1): {}".format( + emb.e_tot + ) +) # 2) Now, we also fragment the CCSD spaces, and use BNOs. These CCSD fragments are individually externally corrected from the FCI clusters. # Similar set up, but we now have multiple CCSD clusters. emb = vayesta.ewf.EWF(mf) with emb.iao_fragmentation() as f: - fci_frags = f.add_all_atomic_fragments(solver='FCI', bath_options=dict(bathtype='dmet'), store_wf_type='CCSDTQ', auxiliary=True) - ccsd_frags = f.add_all_atomic_fragments(solver='CCSD', bath_options=dict(bathtype='mp2', threshold=1.e-5)) + fci_frags = f.add_all_atomic_fragments( + solver="FCI", bath_options=dict(bathtype="dmet"), store_wf_type="CCSDTQ", auxiliary=True + ) + ccsd_frags = f.add_all_atomic_fragments(solver="CCSD", bath_options=dict(bathtype="mp2", threshold=1.0e-5)) # Now add external corrections to all CCSD clusters, and use 'external' correction, with 2 projectors and only contracting with the FCI integrals for cc_frag in ccsd_frags: - cc_frag.add_external_corrections(fci_frags, correction_type='external', projectors=2, low_level_coul=False) + cc_frag.add_external_corrections(fci_frags, correction_type="external", projectors=2, low_level_coul=False) emb.kernel() -print('Total energy from embedded CCSD tailored (FCI Coulomb interaction) by atomic FCI fragments (projectors=2): {}'.format(emb.e_tot)) +print( + "Total energy from embedded CCSD tailored (FCI Coulomb interaction) by atomic FCI fragments (projectors=2): {}".format( + emb.e_tot + ) +) diff --git a/examples/ewf/molecules/26-ebcc-solvers.py b/examples/ewf/molecules/26-ebcc-solvers.py index 90f2d7062..09cf4fa58 100644 --- a/examples/ewf/molecules/26-ebcc-solvers.py +++ b/examples/ewf/molecules/26-ebcc-solvers.py @@ -7,9 +7,9 @@ import vayesta.ewf mol = pyscf.gto.Mole() -mol.atom = ['N 0 0 0', 'N 0 0 2'] -mol.basis = 'aug-cc-pvdz' -mol.output = 'pyscf.out' +mol.atom = ["N 0 0 0", "N 0 0 2"] +mol.basis = "aug-cc-pvdz" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -24,15 +24,17 @@ casscf = pyscf.mcscf.CASSCF(mf, 8, 10) casscf.kernel() -def get_emb_result(ansatz, bathtype='full'): + +def get_emb_result(ansatz, bathtype="full"): # Uses fastest available solver for given ansatz; PySCF if available, otherwise ebcc. - emb = vayesta.ewf.EWF(mf, solver=ansatz, bath_options=dict(bathtype=bathtype), - solver_options=dict(solve_lambda=False)) + emb = vayesta.ewf.EWF( + mf, solver=ansatz, bath_options=dict(bathtype=bathtype), solver_options=dict(solve_lambda=False) + ) # Both these alternative specifications will always use an ebcc solver. # Note that the capitalization of the solver name other than the ansatz is arbitrary. - #emb = vayesta.ewf.EWF(mf, solver=f'EB{ansatz}', bath_options=dict(bathtype=bathtype), + # emb = vayesta.ewf.EWF(mf, solver=f'EB{ansatz}', bath_options=dict(bathtype=bathtype), # solver_options=dict(solve_lambda=False)) - #emb = vayesta.ewf.EWF(mf, solver='ebcc', bath_options=dict(bathtype=bathtype), + # emb = vayesta.ewf.EWF(mf, solver='ebcc', bath_options=dict(bathtype=bathtype), # solver_options=dict(solve_lambda=False, ansatz=ansatz)) with emb.iao_fragmentation() as f: @@ -41,9 +43,10 @@ def get_emb_result(ansatz, bathtype='full'): emb.kernel() return emb.e_tot -e_ccsd = get_emb_result('CCSD', 'full') -e_ccsdt = get_emb_result('CCSDT', 'dmet') -e_ccsdtprime = get_emb_result("CCSDt'", 'full') + +e_ccsd = get_emb_result("CCSD", "full") +e_ccsdt = get_emb_result("CCSDT", "dmet") +e_ccsdtprime = get_emb_result("CCSDt'", "full") print("E(HF)= %+16.8f Ha" % mf.e_tot) print("E(CASCI)= %+16.8f Ha" % casci.e_tot) diff --git a/examples/ewf/molecules/31-population-analysis.py b/examples/ewf/molecules/31-population-analysis.py index 1494ec4c9..f28bbf3a1 100644 --- a/examples/ewf/molecules/31-population-analysis.py +++ b/examples/ewf/molecules/31-population-analysis.py @@ -6,9 +6,9 @@ import vayesta.ewf mol = pyscf.gto.Mole() -mol.atom = 'H 0 0 0 ; F 0 0 2' -mol.basis = 'aug-cc-pVDZ' -mol.output = 'pyscf.out' +mol.atom = "H 0 0 0 ; F 0 0 2" +mol.basis = "aug-cc-pVDZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock: @@ -16,24 +16,23 @@ mf.kernel() # Embedded CCSD: -emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-4), - solver_options=dict(solve_lambda=True)) +emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-4), solver_options=dict(solve_lambda=True)) emb.kernel() # Population analysis of mean-field density-matrix: dm1 = mf.make_rdm1() -emb.pop_analysis(dm1, local_orbitals='mulliken') -emb.pop_analysis(dm1, local_orbitals='lowdin') -emb.pop_analysis(dm1, local_orbitals='iao+pao') +emb.pop_analysis(dm1, local_orbitals="mulliken") +emb.pop_analysis(dm1, local_orbitals="lowdin") +emb.pop_analysis(dm1, local_orbitals="iao+pao") # Population analysis of the correlated density-matrix: dm1 = emb.make_rdm1(ao_basis=True) -emb.pop_analysis(dm1, local_orbitals='mulliken') -emb.pop_analysis(dm1, local_orbitals='lowdin') -emb.pop_analysis(dm1, local_orbitals='iao+pao') +emb.pop_analysis(dm1, local_orbitals="mulliken") +emb.pop_analysis(dm1, local_orbitals="lowdin") +emb.pop_analysis(dm1, local_orbitals="iao+pao") # Population analysis can also be written to a file, when filename is provided, # and include orbital resolution, if orbital_resolved=True: -emb.pop_analysis(dm1, local_orbitals='mulliken', filename='pop-mulliken-cc.txt', orbital_resolved=True) -emb.pop_analysis(dm1, local_orbitals='lowdin', filename='pop-lowdin-cc.txt', orbital_resolved=True) -emb.pop_analysis(dm1, local_orbitals='iao+pao', filename='pop-iaopao-cc.txt', orbital_resolved=True) +emb.pop_analysis(dm1, local_orbitals="mulliken", filename="pop-mulliken-cc.txt", orbital_resolved=True) +emb.pop_analysis(dm1, local_orbitals="lowdin", filename="pop-lowdin-cc.txt", orbital_resolved=True) +emb.pop_analysis(dm1, local_orbitals="iao+pao", filename="pop-iaopao-cc.txt", orbital_resolved=True) diff --git a/examples/ewf/molecules/32-static-corrfuncs.py b/examples/ewf/molecules/32-static-corrfuncs.py index 27e3d3fb8..a43fee8e4 100644 --- a/examples/ewf/molecules/32-static-corrfuncs.py +++ b/examples/ewf/molecules/32-static-corrfuncs.py @@ -11,8 +11,8 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock: @@ -20,24 +20,23 @@ mf.kernel() # Embedded CCSD: -emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-5), - solver_options=dict(solve_lambda=True)) +emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-5), solver_options=dict(solve_lambda=True)) emb.kernel() print() print("Density-density fluctuations :") -corr_mf = emb.get_corrfunc_mf('dN,dN') -corr_cc = emb.get_corrfunc('dN,dN') +corr_mf = emb.get_corrfunc_mf("dN,dN") +corr_cc = emb.get_corrfunc("dN,dN") for a in range(mol.natm): for b in range(mol.natm): - print('A= %d, B= %d: HF= %+.5f CC= %+.5f' % (a, b, corr_mf[a,b], corr_cc[a,b])) + print("A= %d, B= %d: HF= %+.5f CC= %+.5f" % (a, b, corr_mf[a, b], corr_cc[a, b])) print("Total: HF= %+.5f CC= %+.5f" % (corr_mf.sum(), corr_cc.sum())) print() print("Spin-spin correlation :") -corr_mf = emb.get_corrfunc_mf('Sz,Sz') -corr_cc = emb.get_corrfunc('Sz,Sz') +corr_mf = emb.get_corrfunc_mf("Sz,Sz") +corr_cc = emb.get_corrfunc("Sz,Sz") for a in range(mol.natm): for b in range(mol.natm): - print('A= %d, B= %d: HF= %+.5f CC= %+.5f' % (a, b, corr_mf[a,b], corr_cc[a,b])) + print("A= %d, B= %d: HF= %+.5f CC= %+.5f" % (a, b, corr_mf[a, b], corr_cc[a, b])) print("Total: HF= %+.5f CC= %+.5f" % (corr_mf.sum(), corr_cc.sum())) diff --git a/examples/ewf/molecules/35-screening-rpa-corrections.py b/examples/ewf/molecules/35-screening-rpa-corrections.py index 17375d4ef..b72207e9d 100644 --- a/examples/ewf/molecules/35-screening-rpa-corrections.py +++ b/examples/ewf/molecules/35-screening-rpa-corrections.py @@ -10,8 +10,8 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVTZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVTZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -39,11 +39,19 @@ # Note that mRPA screening and external corrections often cancel with each other in the case of the energy. print("E(CCSD)= %+16.8f Ha" % cc.e_tot) -print("E(RPA)= %+16.8f Ha (error= %+.8f Ha)" % (emb.e_mf + emb.e_rpa, - emb.e_mf + emb.e_rpa - cc.e_tot)) -print("E(Emb. CCSD)= %+16.8f Ha (error= %+.8f Ha)" % (emb_bare.e_tot, emb_bare.e_tot-cc.e_tot)) -print("E(Emb. Screened CCSD)= %+16.8f Ha (error= %+.8f Ha)" % (emb.e_tot, emb.e_tot-cc.e_tot)) -print("E(Emb. Screened CCSD + \Delta E_k)= %+16.8f Ha (error= %+.8f Ha)" % (emb.e_tot+e_nonlocal_cumulant, - emb.e_tot+e_nonlocal_cumulant-cc.e_tot)) -print("E(Emb. Screened CCSD + \Delta RPA)= %+16.8f Ha (error= %+.8f Ha)" % (emb.e_tot+e_nonlocal_erpa, - emb.e_tot+e_nonlocal_erpa-cc.e_tot)) +print( + "E(RPA)= %+16.8f Ha (error= %+.8f Ha)" + % (emb.e_mf + emb.e_rpa, emb.e_mf + emb.e_rpa - cc.e_tot) +) +print( + "E(Emb. CCSD)= %+16.8f Ha (error= %+.8f Ha)" % (emb_bare.e_tot, emb_bare.e_tot - cc.e_tot) +) +print("E(Emb. Screened CCSD)= %+16.8f Ha (error= %+.8f Ha)" % (emb.e_tot, emb.e_tot - cc.e_tot)) +print( + "E(Emb. Screened CCSD + \Delta E_k)= %+16.8f Ha (error= %+.8f Ha)" + % (emb.e_tot + e_nonlocal_cumulant, emb.e_tot + e_nonlocal_cumulant - cc.e_tot) +) +print( + "E(Emb. Screened CCSD + \Delta RPA)= %+16.8f Ha (error= %+.8f Ha)" + % (emb.e_tot + e_nonlocal_erpa, emb.e_tot + e_nonlocal_erpa - cc.e_tot) +) diff --git a/examples/ewf/molecules/36-crpa-cas-screening.py b/examples/ewf/molecules/36-crpa-cas-screening.py index c15022df0..b585d98e4 100644 --- a/examples/ewf/molecules/36-crpa-cas-screening.py +++ b/examples/ewf/molecules/36-crpa-cas-screening.py @@ -7,14 +7,14 @@ mol = pyscf.gto.Mole() mol.atom = molecules.arene(6) -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() mf = pyscf.scf.RHF(mol).density_fit() mf.kernel() -ncas = (4,4) +ncas = (4, 4) # Reference CASCI mycasci = pyscf.mcscf.CASCI(mf, *ncas) @@ -39,7 +39,16 @@ casci_mrpa.kernel() print("E(HF)= %+16.8f Ha" % mf.e_tot) -print("ΔE(CASCI)= %+16.8f Ha" % (mycasci.e_tot-mf.e_tot)) -print("ΔE(Emb. CASCI, bare interactions)= %+16.8f Ha (diff= %+.8f Ha)" % (casci_bare.e_tot-mf.e_tot, casci_bare.e_tot-mycasci.e_tot)) -print("ΔE(Emb. CASCI, cRPA screening)= %+16.8f Ha (diff= %+.8f Ha)" % (casci_crpa.e_tot-mf.e_tot, casci_crpa.e_tot-mycasci.e_tot)) -print("ΔE(Emb. CASCI, mRPA screening)= %+16.8f Ha (diff= %+.8f Ha)" % (casci_mrpa.e_tot-mf.e_tot, casci_mrpa.e_tot-mycasci.e_tot)) +print("ΔE(CASCI)= %+16.8f Ha" % (mycasci.e_tot - mf.e_tot)) +print( + "ΔE(Emb. CASCI, bare interactions)= %+16.8f Ha (diff= %+.8f Ha)" + % (casci_bare.e_tot - mf.e_tot, casci_bare.e_tot - mycasci.e_tot) +) +print( + "ΔE(Emb. CASCI, cRPA screening)= %+16.8f Ha (diff= %+.8f Ha)" + % (casci_crpa.e_tot - mf.e_tot, casci_crpa.e_tot - mycasci.e_tot) +) +print( + "ΔE(Emb. CASCI, mRPA screening)= %+16.8f Ha (diff= %+.8f Ha)" + % (casci_mrpa.e_tot - mf.e_tot, casci_mrpa.e_tot - mycasci.e_tot) +) diff --git a/examples/ewf/molecules/37-bosonic-bath.py b/examples/ewf/molecules/37-bosonic-bath.py index 72aa7a8d0..adc91c1ad 100644 --- a/examples/ewf/molecules/37-bosonic-bath.py +++ b/examples/ewf/molecules/37-bosonic-bath.py @@ -6,8 +6,8 @@ mol = pyscf.gto.Mole() mol.atom = molecules.arene(6) -mol.basis = '6-31G' -mol.output = 'pyscf.out' +mol.basis = "6-31G" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -18,7 +18,7 @@ cc = pyscf.cc.CCSD(mf) cc.kernel() -bath_opts = dict(bathtype="mp2", threshold=1e-4, project_dmet_order=1, project_dmet_mode='full') +bath_opts = dict(bathtype="mp2", threshold=1e-4, project_dmet_order=1, project_dmet_mode="full") bosonic_bath_opts = dict(bathtype="rpa", target_orbitals="full", local_projection="fragment", threshold=1e-3) # Embedded CCSD calculation with bare interactions and no energy correction. @@ -44,6 +44,15 @@ # Note that mRPA screening and external corrections often cancel with each other in the case of the energy. print("E(CCSD)= %+16.8f Ha" % cc.e_tot) -print("E(CCSD, projected locally)= %+16.8f Ha (external error= %+.8f Ha)" % (emb_exact.e_tot, emb_exact.e_tot-cc.e_tot)) -print("E(Emb. CCSD)= %+16.8f Ha (internal error= %+.8f Ha)" % (emb_bare.e_tot, emb_bare.e_tot-emb_exact.e_tot)) -print("E(Emb. CCSD with bosons)= %+16.8f Ha (internal error= %+.8f Ha)" % (emb.e_tot, emb.e_tot-emb_exact.e_tot)) +print( + "E(CCSD, projected locally)= %+16.8f Ha (external error= %+.8f Ha)" + % (emb_exact.e_tot, emb_exact.e_tot - cc.e_tot) +) +print( + "E(Emb. CCSD)= %+16.8f Ha (internal error= %+.8f Ha)" + % (emb_bare.e_tot, emb_bare.e_tot - emb_exact.e_tot) +) +print( + "E(Emb. CCSD with bosons)= %+16.8f Ha (internal error= %+.8f Ha)" + % (emb.e_tot, emb.e_tot - emb_exact.e_tot) +) diff --git a/examples/ewf/molecules/40-spectral-moments.py b/examples/ewf/molecules/40-spectral-moments.py index f7ba0a437..567ab2b48 100644 --- a/examples/ewf/molecules/40-spectral-moments.py +++ b/examples/ewf/molecules/40-spectral-moments.py @@ -22,26 +22,26 @@ mol.atom = vayesta.misc.molecules.alkane(4) print(mol.atom) -mol.basis = 'sto-6g' -mol.output = 'pyscf.out' +mol.basis = "sto-6g" +mol.output = "pyscf.out" mol.build() # Hartree-Fock mf = pyscf.scf.RHF(mol) mf.kernel() -niter = (5,0) +niter = (5, 0) # Embedded CCSD emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=-1), solver_options=dict(solve_lambda=True)) -emb.opts.nmoments= 2*niter[0] + 2 +emb.opts.nmoments = 2 * niter[0] + 2 with emb.iao_fragmentation() as f: n = len(mol.atom) - f.add_atomic_fragment([0,1,2,3]) - for i in range(4, n-4, 3): - f.add_atomic_fragment([i,i+1, i+2]) - f.add_atomic_fragment([n-1,n-2,n-3,n-4]) + f.add_atomic_fragment([0, 1, 2, 3]) + for i in range(4, n - 4, 3): + f.add_atomic_fragment([i, i + 1, i + 2]) + f.add_atomic_fragment([n - 1, n - 2, n - 3, n - 4]) emb.kernel() @@ -58,7 +58,7 @@ th = gfcc.build_hole_moments() print(len(th)) -#tp = gfcc.build_part_moments() +# tp = gfcc.build_part_moments() moms = vayesta.ewf.moments.make_ccsdgf_moms(emb) @@ -66,13 +66,15 @@ print(moms[0]) for i in range(len(th)): - th[i] = (th[i] + th[i].T)/2 - moms[0][i] = (moms[0][i] + moms[0][i].T)/2 + th[i] = (th[i] + th[i].T) / 2 + moms[0][i] = (moms[0][i] + moms[0][i].T) / 2 for i in range(len(th)): mask = np.abs(th[i]) > 1e-10 - print("%d mom: norm = %e maxerr = %e"%(i, np.linalg.norm(th[i]-moms[0][i]), (np.abs(th[i]-moms[0][i])).max())) + print( + "%d mom: norm = %e maxerr = %e" % (i, np.linalg.norm(th[i] - moms[0][i]), (np.abs(th[i] - moms[0][i])).max()) + ) mask = np.abs(th) > 1e-10 -print(np.abs(th-moms[0])) +print(np.abs(th - moms[0])) diff --git a/examples/ewf/molecules/41-ccsd-incluster-moments.py b/examples/ewf/molecules/41-ccsd-incluster-moments.py index d2120f14b..d0ad4ff57 100644 --- a/examples/ewf/molecules/41-ccsd-incluster-moments.py +++ b/examples/ewf/molecules/41-ccsd-incluster-moments.py @@ -12,8 +12,8 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.txt' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.txt" mol.build() # Hartree-Fock @@ -21,7 +21,7 @@ mf.kernel() # Embedded CCSD -emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-6), solver_options=dict(n_moments=(2,4), solve_lambda=True)) +emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-6), solver_options=dict(n_moments=(2, 4), solve_lambda=True)) emb.kernel() # Reference full system CCSD: diff --git a/examples/ewf/molecules/42-fci-incluster-moments.py b/examples/ewf/molecules/42-fci-incluster-moments.py index 6d29e0158..718456cb6 100644 --- a/examples/ewf/molecules/42-fci-incluster-moments.py +++ b/examples/ewf/molecules/42-fci-incluster-moments.py @@ -12,8 +12,8 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.txt' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.txt" mol.build() # Hartree-Fock @@ -21,7 +21,7 @@ mf.kernel() # Embedded CCSD -emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-6), solver_options=dict(n_moments=(5,4), solve_lambda=True)) +emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-6), solver_options=dict(n_moments=(5, 4), solve_lambda=True)) emb.kernel() # Reference full system CCSD: diff --git a/examples/ewf/molecules/51-intercluster-mp2.py b/examples/ewf/molecules/51-intercluster-mp2.py index 118465344..ee05ad1bf 100644 --- a/examples/ewf/molecules/51-intercluster-mp2.py +++ b/examples/ewf/molecules/51-intercluster-mp2.py @@ -1,4 +1,3 @@ - import pyscf import pyscf.gto import pyscf.scf @@ -14,14 +13,14 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock mf = pyscf.scf.RHF(mol) # Intercluster MP2 energy requries density fitting! -mf = mf.density_fit(auxbasis='cc-pVDZ-ri') +mf = mf.density_fit(auxbasis="cc-pVDZ-ri") mf.kernel() # Reference full system calculations: @@ -38,8 +37,8 @@ # Inter-cluster MP2 correction: e_icmp2 = emb.get_intercluster_mp2_energy() -print('E(HF)= %+16.8f Ha' % mf.e_tot) -print('E(CCSD)= %+16.8f Ha' % cc.e_tot) -print('E(MP2)= %+16.8f Ha (error= %+12.8f Ha)' % (mp2.e_tot, mp2.e_tot - cc.e_tot)) -print('E(Emb. CCSD)= %+16.8f Ha (error= %+12.8f Ha)' % (emb.e_tot, emb.e_tot - cc.e_tot)) -print('E(Emb. CCSD) + ICMP2= %+16.8f Ha (error= %+12.8f Ha)' % (emb.e_tot+e_icmp2, emb.e_tot+e_icmp2-cc.e_tot)) +print("E(HF)= %+16.8f Ha" % mf.e_tot) +print("E(CCSD)= %+16.8f Ha" % cc.e_tot) +print("E(MP2)= %+16.8f Ha (error= %+12.8f Ha)" % (mp2.e_tot, mp2.e_tot - cc.e_tot)) +print("E(Emb. CCSD)= %+16.8f Ha (error= %+12.8f Ha)" % (emb.e_tot, emb.e_tot - cc.e_tot)) +print("E(Emb. CCSD) + ICMP2= %+16.8f Ha (error= %+12.8f Ha)" % (emb.e_tot + e_icmp2, emb.e_tot + e_icmp2 - cc.e_tot)) diff --git a/examples/ewf/molecules/55-dm-energy.py b/examples/ewf/molecules/55-dm-energy.py index 81a194762..f76e74740 100644 --- a/examples/ewf/molecules/55-dm-energy.py +++ b/examples/ewf/molecules/55-dm-energy.py @@ -12,8 +12,8 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -25,19 +25,18 @@ cc.kernel() # Embedded CCSD -emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-6), - solver_options=dict(solve_lambda=True)) +emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-6), solver_options=dict(solve_lambda=True)) emb.kernel() print("Total Energy") print("E(HF)= %+16.8f Ha" % mf.e_tot) -print("E(EWF-DPart)= %+16.8f Ha"% emb.get_dmet_energy()) +print("E(EWF-DPart)= %+16.8f Ha" % emb.get_dmet_energy()) print("E(EWF-Proj)= %+16.8f Ha" % emb.e_tot) -print("E(EWF-DM)= %+16.8f Ha"% emb.get_dm_energy()) +print("E(EWF-DM)= %+16.8f Ha" % emb.get_dm_energy()) print("E(CCSD)= %+16.8f Ha" % cc.e_tot) print("\nCorrelation Energy") -print("E(EWF-DPart)= %+16.8f Ha"% (emb.get_dmet_energy()-mf.e_tot)) +print("E(EWF-DPart)= %+16.8f Ha" % (emb.get_dmet_energy() - mf.e_tot)) print("E(EWF-Proj)= %+16.8f Ha" % emb.e_corr) -print("E(EWF-DM)= %+16.8f Ha"% emb.get_dm_corr_energy()) +print("E(EWF-DM)= %+16.8f Ha" % emb.get_dm_corr_energy()) print("E(CCSD)= %+16.8f Ha" % cc.e_corr) diff --git a/examples/ewf/molecules/56-dm-energy-uccsd.py b/examples/ewf/molecules/56-dm-energy-uccsd.py index d9e7e44b3..fab35b0fb 100644 --- a/examples/ewf/molecules/56-dm-energy-uccsd.py +++ b/examples/ewf/molecules/56-dm-energy-uccsd.py @@ -12,8 +12,8 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -25,20 +25,19 @@ cc.kernel() # Embedded CCSD -emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-6), - solver_options=dict(solve_lambda=True)) +emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-6), solver_options=dict(solve_lambda=True)) emb.kernel() print("Total Energy") print("E(HF)= %+16.8f Ha" % mf.e_tot) -print("E(EWF-DPart)= %+16.8f Ha"% emb.get_dmet_energy()) +print("E(EWF-DPart)= %+16.8f Ha" % emb.get_dmet_energy()) print("E(EWF-Proj)= %+16.8f Ha" % emb.e_tot) -print("E(EWF-DM)= %+16.8f Ha"% emb.get_dm_energy()) +print("E(EWF-DM)= %+16.8f Ha" % emb.get_dm_energy()) print("E(CCSD)= %+16.8f Ha" % cc.e_tot) print("\nCorrelation Energy") -print("E(EWF-DPart)= %+16.8f Ha"% (emb.get_dmet_energy()-mf.e_tot)) +print("E(EWF-DPart)= %+16.8f Ha" % (emb.get_dmet_energy() - mf.e_tot)) print("E(EWF-Proj)= %+16.8f Ha" % emb.e_corr) -print("E(EWF-DM)= %+16.8f Ha"% emb.get_dm_corr_energy()) +print("E(EWF-DM)= %+16.8f Ha" % emb.get_dm_corr_energy()) print("E(CCSD)= %+16.8f Ha" % cc.e_corr) diff --git a/examples/ewf/molecules/61-lda.py b/examples/ewf/molecules/61-lda.py index 57860ca19..05961098c 100644 --- a/examples/ewf/molecules/61-lda.py +++ b/examples/ewf/molecules/61-lda.py @@ -13,13 +13,13 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() # LDA lda = pyscf.dft.RKS(mol) -lda.xc = 'svwn' +lda.xc = "svwn" lda.kernel() # The KS opbject needs to be converted to a HF object: lda_as_hf = pyscf.scf.RHF(mol) diff --git a/examples/ewf/molecules/71-modify-hcore.py b/examples/ewf/molecules/71-modify-hcore.py index 14c998ce8..5efacf350 100644 --- a/examples/ewf/molecules/71-modify-hcore.py +++ b/examples/ewf/molecules/71-modify-hcore.py @@ -12,35 +12,41 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() + def get_v_mod(mf, shift): """Get potential on oxygen atom.""" ovlp = mf.get_ovlp() # Construct Lowdin orbitals: e, v = np.linalg.eigh(ovlp) - c_sao = np.dot(v*(e**-0.5), v.T) + c_sao = np.dot(v * (e**-0.5), v.T) # Get projector onto oxygen-SAO space: - oxygen = [ao[1].startswith('O') for ao in mf.mol.ao_labels(None)] - sc = np.dot(ovlp, c_sao[:,oxygen]) - v_mod = shift*np.dot(sc, sc.T) + oxygen = [ao[1].startswith("O") for ao in mf.mol.ao_labels(None)] + sc = np.dot(ovlp, c_sao[:, oxygen]) + v_mod = shift * np.dot(sc, sc.T) return v_mod + # Hartree-Fock mf = pyscf.scf.RHF(mol) # Save original H_core function and overwrite mean-field hcore_orig = mf.get_hcore # Shift oxygen atom by -1 Hartree -> electrons move to oxygen v_mod = get_v_mod(mf, -1.0) -mf.get_hcore = (lambda mf, *args : hcore_orig(*args) + v_mod).__get__(mf) +mf.get_hcore = (lambda mf, *args: hcore_orig(*args) + v_mod).__get__(mf) mf.kernel() # get_hcore_for_energy must be overwritten with original H_core function, # if the energy should be calculated without the shift -emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-6), solver_options=dict(solve_lambda=True), - overwrite=dict(get_hcore_for_energy=(lambda emb, *args : hcore_orig()))) +emb = vayesta.ewf.EWF( + mf, + bath_options=dict(threshold=1e-6), + solver_options=dict(solve_lambda=True), + overwrite=dict(get_hcore_for_energy=(lambda emb, *args: hcore_orig())), +) emb.kernel() emb.fragments[0].pop_analysis() diff --git a/examples/ewf/molecules/72-orbital-plotting.py b/examples/ewf/molecules/72-orbital-plotting.py index d519c4489..9f94f9e16 100644 --- a/examples/ewf/molecules/72-orbital-plotting.py +++ b/examples/ewf/molecules/72-orbital-plotting.py @@ -6,26 +6,24 @@ import vayesta import vayesta.ewf + # For plotting: from vayesta.misc.cubefile import CubeFile cell = pyscf.pbc.gto.Cell() a = 3.57 -cell.atom = ['C 0.0 0.0 0.0', 'C %f %f %f' % (a/4, a/4, a/4)] -cell.a = np.asarray([ - [a/2, a/2, 0], - [0, a/2, a/2], - [a/2, 0, a/2]]) -cell.basis = 'sto-6g' -cell.output = 'pyscf.out' +cell.atom = ["C 0.0 0.0 0.0", "C %f %f %f" % (a / 4, a / 4, a / 4)] +cell.a = np.asarray([[a / 2, a / 2, 0], [0, a / 2, a / 2], [a / 2, 0, a / 2]]) +cell.basis = "sto-6g" +cell.output = "pyscf.out" cell.build() -kmesh = [2,2,2] +kmesh = [2, 2, 2] kpts = cell.make_kpts(kmesh) # Hartree-Fock with k-points kmf = pyscf.pbc.scf.KRHF(cell, kpts) -kmf = kmf.density_fit(auxbasis='sto-6g') +kmf = kmf.density_fit(auxbasis="sto-6g") kmf.kernel() # Embedded calculation will automatically fold the k-point sampled mean-field to the supercell @@ -33,18 +31,18 @@ plot = CubeFile(emb.mol, gridsize=(100, 100, 100)) with emb.iao_fragmentation() as f: # Use emb.mol instead of mol, since this is the supercell with 2*(2x2x2)=16 atoms: - for atom in range(3): # Only add the first 3 atomic fragments + for atom in range(3): # Only add the first 3 atomic fragments fx = f.add_atomic_fragment(atom) plot.add_orbital(fx.c_frag) # The filename can also be set in the initialization of the CubeFile -plot.write('iao/fragments.cube') +plot.write("iao/fragments.cube") # The same with IAO+PAO fragmentation and with CubeFile as context manager: emb = vayesta.ewf.EWF(kmf, bath_options=dict(threshold=1e-6)) with emb.iaopao_fragmentation() as f: # Note that in the context manager form, # the filename always needs to be set in the initialization of CubeFile! - with CubeFile(emb.mol, filename='iao+pao/fragments.cube', gridsize=(100, 100, 100)) as plot: + with CubeFile(emb.mol, filename="iao+pao/fragments.cube", gridsize=(100, 100, 100)) as plot: for atom in range(3): fx = f.add_atomic_fragment(atom) plot.add_orbital(fx.c_frag) diff --git a/examples/ewf/molecules/73-fragment-symmetry.py b/examples/ewf/molecules/73-fragment-symmetry.py index a1b38ee06..2f8282099 100644 --- a/examples/ewf/molecules/73-fragment-symmetry.py +++ b/examples/ewf/molecules/73-fragment-symmetry.py @@ -9,8 +9,8 @@ mol = pyscf.gto.Mole() mol.atom = molecules.ferrocene(numbering=True) -mol.basis = 'STO-6G' -mol.output = 'pyscf.txt' +mol.basis = "STO-6G" +mol.output = "pyscf.txt" mol.build() # Hartree-Fock @@ -18,25 +18,25 @@ mf.kernel() # Embedded CCSD with symmetry: -emb_sym = vayesta.ewf.EWF(mf, solver='MP2', bath_options=dict(threshold=1e-4)) +emb_sym = vayesta.ewf.EWF(mf, solver="MP2", bath_options=dict(threshold=1e-4)) # Do not call add_all_atomic_fragments, as it add the symmetry related atoms as fragments! with emb_sym.iao_fragmentation() as frag: - frag.add_atomic_fragment('Fe1') + frag.add_atomic_fragment("Fe1") # Add mirror (reflection) symmetry # Set axis of reflection [tuple(3) or 'x', 'y', 'z'] - with frag.mirror_symmetry(axis='z'): + with frag.mirror_symmetry(axis="z"): # Add rotational symmetry # Set order of rotation (2: 180 degrees, 3: 120 degrees, 4: 90: degrees,...), # axis along which to rotate and center of rotation (default units for axis and center are Angstroms): - with frag.rotational_symmetry(order=5, axis=[0,0,1]): - frag.add_atomic_fragment('C2') - frag.add_atomic_fragment('H7') + with frag.rotational_symmetry(order=5, axis=[0, 0, 1]): + frag.add_atomic_fragment("C2") + frag.add_atomic_fragment("H7") emb_sym.kernel() dm1_sym = emb_sym.make_rdm1() # Reference calculation without symmetry: -emb = vayesta.ewf.EWF(mf, solver='MP2', bath_options=dict(threshold=1e-4)) +emb = vayesta.ewf.EWF(mf, solver="MP2", bath_options=dict(threshold=1e-4)) emb.kernel() dm1 = emb.make_rdm1() diff --git a/examples/ewf/molecules/74-multiple-rotations.py b/examples/ewf/molecules/74-multiple-rotations.py index c0dc5557c..a9ecc1ef8 100644 --- a/examples/ewf/molecules/74-multiple-rotations.py +++ b/examples/ewf/molecules/74-multiple-rotations.py @@ -25,8 +25,8 @@ H15 1.4188 -1.4188 -1.4188 H16 -1.4188 -1.4188 -1.4188 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf.out' +mol.basis = "cc-pVDZ" +mol.output = "pyscf.out" mol.build() # Hartree-Fock @@ -34,7 +34,7 @@ mf.kernel() # Embedded CCSD with rotational symmetry: -emb_sym = vayesta.ewf.EWF(mf, solver='MP2', bath_options=dict(threshold=1e-4)) +emb_sym = vayesta.ewf.EWF(mf, solver="MP2", bath_options=dict(threshold=1e-4)) # Do not call add_all_atomic_fragments, as it add the symmetry related atoms as fragments! with emb_sym.iao_fragmentation() as frag: @@ -46,14 +46,14 @@ # Important when using multiple rotations: # Only use the minimal set of rotations which generate all atomic fragments, # i.e. do not add multiple 4th-order rotations here! - with frag.rotational_symmetry(order=4, axis=[0,0,1]): - with frag.rotational_symmetry(order=2, axis=[0,1,0]): - frag.add_atomic_fragment('C1') - frag.add_atomic_fragment('H9') + with frag.rotational_symmetry(order=4, axis=[0, 0, 1]): + with frag.rotational_symmetry(order=2, axis=[0, 1, 0]): + frag.add_atomic_fragment("C1") + frag.add_atomic_fragment("H9") emb_sym.kernel() # Reference calculation without rotational symmetry: -emb = vayesta.ewf.EWF(mf, solver='MP2', bath_options=dict(threshold=1e-4)) +emb = vayesta.ewf.EWF(mf, solver="MP2", bath_options=dict(threshold=1e-4)) emb.kernel() # Compare energies and density matrices: @@ -63,10 +63,10 @@ dm1_sym = emb_sym.make_rdm1() print("Difference in 1-RDM= %.3e" % np.linalg.norm(dm1 - dm1_sym)) -dndn_sym = emb_sym.get_corrfunc('dN,dN') -dndn = emb.get_corrfunc('dN,dN') +dndn_sym = emb_sym.get_corrfunc("dN,dN") +dndn = emb.get_corrfunc("dN,dN") print("Difference in = %.3e" % np.linalg.norm(dndn - dndn_sym)) -szsz_sym = emb_sym.get_corrfunc('Sz,Sz') -szsz = emb.get_corrfunc('Sz,Sz') +szsz_sym = emb_sym.get_corrfunc("Sz,Sz") +szsz = emb.get_corrfunc("Sz,Sz") print("Difference in = %.3e" % np.linalg.norm(szsz - szsz_sym)) diff --git a/examples/ewf/molecules/90-mpi.py b/examples/ewf/molecules/90-mpi.py index d570db5ad..1bb3e7bb5 100644 --- a/examples/ewf/molecules/90-mpi.py +++ b/examples/ewf/molecules/90-mpi.py @@ -13,8 +13,8 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' -mol.output = 'pyscf-mpi%d.out' % mpi.rank +mol.basis = "cc-pVDZ" +mol.output = "pyscf-mpi%d.out" % mpi.rank mol.build() # Hartree-Fock diff --git a/examples/ewf/other/35-self-consistency.py b/examples/ewf/other/35-self-consistency.py index ee46a395e..be1b3edba 100644 --- a/examples/ewf/other/35-self-consistency.py +++ b/examples/ewf/other/35-self-consistency.py @@ -9,10 +9,10 @@ import vayesta.ewf mol = pyscf.gto.Mole() -mol.atom = ['N 0.0 0.0 0.0', 'N 0.0, 0.0, 1.1'] -mol.basis = 'aug-cc-pvtz' +mol.atom = ["N 0.0 0.0 0.0", "N 0.0, 0.0, 1.1"] +mol.basis = "aug-cc-pvtz" mol.verbose = 10 -mol.output = 'pyscf_out.txt' +mol.output = "pyscf_out.txt" mol.build() # Hartree-Fock @@ -32,14 +32,14 @@ # (conservative external correction) # sc_mode = 2: Project only the first occupied index of other's fragment T2-amplitude onto it's fragment space # (more substantional external correction) -scecc = vayesta.ewf.EWF(mf, solver='TCCSD', bath_options=dict(threshold=1e-4), sc_mode=1) +scecc = vayesta.ewf.EWF(mf, solver="TCCSD", bath_options=dict(threshold=1e-4), sc_mode=1) with scecc.iao_fragmentation() as frag: f0 = frag.add_atomic_fragment(0) f1 = frag.add_atomic_fragment(1) # If each fragment should be tailored by all others (like here), you can also call ecc.tailor_all_fragments() scecc.kernel() -print("E%-14s %+16.8f Ha" % ('(HF)=', mf.e_tot)) -print("E%-14s %+16.8f Ha" % ('(CCSD)=', cc.e_tot)) -print("E%-14s %+16.8f Ha" % ('(EWF-CCSD)=', ecc.e_tot)) -print("E%-14s %+16.8f Ha" % ('(SC-EWF-CCSD)=', scecc.e_tot)) +print("E%-14s %+16.8f Ha" % ("(HF)=", mf.e_tot)) +print("E%-14s %+16.8f Ha" % ("(CCSD)=", cc.e_tot)) +print("E%-14s %+16.8f Ha" % ("(EWF-CCSD)=", ecc.e_tot)) +print("E%-14s %+16.8f Ha" % ("(SC-EWF-CCSD)=", scecc.e_tot)) diff --git a/examples/ewf/other/51-hubbard-1D.py b/examples/ewf/other/51-hubbard-1D.py index e5c4324ee..1e39a15ac 100644 --- a/examples/ewf/other/51-hubbard-1D.py +++ b/examples/ewf/other/51-hubbard-1D.py @@ -21,9 +21,9 @@ # Double site embedding: emb2 = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-4)) with emb2.site_fragmentation() as frag: - frag.add_atomic_fragment([0,1], sym_factor=nsite/2) + frag.add_atomic_fragment([0, 1], sym_factor=nsite / 2) emb2.kernel() -print("E(MF)= %+16.8f Ha" % (mf.e_tot/nelectron)) -print("E(Emb. CCSD, 1-site)= %+16.8f Ha" % (emb.e_tot/nelectron)) -print("E(Emb. CCSD, 2-site)= %+16.8f Ha" % (emb2.e_tot/nelectron)) +print("E(MF)= %+16.8f Ha" % (mf.e_tot / nelectron)) +print("E(Emb. CCSD, 1-site)= %+16.8f Ha" % (emb.e_tot / nelectron)) +print("E(Emb. CCSD, 2-site)= %+16.8f Ha" % (emb2.e_tot / nelectron)) diff --git a/examples/ewf/other/52-hubbard-2D.py b/examples/ewf/other/52-hubbard-2D.py index 2a7f7d30a..9e1e4c9c1 100644 --- a/examples/ewf/other/52-hubbard-2D.py +++ b/examples/ewf/other/52-hubbard-2D.py @@ -3,24 +3,24 @@ import vayesta.lattmod -nsites = (4,4) +nsites = (4, 4) fragment = (2, 2) hubbard_u = 6.0 -boundary = ('PBC', 'APBC') +boundary = ("PBC", "APBC") -nsite = nsites[0]*nsites[1] -nfrag = fragment[0]*fragment[1] +nsite = nsites[0] * nsites[1] +nfrag = fragment[0] * fragment[1] nelectron = nsite mol = vayesta.lattmod.Hubbard2D(nsites, nelectron=nelectron, hubbard_u=hubbard_u, boundary=boundary, tiles=fragment) mf = vayesta.lattmod.LatticeMF(mol) mf.kernel() -emb = vayesta.ewf.EWF(mf, solver='FCI', bath_options=dict(bathtype='dmet')) +emb = vayesta.ewf.EWF(mf, solver="FCI", bath_options=dict(bathtype="dmet")) with emb.site_fragmentation() as frag: for i in range(0, nsite, nfrag): frag.add_atomic_fragment(list(range(i, i + nfrag)), nelectron_target=nfrag) emb.kernel() -print("E(HF)= %+16.8f Ha" % (mf.e_tot/nelectron)) -print("E(Emb. FCI)= %+16.8f Ha" % (emb.e_tot/nelectron)) -print("E(DMET)= %+16.8f Ha" % (emb.get_dmet_energy()/nelectron)) +print("E(HF)= %+16.8f Ha" % (mf.e_tot / nelectron)) +print("E(Emb. FCI)= %+16.8f Ha" % (emb.e_tot / nelectron)) +print("E(DMET)= %+16.8f Ha" % (emb.get_dmet_energy() / nelectron)) diff --git a/examples/ewf/other/61-ext-corr-hubbard-1D.py b/examples/ewf/other/61-ext-corr-hubbard-1D.py index fbcac884b..326f0f83e 100644 --- a/examples/ewf/other/61-ext-corr-hubbard-1D.py +++ b/examples/ewf/other/61-ext-corr-hubbard-1D.py @@ -11,7 +11,7 @@ mol = vayesta.lattmod.Hubbard1D(nsite, nelectron=nelectron, hubbard_u=hubbard_u) mf = vayesta.lattmod.LatticeMF(mol) mf.kernel() -assert(mf.converged) +assert mf.converged # Reference full system CCSD and FCI cc = pyscf.cc.CCSD(mf) @@ -23,9 +23,9 @@ fci.kernel() # Perform embedded FCI with two sites -emb_simp = vayesta.ewf.EWF(mf, solver='FCI', bath_options=dict(bathtype='dmet')) +emb_simp = vayesta.ewf.EWF(mf, solver="FCI", bath_options=dict(bathtype="dmet")) with emb_simp.site_fragmentation() as f: - f.add_atomic_fragment([0, 1], sym_factor=nsite/2, nelectron_target=2*nelectron/nsite) + f.add_atomic_fragment([0, 1], sym_factor=nsite / 2, nelectron_target=2 * nelectron / nsite) emb_simp.kernel() # Perform full system CCSD, externally corrected by two-site+DMET bath FCI level clusters @@ -34,78 +34,97 @@ with emb.site_fragmentation() as f: # Set up a two-site FCI fragmentation of full system as auxiliary clusters # Ensure the right number of electrons on each fragment space of the FCI calculation. - fci_frags.append(f.add_atomic_fragment([0, 1], solver='FCI', bath_options=dict(bathtype='dmet'), store_wf_type='CCSDTQ', nelectron_target=2*nelectron/nsite, auxiliary=True)) + fci_frags.append( + f.add_atomic_fragment( + [0, 1], + solver="FCI", + bath_options=dict(bathtype="dmet"), + store_wf_type="CCSDTQ", + nelectron_target=2 * nelectron / nsite, + auxiliary=True, + ) + ) # Add single 'complete' CCSD fragment covering all sites - ccsd_frag = f.add_full_system(solver='CCSD', bath_options=dict(bathtype='full'), solver_options=dict(solve_lambda=False, init_guess='CISD')) + ccsd_frag = f.add_full_system( + solver="CCSD", bath_options=dict(bathtype="full"), solver_options=dict(solve_lambda=False, init_guess="CISD") + ) # Add symmetry-derived FCI fragments to avoid multiple calculations fci_frags.extend(fci_frags[0].add_tsymmetric_fragments(tvecs=[5, 1, 1])) e_extcorr = [] extcorr_conv = [] -#Main options: 'projectors', which should be an integer between 0 and 2 (inclusive). -#The larger the number, the more fragment projectors are applied to the correcting T2 contributions, and less +# Main options: 'projectors', which should be an integer between 0 and 2 (inclusive). +# The larger the number, the more fragment projectors are applied to the correcting T2 contributions, and less #'bath' correlation from the FCI clusters is used as a constraint in the external correction of the CCSD clusters. -#NOTE that with multiple FCI fragments providing constraints and overlapping bath spaces, proj=0 will -#overcount the correction, so do not use with multiple FCI clusters. It will not e.g. tend to the right answer as -#the FCI bath space becomes complete (for which you must have proj=1). Only use with a single FCI fragment. -ccsd_frag.add_external_corrections(fci_frags, correction_type='external', projectors=1) +# NOTE that with multiple FCI fragments providing constraints and overlapping bath spaces, proj=0 will +# overcount the correction, so do not use with multiple FCI clusters. It will not e.g. tend to the right answer as +# the FCI bath space becomes complete (for which you must have proj=1). Only use with a single FCI fragment. +ccsd_frag.add_external_corrections(fci_frags, correction_type="external", projectors=1) emb.kernel() -e_extcorr.append(emb.e_tot); extcorr_conv.append(emb.converged) +e_extcorr.append(emb.e_tot) +extcorr_conv.append(emb.converged) # For subsequent calculations where we have just changed the mode/projectors in the external tailoring, we want to avoid having # to resolve the FCI fragments. Set them to inactive, so just the CCSD fragments will be resolved. for fci_frag in fci_frags: fci_frag.active = False -ccsd_frag.clear_external_corrections() # Clear any previous corrections applied -ccsd_frag.add_external_corrections(fci_frags, correction_type='external', projectors=2) +ccsd_frag.clear_external_corrections() # Clear any previous corrections applied +ccsd_frag.add_external_corrections(fci_frags, correction_type="external", projectors=2) emb.kernel() -e_extcorr.append(emb.e_tot); extcorr_conv.append(emb.converged) +e_extcorr.append(emb.e_tot) +extcorr_conv.append(emb.converged) -ccsd_frag.clear_external_corrections() # Clear any previous corrections applied -ccsd_frag.add_external_corrections(fci_frags, correction_type='external', projectors=1, low_level_coul=False) +ccsd_frag.clear_external_corrections() # Clear any previous corrections applied +ccsd_frag.add_external_corrections(fci_frags, correction_type="external", projectors=1, low_level_coul=False) emb.kernel() -e_extcorr.append(emb.e_tot); extcorr_conv.append(emb.converged) +e_extcorr.append(emb.e_tot) +extcorr_conv.append(emb.converged) -ccsd_frag.clear_external_corrections() # Clear any previous corrections applied -ccsd_frag.add_external_corrections(fci_frags, correction_type='external', projectors=2, low_level_coul=False) +ccsd_frag.clear_external_corrections() # Clear any previous corrections applied +ccsd_frag.add_external_corrections(fci_frags, correction_type="external", projectors=2, low_level_coul=False) emb.kernel() -e_extcorr.append(emb.e_tot); extcorr_conv.append(emb.converged) +e_extcorr.append(emb.e_tot) +extcorr_conv.append(emb.converged) # Compare to a simpler tailoring e_tailor = [] tailor_conv = [] ccsd_frag.clear_external_corrections() -ccsd_frag.add_external_corrections(fci_frags, correction_type='tailor', projectors=1) +ccsd_frag.add_external_corrections(fci_frags, correction_type="tailor", projectors=1) emb.kernel() -e_tailor.append(emb.e_tot); tailor_conv.append(emb.converged) +e_tailor.append(emb.e_tot) +tailor_conv.append(emb.converged) ccsd_frag.clear_external_corrections() -ccsd_frag.add_external_corrections(fci_frags, correction_type='tailor', projectors=2) +ccsd_frag.add_external_corrections(fci_frags, correction_type="tailor", projectors=2) emb.kernel() -e_tailor.append(emb.e_tot); tailor_conv.append(emb.converged) +e_tailor.append(emb.e_tot) +tailor_conv.append(emb.converged) # Compare to a delta-tailoring, where the correction is the difference between full-system # CCSD and CCSD in the FCI cluster. e_dtailor = [] dtailor_conv = [] ccsd_frag.clear_external_corrections() -ccsd_frag.add_external_corrections(fci_frags, correction_type='delta-tailor', projectors=1) +ccsd_frag.add_external_corrections(fci_frags, correction_type="delta-tailor", projectors=1) emb.kernel() -e_dtailor.append(emb.e_tot); dtailor_conv.append(emb.converged) +e_dtailor.append(emb.e_tot) +dtailor_conv.append(emb.converged) ccsd_frag.clear_external_corrections() -ccsd_frag.add_external_corrections(fci_frags, correction_type='delta-tailor', projectors=2) +ccsd_frag.add_external_corrections(fci_frags, correction_type="delta-tailor", projectors=2) emb.kernel() -e_dtailor.append(emb.e_tot); dtailor_conv.append(emb.converged) +e_dtailor.append(emb.e_tot) +dtailor_conv.append(emb.converged) -print("E(MF)= %+16.8f Ha, conv = %s" % (mf.e_tot/nsite, mf.converged)) -print("E(CCSD)= %+16.8f Ha, conv = %s" % (cc.e_tot/nsite, cc.converged)) -print("E(FCI)= %+16.8f Ha, conv = %s" % (fci.e_tot/nsite, fci.converged)) -print("E(Emb. FCI, 2-site)= %+16.8f Ha, conv = %s" % (emb_simp.e_tot/nsite, emb_simp.converged)) -print("E(EC-CCSD, 2-site FCI, 1 proj, ccsd V)= %+16.8f Ha, conv = %s" % ((e_extcorr[0]/nsite), extcorr_conv[0])) -print("E(EC-CCSD, 2-site FCI, 2 proj, ccsd V)= %+16.8f Ha, conv = %s" % ((e_extcorr[1]/nsite), extcorr_conv[1])) -print("E(EC-CCSD, 2-site FCI, 1 proj, fci V)= %+16.8f Ha, conv = %s" % ((e_extcorr[2]/nsite), extcorr_conv[2])) -print("E(EC-CCSD, 2-site FCI, 2 proj, fci V)= %+16.8f Ha, conv = %s" % ((e_extcorr[3]/nsite), extcorr_conv[3])) -print("E(T-CCSD, 2-site FCI, 1 proj)= %+16.8f Ha, conv = %s" % ((e_tailor[0]/nsite), tailor_conv[0])) -print("E(T-CCSD, 2-site FCI, 2 proj)= %+16.8f Ha, conv = %s" % ((e_tailor[1]/nsite), tailor_conv[1])) -print("E(DT-CCSD, 2-site FCI, 1 proj)= %+16.8f Ha, conv = %s" % ((e_dtailor[0]/nsite), dtailor_conv[0])) -print("E(DT-CCSD, 2-site FCI, 2 proj)= %+16.8f Ha, conv = %s" % ((e_dtailor[1]/nsite), dtailor_conv[1])) +print("E(MF)= %+16.8f Ha, conv = %s" % (mf.e_tot / nsite, mf.converged)) +print("E(CCSD)= %+16.8f Ha, conv = %s" % (cc.e_tot / nsite, cc.converged)) +print("E(FCI)= %+16.8f Ha, conv = %s" % (fci.e_tot / nsite, fci.converged)) +print("E(Emb. FCI, 2-site)= %+16.8f Ha, conv = %s" % (emb_simp.e_tot / nsite, emb_simp.converged)) +print("E(EC-CCSD, 2-site FCI, 1 proj, ccsd V)= %+16.8f Ha, conv = %s" % ((e_extcorr[0] / nsite), extcorr_conv[0])) +print("E(EC-CCSD, 2-site FCI, 2 proj, ccsd V)= %+16.8f Ha, conv = %s" % ((e_extcorr[1] / nsite), extcorr_conv[1])) +print("E(EC-CCSD, 2-site FCI, 1 proj, fci V)= %+16.8f Ha, conv = %s" % ((e_extcorr[2] / nsite), extcorr_conv[2])) +print("E(EC-CCSD, 2-site FCI, 2 proj, fci V)= %+16.8f Ha, conv = %s" % ((e_extcorr[3] / nsite), extcorr_conv[3])) +print("E(T-CCSD, 2-site FCI, 1 proj)= %+16.8f Ha, conv = %s" % ((e_tailor[0] / nsite), tailor_conv[0])) +print("E(T-CCSD, 2-site FCI, 2 proj)= %+16.8f Ha, conv = %s" % ((e_tailor[1] / nsite), tailor_conv[1])) +print("E(DT-CCSD, 2-site FCI, 1 proj)= %+16.8f Ha, conv = %s" % ((e_dtailor[0] / nsite), dtailor_conv[0])) +print("E(DT-CCSD, 2-site FCI, 2 proj)= %+16.8f Ha, conv = %s" % ((e_dtailor[1] / nsite), dtailor_conv[1])) diff --git a/examples/ewf/other/71-sc-h-ring.py b/examples/ewf/other/71-sc-h-ring.py index c7f138804..c349b199d 100644 --- a/examples/ewf/other/71-sc-h-ring.py +++ b/examples/ewf/other/71-sc-h-ring.py @@ -15,15 +15,14 @@ natom = 6 for d in np.arange(0.5, 3.0001, 0.25): - ring = pyscf.tools.ring.make(natom, d) - atom = [('H %f %f %f' % xyz) for xyz in ring] + atom = [("H %f %f %f" % xyz) for xyz in ring] mol = pyscf.gto.Mole() mol.atom = atom - mol.basis = 'aug-cc-pvdz' + mol.basis = "aug-cc-pvdz" mol.verbose = 10 - mol.output = 'pyscf_out.txt' + mol.output = "pyscf_out.txt" mol.build() # Hartree-Fock @@ -37,21 +36,21 @@ # One-shot EWF-CCSD ecc = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-4)) with ecc.iao_fragmentation() as f: - with f.rotational_symmetry(6, axis='z'): + with f.rotational_symmetry(6, axis="z"): f.add_atomic_fragment([0]) ecc.kernel() # Self-consistent EWF-CCSD - scecc = vayesta.ewf.EWF(mf, solver='TCCSD', bath_options=dict(threshold=1e-6), sc_mode=1) + scecc = vayesta.ewf.EWF(mf, solver="TCCSD", bath_options=dict(threshold=1e-6), sc_mode=1) with scecc.iao_fragmentation() as f: - with f.rotational_symmetry(6, axis='z'): + with f.rotational_symmetry(6, axis="z"): f.add_atomic_fragment([0]) scecc.kernel() - print("E%-14s %+16.8f Ha" % ('(HF)=', mf.e_tot)) - print("E%-14s %+16.8f Ha" % ('(CCSD)=', cc.e_tot)) - print("E%-14s %+16.8f Ha" % ('(EWF-CCSD)=', ecc.e_tot)) - print("E%-14s %+16.8f Ha" % ('(SC-EWF-CCSD)=', scecc.e_tot)) + print("E%-14s %+16.8f Ha" % ("(HF)=", mf.e_tot)) + print("E%-14s %+16.8f Ha" % ("(CCSD)=", cc.e_tot)) + print("E%-14s %+16.8f Ha" % ("(EWF-CCSD)=", ecc.e_tot)) + print("E%-14s %+16.8f Ha" % ("(SC-EWF-CCSD)=", scecc.e_tot)) with open("energies.txt", "a") as f: f.write("%.2f % 16.8f % 16.8f % 16.8f %16.8f\n" % (d, mf.e_tot, cc.e_tot, ecc.e_tot, scecc.e_tot)) diff --git a/examples/ewf/solids/01-simple-sym.py b/examples/ewf/solids/01-simple-sym.py index 51a137a3a..14f4f1c47 100644 --- a/examples/ewf/solids/01-simple-sym.py +++ b/examples/ewf/solids/01-simple-sym.py @@ -7,19 +7,19 @@ cell = pyscf.pbc.gto.Cell() -cell.atom = ['He 0.0 0.0 0.0'] +cell.atom = ["He 0.0 0.0 0.0"] cell.a = 1.4 * np.eye(3) -cell.basis = 'def2-svp' -cell.output = 'pyscf.out' +cell.basis = "def2-svp" +cell.output = "pyscf.out" cell.space_group_symmetry = True cell.symmorphic = True cell.build() # Enforce all symmetries in kpoint generation. -kpts = cell.make_kpts([2,2,2], space_group_symmetry=True, time_reversal_symmetry=True, symmorphic=True) +kpts = cell.make_kpts([2, 2, 2], space_group_symmetry=True, time_reversal_symmetry=True, symmorphic=True) # Hartree-Fock with k-points mf = pyscf.pbc.scf.KRHF(cell, kpts) -mf = mf.density_fit(auxbasis='def2-svp-ri') +mf = mf.density_fit(auxbasis="def2-svp-ri") mf.kernel() # Embedded calculation will automatically unfold the k-point sampled mean-field diff --git a/examples/ewf/solids/01-simple.py b/examples/ewf/solids/01-simple.py index b63d6c1e3..f8cbe47a9 100644 --- a/examples/ewf/solids/01-simple.py +++ b/examples/ewf/solids/01-simple.py @@ -8,17 +8,17 @@ # For an equivalent calculation enforcing symmetries, see examples/ewf/solids/01-simple-sym.py cell = pyscf.pbc.gto.Cell() -cell.atom = ['He 0.0 0.0 0.0'] +cell.atom = ["He 0.0 0.0 0.0"] cell.a = 1.4 * np.eye(3) -cell.basis = 'def2-svp' -cell.output = 'pyscf.out' +cell.basis = "def2-svp" +cell.output = "pyscf.out" cell.build() -kpts = cell.make_kpts([2,2,2]) +kpts = cell.make_kpts([2, 2, 2]) # Hartree-Fock with k-points mf = pyscf.pbc.scf.KRHF(cell, kpts) -mf = mf.density_fit(auxbasis='def2-svp-ri') +mf = mf.density_fit(auxbasis="def2-svp-ri") mf.kernel() # Embedded calculation will automatically unfold the k-point sampled mean-field diff --git a/examples/ewf/solids/02-exact-limit-uhf.py b/examples/ewf/solids/02-exact-limit-uhf.py index dc7a36bb9..d93e904fb 100644 --- a/examples/ewf/solids/02-exact-limit-uhf.py +++ b/examples/ewf/solids/02-exact-limit-uhf.py @@ -13,12 +13,12 @@ cell = pyscf.pbc.gto.Cell() cell.atom = "He 0 0 0" cell.a = 1.4 * np.eye(3) -cell.basis = '6-31g' +cell.basis = "6-31g" cell.verbose = 10 -cell.output = 'pyscf.out' +cell.output = "pyscf.out" cell.build() -cell = pyscf.pbc.tools.super_cell(cell, [2,2,2]) +cell = pyscf.pbc.tools.super_cell(cell, [2, 2, 2]) # Hartree-Fock mf = pyscf.pbc.scf.UHF(cell).density_fit() @@ -29,19 +29,19 @@ cc.kernel() # Test exact limit using bath_type='full' -ecc = vayesta.ewf.EWF(mf, bath_options=dict(bathtype='full')) +ecc = vayesta.ewf.EWF(mf, bath_options=dict(bathtype="full")) with ecc.iao_fragmentation() as f: f.add_all_atomic_fragments() ecc.kernel() -nocca = np.count_nonzero(mf.mo_occ[0]>0) -noccb = np.count_nonzero(mf.mo_occ[1]>0) +nocca = np.count_nonzero(mf.mo_occ[0] > 0) +noccb = np.count_nonzero(mf.mo_occ[1] > 0) nocc = nocca + noccb -e_exxdiv = -nocc*pyscf.pbc.tools.madelung(cell, kpts=np.zeros((3,))) / 2 +e_exxdiv = -nocc * pyscf.pbc.tools.madelung(cell, kpts=np.zeros((3,))) / 2 print("E(exx-div)= %+16.8f Ha" % e_exxdiv) print("E(HF)= %+16.8f Ha" % mf.e_tot) -print("E(CCSD)= %+16.8f Ha" % cc.e_tot ) +print("E(CCSD)= %+16.8f Ha" % cc.e_tot) print("E(EWF-CCSD)= %+16.8f Ha" % ecc.e_tot) assert np.allclose(cc.e_tot, ecc.e_tot) diff --git a/examples/ewf/solids/02-exact-limit.py b/examples/ewf/solids/02-exact-limit.py index 83929d70e..55f215509 100644 --- a/examples/ewf/solids/02-exact-limit.py +++ b/examples/ewf/solids/02-exact-limit.py @@ -13,9 +13,9 @@ mol = pyscf.pbc.gto.Cell() mol.atom = "He 0 0 0" mol.a = 1.4 * np.eye(3) -mol.basis = '6-31g' +mol.basis = "6-31g" mol.verbose = 10 -mol.output = 'pyscf_out.txt' +mol.output = "pyscf_out.txt" mol.build() # Hartree-Fock @@ -32,12 +32,12 @@ f.add_all_atomic_fragments() ecc.kernel() -nocc = np.count_nonzero(mf.mo_occ>0) -e_exxdiv = -nocc*pyscf.pbc.tools.madelung(mol, kpts=np.zeros((3,))) +nocc = np.count_nonzero(mf.mo_occ > 0) +e_exxdiv = -nocc * pyscf.pbc.tools.madelung(mol, kpts=np.zeros((3,))) print("E(exx-div)= %+16.8f Ha" % e_exxdiv) print("E(HF)= %+16.8f Ha" % mf.e_tot) -print("E(CCSD)= %+16.8f Ha" % cc.e_tot ) +print("E(CCSD)= %+16.8f Ha" % cc.e_tot) print("E(EWF-CCSD)= %+16.8f Ha" % ecc.e_tot) assert np.allclose(cc.e_tot, ecc.e_tot) diff --git a/examples/ewf/solids/03-cubic-BN.py b/examples/ewf/solids/03-cubic-BN.py index f1b1bb2ea..e13524146 100644 --- a/examples/ewf/solids/03-cubic-BN.py +++ b/examples/ewf/solids/03-cubic-BN.py @@ -9,20 +9,17 @@ cell = pyscf.pbc.gto.Cell() a = 3.615 -cell.atom = 'B 0 0 0 ; N %f %f %f' % (a/4, a/4, a/4) -cell.a = np.asarray([ - [a/2, a/2, 0], - [0, a/2, a/2], - [a/2, 0, a/2]]) -cell.basis = 'sto-6g' -cell.output = 'pyscf.out' +cell.atom = "B 0 0 0 ; N %f %f %f" % (a / 4, a / 4, a / 4) +cell.a = np.asarray([[a / 2, a / 2, 0], [0, a / 2, a / 2], [a / 2, 0, a / 2]]) +cell.basis = "sto-6g" +cell.output = "pyscf.out" cell.build() # Hartree-Fock with k-points -kmesh = [2,2,2] +kmesh = [2, 2, 2] kpts = cell.make_kpts(kmesh) mf = pyscf.pbc.scf.KRHF(cell, kpts) -mf = mf.density_fit(auxbasis='sto-6g') +mf = mf.density_fit(auxbasis="sto-6g") mf.kernel() # Full system CCSD @@ -43,4 +40,4 @@ # Population analysis (q: charge, s: spin) # Possible options for local_orbitals: 'mulliken', 'lowdin', 'iao+pao', or custom N(AO) x N(AO) coefficient matrix # orbital_resolved=True is used to print orbital resolved (rather than only atom resolved) analysis -emb.pop_analysis(dm1, local_orbitals='iao+pao', orbital_resolved=True) +emb.pop_analysis(dm1, local_orbitals="iao+pao", orbital_resolved=True) diff --git a/examples/ewf/solids/04-kpts-vs-supercell.py b/examples/ewf/solids/04-kpts-vs-supercell.py index a84000f23..89d5e6058 100644 --- a/examples/ewf/solids/04-kpts-vs-supercell.py +++ b/examples/ewf/solids/04-kpts-vs-supercell.py @@ -11,21 +11,18 @@ cell = pyscf.pbc.gto.Cell() a = 3.57 -cell.atom = ['C 0.0 0.0 0.0', 'C %f %f %f' % (a/4, a/4, a/4)] -cell.a = np.asarray([ - [a/2, a/2, 0], - [0, a/2, a/2], - [a/2, 0, a/2]]) -cell.basis = 'def2-svp' -cell.output = 'pyscf.out' +cell.atom = ["C 0.0 0.0 0.0", "C %f %f %f" % (a / 4, a / 4, a / 4)] +cell.a = np.asarray([[a / 2, a / 2, 0], [0, a / 2, a / 2], [a / 2, 0, a / 2]]) +cell.basis = "def2-svp" +cell.output = "pyscf.out" cell.build() -kmesh = [1,1,2] +kmesh = [1, 1, 2] kpts = cell.make_kpts(kmesh) # Hartree-Fock with k-points kmf = pyscf.pbc.scf.KRHF(cell, kpts) -kmf = kmf.density_fit(auxbasis='def2-svp-ri') +kmf = kmf.density_fit(auxbasis="def2-svp-ri") kmf.kernel() # Full system CCSD @@ -35,23 +32,23 @@ # Embedded calculation will automatically fold the k-point sampled mean-field to the supercell emb = vayesta.ewf.EWF(kmf, bath_options=dict(threshold=1e-6)) with emb.iao_fragmentation() as f: - f.add_atomic_fragment(0, sym_factor=2) # 2 C-atoms per unit cell + f.add_atomic_fragment(0, sym_factor=2) # 2 C-atoms per unit cell emb.kernel() # Hartree-Fock in supercell scell = pyscf.pbc.tools.super_cell(cell, kmesh) mf_sc = pyscf.pbc.scf.RHF(scell) -mf_sc = mf_sc.density_fit(auxbasis='def2-svp-ri') +mf_sc = mf_sc.density_fit(auxbasis="def2-svp-ri") mf_sc.kernel() emb_sc = vayesta.ewf.EWF(mf_sc, bath_options=dict(threshold=1e-6)) with emb_sc.iao_fragmentation() as f: ncells = np.product(kmesh) - f.add_atomic_fragment(0, sym_factor=2*ncells) + f.add_atomic_fragment(0, sym_factor=2 * ncells) emb_sc.kernel() print("E(k-HF)= %+16.8f Ha" % kmf.e_tot) -print("E(sc-HF)= %+16.8f Ha" % (mf_sc.e_tot/ncells)) +print("E(sc-HF)= %+16.8f Ha" % (mf_sc.e_tot / ncells)) print("E(Emb. CCSD @k-HF)= %+16.8f Ha" % emb.e_tot) -print("E(Emb. CCSD @sc-HF)= %+16.8f Ha" % (emb_sc.e_tot/ncells)) +print("E(Emb. CCSD @sc-HF)= %+16.8f Ha" % (emb_sc.e_tot / ncells)) print("E(k-CCSD)= %+16.8f Ha" % kcc.e_tot) diff --git a/examples/ewf/solids/14-density-matrix.py b/examples/ewf/solids/14-density-matrix.py index ff9f6fa30..fb905e2aa 100644 --- a/examples/ewf/solids/14-density-matrix.py +++ b/examples/ewf/solids/14-density-matrix.py @@ -10,19 +10,19 @@ cell = pyscf.pbc.gto.Cell() a = 3.57 -cell.atom = 'He 0.0 0.0 0.0' -cell.a = 3.0*np.eye(3) -cell.basis = 'def2-svp' +cell.atom = "He 0.0 0.0 0.0" +cell.a = 3.0 * np.eye(3) +cell.basis = "def2-svp" cell.verbose = 10 -cell.output = 'pyscf.out' +cell.output = "pyscf.out" cell.build() -kmesh = [2,2,2] +kmesh = [2, 2, 2] kpts = cell.make_kpts(kmesh) # Hartree-Fock with k-points kmf = pyscf.pbc.scf.KRHF(cell, kpts) -kmf = kmf.density_fit(auxbasis='def2-svp-ri') +kmf = kmf.density_fit(auxbasis="def2-svp-ri") kmf.kernel() # Embedded calculation will automatically fold the k-point sampled mean-field to the supercell @@ -33,7 +33,7 @@ dm1_emb = emb.make_rdm1(ao_basis=True) # Full system reference CCSD -mf = emb.mf.density_fit(auxbasis='def2-svp-ri') +mf = emb.mf.density_fit(auxbasis="def2-svp-ri") cc = pyscf.pbc.cc.CCSD(mf) cc.kernel() dm1_cc = cc.make_rdm1(ao_repr=True) diff --git a/examples/ewf/solids/20-mRPA-interactions.py b/examples/ewf/solids/20-mRPA-interactions.py index f4cc9270f..06ce5eb1e 100644 --- a/examples/ewf/solids/20-mRPA-interactions.py +++ b/examples/ewf/solids/20-mRPA-interactions.py @@ -10,17 +10,17 @@ cell = pyscf.pbc.gto.Cell() cell.a, cell.atom = solids.graphene() -cell.basis = 'sto-3g' -cell.output = 'pyscf.out' +cell.basis = "sto-3g" +cell.output = "pyscf.out" cell.dimension = 2 cell.space_group_symmetry = True cell.symmorphic = True cell.build() # HF -kmesh = [2,2,1] +kmesh = [2, 2, 1] kpts = cell.make_kpts(kmesh, space_group_symmetry=True, time_reversal_symmetry=True, symmorphic=True) -hf = pyscf.pbc.scf.KRHF(cell, cell.make_kpts([2,2,1])) +hf = pyscf.pbc.scf.KRHF(cell, cell.make_kpts([2, 2, 1])) hf = hf.density_fit() hf.kernel() # This may be required to avoid issues discussed in 01-simple-sym.py. @@ -29,8 +29,9 @@ emb_bare = vayesta.ewf.EWF(hf, bath_options=dict(bathtype="rpa", threshold=1e-2), screening=None) emb_bare.kernel() # Run calculation using screened interactions and cumulant correction for nonlocal energy. -emb_mrpa = vayesta.ewf.EWF(hf, bath_options=dict(bathtype="rpa", threshold=1e-2), screening="mrpa", - ext_rpa_correction="cumulant") +emb_mrpa = vayesta.ewf.EWF( + hf, bath_options=dict(bathtype="rpa", threshold=1e-2), screening="mrpa", ext_rpa_correction="cumulant" +) emb_mrpa.kernel() # Reference full system CCSD: @@ -40,5 +41,5 @@ print("Error(HF)= %+16.8f Ha" % (hf.e_tot - cc.e_tot)) print("Error(Emb. bare CCSD)= %+16.8f Ha" % (emb_bare.e_tot - cc.e_tot)) print("Error(Emb. mRPA CCSD)= %+16.8f Ha" % (emb_mrpa.e_tot - cc.e_tot)) -print("Error(Emb. mRPA CCSD + ΔE_k)= %+16.8f Ha" % (emb_mrpa.e_tot+emb_mrpa.e_nonlocal-cc.e_tot)) +print("Error(Emb. mRPA CCSD + ΔE_k)= %+16.8f Ha" % (emb_mrpa.e_tot + emb_mrpa.e_nonlocal - cc.e_tot)) print("E(CCSD)= %+16.8f Ha" % cc.e_tot) diff --git a/examples/ewf/solids/55-dm-energy.py b/examples/ewf/solids/55-dm-energy.py index 56f48e595..d09cb65be 100644 --- a/examples/ewf/solids/55-dm-energy.py +++ b/examples/ewf/solids/55-dm-energy.py @@ -11,21 +11,21 @@ cell = pyscf.pbc.gto.Cell() cell.a = 3.0 * np.eye(3) -cell.atom = 'He 0 0 0' -cell.basis = 'cc-pvdz' -cell.exp_to_discard=0.1 +cell.atom = "He 0 0 0" +cell.basis = "cc-pvdz" +cell.exp_to_discard = 0.1 cell.build() -kmesh = [2,2,1] +kmesh = [2, 2, 1] kpts = cell.make_kpts(kmesh) # --- Hartree-Fock kmf = pyscf.pbc.scf.KRHF(cell, kpts) -kmf = kmf.rs_density_fit(auxbasis='cc-pvdz-ri') +kmf = kmf.rs_density_fit(auxbasis="cc-pvdz-ri") kmf.kernel() # --- Embedding -emb = vayesta.ewf.EWF(kmf, bath_options=dict(bathtype='full'), solver_options=dict(solve_lambda=True)) +emb = vayesta.ewf.EWF(kmf, bath_options=dict(bathtype="full"), solver_options=dict(solve_lambda=True)) emb.kernel() e_dm = emb.get_dm_energy() @@ -34,18 +34,15 @@ cc.kernel() - - print("Total Energy") print("E(HF)= %+16.8f Ha" % kmf.e_tot) -print("E(EWF-DPart)= %+16.8f Ha"% emb.get_dmet_energy()) +print("E(EWF-DPart)= %+16.8f Ha" % emb.get_dmet_energy()) print("E(EWF-Proj)= %+16.8f Ha" % emb.e_tot) -print("E(EWF-DM)= %+16.8f Ha"% emb.get_dm_energy()) +print("E(EWF-DM)= %+16.8f Ha" % emb.get_dm_energy()) print("E(CCSD)= %+16.8f Ha" % cc.e_tot) print("\nCorrelation Energy") -print("E(EWF-DPart)= %+16.8f Ha"% (emb.get_dmet_energy()-kmf.e_tot)) +print("E(EWF-DPart)= %+16.8f Ha" % (emb.get_dmet_energy() - kmf.e_tot)) print("E(EWF-Proj)= %+16.8f Ha" % emb.e_corr) -print("E(EWF-DM)= %+16.8f Ha"% emb.get_dm_corr_energy()) +print("E(EWF-DM)= %+16.8f Ha" % emb.get_dm_corr_energy()) print("E(CCSD)= %+16.8f Ha" % cc.e_corr) - diff --git a/examples/ewf/solids/56-dm-energy-uccsd.py b/examples/ewf/solids/56-dm-energy-uccsd.py index 184538035..277a0e804 100644 --- a/examples/ewf/solids/56-dm-energy-uccsd.py +++ b/examples/ewf/solids/56-dm-energy-uccsd.py @@ -11,21 +11,21 @@ cell = pyscf.pbc.gto.Cell() cell.a = 3.0 * np.eye(3) -cell.atom = 'He 0 0 0' -cell.basis = 'cc-pvdz' -cell.exp_to_discard=0.1 +cell.atom = "He 0 0 0" +cell.basis = "cc-pvdz" +cell.exp_to_discard = 0.1 cell.build() -kmesh = [2,2,1] +kmesh = [2, 2, 1] kpts = cell.make_kpts(kmesh) # --- Hartree-Fock kmf = pyscf.pbc.scf.KUHF(cell, kpts) -kmf = kmf.rs_density_fit(auxbasis='cc-pvdz-ri') +kmf = kmf.rs_density_fit(auxbasis="cc-pvdz-ri") kmf.kernel() # --- Embedding -emb = vayesta.ewf.EWF(kmf, bath_options=dict(bathtype='full'), solver_options=dict(solve_lambda=True)) +emb = vayesta.ewf.EWF(kmf, bath_options=dict(bathtype="full"), solver_options=dict(solve_lambda=True)) emb.kernel() e_dm = emb.get_dm_energy() @@ -34,18 +34,15 @@ cc.kernel() - print("Total Energy") print("E(HF)= %+16.8f Ha" % kmf.e_tot) -print("E(EWF-DPart)= %+16.8f Ha"% emb.get_dmet_energy()) +print("E(EWF-DPart)= %+16.8f Ha" % emb.get_dmet_energy()) print("E(EWF-Proj)= %+16.8f Ha" % emb.e_tot) -print("E(EWF-DM)= %+16.8f Ha"% emb.get_dm_energy()) +print("E(EWF-DM)= %+16.8f Ha" % emb.get_dm_energy()) print("E(CCSD)= %+16.8f Ha" % cc.e_tot) print("\nCorrelation Energy") -print("E(EWF-DPart)= %+16.8f Ha"% (emb.get_dmet_energy()-kmf.e_tot)) +print("E(EWF-DPart)= %+16.8f Ha" % (emb.get_dmet_energy() - kmf.e_tot)) print("E(EWF-Proj)= %+16.8f Ha" % emb.e_corr) -print("E(EWF-DM)= %+16.8f Ha"% emb.get_dm_corr_energy()) +print("E(EWF-DM)= %+16.8f Ha" % emb.get_dm_corr_energy()) print("E(CCSD)= %+16.8f Ha" % cc.e_corr) - - diff --git a/examples/ewf/solids/61-start-from-lda.py b/examples/ewf/solids/61-start-from-lda.py index 202ab829d..9f4551e09 100644 --- a/examples/ewf/solids/61-start-from-lda.py +++ b/examples/ewf/solids/61-start-from-lda.py @@ -10,15 +10,15 @@ cell = pyscf.pbc.gto.Cell() cell.a, cell.atom = solids.graphene() -cell.basis = 'cc-pVDZ' -cell.output = 'pyscf.out' +cell.basis = "cc-pVDZ" +cell.output = "pyscf.out" cell.dimension = 2 cell.build() # LDA lda = pyscf.pbc.dft.RKS(cell) -lda = lda.density_fit(auxbasis='cc-pVDZ-ri') -lda.xc = 'svwn' +lda = lda.density_fit(auxbasis="cc-pVDZ-ri") +lda.xc = "svwn" lda.kernel() # The KS opbject needs to be converted to a HF object # Do NOT use lda.to_rhf(), as this will remove the periodic boundary conditions @@ -31,7 +31,7 @@ # HF hf = pyscf.pbc.scf.RHF(cell) -hf = hf.density_fit(auxbasis='cc-pVDZ-ri') +hf = hf.density_fit(auxbasis="cc-pVDZ-ri") hf.kernel() emb_hf = vayesta.ewf.EWF(hf, bath_options=dict(threshold=1e-6)) diff --git a/examples/ewf/solids/73-rotational-symmetry.py b/examples/ewf/solids/73-rotational-symmetry.py index 0eedc8c6b..45b863eff 100644 --- a/examples/ewf/solids/73-rotational-symmetry.py +++ b/examples/ewf/solids/73-rotational-symmetry.py @@ -6,17 +6,17 @@ import vayesta.ewf cell = pyscf.pbc.gto.Cell() -cell.atom = 'Li 0 0 0; Li 1 0 0; Li 0 1 0; Li 1 1 0' -cell.a = 8*np.eye(3) -cell.basis = 'sto-6g' -cell.exp_to_discard=0.1 -cell.output = 'pyscf.out' +cell.atom = "Li 0 0 0; Li 1 0 0; Li 0 1 0; Li 1 1 0" +cell.a = 8 * np.eye(3) +cell.basis = "sto-6g" +cell.exp_to_discard = 0.1 +cell.output = "pyscf.out" cell.build() # Hartree-Fock with k-points -kpts = cell.make_kpts([2,2,2]) +kpts = cell.make_kpts([2, 2, 2]) mf = pyscf.pbc.scf.KRHF(cell, kpts) -mf = mf.density_fit(auxbasis='sto-6g') +mf = mf.density_fit(auxbasis="sto-6g") mf.kernel() # Embedded CCSD with rotational symmetry: @@ -25,13 +25,13 @@ # Add rotational symmetry: # Set order of rotation (2: 180 degrees, 3: 120 degrees, 4: 90: degrees,...), # axis along which to rotate and center of rotation (default units for axis and center are Angstroms): -#emb_sym.symmetry.add_rotation(order=4, axis=[0,0,1], center=[0.5,0.5,0]) +# emb_sym.symmetry.add_rotation(order=4, axis=[0,0,1], center=[0.5,0.5,0]) # It's also possible to use units of Bohr or lattice vectors: -#emb_sym.symmetry.add_rotation(order=4, axis=[0,0,1], center=[0.5/4,0.5/4,0], unit='latvec') +# emb_sym.symmetry.add_rotation(order=4, axis=[0,0,1], center=[0.5/4,0.5/4,0], unit='latvec') # Do not call add_all_atomic_fragments, as it add the symmetry related atoms as fragments! with emb_sym.iao_fragmentation() as frag: - with frag.rotational_symmetry(order=4, axis=[0,0,1], center=[0.5,0.5,0]): + with frag.rotational_symmetry(order=4, axis=[0, 0, 1], center=[0.5, 0.5, 0]): f = frag.add_atomic_fragment(0) emb_sym.kernel() diff --git a/examples/run_examples.py b/examples/run_examples.py index 58544d075..30739db10 100644 --- a/examples/run_examples.py +++ b/examples/run_examples.py @@ -7,23 +7,22 @@ else: directory = os.path.abspath(os.path.dirname(__file__)) -examples = os.popen('find . | grep \.py$').readlines() +examples = os.popen("find . | grep \.py$").readlines() assert len(examples) > 0 N = len(examples) errs = [] for eg in examples[1:]: - print("Running %s" %eg) - errno = subprocess.call('python ' + eg[:-1] + ' -q', shell=True) + print("Running %s" % eg) + errno = subprocess.call("python " + eg[:-1] + " -q", shell=True) if errno != 0: print("\033[91mException in %s \033[0m" % eg) errs.append(eg) if len(errs) == 0: - print("\033[92mAll examples passed \033[0m") + print("\033[92mAll examples passed \033[0m") else: - print("\033[91m Exceptions found: %d/%d examples failed \033[0m"%(len(errs),len(examples))) + print("\033[91m Exceptions found: %d/%d examples failed \033[0m" % (len(errs), len(examples))) for eg in errs: - print("Execption in %s"%eg) - + print("Execption in %s" % eg) diff --git a/examples/scmf/01-scmf.py b/examples/scmf/01-scmf.py index 6daa97fbd..a7ab3edaf 100644 --- a/examples/scmf/01-scmf.py +++ b/examples/scmf/01-scmf.py @@ -12,7 +12,7 @@ H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ -mol.basis = 'cc-pVDZ' +mol.basis = "cc-pVDZ" mol.build() # Hartree-Fock diff --git a/examples/variational_embedding/01-simple-vembedding.py b/examples/variational_embedding/01-simple-vembedding.py index 2a6e32b96..76ce2e2e6 100644 --- a/examples/variational_embedding/01-simple-vembedding.py +++ b/examples/variational_embedding/01-simple-vembedding.py @@ -7,7 +7,7 @@ import numpy as np -def run_ewf(natom, r, n_per_frag=1, bath_options={"bathtype":"dmet"}): +def run_ewf(natom, r, n_per_frag=1, bath_options={"bathtype": "dmet"}): if abs(natom / n_per_frag - natom // n_per_frag) > 1e-6: raise ValueError(f"Atoms per fragment ({n_per_frag}) doesn't exactly divide natoms ({natom})") @@ -20,8 +20,8 @@ def run_ewf(natom, r, n_per_frag=1, bath_options={"bathtype":"dmet"}): mol.charge = 0 mol.build() - rmf = scf.RHF(mol).density_fit(); - rmf.conv_tol = 1e-12; + rmf = scf.RHF(mol).density_fit() + rmf.conv_tol = 1e-12 rmf.kernel() out = rmf.stability(external=True) @@ -53,7 +53,7 @@ def get_density_projected(emb, inc_mf=False): def get_occ_projected(emb): - p_frags = [x.get_overlap('frag|cluster-occ') for x in emb.fragments] + p_frags = [x.get_overlap("frag|cluster-occ") for x in emb.fragments] p_frags = [np.dot(x.T, x) for x in p_frags] wfs = [x.results.wf.project_occ(y) for x, y in zip(emb.fragments, p_frags)] h, s, dm = variational_params.get_wf_couplings(emb, wfs=wfs, inc_mf=True) @@ -64,6 +64,7 @@ def get_occ_projected(emb): def gen_comp_graph(fname): import matplotlib.pyplot as plt + fig, (ax0, ax1) = plt.subplots(2, 1, sharex=True) fig.set_tight_layout(True) plot_results(fname, False, ax=ax0) @@ -75,6 +76,7 @@ def gen_comp_graph(fname): def draw_comparison_error(fname1, fname2): import matplotlib.pyplot as plt + fig, (ax0, ax1) = plt.subplots(1, 2, sharex=True, sharey=True) fig.set_tight_layout(True) plot_results(fname1, True, ax0) @@ -86,15 +88,28 @@ def draw_comparison_error(fname1, fname2): def plot_results(fname="results.txt", vsfci=False, ax=None, nodmenergy=True): import matplotlib.pyplot as plt + if ax is None: ax = plt.subplots(1, 1)[1] res = np.genfromtxt(fname) - labs = ["$r_{HH}/\AA$", "HF", "FCI", "CCSD", "EWF-proj-wf", "EWF-dm-wf", "NO-CAS-CI", "NO-dproj", "NO-dproj-CAS-CI", - "NO-oproj", "NO-oproj-CAS-CI", "var-NO-FCI"] + labs = [ + "$r_{HH}/\AA$", + "HF", + "FCI", + "CCSD", + "EWF-proj-wf", + "EWF-dm-wf", + "NO-CAS-CI", + "NO-dproj", + "NO-dproj-CAS-CI", + "NO-oproj", + "NO-oproj-CAS-CI", + "var-NO-FCI", + ] def remove_ind(results, labels, i): - labels = labels[:i] + labels[i + 1:] - results = np.concatenate([results[:, :i], results[:, i + 1:]], axis=1) + labels = labels[:i] + labels[i + 1 :] + results = np.concatenate([results[:, :i], results[:, i + 1 :]], axis=1) return results, labels if nodmenergy: @@ -157,5 +172,4 @@ def remove_ind(results, labels, i): res = (mf.e_tot, efci, ecc, eewf_wf, eewf_dm, e_barewf, e_dense_proj, e_dense_opt, e_occ_proj, e_occ_opt, e_opt) with open("results.txt", "a") as f: - f.write((f" {r:4.2f} ") + (" {:12.8f} " * len(res)).format(*res) + "\n") diff --git a/setup.py b/setup.py index a552fa92a..5e8246e27 100644 --- a/setup.py +++ b/setup.py @@ -13,16 +13,14 @@ class CMakeExtension(Extension): - """Initialise the name of a CMake extension. - """ + """Initialise the name of a CMake extension.""" def __init__(self, name): super().__init__(name, sources=[]) class CMakeBuild(build_ext): - """Build and configure a CMake extension. - """ + """Build and configure a CMake extension.""" def run(self): link_args = [] @@ -57,8 +55,7 @@ def build_cmake(self, ext): class CleanCommand(Command): - """Clean up files resulting from compilation except for .so shared objects. - """ + """Clean up files resulting from compilation except for .so shared objects.""" CLEAN_FILES = ["build", "dist", "*.egg-info"] user_options = [] @@ -80,13 +77,12 @@ def run(self): class DiscoverTests(test): - """Discover and dispatch tests. - """ + """Discover and dispatch tests.""" user_options = [ - ("include-veryslow", "v", "Include tests marked as veryslow"), - ("include-slow", "s", "Include tests marked as slow"), - ("pytest-args=", "p", "Extra arguments for pytest"), + ("include-veryslow", "v", "Include tests marked as veryslow"), + ("include-slow", "s", "Include tests marked as slow"), + ("pytest-args=", "p", "Extra arguments for pytest"), ] def initialize_options(self): @@ -118,8 +114,10 @@ def run_tests(self): # From PySCF - ensure the order of build sub-commands: from distutils.command.build import build -build.sub_commands = ([c for c in build.sub_commands if c[0] == 'build_ext'] + - [c for c in build.sub_commands if c[0] != 'build_ext']) + +build.sub_commands = [c for c in build.sub_commands if c[0] == "build_ext"] + [ + c for c in build.sub_commands if c[0] != "build_ext" +] setup( @@ -127,9 +125,9 @@ def run_tests(self): include_package_data=True, ext_modules=[CMakeExtension("vayesta/libs")], cmdclass={ - "build_ext": CMakeBuild, - "test": DiscoverTests, - "clean": CleanCommand, + "build_ext": CMakeBuild, + "test": DiscoverTests, + "clean": CleanCommand, }, zip_safe=False, ) diff --git a/vayesta/__init__.py b/vayesta/__init__.py index 951b300f6..26c1b2f08 100644 --- a/vayesta/__init__.py +++ b/vayesta/__init__.py @@ -9,7 +9,7 @@ from vayesta.mpi import init_mpi -__version__ = '1.0.1' +__version__ = "1.0.1" # Command line arguments args = cmdargs.parse_cmd_args() @@ -23,6 +23,7 @@ # Logging from vayesta.core import vlog + if args.output_dir: os.makedirs(args.output_dir, exist_ok=True) @@ -32,7 +33,7 @@ fmt = vlog.VFormatter(indent=True) # Log to stream -if (not args.quiet and mpi.is_master): +if not args.quiet and mpi.is_master: # Everything except logging.OUTPUT goes to stderr: errh = vlog.VStreamHandler(sys.stderr, formatter=fmt) errh.addFilter(vlog.LevelExcludeFilter(exclude=[logging.OUTPUT])) @@ -42,8 +43,8 @@ outh.addFilter(vlog.LevelIncludeFilter(include=[logging.OUTPUT])) log.addHandler(outh) # Log to file -if (args.log or not mpi.is_master): - logname = (args.log or "output") +if args.log or not mpi.is_master: + logname = args.log or "output" if args.output_dir: logname = os.path.join(args.output_dir, logname) log.addHandler(vlog.VFileHandler(logname, formatter=fmt)) @@ -58,22 +59,23 @@ log.addHandler(errhandler) # Print Logo, unless interactive Python interpreter -is_interactive = hasattr(sys, 'ps1') +is_interactive = hasattr(sys, "ps1") if not is_interactive: - logofile = os.path.abspath(os.path.join(os.path.dirname(__file__), 'logo.txt')) + logofile = os.path.abspath(os.path.join(os.path.dirname(__file__), "logo.txt")) try: - with open(logofile, 'r') as f: + with open(logofile, "r") as f: logo = f.read() - logo = (logo.rstrip() + ' ') + logo = logo.rstrip() + " " except FileNotFoundError: log.error("%s not found.", logofile) - logo = '' - log.info(logo + '\n', "version " + __version__) + logo = "" + log.info(logo + "\n", "version " + __version__) # --- Required modules + def import_package(name, required=True): - fmt = ' * %-10s v%-8s location: %s' + fmt = " * %-10s v%-8s location: %s" try: package = importlib.import_module(name.lower()) log.debug(fmt, name, package.__version__, os.path.dirname(package.__file__)) @@ -85,30 +87,33 @@ def import_package(name, required=True): log.debug("%s not found.", name) return None + log.debug("Required packages:") -numpy = import_package('NumPy') -import_package('SciPy') -import_package('h5py') -pyscf = import_package('PySCF') +numpy = import_package("NumPy") +import_package("SciPy") +import_package("h5py") +pyscf = import_package("PySCF") # Optional -import_package('mpi4py', False) -import_package('cvxpy', False) -dyson = import_package('dyson', False) -ebcc = import_package('ebcc', False) -import_package('pygnme', False) +import_package("mpi4py", False) +import_package("cvxpy", False) +dyson = import_package("dyson", False) +ebcc = import_package("ebcc", False) +import_package("pygnme", False) # --- Git hashes + def get_git_hash(dir): - git_dir = os.path.join(dir, '.git') - cmd = ['git', '--git-dir=%s' % git_dir, 'rev-parse', '--short', 'HEAD'] + git_dir = os.path.join(dir, ".git") + cmd = ["git", "--git-dir=%s" % git_dir, "rev-parse", "--short", "HEAD"] try: githash = subprocess.check_output(cmd, universal_newlines=True, stderr=subprocess.STDOUT).rstrip() except subprocess.CalledProcessError: githash = "" return githash + log.debug("Git hashes:") vdir = os.path.dirname(os.path.dirname(__file__)) vhash = get_git_hash(vdir) @@ -126,7 +131,7 @@ def get_git_hash(dir): log.debug(" * EBCC: %s", ehash) # --- System information -log.debug('System: node= %s processor= %s' % (platform.node(), platform.processor())) +log.debug("System: node= %s processor= %s" % (platform.node(), platform.processor())) # --- MPI if mpi: @@ -134,13 +139,14 @@ def get_git_hash(dir): # --- Environment log.debug("Environment variables:") -omp_num_threads = os.getenv('OMP_NUM_THREADS') +omp_num_threads = os.getenv("OMP_NUM_THREADS") if omp_num_threads is not None: omp_num_threads = int(omp_num_threads) log.debug(" OMP_NUM_THREADS= %s", omp_num_threads) # --- + def new_log(logname, fmt=None, remove_existing=True): if fmt is None: fmt = vlog.VFormatter(indent=True) @@ -152,5 +158,6 @@ def new_log(logname, fmt=None, remove_existing=True): log.removeHandler(hdl) log.addHandler(vlog.VFileHandler(logname, formatter=fmt)) + # --- NumPy numpy.set_printoptions(linewidth=120) diff --git a/vayesta/core/ao2mo/helper.py b/vayesta/core/ao2mo/helper.py index d6370287c..558241502 100644 --- a/vayesta/core/ao2mo/helper.py +++ b/vayesta/core/ao2mo/helper.py @@ -10,7 +10,7 @@ def get_kconserv(cell, kpts, nk=3): - r'''Get the momentum conservation array for a set of k-points. + r"""Get the momentum conservation array for a set of k-points. Given k-point indices (k, l, m) the array kconserv[k,l,m] returns the index n that satifies momentum conservation, @@ -25,25 +25,25 @@ def get_kconserv(cell, kpts, nk=3): This is used for symmetry e.g. integrals of the form [\phi*[k](1) \phi[l](1) | \phi*[m](2) \phi[n](2)] are zero unless n satisfies the above. - ''' + """ nkpts = kpts.shape[0] - a = cell.lattice_vectors() / (2*np.pi) + a = cell.lattice_vectors() / (2 * np.pi) if nk == 1: return list(range(len(kpts))) - kconserv = np.zeros(nk*[nkpts], dtype=int) + kconserv = np.zeros(nk * [nkpts], dtype=int) if nk == 2: - k_klm = kpts[:,None,:] - kpts # k(k) - k(l) + k_klm = kpts[:, None, :] - kpts # k(k) - k(l) elif nk == 3: - k_klm = kpts[:,None,None,:] - kpts[:,None,:] + kpts # k(k) - k(l) + k(m) + k_klm = kpts[:, None, None, :] - kpts[:, None, :] + kpts # k(k) - k(l) + k(m) else: raise ValueError for n, kn in enumerate(kpts): - k_klmn = (k_klm - kn) - k_klmn = einsum('wx,...x->w...', a, k_klmn) + k_klmn = k_klm - kn + k_klmn = einsum("wx,...x->w...", a, k_klmn) # check whether (1/(2pi) k_klmn dot a) is an integer - mask = einsum('w...->...', abs(k_klmn - np.rint(k_klmn))) < 1e-9 + mask = einsum("w...->...", abs(k_klmn - np.rint(k_klmn))) < 1e-9 kconserv[mask] = n return kconserv @@ -51,7 +51,7 @@ def get_kconserv(cell, kpts, nk=3): def get_full_array(eris, mo_coeff=None, out=None): """Get dense ERI array from CCSD _ChemistEris object.""" - if hasattr(eris, 'OOOO'): + if hasattr(eris, "OOOO"): return get_full_array_uhf(eris, mo_coeff=mo_coeff, out=out) return get_full_array_rhf(eris, mo_coeff=mo_coeff, out=out) @@ -64,45 +64,46 @@ def get_full_array_rhf(eris, mo_coeff=None, out=None): nmo = nocc + nvir o, v = np.s_[:nocc], np.s_[nocc:] if out is None: - out = np.zeros(4*[nmo]) + out = np.zeros(4 * [nmo]) - swap = lambda x : x.transpose(2,3,0,1) # Swap electrons - conj = lambda x : x.transpose(1,0,3,2) # Real orbital symmetry + swap = lambda x: x.transpose(2, 3, 0, 1) # Swap electrons + conj = lambda x: x.transpose(1, 0, 3, 2) # Real orbital symmetry # 4-occ - out[o,o,o,o] = eris.oooo + out[o, o, o, o] = eris.oooo # 3-occ - out[o,v,o,o] = eris.ovoo[:] - out[v,o,o,o] = conj(out[o,v,o,o]) - out[o,o,o,v] = swap(out[o,v,o,o]) - out[o,o,v,o] = conj(out[o,o,o,v]) + out[o, v, o, o] = eris.ovoo[:] + out[v, o, o, o] = conj(out[o, v, o, o]) + out[o, o, o, v] = swap(out[o, v, o, o]) + out[o, o, v, o] = conj(out[o, o, o, v]) # 2-occ - out[o,o,v,v] = eris.oovv[:] - out[v,v,o,o] = swap(out[o,o,v,v]) - out[o,v,o,v] = eris.ovov[:] - out[v,o,v,o] = conj(out[o,v,o,v]) - out[o,v,v,o] = eris.ovvo[:] - out[v,o,o,v] = swap(eris.ovvo[:]) + out[o, o, v, v] = eris.oovv[:] + out[v, v, o, o] = swap(out[o, o, v, v]) + out[o, v, o, v] = eris.ovov[:] + out[v, o, v, o] = conj(out[o, v, o, v]) + out[o, v, v, o] = eris.ovvo[:] + out[v, o, o, v] = swap(eris.ovvo[:]) # 1-occ - out[o,v,v,v] = get_ovvv(eris) - out[v,o,v,v] = conj(out[o,v,v,v]) - out[v,v,o,v] = swap(out[o,v,v,v]) - out[v,v,v,o] = conj(out[v,v,o,v]) + out[o, v, v, v] = get_ovvv(eris) + out[v, o, v, v] = conj(out[o, v, v, v]) + out[v, v, o, v] = swap(out[o, v, v, v]) + out[v, v, v, o] = conj(out[v, v, o, v]) # 0-occ - out[v,v,v,v] = get_vvvv(eris) + out[v, v, v, v] = get_vvvv(eris) return out def get_full_array_uhf(eris, mo_coeff=None, out=None): """Get dense ERI array from CCSD _ChemistEris object.""" - if mo_coeff is not None and not (np.allclose(mo_coeff[0], eris.mo_coeff[0]) - and np.allclose(mo_coeff[1], eris.mo_coeff[1])): + if mo_coeff is not None and not ( + np.allclose(mo_coeff[0], eris.mo_coeff[0]) and np.allclose(mo_coeff[1], eris.mo_coeff[1]) + ): raise NotImplementedError nocca, noccb = eris.nocc nmoa, nmob = eris.fock[0].shape[-1], eris.fock[1].shape[-1] nvira, nvirb = nmoa - nocca, nmob - noccb # Alpha-alpha - blocks_aa = ['oooo', 'ovoo', 'oovv', 'ovov', 'ovvo', 'ovvv', 'vvvv'] + blocks_aa = ["oooo", "ovoo", "oovv", "ovov", "ovvo", "ovvv", "vvvv"] eris_aa = Object() eris_aa.fock = eris.fock[0] eris_aa.nocc = nocca @@ -118,38 +119,38 @@ def get_full_array_uhf(eris, mo_coeff=None, out=None): setattr(eris_bb, blocks_aa[i], getattr(eris, block)) eri_bb = get_full_array_rhf(eris_bb, mo_coeff=getif(mo_coeff, 1), out=getif(out, 2)) # Alpha-beta - eri_ab = np.zeros((nmoa,nmoa,nmob,nmob)) if out is None else out[1] + eri_ab = np.zeros((nmoa, nmoa, nmob, nmob)) if out is None else out[1] oa, ob = np.s_[:nocca], np.s_[:noccb] va, vb = np.s_[nocca:], np.s_[noccb:] - swap = lambda x : x.transpose(2,3,0,1) # Swap electrons - conj = lambda x : x.transpose(1,0,3,2) # Real orbital symmetry + swap = lambda x: x.transpose(2, 3, 0, 1) # Swap electrons + conj = lambda x: x.transpose(1, 0, 3, 2) # Real orbital symmetry # 4-occ - eri_ab[oa,oa,ob,ob] = eris.ooOO[:] + eri_ab[oa, oa, ob, ob] = eris.ooOO[:] # 3-occ - eri_ab[oa,va,ob,ob] = eris.ovOO[:] - eri_ab[va,oa,ob,ob] = conj(eri_ab[oa,va,ob,ob]) - eri_ab[oa,oa,ob,vb] = swap(eris.OVoo[:]) - eri_ab[oa,oa,vb,ob] = conj(eri_ab[oa,oa,ob,vb]) + eri_ab[oa, va, ob, ob] = eris.ovOO[:] + eri_ab[va, oa, ob, ob] = conj(eri_ab[oa, va, ob, ob]) + eri_ab[oa, oa, ob, vb] = swap(eris.OVoo[:]) + eri_ab[oa, oa, vb, ob] = conj(eri_ab[oa, oa, ob, vb]) # 2-occ - eri_ab[oa,oa,vb,vb] = eris.ooVV[:] - eri_ab[va,va,ob,ob] = swap(eris.OOvv[:]) - eri_ab[oa,va,vb,ob] = eris.ovVO[:] - eri_ab[va,oa,ob,vb] = conj(eri_ab[oa,va,vb,ob]) - eri_ab[oa,va,ob,vb] = eris.ovOV[:] - eri_ab[va,oa,vb,ob] = conj(eri_ab[oa,va,ob,vb]) + eri_ab[oa, oa, vb, vb] = eris.ooVV[:] + eri_ab[va, va, ob, ob] = swap(eris.OOvv[:]) + eri_ab[oa, va, vb, ob] = eris.ovVO[:] + eri_ab[va, oa, ob, vb] = conj(eri_ab[oa, va, vb, ob]) + eri_ab[oa, va, ob, vb] = eris.ovOV[:] + eri_ab[va, oa, vb, ob] = conj(eri_ab[oa, va, ob, vb]) # 1-occ - eri_ab[oa,va,vb,vb] = get_ovVV(eris, block='ovVV') - eri_ab[va,oa,vb,vb] = conj(eri_ab[oa,va,vb,vb]) - eri_ab[va,va,ob,vb] = swap(get_ovVV(eris, block='OVvv')) - eri_ab[va,va,vb,ob] = conj(eri_ab[va,va,ob,vb]) + eri_ab[oa, va, vb, vb] = get_ovVV(eris, block="ovVV") + eri_ab[va, oa, vb, vb] = conj(eri_ab[oa, va, vb, vb]) + eri_ab[va, va, ob, vb] = swap(get_ovVV(eris, block="OVvv")) + eri_ab[va, va, vb, ob] = conj(eri_ab[va, va, ob, vb]) # 0-occ - eri_ab[va,va,vb,vb] = get_vvVV(eris) + eri_ab[va, va, vb, vb] = get_vvVV(eris) return eri_aa, eri_ab, eri_bb -def get_ovvv(eris, block='ovvv'): - if hasattr(eris,'OOOO'): - s = (0 if block == 'ovvv' else 1) +def get_ovvv(eris, block="ovvv"): + if hasattr(eris, "OOOO"): + s = 0 if block == "ovvv" else 1 nmo = eris.fock[s].shape[-1] nocc = eris.nocc[s] else: @@ -159,15 +160,15 @@ def get_ovvv(eris, block='ovvv'): govvv = getattr(eris, block)[:] if govvv.ndim == 4: return govvv - nvir_pair = nvir*(nvir+1)//2 - govvv = pyscf.lib.unpack_tril(govvv.reshape(nocc*nvir, nvir_pair)) - govvv = govvv.reshape(nocc,nvir,nvir,nvir) + nvir_pair = nvir * (nvir + 1) // 2 + govvv = pyscf.lib.unpack_tril(govvv.reshape(nocc * nvir, nvir_pair)) + govvv = govvv.reshape(nocc, nvir, nvir, nvir) return govvv -def get_ovVV(eris, block='ovVV'): - assert block in ('ovVV', 'OVvv') - sl, sr = (0, 1) if block == 'ovVV' else (1, 0) +def get_ovVV(eris, block="ovVV"): + assert block in ("ovVV", "OVvv") + sl, sr = (0, 1) if block == "ovVV" else (1, 0) nmoL = eris.fock[sl].shape[-1] nmoR = eris.fock[sr].shape[-1] noccL = eris.nocc[sl] @@ -177,15 +178,15 @@ def get_ovVV(eris, block='ovVV'): govvv = getattr(eris, block)[:] if govvv.ndim == 4: return govvv - nvir_pair = nvirR*(nvirR+1)//2 - govvv = pyscf.lib.unpack_tril(govvv.reshape(noccL*nvirL, nvir_pair)) - govvv = govvv.reshape(noccL,nvirL,nvirR,nvirR) + nvir_pair = nvirR * (nvirR + 1) // 2 + govvv = pyscf.lib.unpack_tril(govvv.reshape(noccL * nvirL, nvir_pair)) + govvv = govvv.reshape(noccL, nvirL, nvirR, nvirR) return govvv -def get_vvvv(eris, block='vvvv'): - if hasattr(eris, 'VVVV'): - s = (0 if block == 'vvvv' else 1) +def get_vvvv(eris, block="vvvv"): + if hasattr(eris, "VVVV"): + s = 0 if block == "vvvv" else 1 nmo = eris.fock[s].shape[-1] nocc = eris.nocc[s] else: @@ -201,15 +202,15 @@ def get_vvvv(eris, block='vvvv'): # Note that this will not work for 2D systems: if eris.vvL.ndim == 2: naux = eris.vvL.shape[-1] - vvl = pyscf.lib.unpack_tril(eris.vvL[:], axis=0).reshape(nvir,nvir,naux) + vvl = pyscf.lib.unpack_tril(eris.vvL[:], axis=0).reshape(nvir, nvir, naux) else: vvl = eris.vvL[:] - gvvvv = einsum('ijQ,klQ->ijkl', vvl, vvl) + gvvvv = einsum("ijQ,klQ->ijkl", vvl, vvl) return gvvvv -def get_vvVV(eris, block='vvVV'): - sl, sr = ((0, 1) if block == 'vvVV' else (1, 0)) +def get_vvVV(eris, block="vvVV"): + sl, sr = (0, 1) if block == "vvVV" else (1, 0) nmoL = eris.fock[sl].shape[-1] nmoR = eris.fock[sr].shape[-1] noccL = eris.nocc[sl] @@ -222,27 +223,27 @@ def get_vvVV(eris, block='vvVV'): if gvvvv.ndim == 4: return gvvvv[:] else: - nvv = (-1 if gvvvv.size else 0) + nvv = -1 if gvvvv.size else 0 xVV = pyscf.lib.unpack_tril(gvvvv[:], axis=0).reshape(nvirL**2, nvv) - return pyscf.lib.unpack_tril(xVV[:], axis=1).reshape(nvirL,nvirL,nvirR,nvirR) + return pyscf.lib.unpack_tril(xVV[:], axis=1).reshape(nvirL, nvirL, nvirR, nvirR) raise NotImplementedError def get_block(eris, block): - if block in ['ovvv', 'OVVV']: + if block in ["ovvv", "OVVV"]: return get_ovvv(eris, block=block) - if block in ['ovVV', 'OVvv']: + if block in ["ovVV", "OVvv"]: return get_ovVV(eris, block=block) - if block in ['vvvv', 'VVVV']: + if block in ["vvvv", "VVVV"]: return get_vvvv(eris, block=block) - if block in ['vvVV', 'VVvv']: + if block in ["vvVV", "VVvv"]: return get_vvVV(eris, block=block) return getattr(eris, block) def pack_ovvv(ovvv): nocc, nvir = ovvv.shape[:2] - ovvv = pyscf.lib.pack_tril(ovvv.reshape(nocc*nvir, nvir, nvir)) + ovvv = pyscf.lib.pack_tril(ovvv.reshape(nocc * nvir, nvir, nvir)) return ovvv.reshape(nocc, nvir, -1) @@ -277,7 +278,7 @@ def contract_dm2_eris(dm2, eris): def _contract_4d(a, b, transpose=None): if transpose is not None: b = b[:].transpose(transpose) - #return einsum('pqrs,pqrs', a, b) + # return einsum('pqrs,pqrs', a, b) return np.dot(a[:].reshape(-1), b[:].reshape(-1)) @@ -298,13 +299,13 @@ def contract_dm2_eris_rhf(dm2, eris): """ nocc = eris.oooo.shape[0] o, v = np.s_[:nocc], np.s_[nocc:] - e_oooo = _contract_4d(dm2[o,o,o,o], eris.oooo) - e_ovoo = _contract_4d(dm2[o,v,o,o], eris.ovoo) * 4 - e_oovv = _contract_4d(dm2[o,o,v,v], eris.oovv) * 2 - e_ovov = _contract_4d(dm2[o,v,o,v], eris.ovov) * 2 - e_ovvo = _contract_4d(dm2[o,v,v,o], eris.ovvo) * 2 - e_ovvv = _contract_4d(dm2[o,v,v,v], get_ovvv(eris)) * 4 - e_vvvv = _contract_4d(dm2[v,v,v,v], get_vvvv(eris)) + e_oooo = _contract_4d(dm2[o, o, o, o], eris.oooo) + e_ovoo = _contract_4d(dm2[o, v, o, o], eris.ovoo) * 4 + e_oovv = _contract_4d(dm2[o, o, v, v], eris.oovv) * 2 + e_ovov = _contract_4d(dm2[o, v, o, v], eris.ovov) * 2 + e_ovvo = _contract_4d(dm2[o, v, v, o], eris.ovvo) * 2 + e_ovvv = _contract_4d(dm2[o, v, v, v], get_ovvv(eris)) * 4 + e_vvvv = _contract_4d(dm2[v, v, v, v], get_vvvv(eris)) log.debugv("E(oooo)= %s", energy_string(e_oooo)) log.debugv("E(ovoo)= %s", energy_string(e_ovoo)) log.debugv("E(oovv)= %s", energy_string(e_oovv)) @@ -337,41 +338,50 @@ def contract_dm2_eris_uhf(dm2, eris): e2 = 0 # Alpha-alpha o, v = np.s_[:nocca], np.s_[nocca:] - e2 += _contract_4d(dm2aa[o,o,o,o], eris.oooo) - e2 += _contract_4d(dm2aa[o,v,o,o], eris.ovoo) * 4 - e2 += _contract_4d(dm2aa[o,o,v,v], eris.oovv) * 2 - e2 += _contract_4d(dm2aa[o,v,o,v], eris.ovov) * 2 - e2 += _contract_4d(dm2aa[o,v,v,o], eris.ovvo) * 2 - e2 += _contract_4d(dm2aa[o,v,v,v], get_ovvv(eris)) * 4 - e2 += _contract_4d(dm2aa[v,v,v,v], get_vvvv(eris)) + e2 += _contract_4d(dm2aa[o, o, o, o], eris.oooo) + e2 += _contract_4d(dm2aa[o, v, o, o], eris.ovoo) * 4 + e2 += _contract_4d(dm2aa[o, o, v, v], eris.oovv) * 2 + e2 += _contract_4d(dm2aa[o, v, o, v], eris.ovov) * 2 + e2 += _contract_4d(dm2aa[o, v, v, o], eris.ovvo) * 2 + e2 += _contract_4d(dm2aa[o, v, v, v], get_ovvv(eris)) * 4 + e2 += _contract_4d(dm2aa[v, v, v, v], get_vvvv(eris)) # Beta-beta o, v = np.s_[:noccb], np.s_[noccb:] - e2 += _contract_4d(dm2bb[o,o,o,o], eris.OOOO) - e2 += _contract_4d(dm2bb[o,v,o,o], eris.OVOO) * 4 - e2 += _contract_4d(dm2bb[o,o,v,v], eris.OOVV) * 2 - e2 += _contract_4d(dm2bb[o,v,o,v], eris.OVOV) * 2 - e2 += _contract_4d(dm2bb[o,v,v,o], eris.OVVO) * 2 - e2 += _contract_4d(dm2bb[o,v,v,v], get_ovvv(eris, block='OVVV')) * 4 - e2 += _contract_4d(dm2bb[v,v,v,v], get_vvvv(eris, block='VVVV')) + e2 += _contract_4d(dm2bb[o, o, o, o], eris.OOOO) + e2 += _contract_4d(dm2bb[o, v, o, o], eris.OVOO) * 4 + e2 += _contract_4d(dm2bb[o, o, v, v], eris.OOVV) * 2 + e2 += _contract_4d(dm2bb[o, v, o, v], eris.OVOV) * 2 + e2 += _contract_4d(dm2bb[o, v, v, o], eris.OVVO) * 2 + e2 += _contract_4d(dm2bb[o, v, v, v], get_ovvv(eris, block="OVVV")) * 4 + e2 += _contract_4d(dm2bb[v, v, v, v], get_vvvv(eris, block="VVVV")) # Alpha-beta oa, va = np.s_[:nocca], np.s_[nocca:] ob, vb = np.s_[:noccb], np.s_[noccb:] - e2 += _contract_4d(dm2ab[oa,oa,ob,ob], eris.ooOO) * 2 - e2 += _contract_4d(dm2ab[oa,va,ob,ob], eris.ovOO) * 4 - e2 += _contract_4d(dm2ab[oa,oa,ob,vb], eris.OVoo, transpose=(2,3,0,1)) * 4 - e2 += _contract_4d(dm2ab[oa,oa,vb,vb], eris.ooVV) * 2 - e2 += _contract_4d(dm2ab[va,va,ob,ob], eris.OOvv, transpose=(2,3,0,1)) * 2 - e2 += _contract_4d(dm2ab[oa,va,vb,ob], eris.ovVO) * 4 - e2 += _contract_4d(dm2ab[oa,va,ob,vb], eris.ovOV) * 4 - #e2 += einsum('pqrs,rspq', dm2ab[va,oa,ob,vb], eris.OVvo) * 4 - e2 += _contract_4d(dm2ab[oa,va,vb,vb], get_ovVV(eris, block='ovVV')) * 4 - e2 += _contract_4d(dm2ab[va,va,ob,vb], get_ovVV(eris, block='OVvv'), transpose=(2,3,0,1)) * 4 - e2 += _contract_4d(dm2ab[va,va,vb,vb], get_vvVV(eris)) * 2 + e2 += _contract_4d(dm2ab[oa, oa, ob, ob], eris.ooOO) * 2 + e2 += _contract_4d(dm2ab[oa, va, ob, ob], eris.ovOO) * 4 + e2 += _contract_4d(dm2ab[oa, oa, ob, vb], eris.OVoo, transpose=(2, 3, 0, 1)) * 4 + e2 += _contract_4d(dm2ab[oa, oa, vb, vb], eris.ooVV) * 2 + e2 += _contract_4d(dm2ab[va, va, ob, ob], eris.OOvv, transpose=(2, 3, 0, 1)) * 2 + e2 += _contract_4d(dm2ab[oa, va, vb, ob], eris.ovVO) * 4 + e2 += _contract_4d(dm2ab[oa, va, ob, vb], eris.ovOV) * 4 + # e2 += einsum('pqrs,rspq', dm2ab[va,oa,ob,vb], eris.OVvo) * 4 + e2 += _contract_4d(dm2ab[oa, va, vb, vb], get_ovVV(eris, block="ovVV")) * 4 + e2 += _contract_4d(dm2ab[va, va, ob, vb], get_ovVV(eris, block="OVvv"), transpose=(2, 3, 0, 1)) * 4 + e2 += _contract_4d(dm2ab[va, va, vb, vb], get_vvVV(eris)) * 2 return e2 # Order used in PySCF for 2-DM intermediates: -dm2intermeds = ['ovov', 'vvvv', 'oooo', 'oovv', 'ovvo', 'vvov', 'ovvv', 'ooov',] +dm2intermeds = [ + "ovov", + "vvvv", + "oooo", + "oovv", + "ovvo", + "vvov", + "ovvv", + "ooov", +] def _dm2intermeds_to_dict_rhf(dm2): @@ -417,13 +427,13 @@ def _get_block(block, keep=False): return dm2.pop(block) return dm2[block] - e_oooo = _contract_4d(_get_block('oooo'), eris.oooo) * 4 - e_ovoo = _contract_4d(_get_block('ooov'), eris.ovoo, transpose=(2,3,0,1)) * 4 - e_oovv = _contract_4d(_get_block('oovv'), eris.oovv) * 4 - e_ovov = _contract_4d(_get_block('ovov'), eris.ovov) * 4 - e_ovvo = _contract_4d(_get_block('ovvo'), eris.ovvo) * 4 - e_ovvv = _contract_4d(_get_block('ovvv'), get_ovvv(eris)) * 4 - e_vvvv = _contract_4d(_get_block('vvvv'), get_vvvv(eris)) * 4 + e_oooo = _contract_4d(_get_block("oooo"), eris.oooo) * 4 + e_ovoo = _contract_4d(_get_block("ooov"), eris.ovoo, transpose=(2, 3, 0, 1)) * 4 + e_oovv = _contract_4d(_get_block("oovv"), eris.oovv) * 4 + e_ovov = _contract_4d(_get_block("ovov"), eris.ovov) * 4 + e_ovvo = _contract_4d(_get_block("ovvo"), eris.ovvo) * 4 + e_ovvv = _contract_4d(_get_block("ovvv"), get_ovvv(eris)) * 4 + e_vvvv = _contract_4d(_get_block("vvvv"), get_vvvv(eris)) * 4 log.debugv("E(oooo)= %s", energy_string(e_oooo)) log.debugv("E(ovoo)= %s", energy_string(e_ovoo)) log.debugv("E(oovv)= %s", energy_string(e_oovv)) @@ -459,34 +469,34 @@ def _get_block(block, keep=False): e2 = 0 # Alpha-alpha - e2 += _contract_4d(_get_block('oooo'), eris.oooo) - e2 += _contract_4d(_get_block('ooov'), eris.ovoo, transpose=(2,3,0,1)) * 4 - #e2 += _contract_4d(_get_block('ovvo', keep=True), eris.oovv, transpose=(0,3,2,1)) * -2 - e2 += _contract_4d(_get_block('oovv'), eris.oovv) * 2 - e2 += _contract_4d(_get_block('ovov'), eris.ovov) * 2 - e2 += _contract_4d(_get_block('ovvo'), eris.ovvo) * 2 - e2 += _contract_4d(_get_block('ovvv'), get_ovvv(eris)) * 4 - e2 += _contract_4d(_get_block('vvvv'), get_vvvv(eris)) + e2 += _contract_4d(_get_block("oooo"), eris.oooo) + e2 += _contract_4d(_get_block("ooov"), eris.ovoo, transpose=(2, 3, 0, 1)) * 4 + # e2 += _contract_4d(_get_block('ovvo', keep=True), eris.oovv, transpose=(0,3,2,1)) * -2 + e2 += _contract_4d(_get_block("oovv"), eris.oovv) * 2 + e2 += _contract_4d(_get_block("ovov"), eris.ovov) * 2 + e2 += _contract_4d(_get_block("ovvo"), eris.ovvo) * 2 + e2 += _contract_4d(_get_block("ovvv"), get_ovvv(eris)) * 4 + e2 += _contract_4d(_get_block("vvvv"), get_vvvv(eris)) # Beta-beta - e2 += _contract_4d(_get_block('OOOO'), eris.OOOO) - e2 += _contract_4d(_get_block('OOOV'), eris.OVOO, transpose=(2,3,0,1)) * 4 - #e2 += _contract_4d(_get_block('OVVO', keep=True), eris.OOVV, transpose=(0,3,2,1)) * -2 - e2 += _contract_4d(_get_block('OOVV'), eris.OOVV) * 2 - e2 += _contract_4d(_get_block('OVOV'), eris.OVOV) * 2 - e2 += _contract_4d(_get_block('OVVO'), eris.OVVO) * 2 - e2 += _contract_4d(_get_block('OVVV'), get_ovvv(eris, block='OVVV')) * 4 - e2 += _contract_4d(_get_block('VVVV'), get_vvvv(eris, block='VVVV')) + e2 += _contract_4d(_get_block("OOOO"), eris.OOOO) + e2 += _contract_4d(_get_block("OOOV"), eris.OVOO, transpose=(2, 3, 0, 1)) * 4 + # e2 += _contract_4d(_get_block('OVVO', keep=True), eris.OOVV, transpose=(0,3,2,1)) * -2 + e2 += _contract_4d(_get_block("OOVV"), eris.OOVV) * 2 + e2 += _contract_4d(_get_block("OVOV"), eris.OVOV) * 2 + e2 += _contract_4d(_get_block("OVVO"), eris.OVVO) * 2 + e2 += _contract_4d(_get_block("OVVV"), get_ovvv(eris, block="OVVV")) * 4 + e2 += _contract_4d(_get_block("VVVV"), get_vvvv(eris, block="VVVV")) # Alpha-beta - e2 += _contract_4d(_get_block('ooOO'), eris.ooOO) * 2 - e2 += _contract_4d(_get_block('OOov'), eris.ovOO, transpose=(2,3,0,1)) * 4 - e2 += _contract_4d(_get_block('ooOV'), eris.OVoo, transpose=(2,3,0,1)) * 4 - e2 += _contract_4d(_get_block('ooVV'), eris.ooVV) * 2 - e2 += _contract_4d(_get_block('OOvv'), eris.OOvv) * 2 - e2 += _contract_4d(_get_block('ovVO'), eris.ovVO) * 4 - e2 += _contract_4d(_get_block('ovOV'), eris.ovOV) * 4 - e2 += _contract_4d(_get_block('ovVV'), get_ovVV(eris, block='ovVV')) * 4 - e2 += _contract_4d(_get_block('OVvv'), get_ovVV(eris, block='OVvv')) * 4 - e2 += _contract_4d(_get_block('vvVV'), get_vvVV(eris)) * 2 + e2 += _contract_4d(_get_block("ooOO"), eris.ooOO) * 2 + e2 += _contract_4d(_get_block("OOov"), eris.ovOO, transpose=(2, 3, 0, 1)) * 4 + e2 += _contract_4d(_get_block("ooOV"), eris.OVoo, transpose=(2, 3, 0, 1)) * 4 + e2 += _contract_4d(_get_block("ooVV"), eris.ooVV) * 2 + e2 += _contract_4d(_get_block("OOvv"), eris.OOvv) * 2 + e2 += _contract_4d(_get_block("ovVO"), eris.ovVO) * 4 + e2 += _contract_4d(_get_block("ovOV"), eris.ovOV) * 4 + e2 += _contract_4d(_get_block("ovVV"), get_ovVV(eris, block="ovVV")) * 4 + e2 += _contract_4d(_get_block("OVvv"), get_ovVV(eris, block="OVvv")) * 4 + e2 += _contract_4d(_get_block("vvVV"), get_vvVV(eris)) * 2 return e2 @@ -522,12 +532,12 @@ def project_ccsd_eris(eris, mo_coeff, nocc, ovlp, check_subspace=True): nocc, nvir = c_occ.shape[-1], c_vir.shape[-1] log.debug("Projecting ERIs: N(occ)= %3d -> %3d N(vir)= %3d -> %3d", nocc0, nocc, nvir0, nvir) - transform_occ = (nocc != nocc0 or not np.allclose(c_occ, c_occ0)) + transform_occ = nocc != nocc0 or not np.allclose(c_occ, c_occ0) if transform_occ: r_occ = dot(c_occ.T, ovlp, c_occ0) else: r_occ = np.eye(nocc) - transform_vir = (nvir != nvir0 or not np.allclose(c_vir, c_vir0)) + transform_vir = nvir != nvir0 or not np.allclose(c_vir, c_vir0) if transform_vir: r_vir = dot(c_vir.T, ovlp, c_vir0) else: @@ -546,35 +556,33 @@ def project_ccsd_eris(eris, mo_coeff, nocc, ovlp, check_subspace=True): raise RuntimeError("MO coefficients do not span subspace of eris.mo_coeff.") p_occ = np.dot(r_occ.T, r_occ) e, v = np.linalg.eigh(p_occ) - n = np.count_nonzero(abs(e)>1e-8) + n = np.count_nonzero(abs(e) > 1e-8) if n < nocc: log.debug("e(occ)= %d\n%r", n, e) raise RuntimeError("MO coefficients do not span subspace of eris.mo_coeff.") p_vir = np.dot(r_vir.T, r_vir) e, v = np.linalg.eigh(p_vir) - n = np.count_nonzero(abs(e)>1e-8) + n = np.count_nonzero(abs(e) > 1e-8) if n < nvir: log.debug("e(vir)= %d\n%r", n, e) raise RuntimeError("MO coefficients do not span subspace of eris.mo_coeff.") - r_all = np.block([ - [r_occ, np.zeros((nocc, nvir0))], - [np.zeros((nvir, nocc0)), r_vir]]) + r_all = np.block([[r_occ, np.zeros((nocc, nvir0))], [np.zeros((nvir, nocc0)), r_vir]]) - transform = lambda g, t0, t1, t2, t3 : einsum("abcd,ia,jb,kc,ld -> ijkl", g, t0, t1, t2, t3) + transform = lambda g, t0, t1, t2, t3: einsum("abcd,ia,jb,kc,ld -> ijkl", g, t0, t1, t2, t3) - if hasattr(eris, 'vvL'): - raise NotImplementedError() + if hasattr(eris, "vvL"): + raise NotImplementedError() - for block in ['oooo', 'ovoo', 'oovv', 'ovov', 'ovvo', 'ovvv', 'vvvv']: + for block in ["oooo", "ovoo", "oovv", "ovov", "ovvo", "ovvv", "vvvv"]: log.debugv("Projecting integrals (%2s|%2s)", block[:2], block[2:]) g = get_block(eris, block) - shape0 = [(nocc0 if (pos == 'o') else nvir0) for pos in block] - t0123 = [(r_occ if (pos == 'o') else r_vir) for pos in block] + shape0 = [(nocc0 if (pos == "o") else nvir0) for pos in block] + t0123 = [(r_occ if (pos == "o") else r_vir) for pos in block] pg = transform(g[:].reshape(shape0), *t0123) - if block == 'ovvv' and getattr(eris, block).ndim == 3: + if block == "ovvv" and getattr(eris, block).ndim == 3: pg = pack_ovvv(pg) - if block == 'vvvv' and getattr(eris, block).ndim == 2: + if block == "vvvv" and getattr(eris, block).ndim == 2: pg = pack_vvvv(pg) setattr(eris, block, pg) @@ -585,7 +593,8 @@ def project_ccsd_eris(eris, mo_coeff, nocc, ovlp, check_subspace=True): return eris -if __name__ == '__main__': +if __name__ == "__main__": + def test1(): import pyscf.gto import pyscf.scf @@ -594,11 +603,11 @@ def test1(): mol = pyscf.gto.Mole() mol.atom = water() - mol.basis = 'cc-pVDZ' + mol.basis = "cc-pVDZ" mol.build() hf = pyscf.scf.RHF(mol) - #hf.density_fit(auxbasis='cc-pVDZ-ri') + # hf.density_fit(auxbasis='cc-pVDZ-ri') hf.kernel() ccsd = pyscf.cc.CCSD(hf) @@ -606,7 +615,7 @@ def test1(): eris2 = get_full_array(eris) norb = hf.mo_coeff.shape[-1] - eris_test = pyscf.ao2mo.kernel(hf._eri, hf.mo_coeff, compact=False).reshape(4*[norb]) + eris_test = pyscf.ao2mo.kernel(hf._eri, hf.mo_coeff, compact=False).reshape(4 * [norb]) err = np.linalg.norm(eris2 - eris_test) print(err) assert np.allclose(eris2, eris_test) @@ -621,8 +630,8 @@ def test2(): cell.a, cell.atom = solids.diamond() cell.build() - #kmesh = [3,2,1] - kmesh = [4,5,2] + # kmesh = [3,2,1] + kmesh = [4, 5, 2] kpts = cell.get_kpts(kmesh) nk = 3 @@ -633,6 +642,6 @@ def test2(): kconserv_pyscf = pyscf.pbc.lib.kpts_helper.get_kconserv(cell, kpts, n=nk) t2 = timer() assert np.all(kconserv == kconserv_pyscf) - print(t1-t0, t2-t1) + print(t1 - t0, t2 - t1) test2() diff --git a/vayesta/core/ao2mo/kao2gmo.py b/vayesta/core/ao2mo/kao2gmo.py index 9087c4e1f..ca2ab4a0c 100644 --- a/vayesta/core/ao2mo/kao2gmo.py +++ b/vayesta/core/ao2mo/kao2gmo.py @@ -7,10 +7,12 @@ # Standard import ctypes import logging + # External import numpy as np import pyscf.pbc import pyscf.pbc.tools + # Package from vayesta.core.util import call_once, einsum, timer from vayesta.libs import libcore @@ -19,6 +21,7 @@ log = logging.getLogger(__name__) + def kao2gmo_cderi(gdf, mo_coeffs, make_real=True, blksize=None, tril_kij=True, driver=None): """Transform density-fitted ERIs from primtive cell k-AO, to Gamma-point MOs. @@ -67,21 +70,21 @@ def kao2gmo_cderi(gdf, mo_coeffs, make_real=True, blksize=None, tril_kij=True, d phase = (pyscf.pbc.tools.k2gamma.get_phase(cell, kpts)[1]).T if blksize is None: - max_memory = int(1e9) # 1 GB - max_size = int(max_memory / (nao**2 * 16)) # how many naux fit in max_memory + max_memory = int(1e9) # 1 GB + max_size = int(max_memory / (nao**2 * 16)) # how many naux fit in max_memory blksize = np.clip(max_size, 1, int(1e9)) - log.debugv("max_memory= %.3f MB max_size= %d blksize= %d", max_memory/1e6, max_size, blksize) + log.debugv("max_memory= %.3f MB max_size= %d blksize= %d", max_memory / 1e6, max_size, blksize) if driver is None: if libcore is None: - driver = 'python' + driver = "python" call_once(log.warning, "Libary 'vayesta/libs/libcore.so' not found, using fallback Python driver.") else: - driver = 'c' + driver = "c" log.debugv("Driver for kao2gmo_cderi= %s", driver) - if driver == 'python': - transform = lambda cderi_kij, mo1_ki, mo2_kj : einsum('Lab,ai,bj->Lij', cderi_kij, mo1_ki.conj(), mo2_kj) - elif driver == 'c': + if driver == "python": + transform = lambda cderi_kij, mo1_ki, mo2_kj: einsum("Lab,ai,bj->Lij", cderi_kij, mo1_ki.conj(), mo2_kj) + elif driver == "c": def transform(cderi_kij, mo1_ki, mo2_kj): naux = cderi_kij.shape[0] @@ -89,27 +92,28 @@ def transform(cderi_kij, mo1_ki, mo2_kj): nmo1 = mo1_ki.shape[-1] nmo2 = mo2_kj.shape[-1] buf = np.zeros((naux, nmo1, nmo2), dtype=complex) - cderi_kij = np.asarray(cderi_kij, order='C') - mo1_ki = np.asarray(mo1_ki.conj(), order='C') - mo2_kj = np.asarray(mo2_kj, order='C') + cderi_kij = np.asarray(cderi_kij, order="C") + mo1_ki = np.asarray(mo1_ki.conj(), order="C") + mo2_kj = np.asarray(mo2_kj, order="C") ierr = libcore.ao2mo_cderi( - # In - ctypes.c_int64(nao), - ctypes.c_int64(nmo1), - ctypes.c_int64(nmo2), - ctypes.c_int64(naux), - mo1_ki.ctypes.data_as(ctypes.c_void_p), - mo2_kj.ctypes.data_as(ctypes.c_void_p), - cderi_kij.ctypes.data_as(ctypes.c_void_p), - # Out - buf.ctypes.data_as(ctypes.c_void_p)) - assert (ierr == 0) + # In + ctypes.c_int64(nao), + ctypes.c_int64(nmo1), + ctypes.c_int64(nmo2), + ctypes.c_int64(naux), + mo1_ki.ctypes.data_as(ctypes.c_void_p), + mo2_kj.ctypes.data_as(ctypes.c_void_p), + cderi_kij.ctypes.data_as(ctypes.c_void_p), + # Out + buf.ctypes.data_as(ctypes.c_void_p), + ) + assert ierr == 0 return buf nmo1 = mo_coeffs[0].shape[-1] nmo2 = mo_coeffs[1].shape[-1] - mo1 = einsum('kR,Rai->kai', phase.conj(), mo_coeffs[0].reshape(nk,nao,nmo1)) - mo2 = einsum('kR,Rai->kai', phase.conj(), mo_coeffs[1].reshape(nk,nao,nmo2)) + mo1 = einsum("kR,Rai->kai", phase.conj(), mo_coeffs[0].reshape(nk, nao, nmo1)) + mo2 = einsum("kR,Rai->kai", phase.conj(), mo_coeffs[1].reshape(nk, nao, nmo2)) cderi_mo = np.zeros((nk, naux, nmo1, nmo2), dtype=complex) if cell.dimension < 3: @@ -119,34 +123,34 @@ def transform(cderi_kij, mo1_ki, mo2_kj): # Rare case that one of the MO sets is empty - exit early if (nmo1 * nmo2) == 0: - cderi_mo = cderi_mo.reshape((nk*naux, nmo1, nmo2)).real + cderi_mo = cderi_mo.reshape((nk * naux, nmo1, nmo2)).real if cderi_mo_neg is not None: cderi_mo_neg = cderi_mo_neg.real return cderi_mo, cderi_mo_neg for ki in range(nk): - kjmax = (ki+1) if tril_kij else nk + kjmax = (ki + 1) if tril_kij else nk for kj in range(kjmax): - kk = kconserv[ki,kj] + kk = kconserv[ki, kj] # Load entire 3c-integrals at k-point pair (ki, kj) into memory: kpts_ij = (kpts[ki], kpts[kj]) blk0 = 0 for labr, labi, sign in gdf.sr_loop(kpts_ij, compact=False, blksize=blksize): - blk1 = (blk0 + labr.shape[0]) + blk1 = blk0 + labr.shape[0] blk = np.s_[blk0:blk1] blk0 = blk1 - lab = (labr + 1j*labi).reshape(-1, nao, nao) - if (sign == 1): + lab = (labr + 1j * labi).reshape(-1, nao, nao) + if sign == 1: lij = transform(lab, mo1[ki], mo2[kj]) - cderi_mo[kk,blk] += lij + cderi_mo[kk, blk] += lij if tril_kij and (ki > kj): - kk = kconserv[kj,ki] - lij = transform(lab.transpose(0,2,1).conj(), mo1[kj], mo2[ki]) - cderi_mo[kk,blk] += lij + kk = kconserv[kj, ki] + lij = transform(lab.transpose(0, 2, 1).conj(), mo1[kj], mo2[ki]) + cderi_mo[kk, blk] += lij # For 2D systems: - elif (sign == -1): + elif sign == -1: assert (ki == kj) and (sign == -1) and (lab.shape[0] == 1) - cderi_mo_neg += einsum('Lab,ai,bj->Lij', lab, mo1[ki].conj(), mo2[kj]) + cderi_mo_neg += einsum("Lab,ai,bj->Lij", lab, mo1[ki].conj(), mo2[kj]) else: raise ValueError("Sign = %f" % sign) @@ -166,15 +170,15 @@ def transform(cderi_kij, mo1_ki, mo2_kj): log.debugv("Imaginary part of (L|ij)= %.2e", im) cderi_mo = cderi_mo.real if cderi_mo_neg is not None: - assert (abs(cderi_mo_neg.imag).max() < 1e-10) + assert abs(cderi_mo_neg.imag).max() < 1e-10 cderi_mo_neg = cderi_mo_neg.real - cderi_mo = cderi_mo.reshape(nk*naux, nmo1, nmo2) + cderi_mo = cderi_mo.reshape(nk * naux, nmo1, nmo2) return cderi_mo, cderi_mo_neg -if __name__ == '__main__': +if __name__ == "__main__": import pyscf.pbc import pyscf.pbc.gto import pyscf.pbc.df @@ -182,54 +186,55 @@ def transform(cderi_kij, mo1_ki, mo2_kj): import vayesta.core.ao2mo - import kao2gmo #import gdf_to_eris + import kao2gmo # import gdf_to_eris - card = 'D' + card = "D" cell = pyscf.pbc.gto.Cell() - cell.a = 3.0*np.eye(3) - cell.a[2,2] = 20.0 + cell.a = 3.0 * np.eye(3) + cell.a[2, 2] = 20.0 cell.atom = "He 0 0 0" - cell.basis = 'cc-pV%sZ' % card + cell.basis = "cc-pV%sZ" % card cell.dimension = 2 cell.build() - kmesh = [3,2,1] - #kmesh = [3,3,3] + kmesh = [3, 2, 1] + # kmesh = [3,3,3] kpts = cell.make_kpts(kmesh) gdf = pyscf.pbc.df.GDF(cell, kpts) - gdf.auxbasis = 'cc-pV%sZ-ri' % card + gdf.auxbasis = "cc-pV%sZ-ri" % card gdf.build() scell = pyscf.pbc.tools.super_cell(cell, kmesh) sgdf = pyscf.pbc.df.GDF(scell) - sgdf.auxbasis = 'cc-pV%sZ-ri' % card + sgdf.auxbasis = "cc-pV%sZ-ri" % card sgdf.build() print("Naux= %d" % sgdf.auxcell.nao) nao_sc = scell.nao - #mf = pyscf.pbc.dft.RKS(scell) - #mf.with_df = sgdf - #mf.max_cycle = 1 - #mf.kernel() - #mo_coeff = mf.mo_coeff + # mf = pyscf.pbc.dft.RKS(scell) + # mf.with_df = sgdf + # mf.max_cycle = 1 + # mf.kernel() + # mo_coeff = mf.mo_coeff mo_coeff = np.random.rand(nao_sc, nao_sc) # Exact t0 = timer() - seri = sgdf.ao2mo(mo_coeff, compact=False).reshape(4*[nao_sc]) - print("Time SC= %.4f" % (timer()-t0)) + seri = sgdf.ao2mo(mo_coeff, compact=False).reshape(4 * [nao_sc]) + print("Time SC= %.4f" % (timer() - t0)) # Old code nocc = 3 t0 = timer() eris_old = kao2gmo.gdf_to_eris(gdf, mo_coeff, nocc) - print("Time old= %.4f" % (timer()-t0)) + print("Time old= %.4f" % (timer() - t0)) - class Object(): + class Object: pass + eris_old_2 = Object() for key, val in eris_old.items(): setattr(eris_old_2, key, val) @@ -237,29 +242,29 @@ class Object(): eris_old_2.nocc = nocc eris_old = vayesta.core.ao2mo.helper.get_full_array(eris_old_2) - mo_occ = mo_coeff[:,:nocc] - mo_vir = mo_coeff[:,nocc:] + mo_occ = mo_coeff[:, :nocc] + mo_vir = mo_coeff[:, nocc:] t0 = timer() - cderi, cderi_neg = kao2gmo_cderi(gdf, (mo_coeff, mo_coeff), driver='python', make_real=True) - #cderi_oo, cderi_neg_oo = kao2gmo_gdf(gdf, (mo_occ, mo_occ), driver='python') - #cderi_ov, cderi_neg_ov = kao2gmo_gdf(gdf, (mo_occ, mo_vir), driver='python') - #cderi_vv, cderi_neg_vv = kao2gmo_gdf(gdf, (mo_vir, mo_vir), driver='python') - print("Time k(Py)= %.4f" % (timer()-t0)) - eri = einsum('Lij,Lkl->ijkl', cderi.conj(), cderi) + cderi, cderi_neg = kao2gmo_cderi(gdf, (mo_coeff, mo_coeff), driver="python", make_real=True) + # cderi_oo, cderi_neg_oo = kao2gmo_gdf(gdf, (mo_occ, mo_occ), driver='python') + # cderi_ov, cderi_neg_ov = kao2gmo_gdf(gdf, (mo_occ, mo_vir), driver='python') + # cderi_vv, cderi_neg_vv = kao2gmo_gdf(gdf, (mo_vir, mo_vir), driver='python') + print("Time k(Py)= %.4f" % (timer() - t0)) + eri = einsum("Lij,Lkl->ijkl", cderi.conj(), cderi) if cderi_neg is not None: - eri -= einsum('Lij,Lkl->ijkl', cderi_neg, cderi_neg) + eri -= einsum("Lij,Lkl->ijkl", cderi_neg, cderi_neg) t0 = timer() - cderi, cderi_neg = kao2gmo_cderi(gdf, (mo_coeff, mo_coeff), driver='c') - print("Time k(C)= %.4f" % (timer()-t0)) - eri2 = einsum('Lij,Lkl->ijkl', cderi ,cderi) + cderi, cderi_neg = kao2gmo_cderi(gdf, (mo_coeff, mo_coeff), driver="c") + print("Time k(C)= %.4f" % (timer() - t0)) + eri2 = einsum("Lij,Lkl->ijkl", cderi, cderi) if cderi_neg is not None: - eri2 -= einsum('Lij,Lkl->ijkl', cderi_neg, cderi_neg) + eri2 -= einsum("Lij,Lkl->ijkl", cderi_neg, cderi_neg) - #print(seri[0,0,0,:]) - #print(eri[0,0,0,:]) - #print(eris_old[0,0,0,:]) + # print(seri[0,0,0,:]) + # print(eri[0,0,0,:]) + # print(eris_old[0,0,0,:]) print(np.linalg.norm(eri - seri)) print(np.linalg.norm(eri2 - seri)) diff --git a/vayesta/core/ao2mo/postscf_ao2mo.py b/vayesta/core/ao2mo/postscf_ao2mo.py index 4189e33fa..bbafe99e1 100644 --- a/vayesta/core/ao2mo/postscf_ao2mo.py +++ b/vayesta/core/ao2mo/postscf_ao2mo.py @@ -20,7 +20,7 @@ log = logging.getLogger(__name__) -pyscf_version = [int(x) for x in pyscf.__version__.split('.')] +pyscf_version = [int(x) for x in pyscf.__version__.split(".")] pyscf_version_atleast_2_1 = np.all(np.asarray(pyscf_version) >= (2, 1, 0)) @@ -51,17 +51,17 @@ def postscf_ao2mo(postscf, mo_coeff=None, fock=None, mo_energy=None, e_hf=None): """ replace = {} if fock is not None: - replace['get_fock'] = (fock if callable(fock) else lambda *args, **kwargs: fock) + replace["get_fock"] = fock if callable(fock) else lambda *args, **kwargs: fock # e_hf does not need to be added to ERIs object in PySCF v2.1+ if e_hf is not None and not pyscf_version_atleast_2_1: - replace['energy_tot'] = (e_hf if callable(e_hf) else lambda *args, **kwargs: e_hf) - if (fock is not None and e_hf is not None): + replace["energy_tot"] = e_hf if callable(e_hf) else lambda *args, **kwargs: e_hf + if fock is not None and e_hf is not None: # make_rdm1 and get_veff are called within postscf.ao2mo, to generate # the Fock matrix and SCF energy - since we set these directly, # we can avoid performing any computation in these functions: do_nothing = lambda *args, **kwargs: None - replace['make_rdm1'] = do_nothing - replace['get_veff'] = do_nothing + replace["make_rdm1"] = do_nothing + replace["get_veff"] = do_nothing # Replace attributes in `replace` temporarily for the potscf.ao2mo call; # after the with statement, the attributes are reset to their intial values. @@ -73,6 +73,7 @@ def postscf_ao2mo(postscf, mo_coeff=None, fock=None, mo_energy=None, e_hf=None): return eris + def postscf_kao2gmo(postscf, gdf, fock, mo_energy, e_hf=None, mo_coeff=None): """k-AO to Gamma-MO transformation of ERIs for post-SCF calculations of supercells. @@ -110,7 +111,7 @@ def postscf_kao2gmo(postscf, gdf, fock, mo_energy, e_hf=None, mo_coeff=None): if isinstance(postscf, pyscf.mp.mp2.MP2): eris = MP2_ChemistsERIs() # Only occ-vir block needed for MP2 - mo_coeffs = (mo_coeff[:,occ], mo_coeff[:,vir]) + mo_coeffs = (mo_coeff[:, occ], mo_coeff[:, vir]) elif isinstance(postscf, pyscf.cc.rccsd.RCCSD): eris = RCCSD_ChemistsERIs() mo_coeffs = (mo_coeff, mo_coeff) @@ -144,21 +145,22 @@ def postscf_kao2gmo(postscf, gdf, fock, mo_energy, e_hf=None, mo_coeff=None): eris.ovov = _contract_cderi(cderi, cderi_neg) # CCSD else: - eris.oooo = _contract_cderi(cderi, cderi_neg, block='oooo', nocc=nocc) - eris.ovoo = _contract_cderi(cderi, cderi_neg, block='ovoo', nocc=nocc) - eris.oovv = _contract_cderi(cderi, cderi_neg, block='oovv', nocc=nocc) - eris.ovvo = _contract_cderi(cderi, cderi_neg, block='ovvo', nocc=nocc) - eris.ovov = _contract_cderi(cderi, cderi_neg, block='ovov', nocc=nocc) - eris.ovvv = _contract_cderi(cderi, cderi_neg, block='ovvv', nocc=nocc, pack_right=pack) + eris.oooo = _contract_cderi(cderi, cderi_neg, block="oooo", nocc=nocc) + eris.ovoo = _contract_cderi(cderi, cderi_neg, block="ovoo", nocc=nocc) + eris.oovv = _contract_cderi(cderi, cderi_neg, block="oovv", nocc=nocc) + eris.ovvo = _contract_cderi(cderi, cderi_neg, block="ovvo", nocc=nocc) + eris.ovov = _contract_cderi(cderi, cderi_neg, block="ovov", nocc=nocc) + eris.ovvv = _contract_cderi(cderi, cderi_neg, block="ovvv", nocc=nocc, pack_right=pack) if store_vvl: if cderi_neg is not None: raise ValueError("Cannot use DFCCSD for 2D systems!") - eris.vvL = pyscf.lib.pack_tril(cderi[:,vir,vir].copy()).T + eris.vvL = pyscf.lib.pack_tril(cderi[:, vir, vir].copy()).T else: - eris.vvvv = _contract_cderi(cderi, cderi_neg, block='vvvv', nocc=nocc, pack_left=pack, pack_right=pack) + eris.vvvv = _contract_cderi(cderi, cderi_neg, block="vvvv", nocc=nocc, pack_left=pack, pack_right=pack) return eris + def postscf_kao2gmo_uhf(postscf, gdf, fock, mo_energy, e_hf=None, mo_coeff=None): """k-AO to Gamma-MO transformation of ERIs for unrestricted post-SCF calculations of supercells. @@ -190,7 +192,7 @@ def postscf_kao2gmo_uhf(postscf, gdf, fock, mo_energy, e_hf=None, mo_coeff=None) mo_coeff = postscf.mo_coeff # Remove frozen orbitals: act = postscf.get_frozen_mask() - mo_coeff = (moa, mob) = (mo_coeff[0][:,act[0]], mo_coeff[1][:,act[1]]) + mo_coeff = (moa, mob) = (mo_coeff[0][:, act[0]], mo_coeff[1][:, act[1]]) nocc = (nocca, noccb) = postscf.nocc occa, vira = np.s_[:nocca], np.s_[nocca:] occb, virb = np.s_[:noccb], np.s_[noccb:] @@ -198,8 +200,8 @@ def postscf_kao2gmo_uhf(postscf, gdf, fock, mo_energy, e_hf=None, mo_coeff=None) if isinstance(postscf, pyscf.mp.ump2.UMP2): eris = MP2_ChemistsERIs() # Only occ-vir block needed for MP2 - mo_coeffs_a = (moa[:,occa], moa[:,vira]) - mo_coeffs_b = (mob[:,occb], mob[:,virb]) + mo_coeffs_a = (moa[:, occa], moa[:, vira]) + mo_coeffs_b = (mob[:, occb], mob[:, virb]) elif isinstance(postscf, (pyscf.ci.ucisd.UCISD, pyscf.cc.uccsd.UCCSD)): eris = UCCSD_ChemistsERIs() mo_coeffs_a = (moa, moa) @@ -231,114 +233,116 @@ def postscf_kao2gmo_uhf(postscf, gdf, fock, mo_energy, e_hf=None, mo_coeff=None) # CCSD else: # Alpha-alpha: - eris.oooo = _contract_cderi(cderia, cderia_neg, block='oooo', nocc=nocca) - eris.ovoo = _contract_cderi(cderia, cderia_neg, block='ovoo', nocc=nocca) - eris.ovvo = _contract_cderi(cderia, cderia_neg, block='ovvo', nocc=nocca) - eris.oovv = _contract_cderi(cderia, cderia_neg, block='oovv', nocc=nocca) - eris.ovov = _contract_cderi(cderia, cderia_neg, block='ovov', nocc=nocca) - eris.ovvv = _contract_cderi(cderia, cderia_neg, block='ovvv', nocc=nocca, pack_right=pack) - eris.vvvv = _contract_cderi(cderia, cderia_neg, block='vvvv', nocc=nocca, pack_left=pack, pack_right=pack) + eris.oooo = _contract_cderi(cderia, cderia_neg, block="oooo", nocc=nocca) + eris.ovoo = _contract_cderi(cderia, cderia_neg, block="ovoo", nocc=nocca) + eris.ovvo = _contract_cderi(cderia, cderia_neg, block="ovvo", nocc=nocca) + eris.oovv = _contract_cderi(cderia, cderia_neg, block="oovv", nocc=nocca) + eris.ovov = _contract_cderi(cderia, cderia_neg, block="ovov", nocc=nocca) + eris.ovvv = _contract_cderi(cderia, cderia_neg, block="ovvv", nocc=nocca, pack_right=pack) + eris.vvvv = _contract_cderi(cderia, cderia_neg, block="vvvv", nocc=nocca, pack_left=pack, pack_right=pack) # Beta-beta: - eris.OOOO = _contract_cderi(cderib, cderib_neg, block='oooo', nocc=noccb) - eris.OVOO = _contract_cderi(cderib, cderib_neg, block='ovoo', nocc=noccb) - eris.OVVO = _contract_cderi(cderib, cderib_neg, block='ovvo', nocc=noccb) - eris.OOVV = _contract_cderi(cderib, cderib_neg, block='oovv', nocc=noccb) - eris.OVOV = _contract_cderi(cderib, cderib_neg, block='ovov', nocc=noccb) - eris.OVVV = _contract_cderi(cderib, cderib_neg, block='ovvv', nocc=noccb, pack_right=pack) - eris.VVVV = _contract_cderi(cderib, cderib_neg, block='vvvv', nocc=noccb, pack_left=pack, pack_right=pack) + eris.OOOO = _contract_cderi(cderib, cderib_neg, block="oooo", nocc=noccb) + eris.OVOO = _contract_cderi(cderib, cderib_neg, block="ovoo", nocc=noccb) + eris.OVVO = _contract_cderi(cderib, cderib_neg, block="ovvo", nocc=noccb) + eris.OOVV = _contract_cderi(cderib, cderib_neg, block="oovv", nocc=noccb) + eris.OVOV = _contract_cderi(cderib, cderib_neg, block="ovov", nocc=noccb) + eris.OVVV = _contract_cderi(cderib, cderib_neg, block="ovvv", nocc=noccb, pack_right=pack) + eris.VVVV = _contract_cderi(cderib, cderib_neg, block="vvvv", nocc=noccb, pack_left=pack, pack_right=pack) # Alpha-beta: - eris.ooOO = _contract_cderi_mixed(cderi, cderi_neg, block='ooOO', nocc=nocc) - eris.ovOO = _contract_cderi_mixed(cderi, cderi_neg, block='ovOO', nocc=nocc) - eris.ovVO = _contract_cderi_mixed(cderi, cderi_neg, block='ovVO', nocc=nocc) - eris.ooVV = _contract_cderi_mixed(cderi, cderi_neg, block='ooVV', nocc=nocc) - eris.ovOV = _contract_cderi_mixed(cderi, cderi_neg, block='ovOV', nocc=nocc) - eris.ovVV = _contract_cderi_mixed(cderi, cderi_neg, block='ovVV', nocc=nocc, pack_right=pack) - eris.vvVV = _contract_cderi_mixed(cderi, cderi_neg, block='vvVV', nocc=nocc, pack_left=pack, pack_right=pack) + eris.ooOO = _contract_cderi_mixed(cderi, cderi_neg, block="ooOO", nocc=nocc) + eris.ovOO = _contract_cderi_mixed(cderi, cderi_neg, block="ovOO", nocc=nocc) + eris.ovVO = _contract_cderi_mixed(cderi, cderi_neg, block="ovVO", nocc=nocc) + eris.ooVV = _contract_cderi_mixed(cderi, cderi_neg, block="ooVV", nocc=nocc) + eris.ovOV = _contract_cderi_mixed(cderi, cderi_neg, block="ovOV", nocc=nocc) + eris.ovVV = _contract_cderi_mixed(cderi, cderi_neg, block="ovVV", nocc=nocc, pack_right=pack) + eris.vvVV = _contract_cderi_mixed(cderi, cderi_neg, block="vvVV", nocc=nocc, pack_left=pack, pack_right=pack) # Beta-Alpha: - eris.OVoo = _contract_cderi_mixed(cderi[::-1], cderi_neg[::-1], block='OVoo', nocc=nocc) - eris.OOvv = _contract_cderi_mixed(cderi[::-1], cderi_neg[::-1], block='OOvv', nocc=nocc) - eris.OVvo = _contract_cderi_mixed(cderi[::-1], cderi_neg[::-1], block='OVvo', nocc=nocc) - eris.OVvv = _contract_cderi_mixed(cderi[::-1], cderi_neg[::-1], block='OVvv', nocc=nocc, pack_right=pack) + eris.OVoo = _contract_cderi_mixed(cderi[::-1], cderi_neg[::-1], block="OVoo", nocc=nocc) + eris.OOvv = _contract_cderi_mixed(cderi[::-1], cderi_neg[::-1], block="OOvv", nocc=nocc) + eris.OVvo = _contract_cderi_mixed(cderi[::-1], cderi_neg[::-1], block="OVvo", nocc=nocc) + eris.OVvv = _contract_cderi_mixed(cderi[::-1], cderi_neg[::-1], block="OVvv", nocc=nocc, pack_right=pack) return eris + def _contract_cderi(cderi, cderi_neg, block=None, nocc=None, pack_left=False, pack_right=False, imag_tol=1e-8): if block is not None: - slices = {'o': np.s_[:nocc], 'v': np.s_[nocc:]} - get_slices = (lambda block : (slices[block[0]], slices[block[1]])) + slices = {"o": np.s_[:nocc], "v": np.s_[nocc:]} + get_slices = lambda block: (slices[block[0]], slices[block[1]]) si, sj = get_slices(block[:2]) sk, sl = get_slices(block[2:]) else: si = sj = sk = sl = np.s_[:] # Positive part - cderi_left = cderi[:,si,sj].copy() - cderi_right = cderi[:,sk,sl].copy() + cderi_left = cderi[:, si, sj].copy() + cderi_right = cderi[:, sk, sl].copy() if pack_left: cderi_left = pyscf.lib.pack_tril(cderi_left) if pack_right: cderi_right = pyscf.lib.pack_tril(cderi_right) eri = np.tensordot(cderi_left.conj(), cderi_right, axes=(0, 0)) - #log.debugv("Final shape of (%s|%s)= %r", block[:2], block[2:], list(eri.shape)) + # log.debugv("Final shape of (%s|%s)= %r", block[:2], block[2:], list(eri.shape)) if cderi_neg is None: return eri # Negative part (for 2D systems) - cderi_left = cderi_neg[:,si,sj] - cderi_right = cderi_neg[:,sk,sl] + cderi_left = cderi_neg[:, si, sj] + cderi_right = cderi_neg[:, sk, sl] if pack_left: cderi_left = pyscf.lib.pack_tril(cderi_left) if pack_right: cderi_right = pyscf.lib.pack_tril(cderi_right) # Avoid allocating another N^4 object: - max_memory = int(3e8) # 300 MB - nblks = int((eri.size * 8 * (1+np.iscomplexobj(eri)))/max_memory) + max_memory = int(3e8) # 300 MB + nblks = int((eri.size * 8 * (1 + np.iscomplexobj(eri))) / max_memory) size = cderi_left.shape[1] - blksize = int(size/max(nblks, 1)) - log.debugv("max_memory= %d MB nblks= %d size= %d blksize= %d", max_memory/1e6, nblks, size, blksize) + blksize = int(size / max(nblks, 1)) + log.debugv("max_memory= %d MB nblks= %d size= %d blksize= %d", max_memory / 1e6, nblks, size, blksize) for blk in brange(0, size, blksize): - eri[blk] -= np.tensordot(cderi_left[:,blk].conj(), cderi_right, axes=(0, 0)) - #eri -= np.tensordot(cderi_left.conj(), cderi_right, axes=(0, 0)) + eri[blk] -= np.tensordot(cderi_left[:, blk].conj(), cderi_right, axes=(0, 0)) + # eri -= np.tensordot(cderi_left.conj(), cderi_right, axes=(0, 0)) assert (eri.size == 0) or (abs(eri.imag).max() < imag_tol) return eri + def _contract_cderi_mixed(cderi, cderi_neg, block=None, nocc=None, pack_left=False, pack_right=False, imag_tol=1e-8): if block is not None: noccl, noccr = nocc - slices = {'o': np.s_[:noccl], 'v': np.s_[noccl:], 'O': np.s_[:noccr], 'V': np.s_[noccr:]} - get_slices = (lambda block : (slices[block[0]], slices[block[1]])) + slices = {"o": np.s_[:noccl], "v": np.s_[noccl:], "O": np.s_[:noccr], "V": np.s_[noccr:]} + get_slices = lambda block: (slices[block[0]], slices[block[1]]) si, sj = get_slices(block[:2]) sk, sl = get_slices(block[2:]) else: si = sj = sk = sl = np.s_[:] # Positive part - cderi_left = cderi[0][:,si,sj].copy() - cderi_right = cderi[1][:,sk,sl].copy() + cderi_left = cderi[0][:, si, sj].copy() + cderi_right = cderi[1][:, sk, sl].copy() if pack_left: cderi_left = pyscf.lib.pack_tril(cderi_left) if pack_right: cderi_right = pyscf.lib.pack_tril(cderi_right) eri = np.tensordot(cderi_left.conj(), cderi_right, axes=(0, 0)) - #log.debugv("Final shape of (%s|%s)= %r", block[:2], block[2:], list(eri.shape)) + # log.debugv("Final shape of (%s|%s)= %r", block[:2], block[2:], list(eri.shape)) if cderi_neg[0] is None: return eri # Negative part (for 2D systems) - cderi_left = cderi_neg[0][:,si,sj] - cderi_right = cderi_neg[1][:,sk,sl] + cderi_left = cderi_neg[0][:, si, sj] + cderi_right = cderi_neg[1][:, sk, sl] if pack_left: cderi_left = pyscf.lib.pack_tril(cderi_left) if pack_right: cderi_right = pyscf.lib.pack_tril(cderi_right) # Avoid allocating another N^4 object: - max_memory = int(3e8) # 300 MB - nblks = int((eri.size * 8 * (1+np.iscomplexobj(eri)))/max_memory) + max_memory = int(3e8) # 300 MB + nblks = int((eri.size * 8 * (1 + np.iscomplexobj(eri))) / max_memory) size = cderi_left.shape[1] - blksize = int(size/max(nblks, 1)) - log.debugv("max_memory= %d MB nblks= %d size= %d blksize= %d", max_memory/1e6, nblks, size, blksize) + blksize = int(size / max(nblks, 1)) + log.debugv("max_memory= %d MB nblks= %d size= %d blksize= %d", max_memory / 1e6, nblks, size, blksize) for blk in brange(0, size, blksize): - eri[blk] -= np.tensordot(cderi_left[:,blk].conj(), cderi_right, axes=(0, 0)) - #eri -= np.tensordot(cderi_left.conj(), cderi_right, axes=(0, 0)) + eri[blk] -= np.tensordot(cderi_left[:, blk].conj(), cderi_right, axes=(0, 0)) + # eri -= np.tensordot(cderi_left.conj(), cderi_right, axes=(0, 0)) assert (eri.size == 0) or (abs(eri.imag).max() < imag_tol) return eri diff --git a/vayesta/core/ao2mo/pyscf_eris.py b/vayesta/core/ao2mo/pyscf_eris.py index 9d8a46578..1e90651c3 100644 --- a/vayesta/core/ao2mo/pyscf_eris.py +++ b/vayesta/core/ao2mo/pyscf_eris.py @@ -7,19 +7,19 @@ def _pack_ovvv(ovvv): no, nva, nvb = ovvv.shape[:3] - nvbp = nvb*(nvb+1)//2 - ovvv = pyscf.lib.pack_tril(ovvv.reshape(no*nva,nvb,nvb)).reshape(no,nva,nvbp) + nvbp = nvb * (nvb + 1) // 2 + ovvv = pyscf.lib.pack_tril(ovvv.reshape(no * nva, nvb, nvb)).reshape(no, nva, nvbp) return ovvv def _pack_vvvv(vvvv): nva = vvvv.shape[0] - if np.all(np.asarray(vvvv.shape)==nva): + if np.all(np.asarray(vvvv.shape) == nva): return pyscf.ao2mo.restore(4, vvvv, nva) nvb = vvvv.shape[2] - nvap = nva*(nva+1)//2 - nvbp = nvb*(nvb+1)//2 - vvvv = pyscf.lib.pack_tril(vvvv.reshape(nva*nva, nvb, nvb)) + nvap = nva * (nva + 1) // 2 + nvbp = nvb * (nvb + 1) // 2 + vvvv = pyscf.lib.pack_tril(vvvv.reshape(nva * nva, nvb, nvb)) vvvv = pyscf.lib.pack_tril(vvvv.reshape(nva, nva, nvbp), axis=0) vvvv = vvvv.reshape(nvap, nvbp) return vvvv @@ -49,13 +49,13 @@ def make_ccsd_eris(fock, eris, nocc, mo_energy=None): nmo = fock.shape[-1] nvir = nmo - nocc o, v = np.s_[:nocc], np.s_[nocc:] - eris_out.oooo = eris[o,o,o,o] - eris_out.ovoo = eris[o,v,o,o] - eris_out.oovv = eris[o,o,v,v] - eris_out.ovvo = eris[o,v,v,o] - eris_out.ovov = eris[o,v,o,v] - eris_out.ovvv = _pack_ovvv(eris[o,v,v,v]) - eris_out.vvvv = _pack_vvvv(eris[v,v,v,v]) + eris_out.oooo = eris[o, o, o, o] + eris_out.ovoo = eris[o, v, o, o] + eris_out.oovv = eris[o, o, v, v] + eris_out.ovvo = eris[o, v, v, o] + eris_out.ovov = eris[o, v, o, v] + eris_out.ovvv = _pack_ovvv(eris[o, v, v, v]) + eris_out.vvvv = _pack_vvvv(eris[v, v, v, v]) eris_out.fock = fock eris_out.nocc = nocc if mo_energy is None: @@ -87,40 +87,40 @@ def make_uccsd_eris(fock, eris, nocc, mo_energy=None): """ eris_out = pyscf.cc.uccsd._ChemistsERIs() nmo = (fock[0].shape[-1], fock[1].shape[-1]) - nvir = (nmo[0]-nocc[0], nmo[1]-nocc[1]) - oa, va = np.s_[:nocc[0]], np.s_[nocc[0]:] - ob, vb = np.s_[:nocc[1]], np.s_[nocc[1]:] + nvir = (nmo[0] - nocc[0], nmo[1] - nocc[1]) + oa, va = np.s_[: nocc[0]], np.s_[nocc[0] :] + ob, vb = np.s_[: nocc[1]], np.s_[nocc[1] :] eris_aa, eris_ab, eris_bb = eris # Alpha-alpha - eris_out.oooo = eris_aa[oa,oa,oa,oa] - eris_out.ovoo = eris_aa[oa,va,oa,oa] - eris_out.oovv = eris_aa[oa,oa,va,va] - eris_out.ovvo = eris_aa[oa,va,va,oa] - eris_out.ovov = eris_aa[oa,va,oa,va] - eris_out.ovvv = _pack_ovvv(eris_aa[oa,va,va,va]) - eris_out.vvvv = _pack_vvvv(eris_aa[va,va,va,va]) + eris_out.oooo = eris_aa[oa, oa, oa, oa] + eris_out.ovoo = eris_aa[oa, va, oa, oa] + eris_out.oovv = eris_aa[oa, oa, va, va] + eris_out.ovvo = eris_aa[oa, va, va, oa] + eris_out.ovov = eris_aa[oa, va, oa, va] + eris_out.ovvv = _pack_ovvv(eris_aa[oa, va, va, va]) + eris_out.vvvv = _pack_vvvv(eris_aa[va, va, va, va]) # Beta-beta - eris_out.OOOO = eris_bb[ob,ob,ob,ob] - eris_out.OVOO = eris_bb[ob,vb,ob,ob] - eris_out.OOVV = eris_bb[ob,ob,vb,vb] - eris_out.OVVO = eris_bb[ob,vb,vb,ob] - eris_out.OVOV = eris_bb[ob,vb,ob,vb] - eris_out.OVVV = _pack_ovvv(eris_bb[ob,vb,vb,vb]) - eris_out.VVVV = _pack_vvvv(eris_bb[vb,vb,vb,vb]) + eris_out.OOOO = eris_bb[ob, ob, ob, ob] + eris_out.OVOO = eris_bb[ob, vb, ob, ob] + eris_out.OOVV = eris_bb[ob, ob, vb, vb] + eris_out.OVVO = eris_bb[ob, vb, vb, ob] + eris_out.OVOV = eris_bb[ob, vb, ob, vb] + eris_out.OVVV = _pack_ovvv(eris_bb[ob, vb, vb, vb]) + eris_out.VVVV = _pack_vvvv(eris_bb[vb, vb, vb, vb]) # Alpha-beta - eris_out.ooOO = eris_ab[oa,oa,ob,ob] - eris_out.ovOO = eris_ab[oa,va,ob,ob] - eris_out.ooVV = eris_ab[oa,oa,vb,vb] - eris_out.ovVO = eris_ab[oa,va,vb,ob] - eris_out.ovOV = eris_ab[oa,va,ob,vb] - eris_out.ovVV = _pack_ovvv(eris_ab[oa,va,vb,vb]) - eris_out.vvVV = _pack_vvvv(eris_ab[va,va,vb,vb]) + eris_out.ooOO = eris_ab[oa, oa, ob, ob] + eris_out.ovOO = eris_ab[oa, va, ob, ob] + eris_out.ooVV = eris_ab[oa, oa, vb, vb] + eris_out.ovVO = eris_ab[oa, va, vb, ob] + eris_out.ovOV = eris_ab[oa, va, ob, vb] + eris_out.ovVV = _pack_ovvv(eris_ab[oa, va, vb, vb]) + eris_out.vvVV = _pack_vvvv(eris_ab[va, va, vb, vb]) # Beta-alpha - eris_ba = eris_ab.transpose(2,3,0,1) - eris_out.OVoo = eris_ba[ob,vb,oa,oa] - eris_out.OOvv = eris_ba[ob,ob,va,va] - eris_out.OVvo = eris_ba[ob,vb,va,oa] - eris_out.OVvv = _pack_ovvv(eris_ba[ob,vb,va,va]) + eris_ba = eris_ab.transpose(2, 3, 0, 1) + eris_out.OVoo = eris_ba[ob, vb, oa, oa] + eris_out.OOvv = eris_ba[ob, ob, va, va] + eris_out.OVvo = eris_ba[ob, vb, va, oa] + eris_out.OVvv = _pack_ovvv(eris_ba[ob, vb, va, va]) # Other eris_out.focka = fock[0] eris_out.fockb = fock[1] diff --git a/vayesta/core/bath/__init__.py b/vayesta/core/bath/__init__.py index fb76ddc26..b39cd6e62 100644 --- a/vayesta/core/bath/__init__.py +++ b/vayesta/core/bath/__init__.py @@ -14,36 +14,42 @@ from vayesta.core.bath.r2bath import R2_Bath_RHF + def DMET_Bath(fragment, *args, **kwargs): if fragment.base.is_rhf: return DMET_Bath_RHF(fragment, *args, **kwargs) if fragment.base.is_uhf: return DMET_Bath_UHF(fragment, *args, **kwargs) + def EwDMET_Bath(fragment, *args, **kwargs): if fragment.base.is_rhf: return EwDMET_Bath_RHF(fragment, *args, **kwargs) if fragment.base.is_uhf: raise NotImplementedError + def MP2_Bath(fragment, *args, **kwargs): if fragment.base.is_rhf: return MP2_Bath_RHF(fragment, *args, **kwargs) if fragment.base.is_uhf: return MP2_Bath_UHF(fragment, *args, **kwargs) + def RPA_Bath(fragment, *args, **kwargs): if fragment.base.is_rhf: return RPA_BNO_Bath(fragment, *args, **kwargs) if fragment.base.is_uhf: raise NotImplementedError + def R2_Bath(fragment, *args, **kwargs): if fragment.base.is_rhf: return R2_Bath_RHF(fragment, *args, **kwargs) if fragment.base.is_uhf: raise NotImplementedError + def Full_Bath(fragment, *args, **kwargs): if fragment.base.is_rhf: return Full_Bath_RHF(fragment, *args, **kwargs) diff --git a/vayesta/core/bath/bath.py b/vayesta/core/bath/bath.py index a2011253c..44e2487a0 100644 --- a/vayesta/core/bath/bath.py +++ b/vayesta/core/bath/bath.py @@ -4,18 +4,17 @@ class Bath: - def __init__(self, fragment): self.fragment = fragment - assert (self.spin_restricted or self.spin_unrestricted) + assert self.spin_restricted or self.spin_unrestricted @property def spin_restricted(self): - return (np.ndim(self.mf.mo_coeff[0]) == 1) + return np.ndim(self.mf.mo_coeff[0]) == 1 @property def spin_unrestricted(self): - return (np.ndim(self.mf.mo_coeff[0]) == 2) + return np.ndim(self.mf.mo_coeff[0]) == 2 @property def spinsym(self): diff --git a/vayesta/core/bath/bno.py b/vayesta/core/bath/bno.py index 94df45491..71dd601e5 100644 --- a/vayesta/core/bath/bno.py +++ b/vayesta/core/bath/bno.py @@ -8,7 +8,6 @@ class BNO_Threshold: - def __init__(self, type, threshold): """ number: Fixed number of BNOs @@ -17,7 +16,7 @@ def __init__(self, type, threshold): electron-percent: Add BNOs until 100-x% of the total number of all electrons is captured excited-percent: Add BNOs until 100-x% of the total number of excited electrons is captured """ - if type not in ('number', 'occupation', 'truncation', 'electron-percent', 'excited-percent'): + if type not in ("number", "occupation", "truncation", "electron-percent", "excited-percent"): raise ValueError() self.type = type self.threshold = threshold @@ -28,28 +27,28 @@ def __repr__(self): def get_number(self, bno_occup, electron_total=None): """Get number of BNOs.""" nbno = len(bno_occup) - if (nbno == 0): + if nbno == 0: return 0 - if (self.type == 'number'): + if self.type == "number": return self.threshold - if (self.type in ('truncation', 'electron-percent', 'excited-percent')): + if self.type in ("truncation", "electron-percent", "excited-percent"): npos = np.clip(bno_occup, 0.0, None) nexcited = np.sum(npos) nelec0 = 0 - if self.type == 'truncation': - ntarget = (nexcited - self.threshold) - elif self.type == 'electron-percent': - assert (electron_total is not None) - ntarget = (1.0-self.threshold) * electron_total - nelec0 = (electron_total - nexcited) - elif self.type == 'excited-percent': - ntarget = (1.0-self.threshold) * nexcited - for bno_number in range(nbno+1): - nelec = (nelec0 + np.sum(npos[:bno_number])) + if self.type == "truncation": + ntarget = nexcited - self.threshold + elif self.type == "electron-percent": + assert electron_total is not None + ntarget = (1.0 - self.threshold) * electron_total + nelec0 = electron_total - nexcited + elif self.type == "excited-percent": + ntarget = (1.0 - self.threshold) * nexcited + for bno_number in range(nbno + 1): + nelec = nelec0 + np.sum(npos[:bno_number]) if nelec >= ntarget: return bno_number raise RuntimeError() - if (self.type == 'occupation'): + if self.type == "occupation": return np.count_nonzero(bno_occup >= self.threshold) raise RuntimeError() @@ -60,7 +59,7 @@ class BNO_Bath(Bath): def __init__(self, fragment, dmet_bath, occtype, *args, c_buffer=None, canonicalize=True, **kwargs): super().__init__(fragment, *args, **kwargs) self.dmet_bath = dmet_bath - if occtype not in ('occupied', 'virtual'): + if occtype not in ("occupied", "virtual"): raise ValueError("Invalid occtype: %s" % occtype) self.occtype = occtype self.c_buffer = c_buffer @@ -86,26 +85,26 @@ def make_bno_coeff(self, *args, **kwargs): @property def c_env(self): - if self.occtype == 'occupied': + if self.occtype == "occupied": return self.dmet_bath.c_env_occ - if self.occtype == 'virtual': + if self.occtype == "virtual": return self.dmet_bath.c_env_vir @property def ncluster(self): - if self.occtype == 'occupied': + if self.occtype == "occupied": return self.dmet_bath.c_cluster_occ.shape[-1] - if self.occtype == 'virtual': - return self.dmet_bath.c_cluster_vir.shape[-1] + if self.occtype == "virtual": + return self.dmet_bath.c_cluster_vir.shape[-1] def kernel(self): c_env = self.c_env if self.spin_restricted and (c_env.shape[-1] == 0): return c_env, np.zeros(0), None if self.spin_unrestricted and (c_env[0].shape[-1] + c_env[1].shape[-1] == 0): - return c_env, tuple(2*[np.zeros(0)]), None + return c_env, tuple(2 * [np.zeros(0)]), None self.log.info("Making %s BNOs", self.occtype.capitalize()) - self.log.info("-------%s-----", len(self.occtype)*'-') + self.log.info("-------%s-----", len(self.occtype) * "-") self.log.changeIndentLevel(1) coeff, occup, ecorr = self.make_bno_coeff() self.log_histogram(occup) @@ -119,7 +118,7 @@ def log_histogram(self, n_bno): return self.log.info("%s BNO histogram:", self.occtype.capitalize()) bins = np.hstack([-np.inf, np.logspace(-3, -10, 8)[::-1], np.inf]) - labels = ' ' + ''.join('{:{w}}'.format('E-%d' % d, w=5) for d in range(3, 11)) + labels = " " + "".join("{:{w}}".format("E-%d" % d, w=5) for d in range(3, 11)) self.log.info(helper.make_histogram(n_bno, bins=bins, labels=labels)) def get_bath(self, bno_threshold=None, **kwargs): @@ -146,19 +145,18 @@ def get_finite_bath_correction(self, c_active, c_frozen): c_active_vir = self.fragment.canonicalize_mo(actspace.c_active_vir, fock=fock)[0] else: c_active_vir = actspace.c_active_vir - actspace = Cluster.from_coeffs(c_active_occ, c_active_vir, - actspace.c_frozen_occ, actspace.c_frozen_vir) + actspace = Cluster.from_coeffs(c_active_occ, c_active_vir, actspace.c_frozen_occ, actspace.c_frozen_vir) e0 = self._make_t2(actspace, fock, energy_only=True)[1] - e_fbc = e1-e0 + e_fbc = e1 - e0 return e_fbc def truncate_bno(self, coeff, occup, bno_threshold=None, verbose=True): """Split natural orbitals (NO) into bath and rest.""" - header = '%s BNOs:' % self.occtype + header = "%s BNOs:" % self.occtype if isinstance(bno_threshold, numbers.Number): - bno_threshold = BNO_Threshold('occupation', bno_threshold) + bno_threshold = BNO_Threshold("occupation", bno_threshold) nelec_cluster = self.dmet_bath.get_cluster_electrons() bno_number = bno_threshold.get_number(occup, electron_total=nelec_cluster) @@ -167,13 +165,22 @@ def truncate_bno(self, coeff, occup, bno_threshold=None, verbose=True): if header: self.log.info(header.capitalize()) fmt = " %4s: N= %4d max= % 9.3g min= % 9.3g sum= % 9.3g ( %7.3f %%)" + def log_space(name, n_part): if len(n_part) == 0: - self.log.info(fmt[:fmt.index('max')].rstrip(), name, 0) + self.log.info(fmt[: fmt.index("max")].rstrip(), name, 0) return - with np.errstate(invalid='ignore'): # supress 0/0 warning - self.log.info(fmt, name, len(n_part), max(n_part), min(n_part), np.sum(n_part), - 100*np.sum(n_part)/np.sum(occup)) + with np.errstate(invalid="ignore"): # supress 0/0 warning + self.log.info( + fmt, + name, + len(n_part), + max(n_part), + min(n_part), + np.sum(n_part), + 100 * np.sum(n_part) / np.sum(occup), + ) + log_space("Bath", occup[:bno_number]) log_space("Rest", occup[bno_number:]) @@ -184,7 +191,7 @@ def get_active_space(self, c_active=None): dmet_bath = self.dmet_bath nao = self.mol.nao empty = np.zeros((nao, 0)) if self.spin_restricted else np.zeros((2, nao, 0)) - if self.occtype == 'occupied': + if self.occtype == "occupied": if c_active is None: c_active_occ = spinalg.hstack_matrices(dmet_bath.c_cluster_occ, self.c_env) else: @@ -194,7 +201,7 @@ def get_active_space(self, c_active=None): raise NotImplementedError c_active_vir = dmet_bath.c_cluster_vir c_frozen_vir = dmet_bath.c_env_vir - elif self.occtype == 'virtual': + elif self.occtype == "virtual": if c_active is None: c_active_vir = spinalg.hstack_matrices(dmet_bath.c_cluster_vir, self.c_env) else: @@ -209,7 +216,7 @@ def get_active_space(self, c_active=None): r = dot(self.c_buffer.T, ovlp, dmet_bath.c_env_occ) dm_frozen = np.eye(dmet_bath.c_env_occ.shape[-1]) - np.dot(r.T, r) e, r = np.linalg.eigh(dm_frozen) - c_frozen_occ = np.dot(dmet_bath.c_env_occ, r[:,e>0.5]) + c_frozen_occ = np.dot(dmet_bath.c_env_occ, r[:, e > 0.5]) actspace = Cluster.from_coeffs(c_active_occ, c_active_vir, c_frozen_occ, c_frozen_vir) return actspace @@ -221,7 +228,7 @@ def _dm_take_env(self, dm): ncluster = self.ncluster self.log.debugv("n(cluster)= %d", ncluster) self.log.debugv("tr(D)= %g", np.trace(dm)) - dm = dm[ncluster:,ncluster:] + dm = dm[ncluster:, ncluster:] self.log.debugv("tr(D[env,env])= %g", np.trace(dm)) return dm @@ -229,21 +236,19 @@ def _diagonalize_dm(self, dm): n_bno, r_bno = np.linalg.eigh(dm) sort = np.s_[::-1] n_bno = n_bno[sort] - r_bno = r_bno[:,sort] + r_bno = r_bno[:, sort] return r_bno, n_bno class BNO_Bath_UHF(BNO_Bath): - def _rotate_dm(self, dm, rot): - return (super()._rotate_dm(dm[0], rot[0]), - super()._rotate_dm(dm[1], rot[1])) + return (super()._rotate_dm(dm[0], rot[0]), super()._rotate_dm(dm[1], rot[1])) @property def ncluster(self): - if self.occtype == 'occupied': + if self.occtype == "occupied": return (self.dmet_bath.c_cluster_occ[0].shape[-1], self.dmet_bath.c_cluster_occ[1].shape[-1]) - if self.occtype == 'virtual': + if self.occtype == "virtual": return (self.dmet_bath.c_cluster_vir[0].shape[-1], self.dmet_bath.c_cluster_vir[1].shape[-1]) def _dm_take_env(self, dm): @@ -251,7 +256,7 @@ def _dm_take_env(self, dm): self.log.debugv("n(cluster)= (%d, %d)", ncluster[0], ncluster[1]) self.log.debugv("tr(alpha-D)= %g", np.trace(dm[0])) self.log.debugv("tr( beta-D)= %g", np.trace(dm[1])) - dm = (dm[0][ncluster[0]:,ncluster[0]:], dm[1][ncluster[1]:,ncluster[1]:]) + dm = (dm[0][ncluster[0] :, ncluster[0] :], dm[1][ncluster[1] :, ncluster[1] :]) self.log.debugv("tr(alpha-D[env,env])= %g", np.trace(dm[0])) self.log.debugv("tr( beta-D[env,env])= %g", np.trace(dm[1])) return dm @@ -266,11 +271,11 @@ def log_histogram(self, n_bno): return self.log.info("%s BNO histogram (alpha/beta):", self.occtype.capitalize()) bins = np.hstack([-np.inf, np.logspace(-3, -10, 8)[::-1], np.inf]) - labels = ' ' + ''.join('{:{w}}'.format('E-%d' % d, w=5) for d in range(3, 11)) - ha = helper.make_histogram(n_bno[0], bins=bins, labels=labels, rstrip=False).split('\n') - hb = helper.make_histogram(n_bno[1], bins=bins, labels=labels).split('\n') + labels = " " + "".join("{:{w}}".format("E-%d" % d, w=5) for d in range(3, 11)) + ha = helper.make_histogram(n_bno[0], bins=bins, labels=labels, rstrip=False).split("\n") + hb = helper.make_histogram(n_bno[1], bins=bins, labels=labels).split("\n") for i in range(len(ha)): - self.log.info(ha[i] + ' ' + hb[i]) + self.log.info(ha[i] + " " + hb[i]) def truncate_bno(self, coeff, occup, *args, **kwargs): c_bath_a, c_rest_a = super().truncate_bno(coeff[0], occup[0], *args, **kwargs) @@ -283,8 +288,7 @@ def _has_frozen(c_frozen): class MP2_BNO_Bath(BNO_Bath): - - def __init__(self, *args, project_dmet_order=0, project_dmet_mode='full', project_dmet=None, **kwargs): + def __init__(self, *args, project_dmet_order=0, project_dmet_mode="full", project_dmet=None, **kwargs): # Backwards compatibility: if project_dmet: project_dmet_order = 1 @@ -304,13 +308,13 @@ def _make_t2(self, actspace, fock, eris=None, max_memory=None, blksize=None, ene # (ov|ov) if eris is not None: self.log.debugv("Making T2 amplitudes from ERIs") - assert (eris.ndim == 4) + assert eris.ndim == 4 nocc, nvir = eris.shape[:2] # (L|ov) elif cderi is not None: self.log.debugv("Making T2 amplitudes from CD-ERIs") - assert (cderi.ndim == 3) - assert (cderi_neg is None or cderi_neg.ndim == 3) + assert cderi.ndim == 3 + assert cderi_neg is None or cderi_neg.ndim == 3 nocc, nvir = cderi.shape[1:] else: raise ValueError() @@ -321,39 +325,38 @@ def _make_t2(self, actspace, fock, eris=None, max_memory=None, blksize=None, ene t2 = np.empty((nocc, nocc, nvir, nvir)) if not energy_only else None mo_energy = self._get_mo_energy(fock, actspace) - eia = (mo_energy[:nocc,None] - mo_energy[None,nocc:]) + eia = mo_energy[:nocc, None] - mo_energy[None, nocc:] max_memory = max_memory or int(1e9) if blksize is None: - blksize = int(max_memory / max(nocc*nvir*nvir*8, 1)) - nenv = (nocc if self.occtype == 'occupied' else nvir) + blksize = int(max_memory / max(nocc * nvir * nvir * 8, 1)) + nenv = nocc if self.occtype == "occupied" else nvir ecorr = 0 for blk in brange(0, nocc, blksize): if eris is not None: - gijab = eris[blk].transpose(0,2,1,3) + gijab = eris[blk].transpose(0, 2, 1, 3) else: - gijab = einsum('Lia,Ljb->ijab', cderi[:,blk], cderi) + gijab = einsum("Lia,Ljb->ijab", cderi[:, blk], cderi) if cderi_neg is not None: - gijab -= einsum('Lia,Ljb->ijab', cderi_neg[:,blk], cderi_neg) - eijab = (eia[blk][:,None,:,None] + eia[None,:,None,:]) - t2blk = (gijab / eijab) + gijab -= einsum("Lia,Ljb->ijab", cderi_neg[:, blk], cderi_neg) + eijab = eia[blk][:, None, :, None] + eia[None, :, None, :] + t2blk = gijab / eijab if not energy_only: t2[blk] = t2blk # Projected correlation energy: - tp = einsum('ix,i...->x...', rfrag[blk], t2blk) - gp = einsum('ix,i...->x...', rfrag[blk], gijab) - ecorr += (2*einsum('ijab,ijab->', tp, gp) - - einsum('ijab,ijba->', tp, gp)) + tp = einsum("ix,i...->x...", rfrag[blk], t2blk) + gp = einsum("ix,i...->x...", rfrag[blk], gijab) + ecorr += 2 * einsum("ijab,ijab->", tp, gp) - einsum("ijab,ijba->", tp, gp) return t2, ecorr def _get_mo_energy(self, fock, actspace): c_act = actspace.c_active - mo_energy = einsum('ai,ab,bi->i', c_act, fock, c_act) + mo_energy = einsum("ai,ab,bi->i", c_act, fock, c_act) return mo_energy def _get_eris(self, actspace): # We only need the (ov|ov) block for MP2: - mo_coeff = 2*[actspace.c_active_occ, actspace.c_active_vir] + mo_coeff = 2 * [actspace.c_active_occ, actspace.c_active_vir] eris = self.base.get_eris_array(mo_coeff) return eris @@ -370,68 +373,69 @@ def get_eris_or_cderi(self, actspace): cderi, cderi_neg = self._get_cderi(actspace) else: eris = self._get_eris(actspace) - self.log.timingv("Time for AO->MO transformation: %s", time_string(timer()-t0)) + self.log.timingv("Time for AO->MO transformation: %s", time_string(timer() - t0)) # TODO: Reuse previously obtained integral transformation into N^2 sized quantity (rather than N^4) - #else: + # else: # self.log.debug("Transforming previous eris.") # eris = transform_mp2_eris(eris, actspace.c_active_occ, actspace.c_active_vir, ovlp=self.base.get_ovlp()) return eris, cderi, cderi_neg def _get_dmet_projector_weights(self, eig): assert np.all(eig > -1e-10) - assert np.all(eig-1 < 1e-10) + assert np.all(eig - 1 < 1e-10) eig = np.clip(eig, 0, 1) mode = self.project_dmet_mode - if mode == 'full': + if mode == "full": weights = np.zeros(len(eig)) - elif mode == 'half': + elif mode == "half": weights = np.full(len(eig), 0.5) - elif mode == 'linear': - weights = 2*abs(np.fmin(eig, 1-eig)) - elif mode == 'cosine': - weights = (1-np.cos(2*eig*np.pi))/2 - elif mode == 'cosine-half': - weights = (1-np.cos(2*eig*np.pi))/4 - elif mode == 'entropy': - weights = 4*eig*(1-eig) - elif mode == 'sqrt-entropy': - weights = 2*np.sqrt(eig*(1-eig)) - elif mode == 'squared-entropy': - weights = (4*eig*(1-eig))**2 + elif mode == "linear": + weights = 2 * abs(np.fmin(eig, 1 - eig)) + elif mode == "cosine": + weights = (1 - np.cos(2 * eig * np.pi)) / 2 + elif mode == "cosine-half": + weights = (1 - np.cos(2 * eig * np.pi)) / 4 + elif mode == "entropy": + weights = 4 * eig * (1 - eig) + elif mode == "sqrt-entropy": + weights = 2 * np.sqrt(eig * (1 - eig)) + elif mode == "squared-entropy": + weights = (4 * eig * (1 - eig)) ** 2 else: raise ValueError("Invalid value for project_dmet_mode: %s" % mode) assert np.all(weights > -1e-14) - assert np.all(weights-1 < 1e-14) + assert np.all(weights - 1 < 1e-14) weights = np.clip(weights, 0, 1) return weights def _project_t2(self, t2, actspace): """Project and symmetrize T2 amplitudes""" - self.log.info("Projecting DMET space for MP2 bath (mode= %s, order= %d).", - self.project_dmet_mode, self.project_dmet_order) + self.log.info( + "Projecting DMET space for MP2 bath (mode= %s, order= %d).", self.project_dmet_mode, self.project_dmet_order + ) weights = self._get_dmet_projector_weights(self.dmet_bath.n_dmet) - weights = hstack(self.fragment.n_frag*[1], weights) + weights = hstack(self.fragment.n_frag * [1], weights) ovlp = self.fragment.base.get_ovlp() c_fragdmet = hstack(self.fragment.c_frag, self.dmet_bath.c_dmet) - if self.occtype == 'occupied': + if self.occtype == "occupied": rot = dot(actspace.c_active_vir.T, ovlp, c_fragdmet) - proj = einsum('ix,x,jx->ij', rot, weights, rot) + proj = einsum("ix,x,jx->ij", rot, weights, rot) if self.project_dmet_order == 1: - t2 = einsum('xa,ijab->ijxb', proj, t2) + t2 = einsum("xa,ijab->ijxb", proj, t2) elif self.project_dmet_order == 2: - t2 = einsum('xa,yb,ijab->ijxy', proj, proj, t2) + t2 = einsum("xa,yb,ijab->ijxy", proj, proj, t2) else: raise ValueError - elif self.occtype == 'virtual': + elif self.occtype == "virtual": rot = dot(actspace.c_active_occ.T, ovlp, c_fragdmet) - proj = einsum('ix,x,jx->ij', rot, weights, rot) + proj = einsum("ix,x,jx->ij", rot, weights, rot) if self.project_dmet_order == 1: - t2 = einsum('xi,i...->x...', proj, t2) + t2 = einsum("xi,i...->x...", proj, t2) elif self.project_dmet_order == 2: - t2 = einsum('xi,yj,ij...->xy...', proj, proj, t2) + t2 = einsum("xi,yj,ij...->xy...", proj, proj, t2) else: raise ValueError - t2 = (t2 + t2.transpose(1,0,3,2))/2 + t2 = (t2 + t2.transpose(1, 0, 3, 2)) / 2 return t2 def make_delta_dm1(self, t2, actspace): @@ -443,12 +447,10 @@ def make_delta_dm1(self, t2, actspace): # This is equivalent to: # do, dv = pyscf.mp.mp2._gamma1_intermediates(mp2, eris=eris) # do, dv = -2*do, 2*dv - if self.occtype == 'occupied': - dm = (2*einsum('ikab,jkab->ij', t2, t2) - - einsum('ikab,jkba->ij', t2, t2)) - elif self.occtype == 'virtual': - dm = (2*einsum('ijac,ijbc->ab', t2, t2) - - einsum('ijac,ijcb->ab', t2, t2)) + if self.occtype == "occupied": + dm = 2 * einsum("ikab,jkab->ij", t2, t2) - einsum("ikab,jkba->ij", t2, t2) + elif self.occtype == "virtual": + dm = 2 * einsum("ijac,ijbc->ab", t2, t2) - einsum("ijac,ijcb->ab", t2, t2) assert np.allclose(dm, dm.T) return dm @@ -486,40 +488,42 @@ def make_bno_coeff(self, eris=None): else: c_active_vir = actspace_orig.c_active_vir r_vir = None - actspace = Cluster.from_coeffs(c_active_occ, c_active_vir, - actspace_orig.c_frozen_occ, actspace_orig.c_frozen_vir) + actspace = Cluster.from_coeffs( + c_active_occ, c_active_vir, actspace_orig.c_frozen_occ, actspace_orig.c_frozen_vir + ) t0 = timer() t2, ecorr = self._make_t2(actspace, fock, eris=eris) - t_amps = timer()-t0 + t_amps = timer() - t0 dm = self.make_delta_dm1(t2, actspace) # --- Undo canonicalization - if self.occtype == 'occupied' and r_occ is not None: + if self.occtype == "occupied" and r_occ is not None: dm = self._rotate_dm(dm, r_occ) - elif self.occtype == 'virtual' and r_vir is not None: + elif self.occtype == "virtual" and r_vir is not None: dm = self._rotate_dm(dm, r_vir) # --- Diagonalize environment-environment block dm = self._dm_take_env(dm) t0 = timer() r_bno, n_bno = self._diagonalize_dm(dm) - t_diag = timer()-t0 + t_diag = timer() - t0 c_bno = spinalg.dot(self.c_env, r_bno) c_bno = fix_orbital_sign(c_bno)[0] - self.log.timing("Time MP2 bath: amplitudes= %s diagonal.= %s total= %s", - *map(time_string, (t_amps, t_diag, (timer()-t_init)))) + self.log.timing( + "Time MP2 bath: amplitudes= %s diagonal.= %s total= %s", + *map(time_string, (t_amps, t_diag, (timer() - t_init))), + ) return c_bno, n_bno, ecorr class UMP2_BNO_Bath(MP2_BNO_Bath, BNO_Bath_UHF): - def _get_mo_energy(self, fock, actspace): c_act_a, c_act_b = actspace.c_active - mo_energy_a = einsum('ai,ab,bi->i', c_act_a, fock[0], c_act_a) - mo_energy_b = einsum('ai,ab,bi->i', c_act_b, fock[1], c_act_b) + mo_energy_a = einsum("ai,ab,bi->i", c_act_a, fock[0], c_act_a) + mo_energy_b = einsum("ai,ab,bi->i", c_act_b, fock[1], c_act_b) return (mo_energy_a, mo_energy_b) def _get_eris(self, actspace): @@ -542,16 +546,16 @@ def _make_t2(self, actspace, fock, eris=None, max_memory=None, blksize=None, ene # (ov|ov) if eris is not None: assert len(eris) == 3 - assert (eris[0].ndim == 4) - assert (eris[1].ndim == 4) - assert (eris[2].ndim == 4) + assert eris[0].ndim == 4 + assert eris[1].ndim == 4 + assert eris[2].ndim == 4 nocca, nvira = eris[0].shape[:2] noccb, nvirb = eris[2].shape[:2] # (L|ov) elif cderi is not None: assert len(cderi) == 2 - assert (cderi[0].ndim == 3) - assert (cderi[1].ndim == 3) + assert cderi[0].ndim == 3 + assert cderi[1].ndim == 3 nocca, nvira = cderi[0].shape[1:] noccb, nvirb = cderi[1].shape[1:] else: @@ -568,132 +572,131 @@ def _make_t2(self, actspace, fock, eris=None, max_memory=None, blksize=None, ene else: t2aa = t2ab = t2bb = None mo_energy = self._get_mo_energy(fock, actspace) - eia_a = (mo_energy[0][:nocca,None] - mo_energy[0][None,nocca:]) - eia_b = (mo_energy[1][:noccb,None] - mo_energy[1][None,noccb:]) + eia_a = mo_energy[0][:nocca, None] - mo_energy[0][None, nocca:] + eia_b = mo_energy[1][:noccb, None] - mo_energy[1][None, noccb:] # Alpha-alpha and Alpha-beta: max_memory = max_memory or int(1e9) if blksize is None: - blksize_a = int(max_memory / max(nocca*nvira*nvira * 8, 1)) + blksize_a = int(max_memory / max(nocca * nvira * nvira * 8, 1)) else: blksize_a = blksize ecorr = 0 for blk in brange(0, nocca, blksize_a): # Alpha-alpha if eris is not None: - gijab = eris[0][blk].transpose(0,2,1,3) + gijab = eris[0][blk].transpose(0, 2, 1, 3) else: - gijab = einsum('Lia,Ljb->ijab', cderi[0][:,blk], cderi[0]) + gijab = einsum("Lia,Ljb->ijab", cderi[0][:, blk], cderi[0]) if cderi_neg[0] is not None: - gijab -= einsum('Lia,Ljb->ijab', cderi_neg[0][:,blk], cderi_neg[0]) - eijab = (eia_a[blk][:,None,:,None] + eia_a[None,:,None,:]) - t2blk = (gijab / eijab) - t2blk -= t2blk.transpose(0,1,3,2) + gijab -= einsum("Lia,Ljb->ijab", cderi_neg[0][:, blk], cderi_neg[0]) + eijab = eia_a[blk][:, None, :, None] + eia_a[None, :, None, :] + t2blk = gijab / eijab + t2blk -= t2blk.transpose(0, 1, 3, 2) if not energy_only: t2aa[blk] = t2blk # Projected correlation energy: - tp = einsum('ix,i...->x...', rfrag[0][blk], t2blk) - gp = einsum('ix,i...->x...', rfrag[0][blk], gijab) - ecorr += (einsum('ijab,ijab->', tp, gp) - - einsum('ijab,ijba->', tp, gp))/4 + tp = einsum("ix,i...->x...", rfrag[0][blk], t2blk) + gp = einsum("ix,i...->x...", rfrag[0][blk], gijab) + ecorr += (einsum("ijab,ijab->", tp, gp) - einsum("ijab,ijba->", tp, gp)) / 4 # Alpha-beta if eris is not None: - gijab = eris[1][blk].transpose(0,2,1,3) + gijab = eris[1][blk].transpose(0, 2, 1, 3) else: - gijab = einsum('Lia,Ljb->ijab', cderi[0][:,blk], cderi[1]) + gijab = einsum("Lia,Ljb->ijab", cderi[0][:, blk], cderi[1]) if cderi_neg[0] is not None: - gijab -= einsum('Lia,Ljb->ijab', cderi_neg[0][:,blk], cderi_neg[1]) - eijab = (eia_a[blk][:,None,:,None] + eia_b[None,:,None,:]) - t2blk = (gijab / eijab) + gijab -= einsum("Lia,Ljb->ijab", cderi_neg[0][:, blk], cderi_neg[1]) + eijab = eia_a[blk][:, None, :, None] + eia_b[None, :, None, :] + t2blk = gijab / eijab if not energy_only: t2ab[blk] = t2blk # Projected correlation energy: # Alpha projected: - tp = einsum('ix,i...->x...', rfrag[0][blk], t2blk) - gp = einsum('ix,i...->x...', rfrag[0][blk], gijab) - ecorr += einsum('ijab,ijab->', tp, gp)/2 + tp = einsum("ix,i...->x...", rfrag[0][blk], t2blk) + gp = einsum("ix,i...->x...", rfrag[0][blk], gijab) + ecorr += einsum("ijab,ijab->", tp, gp) / 2 # Beta projected: - tp = einsum('jx,ij...->ix...', rfrag[1], t2blk) - gp = einsum('jx,ij...->ix...', rfrag[1], gijab) - ecorr += einsum('ijab,ijab->', tp, gp)/2 + tp = einsum("jx,ij...->ix...", rfrag[1], t2blk) + gp = einsum("jx,ij...->ix...", rfrag[1], gijab) + ecorr += einsum("ijab,ijab->", tp, gp) / 2 # Beta-beta: if blksize is None: - blksize_b = int(max_memory / max(noccb*nvirb*nvirb * 8, 1)) + blksize_b = int(max_memory / max(noccb * nvirb * nvirb * 8, 1)) else: blksize_b = blksize for blk in brange(0, noccb, blksize_b): if eris is not None: - gijab = eris[2][blk].transpose(0,2,1,3) + gijab = eris[2][blk].transpose(0, 2, 1, 3) else: - gijab = einsum('Lia,Ljb->ijab', cderi[1][:,blk], cderi[1]) + gijab = einsum("Lia,Ljb->ijab", cderi[1][:, blk], cderi[1]) if cderi_neg[0] is not None: - gijab -= einsum('Lia,Ljb->ijab', cderi_neg[1][:,blk], cderi_neg[1]) - eijab = (eia_b[blk][:,None,:,None] + eia_b[None,:,None,:]) - t2blk = (gijab / eijab) - t2blk -= t2blk.transpose(0,1,3,2) + gijab -= einsum("Lia,Ljb->ijab", cderi_neg[1][:, blk], cderi_neg[1]) + eijab = eia_b[blk][:, None, :, None] + eia_b[None, :, None, :] + t2blk = gijab / eijab + t2blk -= t2blk.transpose(0, 1, 3, 2) if not energy_only: t2bb[blk] = t2blk # Projected correlation energy: - tp = einsum('ix,i...->x...', rfrag[1][blk], t2blk) - gp = einsum('ix,i...->x...', rfrag[1][blk], gijab) - ecorr += (einsum('ijab,ijab->', tp, gp) - - einsum('ijab,ijba->', tp, gp))/4 + tp = einsum("ix,i...->x...", rfrag[1][blk], t2blk) + gp = einsum("ix,i...->x...", rfrag[1][blk], gijab) + ecorr += (einsum("ijab,ijab->", tp, gp) - einsum("ijab,ijba->", tp, gp)) / 4 return (t2aa, t2ab, t2bb), ecorr def _project_t2(self, t2, actspace): """Project and symmetrize T2 amplitudes""" - self.log.info("Projecting DMET space for MP2 bath (mode= %s, order= %d).", - self.project_dmet_mode, self.project_dmet_order) + self.log.info( + "Projecting DMET space for MP2 bath (mode= %s, order= %d).", self.project_dmet_mode, self.project_dmet_order + ) weightsa = self._get_dmet_projector_weights(self.dmet_bath.n_dmet[0]) weightsb = self._get_dmet_projector_weights(self.dmet_bath.n_dmet[1]) - weightsa = hstack(self.fragment.n_frag[0]*[1], weightsa) - weightsb = hstack(self.fragment.n_frag[1]*[1], weightsb) + weightsa = hstack(self.fragment.n_frag[0] * [1], weightsa) + weightsb = hstack(self.fragment.n_frag[1] * [1], weightsb) # Project and symmetrize: t2aa, t2ab, t2bb = t2 ovlp = self.fragment.base.get_ovlp() c_fragdmet_a = hstack(self.fragment.c_frag[0], self.dmet_bath.c_dmet[0]) c_fragdmet_b = hstack(self.fragment.c_frag[1], self.dmet_bath.c_dmet[1]) - if self.occtype == 'occupied': + if self.occtype == "occupied": rota = dot(actspace.c_active_vir[0].T, ovlp, c_fragdmet_a) rotb = dot(actspace.c_active_vir[1].T, ovlp, c_fragdmet_b) - proja = einsum('ix,x,jx->ij', rota, weightsa, rota) - projb = einsum('ix,x,jx->ij', rotb, weightsb, rotb) + proja = einsum("ix,x,jx->ij", rota, weightsa, rota) + projb = einsum("ix,x,jx->ij", rotb, weightsb, rotb) if self.project_dmet_order == 1: - t2aa = einsum('xa,ijab->ijxb', proja, t2aa) - t2bb = einsum('xa,ijab->ijxb', projb, t2bb) - t2ab = (einsum('xa,ijab->ijxb', proja, t2ab) - + einsum('xb,ijab->ijax', projb, t2ab))/2 + t2aa = einsum("xa,ijab->ijxb", proja, t2aa) + t2bb = einsum("xa,ijab->ijxb", projb, t2bb) + t2ab = (einsum("xa,ijab->ijxb", proja, t2ab) + einsum("xb,ijab->ijax", projb, t2ab)) / 2 # Not tested: elif self.project_dmet_order == 2: - t2aa = einsum('xa,yb,ijab->ijxy', proja, proja, t2aa) - t2bb = einsum('xa,yb,ijab->ijxy', projb, projb, t2bb) - t2ab = (einsum('xa,yb,ijab->ijxy', proja, projb, t2ab) - + einsum('xb,ya,ijab->ijyx', projb, proja, t2ab))/2 + t2aa = einsum("xa,yb,ijab->ijxy", proja, proja, t2aa) + t2bb = einsum("xa,yb,ijab->ijxy", projb, projb, t2bb) + t2ab = ( + einsum("xa,yb,ijab->ijxy", proja, projb, t2ab) + einsum("xb,ya,ijab->ijyx", projb, proja, t2ab) + ) / 2 else: raise ValueError - elif self.occtype == 'virtual': + elif self.occtype == "virtual": rota = dot(actspace.c_active_occ[0].T, ovlp, c_fragdmet_a) rotb = dot(actspace.c_active_occ[1].T, ovlp, c_fragdmet_b) - proja = einsum('ix,x,jx->ij', rota, weightsa, rota) - projb = einsum('ix,x,jx->ij', rotb, weightsb, rotb) + proja = einsum("ix,x,jx->ij", rota, weightsa, rota) + projb = einsum("ix,x,jx->ij", rotb, weightsb, rotb) if self.project_dmet_order == 1: - t2aa = einsum('xi,i...->x...', proja, t2aa) - t2bb = einsum('xi,i...->x...', projb, t2bb) - t2ab = (einsum('xi,i...->x...', proja, t2ab) - + einsum('xj,ij...->ix...', projb, t2ab))/2 + t2aa = einsum("xi,i...->x...", proja, t2aa) + t2bb = einsum("xi,i...->x...", projb, t2bb) + t2ab = (einsum("xi,i...->x...", proja, t2ab) + einsum("xj,ij...->ix...", projb, t2ab)) / 2 # Not tested: elif self.project_dmet_order == 2: - t2aa = einsum('xi,yj,ij...->xy...', proja, proja, t2aa) - t2bb = einsum('xi,yj,ij...->xy...', projb, projb, t2bb) - t2ab = (einsum('xi,yj,ij...->xy...', proja, projb, t2ab) - + einsum('xj,yi,ij...->yx...', projb, proja, t2ab))/2 + t2aa = einsum("xi,yj,ij...->xy...", proja, proja, t2aa) + t2bb = einsum("xi,yj,ij...->xy...", projb, projb, t2bb) + t2ab = ( + einsum("xi,yj,ij...->xy...", proja, projb, t2ab) + einsum("xj,yi,ij...->yx...", projb, proja, t2ab) + ) / 2 else: raise ValueError - t2aa = (t2aa + t2aa.transpose(1,0,3,2))/2 - t2bb = (t2bb + t2bb.transpose(1,0,3,2))/2 + t2aa = (t2aa + t2aa.transpose(1, 0, 3, 2)) / 2 + t2bb = (t2bb + t2bb.transpose(1, 0, 3, 2)) / 2 return (t2aa, t2ab, t2bb) def make_delta_dm1(self, t2, actspace): @@ -704,17 +707,13 @@ def make_delta_dm1(self, t2, actspace): t2aa, t2ab, t2bb = t2 # Construct occupied-occupied DM - if self.occtype == 'occupied': - dma = (einsum('imef,jmef->ij', t2aa.conj(), t2aa)/2 - + einsum('imef,jmef->ij', t2ab.conj(), t2ab)) - dmb = (einsum('imef,jmef->ij', t2bb.conj(), t2bb)/2 - + einsum('mief,mjef->ij', t2ab.conj(), t2ab)) + if self.occtype == "occupied": + dma = einsum("imef,jmef->ij", t2aa.conj(), t2aa) / 2 + einsum("imef,jmef->ij", t2ab.conj(), t2ab) + dmb = einsum("imef,jmef->ij", t2bb.conj(), t2bb) / 2 + einsum("mief,mjef->ij", t2ab.conj(), t2ab) # Construct virtual-virtual DM - elif self.occtype == 'virtual': - dma = (einsum('mnae,mnbe->ba', t2aa.conj(), t2aa)/2 - + einsum('mnae,mnbe->ba', t2ab.conj(), t2ab)) - dmb = (einsum('mnae,mnbe->ba', t2bb.conj(), t2bb)/2 - + einsum('mnea,mneb->ba', t2ab.conj(), t2ab)) + elif self.occtype == "virtual": + dma = einsum("mnae,mnbe->ba", t2aa.conj(), t2aa) / 2 + einsum("mnae,mnbe->ba", t2ab.conj(), t2ab) + dmb = einsum("mnae,mnbe->ba", t2bb.conj(), t2bb) / 2 + einsum("mnea,mneb->ba", t2ab.conj(), t2ab) assert np.allclose(dma, dma.T) assert np.allclose(dmb, dmb.T) return (dma, dmb) diff --git a/vayesta/core/bath/dmet.py b/vayesta/core/bath/dmet.py index fbecb3856..c0f38c07e 100644 --- a/vayesta/core/bath/dmet.py +++ b/vayesta/core/bath/dmet.py @@ -7,8 +7,8 @@ DEFAULT_DMET_THRESHOLD = 1e-6 -class DMET_Bath_RHF(Bath): +class DMET_Bath_RHF(Bath): def __init__(self, fragment, dmet_threshold=DEFAULT_DMET_THRESHOLD): super().__init__(fragment) self.dmet_threshold = dmet_threshold @@ -24,7 +24,7 @@ def __init__(self, fragment, dmet_threshold=DEFAULT_DMET_THRESHOLD): def get_cluster_electrons(self): """Number of cluster electrons.""" - return 2*self.c_cluster_occ.shape[-1] + return 2 * self.c_cluster_occ.shape[-1] def get_occupied_bath(self, *args, **kwargs): """Inherited bath classes can overwrite this to return additional occupied bath orbitals.""" @@ -46,18 +46,28 @@ def kernel(self): # --- Separate occupied and virtual in cluster cluster = [self.c_frag, c_dmet] - self.base._check_orthonormal(*cluster, mo_name='cluster MO') - tol = self.base.opts.bath_options['occupation_tolerance'] + self.base._check_orthonormal(*cluster, mo_name="cluster MO") + tol = self.base.opts.bath_options["occupation_tolerance"] c_cluster_occ, c_cluster_vir = self.fragment.diagonalize_cluster_dm(*cluster, tol=tol) # Canonicalize c_cluster_occ = self.fragment.canonicalize_mo(c_cluster_occ)[0] c_cluster_vir = self.fragment.canonicalize_mo(c_cluster_vir)[0] if self.base.is_rhf: - self.log.info("Cluster orbitals: n(occ)= %3d n(vir)= %3d", c_cluster_occ.shape[-1], c_cluster_vir.shape[-1]) + self.log.info( + "Cluster orbitals: n(occ)= %3d n(vir)= %3d", c_cluster_occ.shape[-1], c_cluster_vir.shape[-1] + ) else: - self.log.info("Alpha-cluster orbitals: n(occ)= %3d n(vir)= %3d", c_cluster_occ[0].shape[-1], c_cluster_vir[0].shape[-1]) - self.log.info(" Beta-cluster orbitals: n(occ)= %3d n(vir)= %3d", c_cluster_occ[1].shape[-1], c_cluster_vir[1].shape[-1]) - self.log.timing("Time for DMET bath: %s", time_string(timer()-t0)) + self.log.info( + "Alpha-cluster orbitals: n(occ)= %3d n(vir)= %3d", + c_cluster_occ[0].shape[-1], + c_cluster_vir[0].shape[-1], + ) + self.log.info( + " Beta-cluster orbitals: n(occ)= %3d n(vir)= %3d", + c_cluster_occ[1].shape[-1], + c_cluster_vir[1].shape[-1], + ) + self.log.timing("Time for DMET bath: %s", time_string(timer() - t0)) self.log.changeIndentLevel(-1) self.c_dmet = c_dmet @@ -117,17 +127,18 @@ def make_dmet_bath(self, c_env, dm1=None, c_ref=None, nbath=None, verbose=True, # Divide by 2 to get eigenvalues in [0,1] sc = np.dot(self.base.get_ovlp(), c_env) - if dm1 is None: dm1 = self.mf.make_rdm1() + if dm1 is None: + dm1 = self.mf.make_rdm1() dm_env = dot(sc.T, dm1, sc) / 2 try: eig, r = np.linalg.eigh(dm_env) except np.linalg.LinAlgError: eig, r = scipy.linalg.eigh(dm_env) # Sort: occ. env -> DMET bath -> vir. env - eig, r = eig[::-1], r[:,::-1] - if (eig.min() < -1e-8): + eig, r = eig[::-1], r[:, ::-1] + if eig.min() < -1e-8: self.log.error("Smallest eigenvalue of environment 1-DM below 0: n= %.10e !", eig.min()) - if ((eig.max()-1) > 1e-8): + if (eig.max() - 1) > 1e-8: self.log.error("Largest eigenvalue of environment 1-DM above 1: n= %.10e !", eig.max()) c_env = np.dot(c_env, r) c_env = fix_orbital_sign(c_env)[0] @@ -136,26 +147,28 @@ def make_dmet_bath(self, c_env, dm1=None, c_ref=None, nbath=None, verbose=True, # FIXME raise NotImplementedError() # Work out tolerance which leads to nbath bath orbitals. This overwrites `tol`. - abseig = abs(eig[np.argsort(abs(eig-0.5))]) - low, up = abseig[nbath-1], abseig[nbath] + abseig = abs(eig[np.argsort(abs(eig - 0.5))]) + low, up = abseig[nbath - 1], abseig[nbath] if abs(low - up) < 1e-14: - raise RuntimeError("Degeneracy in env. DM does not allow for clear identification of %d bath orbitals!\nabs(eig)= %r" - % (nbath, abseig[:nbath+5])) - tol = (low + up)/2 + raise RuntimeError( + "Degeneracy in env. DM does not allow for clear identification of %d bath orbitals!\nabs(eig)= %r" + % (nbath, abseig[: nbath + 5]) + ) + tol = (low + up) / 2 self.log.debugv("Tolerance for %3d bath orbitals= %.8g", nbath, tol) - mask_bath = np.logical_and(eig >= tol, eig <= 1-tol) - mask_occenv = (eig > 1-tol) - mask_virenv = (eig < tol) + mask_bath = np.logical_and(eig >= tol, eig <= 1 - tol) + mask_occenv = eig > 1 - tol + mask_virenv = eig < tol nbath = sum(mask_bath) noccenv = sum(mask_occenv) nvirenv = sum(mask_virenv) self.log.info("DMET bath: n(Bath)= %4d n(occ-Env)= %4d n(vir-Env)= %4d", nbath, noccenv, nvirenv) - assert (nbath + noccenv + nvirenv == c_env.shape[-1]) - c_bath = c_env[:,mask_bath].copy() - c_occenv = c_env[:,mask_occenv].copy() - c_virenv = c_env[:,mask_virenv].copy() + assert nbath + noccenv + nvirenv == c_env.shape[-1] + c_bath = c_env[:, mask_bath].copy() + c_occenv = c_env[:, mask_occenv].copy() + c_virenv = c_env[:, mask_virenv].copy() if verbose: self.log_info(eig, c_env) @@ -180,9 +193,9 @@ def make_dmet_bath_fast(self, c_env, dm1=None): rb = dot(c_occ.T, ovlp, cb) d11 = np.dot(ra.T, ra) ea, ua = np.linalg.eigh(d11) - if (ea.min() < -1e-9): + if ea.min() < -1e-9: self.log.error("Min eigenvalue of frag. DM = %.6e !", ea.min()) - if ((ea.max()-1) > 1e-9): + if (ea.max() - 1) > 1e-9: self.log.error("Max eigenvalue of frag. DM = %.6e !", ea.max()) # Fragment singular values: ea = np.clip(ea, 0, 1) @@ -190,38 +203,44 @@ def make_dmet_bath_fast(self, c_env, dm1=None): d21 = np.dot(rb.T, ra) ub = np.dot(d21, ua) sab = np.linalg.norm(ub, axis=0) - sb = sab/sa - mask_bath = (sb**2 >= self.dmet_threshold) - ub = ub[:,mask_bath] + sb = sab / sa + mask_bath = sb**2 >= self.dmet_threshold + ub = ub[:, mask_bath] # In AO basis - c_bath = np.dot(cb, ub/sab[mask_bath]) + c_bath = np.dot(cb, ub / sab[mask_bath]) return c_bath def log_info(self, eig, c_env, threshold=1e-10): tol = self.dmet_threshold - mask = np.logical_and(eig >= threshold, eig <= 1-threshold) + mask = np.logical_and(eig >= threshold, eig <= 1 - threshold) ovlp = self.base.get_ovlp() - maxocc = 2 if self.base.spinsym == 'restricted' else 1 + maxocc = 2 if self.base.spinsym == "restricted" else 1 if np.any(mask): self.log.info("Mean-field entangled orbitals:") self.log.info(" Bath Occupation Entanglement Character") - self.log.info(" ---- ---------- ------------ ------------------------------------------------------") + self.log.info( + " ---- ---------- ------------ ------------------------------------------------------" + ) for idx, e in enumerate(eig[mask]): - bath = 'Yes' if (tol <= e <= 1-tol) else 'No' - entang = 4*e*(1-e) + bath = "Yes" if (tol <= e <= 1 - tol) else "No" + entang = 4 * e * (1 - e) # Mulliken population of DMET orbital: - pop = einsum('a,b,ba->a', c_env[:,mask][:,idx], c_env[:,mask][:,idx], ovlp) + pop = einsum("a,b,ba->a", c_env[:, mask][:, idx], c_env[:, mask][:, idx], ovlp) sort = np.argsort(-pop) pop = pop[sort] - labels = np.asarray(self.mol.ao_labels(None))[sort][:min(len(pop), 4)] - char = ', '.join('%s %s%s (%.0f%%)' % (*(l[1:]), 100*pop[i]) for (i,l) in enumerate(labels)) - self.log.info(" %2d %4s %10.3g %12.3g %s", idx+1, bath, e*maxocc, entang, char) + labels = np.asarray(self.mol.ao_labels(None))[sort][: min(len(pop), 4)] + char = ", ".join("%s %s%s (%.0f%%)" % (*(l[1:]), 100 * pop[i]) for (i, l) in enumerate(labels)) + self.log.info(" %2d %4s %10.3g %12.3g %s", idx + 1, bath, e * maxocc, entang, char) # Calculate entanglement entropy - mask_bath = np.logical_and(eig >= tol, eig <= 1-tol) - entropy = np.sum(eig * (1-eig)) - entropy_bath = np.sum(eig[mask_bath] * (1-eig[mask_bath])) - self.log.info("Entanglement entropy: total= %.3e bath= %.3e (%.2f %%)", - entropy, entropy_bath, 100*entropy_bath/entropy) + mask_bath = np.logical_and(eig >= tol, eig <= 1 - tol) + entropy = np.sum(eig * (1 - eig)) + entropy_bath = np.sum(eig[mask_bath] * (1 - eig[mask_bath])) + self.log.info( + "Entanglement entropy: total= %.3e bath= %.3e (%.2f %%)", + entropy, + entropy_bath, + 100 * entropy_bath / entropy, + ) def use_ref_orbitals(self, c_bath, c_occenv, c_virenv, c_ref, reftol=0.8): """Not maintained!""" @@ -255,9 +274,9 @@ def use_ref_orbitals(self, c_bath, c_occenv, c_virenv, c_ref, reftol=0.8): if np.any(mask_virenv): self.log.debug("Largest remaining: %s", max(eig[mask_virenv])) # -- Update coefficient matrices - c_bath = np.hstack((c_bath, c_occenv[:,mask_occref], c_virenv[:,mask_virref])) - c_occenv = c_occenv[:,mask_occenv].copy() - c_virenv = c_virenv[:,mask_virenv].copy() + c_bath = np.hstack((c_bath, c_occenv[:, mask_occref], c_virenv[:, mask_virref])) + c_occenv = c_occenv[:, mask_occenv].copy() + c_virenv = c_virenv[:, mask_virenv].copy() nbath = c_bath.shape[-1] self.log.debug("New number of occupied environment orbitals: %d", c_occenv.shape[-1]) self.log.debug("New number of virtual environment orbitals: %d", c_virenv.shape[-1]) @@ -273,10 +292,9 @@ def use_ref_orbitals(self, c_bath, c_occenv, c_virenv, c_ref, reftol=0.8): class DMET_Bath_UHF(DMET_Bath_RHF): - def get_cluster_electrons(self): """Number of (alpha, beta) cluster electrons.""" - return (self.c_cluster_occ[0].shape[-1] + self.c_cluster_occ[1].shape[-1]) + return self.c_cluster_occ[0].shape[-1] + self.c_cluster_occ[1].shape[-1] def get_occupied_bath(self, *args, **kwargs): """Inherited bath classes can overwrite this to return additional occupied bath orbitals.""" @@ -289,10 +307,11 @@ def get_virtual_bath(self, *args, **kwargs): return np.zeros((2, nao, 0)), self.c_env_vir def make_dmet_bath(self, c_env, dm1=None, **kwargs): - if dm1 is None: dm1 = self.mf.make_rdm1() + if dm1 is None: + dm1 = self.mf.make_rdm1() results = [] - for s, spin in enumerate(('alpha', 'beta')): + for s, spin in enumerate(("alpha", "beta")): self.log.info("Making %s-DMET bath", spin) # Use restricted DMET bath routine for each spin: - results.append(super().make_dmet_bath(c_env[s], dm1=2*dm1[s], **kwargs)) + results.append(super().make_dmet_bath(c_env[s], dm1=2 * dm1[s], **kwargs)) return tuple(zip(*results)) diff --git a/vayesta/core/bath/ewdmet.py b/vayesta/core/bath/ewdmet.py index 861b70bfb..2de29da1a 100644 --- a/vayesta/core/bath/ewdmet.py +++ b/vayesta/core/bath/ewdmet.py @@ -6,11 +6,10 @@ class EwDMET_Bath_RHF(Bath): - def __init__(self, fragment, dmet_bath, occtype, *args, threshold=None, max_order=20, **kwargs): super().__init__(fragment, *args, **kwargs) self.dmet_bath = dmet_bath - if occtype not in ('occupied', 'virtual'): + if occtype not in ("occupied", "virtual"): raise ValueError("Invalid occtype: %s" % occtype) self.occtype = occtype if threshold is None: @@ -26,8 +25,8 @@ def get_fock(self, *args, **kwargs): def kernel(self): c_bath, sv, orders = self._make_svd() # Output - for order in range(1, self.max_order+1): - mask = (orders == order) + for order in range(1, self.max_order + 1): + mask = orders == order if np.count_nonzero(mask) == 0: break if order == 1: @@ -37,7 +36,7 @@ def kernel(self): def _make_svd(self): c_env = self.c_env - if (c_env.shape[-1] == 0): + if c_env.shape[-1] == 0: return c_env, np.zeros(0), np.zeros(0) c_frag = self.fragment.c_frag n_frag = c_frag.shape[-1] @@ -50,9 +49,9 @@ def _make_svd(self): @property def c_env(self): - if self.occtype == 'occupied': + if self.occtype == "occupied": return self.dmet_bath.c_env_occ - if self.occtype == 'virtual': + if self.occtype == "virtual": return self.dmet_bath.c_env_vir def get_bath(self, order): diff --git a/vayesta/core/bath/full.py b/vayesta/core/bath/full.py index 3944c2b87..ddbba6a59 100644 --- a/vayesta/core/bath/full.py +++ b/vayesta/core/bath/full.py @@ -3,19 +3,18 @@ class Full_Bath_RHF(Bath): - def __init__(self, fragment, dmet_bath, occtype, *args, **kwargs): super().__init__(fragment, *args, **kwargs) self.dmet_bath = dmet_bath - if occtype not in ('occupied', 'virtual'): + if occtype not in ("occupied", "virtual"): raise ValueError("Invalid occtype: %s" % occtype) self.occtype = occtype @property def c_env(self): - if self.occtype == 'occupied': + if self.occtype == "occupied": return self.dmet_bath.c_env_occ - if self.occtype == 'virtual': + if self.occtype == "virtual": return self.dmet_bath.c_env_vir def get_bath(self, *args, **kwargs): @@ -24,7 +23,6 @@ def get_bath(self, *args, **kwargs): class Full_Bath_UHF(Full_Bath_RHF): - def get_bath(self, *args, **kwargs): nao = self.c_env[0].shape[0] - return self.c_env, tuple(2*[np.zeros((nao, 0))]) + return self.c_env, tuple(2 * [np.zeros((nao, 0))]) diff --git a/vayesta/core/bath/helper.py b/vayesta/core/bath/helper.py index c33aaf706..02aec590b 100644 --- a/vayesta/core/bath/helper.py +++ b/vayesta/core/bath/helper.py @@ -2,54 +2,57 @@ from vayesta.core.util import einsum -def make_histogram(values, bins, labels=None, binwidth=5, height=6, fill=':', show_number=False, invertx=True, - rstrip=True): + +def make_histogram( + values, bins, labels=None, binwidth=5, height=6, fill=":", show_number=False, invertx=True, rstrip=True +): hist = np.histogram(values, bins)[0] if invertx: bins, hist = bins[::-1], hist[::-1] hmax = hist.max() - width = binwidth*len(hist) + width = binwidth * len(hist) plot = np.zeros((height + show_number, width), dtype=str) - plot[:] = ' ' - if hmax > 0: + plot[:] = " " + if hmax > 0: for i, hval in enumerate(hist): - colstart = i*binwidth - colend = (i+1)*binwidth - barheight = int(np.rint(height * hval/hmax)) + colstart = i * binwidth + colend = (i + 1) * binwidth + barheight = int(np.rint(height * hval / hmax)) if barheight == 0: continue # Top - plot[-barheight,colstart+1:colend-1] = '_' + plot[-barheight, colstart + 1 : colend - 1] = "_" if show_number: - number = ' {:^{w}s}'.format('(%d)' % hval, w=binwidth-1) + number = " {:^{w}s}".format("(%d)" % hval, w=binwidth - 1) for idx, i in enumerate(range(colstart, colend)): - plot[-barheight-1,i] = number[idx] + plot[-barheight - 1, i] = number[idx] if barheight == 1: continue # Fill if fill: - plot[-barheight+1:,colstart+1:colend] = fill + plot[-barheight + 1 :, colstart + 1 : colend] = fill # Left/right border - plot[-barheight+1:,colstart] = '|' - plot[-barheight+1:,colend-1] = '|' + plot[-barheight + 1 :, colstart] = "|" + plot[-barheight + 1 :, colend - 1] = "|" - lines = [''.join(plot[r,:].tolist()) for r in range(height)] + lines = ["".join(plot[r, :].tolist()) for r in range(height)] # Baseline - lines.append('+' + ((width-2) * '-') + '+') + lines.append("+" + ((width - 2) * "-") + "+") if labels: if isinstance(labels, str): lines += [labels] else: - lines += [''.join(['{:^{w}}'.format(l, w=binwidth) for l in labels])] + lines += ["".join(["{:^{w}}".format(l, w=binwidth) for l in labels])] if rstrip: lines = [line.rstrip() for line in lines] - txt = '\n'.join(lines) + txt = "\n".join(lines) return txt + def make_horizontal_histogram(values, bins=None, maxbarlength=50, invertx=True): if bins is None: bins = np.hstack([-np.inf, np.logspace(-3, -12, 10)[::-1], np.inf]) @@ -60,42 +63,41 @@ def make_horizontal_histogram(values, bins=None, maxbarlength=50, invertx=True): lines = [" {:^13s} {:^4s} {:^51s}".format("Interval", "Sum", "Histogram").rstrip()] for i, hval in enumerate(hist): cumsum += hval - barlength = int(maxbarlength * hval/hist.max()) + barlength = int(maxbarlength * hval / hist.max()) if hval == 0: bar = "" else: barlength = max(barlength, 1) - bar = ((barlength-1) * "|") + "]" + (" (%d)" % hval) - #log.info(" %5.0e - %5.0e %4d |%s", bins[i+1], bins[i], cumsum, bar) - lines.append(" %5.0e - %5.0e %4d |%s" % (bins[i+1], bins[i], cumsum, bar)) - txt = '\n'.join(lines) + bar = ((barlength - 1) * "|") + "]" + (" (%d)" % hval) + # log.info(" %5.0e - %5.0e %4d |%s", bins[i+1], bins[i], cumsum, bar) + lines.append(" %5.0e - %5.0e %4d |%s" % (bins[i + 1], bins[i], cumsum, bar)) + txt = "\n".join(lines) return txt + def transform_mp2_eris(eris, c_occ, c_vir, ovlp): # pragma: no cover """Transform eris of kind (ov|ov) (occupied-virtual-occupied-virtual) OBSOLETE: replaced by transform_eris """ - assert (eris is not None) - assert (eris.ovov is not None) + assert eris is not None + assert eris.ovov is not None c_occ0, c_vir0 = np.hsplit(eris.mo_coeff, [eris.nocc]) nocc0, nvir0 = c_occ0.shape[-1], c_vir0.shape[-1] nocc, nvir = c_occ.shape[-1], c_vir.shape[-1] - transform_occ = (nocc != nocc0 or not np.allclose(c_occ, c_occ0)) + transform_occ = nocc != nocc0 or not np.allclose(c_occ, c_occ0) if transform_occ: r_occ = np.linalg.multi_dot((c_occ.T, ovlp, c_occ0)) else: r_occ = np.eye(nocc) - transform_vir = (nvir != nvir0 or not np.allclose(c_vir, c_vir0)) + transform_vir = nvir != nvir0 or not np.allclose(c_vir, c_vir0) if transform_vir: r_vir = np.linalg.multi_dot((c_vir.T, ovlp, c_vir0)) else: r_vir = np.eye(nvir) - r_all = np.block([ - [r_occ, np.zeros((nocc, nvir0))], - [np.zeros((nvir, nocc0)), r_vir]]) + r_all = np.block([[r_occ, np.zeros((nocc, nvir0))], [np.zeros((nvir, nocc0)), r_vir]]) # eris.ovov may be hfd5 dataset on disk -> allocate in memory with [:] govov = eris.ovov[:].reshape(nocc0, nvir0, nocc0, nvir0) @@ -105,17 +107,18 @@ def transform_mp2_eris(eris, c_occ, c_vir, ovlp): # pragma: no cover govov = einsum("iajb,xi,zj->xazb", govov, r_occ, r_occ) elif transform_vir: govov = einsum("iajb,ya,wb->iyjw", govov, r_vir, r_vir) - eris.ovov = govov.reshape((nocc*nvir, nocc*nvir)) + eris.ovov = govov.reshape((nocc * nvir, nocc * nvir)) eris.mo_coeff = np.hstack((c_occ, c_vir)) eris.fock = np.linalg.multi_dot((r_all, eris.fock, r_all.T)) eris.mo_energy = np.diag(eris.fock) return eris -if __name__ == '__main__': + +if __name__ == "__main__": vals = sorted(np.random.rand(30)) print(make_vertical_histogram(vals)) - print('') + print("") bins = np.linspace(0, 1, 12) - #for line in horizontal_histogram(vals, bins): - labels = ' ' + ''.join('{:{w}}'.format('E-%d' % d, w=6) for d in range(3, 13)) + # for line in horizontal_histogram(vals, bins): + labels = " " + "".join("{:{w}}".format("E-%d" % d, w=6) for d in range(3, 13)) print(make_histogram(vals, bins, labels=labels)) diff --git a/vayesta/core/bath/r2bath.py b/vayesta/core/bath/r2bath.py index 031665c2f..481d9afdb 100644 --- a/vayesta/core/bath/r2bath.py +++ b/vayesta/core/bath/r2bath.py @@ -7,62 +7,64 @@ BOHR = 0.529177210903 + def _to_bohr(rcut, unit): unit = unit.lower() - if unit.startswith('ang'): - return rcut/BOHR - if unit.startswith('b'): + if unit.startswith("ang"): + return rcut / BOHR + if unit.startswith("b"): return rcut raise ValueError("Invalid unit: %s" % unit) + def _get_r2(mol, center, mesh=None): - if getattr(mol, 'dimension', 0) == 0: + if getattr(mol, "dimension", 0) == 0: # TODO: instead of evaluating for each center R, # use = - 2* + ^2 with mol.with_common_origin(center): - return mol.intor_symmetric('int1e_r2') + return mol.intor_symmetric("int1e_r2") # For PBC: # Numerical integration over unit cell if mesh is None: - mesh = 3*[100] - dx, dy, dz = 1/(2*np.asarray(mesh)) - x = np.linspace(-0.5+dx, 0.5-dx, mesh[0]) - y = np.linspace(-0.5+dy, 0.5-dy, mesh[1]) - z = np.linspace(-0.5+dz, 0.5-dz, mesh[2]) - mx, my, mz = np.meshgrid(x, y, z, indexing='ij') + mesh = 3 * [100] + dx, dy, dz = 1 / (2 * np.asarray(mesh)) + x = np.linspace(-0.5 + dx, 0.5 - dx, mesh[0]) + y = np.linspace(-0.5 + dy, 0.5 - dy, mesh[1]) + z = np.linspace(-0.5 + dz, 0.5 - dz, mesh[2]) + mx, my, mz = np.meshgrid(x, y, z, indexing="ij") grid = np.stack((mx.flatten(), my.flatten(), mz.flatten()), axis=1) coords = np.dot(grid, mol.lattice_vectors()) # Instead of: - #coords = mol.get_uniform_grids(mesh)) + # coords = mol.get_uniform_grids(mesh)) assert not mol.cart # We shift the coords around the center: - gtoval = mol.pbc_eval_gto('GTOval_sph', coords + center) - r2norm = np.linalg.norm(coords, axis=1)**2 - dvol = mol.vol/len(coords) - r2 = dvol*einsum('xa,x,xb->ab', gtoval, r2norm, gtoval) + gtoval = mol.pbc_eval_gto("GTOval_sph", coords + center) + r2norm = np.linalg.norm(coords, axis=1) ** 2 + dvol = mol.vol / len(coords) + r2 = dvol * einsum("xa,x,xb->ab", gtoval, r2norm, gtoval) return r2 -class R2_Bath_RHF(Bath): +class R2_Bath_RHF(Bath): def __init__(self, fragment, dmet_bath, occtype, *args, **kwargs): super().__init__(fragment, *args, **kwargs) self.dmet_bath = dmet_bath - if occtype not in ('occupied', 'virtual'): + if occtype not in ("occupied", "virtual"): raise ValueError("Invalid occtype: %s" % occtype) self.occtype = occtype if len(self.fragment.atoms) != 1: raise NotImplementedError atom = self.fragment.atoms[0] - self.center = self.mol.atom_coord(atom) # In Bohr! + self.center = self.mol.atom_coord(atom) # In Bohr! self.coeff, self.eig = self.kernel() @property def c_env(self): - if self.occtype == 'occupied': + if self.occtype == "occupied": return self.dmet_bath.c_env_occ - if self.occtype == 'virtual': + if self.occtype == "virtual": return self.dmet_bath.c_env_vir def get_r2(self): @@ -71,7 +73,7 @@ def get_r2(self): hermierr = np.linalg.norm(r2 - r2.T) if hermierr > 1e-11: self.log.warning("Hermiticity error= %.3e", hermierr) - r2 = (r2 + r2.T)/2 + r2 = (r2 + r2.T) / 2 else: self.log.debug("Hermiticity error= %.3e", hermierr) return r2 @@ -79,21 +81,22 @@ def get_r2(self): def kernel(self): t_init = t0 = timer() r2 = self.get_r2() - t_r2 = timer()-t0 + t_r2 = timer() - t0 t0 = timer() eig, rot = np.linalg.eigh(r2) - t_diag = timer()-t0 + t_diag = timer() - t0 if np.any(eig < -1e-13): - raise RuntimeError("Negative eigenvalues: %r" % eig[eig<0]) + raise RuntimeError("Negative eigenvalues: %r" % eig[eig < 0]) eig = np.sqrt(np.clip(eig, 0, None)) coeff = np.dot(self.c_env, rot) - self.log.debug("%s eigenvalues (A):\n%r", self.occtype.capitalize(), eig*BOHR) + self.log.debug("%s eigenvalues (A):\n%r", self.occtype.capitalize(), eig * BOHR) self.log_histogram(eig, self.occtype) - self.log.timing("Time R2 bath: R2= %s diagonal.= %s total= %s", - *map(time_string, (t_r2, t_diag, (timer()-t_init)))) + self.log.timing( + "Time R2 bath: R2= %s diagonal.= %s total= %s", *map(time_string, (t_r2, t_diag, (timer() - t_init))) + ) return coeff, eig - def get_bath(self, rcut, unit='Ang'): + def get_bath(self, rcut, unit="Ang"): rcut = _to_bohr(rcut, unit) nbath = np.count_nonzero(self.eig <= rcut) c_bath, c_rest = np.hsplit(self.coeff, [nbath]) diff --git a/vayesta/core/bath/rpa.py b/vayesta/core/bath/rpa.py index be9f7d55f..b1b7b329d 100644 --- a/vayesta/core/bath/rpa.py +++ b/vayesta/core/bath/rpa.py @@ -12,14 +12,13 @@ import numpy as np -class RPA_BNO_Bath(BNO_Bath): - def __init__(self, *args, project_dmet_order=0, project_dmet_mode='full', project_dmet=None, **kwargs): +class RPA_BNO_Bath(BNO_Bath): + def __init__(self, *args, project_dmet_order=0, project_dmet_mode="full", project_dmet=None, **kwargs): self.project_dmet_order = project_dmet_order self.project_dmet_mode = project_dmet_mode super().__init__(*args, **kwargs) - def make_bno_coeff(self, cderis=None): """Construct RPA bath natural orbital coefficients and occupation numbers. @@ -43,14 +42,26 @@ def make_bno_coeff(self, cderis=None): cderis = get_cderi(self.base, (self.base.mo_coeff_occ, self.base.mo_coeff_vir), compact=False) if self.occtype == "occupied": - proj = dot(self.dmet_bath.c_cluster_vir.T, self.base.get_ovlp(), self.fragment.c_frag, - self.fragment.c_frag.T, self.base.get_ovlp(), self.dmet_bath.c_cluster_vir) + proj = dot( + self.dmet_bath.c_cluster_vir.T, + self.base.get_ovlp(), + self.fragment.c_frag, + self.fragment.c_frag.T, + self.base.get_ovlp(), + self.dmet_bath.c_cluster_vir, + ) rot_vir = dot(self.dmet_bath.c_cluster_vir.T, self.base.get_ovlp(), self.base.mo_coeff_vir) rot_occ = np.eye(self.base.nocc) else: - proj = dot(self.dmet_bath.c_cluster_occ.T, self.base.get_ovlp(), self.fragment.c_frag, self.fragment.c_frag.T, - self.base.get_ovlp(), self.dmet_bath.c_cluster_occ) + proj = dot( + self.dmet_bath.c_cluster_occ.T, + self.base.get_ovlp(), + self.fragment.c_frag, + self.fragment.c_frag.T, + self.base.get_ovlp(), + self.dmet_bath.c_cluster_occ, + ) rot_occ = dot(self.dmet_bath.c_cluster_occ.T, self.base.get_ovlp(), self.base.mo_coeff_occ) rot_vir = np.eye(self.base.nvir) @@ -72,23 +83,29 @@ def make_bno_coeff(self, cderis=None): corr_dm = einsum("iajb,ab->ij", m0, proj) else: corr_dm = einsum("iajb,ij->ab", m0, proj) - t_eval = timer()-t0 + t_eval = timer() - t0 corr_dm = (corr_dm + corr_dm.T) / 2 # --- Diagonalize environment-environment block - if self.occtype == 'occupied': - corr_dm = self._rotate_dm(corr_dm, dot(self.dmet_bath.c_env_occ.T, self.base.get_ovlp(), self.base.mo_coeff_occ)) - elif self.occtype == 'virtual': - corr_dm = self._rotate_dm(corr_dm, dot(self.dmet_bath.c_env_vir.T, self.base.get_ovlp(), self.base.mo_coeff_vir)) + if self.occtype == "occupied": + corr_dm = self._rotate_dm( + corr_dm, dot(self.dmet_bath.c_env_occ.T, self.base.get_ovlp(), self.base.mo_coeff_occ) + ) + elif self.occtype == "virtual": + corr_dm = self._rotate_dm( + corr_dm, dot(self.dmet_bath.c_env_vir.T, self.base.get_ovlp(), self.base.mo_coeff_vir) + ) t0 = timer() r_bno, n_bno = self._diagonalize_dm(corr_dm) - t_diag = timer()-t0 + t_diag = timer() - t0 c_bno = spinalg.dot(self.c_env, r_bno) c_bno = fix_orbital_sign(c_bno)[0] - self.log.timing("Time RPA bath: evaluation= %s diagonal.= %s total= %s", - *map(time_string, (t_eval, t_diag, (timer()-t_init)))) + self.log.timing( + "Time RPA bath: evaluation= %s diagonal.= %s total= %s", + *map(time_string, (t_eval, t_diag, (timer() - t_init))), + ) if min(n_bno) < 0.0: self.log.critical("Negative bath occupation number encountered: %s", n_bno) diff --git a/vayesta/core/bosonic_bath/bbath.py b/vayesta/core/bosonic_bath/bbath.py index a05b3a644..859ba1a27 100644 --- a/vayesta/core/bosonic_bath/bbath.py +++ b/vayesta/core/bosonic_bath/bbath.py @@ -7,10 +7,11 @@ class Boson_Threshold(BNO_Threshold): def __init__(self, type, threshold): - if type in ('electron-percent', 'excited-percent'): + if type in ("electron-percent", "excited-percent"): raise ValueError("Electron-percent and excited-percent are not supported truncations for bosonic baths.") super().__init__(type, threshold) + class Bosonic_Bath(Bath): def __init__(self, fragment): super().__init__(fragment) @@ -18,8 +19,8 @@ def __init__(self, fragment): @property def cluster_excitations(self): - co = self.fragment.get_overlap('cluster[occ]|mo[occ]') - cv = self.fragment.get_overlap('cluster[vir]|mo[vir]') + co = self.fragment.get_overlap("cluster[occ]|mo[occ]") + cv = self.fragment.get_overlap("cluster[vir]|mo[vir]") ov_ss = einsum("Ii,Aa->IAia", co, cv).reshape(-1, co.shape[1] * cv.shape[1]) / np.sqrt(2) return np.hstack((ov_ss, ov_ss)) @@ -48,19 +49,28 @@ def truncate_bosons(self, coeff=None, occup=None, boson_threshold=None, verbose= occup = self.occup if isinstance(boson_threshold, numbers.Number): - boson_threshold = Boson_Threshold('occupation', boson_threshold) + boson_threshold = Boson_Threshold("occupation", boson_threshold) boson_number = boson_threshold.get_number(occup) if verbose: fmt = " %4s: N= %4d max= % 9.3g min= % 9.3g sum= % 9.3g ( %7.3f %%)" + def log_space(name, n_part): if len(n_part) == 0: - self.log.info(fmt[:fmt.index('max')].rstrip(), name, 0) + self.log.info(fmt[: fmt.index("max")].rstrip(), name, 0) return - with np.errstate(invalid='ignore'): # supress 0/0 warning - self.log.info(fmt, name, len(n_part), max(n_part), min(n_part), np.sum(n_part), - 100*np.sum(n_part)/np.sum(occup)) + with np.errstate(invalid="ignore"): # supress 0/0 warning + self.log.info( + fmt, + name, + len(n_part), + max(n_part), + min(n_part), + np.sum(n_part), + 100 * np.sum(n_part) / np.sum(occup), + ) + log_space("Bath", occup[:boson_number]) log_space("Rest", occup[boson_number:]) @@ -72,5 +82,5 @@ def log_histogram(self, n_bos): return self.log.info("Bosonic Coupling histogram:") bins = np.hstack([-np.inf, np.logspace(-3, -10, 8)[::-1], np.inf]) - labels = ' ' + ''.join('{:{w}}'.format('E-%d' % d, w=5) for d in range(3, 11)) + labels = " " + "".join("{:{w}}".format("E-%d" % d, w=5) for d in range(3, 11)) self.log.info(helper.make_histogram(n_bos, bins=bins, labels=labels)) diff --git a/vayesta/core/bosonic_bath/projected_interactions.py b/vayesta/core/bosonic_bath/projected_interactions.py index 9e10ab541..94bf19344 100644 --- a/vayesta/core/bosonic_bath/projected_interactions.py +++ b/vayesta/core/bosonic_bath/projected_interactions.py @@ -13,7 +13,7 @@ def __init__(self, cluster, mo_cderi_getter, mf, fock=None, log=None): self.mo_cderi_getter = mo_cderi_getter self.mf = mf self.fock = fock or mf.get_fock() - assert (self.cluster.inc_bosons) + assert self.cluster.inc_bosons self._cderi_clus = None self._cderi_bos = None self.log = log or NoLogger() @@ -47,9 +47,9 @@ def cderi_bos(self): cderi_neg = None for blk, lab in self._loop_df(): if blk is not None: - cderi[blk] = einsum('Lab,nab->Ln', lab, rbos) + cderi[blk] = einsum("Lab,nab->Ln", lab, rbos) else: - cderi_neg = einsum('Lab,nab->Ln', lab, rbos) + cderi_neg = einsum("Lab,nab->Ln", lab, rbos) self._cderi_bos = (cderi, cderi_neg) return self._cderi_bos @@ -57,7 +57,7 @@ def cderi_bos(self): def naux(self): df = self.mf.with_df try: - return (df.auxcell.nao if hasattr(df, 'auxcell') else df.auxmol.nao) + return df.auxcell.nao if hasattr(df, "auxcell") else df.auxmol.nao except AttributeError: return df.get_naoaux() @@ -93,16 +93,17 @@ def kernel(self, coupling_exchange=True, freq_exchange=False): return freqs, tuple([einsum("nm,npq->mpq", c, x) for x in couplings]), c, einsum("n,nm->m", nonconserving, c) def project_freqs(self, exchange=False): - if exchange: raise NotImplementedError ca, cb = self.bcluster.coeff_3d_ao - hbb_fock = einsum("nia,mjb,ab,ij->nm", ca, ca, self.fock_a, self.overlap) + \ - einsum("nia,mjb,ab,ij->nm", cb, cb, self.fock_b, self.overlap) - \ - einsum("nia,mjb,ij,ab->nm", ca, ca, self.fock_a, self.overlap) - \ - einsum("nia,mjb,ij,ab->nm", cb, cb, self.fock_b, self.overlap) + hbb_fock = ( + einsum("nia,mjb,ab,ij->nm", ca, ca, self.fock_a, self.overlap) + + einsum("nia,mjb,ab,ij->nm", cb, cb, self.fock_b, self.overlap) + - einsum("nia,mjb,ij,ab->nm", ca, ca, self.fock_a, self.overlap) + - einsum("nia,mjb,ij,ab->nm", cb, cb, self.fock_b, self.overlap) + ) cderi_bos, cderi_bos_neg = self.cderi_bos hbb_coulomb = einsum("Ln,Lm->nm", cderi_bos, cderi_bos) @@ -133,7 +134,7 @@ def _gen_fock_spinchannel_contrib(rbos, pocc, pvir, c_active, fock): # oo (and ai) contrib contrib = einsum("njc,ck,jl->nlk", rbos, dot(fock, c_active), dot(self.overlap, c_active)) # just oo contrib - contrib -= einsum("nkc,kc,pq->npq", rbos, fock, pocc) + contrib -= einsum("nkc,kc,pq->npq", rbos, fock, pocc) # just vv contrib contrib += einsum("nkc,kc,pq->npq", rbos, fock, pvir) # vv (and ai) contrib. @@ -143,10 +144,12 @@ def _gen_fock_spinchannel_contrib(rbos, pocc, pvir, c_active, fock): pocc, pvir = self.get_cluster_projectors() - couplings_fock = [_gen_fock_spinchannel_contrib(r, po, pv, c, f) for r, po, pv, c, f in zip( - self.bcluster.coeff_3d_ao, pocc, pvir, self.c_cluster, (self.fock_a, self.fock_b) - )] - + couplings_fock = [ + _gen_fock_spinchannel_contrib(r, po, pv, c, f) + for r, po, pv, c, f in zip( + self.bcluster.coeff_3d_ao, pocc, pvir, self.c_cluster, (self.fock_a, self.fock_b) + ) + ] # Finally, optionally compute exchange contributions. couplings_exchange = [np.zeros_like(x) for x in couplings_coulomb] @@ -162,19 +165,19 @@ def _gen_fock_spinchannel_contrib(rbos, pocc, pvir, c_active, fock): # Want -C_{nkc} = - C_{nkc} V_{Lpc} V_{Lkq} def _gen_exchange_spinchannel_contrib(l, c, rbos): - la_loc = np.tensordot(l, c, axes=(2, 0)) # "Lab,bp->Lap" + la_loc = np.tensordot(l, c, axes=(2, 0)) # "Lab,bp->Lap" temp = einsum("Lkp,Lcq->kcpq", la_loc, la_loc) contrib = einsum("kcpq,nkc->npq", temp, rbos) return contrib - for i,(blk, lab) in enumerate(self._loop_df()): + for i, (blk, lab) in enumerate(self._loop_df()): if blk is not None: couplings_exchange[0] = couplings_exchange[0] + _gen_exchange_spinchannel_contrib(lab, ca, rbos_a) couplings_exchange[1] = couplings_exchange[1] + _gen_exchange_spinchannel_contrib(lab, cb, rbos_b) else: raise NotImplementedError("CDERI_neg contributions to bosons not yet supported") - couplings = [x+y+z for x, y, z in zip(couplings_coulomb, couplings_exchange, couplings_fock)] + couplings = [x + y + z for x, y, z in zip(couplings_coulomb, couplings_exchange, couplings_fock)] return couplings def gen_nonconserving(self, couplings): @@ -198,13 +201,17 @@ def _get_cluster_spinchannel(c, co, cv): return po, pv if self.cluster.c_active_occ[0].ndim == 1: - poa, pva = _get_cluster_spinchannel(self.cluster.c_active, self.cluster.c_active_occ, self.cluster.c_active_vir) + poa, pva = _get_cluster_spinchannel( + self.cluster.c_active, self.cluster.c_active_occ, self.cluster.c_active_vir + ) pob, pvb = poa, pva else: - poa, pva = _get_cluster_spinchannel(self.cluster.c_active[0], self.cluster.c_active_occ[0], - self.cluster.c_active_vir[0]) - pob, pvb = _get_cluster_spinchannel(self.cluster.c_active[1], self.cluster.c_active_occ[1], - self.cluster.c_active_vir[1]) + poa, pva = _get_cluster_spinchannel( + self.cluster.c_active[0], self.cluster.c_active_occ[0], self.cluster.c_active_vir[0] + ) + pob, pvb = _get_cluster_spinchannel( + self.cluster.c_active[1], self.cluster.c_active_occ[1], self.cluster.c_active_vir[1] + ) return (poa, pob), (pva, pvb) def _loop_df(self, blksize=None): @@ -214,23 +221,23 @@ def _loop_df(self, blksize=None): if blksize is None: blksize = int(1e9 / naux * nao * nao * 8) # PBC: - if hasattr(df, 'sr_loop'): + if hasattr(df, "sr_loop"): blk0 = 0 for labr, labi, sign in df.sr_loop(compact=False, blksize=blksize): assert np.allclose(labi, 0) assert np.allclose(labi, 0) labr = labr.reshape(-1, nao, nao) - if (sign == 1): - blk1 = (blk0 + labr.shape[0]) + if sign == 1: + blk1 = blk0 + labr.shape[0] blk = np.s_[blk0:blk1] blk0 = blk1 yield blk, labr - elif (sign == -1): + elif sign == -1: yield None, labr # No PBC: blk0 = 0 for lab in df.loop(blksize=blksize): - blk1 = (blk0 + lab.shape[0]) + blk1 = blk0 + lab.shape[0] blk = np.s_[blk0:blk1] blk0 = blk1 lab = pyscf.lib.unpack_tril(lab) diff --git a/vayesta/core/bosonic_bath/qba_rpa.py b/vayesta/core/bosonic_bath/qba_rpa.py index 9b49e520b..70697b622 100644 --- a/vayesta/core/bosonic_bath/qba_rpa.py +++ b/vayesta/core/bosonic_bath/qba_rpa.py @@ -9,7 +9,8 @@ class RPA_Boson_Target_Space(Bath): This can either start from either the DMET or fully extended cluster, and can be optionally projected onto excitations local to the fragment in either just the occupied space or both the occupied and virtual spaces. """ - def __init__(self, fragment, target_orbitals="full", local_projection='fragment'): + + def __init__(self, fragment, target_orbitals="full", local_projection="fragment"): self.target_orbitals = target_orbitals self.local_projection = local_projection super().__init__(fragment) @@ -30,11 +31,13 @@ def ovlp(self): def get_c_target(self): if self.target_orbitals == "full": - return dot(self.mo_coeff_occ.T, self.ovlp, self.fragment.cluster.c_active_occ), \ - dot(self.mo_coeff_vir.T, self.ovlp, self.fragment.cluster.c_active_vir) + return dot(self.mo_coeff_occ.T, self.ovlp, self.fragment.cluster.c_active_occ), dot( + self.mo_coeff_vir.T, self.ovlp, self.fragment.cluster.c_active_vir + ) elif self.target_orbitals == "dmet": - return dot(self.mo_coeff_occ.T, self.ovlp, self.fragment._dmet_bath.c_cluster_occ), \ - dot(self.mo_coeff_vir.T, self.ovlp, self.fragment._dmet_bath.c_cluster_vir) + return dot(self.mo_coeff_occ.T, self.ovlp, self.fragment._dmet_bath.c_cluster_occ), dot( + self.mo_coeff_vir.T, self.ovlp, self.fragment._dmet_bath.c_cluster_vir + ) else: raise ValueError("Unknown target orbital requested.") @@ -45,20 +48,26 @@ def get_c_loc(self): if len(self.local_projection) == 8 or self.local_projection[-3:] == "occ": return dot(self.mo_coeff_occ.T, self.ovlp, self.fragment.c_frag), None elif self.local_projection[-2:] == "ov": - return dot(self.mo_coeff_occ.T, self.ovlp, self.fragment.c_frag), \ - dot(self.mo_coeff_vir.T, self.ovlp, self.fragment.c_frag) + return dot(self.mo_coeff_occ.T, self.ovlp, self.fragment.c_frag), dot( + self.mo_coeff_vir.T, self.ovlp, self.fragment.c_frag + ) raise ValueError("Unknown fragment projection requested.") def gen_target_excitation(self): """Generate the targeted excitation space for a given fragment""" - self.fragment.log.debug("Fragment %s generating RPA bosonic bath using %s excitations and projection onto %s.", self.fragment.id, self.target_orbitals, self.local_projection) + self.fragment.log.debug( + "Fragment %s generating RPA bosonic bath using %s excitations and projection onto %s.", + self.fragment.id, + self.target_orbitals, + self.local_projection, + ) # Obtain all values in the equivalent global space. c_occ, c_vir = self.get_c_target() c_loc_occ, c_loc_vir = self.get_c_loc() if c_loc_occ is not None: - s_occ = dot(c_loc_occ.T, c_occ) + s_occ = dot(c_loc_occ.T, c_occ) c_occ = dot(c_occ, s_occ.T, s_occ) if c_loc_vir is not None: s_vir = dot(c_loc_vir.T, c_vir) @@ -76,7 +85,6 @@ def _get_target_orbitals(self): raise ValueError("Unknown target orbital requested.") - class RPA_QBA_Bath(Bosonic_Bath): def __init__(self, fragment, target_m0): self.target_m0 = target_m0 diff --git a/vayesta/core/cmdargs.py b/vayesta/core/cmdargs.py index ec6e87a42..4fc3319da 100644 --- a/vayesta/core/cmdargs.py +++ b/vayesta/core/cmdargs.py @@ -1,35 +1,52 @@ import argparse import sys -DEFAULT_LOG = 'vlog.txt' -DEFAULT_ERR = 'verr.txt' +DEFAULT_LOG = "vlog.txt" +DEFAULT_ERR = "verr.txt" DEFAULT_LOGLVL = 20 DEFAULT_ERRLVL = 30 + def parse_cmd_args(): parser = argparse.ArgumentParser(allow_abbrev=False) # Log files - parser.add_argument('-o', '--output-dir', default='.', help="directory for Vayesta output files [default: current working directory]") - parser.add_argument('--log', default='vlog.txt', help="name of the log file [default: %s]" % DEFAULT_LOG) - parser.add_argument('--errlog', default='verr.txt', help="name of the error log file [default: %s]" % DEFAULT_ERR) + parser.add_argument( + "-o", + "--output-dir", + default=".", + help="directory for Vayesta output files [default: current working directory]", + ) + parser.add_argument("--log", default="vlog.txt", help="name of the log file [default: %s]" % DEFAULT_LOG) + parser.add_argument("--errlog", default="verr.txt", help="name of the error log file [default: %s]" % DEFAULT_ERR) # Log level - parser.add_argument('--log-level', type=int, default=DEFAULT_LOGLVL, - help="logging level for the log file [default: %d]" % DEFAULT_LOGLVL) - parser.add_argument('--errlog-level', type=int, default=DEFAULT_ERRLVL, - help="logging level for the error log file [default: %d]" % DEFAULT_ERRLVL) - parser.add_argument('-q', '--quiet', action='store_true', help='Do not print to terminal') + parser.add_argument( + "--log-level", + type=int, + default=DEFAULT_LOGLVL, + help="logging level for the log file [default: %d]" % DEFAULT_LOGLVL, + ) + parser.add_argument( + "--errlog-level", + type=int, + default=DEFAULT_ERRLVL, + help="logging level for the error log file [default: %d]" % DEFAULT_ERRLVL, + ) + parser.add_argument("-q", "--quiet", action="store_true", help="Do not print to terminal") # Enables infov: - parser.add_argument('-v', action='store_const', dest='log_level', const=15, - help="Enables verbose logging output.") + parser.add_argument("-v", action="store_const", dest="log_level", const=15, help="Enables verbose logging output.") # Enables debug, timing - parser.add_argument('-vv', action='store_const', dest='log_level', const=10, - help="Enables very verbose logging output.") + parser.add_argument( + "-vv", action="store_const", dest="log_level", const=10, help="Enables very verbose logging output." + ) # Enables debugv, timingv, trace - parser.add_argument('-vvv', action='store_const', dest='log_level', const=1, - help="Enables complete logging output.") + parser.add_argument( + "-vvv", action="store_const", dest="log_level", const=1, help="Enables complete logging output." + ) # MPI - parser.add_argument('--mpi', action='store_true', dest='mpi', default=None, help='Import mpi4py [default: attempt import]') - parser.add_argument('--no-mpi', action='store_false', dest='mpi', default=None, help='Do not import mpi4py') + parser.add_argument( + "--mpi", action="store_true", dest="mpi", default=None, help="Import mpi4py [default: attempt import]" + ) + parser.add_argument("--no-mpi", action="store_false", dest="mpi", default=None, help="Do not import mpi4py") args, unknown_args = parser.parse_known_args() # Remove known arguments: diff --git a/vayesta/core/eris.py b/vayesta/core/eris.py index 6bb6e4d23..a8d70c661 100644 --- a/vayesta/core/eris.py +++ b/vayesta/core/eris.py @@ -28,7 +28,7 @@ def get_cderi_df(mf, mo_coeff, compact=False, blksize=None): nao = mf.mol.nao df = mf.with_df try: - naux = (df.auxcell.nao if hasattr(df, 'auxcell') else df.auxmol.nao) + naux = df.auxcell.nao if hasattr(df, "auxcell") else df.auxmol.nao except AttributeError: naux = df.get_naoaux() @@ -37,30 +37,31 @@ def get_cderi_df(mf, mo_coeff, compact=False, blksize=None): if blksize is None: blksize = int(1e9 / naux * nao * nao * 8) # PBC: - if hasattr(df, 'sr_loop'): + if hasattr(df, "sr_loop"): blk0 = 0 for labr, labi, sign in df.sr_loop(compact=False, blksize=blksize): assert np.allclose(labi, 0) - assert (cderi_neg is None) # There should be only one block with sign -1 + assert cderi_neg is None # There should be only one block with sign -1 labr = labr.reshape(-1, nao, nao) - if (sign == 1): - blk1 = (blk0 + labr.shape[0]) + if sign == 1: + blk1 = blk0 + labr.shape[0] blk = np.s_[blk0:blk1] blk0 = blk1 - cderi[blk] = einsum('Lab,ai,bj->Lij', labr, mo_coeff[0], mo_coeff[1]) - elif (sign == -1): - cderi_neg = einsum('Lab,ai,bj->Lij', labr, mo_coeff[0], mo_coeff[1]) + cderi[blk] = einsum("Lab,ai,bj->Lij", labr, mo_coeff[0], mo_coeff[1]) + elif sign == -1: + cderi_neg = einsum("Lab,ai,bj->Lij", labr, mo_coeff[0], mo_coeff[1]) return cderi, cderi_neg # No PBC: blk0 = 0 for lab in df.loop(blksize=blksize): - blk1 = (blk0 + lab.shape[0]) + blk1 = blk0 + lab.shape[0] blk = np.s_[blk0:blk1] blk0 = blk1 lab = pyscf.lib.unpack_tril(lab) - cderi[blk] = einsum('Lab,ai,bj->Lij', lab, mo_coeff[0], mo_coeff[1]) + cderi[blk] = einsum("Lab,ai,bj->Lij", lab, mo_coeff[0], mo_coeff[1]) return cderi, None + def get_cderi_exspace(emb, ex_coeff, compact=False, blksize=None): if compact: raise NotImplementedError() @@ -69,6 +70,7 @@ def get_cderi_exspace(emb, ex_coeff, compact=False, blksize=None): else: return get_cderi_df_exspace(emb.mf, ex_coeff, compact=compact, blksize=blksize) + def get_cderi_df_exspace(mf, ex_coeff, compact=False, blksize=None): """Get density-fitted three-center integrals in MO basis.""" if compact: @@ -77,7 +79,7 @@ def get_cderi_df_exspace(mf, ex_coeff, compact=False, blksize=None): nao = mf.mol.nao df = mf.with_df try: - naux = (df.auxcell.nao if hasattr(df, 'auxcell') else df.auxmol.nao) + naux = df.auxcell.nao if hasattr(df, "auxcell") else df.auxmol.nao except AttributeError: naux = df.get_naoaux() @@ -86,24 +88,24 @@ def get_cderi_df_exspace(mf, ex_coeff, compact=False, blksize=None): if blksize is None: blksize = int(1e9 / naux * nao * nao * 8) # PBC: - if hasattr(df, 'sr_loop'): + if hasattr(df, "sr_loop"): blk0 = 0 for labr, labi, sign in df.sr_loop(compact=False, blksize=blksize): assert np.allclose(labi, 0) - assert (cderi_neg is None) # There should be only one block with sign -1 + assert cderi_neg is None # There should be only one block with sign -1 labr = labr.reshape(-1, nao, nao) - if (sign == 1): - blk1 = (blk0 + labr.shape[0]) + if sign == 1: + blk1 = blk0 + labr.shape[0] blk = np.s_[blk0:blk1] blk0 = blk1 cderi[blk] = einsum("Lab,nab->Ln", labr, ex_coeff) - elif (sign == -1): + elif sign == -1: cderi_neg = einsum("Lab,nab->Ln", labr, ex_coeff) return cderi, cderi_neg # No PBC: blk0 = 0 for lab in df.loop(blksize=blksize): - blk1 = (blk0 + lab.shape[0]) + blk1 = blk0 + lab.shape[0] blk = np.s_[blk0:blk1] blk0 = blk1 lab = pyscf.lib.unpack_tril(lab) @@ -136,12 +138,12 @@ def get_eris_array(emb, mo_coeff, compact=False): cderi2, cderi2_neg = cderi1, cderi1_neg else: cderi2, cderi2_neg = kao2gmo_cderi(emb.kdf, mo_coeff[2:]) - eris = einsum('Lij,Lkl->ijkl', cderi1.conj(), cderi2) + eris = einsum("Lij,Lkl->ijkl", cderi1.conj(), cderi2) if cderi1_neg is not None: - eris -= einsum('Lij,Lkl->ijkl', cderi1_neg.conj(), cderi2_neg) + eris -= einsum("Lij,Lkl->ijkl", cderi1_neg.conj(), cderi2_neg) return eris # Molecules and Gamma-point PBC: - if hasattr(emb.mf, 'with_df') and emb.mf.with_df is not None: + if hasattr(emb.mf, "with_df") and emb.mf.with_df is not None: eris = emb.mf.with_df.ao2mo(mo_coeff, compact=compact) elif emb.mf._eri is not None: eris = pyscf.ao2mo.kernel(emb.mf._eri, mo_coeff, compact=compact) @@ -182,7 +184,7 @@ def get_eris_object(emb, postscf, fock=None): raise ValueError("Unknown post-SCF method: %r", type(postscf)) # For MO energies, always use get_fock(): mo_act = _mo_without_core(postscf, postscf.mo_coeff) - mo_energy = einsum('ai,ab,bi->i', mo_act, emb.get_fock(), mo_act) + mo_energy = einsum("ai,ab,bi->i", mo_act, emb.get_fock(), mo_act) e_hf = emb.mf.e_tot # Fold MOs into k-point sampled primitive cell, to perform efficient AO->MO transformation: diff --git a/vayesta/core/foldscf.py b/vayesta/core/foldscf.py index 4f1acbaac..c2bb22dd9 100644 --- a/vayesta/core/foldscf.py +++ b/vayesta/core/foldscf.py @@ -15,6 +15,7 @@ log = logging.getLogger(__name__) + def fold_scf(kmf, *args, **kwargs): """Fold k-point sampled mean-field object to Born-von Karman (BVK) supercell. See also :class:`FoldedSCF`.""" @@ -24,6 +25,7 @@ def fold_scf(kmf, *args, **kwargs): return FoldedUHF(kmf, *args, **kwargs) raise NotImplementedError("Mean-field type= %r" % kmf) + class FoldedSCF: """Fold k-point sampled SCF calculation to the BVK (Born-von Karman) supercell. @@ -47,20 +49,19 @@ class FoldedSCF: """ # Propagate the following attributes to the k-point mean-field: - _from_kmf = ['converged', 'exxdiv', 'verbose', 'max_memory', 'conv_tol', 'conv_tol_grad', - 'stdout', '_eri'] + _from_kmf = ["converged", "exxdiv", "verbose", "max_memory", "conv_tol", "conv_tol_grad", "stdout", "_eri"] def __init__(self, kmf, kpt=np.zeros(3), **kwargs): # Create a copy, so that the original mean-field object does not get modified kmf = copy.copy(kmf) # Support for k-point symmetry: - if hasattr(kmf, 'to_khf'): + if hasattr(kmf, "to_khf"): kmf = kmf.to_khf() self.kmf = kmf self.subcellmesh = kpts_to_kmesh(self.kmf.cell, kmf.kpts) cell, self.kphase = get_phase(self.kcell, self.kmf.kpts) # We cannot call the PySCF __init__.... - #super().__init__(scell, **kwargs) + # super().__init__(scell, **kwargs) # ... so we have to intialize a few attributes here: self.mol = self.cell = cell @@ -89,11 +90,11 @@ def __setattr__(self, name, value): @property def e_tot(self): - return (self.ncells * self.kmf.e_tot) + return self.ncells * self.kmf.e_tot @e_tot.setter def e_tot(self, value): - self.kmf.e_tot = (value / self.ncells) + self.kmf.e_tot = value / self.ncells @property def ncells(self): @@ -118,24 +119,28 @@ def get_hcore(self, *args, make_real=True, **kwargs): return hcore def get_veff(self, mol=None, dm=None, *args, make_real=True, **kwargs): - assert (mol is None or mol is self.mol) + assert mol is None or mol is self.mol # Unfold DM into k-space - if dm is not None: dm = bvk2k_2d(dm, self.kphase) + if dm is not None: + dm = bvk2k_2d(dm, self.kphase) vk = self.kmf.get_veff(self.kmf.mol, dm, *args, **kwargs) veff = k2bvk_2d(vk, self.kphase, make_real=make_real) return veff + class FoldedRHF(FoldedSCF, pyscf.pbc.scf.hf.RHF): __doc__ = FoldedSCF.__doc__ def __init__(self, kmf, *args, **kwargs): super().__init__(kmf, *args, **kwargs) ovlp = self.get_ovlp() - self.mo_energy, self.mo_coeff, self.mo_occ = fold_mos(self.kmf.mo_energy, self.kmf.mo_coeff, self.kmf.mo_occ, - self.kphase, ovlp) + self.mo_energy, self.mo_coeff, self.mo_occ = fold_mos( + self.kmf.mo_energy, self.kmf.mo_coeff, self.kmf.mo_occ, self.kphase, ovlp + ) assert np.all(self.mo_coeff.imag == 0) + class FoldedUHF(FoldedSCF, pyscf.pbc.scf.uhf.UHF): __doc__ = FoldedSCF.__doc__ @@ -144,11 +149,13 @@ def __init__(self, kmf, *args, **kwargs): ovlp = self.get_ovlp() self.mo_energy, self.mo_coeff, self.mo_occ = zip( - fold_mos(self.kmf.mo_energy[0], self.kmf.mo_coeff[0], self.kmf.mo_occ[0], self.kphase, ovlp), - fold_mos(self.kmf.mo_energy[1], self.kmf.mo_coeff[1], self.kmf.mo_occ[1], self.kphase, ovlp)) + fold_mos(self.kmf.mo_energy[0], self.kmf.mo_coeff[0], self.kmf.mo_occ[0], self.kphase, ovlp), + fold_mos(self.kmf.mo_energy[1], self.kmf.mo_coeff[1], self.kmf.mo_occ[1], self.kphase, ovlp), + ) assert np.all(self.mo_coeff[0].imag == 0) assert np.all(self.mo_coeff[1].imag == 0) + def fold_mos(kmo_energy, kmo_coeff, kmo_occ, kphase, ovlp, make_real=True, sort=True): # --- MO energy and occupations mo_energy = np.hstack(kmo_energy) @@ -157,14 +164,14 @@ def fold_mos(kmo_energy, kmo_coeff, kmo_occ, kphase, ovlp, make_real=True, sort= # Number of MOs per k-point (can be k-point depedent, for example due to linear-dependency treatment) mo_coeff = [] for k, ck in enumerate(kmo_coeff): - cr = np.multiply.outer(kphase[k], ck) # R,ai -> Rai - mo_coeff.append(cr.reshape(cr.shape[0]*cr.shape[1], cr.shape[2])) # Rai -> (Ra),i + cr = np.multiply.outer(kphase[k], ck) # R,ai -> Rai + mo_coeff.append(cr.reshape(cr.shape[0] * cr.shape[1], cr.shape[2])) # Rai -> (Ra),i mo_coeff = np.hstack(mo_coeff) # --- Sort MOs according to energy if sort: reorder = np.argsort(mo_energy) mo_energy = mo_energy[reorder] - mo_coeff = mo_coeff[:,reorder] + mo_coeff = mo_coeff[:, reorder] mo_occ = mo_occ[reorder] # --- Make MOs real if make_real: @@ -185,17 +192,19 @@ def fold_mos(kmo_energy, kmo_coeff, kmo_occ, kphase, ovlp, make_real=True, sort= return mo_energy, mo_coeff, mo_occ + def log_error_norms(msg, err, error_tol=1e-3, warn_tol=1e-6): l2 = np.linalg.norm(err) linf = abs(err).max() lmax = max(l2, linf) if lmax > error_tol: - log.error(msg+" !!!", l2, linf) + log.error(msg + " !!!", l2, linf) elif lmax > warn_tol: - log.warning(msg+" !", l2, linf) + log.warning(msg + " !", l2, linf) else: log.debug(msg, l2, linf) + def make_mo_coeff_real(mo_energy, mo_coeff, ovlp, imag_tol=1e-10): mo_coeff = mo_coeff.copy() # Check orthonormality @@ -203,13 +212,13 @@ def make_mo_coeff_real(mo_energy, mo_coeff, ovlp, imag_tol=1e-10): log.debugv("Orthonormality error before make_mo_coeff_real: %.2e", ortherr) # Testing - im = (np.linalg.norm(mo_coeff.imag, axis=0) > imag_tol) + im = np.linalg.norm(mo_coeff.imag, axis=0) > imag_tol log.debugv("%d complex MOs found. L(2)= %.2e", np.count_nonzero(im), np.linalg.norm(mo_coeff.imag)) if not np.any(im): return mo_energy, mo_coeff.real shift = 1.0 - min(mo_energy[im]) - sc = np.dot(ovlp, mo_coeff[:,im]) - fock = np.dot(sc*(mo_energy[im]+shift), sc.T.conj()) + sc = np.dot(ovlp, mo_coeff[:, im]) + fock = np.dot(sc * (mo_energy[im] + shift), sc.T.conj()) log_error_norms("Imaginary part in folded Fock matrix: L(2)= %.2e L(inf)= %.2e", fock.imag) # Diagonalize subspace Fock matrix # TODO: eigensolver for linear dependencies... @@ -218,45 +227,46 @@ def make_mo_coeff_real(mo_energy, mo_coeff, ovlp, imag_tol=1e-10): # eigh = cell.eigh_factory(lindep_threshold=1e-13, fallback_mode=True) e, v = eigh(fock.real, ovlp) # Extract MOs from rank-deficient Fock matrix - mask = (e > 0.5) + mask = e > 0.5 assert np.count_nonzero(mask) == len(mo_energy[im]) - e, v = e[mask], v[:,mask] - log_error_norms("Error in folded MO energies: L(2)= %.2e L(inf)= %.2e", mo_energy[im]-(e-shift)) - mo_coeff[:,im] = v + e, v = e[mask], v[:, mask] + log_error_norms("Error in folded MO energies: L(2)= %.2e L(inf)= %.2e", mo_energy[im] - (e - shift)) + mo_coeff[:, im] = v assert np.all(np.linalg.norm(mo_coeff.imag, axis=0) <= imag_tol) return mo_energy, mo_coeff.real + def make_mo_coeff_real_2(mo_energy, mo_coeff, mo_occ, ovlp, hcore, imag_tol=1e-8): mo_coeff = mo_coeff.copy() # Check orthonormality ortherr = abs(dot(mo_coeff.T.conj(), ovlp, mo_coeff) - np.eye(mo_coeff.shape[-1])).max() log.debugv("Orthonormality error before make_mo_coeff_real: %.2e", ortherr) - mo_coeff_occ = mo_coeff[:,mo_occ>0] - mo_coeff_vir = mo_coeff[:,mo_occ==0] + mo_coeff_occ = mo_coeff[:, mo_occ > 0] + mo_coeff_vir = mo_coeff[:, mo_occ == 0] e_hcore_min = scipy.linalg.eigh(hcore, b=ovlp)[0][0] - shift = (1.0 - e_hcore_min) + shift = 1.0 - e_hcore_min def make_subspace_real(mo_coeff_sub): # Diagonalize Hcore to separate symmetry sectors nsub = mo_coeff_sub.shape[-1] - hsub = dot(mo_coeff_sub.T.conj(), hcore, mo_coeff_sub) + shift*np.eye(nsub) + hsub = dot(mo_coeff_sub.T.conj(), hcore, mo_coeff_sub) + shift * np.eye(nsub) cs = dot(mo_coeff_sub.T.conj(), ovlp) hsub = dot(cs.T.conj(), hsub, cs) im = abs(hsub.imag).max() - assert (im < imag_tol), ("Imaginary part of Hcore= %.3e" % im) + assert im < imag_tol, "Imaginary part of Hcore= %.3e" % im e, c = scipy.linalg.eigh(hsub.real, b=ovlp) - colspace = (e > 0.5) - assert (np.count_nonzero(colspace) == nsub) - mo_coeff_sub = c[:,colspace] + colspace = e > 0.5 + assert np.count_nonzero(colspace) == nsub + mo_coeff_sub = c[:, colspace] # Canonicalize subspace MO coefficients p = dot(mo_coeff.T.conj(), ovlp, mo_coeff_sub) - fsub = einsum('ia,i,ib->ab', p.conj(), mo_energy, p) + fsub = einsum("ia,i,ib->ab", p.conj(), mo_energy, p) im = abs(fsub.imag).max() - assert (im < imag_tol), ("Imaginary part of Fock= %.3e" % im) + assert im < imag_tol, "Imaginary part of Fock= %.3e" % im e, r = np.linalg.eigh(fsub.real) mo_energy_sub = e mo_coeff_sub = np.dot(mo_coeff_sub, r) @@ -267,7 +277,7 @@ def make_subspace_real(mo_coeff_sub): mo_energy_real = np.hstack((mo_energy_occ, mo_energy_vir)) mo_coeff_real = np.hstack((mo_coeff_occ, mo_coeff_vir)) - log_error_norms("Error in MO energies of real orbitals: L(2)= %.2e L(inf)= %.2e", (mo_energy_real-mo_energy)) + log_error_norms("Error in MO energies of real orbitals: L(2)= %.2e L(inf)= %.2e", (mo_energy_real - mo_energy)) return mo_energy_real, mo_coeff_real @@ -275,12 +285,14 @@ def make_subspace_real(mo_coeff_sub): # ========================== # From PySCF, modified + def kpts_to_kmesh(cell, kpts): """Guess k-mesh from k-points.""" scaled_k = cell.get_scaled_kpts(kpts).round(8) - kmesh = [len(np.unique(scaled_k[:,d])) for d in range(3)] + kmesh = [len(np.unique(scaled_k[:, d])) for d in range(3)] return kmesh + def translation_vectors_for_kmesh(cell, kmesh): """Translation vectors to construct super-cell of which the gamma point is identical to the k-point mesh of primitive cell""" @@ -290,64 +302,68 @@ def translation_vectors_for_kmesh(cell, kmesh): r_vec_abs = np.dot(r_vec_rel, latt_vec) return r_vec_abs + def get_phase(cell, kpts, kmesh=None): """The unitary transformation that transforms the supercell basis k-mesh adapted basis. Important: This is ordered as (k,R), different to PySCF k2gamma.get_phase! """ - if kmesh is None: kmesh = kpts_to_kmesh(cell, kpts) + if kmesh is None: + kmesh = kpts_to_kmesh(cell, kpts) r_vec_abs = translation_vectors_for_kmesh(cell, kmesh) nr = len(r_vec_abs) - phase = np.exp(1j*np.dot(kpts, r_vec_abs.T)) / np.sqrt(nr) + phase = np.exp(1j * np.dot(kpts, r_vec_abs.T)) / np.sqrt(nr) scell = tools.super_cell(cell, kmesh) return scell, phase + def k2bvk_2d(ak, phase, make_real=True, imag_tol=1e-6): """Transform unit-cell k-point AO integrals to the supercell gamma-point AO integrals.""" - ag = einsum('kR,...kij,kS->...RiSj', phase, ak, phase.conj()) + ag = einsum("kR,...kij,kS->...RiSj", phase, ak, phase.conj()) imag_norm = abs(ag.imag).max() if make_real and (imag_norm > imag_tol): msg = "Imaginary part of supercell integrals: %.2e (tolerance= %.2e)" log.fatal(msg, imag_norm, imag_tol) raise ImaginaryPartError(msg % (imag_norm, imag_tol)) nr, nao = phase.shape[1], ak.shape[-1] - shape = (*ag.shape[:-4], nr*nao, nr*nao) + shape = (*ag.shape[:-4], nr * nao, nr * nao) ag = ag.reshape(shape) if make_real: return ag.real return ag + def bvk2k_2d(ag, phase): """Transform supercell gamma-point AO integrals to the unit-cell k-point AO integrals.""" - nr, nao = phase.shape[1], ag.shape[-1]//phase.shape[1] + nr, nao = phase.shape[1], ag.shape[-1] // phase.shape[1] shape = (*ag.shape[:-2], nr, nao, nr, nao) ag = ag.reshape(shape) - ak = einsum('kR,...RiSj,kS->...kij', phase.conj(), ag, phase) + ak = einsum("kR,...RiSj,kS->...kij", phase.conj(), ag, phase) return ak + # Depreciated functionality removed; rotation of mos to minimise imaginary part and conversion between kpoint and # supercell calculations. # Check out v1.0.0 or v1.0.1 if needed. -if __name__ == '__main__': - +if __name__ == "__main__": import vayesta from pyscf.pbc import gto, scf log = vayesta.log cell = gto.Cell() - cell.atom = ''' + cell.atom = """ H 0.0 0.0 0.0 H 0.6 0.4 0.0 - ''' + """ - cell.basis = 'cc-pvdz' + cell.basis = "cc-pvdz" cell.a = np.eye(3) * 4.0 - cell.a[2,2] = 20 - cell.unit='B' + cell.a[2, 2] = 20 + cell.unit = "B" cell.dimension = 2 cell.build() @@ -355,18 +371,18 @@ def bvk2k_2d(ag, phase): kpts = cell.make_kpts(kmesh) khf = scf.KRHF(cell, kpts) - #khf = scf.KUHF(cell, kpts) + # khf = scf.KUHF(cell, kpts) khf.conv_tol = 1e-12 - khf = khf.density_fit(auxbasis='cc-pvdz-jkfit') + khf = khf.density_fit(auxbasis="cc-pvdz-jkfit") khf.kernel() hf = fold_scf(khf) scell = pyscf.pbc.tools.super_cell(cell, kmesh) shf = scf.RHF(scell) - #shf = scf.UHF(scell) + # shf = scf.UHF(scell) shf.conv_tol = 1e-12 - shf = shf.density_fit(auxbasis='cc-pvdz-jkfit') + shf = shf.density_fit(auxbasis="cc-pvdz-jkfit") shf.kernel() # Overlap matrix diff --git a/vayesta/core/fragmentation/__init__.py b/vayesta/core/fragmentation/__init__.py index a4e74d3c8..e72b52648 100644 --- a/vayesta/core/fragmentation/__init__.py +++ b/vayesta/core/fragmentation/__init__.py @@ -12,26 +12,31 @@ from vayesta.core.fragmentation.cas import CAS_Fragmentation as CAS_Fragmentation_RHF from vayesta.core.fragmentation.cas import CAS_Fragmentation_UHF + def SAO_Fragmentation(emb, *args, **kwargs): if emb.is_uhf: return SAO_Fragmentation_UHF(emb, *args, **kwargs) return SAO_Fragmentation_RHF(emb, *args, **kwargs) + def Site_Fragmentation(emb, *args, **kwargs): if emb.is_uhf: return Site_Fragmentation_UHF(emb, *args, **kwargs) return Site_Fragmentation_RHF(emb, *args, **kwargs) + def IAO_Fragmentation(emb, *args, **kwargs): if emb.is_uhf: return IAO_Fragmentation_UHF(emb, *args, **kwargs) return IAO_Fragmentation_RHF(emb, *args, **kwargs) + def IAOPAO_Fragmentation(emb, *args, **kwargs): if emb.is_uhf: return IAOPAO_Fragmentation_UHF(emb, *args, **kwargs) return IAOPAO_Fragmentation_RHF(emb, *args, **kwargs) + def CAS_Fragmentation(emb, *args, **kwargs): if emb.is_uhf: return CAS_Fragmentation_UHF(emb, *args, **kwargs) diff --git a/vayesta/core/fragmentation/cas.py b/vayesta/core/fragmentation/cas.py index dc9c01cf0..2f32547e8 100644 --- a/vayesta/core/fragmentation/cas.py +++ b/vayesta/core/fragmentation/cas.py @@ -24,7 +24,7 @@ def get_orbital_indices_labels(self, orbitals): if isinstance(orbitals[0], (int, np.integer)): orbital_indices = orbitals orbital_labels = (np.asarray(self.labels, dtype=object)[orbitals]).tolist() - orbital_labels = [('%s%3s %s%-s' % tuple(l)).strip() for l in orbital_labels] + orbital_labels = [("%s%3s %s%-s" % tuple(l)).strip() for l in orbital_labels] return orbital_indices, orbital_labels raise ValueError("A list of integers is required! orbitals= %r" % orbitals) @@ -59,26 +59,27 @@ def add_cas_fragment(self, ncas, nelec, name=None, degen_tol=1e-8, **kwargs): nelec_curr += int(occ[anyocc[-1] - offset]) if nelec_curr > nelec or offset > ncas: - raise ValueError( - "Cannot create CAS with required properties around Fermi level with current MO occupancy.") + raise ValueError("Cannot create CAS with required properties around Fermi level with current MO occupancy.") def check_for_degen(energies, po, pv, name=""): # Log orbital energies - for i in range(max(po-2, 0), min(pv+2, len(energies))): + for i in range(max(po - 2, 0), min(pv + 2, len(energies))): if i < po: - orbtype = 'core' + orbtype = "core" elif i >= pv: - orbtype = 'external' + orbtype = "external" else: - orbtype = 'CAS' + orbtype = "CAS" if i == po: - self.log.info(62*'-') - self.log.info("MO %4d: %-8s occupation= %1d energy= %s", i, orbtype, occ[i], energy_string(energies[i])) - if i == (pv-1): - self.log.info(62*'-') + self.log.info(62 * "-") + self.log.info( + "MO %4d: %-8s occupation= %1d energy= %s", i, orbtype, occ[i], energy_string(energies[i]) + ) + if i == (pv - 1): + self.log.info(62 * "-") if po > 0: - ogap = energies[po] - energies[po-1] + ogap = energies[po] - energies[po - 1] self.log.info("%sCAS occupied energy gap: %s", name, energy_string(ogap)) elif po == 0: self.log.info("%sCAS contains all occupied orbitals.", name) @@ -93,7 +94,7 @@ def check_for_degen(energies, po, pv, name=""): try: vgap = energies[pv] - energies[pv - 1] except IndexError: - assert(pv == len(energies)) + assert pv == len(energies) self.log.info("%sCAS contains all virtual orbitals.", name) vgap = np.inf else: @@ -102,16 +103,15 @@ def check_for_degen(energies, po, pv, name=""): raise ValueError("Requested CAS splits degenerate virtual orbitals.") if self.emb.is_rhf: - check_for_degen(self.emb.mo_energy, anyocc[-1]-offset, anyocc[-1]-offset+ncas) + check_for_degen(self.emb.mo_energy, anyocc[-1] - offset, anyocc[-1] - offset + ncas) else: - check_for_degen(self.emb.mo_energy[0], anyocc[-1]-offset, anyocc[-1]-offset+ncas, "alpha ") - check_for_degen(self.emb.mo_energy[1], anyocc[-1]-offset, anyocc[-1]-offset+ncas, "beta ") + check_for_degen(self.emb.mo_energy[0], anyocc[-1] - offset, anyocc[-1] - offset + ncas, "alpha ") + check_for_degen(self.emb.mo_energy[1], anyocc[-1] - offset, anyocc[-1] - offset + ncas, "beta ") - orbs = list(range(anyocc[-1]-offset, anyocc[-1]-offset+ncas)) + orbs = list(range(anyocc[-1] - offset, anyocc[-1] - offset + ncas)) return self.add_orbital_fragment(orbs, name=name, **kwargs) class CAS_Fragmentation_UHF(Fragmentation_UHF, CAS_Fragmentation): - def get_labels(self): return [("", "", "MO", str(x)) for x in range(0, self.nmo[0])] diff --git a/vayesta/core/fragmentation/fragmentation.py b/vayesta/core/fragmentation/fragmentation.py index d369b28eb..fb21fec3e 100644 --- a/vayesta/core/fragmentation/fragmentation.py +++ b/vayesta/core/fragmentation/fragmentation.py @@ -6,6 +6,7 @@ from vayesta.core.util import dot, fix_orbital_sign, time_string, timer from vayesta.core.fragmentation import helper + def check_orthonormal(log, mo_coeff, ovlp, mo_name="orbital", tol=1e-7): """Check orthonormality of mo_coeff. @@ -22,8 +23,8 @@ def check_orthonormal(log, mo_coeff, ovlp, mo_name="orbital", tol=1e-7): log.debugv("Orthogonality error of %ss: L(2)= %.2e L(inf)= %.2e", mo_name, l2, linf) return l2, linf # UHF - l2a, linfa = check_orthonormal(log, mo_coeff[0], ovlp, mo_name='alpha-%s' % mo_name, tol=tol) - l2b, linfb = check_orthonormal(log, mo_coeff[1], ovlp, mo_name='beta-%s' % mo_name, tol=tol) + l2a, linfa = check_orthonormal(log, mo_coeff[0], ovlp, mo_name="alpha-%s" % mo_name, tol=tol) + l2b, linfb = check_orthonormal(log, mo_coeff[1], ovlp, mo_name="beta-%s" % mo_name, tol=tol) return (l2a, l2b), (linfa, linfb) @@ -36,8 +37,8 @@ def __init__(self, emb, add_symmetric=True, log=None): self.emb = emb self.add_symmetric = add_symmetric self.log = log or emb.log - self.log.info('%s Fragmentation' % self.name) - self.log.info('%s--------------' % (len(self.name)*'-')) + self.log.info("%s Fragmentation" % self.name) + self.log.info("%s--------------" % (len(self.name) * "-")) self.ovlp = self.mf.get_ovlp() # Secondary fragment state: self.secfrag_register = [] @@ -71,8 +72,11 @@ def __exit__(self, exc_type, exc_value, exc_traceback): translation = self.emb.symmetry.translation if translation is not None: fragments_sym = self.emb.create_transsym_fragments(translation, fragments=self.fragments) - self.log.info("Adding %d translationally-symmetry related fragments from %d base fragments", - len(fragments_sym), len(self.fragments)) + self.log.info( + "Adding %d translationally-symmetry related fragments from %d base fragments", + len(fragments_sym), + len(self.fragments), + ) self.fragments.extend(fragments_sym) # Add fragments to embedding class @@ -83,11 +87,13 @@ def __exit__(self, exc_type, exc_value, exc_traceback): orth = self.emb.has_orthonormal_fragmentation() comp = self.emb.has_complete_fragmentation() occcomp = self.emb.has_complete_occupied_fragmentation() - self.log.info("Fragmentation: orthogonal= %r, occupied-complete= %r, virtual-complete= %r", - self.emb.has_orthonormal_fragmentation(), - self.emb.has_complete_occupied_fragmentation(), - self.emb.has_complete_virtual_fragmentation()) - self.log.timing("Time for %s fragmentation: %s", self.name, time_string(timer()-self._time0)) + self.log.info( + "Fragmentation: orthogonal= %r, occupied-complete= %r, virtual-complete= %r", + self.emb.has_orthonormal_fragmentation(), + self.emb.has_complete_occupied_fragmentation(), + self.emb.has_complete_virtual_fragmentation(), + ) + self.log.timing("Time for %s fragmentation: %s", self.name, time_string(timer() - self._time0)) del self._time0 self.log.changeIndentLevel(-1) @@ -121,7 +127,7 @@ def add_atomshell_fragment(self, atoms, shells, **kwargs): atom_indices, atom_symbols = self.get_atom_indices_symbols(atoms) for idx, sym in zip(atom_indices, atom_symbols): for shell in shells: - orbitals.append('%d%3s %s' % (idx, sym, shell)) + orbitals.append("%d%3s %s" % (idx, sym, shell)) return self.add_orbital_fragment(orbitals, atoms=atom_indices, **kwargs) def add_orbital_fragment(self, orbitals, atom_filter=None, name=None, **kwargs): @@ -159,7 +165,7 @@ def add_all_atomic_fragments(self, **kwargs): fragments.append(frag) return fragments - def add_full_system(self, name='full-system', **kwargs): + def add_full_system(self, name="full-system", **kwargs): atoms = list(range(self.mol.natm)) return self.add_atomic_fragment(atoms, name=name, **kwargs) @@ -189,14 +195,15 @@ def _create_fragment(self, indices, name, **kwargs): # --- Rotational symmetry fragments: @contextlib.contextmanager - def rotational_symmetry(self, order, axis, center=(0,0,0), unit='Ang'): + def rotational_symmetry(self, order, axis, center=(0, 0, 0), unit="Ang"): if self.secfrag_register: raise NotImplementedError("Rotational symmetries have to be added before adding secondary fragments") self.sym_register.append([]) yield fragments = self.sym_register.pop() - fragments_sym = self.emb.create_rotsym_fragments(order, axis, center, fragments=fragments, unit=unit, - symbol='R%d' % len(self.sym_register)) + fragments_sym = self.emb.create_rotsym_fragments( + order, axis, center, fragments=fragments, unit=unit, symbol="R%d" % len(self.sym_register) + ) self.log.info("Adding %d rotationally-symmetric fragments", len(fragments_sym)) self.fragments.extend(fragments_sym) # For additional (nested) symmetries: @@ -204,13 +211,13 @@ def rotational_symmetry(self, order, axis, center=(0,0,0), unit='Ang'): sym.extend(fragments_sym) @contextlib.contextmanager - def inversion_symmetry(self, center=(0,0,0), unit='Ang'): + def inversion_symmetry(self, center=(0, 0, 0), unit="Ang"): if self.secfrag_register: raise NotImplementedError("Symmetries have to be added before adding secondary fragments") self.sym_register.append([]) yield fragments = self.sym_register.pop() - fragments_sym = self.emb.create_invsym_fragments(center, fragments=fragments, unit=unit, symbol='I') + fragments_sym = self.emb.create_invsym_fragments(center, fragments=fragments, unit=unit, symbol="I") self.log.info("Adding %d inversion-symmetric fragments", len(fragments_sym)) self.fragments.extend(fragments_sym) # For additional (nested) symmetries: @@ -218,45 +225,45 @@ def inversion_symmetry(self, center=(0,0,0), unit='Ang'): sym.extend(fragments_sym) @contextlib.contextmanager - def mirror_symmetry(self, axis, center=(0,0,0), unit='Ang'): + def mirror_symmetry(self, axis, center=(0, 0, 0), unit="Ang"): if self.secfrag_register: raise NotImplementedError("Symmetries have to be added before adding secondary fragments") self.sym_register.append([]) yield fragments = self.sym_register.pop() - fragments_sym = self.emb.create_mirrorsym_fragments(axis, center=center, fragments=fragments, unit=unit, - symbol='M') + fragments_sym = self.emb.create_mirrorsym_fragments( + axis, center=center, fragments=fragments, unit=unit, symbol="M" + ) self.log.info("Adding %d mirror-symmetric fragments", len(fragments_sym)) self.fragments.extend(fragments_sym) # For additional (nested) symmetries: for sym in self.sym_register: sym.extend(fragments_sym) - # --- Secondary fragments: @contextlib.contextmanager - def secondary_fragments(self, bno_threshold=None, bno_threshold_factor=0.1, solver='MP2'): + def secondary_fragments(self, bno_threshold=None, bno_threshold_factor=0.1, solver="MP2"): if self.secfrag_register: raise NotImplementedError("Nested secondary fragments") self.secfrag_register.append([]) yield fragments = self.secfrag_register.pop() - fragments_sec = self._create_secondary_fragments(fragments, bno_threshold=bno_threshold, - bno_threshold_factor=bno_threshold_factor, solver=solver) + fragments_sec = self._create_secondary_fragments( + fragments, bno_threshold=bno_threshold, bno_threshold_factor=bno_threshold_factor, solver=solver + ) self.log.info("Adding %d secondary fragments", len(fragments_sec)) self.fragments.extend(fragments_sec) # If we are already in a symmetry context, the symmetry-related secondary fragments should also be added: for sym in self.sym_register: sym.extend(fragments_sec) - def _create_secondary_fragments(self, fragments, bno_threshold=None, bno_threshold_factor=0.1, solver='MP2'): - + def _create_secondary_fragments(self, fragments, bno_threshold=None, bno_threshold_factor=0.1, solver="MP2"): def _create_fragment(fx, flags=None, **kwargs): if fx.sym_parent is not None: raise NotImplementedError("Secondary fragments need to be added before symmetry-derived fragments") flags = (flags or {}).copy() - flags['is_secfrag'] = True + flags["is_secfrag"] = True fx_copy = fx.copy(solver=solver, flags=flags, **kwargs) fx_copy.flags.bath_parent_fragment_id = fx.id self.log.debugv("Adding secondary fragment: %s", fx_copy) @@ -264,24 +271,25 @@ def _create_fragment(fx, flags=None, **kwargs): fragments_sec = [] for fx in fragments: - bath_opts = fx.opts.bath_options.copy() if bno_threshold is not None: - bath_opts['threshold'] = bno_threshold - bath_opts.pop('threshold_occ', None) - bath_opts.pop('threshold_vir', None) + bath_opts["threshold"] = bno_threshold + bath_opts.pop("threshold_occ", None) + bath_opts.pop("threshold_vir", None) else: - if bath_opts.get('threshold', None) is not None: - bath_opts['threshold'] *= bno_threshold_factor - if bath_opts.get('threshold_occ', None) is not None: - bath_opts['threshold_occ'] *= bno_threshold_factor - if bath_opts.get('threshold_vir', None) is not None: - bath_opts['threshold_vir'] *= bno_threshold_factor - frag = _create_fragment(fx, name='%s[x%d|+%s]' % (fx.name, fx.id, solver), bath_options=bath_opts) + if bath_opts.get("threshold", None) is not None: + bath_opts["threshold"] *= bno_threshold_factor + if bath_opts.get("threshold_occ", None) is not None: + bath_opts["threshold_occ"] *= bno_threshold_factor + if bath_opts.get("threshold_vir", None) is not None: + bath_opts["threshold_vir"] *= bno_threshold_factor + frag = _create_fragment(fx, name="%s[x%d|+%s]" % (fx.name, fx.id, solver), bath_options=bath_opts) fragments_sec.append(frag) # Double counting wf_factor = -(fx.opts.wf_factor or 1) - frag = _create_fragment(fx, name='%s[x%d|-%s]' % (fx.name, fx.id, solver), wf_factor=wf_factor, flags=dict(is_envelop=False)) + frag = _create_fragment( + fx, name="%s[x%d|-%s]" % (fx.name, fx.id, solver), wf_factor=wf_factor, flags=dict(is_envelop=False) + ) fragments_sec.append(frag) fx.flags.is_envelop = False return fragments_sec @@ -337,17 +345,18 @@ def get_atoms(self): def symmetric_orth(self, mo_coeff, ovlp=None, tol=1e-15): """Use as mo_coeff = np.dot(mo_coeff, x) to get orthonormal orbitals.""" - if ovlp is None: ovlp = self.get_ovlp() + if ovlp is None: + ovlp = self.get_ovlp() m = dot(mo_coeff.T, ovlp, mo_coeff) e, v = scipy.linalg.eigh(m) e_min = e.min() - keep = (e >= tol) - e, v = e[keep], v[:,keep] - x = dot(v/np.sqrt(e), v.T) + keep = e >= tol + e, v = e[keep], v[:, keep] + x = dot(v / np.sqrt(e), v.T) x = fix_orbital_sign(x)[0] return x, e_min - #def check_orth(self, mo_coeff, mo_name=None, tol=1e-7): + # def check_orth(self, mo_coeff, mo_name=None, tol=1e-7): # """Check orthonormality of mo_coeff.""" # err = dot(mo_coeff.T, self.get_ovlp(), mo_coeff) - np.eye(mo_coeff.shape[-1]) # l2 = np.linalg.norm(err) @@ -360,12 +369,14 @@ def symmetric_orth(self, mo_coeff, ovlp=None, tol=1e-15): # return l2, linf def check_orthonormal(self, mo_coeff, mo_name=None, tol=1e-7): - if mo_name is None: mo_name = self.name + if mo_name is None: + mo_name = self.name return check_orthonormal(self.log, mo_coeff, self.get_ovlp(), mo_name=mo_name, tol=tol) def get_atom_indices_symbols(self, atoms): """Convert a list of integer or strings to atom indices and symbols.""" - if np.ndim(atoms) == 0: atoms = [atoms] + if np.ndim(atoms) == 0: + atoms = [atoms] if isinstance(atoms[0], (int, np.integer)): atom_indices = atoms @@ -401,7 +412,8 @@ def get_atomic_fragment_indices(self, atoms, orbital_filter=None, name=None): List of fragment orbitals indices, with coefficients corresponding to `self.coeff[:,indices]`. """ atom_indices, atom_symbols = self.get_atom_indices_symbols(atoms) - if name is None: name = '/'.join(atom_symbols) + if name is None: + name = "/".join(atom_symbols) self.log.debugv("Atom indices of fragment %s: %r", name, atom_indices) self.log.debugv("Atom symbols of fragment %s: %r", name, atom_symbols) # Indices of IAOs based at atoms @@ -414,12 +426,13 @@ def get_atomic_fragment_indices(self, atoms, orbital_filter=None, name=None): def get_orbital_indices_labels(self, orbitals): """Convert a list of integer or strings to orbital indices and labels.""" - if np.ndim(orbitals) == 0: orbitals = [orbitals] + if np.ndim(orbitals) == 0: + orbitals = [orbitals] if isinstance(orbitals[0], (int, np.integer)): orbital_indices = orbitals orbital_labels = (np.asarray(self.labels, dtype=object)[orbitals]).tolist() - orbital_labels = [('%d%3s %s%-s' % tuple(l)).strip() for l in orbital_labels] + orbital_labels = [("%d%3s %s%-s" % tuple(l)).strip() for l in orbital_labels] return orbital_indices, orbital_labels if isinstance(orbitals[0], str): orbital_labels = orbitals @@ -435,19 +448,20 @@ def get_orbital_fragment_indices(self, orbitals, atom_filter=None, name=None): if atom_filter is not None: raise NotImplementedError() indices, orbital_labels = self.get_orbital_indices_labels(orbitals) - if name is None: name = '/'.join(orbital_labels) + if name is None: + name = "/".join(orbital_labels) self.log.debugv("Orbital indices of fragment %s: %r", name, indices) self.log.debugv("Orbital labels of fragment %s: %r", name, orbital_labels) return name, indices def get_frag_coeff(self, indices): """Get fragment coefficients for a given set of orbital indices.""" - c_frag = self.coeff[:,indices].copy() + c_frag = self.coeff[:, indices].copy() return c_frag def get_env_coeff(self, indices): """Get environment coefficients for a given set of orbital indices.""" env = np.ones((self.coeff.shape[-1]), dtype=bool) env[indices] = False - c_env = self.coeff[:,env].copy() + c_env = self.coeff[:, env].copy() return c_env diff --git a/vayesta/core/fragmentation/helper.py b/vayesta/core/fragmentation/helper.py index 5a9d7be82..dde4a9cf0 100644 --- a/vayesta/core/fragmentation/helper.py +++ b/vayesta/core/fragmentation/helper.py @@ -1,5 +1,3 @@ - - def log_orbitals(logger, labels, ncol=8): # Group orbitals by atom - list(dict.fromkeys(...)) to only get unique atom indices: atoms = list(dict.fromkeys([l[0] for l in labels])) @@ -8,9 +6,9 @@ def log_orbitals(logger, labels, ncol=8): prefix = "Atom %4s %3s:" % (atom[0][0], atom[0][1]) # Print up to ncol orbitals per line: for idx in range(0, len(atom), ncol): - line = atom[idx:idx+ncol] - fmt = ' %14s ' + len(line)*' %8s' + line = atom[idx : idx + ncol] + fmt = " %14s " + len(line) * " %8s" # Preformat - orbs = [('%s-%s' % (nl, ml) if ml else nl) for a, sym, nl, ml in line] + orbs = [("%s-%s" % (nl, ml) if ml else nl) for a, sym, nl, ml in line] logger(fmt, prefix, *orbs) prefix = "" diff --git a/vayesta/core/fragmentation/iao.py b/vayesta/core/fragmentation/iao.py index f77ae53f8..a3e4656a9 100644 --- a/vayesta/core/fragmentation/iao.py +++ b/vayesta/core/fragmentation/iao.py @@ -11,32 +11,39 @@ # Load default minimal basis set on module initialization default_minao = {} path = os.path.dirname(__file__) -with open(os.path.join(path, 'minao.dat'), 'r') as f: +with open(os.path.join(path, "minao.dat"), "r") as f: for line in f: - if line.startswith('#'): continue + if line.startswith("#"): + continue (basis, minao) = line.split() - if minao == 'none': minao = None + if minao == "none": + minao = None default_minao[basis] = minao + def get_default_minao(basis): # TODO: Add more to data file if not isinstance(basis, str): - return 'minao' - bas = basis.replace('-', '').lower() - minao = default_minao.get(bas, 'minao') + return "minao" + bas = basis.replace("-", "").lower() + minao = default_minao.get(bas, "minao") if minao is None: raise ValueError("Could not chose minimal basis for basis %s automatically!", basis) return minao -class IAO_Fragmentation(Fragmentation): +class IAO_Fragmentation(Fragmentation): name = "IAO" - def __init__(self, *args, minao='auto', **kwargs): + def __init__(self, *args, minao="auto", **kwargs): super().__init__(*args, **kwargs) - if minao.lower() == 'auto': + if minao.lower() == "auto": minao = get_default_minao(self.mol.basis) - self.log.info("IAO: computational basis= %s minimal reference basis= %s (automatically chosen)", self.mol.basis, minao) + self.log.info( + "IAO: computational basis= %s minimal reference basis= %s (automatically chosen)", + self.mol.basis, + minao, + ) else: self.log.debug("IAO: computational basis= %s minimal reference basis= %s", self.mol.basis, minao) self.minao = minao @@ -48,12 +55,13 @@ def __init__(self, *args, minao='auto', **kwargs): self.log.error("Could not find IAOs when using space group symmetry.") self.log.error("This is a known issue with some PySCF versions.") self.log.error( - "Please set `emb.mf.mol.space_group_symmetry=False` when initialising the fragmentation (it can be turned back on afterwards).") + "Please set `emb.mf.mol.space_group_symmetry=False` when initialising the fragmentation (it can be turned back on afterwards)." + ) raise ValueError( - "Could not find IAOs when using space group symmetry. Please set `emb.mf.mol.space_group_symmetry=False`.") + "Could not find IAOs when using space group symmetry. Please set `emb.mf.mol.space_group_symmetry=False`." + ) raise e - @property def n_iao(self): return self.refmol.nao @@ -66,19 +74,31 @@ def get_coeff(self, mo_coeff=None, mo_occ=None, add_virtuals=True): c_iao : (n(AO), n(IAO)) array Orthonormalized IAO coefficients. """ - if mo_coeff is None: mo_coeff = self.mo_coeff - if mo_occ is None: mo_occ = self.mo_occ + if mo_coeff is None: + mo_coeff = self.mo_coeff + if mo_occ is None: + mo_occ = self.mo_occ ovlp = self.get_ovlp() - c_occ = mo_coeff[:,mo_occ>0] + c_occ = mo_coeff[:, mo_occ > 0] c_iao = pyscf.lo.iao.iao(self.mol, c_occ, minao=self.minao) n_iao = c_iao.shape[-1] - self.log.info("n(AO)= %4d n(MO)= %4d n(occ-MO)= %4d n(IAO)= %4d", - mo_coeff.shape[0], mo_coeff.shape[-1], c_occ.shape[-1], n_iao) + self.log.info( + "n(AO)= %4d n(MO)= %4d n(occ-MO)= %4d n(IAO)= %4d", + mo_coeff.shape[0], + mo_coeff.shape[-1], + c_occ.shape[-1], + n_iao, + ) # Orthogonalize IAO using symmetric (Lowdin) orthogonalization x, e_min = self.symmetric_orth(c_iao, ovlp) - self.log.debugv("Lowdin orthogonalization of IAOs: n(in)= %3d -> n(out)= %3d , min(eig)= %.3e", x.shape[0], x.shape[1], e_min) + self.log.debugv( + "Lowdin orthogonalization of IAOs: n(in)= %3d -> n(out)= %3d , min(eig)= %.3e", + x.shape[0], + x.shape[1], + e_min, + ) if e_min < 1e-10: self.log.warning("Small eigenvalue in Lowdin orthogonalization: %.3e !", e_min) c_iao = np.dot(c_iao, x) @@ -92,12 +112,14 @@ def get_coeff(self, mo_coeff=None, mo_occ=None, add_virtuals=True): return c_iao def check_nelectron(self, c_iao, mo_coeff, mo_occ): - dm = np.einsum('ai,i,bi->ab', mo_coeff, mo_occ, mo_coeff) + dm = np.einsum("ai,i,bi->ab", mo_coeff, mo_occ, mo_coeff) ovlp = self.get_ovlp() - ne_iao = einsum('ai,ab,bc,cd,di->', c_iao, ovlp, dm, ovlp, c_iao) - ne_tot = einsum('ab,ab->', dm, ovlp) + ne_iao = einsum("ai,ab,bc,cd,di->", c_iao, ovlp, dm, ovlp, c_iao) + ne_tot = einsum("ab,ab->", dm, ovlp) if abs(ne_iao - ne_tot) > 1e-8: - self.log.error("IAOs do not contain the correct number of electrons: IAO= %.8f total= %.8f", ne_iao, ne_tot) + self.log.error( + "IAOs do not contain the correct number of electrons: IAO= %.8f total= %.8f", ne_iao, ne_tot + ) else: self.log.debugv("Number of electrons: IAO= %.8f total= %.8f", ne_iao, ne_tot) return ne_iao @@ -111,7 +133,7 @@ def get_labels(self): Orbital label (atom-id, atom symbol, nl string, m string) for each IAO. """ iao_labels_refmol = self.refmol.ao_labels(None) - self.log.debugv('iao_labels_refmol: %r', iao_labels_refmol) + self.log.debugv("iao_labels_refmol: %r", iao_labels_refmol) if self.refmol.natm == self.mol.natm: iao_labels = iao_labels_refmol # If there are ghost atoms in the system, they will be removed in refmol. @@ -125,7 +147,7 @@ def get_labels(self): for atm in range(self.mol.natm): coords = self.mol.atom_coord(atm) if np.allclose(coords, ref_coords): - self.log.debugv('reference cell atom %r maps to atom %r', refatm, atm) + self.log.debugv("reference cell atom %r maps to atom %r", refatm, atm) ref2mol.append(atm) break else: @@ -133,15 +155,16 @@ def get_labels(self): iao_labels = [] for iao in iao_labels_refmol: iao_labels.append((ref2mol[iao[0]], iao[1], iao[2], iao[3])) - self.log.debugv('iao_labels: %r', iao_labels) - assert (len(iao_labels_refmol) == len(iao_labels)) + self.log.debugv("iao_labels: %r", iao_labels) + assert len(iao_labels_refmol) == len(iao_labels) return iao_labels def search_labels(self, labels): return self.refmol.search_ao_label(labels) def get_virtual_coeff(self, c_iao, mo_coeff=None): - if mo_coeff is None: mo_coeff = self.mo_coeff + if mo_coeff is None: + mo_coeff = self.mo_coeff ovlp = self.get_ovlp() # Add remaining virtual space, work in MO space, so that we automatically get the # correct linear dependency treatment, if n(MO) < n(AO) @@ -163,20 +186,28 @@ def get_virtual_coeff(self, c_iao, mo_coeff=None): if np.any(abs(e_iao) > 1e-3): self.log.error("CRITICAL: Some IAO eigenvalues of 1-P_IAO are not close to 0:\n%r", e_iao) elif np.any(abs(e_iao) > 1e-6): - self.log.warning("Some IAO eigenvalues e of 1-P_IAO are not close to 0: n= %d max|e|= %.2e", - np.count_nonzero(abs(e_iao) > 1e-6), abs(e_iao).max()) - if np.any(abs(1-e_rest) > 1e-3): + self.log.warning( + "Some IAO eigenvalues e of 1-P_IAO are not close to 0: n= %d max|e|= %.2e", + np.count_nonzero(abs(e_iao) > 1e-6), + abs(e_iao).max(), + ) + if np.any(abs(1 - e_rest) > 1e-3): self.log.error("CRITICAL: Some non-IAO eigenvalues of 1-P_IAO are not close to 1:\n%r", e_rest) - elif np.any(abs(1-e_rest) > 1e-6): - self.log.warning("Some non-IAO eigenvalues e of 1-P_IAO are not close to 1: n= %d max|1-e|= %.2e", - np.count_nonzero(abs(1-e_rest) > 1e-6), abs(1-e_rest).max()) + elif np.any(abs(1 - e_rest) > 1e-6): + self.log.warning( + "Some non-IAO eigenvalues e of 1-P_IAO are not close to 1: n= %d max|1-e|= %.2e", + np.count_nonzero(abs(1 - e_rest) > 1e-6), + abs(1 - e_rest).max(), + ) if not (np.sum(mask_rest) + c_iao.shape[-1] == mo_coeff.shape[-1]): - self.log.critical("Error in construction of remaining virtual orbitals! Eigenvalues of projector 1-P_IAO:\n%r", e) + self.log.critical( + "Error in construction of remaining virtual orbitals! Eigenvalues of projector 1-P_IAO:\n%r", e + ) self.log.critical("Number of eigenvalues above 0.5 = %d", np.sum(mask_rest)) self.log.critical("Total number of orbitals = %d", mo_coeff.shape[-1]) raise RuntimeError("Incorrect number of remaining virtual orbitals") - c_rest = np.dot(mo_coeff, c[:,mask_rest]) # Transform back to AO basis + c_rest = np.dot(mo_coeff, c[:, mask_rest]) # Transform back to AO basis c_rest = fix_orbital_sign(c_rest)[0] self.check_orthonormal(np.hstack((c_iao, c_rest)), "IAO+virtual orbital") @@ -184,10 +215,11 @@ def get_virtual_coeff(self, c_iao, mo_coeff=None): class IAO_Fragmentation_UHF(Fragmentation_UHF, IAO_Fragmentation): - def get_coeff(self, mo_coeff=None, mo_occ=None, add_virtuals=True): - if mo_coeff is None: mo_coeff = self.mo_coeff - if mo_occ is None: mo_occ = self.mo_occ + if mo_coeff is None: + mo_coeff = self.mo_coeff + if mo_occ is None: + mo_occ = self.mo_occ self.log.info("Alpha-IAOs:") c_iao_a = IAO_Fragmentation.get_coeff(self, mo_coeff=mo_coeff[0], mo_occ=mo_occ[0], add_virtuals=add_virtuals) diff --git a/vayesta/core/fragmentation/iaopao.py b/vayesta/core/fragmentation/iaopao.py index 343c45931..b14985f6a 100644 --- a/vayesta/core/fragmentation/iaopao.py +++ b/vayesta/core/fragmentation/iaopao.py @@ -8,7 +8,6 @@ class IAOPAO_Fragmentation(IAO_Fragmentation): - name = "IAO/PAO" def __init__(self, *args, **kwargs): @@ -28,7 +27,7 @@ def get_pao_coeff(self, iao_coeff): core, valence, rydberg = pyscf.lo.nao._core_val_ryd_list(self.mol) niao = iao_coeff.shape[-1] npao = len(rydberg) - if (niao+npao != self.nao): + if niao + npao != self.nao: self.log.fatal("Incorrect number of PAOs!") self.log.fatal("n(IAO)= %d n(PAO)= %d n(AO)= %d", niao, npao, self.nao) labels = np.asarray(self.mol.ao_labels()) @@ -41,7 +40,7 @@ def get_pao_coeff(self, iao_coeff): if not rydberg: return np.zeros((self.nao, 0)) # "Representation of Rydberg-AOs in terms of AOs" - pao_coeff = np.eye(self.nao)[:,rydberg] + pao_coeff = np.eye(self.nao)[:, rydberg] # Project AOs onto non-IAO space: # (S^-1 - C.CT) . S = (1 - C.CT.S) ovlp = self.get_ovlp() @@ -50,8 +49,9 @@ def get_pao_coeff(self, iao_coeff): # Orthogonalize PAOs: x, e_min = self.symmetric_orth(pao_coeff, ovlp) - self.log.debugv("Lowdin orthogonalization of PAOs: n(in)= %3d -> n(out)= %3d , e(min)= %.3e", - x.shape[0], x.shape[1], e_min) + self.log.debugv( + "Lowdin orthogonalization of PAOs: n(in)= %3d -> n(out)= %3d , e(min)= %.3e", x.shape[0], x.shape[1], e_min + ) if e_min < 1e-10: self.log.warning("Small eigenvalue in Lowdin orthogonalization: %.3e !", e_min) pao_coeff = np.dot(pao_coeff, x) @@ -59,19 +59,21 @@ def get_pao_coeff(self, iao_coeff): def get_coeff(self, order=None): """Make projected atomic orbitals (PAOs).""" - if order is None: order = self.order + if order is None: + order = self.order iao_coeff = IAO_Fragmentation.get_coeff(self, add_virtuals=False) pao_coeff = self.get_pao_coeff(iao_coeff) coeff = spinalg.hstack_matrices(iao_coeff, pao_coeff) - assert (coeff.shape[-1] == self.mf.mo_coeff.shape[-1]) + assert coeff.shape[-1] == self.mf.mo_coeff.shape[-1] # Test orthogonality of IAO+PAO self.check_orthonormal(coeff) if order is not None: - return coeff[:,order] + return coeff[:, order] return coeff def get_labels(self, order=None): - if order is None: order = self.order + if order is None: + order = self.order iao_labels = super().get_labels() core, valence, rydberg = pyscf.lo.nao._core_val_ryd_list(self.mol) pao_labels = [tuple(x) for x in np.asarray(self.mol.ao_labels(None), dtype=tuple)[rydberg]] @@ -83,11 +85,12 @@ def get_labels(self, order=None): def search_labels(self, labels): return self.mol.search_ao_label(labels) -class IAOPAO_Fragmentation_UHF(IAOPAO_Fragmentation, IAO_Fragmentation_UHF): +class IAOPAO_Fragmentation_UHF(IAOPAO_Fragmentation, IAO_Fragmentation_UHF): def get_coeff(self, order=None): """Make projected atomic orbitals (PAOs).""" - if order is None: order = self.order + if order is None: + order = self.order iao_coeff = IAO_Fragmentation_UHF.get_coeff(self, add_virtuals=False) pao_coeff_a = IAOPAO_Fragmentation.get_pao_coeff(self, iao_coeff[0]) @@ -95,25 +98,26 @@ def get_coeff(self, order=None): pao_coeff = (pao_coeff_a, pao_coeff_b) coeff = spinalg.hstack_matrices(iao_coeff, pao_coeff) - assert (coeff[0].shape[-1] == self.mf.mo_coeff[0].shape[-1]) - assert (coeff[1].shape[-1] == self.mf.mo_coeff[1].shape[-1]) + assert coeff[0].shape[-1] == self.mf.mo_coeff[0].shape[-1] + assert coeff[1].shape[-1] == self.mf.mo_coeff[1].shape[-1] # Test orthogonality of IAO+PAO self.check_orthonormal(coeff) if order is not None: - return (coeff[0][:,order], coeff[1][:,order]) + return (coeff[0][:, order], coeff[1][:, order]) return coeff -if __name__ == '__main__': +if __name__ == "__main__": import logging + log = logging.getLogger(__name__) import pyscf.gto import pyscf.scf mol = pyscf.gto.Mole() - mol.atom = 'O 0 0 -1.2 ; C 0 0 0 ; O 0 0 1.2' - mol.basis = 'cc-pVDZ' + mol.atom = "O 0 0 -1.2 ; C 0 0 0 ; O 0 0 1.2" + mol.basis = "cc-pVDZ" mol.build() mf = pyscf.scf.RHF(mol) diff --git a/vayesta/core/fragmentation/sao.py b/vayesta/core/fragmentation/sao.py index f37e50cc1..5795c49ad 100644 --- a/vayesta/core/fragmentation/sao.py +++ b/vayesta/core/fragmentation/sao.py @@ -3,8 +3,8 @@ from vayesta.core.fragmentation.fragmentation import Fragmentation from vayesta.core.fragmentation.ufragmentation import Fragmentation_UHF -class SAO_Fragmentation(Fragmentation): +class SAO_Fragmentation(Fragmentation): name = "SAO" def get_coeff(self): @@ -13,8 +13,9 @@ def get_coeff(self): if np.allclose(ovlp, idt): return idt x, e_min = self.symmetric_orth(idt, ovlp) - self.log.debugv("Lowdin orthogonalization of AOs: n(in)= %3d -> n(out)= %3d , e(min)= %.3e", - x.shape[0], x.shape[1], e_min) + self.log.debugv( + "Lowdin orthogonalization of AOs: n(in)= %3d -> n(out)= %3d , e(min)= %.3e", x.shape[0], x.shape[1], e_min + ) if e_min < 1e-10: self.log.warning("Small eigenvalue in Lowdin orthogonalization: %.3e !", e_min) self.check_orthonormal(x) @@ -26,8 +27,8 @@ def get_labels(self): def search_labels(self, labels): return self.mol.search_ao_label(labels) -class SAO_Fragmentation_UHF(Fragmentation_UHF, SAO_Fragmentation): +class SAO_Fragmentation_UHF(Fragmentation_UHF, SAO_Fragmentation): def get_coeff(self): x = super().get_coeff() return (x, x) diff --git a/vayesta/core/fragmentation/site.py b/vayesta/core/fragmentation/site.py index 5ecb4de61..f38fa2223 100644 --- a/vayesta/core/fragmentation/site.py +++ b/vayesta/core/fragmentation/site.py @@ -1,10 +1,10 @@ from vayesta.core.fragmentation.sao import SAO_Fragmentation from vayesta.core.fragmentation.sao import SAO_Fragmentation_UHF -class Site_Fragmentation(SAO_Fragmentation): +class Site_Fragmentation(SAO_Fragmentation): name = "Site" -class Site_Fragmentation_UHF(SAO_Fragmentation_UHF): +class Site_Fragmentation_UHF(SAO_Fragmentation_UHF): name = "Site" diff --git a/vayesta/core/fragmentation/ufragmentation.py b/vayesta/core/fragmentation/ufragmentation.py index 7e2fa4ad4..f06bf35a2 100644 --- a/vayesta/core/fragmentation/ufragmentation.py +++ b/vayesta/core/fragmentation/ufragmentation.py @@ -4,15 +4,15 @@ # TODO: Allow different indices for alpha and beta + class Fragmentation_UHF(Fragmentation): """Fragmentation for unrestricted HF.""" @property def nmo(self): - return (self.mo_coeff[0].shape[-1], - self.mo_coeff[1].shape[-1]) + return (self.mo_coeff[0].shape[-1], self.mo_coeff[1].shape[-1]) - #def check_orth(self, mo_coeff, mo_name=None, *args, **kwargs): + # def check_orth(self, mo_coeff, mo_name=None, *args, **kwargs): # results = [] # for s, spin in enumerate(('alpha', 'beta')): # results.append(super().check_orth(mo_coeff[s], '%s-%s' % (spin[0], mo_name), *args, **kwargs)) @@ -20,16 +20,13 @@ def nmo(self): def get_frag_coeff(self, indices): """Get fragment coefficients for a given set of orbital indices.""" - c_frag = (self.coeff[0][:,indices].copy(), - self.coeff[1][:,indices].copy()) + c_frag = (self.coeff[0][:, indices].copy(), self.coeff[1][:, indices].copy()) return c_frag def get_env_coeff(self, indices): """Get environment coefficients for a given set of orbital indices.""" - env = [np.ones((self.coeff[0].shape[-1]), dtype=bool), - np.ones((self.coeff[1].shape[-1]), dtype=bool)] + env = [np.ones((self.coeff[0].shape[-1]), dtype=bool), np.ones((self.coeff[1].shape[-1]), dtype=bool)] env[0][indices] = False env[1][indices] = False - c_env = (self.coeff[0][:,env[0]].copy(), - self.coeff[1][:,env[1]].copy()) + c_env = (self.coeff[0][:, env[0]].copy(), self.coeff[1][:, env[1]].copy()) return c_env diff --git a/vayesta/core/helper.py b/vayesta/core/helper.py index ade0b6dab..76d1561d1 100644 --- a/vayesta/core/helper.py +++ b/vayesta/core/helper.py @@ -1,50 +1,57 @@ import numpy as np + def orbital_sign_convention(mo_coeff, inplace=True): if not inplace: mo_coeff = mo_coeff.copy() absmax = np.argmax(abs(mo_coeff), axis=0) nmo = mo_coeff.shape[-1] - swap = mo_coeff[absmax,np.arange(nmo)] < 0 - mo_coeff[:,swap] *= -1 + swap = mo_coeff[absmax, np.arange(nmo)] < 0 + mo_coeff[:, swap] *= -1 signs = np.ones((nmo,), dtype=int) signs[swap] = -1 return mo_coeff, signs + # --- Packing/unpacking arrays + def get_dtype_int(obj): if obj is None: return 0 - dtint = np.asarray(obj.dtype.char, dtype='a8').view(int)[()] + dtint = np.asarray(obj.dtype.char, dtype="a8").view(int)[()] return dtint + def get_dtype(dtint): if dtint == 0: return None - val = np.asarray(dtint).view('a8')[()] + val = np.asarray(dtint).view("a8")[()] dtype = np.dtype(val) return dtype + def pack_metadata(array, maxdim=8): if np.ndim(array) > maxdim: raise NotImplementedError dtint = get_dtype_int(array) if dtint: ndim = array.ndim - shape = list(array.shape) + (maxdim-array.ndim)*[0] + shape = list(array.shape) + (maxdim - array.ndim) * [0] else: ndim = 0 - shape = maxdim*[0] + shape = maxdim * [0] metadata = [dtint, ndim] + shape return np.asarray(metadata, dtype=int) + def unpack_metadata(array, maxdim=8): - metadata = array[:maxdim+2].view(int) + metadata = array[: maxdim + 2].view(int) dtype = get_dtype(metadata[0]) ndim, shape = metadata[1], metadata[2:] return dtype, ndim, shape + def pack_arrays(*arrays, dtype=float, maxdim=8): """Pack multiple arrays into a single array of data type `dtype`. @@ -62,6 +69,7 @@ def pack(array): packed.append(pack(array)) return np.hstack(packed) + def unpack_arrays(packed, dtype=float, maxdim=8): """Unpack a single array of data type `dtype` into multiple arrays. @@ -71,7 +79,7 @@ def unpack_arrays(packed, dtype=float, maxdim=8): while True: if packed.size == 0: break - metadata, packed = np.hsplit(packed, [maxdim+2]) + metadata, packed = np.hsplit(packed, [maxdim + 2]) dtype, ndim, shape = unpack_metadata(metadata) if dtype is None: unpacked.append(None) @@ -83,16 +91,16 @@ def unpack_arrays(packed, dtype=float, maxdim=8): return unpacked -if __name__ == '__main__': +if __name__ == "__main__": import sys arrays_in = [ np.asarray(list(range(100))), None, np.random.rand(70), - #np.random.rand(70)*1j, - #np.asarray([True, False, False]) - ] + # np.random.rand(70)*1j, + # np.asarray([True, False, False]) + ] pack = pack_arrays(*arrays_in) arrays_out = unpack_arrays(pack) assert len(arrays_in) == len(arrays_out) @@ -102,7 +110,7 @@ def unpack_arrays(packed, dtype=float, maxdim=8): else: assert np.all(x == arrays_out[i]) - 1/0 + 1 / 0 obj = np.random.rand(3) dtint = get_dtype_int(obj) diff --git a/vayesta/core/linalg.py b/vayesta/core/linalg.py index faa74b2da..a81980b9e 100644 --- a/vayesta/core/linalg.py +++ b/vayesta/core/linalg.py @@ -4,6 +4,7 @@ log = logging.getLogger(__name__) + def recursive_block_svd(a, n, tol=1e-10, maxblock=100): """Perform SVD of rectangular, offdiagonal blocks of a matrix recursively. @@ -30,15 +31,15 @@ def recursive_block_svd(a, n, tol=1e-10, maxblock=100): size = a.shape[-1] log.debugv("Recursive block SVD of %dx%d matrix" % a.shape) coeff = np.eye(size) - sv = np.full((size-n,), 0.0) - orders = np.full((size-n,), np.inf) + sv = np.full((size - n,), 0.0) + orders = np.full((size - n,), np.inf) ndone = 0 low = np.s_[:n] env = np.s_[n:] - for order in range(1, maxblock+1): - blk = np.linalg.multi_dot((coeff.T, a, coeff))[low,env] + for order in range(1, maxblock + 1): + blk = np.linalg.multi_dot((coeff.T, a, coeff))[low, env] nmax = blk.shape[-1] assert blk.ndim == 2 assert np.all(np.asarray(blk.shape) > 0) @@ -46,37 +47,41 @@ def recursive_block_svd(a, n, tol=1e-10, maxblock=100): u, s, vh = np.linalg.svd(blk) rot = vh.T.conj() ncpl = np.count_nonzero(s >= tol) - log.debugv("Order= %3d - found %3d bath orbitals in %3d with tol= %8.2e: SV= %r" % (order, ncpl, blk.shape[1], tol, s[:ncpl].tolist())) + log.debugv( + "Order= %3d - found %3d bath orbitals in %3d with tol= %8.2e: SV= %r" + % (order, ncpl, blk.shape[1], tol, s[:ncpl].tolist()) + ) if ncpl == 0: log.debugv("Remaining environment orbitals are decoupled; exiting.") break # Update output - coeff[:,env] = np.dot(coeff[:,env], rot) - sv[ndone:(ndone+ncpl)] = s[:ncpl] - orders[ndone:(ndone+ncpl)] = order + coeff[:, env] = np.dot(coeff[:, env], rot) + sv[ndone : (ndone + ncpl)] = s[:ncpl] + orders[ndone : (ndone + ncpl)] = order # Update spaces - low = np.s_[(n+ndone):(n+ndone+ncpl)] - env = np.s_[(n+ndone+ncpl):] + low = np.s_[(n + ndone) : (n + ndone + ncpl)] + env = np.s_[(n + ndone + ncpl) :] ndone += ncpl if ndone == (size - n): log.debugv("All bath orbitals found; exiting.") break - assert (ndone < (size - n)) + assert ndone < (size - n) else: - log.debug("Found %d out of %d bath orbitals in %d recursions", ndone, size-n, maxblock) + log.debug("Found %d out of %d bath orbitals in %d recursions", ndone, size - n, maxblock) - coeff = coeff[n:,n:] - assert np.allclose(np.dot(coeff.T, coeff)-np.eye(coeff.shape[-1]), 0) + coeff = coeff[n:, n:] + assert np.allclose(np.dot(coeff.T, coeff) - np.eye(coeff.shape[-1]), 0) log.debugv("SV= %r", sv) log.debugv("orders= %r", orders) return coeff, sv, orders -if __name__ == '__main__': +if __name__ == "__main__": import pyscf import pyscf.gto import pyscf.scf + atom = """ Ti 0.0 0.0 0.0 O -%f 0.0 0.0 @@ -87,8 +92,8 @@ def recursive_block_svd(a, n, tol=1e-10, maxblock=100): O 0.0 0.0 +%f """ d = 1.85 - basis = '6-31G' - atom = atom % tuple(6*[d]) + basis = "6-31G" + atom = atom % tuple(6 * [d]) mol = pyscf.gto.Mole(atom=atom, basis=basis, charge=-2) mol.build() @@ -97,12 +102,13 @@ def recursive_block_svd(a, n, tol=1e-10, maxblock=100): import vayesta import vayesta.ewf + log = vayesta.log ewf = vayesta.ewf.EWF(hf) - #f = ewf.make_atom_fragment(0) - f = ewf.make_ao_fragment('Ti 3d') + # f = ewf.make_atom_fragment(0) + f = ewf.make_ao_fragment("Ti 3d") c_cluster_occ, c_cluster_vir, c_env_occ, _, c_env_vir, _ = f.make_bath(bath_type=None) - #f.kernel() + # f.kernel() dm_hf = hf.make_rdm1() fock = ewf.get_fock() c_frag = f.c_frag @@ -115,7 +121,7 @@ def recursive_block_svd(a, n, tol=1e-10, maxblock=100): ncpl = len(s) print("Order1 SV= %r" % s) - dmocc2 = np.linalg.multi_dot((mo_svd[:,:ncpl].T, fock, mo_svd[:,ncpl:])) + dmocc2 = np.linalg.multi_dot((mo_svd[:, :ncpl].T, fock, mo_svd[:, ncpl:])) u, s, vh = np.linalg.svd(dmocc2) print("Order2 SV= %r" % s) @@ -133,9 +139,9 @@ def recursive_block_svd(a, n, tol=1e-10, maxblock=100): assert np.allclose(e_svd, e_svd2) # Construct directly - #nocc = np.count_nonzero(hf.mo_occ > 0) - #occ = np.s_[:nocc] - #ovlp = hf.get_ovlp() - #lhs = np.linalg.multi_dot((c_frag.T, ovlp, hf.mo_coeff[:,occ])) - #rhs = np.linalg.multi_dot((c_env_occ.T, ovlp, hf.mo_coeff[:,occ])) - #mo_direct = np.einsum('ai,i,xi->xa', lhs, hf.mo_energy[occ], rhs) + # nocc = np.count_nonzero(hf.mo_occ > 0) + # occ = np.s_[:nocc] + # ovlp = hf.get_ovlp() + # lhs = np.linalg.multi_dot((c_frag.T, ovlp, hf.mo_coeff[:,occ])) + # rhs = np.linalg.multi_dot((c_env_occ.T, ovlp, hf.mo_coeff[:,occ])) + # mo_direct = np.einsum('ai,i,xi->xa', lhs, hf.mo_energy[occ], rhs) diff --git a/vayesta/core/qemb/corrfunc.py b/vayesta/core/qemb/corrfunc.py index 1b219e7fa..681d178d4 100644 --- a/vayesta/core/qemb/corrfunc.py +++ b/vayesta/core/qemb/corrfunc.py @@ -7,30 +7,30 @@ from vayesta.misc import corrfunc -def get_corrfunc_mf(emb, kind, dm1=None, atoms=None, projection='sao', orbital_filter=None): +def get_corrfunc_mf(emb, kind, dm1=None, atoms=None, projection="sao", orbital_filter=None): """dm1 in MO basis""" - if emb.spinsym == 'unrestricted' and kind.lower() in ('n,n', 'dn,dn'): + if emb.spinsym == "unrestricted" and kind.lower() in ("n,n", "dn,dn"): raise NotImplementedError if dm1 is None: - if emb.spinsym == 'restricted': + if emb.spinsym == "restricted": dm1 = np.zeros((emb.nmo, emb.nmo)) dm1[np.diag_indices(emb.nocc)] = 2 - elif emb.spinsym == 'unrestricted': + elif emb.spinsym == "unrestricted": dm1a = np.zeros((emb.nmo[0], emb.nmo[0])) dm1b = np.zeros((emb.nmo[1], emb.nmo[1])) dm1a[np.diag_indices(emb.nocc[0])] = 1 dm1b[np.diag_indices(emb.nocc[1])] = 1 dm1 = (dm1a, dm1b) - if emb.spinsym == 'restricted': + if emb.spinsym == "restricted": funcs = { - 'n,n': functools.partial(corrfunc.chargecharge, subtract_indep=False), - 'dn,dn': functools.partial(corrfunc.chargecharge, subtract_indep=True), - 'sz,sz': corrfunc.spinspin_z, - } - elif emb.spinsym == 'unrestricted': + "n,n": functools.partial(corrfunc.chargecharge, subtract_indep=False), + "dn,dn": functools.partial(corrfunc.chargecharge, subtract_indep=True), + "sz,sz": corrfunc.spinspin_z, + } + elif emb.spinsym == "unrestricted": funcs = { - 'sz,sz': corrfunc.spinspin_z_unrestricted, - } + "sz,sz": corrfunc.spinspin_z_unrestricted, + } func = funcs.get(kind.lower()) if func is None: raise ValueError(kind) @@ -38,12 +38,21 @@ def get_corrfunc_mf(emb, kind, dm1=None, atoms=None, projection='sao', orbital_f corr = np.zeros((len(atoms1), len(atoms2))) for a, atom1 in enumerate(atoms1): for b, atom2 in enumerate(atoms2): - corr[a,b] = func(dm1, None, proj1=proj[atom1], proj2=proj[atom2]) + corr[a, b] = func(dm1, None, proj1=proj[atom1], proj2=proj[atom2]) return corr -def get_corrfunc(emb, kind, dm1=None, dm2=None, atoms=None, projection='sao', dm2_with_dm1=None, use_symmetry=True, - orbital_filter=None): +def get_corrfunc( + emb, + kind, + dm1=None, + dm2=None, + atoms=None, + projection="sao", + dm2_with_dm1=None, + use_symmetry=True, + orbital_filter=None, +): """Get expectation values , where P(X) are projectors onto atoms X. TODO: MPI @@ -65,22 +74,22 @@ def get_corrfunc(emb, kind, dm1=None, dm2=None, atoms=None, projection='sao', dm Atom projected correlation function. """ kind = kind.lower() - if kind not in ('n,n', 'dn,dn', 'sz,sz'): + if kind not in ("n,n", "dn,dn", "sz,sz"): raise ValueError(kind) # --- Setup f1, f2, f22 = { - 'n,n': (1, 2, 1), - 'dn,dn': (1, 2, 1), - 'sz,sz': (1/4, 1/2, 1/2), - }[kind] + "n,n": (1, 2, 1), + "dn,dn": (1, 2, 1), + "sz,sz": (1 / 4, 1 / 2, 1 / 2), + }[kind] if dm2_with_dm1 is None: dm2_with_dm1 = False if dm2 is not None: # Determine if DM2 contains DM1 by calculating norm - norm = einsum('iikk->', dm2) - ne2 = emb.mol.nelectron*(emb.mol.nelectron-1) - dm2_with_dm1 = (norm > ne2/2) + norm = einsum("iikk->", dm2) + ne2 = emb.mol.nelectron * (emb.mol.nelectron - 1) + dm2_with_dm1 = norm > ne2 / 2 atoms1, atoms2, proj = emb._get_atom_projectors(atoms, projection, orbital_filter=orbital_filter) corr = np.zeros((len(atoms1), len(atoms2))) @@ -91,27 +100,27 @@ def get_corrfunc(emb, kind, dm1=None, dm2=None, atoms=None, projection='sao', dm for a, atom1 in enumerate(atoms1): tmp = np.dot(proj[atom1], dm1) for b, atom2 in enumerate(atoms2): - corr[a,b] = f1*np.sum(tmp*proj[atom2]) + corr[a, b] = f1 * np.sum(tmp * proj[atom2]) # Non-(approximate cumulant) DM2 contribution: if not dm2_with_dm1: with log_time(emb.log.timing, "Time for non-cumulant 2-DM contribution: %s"): - occ = np.s_[:emb.nocc] + occ = np.s_[: emb.nocc] occdiag = np.diag_indices(emb.nocc) ddm1 = dm1.copy() ddm1[occdiag] -= 1 for a, atom1 in enumerate(atoms1): tmp = np.dot(proj[atom1], ddm1) for b, atom2 in enumerate(atoms2): - corr[a,b] -= f2*np.sum(tmp[occ] * proj[atom2][occ]) # N_atom^2 * N^2 scaling - if kind in ('n,n', 'dn,dn'): + corr[a, b] -= f2 * np.sum(tmp[occ] * proj[atom2][occ]) # N_atom^2 * N^2 scaling + if kind in ("n,n", "dn,dn"): # These terms are zero for Sz,Sz (but not in UHF) # Traces of projector*DM(HF) and projector*[DM(CC)+DM(HF)/2]: - tr1 = {a: np.trace(p[occ,occ]) for a, p in proj.items()} # DM(HF) - tr2 = {a: np.sum(p * ddm1) for a, p in proj.items()} # DM(CC) + DM(HF)/2 + tr1 = {a: np.trace(p[occ, occ]) for a, p in proj.items()} # DM(HF) + tr2 = {a: np.sum(p * ddm1) for a, p in proj.items()} # DM(CC) + DM(HF)/2 for a, atom1 in enumerate(atoms1): for b, atom2 in enumerate(atoms2): - corr[a,b] += f2*(tr1[atom1]*tr2[atom2] + tr1[atom2]*tr2[atom1]) + corr[a, b] += f2 * (tr1[atom1] * tr2[atom2] + tr1[atom2] * tr2[atom1]) with log_time(emb.log.timing, "Time for cumulant 2-DM contribution: %s"): if dm2 is not None: @@ -120,15 +129,15 @@ def get_corrfunc(emb, kind, dm1=None, dm2=None, atoms=None, projection='sao', dm # DM2(aa) - DM2(ab)] = 2*DM2(aa) - DM2/2 # = DM2/3 - DM2.transpose(0,3,2,1)/3 - DM2/2 # = -DM2/6 - DM2.transpose(0,3,2,1)/3 - if kind in ('n,n', 'dn,dn'): + if kind in ("n,n", "dn,dn"): pass - elif kind == 'sz,sz': + elif kind == "sz,sz": # DM2 is not needed anymore, so we can overwrite: - dm2 = -(dm2/6 + dm2.transpose(0,3,2,1)/3) + dm2 = -(dm2 / 6 + dm2.transpose(0, 3, 2, 1) / 3) for a, atom1 in enumerate(atoms1): tmp = np.tensordot(proj[atom1], dm2) for b, atom2 in enumerate(atoms2): - corr[a,b] += f22*np.sum(tmp*proj[atom2]) + corr[a, b] += f22 * np.sum(tmp * proj[atom2]) else: # Cumulant DM2 contribution: ffilter = dict(sym_parent=None) if use_symmetry else {} @@ -138,15 +147,15 @@ def get_corrfunc(emb, kind, dm1=None, dm2=None, atoms=None, projection='sao', dm # Currently only defined for EWF # (but could also be defined for a democratically partitioned cumulant): dm2 = fx.make_fragment_dm2cumulant() - if kind in ('n,n', 'dn,dn'): + if kind in ("n,n", "dn,dn"): pass # DM2(aa) = (DM2 - DM2.transpose(0,3,2,1))/6 # DM2(ab) = DM2/2 - DM2(aa) # DM2(aa) - DM2(ab)] = 2*DM2(aa) - DM2/2 # = DM2/3 - DM2.transpose(0,3,2,1)/3 - DM2/2 # = -DM2/6 - DM2.transpose(0,3,2,1)/3 - elif kind == 'sz,sz': - dm2 = -(dm2/6 + dm2.transpose(0,3,2,1)/3) + elif kind == "sz,sz": + dm2 = -(dm2 / 6 + dm2.transpose(0, 3, 2, 1) / 3) for fx2, cx2_coeff in fx.loop_symmetry_children([fx.cluster.coeff], include_self=True, maxgen=maxgen): rx = np.dot(cx2_coeff.T, cst) @@ -154,18 +163,28 @@ def get_corrfunc(emb, kind, dm1=None, dm2=None, atoms=None, projection='sao', dm for a, atom1 in enumerate(atoms1): tmp = np.tensordot(projx[atom1], dm2) for b, atom2 in enumerate(atoms2): - corr[a,b] += f22*np.sum(tmp*projx[atom2]) + corr[a, b] += f22 * np.sum(tmp * projx[atom2]) # Remove independent particle [P(A).DM1 * P(B).DM1] contribution - if kind == 'dn,dn': + if kind == "dn,dn": for a, atom1 in enumerate(atoms1): for b, atom2 in enumerate(atoms2): - corr[a,b] -= np.sum(dm1*proj[atom1]) * np.sum(dm1*proj[atom2]) + corr[a, b] -= np.sum(dm1 * proj[atom1]) * np.sum(dm1 * proj[atom2]) return corr -def get_corrfunc_unrestricted(emb, kind, dm1=None, dm2=None, atoms=None, projection='sao', dm2_with_dm1=None, - use_symmetry=True, orbital_filter=None): + +def get_corrfunc_unrestricted( + emb, + kind, + dm1=None, + dm2=None, + atoms=None, + projection="sao", + dm2_with_dm1=None, + use_symmetry=True, + orbital_filter=None, +): """Get expectation values , where P(X) are projectors onto atoms X. TODO: MPI @@ -187,23 +206,23 @@ def get_corrfunc_unrestricted(emb, kind, dm1=None, dm2=None, atoms=None, project Atom projected correlation function. """ kind = kind.lower() - #if kind not in ('n,n', 'dn,dn', 'sz,sz'): - if kind not in ('sz,sz',): + # if kind not in ('n,n', 'dn,dn', 'sz,sz'): + if kind not in ("sz,sz",): raise ValueError(kind) # --- Setup f1, f2, f22 = { - #'n,n': (1, 2, 1), - #'dn,dn': (1, 2, 1), - 'sz,sz': (1/4, 1/2, 1/4), - }[kind] + #'n,n': (1, 2, 1), + #'dn,dn': (1, 2, 1), + "sz,sz": (1 / 4, 1 / 2, 1 / 4), + }[kind] if dm2_with_dm1 is None: dm2_with_dm1 = False if dm2 is not None: # Determine if DM2 contains DM1 by calculating norm - norm = einsum('iikk->', dm2[0]) + 2*einsum('iikk->', dm2[1]) + einsum('iikk->', dm2[2]) - ne2 = emb.mol.nelectron*(emb.mol.nelectron-1) - dm2_with_dm1 = (norm > ne2/2) + norm = einsum("iikk->", dm2[0]) + 2 * einsum("iikk->", dm2[1]) + einsum("iikk->", dm2[2]) + ne2 = emb.mol.nelectron * (emb.mol.nelectron - 1) + dm2_with_dm1 = norm > ne2 / 2 atoms1, atoms2, proj = emb._get_atom_projectors(atoms, projection, orbital_filter=orbital_filter) corr = np.zeros((len(atoms1), len(atoms2))) @@ -216,7 +235,7 @@ def get_corrfunc_unrestricted(emb, kind, dm1=None, dm2=None, atoms=None, project for a, atom1 in enumerate(atoms1): tmp = np.dot(proj[atom1][s], dm1[s]) for b, atom2 in enumerate(atoms2): - corr[a,b] += f1*np.sum(tmp*proj[atom2][s]) + corr[a, b] += f1 * np.sum(tmp * proj[atom2][s]) # Non-(approximate cumulant) DM2 contribution: if not dm2_with_dm1: @@ -225,11 +244,11 @@ def get_corrfunc_unrestricted(emb, kind, dm1=None, dm2=None, atoms=None, project ddm1[0][np.diag_indices(emb.nocc[0])] -= 0.5 ddm1[1][np.diag_indices(emb.nocc[1])] -= 0.5 for s in range(2): - occ = np.s_[:emb.nocc[s]] + occ = np.s_[: emb.nocc[s]] for a, atom1 in enumerate(atoms1): tmp = np.dot(proj[atom1][s], ddm1[s]) for b, atom2 in enumerate(atoms2): - corr[a,b] -= f2*np.sum(tmp[occ] * proj[atom2][s][occ]) # N_atom^2 * N^2 scaling + corr[a, b] -= f2 * np.sum(tmp[occ] * proj[atom2][s][occ]) # N_atom^2 * N^2 scaling ## Note that this contribution cancel to 0 in RHF, # since tr1[0] == tr1[1] and tr2[0] == tr2[1]: @@ -237,39 +256,39 @@ def get_corrfunc_unrestricted(emb, kind, dm1=None, dm2=None, atoms=None, project tr2 = [] # Loop over spin: for s in range(2): - occ = np.s_[:emb.nocc[s]] - tr1.append({a: np.trace(p[s][occ,occ]) for a, p in proj.items()}) # DM(HF) - tr2.append({a: np.sum(p[s] * ddm1[s]) for a, p in proj.items()}) # DM(CC) + DM(HF)/2 + occ = np.s_[: emb.nocc[s]] + tr1.append({a: np.trace(p[s][occ, occ]) for a, p in proj.items()}) # DM(HF) + tr2.append({a: np.sum(p[s] * ddm1[s]) for a, p in proj.items()}) # DM(CC) + DM(HF)/2 # Loop over spins s1, s2: for s1, s2 in itertools.product(range(2), repeat=2): - sign = (1 if (s1 == s2) else -1) + sign = 1 if (s1 == s2) else -1 for a, atom1 in enumerate(atoms1): for b, atom2 in enumerate(atoms2): - corr[a,b] += sign*(tr1[s1][atom1]*tr2[s2][atom2] + tr1[s1][atom2]*tr2[s2][atom1])/4 + corr[a, b] += sign * (tr1[s1][atom1] * tr2[s2][atom2] + tr1[s1][atom2] * tr2[s2][atom1]) / 4 - if kind in ('n,n', 'dn,dn'): + if kind in ("n,n", "dn,dn"): # TODO raise NotImplementedError # These terms are zero for Sz,Sz (but not in UHF) # Traces of projector*DM(HF) and projector*[DM(CC)+DM(HF)/2]: - tr1 = {a: np.trace(p[occ,occ]) for a, p in proj.items()} # DM(HF) - tr2 = {a: np.sum(p * ddm1) for a, p in proj.items()} # DM(CC) + DM(HF)/2 + tr1 = {a: np.trace(p[occ, occ]) for a, p in proj.items()} # DM(HF) + tr2 = {a: np.sum(p * ddm1) for a, p in proj.items()} # DM(CC) + DM(HF)/2 for a, atom1 in enumerate(atoms1): for b, atom2 in enumerate(atoms2): - corr[a,b] += f2*(tr1[atom1]*tr2[atom2] + tr1[atom2]*tr2[atom1]) + corr[a, b] += f2 * (tr1[atom1] * tr2[atom2] + tr1[atom2] * tr2[atom1]) with log_time(emb.log.timing, "Time for cumulant 2-DM contribution: %s"): if dm2 is not None: dm2aa, dm2ab, dm2bb = dm2 - if kind in ('n,n', 'dn,dn'): + if kind in ("n,n", "dn,dn"): raise NotImplementedError - elif kind == 'sz,sz': + elif kind == "sz,sz": pass for a, atom1 in enumerate(atoms1): tmpa = np.tensordot(proj[atom1][0], dm2aa) - np.tensordot(dm2ab, proj[atom1][1]) tmpb = np.tensordot(proj[atom1][1], dm2bb) - np.tensordot(proj[atom1][0], dm2ab) for b, atom2 in enumerate(atoms2): - corr[a,b] += f22*(np.sum(tmpa*proj[atom2][0]) + np.sum(tmpb*proj[atom2][1])) + corr[a, b] += f22 * (np.sum(tmpa * proj[atom2][0]) + np.sum(tmpb * proj[atom2][1])) else: # Cumulant DM2 contribution: ffilter = dict(sym_parent=None) if use_symmetry else {} @@ -281,29 +300,32 @@ def get_corrfunc_unrestricted(emb, kind, dm1=None, dm2=None, atoms=None, project # Currently only defined for EWF # (but could also be defined for a democratically partitioned cumulant): dm2aa, dm2ab, dm2bb = fx.make_fragment_dm2cumulant() - if kind in ('n,n', 'dn,dn'): + if kind in ("n,n", "dn,dn"): # TODO raise NotImplementedError - if kind == 'sz,sz': + if kind == "sz,sz": pass - for fx2, (cx2_coeffa, cx2_coeffb) in fx.loop_symmetry_children([fx.cluster.coeff[0], fx.cluster.coeff[1]], - include_self=True, maxgen=maxgen): + for fx2, (cx2_coeffa, cx2_coeffb) in fx.loop_symmetry_children( + [fx.cluster.coeff[0], fx.cluster.coeff[1]], include_self=True, maxgen=maxgen + ): rxa = np.dot(cx2_coeffa.T, csta) rxb = np.dot(cx2_coeffb.T, cstb) - projx = {atom: (dot(rxa, p_atom[0], rxa.T), dot(rxb, p_atom[1], rxb.T)) - for (atom, p_atom) in proj.items()} + projx = { + atom: (dot(rxa, p_atom[0], rxa.T), dot(rxb, p_atom[1], rxb.T)) + for (atom, p_atom) in proj.items() + } for a, atom1 in enumerate(atoms1): tmpa = np.tensordot(projx[atom1][0], dm2aa) - np.tensordot(dm2ab, projx[atom1][1]) tmpb = np.tensordot(projx[atom1][1], dm2bb) - np.tensordot(projx[atom1][0], dm2ab) for b, atom2 in enumerate(atoms2): - corr[a,b] += f22*(np.sum(tmpa*projx[atom2][0]) + np.sum(tmpb*projx[atom2][1])) + corr[a, b] += f22 * (np.sum(tmpa * projx[atom2][0]) + np.sum(tmpb * projx[atom2][1])) # Remove independent particle [P(A).DM1 * P(B).DM1] contribution - if kind == 'dn,dn': + if kind == "dn,dn": # TODO raise NotImplementedError for a, atom1 in enumerate(atoms1): for b, atom2 in enumerate(atoms2): - corr[a,b] -= np.sum(dm1*proj[atom1]) * np.sum(dm1*proj[atom2]) + corr[a, b] -= np.sum(dm1 * proj[atom1]) * np.sum(dm1 * proj[atom2]) return corr diff --git a/vayesta/core/qemb/fragment.py b/vayesta/core/qemb/fragment.py index 40e79991d..cb6c3861c 100644 --- a/vayesta/core/qemb/fragment.py +++ b/vayesta/core/qemb/fragment.py @@ -3,13 +3,27 @@ import itertools import os.path import typing + # --- External import numpy as np import pyscf import pyscf.lo + # --- Internal -from vayesta.core.util import (OptionsBase, cache, deprecated, dot, einsum, energy_string, fix_orbital_sign, hstack, - log_time, time_string, timer, AbstractMethodError) +from vayesta.core.util import ( + OptionsBase, + cache, + deprecated, + dot, + einsum, + energy_string, + fix_orbital_sign, + hstack, + log_time, + time_string, + timer, + AbstractMethodError, +) from vayesta.core import spinalg from vayesta.core.types import Cluster, Orbitals from vayesta.core.symmetry import SymmetryIdentity @@ -17,6 +31,7 @@ import vayesta.core.ao2mo import vayesta.core.ao2mo.helper from vayesta.core.types import WaveFunction + # Bath from vayesta.core.bath import BNO_Threshold from vayesta.core.bath import DMET_Bath @@ -25,6 +40,7 @@ from vayesta.core.bath import Full_Bath from vayesta.core.bath import R2_Bath from vayesta.core.bath import RPA_Bath + # Bosonic Bath from vayesta.core.bosonic_bath import RPA_Boson_Target_Space, RPA_QBA_Bath, Boson_Threshold from vayesta.core.types.bosonic_orbitals import QuasiBosonOrbitals @@ -36,7 +52,7 @@ from vayesta.core.screening.screening_crpa import get_frag_W # Get MPI rank of fragment -get_fragment_mpi_rank = lambda *args : args[0].mpi_rank +get_fragment_mpi_rank = lambda *args: args[0].mpi_rank @dataclasses.dataclass @@ -49,8 +65,8 @@ class Options(OptionsBase): # --- Solver options solver_options: dict = None # --- Other - store_eris: bool = None # If True, ERIs will be cached in Fragment.hamil - dm_with_frozen: bool = None # TODO: is still used? + store_eris: bool = None # If True, ERIs will be cached in Fragment.hamil + dm_with_frozen: bool = None # TODO: is still used? screening: typing.Optional[str] = None match_cluster_fock: bool = None # Fragment specific @@ -62,7 +78,6 @@ class Options(OptionsBase): class Fragment: - Options = Options @dataclasses.dataclass @@ -77,23 +92,36 @@ class Flags: @dataclasses.dataclass class Results: - fid: int = None # Fragment ID - converged: bool = None # True, if solver reached convergence criterion or no convergence required (eg. MP2 solver) + fid: int = None # Fragment ID + converged: bool = ( + None # True, if solver reached convergence criterion or no convergence required (eg. MP2 solver) + ) # --- Energies - e_corr: float = None # Fragment correlation energy contribution - e_corr_rpa: float = None # Fragment correlation energy contribution at the level of RPA. + e_corr: float = None # Fragment correlation energy contribution + e_corr_rpa: float = None # Fragment correlation energy contribution at the level of RPA. # --- Wave-function - wf: WaveFunction = None # WaveFunction object (MP2, CCSD,...) - pwf: WaveFunction = None # Fragment-projected wave function + wf: WaveFunction = None # WaveFunction object (MP2, CCSD,...) + pwf: WaveFunction = None # Fragment-projected wave function moms: tuple = None - - def __init__(self, base, fid, name, c_frag, c_env, - solver=None, - atoms=None, aos=None, active=True, - sym_parent=None, sym_op=None, - mpi_rank=0, flags=None, - log=None, **kwargs): + def __init__( + self, + base, + fid, + name, + c_frag, + c_env, + solver=None, + atoms=None, + aos=None, + active=True, + sym_parent=None, + sym_op=None, + mpi_rank=0, + flags=None, + log=None, + **kwargs, + ): """Abstract base class for quantum embedding fragments. The fragment may keep track of associated atoms or atomic orbitals, using @@ -162,9 +190,9 @@ def __init__(self, base, fid, name, c_frag, c_env, self.base = base # Options - self.opts = self.Options() # Default options - self.opts.update(**self.base.opts.asdict(deepcopy=True)) # Update with embedding class options - self.opts.replace(**kwargs) # Replace with keyword arguments + self.opts = self.Options() # Default options + self.opts.update(**self.base.opts.asdict(deepcopy=True)) # Update with embedding class options + self.opts.replace(**kwargs) # Replace with keyword arguments # Flags self.flags = self.Flags(**(flags or {})) @@ -195,27 +223,26 @@ def __init__(self, base, fid, name, c_frag, c_env, self.reset() self.log.debugv("Creating %r", self) - #self.log.info(break_into_lines(str(self.opts), newline='\n ')) + # self.log.info(break_into_lines(str(self.opts), newline='\n ')) def __repr__(self): if mpi: - return '%s(id= %d, name= %s, mpi_rank= %d)' % ( - self.__class__.__name__, self.id, self.name, self.mpi_rank) - return '%s(id= %d, name= %s)' % (self.__class__.__name__, self.id, self.name) + return "%s(id= %d, name= %s, mpi_rank= %d)" % (self.__class__.__name__, self.id, self.name, self.mpi_rank) + return "%s(id= %d, name= %s)" % (self.__class__.__name__, self.id, self.name) def __str__(self): - return '%s %d: %s' % (self.__class__.__name__, self.id, self.name) + return "%s %d: %s" % (self.__class__.__name__, self.id, self.name) def log_info(self): # Some output - fmt = ' > %-24s ' - self.log.info(fmt+'%d', "Fragment orbitals:", self.n_frag) - self.log.info(fmt+'%f', "Symmetry factor:", self.sym_factor) - self.log.info(fmt+'%.10f', "Number of electrons:", self.nelectron) + fmt = " > %-24s " + self.log.info(fmt + "%d", "Fragment orbitals:", self.n_frag) + self.log.info(fmt + "%f", "Symmetry factor:", self.sym_factor) + self.log.info(fmt + "%.10f", "Number of electrons:", self.nelectron) if self.atoms is not None: - self.log.info(fmt+'%r', "Associated atoms:", self.atoms) + self.log.info(fmt + "%r", "Associated atoms:", self.atoms) if self.aos is not None: - self.log.info(fmt+'%r', "Associated AOs:", self.aos) + self.log.info(fmt + "%r", "Associated AOs:", self.aos) @property def mol(self): @@ -234,7 +261,7 @@ def n_frag(self): def nelectron(self): """Number of mean-field electrons.""" sc = np.dot(self.base.get_ovlp(), self.c_frag) - ne = einsum('ai,ab,bi->', sc, self.mf.make_rdm1(), sc) + ne = einsum("ai,ab,bi->", sc, self.mf.make_rdm1(), sc) return ne def trimmed_name(self, length=10, add_dots=True): @@ -242,7 +269,7 @@ def trimmed_name(self, length=10, add_dots=True): if len(self.name) <= length: return self.name if add_dots: - return self.name[:(length-3)] + "..." + return self.name[: (length - 3)] + "..." return self.name[:length] @property @@ -291,35 +318,35 @@ def get_overlap(self, key): >>> s = self.get_overlap('mo[occ]|cluster[occ]') >>> s = self.get_overlap('mo[vir]|cluster[vir]') """ - if key.count('|') > 1: - left, center, right = key.rsplit('|', maxsplit=2) - overlap_left = self.get_overlap('|'.join((left, center))) - overlap_right = self.get_overlap('|'.join((center, right))) + if key.count("|") > 1: + left, center, right = key.rsplit("|", maxsplit=2) + overlap_left = self.get_overlap("|".join((left, center))) + overlap_right = self.get_overlap("|".join((center, right))) return self._csc_dot(overlap_left, overlap_right, ovlp=None, transpose_left=False) # Standardize key to reduce cache misses: - key_mod = key.lower().replace(' ', '') + key_mod = key.lower().replace(" ", "") if key_mod != key: return self.get_overlap(key_mod) def _get_coeff(key): - if 'frag' in key: + if "frag" in key: return self.c_frag - if 'proj' in key: + if "proj" in key: return self.c_proj - if 'occ' in key: - part = '_occ' - elif 'vir' in key: - part = '_vir' + if "occ" in key: + part = "_occ" + elif "vir" in key: + part = "_vir" else: - part = '' - if 'mo' in key: - return getattr(self.base, 'mo_coeff%s' % part) - if 'cluster' in key: - return getattr(self.cluster, 'c_active%s' % part) + part = "" + if "mo" in key: + return getattr(self.base, "mo_coeff%s" % part) + if "cluster" in key: + return getattr(self.cluster, "c_active%s" % part) raise ValueError("Invalid key: '%s'") - left, right = key.split('|') + left, right = key.split("|") c_left = _get_coeff(left) c_right = _get_coeff(right) return self._csc_dot(c_left, c_right) @@ -352,8 +379,14 @@ def cluster(self, value): self._cluster = value def reset(self, reset_bath=True, reset_cluster=True, reset_eris=True, reset_inactive=True): - self.log.debugv("Resetting %s (reset_bath= %r, reset_cluster= %r, reset_eris= %r, reset_inactive= %r)", - self, reset_bath, reset_cluster, reset_eris, reset_inactive) + self.log.debugv( + "Resetting %s (reset_bath= %r, reset_cluster= %r, reset_eris= %r, reset_inactive= %r)", + self, + reset_bath, + reset_cluster, + reset_eris, + reset_inactive, + ) if not reset_inactive and not self.active: return if reset_bath: @@ -370,20 +403,22 @@ def reset(self, reset_bath=True, reset_cluster=True, reset_eris=True, reset_inac def get_fragments_with_overlap(self, tol=1e-8, **kwargs): """Get list of fragments which overlap both in occupied and virtual space.""" - c_occ = self.get_overlap('mo[occ]|cluster[occ]') - c_vir = self.get_overlap('mo[vir]|cluster[vir]') + c_occ = self.get_overlap("mo[occ]|cluster[occ]") + c_vir = self.get_overlap("mo[vir]|cluster[vir]") + def svd(cx, cy): rxy = np.dot(cx.T, cy) return np.linalg.svd(rxy, compute_uv=False) + frags = [] for fx in self.base.get_fragments(**kwargs): - if (fx.id == self.id): + if fx.id == self.id: continue - cx_occ = fx.get_overlap('mo[occ]|cluster[occ]') + cx_occ = fx.get_overlap("mo[occ]|cluster[occ]") s_occ = svd(c_occ, cx_occ) if s_occ.max() < tol: continue - cx_vir = fx.get_overlap('mo[vir]|cluster[vir]') + cx_vir = fx.get_overlap("mo[vir]|cluster[vir]") s_vir = svd(c_vir, cx_vir) if s_vir.max() < tol: continue @@ -406,15 +441,15 @@ def get_fragment_mf_energy(self): Does not include nuclear-nuclear repulsion! """ px = self.get_fragment_projector(self.base.mo_coeff) - hveff = dot(px, self.base.mo_coeff.T, 2*self.base.get_hcore()+self.base.get_veff(), self.base.mo_coeff) - occ = (self.base.mo_occ > 0) + hveff = dot(px, self.base.mo_coeff.T, 2 * self.base.get_hcore() + self.base.get_veff(), self.base.mo_coeff) + occ = self.base.mo_occ > 0 e_mf = np.sum(np.diag(hveff)[occ]) return e_mf @property def contributes(self): """True if fragment contributes to expectation values, else False.""" - return (self.active and not self.opts.auxiliary) + return self.active and not self.opts.auxiliary def get_fragment_projector(self, coeff, c_proj=None, inverse=False): """Projector for one index of amplitudes local energy expression. @@ -433,11 +468,12 @@ def get_fragment_projector(self, coeff, c_proj=None, inverse=False): p : (n, n) array Projection matrix. """ - if c_proj is None: c_proj = self.c_proj + if c_proj is None: + c_proj = self.c_proj r = dot(coeff.T, self.base.get_ovlp(), c_proj) p = np.dot(r, r.T) if inverse: - p = (np.eye(p.shape[-1]) - p) + p = np.eye(p.shape[-1]) - p return p def get_mo_occupation(self, *mo_coeff, dm1=None): @@ -454,12 +490,13 @@ def get_mo_occupation(self, *mo_coeff, dm1=None): Occupation numbers of orbitals. """ mo_coeff = hstack(*mo_coeff) - if dm1 is None: dm1 = self.mf.make_rdm1() + if dm1 is None: + dm1 = self.mf.make_rdm1() sc = np.dot(self.base.get_ovlp(), mo_coeff) - occup = einsum('ai,ab,bi->i', sc, dm1, sc) + occup = einsum("ai,ab,bi->i", sc, dm1, sc) return occup - #def check_mo_occupation(self, expected, *mo_coeff, tol=None): + # def check_mo_occupation(self, expected, *mo_coeff, tol=None): # if tol is None: tol = 2*self.opts.dmet_threshold # occup = self.get_mo_occupation(*mo_coeff) # if not np.allclose(occup, expected, atol=tol): @@ -485,7 +522,8 @@ def canonicalize_mo(self, *mo_coeff, fock=None, eigvals=False, sign_convention=T rot : ndarray Rotation matrix: np.dot(mo_coeff, rot) = mo_canon. """ - if fock is None: fock = self.base.get_fock() + if fock is None: + fock = self.base.get_fock() mo_coeff = hstack(*mo_coeff) fock = dot(mo_coeff.T, fock, mo_coeff) mo_energy, rot = np.linalg.eigh(fock) @@ -493,7 +531,7 @@ def canonicalize_mo(self, *mo_coeff, fock=None, eigvals=False, sign_convention=T mo_can = np.dot(mo_coeff, rot) if sign_convention: mo_can, signs = fix_orbital_sign(mo_can) - rot = rot*signs[np.newaxis] + rot = rot * signs[np.newaxis] assert np.allclose(np.dot(mo_coeff, rot), mo_can) assert np.allclose(np.dot(mo_can, rot.T), mo_coeff) if eigvals: @@ -521,17 +559,18 @@ def diagonalize_cluster_dm(self, *mo_coeff, dm1=None, norm=2, tol=1e-4): c_cluster_vir: (n(AO), n(vir cluster)) array Virtual cluster orbital coefficients. """ - if dm1 is None: dm1 = self.mf.make_rdm1() + if dm1 is None: + dm1 = self.mf.make_rdm1() c_cluster = hstack(*mo_coeff) sc = np.dot(self.base.get_ovlp(), c_cluster) dm = dot(sc.T, dm1, sc) e, r = np.linalg.eigh(dm) - if tol and not np.allclose(np.fmin(abs(e), abs(e-norm)), 0, atol=2*tol, rtol=0): + if tol and not np.allclose(np.fmin(abs(e), abs(e - norm)), 0, atol=2 * tol, rtol=0): self.log.warn("Eigenvalues of cluster-DM not all close to 0 or %d:\n%s" % (norm, e)) - e, r = e[::-1], r[:,::-1] + e, r = e[::-1], r[:, ::-1] c_cluster = np.dot(c_cluster, r) c_cluster = fix_orbital_sign(c_cluster)[0] - nocc = np.count_nonzero(e >= (norm/2)) + nocc = np.count_nonzero(e >= (norm / 2)) c_cluster_occ, c_cluster_vir = np.hsplit(c_cluster, [nocc]) return c_cluster_occ, c_cluster_vir @@ -549,18 +588,18 @@ def project_ref_orbitals(self, c_ref, c): Orbital coefficients of reference orbitals. """ nref = c_ref.shape[-1] - assert (nref > 0) - assert (c.shape[-1] > 0) + assert nref > 0 + assert c.shape[-1] > 0 self.log.debug("Projecting %d reference orbitals into space of %d orbitals", nref, c.shape[-1]) s = self.base.get_ovlp() # Diagonalize reference orbitals among themselves (due to change in overlap matrix) c_ref_orth = pyscf.lo.vec_lowdin(c_ref, s) - assert (c_ref_orth.shape == c_ref.shape) + assert c_ref_orth.shape == c_ref.shape # Diagonalize projector in space csc = np.linalg.multi_dot((c_ref_orth.T, s, c)) p = np.dot(csc.T, csc) e, r = np.linalg.eigh(p) - e, r = e[::-1], r[:,::-1] + e, r = e[::-1], r[:, ::-1] c = np.dot(c, r) return c, e @@ -568,14 +607,14 @@ def project_ref_orbitals(self, c_ref, c): # --- Symmetry # ============ - def copy(self, fid=None, name=None, **kwargs): + def copy(self, fid=None, name=None, **kwargs): """Create copy of fragment, without adding it to the fragments list.""" if fid is None: fid = self.base.register.get_next_id() - name = name or ('%s(copy)' % self.name) + name = name or ("%s(copy)" % self.name) kwargs_copy = self.opts.asdict().copy() kwargs_copy.update(kwargs) - attrs = ['c_frag', 'c_env', 'solver', 'atoms', 'aos', 'active', 'sym_parent', 'sym_op', 'mpi_rank', 'log'] + attrs = ["c_frag", "c_env", "solver", "atoms", "aos", "active", "sym_parent", "sym_op", "mpi_rank", "log"] for attr in attrs: if attr in kwargs_copy: continue @@ -606,45 +645,76 @@ def add_tsymmetric_fragments(self, tvecs, symtol=1e-6): fragments = [] for i, (dx, dy, dz) in enumerate(itertools.product(range(tvecs[0]), range(tvecs[1]), range(tvecs[2]))): - if i == 0: continue - tvec = (dx/tvecs[0], dy/tvecs[1], dz/tvecs[2]) + if i == 0: + continue + tvec = (dx / tvecs[0], dy / tvecs[1], dz / tvecs[2]) sym_op = SymmetryTranslation(self.base.symmetry, tvec) if sym_op is None: - self.log.error("No T-symmetric fragment found for translation (%d,%d,%d) of fragment %s", dx, dy, dz, self.name) + self.log.error( + "No T-symmetric fragment found for translation (%d,%d,%d) of fragment %s", dx, dy, dz, self.name + ) continue # Name for translationally related fragments - name = '%s_T(%d,%d,%d)' % (self.name, dx, dy, dz) + name = "%s_T(%d,%d,%d)" % (self.name, dx, dy, dz) # Translated coefficients c_frag_t = sym_op(self.c_frag) c_env_t = None # Avoid expensive symmetry operation on environment orbitals # Check that translated fragment does not overlap with current fragment: fragovlp = self._csc_dot(self.c_frag, c_frag_t, ovlp=ovlp) - if self.base.spinsym == 'restricted': + if self.base.spinsym == "restricted": fragovlp = abs(fragovlp).max() - elif self.base.spinsym == 'unrestricted': + elif self.base.spinsym == "unrestricted": fragovlp = max(abs(fragovlp[0]).max(), abs(fragovlp[1]).max()) - if (fragovlp > 1e-8): - self.log.critical("Translation (%d,%d,%d) of fragment %s not orthogonal to original fragment (overlap= %.3e)!", - dx, dy, dz, self.name, fragovlp) + if fragovlp > 1e-8: + self.log.critical( + "Translation (%d,%d,%d) of fragment %s not orthogonal to original fragment (overlap= %.3e)!", + dx, + dy, + dz, + self.name, + fragovlp, + ) raise RuntimeError("Overlapping fragment spaces.") # Add fragment frag_id = self.base.register.get_next_id() - frag = self.base.Fragment(self.base, frag_id, name, c_frag_t, c_env_t, - sym_parent=self, sym_op=sym_op, mpi_rank=self.mpi_rank, - **self.opts.asdict()) + frag = self.base.Fragment( + self.base, + frag_id, + name, + c_frag_t, + c_env_t, + sym_parent=self, + sym_op=sym_op, + mpi_rank=self.mpi_rank, + **self.opts.asdict(), + ) self.base.fragments.append(frag) # Check symmetry # (only for the primitive translations (1,0,0), (0,1,0), and (0,0,1) to reduce number of sym_op(c_env) calls) - if (abs(dx)+abs(dy)+abs(dz) == 1): + if abs(dx) + abs(dy) + abs(dz) == 1: charge_err, spin_err = self.get_symmetry_error(frag, dm1=dm1) if max(charge_err, spin_err) > symtol: - self.log.critical("Mean-field DM1 not symmetric for translation (%d,%d,%d) of %s (errors: charge= %.3e, spin= %.3e)!", - dx, dy, dz, self.name, charge_err, spin_err) + self.log.critical( + "Mean-field DM1 not symmetric for translation (%d,%d,%d) of %s (errors: charge= %.3e, spin= %.3e)!", + dx, + dy, + dz, + self.name, + charge_err, + spin_err, + ) raise RuntimeError("MF not symmetric under translation (%d,%d,%d)" % (dx, dy, dz)) else: - self.log.debugv("Mean-field DM symmetry error for translation (%d,%d,%d) of %s: charge= %.3e, spin= %.3e", - dx, dy, dz, self.name, charge_err, spin_err) + self.log.debugv( + "Mean-field DM symmetry error for translation (%d,%d,%d) of %s: charge= %.3e, spin= %.3e", + dx, + dy, + dz, + self.name, + charge_err, + spin_err, + ) fragments.append(frag) return fragments @@ -696,7 +766,7 @@ def get_symmetry_tree(self, maxgen=None, **filters): # Get direct children: children = self.get_symmetry_children(maxgen=1, **filters) # Build tree recursively: - tree = [(x, x.get_symmetry_tree(maxgen=maxgen-1, **filters)) for x in children] + tree = [(x, x.get_symmetry_tree(maxgen=maxgen - 1, **filters)) for x in children] return tree def loop_symmetry_children(self, arrays=None, axes=None, symtree=None, maxgen=None, include_self=False): @@ -728,14 +798,16 @@ def get_yield(fragment, arrays): if arrays is None: arrays = [] if axes is None: - axes = len(arrays)*[0] + axes = len(arrays) * [0] if symtree is None: symtree = self.get_symmetry_tree() for child, grandchildren in symtree: intermediates = [child.sym_op(arr, axis=axis) for (arr, axis) in zip(arrays, axes)] yield get_yield(child, intermediates) if grandchildren and maxgen > 1: - yield from child.loop_symmetry_children(intermediates, axes=axes, symtree=grandchildren, maxgen=(maxgen-1)) + yield from child.loop_symmetry_children( + intermediates, axes=axes, symtree=grandchildren, maxgen=(maxgen - 1) + ) @property def n_symmetry_children(self): @@ -745,7 +817,7 @@ def n_symmetry_children(self): @property def symmetry_factor(self): """Includes children of children, etc.""" - return (self.n_symmetry_children+1) + return self.n_symmetry_children + 1 def get_symmetry_error(self, frag, dm1=None): """Get translational symmetry error between two fragments.""" @@ -761,7 +833,7 @@ def get_symmetry_error(self, frag, dm1=None): err = abs(dmx - dmy).max() return err, 0.0 - #def check_mf_tsymmetry(self): + # def check_mf_tsymmetry(self): # """Check translational symmetry of the mean-field between fragment and its children.""" # ovlp = self.base.get_ovlp() # sds = dot(ovlp, self.mf.make_rdm1(), ovlp) @@ -784,92 +856,103 @@ def get_symmetry_error(self, frag, dm1=None): def _get_bath_option(self, key, occtype): """Get bath-option, checking if a occupied-virtual specific option has been set.""" opts = self.opts.bath_options - opt = opts.get('%s_%s' % (key, occtype[:3]), None) + opt = opts.get("%s_%s" % (key, occtype[:3]), None) if opt is not None: return opt return opts[key] def make_bath(self): - # --- DMET bath - dmet = DMET_Bath(self, dmet_threshold=self.opts.bath_options['dmet_threshold']) + dmet = DMET_Bath(self, dmet_threshold=self.opts.bath_options["dmet_threshold"]) dmet.kernel() self._dmet_bath = dmet # --- Additional bath def get_bath(occtype): otype = occtype[:3] - assert otype in ('occ', 'vir') - btype = self._get_bath_option('bathtype', occtype) + assert otype in ("occ", "vir") + btype = self._get_bath_option("bathtype", occtype) # DMET bath only - if btype == 'dmet': + if btype == "dmet": return None # Full bath (for debugging) - if btype == 'full': + if btype == "full": return Full_Bath(self, dmet_bath=dmet, occtype=occtype) # Energy-weighted DMET bath - if btype == 'ewdmet': - threshold = self._get_bath_option('threshold', occtype) - max_order = self._get_bath_option('max_order', occtype) + if btype == "ewdmet": + threshold = self._get_bath_option("threshold", occtype) + max_order = self._get_bath_option("max_order", occtype) return EwDMET_Bath(self, dmet_bath=dmet, occtype=occtype, threshold=threshold, max_order=max_order) # Spatially close orbitals - if btype == 'r2': + if btype == "r2": return R2_Bath(self, dmet, occtype=occtype) # MP2 bath natural orbitals - if btype == 'mp2': - project_dmet_order = self._get_bath_option('project_dmet_order', occtype) - project_dmet_mode = self._get_bath_option('project_dmet_mode', occtype) - addbuffer = self._get_bath_option('addbuffer', occtype) and occtype == 'virtual' + if btype == "mp2": + project_dmet_order = self._get_bath_option("project_dmet_order", occtype) + project_dmet_mode = self._get_bath_option("project_dmet_mode", occtype) + addbuffer = self._get_bath_option("addbuffer", occtype) and occtype == "virtual" if addbuffer: - other = 'occ' if (otype == 'vir') else 'vir' - c_buffer = getattr(dmet, 'c_env_%s' % other) + other = "occ" if (otype == "vir") else "vir" + c_buffer = getattr(dmet, "c_env_%s" % other) else: c_buffer = None - return MP2_Bath(self, dmet_bath=dmet, occtype=occtype, c_buffer=c_buffer, - project_dmet_order=project_dmet_order, project_dmet_mode=project_dmet_mode) - if btype == 'rpa': - project_dmet_order = self._get_bath_option('project_dmet_order', occtype) - project_dmet_mode = self._get_bath_option('project_dmet_mode', occtype) - return RPA_Bath(self, dmet_bath=dmet, occtype=occtype, c_buffer=None, - project_dmet_order=project_dmet_order, project_dmet_mode=project_dmet_mode) - raise NotImplementedError('bathtype= %s' % btype) - self._bath_factory_occ = get_bath(occtype='occupied') - self._bath_factory_vir = get_bath(occtype='virtual') + return MP2_Bath( + self, + dmet_bath=dmet, + occtype=occtype, + c_buffer=c_buffer, + project_dmet_order=project_dmet_order, + project_dmet_mode=project_dmet_mode, + ) + if btype == "rpa": + project_dmet_order = self._get_bath_option("project_dmet_order", occtype) + project_dmet_mode = self._get_bath_option("project_dmet_mode", occtype) + return RPA_Bath( + self, + dmet_bath=dmet, + occtype=occtype, + c_buffer=None, + project_dmet_order=project_dmet_order, + project_dmet_mode=project_dmet_mode, + ) + raise NotImplementedError("bathtype= %s" % btype) + + self._bath_factory_occ = get_bath(occtype="occupied") + self._bath_factory_vir = get_bath(occtype="virtual") def make_cluster(self): - def get_orbitals(occtype): - factory = getattr(self, '_bath_factory_%s' % occtype[:3]) - btype = self._get_bath_option('bathtype', occtype) - if btype == 'dmet': + factory = getattr(self, "_bath_factory_%s" % occtype[:3]) + btype = self._get_bath_option("bathtype", occtype) + if btype == "dmet": c_bath = None - c_frozen = getattr(self._dmet_bath, 'c_env_%s' % occtype[:3]) - elif btype == 'full': + c_frozen = getattr(self._dmet_bath, "c_env_%s" % occtype[:3]) + elif btype == "full": c_bath, c_frozen = factory.get_bath() - elif btype == 'r2': - rcut = self._get_bath_option('rcut', occtype) - unit = self._get_bath_option('unit', occtype) + elif btype == "r2": + rcut = self._get_bath_option("rcut", occtype) + unit = self._get_bath_option("unit", occtype) c_bath, c_frozen = factory.get_bath(rcut=rcut) - elif btype == 'ewdmet': - order = self._get_bath_option('order', occtype) + elif btype == "ewdmet": + order = self._get_bath_option("order", occtype) c_bath, c_frozen = factory.get_bath(order) - elif btype in ['mp2', 'rpa', 'rpacorr']: - threshold = self._get_bath_option('threshold', occtype) - truncation = self._get_bath_option('truncation', occtype) + elif btype in ["mp2", "rpa", "rpacorr"]: + threshold = self._get_bath_option("threshold", occtype) + truncation = self._get_bath_option("truncation", occtype) bno_threshold = BNO_Threshold(truncation, threshold) c_bath, c_frozen = factory.get_bath(bno_threshold) else: raise ValueError return c_bath, c_frozen - c_bath_occ, c_frozen_occ = get_orbitals('occupied') - c_bath_vir, c_frozen_vir = get_orbitals('virtual') + c_bath_occ, c_frozen_occ = get_orbitals("occupied") + c_bath_vir, c_frozen_vir = get_orbitals("virtual") c_active_occ = spinalg.hstack_matrices(self._dmet_bath.c_cluster_occ, c_bath_occ) c_active_vir = spinalg.hstack_matrices(self._dmet_bath.c_cluster_vir, c_bath_vir) # Canonicalize orbitals - if self._get_bath_option('canonicalize', 'occupied'): + if self._get_bath_option("canonicalize", "occupied"): c_active_occ = self.canonicalize_mo(c_active_occ)[0] - if self._get_bath_option('canonicalize', 'virtual'): + if self._get_bath_option("canonicalize", "virtual"): c_active_vir = self.canonicalize_mo(c_active_vir)[0] cluster = Cluster.from_coeffs(c_active_occ, c_active_vir, c_frozen_occ, c_frozen_vir) @@ -877,18 +960,19 @@ def get_orbitals(occtype): def check_occup(mo_coeff, expected): occup = self.get_mo_occupation(mo_coeff) # RHF - atol = self.opts.bath_options['occupation_tolerance'] + atol = self.opts.bath_options["occupation_tolerance"] if np.ndim(occup[0]) == 0: - assert np.allclose(occup, 2*expected, rtol=0, atol=2*atol) + assert np.allclose(occup, 2 * expected, rtol=0, atol=2 * atol) else: assert np.allclose(occup[0], expected, rtol=0, atol=atol) assert np.allclose(occup[1], expected, rtol=0, atol=atol) + check_occup(cluster.c_total_occ, 1) check_occup(cluster.c_total_vir, 0) - self.log.info('Orbitals for %s', self) - self.log.info('-------------%s', len(str(self))*'-') - self.log.info(cluster.repr_size().replace('%', '%%')) + self.log.info("Orbitals for %s", self) + self.log.info("-------------%s", len(str(self)) * "-") + self.log.info(cluster.repr_size().replace("%", "%%")) self.cluster = cluster return cluster @@ -896,23 +980,27 @@ def check_occup(mo_coeff, expected): def make_bosonic_bath_target(self): """Get the target space for bosonic bath orbitals. This can either be the DMET cluster or the full space, and can include a projection onto the fragment.""" - if self.opts.bosonic_bath_options['bathtype'] != "rpa": + if self.opts.bosonic_bath_options["bathtype"] != "rpa": return None, 0 - target_space = RPA_Boson_Target_Space(self, target_orbitals=self.opts.bosonic_bath_options['target_orbitals'], - local_projection=self.opts.bosonic_bath_options['local_projection']) + target_space = RPA_Boson_Target_Space( + self, + target_orbitals=self.opts.bosonic_bath_options["target_orbitals"], + local_projection=self.opts.bosonic_bath_options["local_projection"], + ) target_excits = target_space.gen_target_excitation() return target_excits, target_excits.shape[0] def make_bosonic_cluster(self, m0_target): """Set bosonic component of the cluster.""" - if self.opts.bosonic_bath_options['bathtype'] != "rpa": + if self.opts.bosonic_bath_options["bathtype"] != "rpa": return self._boson_bath_factory = RPA_QBA_Bath(self, target_m0=m0_target) - boson_threshold = Boson_Threshold(self.opts.bosonic_bath_options['truncation'], - self.opts.bosonic_bath_options['threshold']) + boson_threshold = Boson_Threshold( + self.opts.bosonic_bath_options["truncation"], self.opts.bosonic_bath_options["threshold"] + ) c_bath, c_frozen = self._boson_bath_factory.get_bath(boson_threshold=boson_threshold) fullorbs = Orbitals(coeff=self.base.mo_coeff, occ=self.base.mo_occ) @@ -929,13 +1017,17 @@ def get_fragment_mo_energy(self, c_active=None, fock=None): c_active: array, optional fock: array, optional """ - if c_active is None: c_active = self.cluster.c_active - if fock is None: fock = self.base.get_fock() - mo_energy = einsum('ai,ab,bi->i', c_active, fock, c_active) + if c_active is None: + c_active = self.cluster.c_active + if fock is None: + fock = self.base.get_fock() + mo_energy = einsum("ai,ab,bi->i", c_active, fock, c_active) return mo_energy @mpi.with_send(source=get_fragment_mpi_rank) - def get_fragment_dmet_energy(self, dm1=None, dm2=None, h1e_eff=None, hamil=None, part_cumulant=True, approx_cumulant=True): + def get_fragment_dmet_energy( + self, dm1=None, dm2=None, h1e_eff=None, hamil=None, part_cumulant=True, approx_cumulant=True + ): """Get fragment contribution to whole system DMET energy from cluster DMs. After fragment summation, the nuclear-nuclear repulsion must be added to get the total energy! @@ -961,9 +1053,11 @@ def get_fragment_dmet_energy(self, dm1=None, dm2=None, h1e_eff=None, hamil=None, e_dmet: float Electronic fragment DMET energy. """ - assert (mpi.rank == self.mpi_rank) - if dm1 is None: dm1 = self.results.dm1 - if dm1 is None: raise RuntimeError("DM1 not found for %s" % self) + assert mpi.rank == self.mpi_rank + if dm1 is None: + dm1 = self.results.dm1 + if dm1 is None: + raise RuntimeError("DM1 not found for %s" % self) c_act = self.cluster.c_active t0 = timer() if hamil is None: @@ -982,31 +1076,31 @@ def get_fragment_dmet_energy(self, dm1=None, dm2=None, h1e_eff=None, hamil=None, h1e_eff = dot(c_act.T, self.base.get_hcore_for_energy(), c_act) else: # Use the original Hcore (without chemical potential modifications), but updated mf-potential! - h1e_eff = self.base.get_hcore_for_energy() + self.base.get_veff_for_energy(with_exxdiv=False)/2 + h1e_eff = self.base.get_hcore_for_energy() + self.base.get_veff_for_energy(with_exxdiv=False) / 2 h1e_eff = dot(c_act.T, h1e_eff, c_act) - occ = np.s_[:self.cluster.nocc_active] - v_act = einsum('iipq->pq', eris[occ,occ,:,:]) - einsum('iqpi->pq', eris[occ,:,:,occ])/2 + occ = np.s_[: self.cluster.nocc_active] + v_act = einsum("iipq->pq", eris[occ, occ, :, :]) - einsum("iqpi->pq", eris[occ, :, :, occ]) / 2 h1e_eff -= v_act p_frag = self.get_fragment_projector(c_act) # Check number of electrons - ne = einsum('ix,ij,jx->', p_frag, dm1, p_frag) + ne = einsum("ix,ij,jx->", p_frag, dm1, p_frag) self.log.debugv("Number of electrons for DMET energy in fragment %12s: %.8f", self, ne) # Evaluate energy - e1b = einsum('xj,xi,ij->', h1e_eff, p_frag, dm1) - e2b = einsum('xjkl,xi,ijkl->', eris, p_frag, dm2)/2 + e1b = einsum("xj,xi,ij->", h1e_eff, p_frag, dm1) + e2b = einsum("xjkl,xi,ijkl->", eris, p_frag, dm2) / 2 self.log.debugv("E(DMET): E(1)= %s E(2)= %s", energy_string(e1b), energy_string(e2b)) - e_dmet = self.opts.sym_factor*(e1b + e2b) + e_dmet = self.opts.sym_factor * (e1b + e2b) self.log.debugv("Fragment E(DMET)= %+16.8f Ha", e_dmet) - self.log.timingv("Time for DMET energy: %s", time_string(timer()-t0)) + self.log.timingv("Time for DMET energy: %s", time_string(timer() - t0)) return e_dmet # --- Counterpoise # ================ - def make_counterpoise_mol(self, rmax, nimages=1, unit='A', **kwargs): + def make_counterpoise_mol(self, rmax, nimages=1, unit="A", **kwargs): """Make molecule object for counterposise calculation. WARNING: This has only been tested for periodic systems so far! @@ -1032,16 +1126,22 @@ def make_counterpoise_mol(self, rmax, nimages=1, unit='A', **kwargs): if len(self.atoms) != 1: raise NotImplementedError import vayesta.misc - return vayesta.misc.counterpoise.make_mol(self.mol, self.atoms[1], rmax=rmax, nimages=nimages, unit=unit, **kwargs) + + return vayesta.misc.counterpoise.make_mol( + self.mol, self.atoms[1], rmax=rmax, nimages=nimages, unit=unit, **kwargs + ) # --- Orbital plotting # -------------------- @mpi.with_send(source=get_fragment_mpi_rank) def pop_analysis(self, cluster=None, dm1=None, **kwargs): - if cluster is None: cluster = self.cluster - if dm1 is None: dm1 = self.results.dm1 - if dm1 is None: raise ValueError("DM1 not found for %s" % self) + if cluster is None: + cluster = self.cluster + if dm1 is None: + dm1 = self.results.dm1 + if dm1 is None: + raise ValueError("DM1 not found for %s" % self) # Add frozen mean-field contribution: dm1 = cluster.add_frozen_rdm1(dm1) return self.base.pop_analysis(dm1, mo_coeff=cluster.coeff, **kwargs) @@ -1088,12 +1188,22 @@ def get_local_rpa_correction(self, hamil=None): def get_frag_hamil(self): if self.opts.screening is not None: if "crpa_full" in self.opts.screening: - self.bos_freqs, self.couplings = get_frag_W(self.mf, self, pcoupling=("pcoupled" in self.opts.screening), - only_ov_screened=("ov" in self.opts.screening), - log=self.log) + self.bos_freqs, self.couplings = get_frag_W( + self.mf, + self, + pcoupling=("pcoupled" in self.opts.screening), + only_ov_screened=("ov" in self.opts.screening), + log=self.log, + ) # This detects based on fragment what kind of Hamiltonian is appropriate (restricted and/or EB). - return ClusterHamiltonian(self, self.mf, self.log, screening=self.opts.screening, - cache_eris=self.opts.store_eris, match_fock=self.opts.match_cluster_fock) + return ClusterHamiltonian( + self, + self.mf, + self.log, + screening=self.opts.screening, + cache_eris=self.opts.store_eris, + match_fock=self.opts.match_cluster_fock, + ) def get_solver_options(self, *args, **kwargs): raise AbstractMethodError diff --git a/vayesta/core/qemb/qemb.py b/vayesta/core/qemb/qemb.py index 38659681e..1b99529f2 100644 --- a/vayesta/core/qemb/qemb.py +++ b/vayesta/core/qemb/qemb.py @@ -19,8 +19,19 @@ from pyscf.mp.mp2 import _mo_without_core from vayesta.core.foldscf import FoldedSCF, fold_scf -from vayesta.core.util import (OptionsBase, OrthonormalityError, SymmetryError, dot, einsum, energy_string, - getattr_recursive, hstack, log_method, log_time, with_doc) +from vayesta.core.util import ( + OptionsBase, + OrthonormalityError, + SymmetryError, + dot, + einsum, + energy_string, + getattr_recursive, + hstack, + log_method, + log_time, + with_doc, +) from vayesta.core import spinalg, eris from vayesta.core.scmf import PDMET, Brueckner from vayesta.core.screening.screening_moment import build_screened_eris @@ -52,78 +63,111 @@ # --- This Package from vayesta.core.qemb.fragment import Fragment -#from . import helper + +# from . import helper from vayesta.core.qemb.rdm import make_rdm1_demo_rhf from vayesta.core.qemb.rdm import make_rdm2_demo_rhf @dataclasses.dataclass class Options(OptionsBase): - store_eris: bool = True # If True, ERIs will be stored in Fragment.hamil; otherwise they will be recalculated whenever needed. - global_frag_chempot: float = None # Global fragment chemical potential (e.g. for democratically partitioned DMs) - dm_with_frozen: bool = False # Add frozen parts to cluster DMs + store_eris: bool = ( + True # If True, ERIs will be stored in Fragment.hamil; otherwise they will be recalculated whenever needed. + ) + global_frag_chempot: float = None # Global fragment chemical potential (e.g. for democratically partitioned DMs) + dm_with_frozen: bool = False # Add frozen parts to cluster DMs # --- Bath options bath_options: dict = OptionsBase.dict_with_defaults( # General - bathtype='dmet', canonicalize=True, occupation_tolerance=1e-8, + bathtype="dmet", + canonicalize=True, + occupation_tolerance=1e-8, # DMET bath dmet_threshold=1e-8, # R2 bath - rcut=None, unit='Ang', + rcut=None, + unit="Ang", # EwDMET bath - order=None, max_order=20, # +threshold (same as MP2 bath) + order=None, + max_order=20, # +threshold (same as MP2 bath) # MP2 bath - threshold=None, truncation='occupation', project_dmet_order=2, - project_dmet_mode='squared-entropy', addbuffer=False, + threshold=None, + truncation="occupation", + project_dmet_order=2, + project_dmet_mode="squared-entropy", + addbuffer=False, # The following options can be set occupied/virtual-specific: - bathtype_occ=None, bathtype_vir=None, - rcut_occ=None, rcut_vir=None, - unit_occ=None, unit_vir=None, - order_occ=None, order_vir=None, - max_order_occ=None, max_order_vir=None, - threshold_occ=None, threshold_vir=None, - truncation_occ=None, truncation_vir=None, - project_dmet_order_occ=None, project_dmet_order_vir=None, - project_dmet_mode_occ=None, project_dmet_mode_vir=None, - addbuffer_occ=None, addbuffer_dmet_vir=None, - canonicalize_occ=None, canonicalize_vir=None, - ) + bathtype_occ=None, + bathtype_vir=None, + rcut_occ=None, + rcut_vir=None, + unit_occ=None, + unit_vir=None, + order_occ=None, + order_vir=None, + max_order_occ=None, + max_order_vir=None, + threshold_occ=None, + threshold_vir=None, + truncation_occ=None, + truncation_vir=None, + project_dmet_order_occ=None, + project_dmet_order_vir=None, + project_dmet_mode_occ=None, + project_dmet_mode_vir=None, + addbuffer_occ=None, + addbuffer_dmet_vir=None, + canonicalize_occ=None, + canonicalize_vir=None, + ) # --- Bosonic bath options bosonic_bath_options: dict = OptionsBase.dict_with_defaults( # General bathtype=None, # construction options. - target_orbitals="full", local_projection='fragment', + target_orbitals="full", + local_projection="fragment", # bath truncation options. - threshold=1e-6, truncation='occupation' - ) + threshold=1e-6, + truncation="occupation", + ) # --- Solver options solver_options: dict = OptionsBase.dict_with_defaults( - # General - conv_tol=None, - n_moments=None, - # CCSD - solve_lambda=True, conv_tol_normt=None, - level_shift=None, diis_space=None, diis_start_cycle=None, - iterative_damping=None, - # FCI - threads=1, max_cycle=300, fix_spin=None, lindep=None, - davidson_only=True, init_guess='default', - # EBFCI/EBCCSD - max_boson_occ=2, - # EBCC - ansatz=None, store_as_ccsd=None, - # Dump - dumpfile='clusters.h5', - # MP2 - compress_cderi=False) + # General + conv_tol=None, + n_moments=None, + # CCSD + solve_lambda=True, + conv_tol_normt=None, + level_shift=None, + diis_space=None, + diis_start_cycle=None, + iterative_damping=None, + # FCI + threads=1, + max_cycle=300, + fix_spin=None, + lindep=None, + davidson_only=True, + init_guess="default", + # EBFCI/EBCCSD + max_boson_occ=2, + # EBCC + ansatz=None, + store_as_ccsd=None, + # Dump + dumpfile="clusters.h5", + # MP2 + compress_cderi=False, + ) # --- Other - symmetry_tol: float = 1e-6 # Tolerance (in Bohr) for atomic positions - symmetry_mf_tol: float = 1e-5 # Tolerance for mean-field solution - screening: Optional[str] = None # What form of screening to use in clusters. + symmetry_tol: float = 1e-6 # Tolerance (in Bohr) for atomic positions + symmetry_mf_tol: float = 1e-5 # Tolerance for mean-field solution + screening: Optional[str] = None # What form of screening to use in clusters. ext_rpa_correction: Optional[str] = None match_cluster_fock: bool = False + class Embedding: """Base class for quantum embedding methods. @@ -214,17 +258,16 @@ class Embedding: is_rhf = True is_uhf = False # Use instead: - spinsym = 'restricted' + spinsym = "restricted" - def __init__(self, mf, solver='CCSD', log=None, overwrite=None, **kwargs): + def __init__(self, mf, solver="CCSD", log=None, overwrite=None, **kwargs): # 1) Logging # ---------- self.log = log or logging.getLogger(__name__) self.log.info("") self.log.info("INITIALIZING %s" % self.__class__.__name__.upper()) - self.log.info("=============%s" % (len(str(self.__class__.__name__))*"=")) + self.log.info("=============%s" % (len(str(self.__class__.__name__)) * "=")) with self.log.indent(): - # 2) Options # ---------- self.opts = self.Options().replace(**kwargs) @@ -255,28 +298,27 @@ def __init__(self, mf, solver='CCSD', log=None, overwrite=None, **kwargs): self.check_solver(solver) self.solver = solver self.symmetry = SymmetryGroup(self.mol, xtol=self.opts.symmetry_tol) - nimages = getattr(self.mf, 'subcellmesh', None) + nimages = getattr(self.mf, "subcellmesh", None) if nimages: self.symmetry.set_translations(nimages) # Rotations need to be added manually! self.register = FragmentRegister() self.fragments = [] - self.with_scmf = None # Self-consistent mean-field + self.with_scmf = None # Self-consistent mean-field # Initialize results self._reset() - def _mpi_bcast_mf(self, mf): """Use mo_energy and mo_coeff from master MPI rank only.""" # If vayesta.misc.scf_with_mpi was used, we do not need broadcast # as the MO coefficients will already be the same - if getattr(mf, 'with_mpi', False): + if getattr(mf, "with_mpi", False): return with log_time(self.log.timing, "Time to broadcast mean-field to all MPI ranks: %s"): # Check if all MPI ranks have the same mean-field MOs - #mo_energy = mpi.world.gather(mf.mo_energy) - #if mpi.is_master: + # mo_energy = mpi.world.gather(mf.mo_energy) + # if mpi.is_master: # moerr = np.max([abs(mo_energy[i] - mo_energy[0]).max() for i in range(len(mpi))]) # if moerr > 1e-6: # self.log.warning("Large difference of MO energies between MPI ranks= %.2e !", moerr) @@ -287,7 +329,9 @@ def _mpi_bcast_mf(self, mf): mf.mo_coeff = mpi.world.bcast(mf.mo_coeff, root=0) def init_mf(self, mf): - self._mf_orig = mf # Keep track of original mean-field object - be careful not to modify in any way, to avoid side effects! + self._mf_orig = ( + mf # Keep track of original mean-field object - be careful not to modify in any way, to avoid side effects! + ) # Create shallow copy of mean-field object; this way it can be updated without side effects outside the quantum # embedding method if attributes are replaced in their entirety @@ -295,7 +339,7 @@ def init_mf(self, mf): mf = copy.copy(mf) self.log.debugv("type(mf)= %r", type(mf)) # If the mean-field has k-points, automatically fold to the supercell: - if getattr(mf, 'kpts', None) is not None: + if getattr(mf, "kpts", None) is not None: with log_time(self.log.timing, "Time for k->G folding of MOs: %s"): mf = fold_scf(mf) if isinstance(mf, FoldedSCF): @@ -322,10 +366,10 @@ def init_mf(self, mf): # Hartree-Fock energy - this can be different from mf.e_tot, when the mean-field # is not a (converged) HF calculations - e_mf = (mf.e_tot / self.ncells) + e_mf = mf.e_tot / self.ncells e_hf = self.e_mf - de = (e_mf - e_hf) - rde = (de / e_mf) + de = e_mf - e_hf + rde = de / e_mf if not self.mf.converged: self.log.warning("Mean-field not converged!") self.log.info("Initial E(mean-field)= %s", energy_string(e_mf)) @@ -334,14 +378,19 @@ def init_mf(self, mf): if (abs(de) > 1e-3) or (abs(rde) > 1e-6): self.log.warning("Large difference between initial E(mean-field) and calculated E(HF)!") - #FIXME (no RHF/UHF dependent code here) + # FIXME (no RHF/UHF dependent code here) if self.is_rhf: - self.log.info("n(AO)= %4d n(MO)= %4d n(linear dep.)= %4d", self.nao, self.nmo, self.nao-self.nmo) + self.log.info("n(AO)= %4d n(MO)= %4d n(linear dep.)= %4d", self.nao, self.nmo, self.nao - self.nmo) else: - self.log.info("n(AO)= %4d n(alpha/beta-MO)= (%4d, %4d) n(linear dep.)= (%4d, %4d)", - self.nao, *self.nmo, self.nao-self.nmo[0], self.nao-self.nmo[1]) + self.log.info( + "n(AO)= %4d n(alpha/beta-MO)= (%4d, %4d) n(linear dep.)= (%4d, %4d)", + self.nao, + *self.nmo, + self.nao - self.nmo[0], + self.nao - self.nmo[1], + ) - self._check_orthonormal(self.mo_coeff, mo_name='MO') + self._check_orthonormal(self.mo_coeff, mo_name="MO") if self.mo_energy is not None: if self.is_rhf: @@ -356,15 +405,15 @@ def init_mf(self, mf): def change_options(self, **kwargs): self.opts.replace(**kwargs) for fx in self.fragments: - fkwds = {key : kwargs[key] for key in [key for key in kwargs if hasattr(fx.opts, key)]} + fkwds = {key: kwargs[key] for key in [key for key in kwargs if hasattr(fx.opts, key)]} fx.change_options(**fkwds) # --- Basic properties and methods # ================================ def __repr__(self): - keys = ['mf'] - fmt = ('%s(' + len(keys)*'%s: %r, ')[:-2] + ')' + keys = ["mf"] + fmt = ("%s(" + len(keys) * "%s: %r, ")[:-2] + ")" values = [self.__dict__[k] for k in keys] return fmt % (self.__class__.__name__, *[x for y in zip(keys, values) for x in y]) @@ -378,7 +427,7 @@ def mol(self): @property def has_exxdiv(self): """Correction for divergent exact-exchange potential.""" - return (hasattr(self.mf, 'exxdiv') and self.mf.exxdiv is not None) + return hasattr(self.mf, "exxdiv") and self.mf.exxdiv is not None def get_exxdiv(self): """Get divergent exact-exchange (exxdiv) energy correction and potential. @@ -390,16 +439,17 @@ def get_exxdiv(self): v_exxdiv: array Divergent exact-exchange potential correction in AO basis. """ - if not self.has_exxdiv: return 0, None - sc = np.dot(self.get_ovlp(), self.mo_coeff[:,:self.nocc]) + if not self.has_exxdiv: + return 0, None + sc = np.dot(self.get_ovlp(), self.mo_coeff[:, : self.nocc]) e_exxdiv = -self.madelung * self.nocc v_exxdiv = -self.madelung * np.dot(sc, sc.T) self.log.debugv("Divergent exact-exchange (exxdiv) correction= %+16.8f Ha", e_exxdiv) - return e_exxdiv/self.ncells, v_exxdiv + return e_exxdiv / self.ncells, v_exxdiv @property def pbc_dimension(self): - return getattr(self.mol, 'dimension', 0) + return getattr(self.mol, "dimension", 0) @property def nao(self): @@ -409,7 +459,8 @@ def nao(self): @property def ncells(self): """Number of primitive cells within supercell.""" - if self.kpts is None: return 1 + if self.kpts is None: + return 1 return len(self.kpts) @property @@ -418,13 +469,13 @@ def has_df(self): @property def df(self): - if hasattr(self.mf, 'with_df') and self.mf.with_df is not None: + if hasattr(self.mf, "with_df") and self.mf.with_df is not None: return self.mf.with_df return None # Mean-field properties - #def init_vhf_ehf(self): + # def init_vhf_ehf(self): # """Get Hartree-Fock potential and energy.""" # if self.opts.recalc_vhf: # self.log.debug("Calculating HF potential from mean-field object.") @@ -455,22 +506,22 @@ def mo_occ(self): # MOs setters: - #@mo_energy.setter - #def mo_energy(self, mo_energy): + # @mo_energy.setter + # def mo_energy(self, mo_energy): # """Updating the MOs resets the effective potential cache `_veff`.""" # self.log.debugv("MF attribute 'mo_energy' is updated; deleting cached _veff.") # #self._veff = None # self.mf.mo_energy = mo_energy - #@mo_coeff.setter - #def mo_coeff(self, mo_coeff): + # @mo_coeff.setter + # def mo_coeff(self, mo_coeff): # """Updating the MOs resets the effective potential cache `_veff`.""" # self.log.debugv("MF attribute 'mo_coeff' is updated; deleting chached _veff.") # #self._veff = None # self.mf.mo_coeff = mo_coeff - #@mo_occ.setter - #def mo_occ(self, mo_occ): + # @mo_occ.setter + # def mo_occ(self, mo_occ): # """Updating the MOs resets the effective potential cache `_veff`.""" # self.log.debugv("MF attribute 'mo_occ' is updated; deleting chached _veff.") # #self._veff = None @@ -494,22 +545,22 @@ def nvir(self): @property def mo_energy_occ(self): """Occupied MO energies.""" - return self.mo_energy[:self.nocc] + return self.mo_energy[: self.nocc] @property def mo_energy_vir(self): """Virtual MO coefficients.""" - return self.mo_energy[self.nocc:] + return self.mo_energy[self.nocc :] @property def mo_coeff_occ(self): """Occupied MO coefficients.""" - return self.mo_coeff[:,:self.nocc] + return self.mo_coeff[:, : self.nocc] @property def mo_coeff_vir(self): """Virtual MO coefficients.""" - return self.mo_coeff[:,self.nocc:] + return self.mo_coeff[:, self.nocc :] @property def e_mf(self): @@ -520,12 +571,12 @@ def e_mf(self): h1e = self.get_hcore_for_energy() vhf = self.get_veff_for_energy() e_mf = self.mf.energy_tot(h1e=h1e, vhf=vhf) - return e_mf/self.ncells + return e_mf / self.ncells @property def e_nuc(self): """Nuclear-repulsion energy per unit cell (not folded supercell).""" - return self.mol.energy_nuc()/self.ncells + return self.mol.energy_nuc() / self.ncells @property def e_nonlocal(self): @@ -564,7 +615,7 @@ def _get_veff_orig(self, with_exxdiv=True): return self._veff_orig def _get_fock_orig(self, with_exxdiv=True): - return (self._get_hcore_orig() + self._get_veff_orig(with_exxdiv=with_exxdiv)) + return self._get_hcore_orig() + self._get_veff_orig(with_exxdiv=with_exxdiv) # Integrals which change with mean-field updates or chemical potential shifts: @@ -614,7 +665,7 @@ def get_veff_for_energy(self, dm1=None, with_exxdiv=True): def get_fock_for_energy(self, dm1=None, with_exxdiv=True): """Fock matrix used for energy evaluation.""" - return (self.get_hcore_for_energy() + self.get_veff_for_energy(dm1=dm1, with_exxdiv=with_exxdiv)) + return self.get_hcore_for_energy() + self.get_veff_for_energy(dm1=dm1, with_exxdiv=with_exxdiv) def get_fock_for_bath(self, dm1=None, with_exxdiv=True): """Fock matrix used for bath orbitals.""" @@ -637,13 +688,14 @@ def get_ovlp_power(self, power): spow : (n(AO), n(AO)) array Matrix power of AO overlap matrix """ - if power == 1: return self.get_ovlp() + if power == 1: + return self.get_ovlp() if self.kcell is None: e, v = np.linalg.eigh(self.get_ovlp()) - return np.dot(v*(e**power), v.T.conj()) - sk = self.kcell.pbc_intor('int1e_ovlp', hermi=1, kpts=self.kpts, pbcopt=pyscf.lib.c_null_ptr()) + return np.dot(v * (e**power), v.T.conj()) + sk = self.kcell.pbc_intor("int1e_ovlp", hermi=1, kpts=self.kpts, pbcopt=pyscf.lib.c_null_ptr()) ek, vk = np.linalg.eigh(sk) - spowk = einsum('kai,ki,kbi->kab', vk, ek**power, vk.conj()) + spowk = einsum("kai,ki,kbi->kab", vk, ek**power, vk.conj()) spow = pyscf.pbc.tools.k2gamma.to_supercell_ao_integrals(self.kcell, self.kpts, spowk) return spow @@ -658,10 +710,12 @@ def get_ovlp_power(self, power): def build_screened_interactions(self, *args, **kwargs): """Build screened interactions, be they dynamic or static.""" have_static_screening = any([x.opts.screening is not None for x in self.fragments]) - have_dynamic_screening = any([x.opts.bosonic_bath_options['bathtype'] is not None for x in self.fragments]) + have_dynamic_screening = any([x.opts.bosonic_bath_options["bathtype"] is not None for x in self.fragments]) if have_static_screening and have_dynamic_screening: - raise ValueError("Cannot currently use both static screened coulomb interaction and bosonic baths at the same time.") + raise ValueError( + "Cannot currently use both static screened coulomb interaction and bosonic baths at the same time." + ) if have_static_screening: self.build_screened_eris(*args, **kwargs) @@ -678,10 +732,9 @@ def build_bosonic_bath(self): fragments = self.get_fragments(active=True, sym_parent=None, mpi_rank=mpi.rank) with log_time(self.log.timing, "Total time for bath and clusters: %s"): - msg = "Generating required information for bosonic bath generation" self.log.info(msg) - self.log.info(len(msg)*"-") + self.log.info(len(msg) * "-") # Generate list of all required target information. targets, ntarget = zip(*[x.make_bosonic_bath_target() for x in fragments]) target_rot = np.vstack([x for x in targets if x is not None]) @@ -692,11 +745,10 @@ def build_bosonic_bath(self): for x, moment in zip(fragments, moments): msg = "Making bosonic bath for %s%s" % (x, (" on MPI process %d" % mpi.rank) if mpi else "") self.log.info(msg) - self.log.info(len(msg)*"-") + self.log.info(len(msg) * "-") with self.log.indent(): x.make_bosonic_cluster(moment) - @log_method() @with_doc(build_screened_eris) def build_screened_eris(self, *args, **kwargs): @@ -708,7 +760,9 @@ def build_screened_eris(self, *args, **kwargs): if self.opts.ext_rpa_correction: cumulant = self.opts.ext_rpa_correction == "cumulant" if nmomscr < self.nfrag: - raise NotImplementedError("External dRPA correction currently requires all fragments use mrpa screening.") + raise NotImplementedError( + "External dRPA correction currently requires all fragments use mrpa screening." + ) if self.opts.ext_rpa_correction not in ["erpa", "cumulant"]: raise ValueError("Unknown external rpa correction %s specified.") @@ -732,7 +786,7 @@ def build_screened_eris(self, *args, **kwargs): self.e_rpa = 0.5 * einsum("pq,pq->", m0 - l_, l_2) else: # Calculate total dRPA energy in N^4 time; this is cheaper than screening calculations. - self.e_rpa, energy_error = rpa.kernel_energy(correction='linear') + self.e_rpa, energy_error = rpa.kernel_energy(correction="linear") self.e_rpa = self.e_rpa / self.ncells self.log.info("Set total RPA correlation energy contribution as %s", energy_string(self.e_rpa)) if nmomscr > 0: @@ -760,24 +814,24 @@ def create_symmetric_fragments(self, symmetry, fragments=None, symbol=None, mf_t fragments: list List of T-symmetry related fragments. These will have the attributes `sym_parent` and `sym_op` set. """ - default_axes = {'x': (1,0,0), 'y': (0,1,0), 'z': (0,0,1)} + default_axes = {"x": (1, 0, 0), "y": (0, 1, 0), "z": (0, 0, 1)} def catch_default_axes(axis): if isinstance(axis, str): return default_axes[axis.lower()] return axis - symtype = symmetry['type'] + symtype = symmetry["type"] def to_bohr(point, unit): unit = unit.lower() point = np.asarray(point, dtype=float) - if unit.startswith('ang'): + if unit.startswith("ang"): return point / 0.529177210903 - if unit == 'latvec': - #kcell = self.kcell if self.kcell is not None else self.mol + if unit == "latvec": + # kcell = self.kcell if self.kcell is not None else self.mol return np.dot(point, (self.kcell or self.mol).lattice_vectors()) - if unit.startswith('bohr'): + if unit.startswith("bohr"): return point raise ValueError("unit= %s" % unit) @@ -786,40 +840,40 @@ def shift_point_to_supercell(point): if self.kcell is None: # No PBC or no supercell return point - ak = self.kcell.lattice_vectors() # primtive cell lattice vectors + ak = self.kcell.lattice_vectors() # primtive cell lattice vectors bk = np.linalg.inv(ak) - a = self.mol.lattice_vectors() # supercell lattice vectors - shift = (np.diag(a)/np.diag(ak) - 1)/2 + a = self.mol.lattice_vectors() # supercell lattice vectors + shift = (np.diag(a) / np.diag(ak) - 1) / 2 # Shift in internal coordinates, then transform back point = np.dot(np.dot(point, bk) + shift, ak) return point - if symtype == 'inversion': - center = to_bohr(symmetry['center'], symmetry['unit']) + if symtype == "inversion": + center = to_bohr(symmetry["center"], symmetry["unit"]) center = shift_point_to_supercell(center) - symbol = symbol or 'I' + symbol = symbol or "I" symlist = [1] - elif symtype == 'reflection': - center = to_bohr(symmetry['center'], symmetry['unit']) + elif symtype == "reflection": + center = to_bohr(symmetry["center"], symmetry["unit"]) center = shift_point_to_supercell(center) - axis = symmetry['axis'] + axis = symmetry["axis"] axis = np.asarray(catch_default_axes(axis), dtype=float) - axis = to_bohr(axis, symmetry['unit']) - symbol = symbol or 'M' + axis = to_bohr(axis, symmetry["unit"]) + symbol = symbol or "M" symlist = [1] - elif symtype == 'rotation': - order = symmetry['order'] - axis = symmetry['axis'] + elif symtype == "rotation": + order = symmetry["order"] + axis = symmetry["axis"] axis = np.asarray(catch_default_axes(axis), dtype=float) - axis = to_bohr(axis, symmetry['unit']) - center = to_bohr(symmetry['center'], symmetry['unit']) + axis = to_bohr(axis, symmetry["unit"]) + center = to_bohr(symmetry["center"], symmetry["unit"]) center = shift_point_to_supercell(center) symlist = range(1, order) - symbol = symbol or 'R' - elif symtype == 'translation': - translation = np.asarray(symmetry['translation']) + symbol = symbol or "R" + elif symtype == "translation": + translation = np.asarray(symmetry["translation"]) symlist = list(itertools.product(range(translation[0]), range(translation[1]), range(translation[2])))[1:] - symbol = symbol or 'T' + symbol = symbol or "T" else: raise ValueError("Symmetry type= %s" % symtype) @@ -831,69 +885,92 @@ def shift_point_to_supercell(point): fragments = self.get_fragments() ftree = [[fx] for fx in fragments] for i, sym in enumerate(symlist): - - if symtype == 'inversion': + if symtype == "inversion": sym_op = SymmetryInversion(self.symmetry, center=center) - elif symtype == 'inversion': + elif symtype == "inversion": sym_op = SymmetryReflection(self.symmetry, axis=axis, center=center) - elif symtype == 'reflection': + elif symtype == "reflection": sym_op = SymmetryReflection(self.symmetry, axis=axis, center=center) - elif symtype == 'rotation': - rotvec = 2*np.pi * (sym/order) * axis/np.linalg.norm(axis) + elif symtype == "rotation": + rotvec = 2 * np.pi * (sym / order) * axis / np.linalg.norm(axis) sym_op = SymmetryRotation(self.symmetry, rotvec, center=center) - elif symtype == 'translation': - transvec = np.asarray(sym)/translation + elif symtype == "translation": + transvec = np.asarray(sym) / translation sym_op = SymmetryTranslation(self.symmetry, transvec) for flist in ftree: parent = flist[0] # Name for symmetry related fragment - if symtype == 'inversion': - name = '%s_%s' % (parent.name, symbol) - elif symtype == 'reflection': - name = '%s_%s' % (parent.name, symbol) - elif symtype == 'rotation': - name = '%s_%s(%d)' % (parent.name, symbol, sym) - elif symtype == 'translation': - name = '%s_%s(%d,%d,%d)' % (parent.name, symbol, *sym) + if symtype == "inversion": + name = "%s_%s" % (parent.name, symbol) + elif symtype == "reflection": + name = "%s_%s" % (parent.name, symbol) + elif symtype == "rotation": + name = "%s_%s(%d)" % (parent.name, symbol, sym) + elif symtype == "translation": + name = "%s_%s(%d,%d,%d)" % (parent.name, symbol, *sym) # Translated coefficients c_frag_t = sym_op(parent.c_frag) c_env_t = None # Avoid expensive symmetry operation on environment orbitals # Check that translated fragment does not overlap with current fragment: fragovlp = parent._csc_dot(parent.c_frag, c_frag_t, ovlp=ovlp) - if self.spinsym == 'restricted': + if self.spinsym == "restricted": fragovlp = abs(fragovlp).max() - elif self.spinsym == 'unrestricted': + elif self.spinsym == "unrestricted": fragovlp = max(abs(fragovlp[0]).max(), abs(fragovlp[1]).max()) - if (fragovlp > 1e-6): - self.log.critical("%s of fragment %s not orthogonal to original fragment (overlap= %.1e)!", - sym_op, parent.name, fragovlp) + if fragovlp > 1e-6: + self.log.critical( + "%s of fragment %s not orthogonal to original fragment (overlap= %.1e)!", + sym_op, + parent.name, + fragovlp, + ) raise RuntimeError("Overlapping fragment spaces (overlap= %.1e)" % fragovlp) # Add fragment frag_id = self.register.get_next_id() - frag = self.Fragment(self, frag_id, name, c_frag_t, c_env_t, solver=parent.solver, sym_parent=parent, - sym_op=sym_op, mpi_rank=parent.mpi_rank, flags=dataclasses.asdict(parent.flags), - **parent.opts.asdict()) + frag = self.Fragment( + self, + frag_id, + name, + c_frag_t, + c_env_t, + solver=parent.solver, + sym_parent=parent, + sym_op=sym_op, + mpi_rank=parent.mpi_rank, + flags=dataclasses.asdict(parent.flags), + **parent.opts.asdict(), + ) # Check symmetry # (only for the first rotation or primitive translations (1,0,0), (0,1,0), and (0,0,1) # to reduce number of sym_op(c_env) calls) if check_mf and (abs(np.asarray(sym)).sum() == 1): charge_err, spin_err = parent.get_symmetry_error(frag, dm1=dm1) if max(charge_err, spin_err) > (mf_tol or self.opts.symmetry_mf_tol): - self.log.critical("Mean-field DM1 not symmetric for %s of %s (errors: charge= %.1e, spin= %.1e)!", - sym_op, parent.name, charge_err, spin_err) + self.log.critical( + "Mean-field DM1 not symmetric for %s of %s (errors: charge= %.1e, spin= %.1e)!", + sym_op, + parent.name, + charge_err, + spin_err, + ) raise RuntimeError("MF not symmetric under %s" % sym_op) else: - self.log.debugv("Mean-field DM symmetry error for %s of %s: charge= %.1e, spin= %.1e", - sym_op, parent.name, charge_err, spin_err) + self.log.debugv( + "Mean-field DM symmetry error for %s of %s: charge= %.1e, spin= %.1e", + sym_op, + parent.name, + charge_err, + spin_err, + ) # Insert after parent fragment flist.append(frag) fragments_sym = [fx for flist in ftree for fx in flist[1:]] return fragments_sym - def create_invsym_fragments(self, center, fragments=None, unit='Ang', **kwargs): + def create_invsym_fragments(self, center, fragments=None, unit="Ang", **kwargs): """Create inversion symmetric fragments. Parameters @@ -908,10 +985,10 @@ def create_invsym_fragments(self, center, fragments=None, unit='Ang', **kwargs): fragments: list List of inversion-symmetry related fragments. These will have have the attributes `sym_parent` and `sym_op` set. """ - symmetry = dict(type='inversion', center=center, unit=unit) + symmetry = dict(type="inversion", center=center, unit=unit) return self.create_symmetric_fragments(symmetry, fragments=fragments, **kwargs) - def create_mirrorsym_fragments(self, axis, center, fragments=None, unit='Ang', **kwargs): + def create_mirrorsym_fragments(self, axis, center, fragments=None, unit="Ang", **kwargs): """Create mirror symmetric fragments. Parameters @@ -926,10 +1003,10 @@ def create_mirrorsym_fragments(self, axis, center, fragments=None, unit='Ang', * fragments: list List of mirror-symmetry related fragments. These will have have the attributes `sym_parent` and `sym_op` set. """ - symmetry = dict(type='reflection', axis=axis, center=center, unit=unit) + symmetry = dict(type="reflection", axis=axis, center=center, unit=unit) return self.create_symmetric_fragments(symmetry, fragments=fragments, **kwargs) - def create_rotsym_fragments(self, order, axis, center, fragments=None, unit='Ang', **kwargs): + def create_rotsym_fragments(self, order, axis, center, fragments=None, unit="Ang", **kwargs): """Create rotationally symmetric fragments. Parameters @@ -944,7 +1021,7 @@ def create_rotsym_fragments(self, order, axis, center, fragments=None, unit='Ang fragments: list List of rotationally-symmetry related fragments. These will have have the attributes `sym_parent` and `sym_op` set. """ - symmetry = dict(type='rotation', order=order, axis=axis, center=center, unit=unit) + symmetry = dict(type="rotation", order=order, axis=axis, center=center, unit=unit) return self.create_symmetric_fragments(symmetry, fragments=fragments, **kwargs) def create_transsym_fragments(self, translation, fragments=None, **kwargs): @@ -964,7 +1041,7 @@ def create_transsym_fragments(self, translation, fragments=None, **kwargs): fragments: list List of T-symmetry related fragments. These will have the attributes `sym_parent` and `sym_op` set. """ - symmetry = dict(type='translation', translation=translation) + symmetry = dict(type="translation", translation=translation) return self.create_symmetric_fragments(symmetry, fragments=fragments, **kwargs) def get_symmetry_parent_fragments(self): @@ -1003,9 +1080,10 @@ def get_symmetry_child_fragments(self, include_parents=False): children = [[] for p in parents] parent_ids = [p.id for p in parents] for f in self.fragments: - if f.sym_parent is None: continue + if f.sym_parent is None: + continue pid = f.sym_parent.id - assert (pid in parent_ids) + assert pid in parent_ids idx = parent_ids.index(pid) children[idx].append(f) return children @@ -1044,6 +1122,7 @@ def get_fragments(self, fragments=None, options=None, flags=None, **filters): def _values_atleast_1d(d): return {k: (v if callable(v) else np.atleast_1d(v)) for k, v in d.items()} + filters = _values_atleast_1d(filters) options = _values_atleast_1d(options) flags = _values_atleast_1d(flags) @@ -1060,19 +1139,22 @@ def _skip(attr, filt): for key, filt in filters.items(): attr = getattr(frag, key) skip = _skip(attr, filt) - if skip: break + if skip: + break if skip: continue # Check options: for key, filt in options.items(): attr = getattr_recursive(frag.opts, key) skip = _skip(attr, filt) - if skip: break + if skip: + break # Check flags: for key, filt in flags.items(): attr = getattr_recursive(frag.flags, key) skip = _skip(attr, filt) - if skip: break + if skip: + break if skip: continue filtered_fragments.append(frag) @@ -1083,7 +1165,7 @@ def get_fragment_overlap_norm(self, fragments=None, occupied=True, virtual=True, if fragments is None: fragments = self.get_fragments() if isinstance(fragments[0], self.Fragment): - fragments = 2*[fragments] + fragments = 2 * [fragments] if not (occupied or virtual): raise ValueError @@ -1102,14 +1184,14 @@ def get_fragment_overlap_norm(self, fragments=None, occupied=True, virtual=True, if virtual: rxy_vir = spinalg.dot(cxs_vir, fy.cluster.c_vir) nxy_vir = np.amax(spinalg.norm(rxy_vir, ord=norm)) - overlap[i,j] = np.amin((nxy_occ, nxy_vir)) + overlap[i, j] = np.amin((nxy_occ, nxy_vir)) return overlap def _absorb_fragments(self, tol=1e-10): """TODO""" for fx in self.get_fragments(active=True): for fy in self.get_fragments(active=True): - if (fx.id == fy.id): + if fx.id == fy.id: continue if not (fx.active and fy.active): continue @@ -1117,21 +1199,21 @@ def _absorb_fragments(self, tol=1e-10): def svd(cx, cy): rxy = np.dot(cx.T, cy) u, s, v = np.linalg.svd(rxy, full_matrices=False) - if s.min() >= (1-tol): + if s.min() >= (1 - tol): nx = cx.shape[-1] ny = cy.shape[-1] swap = False if (nx >= ny) else True return swap return None - cx_occ = fx.get_overlap('mo[occ]|cluster[occ]') - cy_occ = fy.get_overlap('mo[occ]|cluster[occ]') + cx_occ = fx.get_overlap("mo[occ]|cluster[occ]") + cy_occ = fy.get_overlap("mo[occ]|cluster[occ]") swocc = svd(cx_occ, cy_occ) if swocc is None: continue - cx_vir = fx.get_overlap('mo[vir]|cluster[vir]') - cy_vir = fy.get_overlap('mo[vir]|cluster[vir]') + cx_vir = fx.get_overlap("mo[vir]|cluster[vir]") + cy_vir = fy.get_overlap("mo[vir]|cluster[vir]") swvir = svd(cx_vir, cy_vir) if swocc != swvir: continue @@ -1141,7 +1223,7 @@ def svd(cx, cy): fx, fy = fy, fx c_frag = hstack(fx.c_frag, fy.c_frag) fx.c_frag = c_frag - name = '/'.join((fx.name, fy.name)) + name = "/".join((fx.name, fy.name)) fy.active = False self.log.info("Subspace found: adding %s to %s (new name= %s)!", fy, fx, name) # Update fx @@ -1161,10 +1243,10 @@ def communicate_clusters(self): with log_time(self.log.timing, "Time to communicate clusters: %s"): for x in self.get_fragments(sym_parent=None): source = x.mpi_rank - if (mpi.rank == source): + if mpi.rank == source: x.cluster.orig_mf = None cluster = mpi.world.bcast(x.cluster, root=source) - if (mpi.rank != source): + if mpi.rank != source: x.cluster = cluster x.cluster.orig_mf = self.mf @@ -1199,19 +1281,19 @@ def get_dmet_elec_energy(self, part_cumulant=True, approx_cumulant=True): e_dmet = 0.0 for x in self.get_fragments(active=True, mpi_rank=mpi.rank, sym_parent=None): wx = x.symmetry_factor - e_dmet += wx*x.get_fragment_dmet_energy(part_cumulant=part_cumulant, approx_cumulant=approx_cumulant) + e_dmet += wx * x.get_fragment_dmet_energy(part_cumulant=part_cumulant, approx_cumulant=approx_cumulant) if mpi: mpi.world.allreduce(e_dmet) if part_cumulant: dm1 = self.make_rdm1_demo(ao_basis=True) if not approx_cumulant: vhf = self.get_veff_for_energy(dm1=dm1, with_exxdiv=False) - elif (int(approx_cumulant) == 1): - dm1 = 2*np.asarray(dm1) - self.mf.make_rdm1() + elif int(approx_cumulant) == 1: + dm1 = 2 * np.asarray(dm1) - self.mf.make_rdm1() vhf = self.get_veff_for_energy(with_exxdiv=False) else: raise ValueError - e_dmet += np.sum(np.asarray(vhf) * dm1)/2 + e_dmet += np.sum(np.asarray(vhf) * dm1) / 2 self.log.debugv("E_elec(DMET)= %s", energy_string(e_dmet)) return e_dmet / self.ncells @@ -1251,14 +1333,14 @@ def get_dmet_energy(self, part_cumulant=True, approx_cumulant=True, with_nuc=Tru # Utility # ------- - def _check_orthonormal(self, *mo_coeff, mo_name='', crit_tol=1e-2, err_tol=1e-7): + def _check_orthonormal(self, *mo_coeff, mo_name="", crit_tol=1e-2, err_tol=1e-7): """Check orthonormality of mo_coeff.""" mo_coeff = hstack(*mo_coeff) err = dot(mo_coeff.T, self.get_ovlp(), mo_coeff) - np.eye(mo_coeff.shape[-1]) l2 = np.linalg.norm(err) linf = abs(err).max() if mo_name: - mo_name = (' of %ss' % mo_name) + mo_name = " of %ss" % mo_name if max(l2, linf) > crit_tol: self.log.critical("Orthonormality error%s: L(2)= %.2e L(inf)= %.2e !", mo_name, l2, linf) raise OrthonormalityError("Orbitals not orhonormal!") @@ -1271,8 +1353,8 @@ def _check_orthonormal(self, *mo_coeff, mo_name='', crit_tol=1e-2, err_tol=1e-7) def get_mean_cluster_size(self): return np.mean([x.cluster.norb_active for x in self.fragments]) - def get_average_cluster_size(self, average='mean'): - if average == 'mean': + def get_average_cluster_size(self, average="mean"): + if average == "mean": return self.get_mean_cluster_size() raise ValueError @@ -1285,7 +1367,7 @@ def get_max_cluster_size(self): # --- Population analysis # ----------------------- - def _get_atom_projectors(self, atoms=None, projection='sao', orbital_filter=None): + def _get_atom_projectors(self, atoms=None, projection="sao", orbital_filter=None): if atoms is None: atoms2 = list(range(self.mol.natm)) # For supercell systems, we do not want all supercell-atom pairs, @@ -1298,11 +1380,11 @@ def _get_atom_projectors(self, atoms=None, projection='sao', orbital_filter=None # Get atomic projectors: projection = projection.lower() - if projection == 'sao': + if projection == "sao": frag = SAO_Fragmentation(self) - elif projection.replace('+', '').replace('/', '') == 'iaopao': + elif projection.replace("+", "").replace("/", "") == "iaopao": frag = IAOPAO_Fragmentation(self) - elif projection == 'iao': + elif projection == "iao": frag = IAO_Fragmentation(self) self.log.warning("IAO projection is not recommended for population analysis! Use IAO+PAO instead.") else: @@ -1327,18 +1409,28 @@ def _get_atom_projectors(self, atoms=None, projection='sao', orbital_filter=None return atoms1, atoms2, projectors - def get_lo_coeff(self, local_orbitals='lowdin', minao='auto'): - if local_orbitals.lower() == 'lowdin': + def get_lo_coeff(self, local_orbitals="lowdin", minao="auto"): + if local_orbitals.lower() == "lowdin": # Avoid pre_orth_ao step! - #self.c_lo = c_lo = pyscf.lo.orth_ao(self.mol, 'lowdin') - #self.c_lo = c_lo = pyscf.lo.orth_ao(self.mol, 'meta-lowdin', pre_orth_ao=None) + # self.c_lo = c_lo = pyscf.lo.orth_ao(self.mol, 'lowdin') + # self.c_lo = c_lo = pyscf.lo.orth_ao(self.mol, 'meta-lowdin', pre_orth_ao=None) return self.get_ovlp_power(power=-0.5) - elif local_orbitals.lower() == 'iao+pao': + elif local_orbitals.lower() == "iao+pao": return IAOPAO_Fragmentation(self, minao=minao).get_coeff() raise ValueError("Unknown local orbitals: %r" % local_orbitals) - def pop_analysis(self, dm1, mo_coeff=None, local_orbitals='lowdin', minao='auto', write=True, filename=None, filemode='a', - orbital_resolved=False, mpi_rank=0): + def pop_analysis( + self, + dm1, + mo_coeff=None, + local_orbitals="lowdin", + minao="auto", + write=True, + filename=None, + filemode="a", + orbital_resolved=False, + mpi_rank=0, + ): """ Parameters ---------- @@ -1353,12 +1445,12 @@ def pop_analysis(self, dm1, mo_coeff=None, local_orbitals='lowdin', minao='auto' Population of atomic orbitals. """ if mo_coeff is not None: - dm1 = einsum('ai,ij,bj->ab', mo_coeff, dm1, mo_coeff) + dm1 = einsum("ai,ij,bj->ab", mo_coeff, dm1, mo_coeff) ovlp = self.get_ovlp() if isinstance(local_orbitals, str): lo = local_orbitals.lower() - if lo == 'mulliken': + if lo == "mulliken": c_lo = None else: c_lo = self.get_lo_coeff(lo, minao=minao) @@ -1366,10 +1458,10 @@ def pop_analysis(self, dm1, mo_coeff=None, local_orbitals='lowdin', minao='auto' c_lo = local_orbitals if c_lo is None: - pop = einsum('ab,ba->a', dm1, ovlp) + pop = einsum("ab,ba->a", dm1, ovlp) else: cs = np.dot(c_lo.T, ovlp) - pop = einsum('ia,ab,ib->i', cs, dm1, cs) + pop = einsum("ia,ab,ib->i", cs, dm1, cs) if write and (mpi.rank == mpi_rank): self.write_population(pop, filename=filename, filemode=filemode, orbital_resolved=orbital_resolved) @@ -1386,29 +1478,30 @@ def get_atomic_charges(self, pop): charges += self.mol.atom_charges() return charges, spins - def write_population(self, pop, filename=None, filemode='a', orbital_resolved=False): + def write_population(self, pop, filename=None, filemode="a", orbital_resolved=False): charges, spins = self.get_atomic_charges(pop) if orbital_resolved: - aoslices = self.mol.aoslice_by_atom()[:,2:] + aoslices = self.mol.aoslice_by_atom()[:, 2:] aolabels = self.mol.ao_labels() if filename is None: - write = lambda *args : self.log.info(*args) + write = lambda *args: self.log.info(*args) write("Population analysis") write("-------------------") else: dirname = os.path.dirname(filename) - if dirname: os.makedirs(dirname, exist_ok=True) + if dirname: + os.makedirs(dirname, exist_ok=True) f = open(filename, filemode) - write = lambda fmt, *args : f.write((fmt+'\n') % args) + write = lambda fmt, *args: f.write((fmt + "\n") % args) tstamp = datetime.now() - self.log.info("Writing population analysis to file \"%s\". Time-stamp: %s", filename, tstamp) + self.log.info('Writing population analysis to file "%s". Time-stamp: %s', filename, tstamp) write("# Time-stamp: %s", tstamp) write("# Population analysis") write("# -------------------") for atom, charge in enumerate(charges): - write("%3d %-7s q= % 11.8f s= % 11.8f", atom, self.mol.atom_symbol(atom) + ':', charge, spins[atom]) + write("%3d %-7s q= % 11.8f s= % 11.8f", atom, self.mol.atom_symbol(atom) + ":", charge, spins[atom]) if orbital_resolved: aos = aoslices[atom] for ao in range(aos[0], aos[1]): @@ -1425,7 +1518,7 @@ def _check_fragment_nelectron(self, fragments=None, nelec=None): fragments = self.get_fragments(flags=dict(is_envelop=True)) if nelec is None: nelec = self.mol.nelectron - nelec_frags = sum([f.sym_factor*f.nelectron for f in fragments]) + nelec_frags = sum([f.sym_factor * f.nelectron for f in fragments]) self.log.info("Number of electrons over %d fragments= %.8f target= %.8f", len(fragments), nelec_frags, nelec) if abs(nelec_frags - nelec) > 1e-6: @@ -1442,7 +1535,7 @@ def site_fragmentation(self, **kwargs): """Initialize the quantum embedding method for the use of site fragments.""" return Site_Fragmentation(self, **kwargs) - def iao_fragmentation(self, minao='auto', **kwargs): + def iao_fragmentation(self, minao="auto", **kwargs): """Initialize the quantum embedding method for the use of IAO fragments. Parameters @@ -1452,7 +1545,7 @@ def iao_fragmentation(self, minao='auto', **kwargs): """ return IAO_Fragmentation(self, minao=minao, **kwargs) - def iaopao_fragmentation(self, minao='auto', **kwargs): + def iaopao_fragmentation(self, minao="auto", **kwargs): """Initialize the quantum embedding method for the use of IAO+PAO fragments. Parameters @@ -1468,13 +1561,13 @@ def cas_fragmentation(self, **kwargs): def _check_fragmentation(self, complete_occupied=True, complete_virtual=True, tol=1e-7): """Check if union of fragment spaces is orthonormal and complete.""" - if self.spinsym == 'restricted': + if self.spinsym == "restricted": nspin = 1 - tspin = lambda x, s : x + tspin = lambda x, s: x nelec = self.mol.nelectron - elif self.spinsym == 'unrestricted': + elif self.spinsym == "unrestricted": nspin = 2 - tspin = lambda x, s : x[s] + tspin = lambda x, s: x[s] nelec = self.mol.nelec ovlp = self.get_ovlp() dm1 = self.mf.make_rdm1() @@ -1491,14 +1584,14 @@ def _check_fragmentation(self, complete_occupied=True, complete_virtual=True, to self.log.debug("Non-orthogonal error= %.3e", abs(csc - np.eye(nfrags)).max()) return False if complete_occupied and complete_virtual: - if (nfrags != nmo_s): + if nfrags != nmo_s: return False elif complete_occupied or complete_virtual: cs = np.dot(c_frags.T, ovlp) - ne = einsum('ia,ab,ib->', cs, tspin(dm1, s), cs) + ne = einsum("ia,ab,ib->", cs, tspin(dm1, s), cs) if complete_occupied and (abs(ne - nelec_s) > tol): return False - if complete_virtual and (abs((nfrags-ne) - (nmo_s-nelec_s)) > tol): + if complete_virtual and (abs((nfrags - ne) - (nmo_s - nelec_s)) > tol): return False return True @@ -1526,9 +1619,9 @@ def require_complete_fragmentation(self, message=None, incl_virtual=True, **kwar if complete: return if message: - message = ' %s' % message + message = " %s" % message else: - message = '' + message = "" self.log.error("Fragmentation is not orthogonal and complete.%s", message) # --- Reset @@ -1560,7 +1653,7 @@ def update_mf(self, mo_coeff, mo_energy=None, veff=None): self.set_veff(veff) if mo_energy is None: # Use diagonal of Fock matrix as MO energies - mo_energy = einsum('ai,ab,bi->i', mo_coeff, self.get_fock(), mo_coeff) + mo_energy = einsum("ai,ab,bi->i", mo_coeff, self.get_fock(), mo_coeff) self.mf.mo_energy = mo_energy self.mf.e_tot = self.mf.energy_tot(dm=dm, h1e=self.get_hcore(), vhf=veff) @@ -1571,18 +1664,24 @@ def check_fragment_symmetry(self, dm1, symtol=1e-6): parent, children = group[0], group[1:] for child in children: charge_err, spin_err = parent.get_symmetry_error(child, dm1=dm1) - if (max(charge_err, spin_err) > symtol): - raise SymmetryError("%s and %s not symmetric! Errors: charge= %.2e spin= %.2e" - % (parent.name, child.name, charge_err, spin_err)) + if max(charge_err, spin_err) > symtol: + raise SymmetryError( + "%s and %s not symmetric! Errors: charge= %.2e spin= %.2e" + % (parent.name, child.name, charge_err, spin_err) + ) else: - self.log.debugv("Symmetry between %s and %s: Errors: charge= %.2e spin= %.2e", - parent.name, child.name, charge_err, spin_err) + self.log.debugv( + "Symmetry between %s and %s: Errors: charge= %.2e spin= %.2e", + parent.name, + child.name, + charge_err, + spin_err, + ) # --- Decorators # These replace the qemb.kernel method! def optimize_chempot(self, cpt_init=0.0, dm1func=None, dm1kwds=None, robust=False): - if dm1func is None: dm1func = self.make_rdm1_demo if dm1kwds is None: @@ -1601,7 +1700,7 @@ def func(cpt, *args, **kwargs): ne = np.trace(dm1) else: ne = np.trace(dm1[0]) + np.trace(dm1[1]) - err = (ne - self.mol.nelectron) + err = ne - self.mol.nelectron iters.append((cpt, err, self.converged, self.e_tot)) return err @@ -1615,11 +1714,13 @@ def kernel(self, *args, **kwargs): self.log.info("-------------------------------") self.log.info(" Iteration Chemical potential N(elec) error Converged Total Energy") for i, (cpt, err, conv, etot) in enumerate(iters): - self.log.info(" %9d %19s %+14.8f %9r %19s", - i+1, energy_string(cpt), err, conv, energy_string(etot)) + self.log.info( + " %9d %19s %+14.8f %9r %19s", i + 1, energy_string(cpt), err, conv, energy_string(etot) + ) if not bisect.converged: - self.log.error('Chemical potential not found!') + self.log.error("Chemical potential not found!") return result + self.kernel = kernel.__get__(self) def pdmet_scmf(self, *args, **kwargs): diff --git a/vayesta/core/qemb/rdm.py b/vayesta/core/qemb/rdm.py index 27dbe84df..0b2470f19 100644 --- a/vayesta/core/qemb/rdm.py +++ b/vayesta/core/qemb/rdm.py @@ -37,11 +37,11 @@ def make_rdm1_demo_rhf(emb, ao_basis=False, with_mf=True, symmetrize=True): for x in _get_fragments(emb): emb.log.debugv("Now adding projected DM of fragment %s", x) dm1x = x.results.wf.make_rdm1(with_mf=False) - rx = x.get_overlap('mo|cluster') - px = x.get_overlap('cluster|frag|cluster') - dm1 += einsum('xi,ij,px,qj->pq', px, dm1x, rx, rx) + rx = x.get_overlap("mo|cluster") + px = x.get_overlap("cluster|frag|cluster") + dm1 += einsum("xi,ij,px,qj->pq", px, dm1x, rx, rx) if symmetrize: - dm1 = (dm1 + dm1.T)/2 + dm1 = (dm1 + dm1.T) / 2 if ao_basis: dm1 = dot(mo_coeff, dm1, mo_coeff.T) return dm1 @@ -78,13 +78,13 @@ def make_rdm1_demo_uhf(emb, ao_basis=False, with_mf=True, symmetrize=True): for x in _get_fragments(emb): emb.log.debugv("Now adding projected DM of fragment %s", x) dm1xa, dm1xb = x.results.wf.make_rdm1(with_mf=False) - rxa, rxb = x.get_overlap('mo|cluster') - pxa, pxb = x.get_overlap('cluster|frag|cluster') - dm1a += einsum('xi,ij,px,qj->pq', pxa, dm1xa, rxa, rxa) - dm1b += einsum('xi,ij,px,qj->pq', pxb, dm1xb, rxb, rxb) + rxa, rxb = x.get_overlap("mo|cluster") + pxa, pxb = x.get_overlap("cluster|frag|cluster") + dm1a += einsum("xi,ij,px,qj->pq", pxa, dm1xa, rxa, rxa) + dm1b += einsum("xi,ij,px,qj->pq", pxb, dm1xb, rxb, rxb) if symmetrize: - dm1a = (dm1a + dm1a.T)/2 - dm1b = (dm1b + dm1b.T)/2 + dm1a = (dm1a + dm1a.T) / 2 + dm1b = (dm1b + dm1b.T) / 2 if ao_basis: dm1a = dot(mo_coeff[0], dm1a, mo_coeff[0].T) dm1b = dot(mo_coeff[1], dm1b, mo_coeff[1].T) @@ -94,8 +94,10 @@ def make_rdm1_demo_uhf(emb, ao_basis=False, with_mf=True, symmetrize=True): # --- Two-particle # ---------------- -def make_rdm2_demo_rhf(emb, ao_basis=False, with_mf=True, with_dm1=True, - part_cumulant=True, approx_cumulant=True, symmetrize=True): + +def make_rdm2_demo_rhf( + emb, ao_basis=False, with_mf=True, with_dm1=True, part_cumulant=True, approx_cumulant=True, symmetrize=True +): """Make democratically partitioned two-particle reduced density-matrix from fragment calculations. Warning: A democratically partitioned DM is only expected to yield reasonable results @@ -168,8 +170,8 @@ def make_rdm2_demo_rhf(emb, ao_basis=False, with_mf=True, with_dm1=True, # Loop over fragments to get cumulant contributions + non-cumulant contributions, # if (approx_cumulant and part_cumulant): for x in _get_fragments(emb): - rx = x.get_overlap('mo|cluster') - px = x.get_overlap('cluster|frag|cluster') + rx = x.get_overlap("mo|cluster") + px = x.get_overlap("cluster|frag|cluster") # Partitioned cumulant: if part_cumulant or not with_dm1: @@ -182,8 +184,7 @@ def make_rdm2_demo_rhf(emb, ao_basis=False, with_mf=True, with_dm1=True, except NotImplementedError: dm1x = x.results.wf.make_rdm1() dm2x = x.results.wf.make_rdm2() - dm2x -= (einsum('ij,kl->ijkl', dm1x, dm1x) - - einsum('ij,kl->iklj', dm1x, dm1x)/2) + dm2x -= einsum("ij,kl->ijkl", dm1x, dm1x) - einsum("ij,kl->iklj", dm1x, dm1x) / 2 # Partitioned 2-DM: else: dm2x = x.results.wf.make_rdm2(with_dm1=False, approx_cumulant=True) @@ -201,12 +202,12 @@ def make_rdm2_demo_rhf(emb, ao_basis=False, with_mf=True, with_dm1=True, # ddm2[i,:,:,i] -= dm1x # dm2 += einsum('xi,ijkl->xjkl', p, ddm2) - p = x.get_overlap('mo|frag|mo') + p = x.get_overlap("mo|frag|mo") pdm1x = np.dot(p, dm1x) # Projected DM1(HF) - p = x.get_overlap('mo|mo[occ]|frag|mo') - dm2 += 2*einsum('ij,kl->ijkl', p, dm1x) - dm2 -= einsum('ij,kl->iklj', p, dm1x) + p = x.get_overlap("mo|mo[occ]|frag|mo") + dm2 += 2 * einsum("ij,kl->ijkl", p, dm1x) + dm2 -= einsum("ij,kl->iklj", p, dm1x) # Replacing the above with this would lead to the new DMET energy: # for i in range(emb.nocc): # dm2[i,i] += 2*pdm1x @@ -214,10 +215,10 @@ def make_rdm2_demo_rhf(emb, ao_basis=False, with_mf=True, with_dm1=True, # Projected DM1(CC) for i in range(emb.nocc): - dm2[:,:,i,i] += 2*pdm1x - dm2[:,i,i,:] -= pdm1x + dm2[:, :, i, i] += 2 * pdm1x + dm2[:, i, i, :] -= pdm1x - dm2 += einsum('xi,ijkl,px,qj,rk,sl->pqrs', px, dm2x, rx, rx, rx, rx) + dm2 += einsum("xi,ijkl,px,qj,rk,sl->pqrs", px, dm2x, rx, rx, rx, rx) # Add non-cumulant contribution (unless added above, for part_cumulant=False) if with_dm1 and part_cumulant: @@ -225,25 +226,25 @@ def make_rdm2_demo_rhf(emb, ao_basis=False, with_mf=True, with_dm1=True, ddm1 = make_rdm1_demo_rhf(emb, with_mf=False) ddm1[np.diag_indices(emb.nocc)] += 1 for i in range(emb.nocc): - dm2[i,i,:,:] += 2*ddm1 - dm2[:,:,i,i] += 2*ddm1 - dm2[:,i,i,:] -= ddm1 - dm2[i,:,:,i] -= ddm1 + dm2[i, i, :, :] += 2 * ddm1 + dm2[:, :, i, i] += 2 * ddm1 + dm2[:, i, i, :] -= ddm1 + dm2[i, :, :, i] -= ddm1 else: dm1 = make_rdm1_demo_rhf(emb) - dm2 += (einsum('ij,kl->ijkl', dm1, dm1) - - einsum('ij,kl->iklj', dm1, dm1)/2) + dm2 += einsum("ij,kl->ijkl", dm1, dm1) - einsum("ij,kl->iklj", dm1, dm1) / 2 if symmetrize: - dm2 = (dm2 + dm2.transpose(1,0,3,2))/2 + dm2 = (dm2 + dm2.transpose(1, 0, 3, 2)) / 2 if ao_basis: - dm2 = einsum('ijkl,pi,qj,rk,sl->pqrs', dm2, *(4*[emb.mo_coeff])) + dm2 = einsum("ijkl,pi,qj,rk,sl->pqrs", dm2, *(4 * [emb.mo_coeff])) return dm2 @with_doc(make_rdm2_demo_rhf) -def make_rdm2_demo_uhf(emb, ao_basis=False, with_mf=True, with_dm1=True, - part_cumulant=True, approx_cumulant=True, symmetrize=True): +def make_rdm2_demo_uhf( + emb, ao_basis=False, with_mf=True, with_dm1=True, part_cumulant=True, approx_cumulant=True, symmetrize=True +): na, nb = emb.nmo dm2aa = np.zeros((na, na, na, na)) dm2ab = np.zeros((na, na, nb, nb)) @@ -252,8 +253,8 @@ def make_rdm2_demo_uhf(emb, ao_basis=False, with_mf=True, with_dm1=True, # Loop over fragments to get cumulant contributions + non-cumulant contributions, # if (approx_cumulant and part_cumulant): for x in _get_fragments(emb): - rxa, rxb = x.get_overlap('mo|cluster') - pxa, pxb = x.get_overlap('cluster|frag|cluster') + rxa, rxb = x.get_overlap("mo|cluster") + pxa, pxb = x.get_overlap("cluster|frag|cluster") # Partitioned cumulant: if part_cumulant or not with_dm1: @@ -262,9 +263,9 @@ def make_rdm2_demo_uhf(emb, ao_basis=False, with_mf=True, with_dm1=True, else: dm1xa, dm1xb = x.results.wf.make_rdm1() dm2xaa, dm2xab, dm2xbb = x.results.wf.make_rdm2() - dm2xaa -= einsum('ij,kl->ijkl', dm1xa, dm1xa) - einsum('ij,kl->iklj', dm1xa, dm1xa) - dm2xab -= einsum('ij,kl->ijkl', dm1xa, dm1xb) - dm2xbb -= einsum('ij,kl->ijkl', dm1xb, dm1xb) - einsum('ij,kl->iklj', dm1xb, dm1xb) + dm2xaa -= einsum("ij,kl->ijkl", dm1xa, dm1xa) - einsum("ij,kl->iklj", dm1xa, dm1xa) + dm2xab -= einsum("ij,kl->ijkl", dm1xa, dm1xb) + dm2xbb -= einsum("ij,kl->ijkl", dm1xb, dm1xb) - einsum("ij,kl->iklj", dm1xb, dm1xb) # Partitioned 2-DM: else: dm2xaa, dm2xab, dm2xbb = x.results.wf.make_rdm2(with_dm1=False, approx_cumulant=True) @@ -275,31 +276,32 @@ def make_rdm2_demo_uhf(emb, ao_basis=False, with_mf=True, with_dm1=True, dm1xa[np.diag_indices(emb.nocc[0])] += 0.5 dm1xb[np.diag_indices(emb.nocc[1])] += 0.5 - pa, pb = x.get_overlap('mo|frag|mo') + pa, pb = x.get_overlap("mo|frag|mo") ddm2aa = np.zeros_like(dm2aa) ddm2ab = np.zeros_like(dm2ab) ddm2bb = np.zeros_like(dm2bb) for i in range(emb.nocc[0]): - ddm2aa[i,i,:,:] += dm1xa - ddm2aa[:,:,i,i] += dm1xa - ddm2aa[:,i,i,:] -= dm1xa - ddm2aa[i,:,:,i] -= dm1xa - ddm2ab[i,i,:,:] += dm1xb + ddm2aa[i, i, :, :] += dm1xa + ddm2aa[:, :, i, i] += dm1xa + ddm2aa[:, i, i, :] -= dm1xa + ddm2aa[i, :, :, i] -= dm1xa + ddm2ab[i, i, :, :] += dm1xb for i in range(emb.nocc[1]): - ddm2bb[i,i,:,:] += dm1xb - ddm2bb[:,:,i,i] += dm1xb - ddm2bb[:,i,i,:] -= dm1xb - ddm2bb[i,:,:,i] -= dm1xb - ddm2ab[:,:,i,i] += dm1xa - dm2aa += einsum('xi,ijkl->xjkl', pa, ddm2aa) - dm2bb += einsum('xi,ijkl->xjkl', pb, ddm2bb) - dm2ab += (einsum('xi,ijkl->xjkl', pa, ddm2ab) - + einsum('xk,ijkl->ijxl', pb, ddm2ab))/2 - - dm2aa += einsum('xi,ijkl,px,qj,rk,sl->pqrs', pxa, dm2xaa, rxa, rxa, rxa, rxa) - dm2bb += einsum('xi,ijkl,px,qj,rk,sl->pqrs', pxb, dm2xbb, rxb, rxb, rxb, rxb) - dm2ab += (einsum('xi,ijkl,px,qj,rk,sl->pqrs', pxa, dm2xab, rxa, rxa, rxb, rxb) - + einsum('xk,ijkl,pi,qj,rx,sl->pqrs', pxb, dm2xab, rxa, rxa, rxb, rxb))/2 + ddm2bb[i, i, :, :] += dm1xb + ddm2bb[:, :, i, i] += dm1xb + ddm2bb[:, i, i, :] -= dm1xb + ddm2bb[i, :, :, i] -= dm1xb + ddm2ab[:, :, i, i] += dm1xa + dm2aa += einsum("xi,ijkl->xjkl", pa, ddm2aa) + dm2bb += einsum("xi,ijkl->xjkl", pb, ddm2bb) + dm2ab += (einsum("xi,ijkl->xjkl", pa, ddm2ab) + einsum("xk,ijkl->ijxl", pb, ddm2ab)) / 2 + + dm2aa += einsum("xi,ijkl,px,qj,rk,sl->pqrs", pxa, dm2xaa, rxa, rxa, rxa, rxa) + dm2bb += einsum("xi,ijkl,px,qj,rk,sl->pqrs", pxb, dm2xbb, rxb, rxb, rxb, rxb) + dm2ab += ( + einsum("xi,ijkl,px,qj,rk,sl->pqrs", pxa, dm2xab, rxa, rxa, rxb, rxb) + + einsum("xk,ijkl,pi,qj,rx,sl->pqrs", pxb, dm2xab, rxa, rxa, rxb, rxb) + ) / 2 if with_dm1 and part_cumulant: if approx_cumulant: @@ -307,28 +309,28 @@ def make_rdm2_demo_uhf(emb, ao_basis=False, with_mf=True, with_dm1=True, ddm1a[np.diag_indices(emb.nocc[0])] += 0.5 ddm1b[np.diag_indices(emb.nocc[1])] += 0.5 for i in range(emb.nocc[0]): - dm2aa[i,i,:,:] += ddm1a - dm2aa[:,:,i,i] += ddm1a - dm2aa[:,i,i,:] -= ddm1a - dm2aa[i,:,:,i] -= ddm1a - dm2ab[i,i,:,:] += ddm1b + dm2aa[i, i, :, :] += ddm1a + dm2aa[:, :, i, i] += ddm1a + dm2aa[:, i, i, :] -= ddm1a + dm2aa[i, :, :, i] -= ddm1a + dm2ab[i, i, :, :] += ddm1b for i in range(emb.nocc[1]): - dm2bb[i,i,:,:] += ddm1b - dm2bb[:,:,i,i] += ddm1b - dm2bb[:,i,i,:] -= ddm1b - dm2bb[i,:,:,i] -= ddm1b - dm2ab[:,:,i,i] += ddm1a + dm2bb[i, i, :, :] += ddm1b + dm2bb[:, :, i, i] += ddm1b + dm2bb[:, i, i, :] -= ddm1b + dm2bb[i, :, :, i] -= ddm1b + dm2ab[:, :, i, i] += ddm1a else: dm1a, dm1b = make_rdm1_demo_uhf(emb) - dm2aa += einsum('ij,kl->ijkl', dm1a, dm1a) - einsum('ij,kl->iklj', dm1a, dm1a) - dm2bb += einsum('ij,kl->ijkl', dm1b, dm1b) - einsum('ij,kl->iklj', dm1b, dm1b) - dm2ab += einsum('ij,kl->ijkl', dm1a, dm1b) + dm2aa += einsum("ij,kl->ijkl", dm1a, dm1a) - einsum("ij,kl->iklj", dm1a, dm1a) + dm2bb += einsum("ij,kl->ijkl", dm1b, dm1b) - einsum("ij,kl->iklj", dm1b, dm1b) + dm2ab += einsum("ij,kl->ijkl", dm1a, dm1b) if symmetrize: - dm2aa = (dm2aa + dm2aa.transpose(1,0,3,2))/2 - dm2bb = (dm2bb + dm2bb.transpose(1,0,3,2))/2 + dm2aa = (dm2aa + dm2aa.transpose(1, 0, 3, 2)) / 2 + dm2bb = (dm2bb + dm2bb.transpose(1, 0, 3, 2)) / 2 if ao_basis: - dm2aa = einsum('ijkl,pi,qj,rk,sl->pqrs', dm2aa, *(4*[emb.mo_coeff[0]])) - dm2bb = einsum('ijkl,pi,qj,rk,sl->pqrs', dm2bb, *(4*[emb.mo_coeff[1]])) - dm2ab = einsum('ijkl,pi,qj,rk,sl->pqrs', dm2ab, *(2*[emb.mo_coeff[0]] + 2*[emb.mo_coeff[1]])) + dm2aa = einsum("ijkl,pi,qj,rk,sl->pqrs", dm2aa, *(4 * [emb.mo_coeff[0]])) + dm2bb = einsum("ijkl,pi,qj,rk,sl->pqrs", dm2bb, *(4 * [emb.mo_coeff[1]])) + dm2ab = einsum("ijkl,pi,qj,rk,sl->pqrs", dm2ab, *(2 * [emb.mo_coeff[0]] + 2 * [emb.mo_coeff[1]])) return (dm2aa, dm2ab, dm2bb) diff --git a/vayesta/core/qemb/register.py b/vayesta/core/qemb/register.py index fb543018a..6726bec32 100644 --- a/vayesta/core/qemb/register.py +++ b/vayesta/core/qemb/register.py @@ -2,7 +2,6 @@ class FragmentRegister: - def __init__(self, mpi_size=None): self._next_id = -1 if mpi_size is None: diff --git a/vayesta/core/qemb/ufragment.py b/vayesta/core/qemb/ufragment.py index 7529be4dc..dee4fbd48 100644 --- a/vayesta/core/qemb/ufragment.py +++ b/vayesta/core/qemb/ufragment.py @@ -1,4 +1,3 @@ - import numpy as np from vayesta.core.util import dot, einsum, energy_string, log_time, time_string, timer, with_doc @@ -7,23 +6,21 @@ class UFragment(Fragment): - def log_info(self): # Some output - fmt = ' > %-24s ' - self.log.info(fmt+'%d , %d', "Fragment orbitals:", *self.n_frag) - self.log.info(fmt+'%f', "Symmetry factor:", self.sym_factor) - self.log.info(fmt+'%.10f , %.10f', "Number of electrons:", *self.nelectron) + fmt = " > %-24s " + self.log.info(fmt + "%d , %d", "Fragment orbitals:", *self.n_frag) + self.log.info(fmt + "%f", "Symmetry factor:", self.sym_factor) + self.log.info(fmt + "%.10f , %.10f", "Number of electrons:", *self.nelectron) if self.atoms is not None: - self.log.info(fmt+'%r', "Associated atoms:", self.atoms) + self.log.info(fmt + "%r", "Associated atoms:", self.atoms) if self.aos is not None: - self.log.info(fmt+'%r', "Associated AOs:", self.aos) + self.log.info(fmt + "%r", "Associated AOs:", self.aos) @property def n_frag(self): """Number of fragment orbitals.""" - return (self.c_frag[0].shape[-1], - self.c_frag[1].shape[-1]) + return (self.c_frag[0].shape[-1], self.c_frag[1].shape[-1]) @property def nelectron(self): @@ -33,7 +30,7 @@ def nelectron(self): ne = [] for s in range(2): sc = np.dot(ovlp, self.c_frag[s]) - ne.append(einsum('ai,ab,bi->', sc, dm[s], sc)) + ne.append(einsum("ai,ab,bi->", sc, dm[s], sc)) return tuple(ne) def get_mo_occupation(self, *mo_coeff, dm1=None, **kwargs): @@ -50,9 +47,10 @@ def get_mo_occupation(self, *mo_coeff, dm1=None, **kwargs): Occupation numbers of orbitals. """ mo_coeff = spinalg.hstack_matrices(*mo_coeff) - if dm1 is None: dm1 = self.mf.make_rdm1() + if dm1 is None: + dm1 = self.mf.make_rdm1() results = [] - for s, spin in enumerate(('alpha', 'beta')): + for s, spin in enumerate(("alpha", "beta")): results.append(super().get_mo_occupation(mo_coeff[s], dm1=dm1[s], **kwargs)) return results @@ -73,10 +71,11 @@ def canonicalize_mo(self, *mo_coeff, fock=None, **kwargs): rot : ndarray Rotation matrix: np.dot(mo_coeff, rot) = mo_canon. """ - if fock is None: fock = self.base.get_fock() + if fock is None: + fock = self.base.get_fock() mo_coeff = spinalg.hstack_matrices(*mo_coeff) results = [] - for s, spin in enumerate(('alpha', 'beta')): + for s, spin in enumerate(("alpha", "beta")): results.append(super().canonicalize_mo(mo_coeff[s], fock=fock[s], **kwargs)) return tuple(zip(*results)) @@ -99,9 +98,10 @@ def diagonalize_cluster_dm(self, *mo_coeff, dm1=None, norm=1, **kwargs): Virtual cluster orbitals. """ mo_coeff = spinalg.hstack_matrices(*mo_coeff) - if dm1 is None: dm1 = self.mf.make_rdm1() + if dm1 is None: + dm1 = self.mf.make_rdm1() results = [] - for s, spin in enumerate(('alpha', 'beta')): + for s, spin in enumerate(("alpha", "beta")): res_s = super().diagonalize_cluster_dm(mo_coeff[s], dm1=dm1[s], norm=norm, **kwargs) results.append(res_s) return tuple(zip(*results)) @@ -110,7 +110,8 @@ def diagonalize_cluster_dm(self, *mo_coeff, dm1=None, norm=1, **kwargs): # -------------------- def get_fragment_projector(self, coeff, c_proj=None, **kwargs): - if c_proj is None: c_proj = self.c_proj + if c_proj is None: + c_proj = self.c_proj projectors = [] for s in range(2): projectors.append(super().get_fragment_projector(coeff[s], c_proj=c_proj[s], **kwargs)) @@ -122,8 +123,14 @@ def get_fragment_mf_energy(self): Does not include nuclear-nuclear repulsion! """ pa, pb = self.get_fragment_projector(self.base.mo_coeff) - hveff = (dot(pa, self.base.mo_coeff[0].T, self.base.get_hcore()+self.base.get_veff()[0]/2, self.base.mo_coeff[0]), - dot(pb, self.base.mo_coeff[1].T, self.base.get_hcore()+self.base.get_veff()[1]/2, self.base.mo_coeff[1])) + hveff = ( + dot( + pa, self.base.mo_coeff[0].T, self.base.get_hcore() + self.base.get_veff()[0] / 2, self.base.mo_coeff[0] + ), + dot( + pb, self.base.mo_coeff[1].T, self.base.get_hcore() + self.base.get_veff()[1] / 2, self.base.mo_coeff[1] + ), + ) occ = ((self.base.mo_occ[0] > 0), (self.base.mo_occ[1] > 0)) e_mf = np.sum(np.diag(hveff[0])[occ[0]]) + np.sum(np.diag(hveff[1])[occ[1]]) return e_mf @@ -136,16 +143,22 @@ def get_fragment_mo_energy(self, c_active=None, fock=None): c_active: array, optional fock: array, optional """ - if c_active is None: c_active = self.cluster.c_active - if fock is None: fock = self.base.get_fock() - mo_energy_a = einsum('ai,ab,bi->i', c_active[0], fock[0], c_active[0]) - mo_energy_b = einsum('ai,ab,bi->i', c_active[1], fock[1], c_active[1]) + if c_active is None: + c_active = self.cluster.c_active + if fock is None: + fock = self.base.get_fock() + mo_energy_a = einsum("ai,ab,bi->i", c_active[0], fock[0], c_active[0]) + mo_energy_b = einsum("ai,ab,bi->i", c_active[1], fock[1], c_active[1]) return (mo_energy_a, mo_energy_b) @with_doc(Fragment.get_fragment_dmet_energy) - def get_fragment_dmet_energy(self, dm1=None, dm2=None, h1e_eff=None, hamil=None, part_cumulant=True, approx_cumulant=True): - if dm1 is None: dm1 = self.results.dm1 - if dm1 is None: raise RuntimeError("DM1 not found for %s" % self) + def get_fragment_dmet_energy( + self, dm1=None, dm2=None, h1e_eff=None, hamil=None, part_cumulant=True, approx_cumulant=True + ): + if dm1 is None: + dm1 = self.results.dm1 + if dm1 is None: + raise RuntimeError("DM1 not found for %s" % self) c_act = self.cluster.c_active t0 = timer() if hamil is None: @@ -161,38 +174,43 @@ def get_fragment_dmet_energy(self, dm1=None, dm2=None, h1e_eff=None, hamil=None, if h1e_eff is None: if part_cumulant: h1e_eff = self.base.get_hcore_for_energy() - h1e_eff = (dot(c_act[0].T, h1e_eff, c_act[0]), - dot(c_act[1].T, h1e_eff, c_act[1])) + h1e_eff = (dot(c_act[0].T, h1e_eff, c_act[0]), dot(c_act[1].T, h1e_eff, c_act[1])) else: # Use the original Hcore (without chemical potential modifications), but updated mf-potential! - h1e_eff = self.base.get_hcore_for_energy() + self.base.get_veff_for_energy(with_exxdiv=False)/2 - h1e_eff = (dot(c_act[0].T, h1e_eff[0], c_act[0]), - dot(c_act[1].T, h1e_eff[1], c_act[1])) - oa = np.s_[:self.cluster.nocc_active[0]] - ob = np.s_[:self.cluster.nocc_active[1]] - va = (einsum('iipq->pq', gaa[oa,oa,:,:]) + einsum('pqii->pq', gab[:,:,ob,ob]) - - einsum('ipqi->pq', gaa[oa,:,:,oa]))/2 - vb = (einsum('iipq->pq', gbb[ob,ob,:,:]) + einsum('iipq->pq', gab[oa,oa,:,:]) - - einsum('ipqi->pq', gbb[ob,:,:,ob]))/2 - h1e_eff = (h1e_eff[0]-va, h1e_eff[1]-vb) + h1e_eff = self.base.get_hcore_for_energy() + self.base.get_veff_for_energy(with_exxdiv=False) / 2 + h1e_eff = (dot(c_act[0].T, h1e_eff[0], c_act[0]), dot(c_act[1].T, h1e_eff[1], c_act[1])) + oa = np.s_[: self.cluster.nocc_active[0]] + ob = np.s_[: self.cluster.nocc_active[1]] + va = ( + einsum("iipq->pq", gaa[oa, oa, :, :]) + + einsum("pqii->pq", gab[:, :, ob, ob]) + - einsum("ipqi->pq", gaa[oa, :, :, oa]) + ) / 2 + vb = ( + einsum("iipq->pq", gbb[ob, ob, :, :]) + + einsum("iipq->pq", gab[oa, oa, :, :]) + - einsum("ipqi->pq", gbb[ob, :, :, ob]) + ) / 2 + h1e_eff = (h1e_eff[0] - va, h1e_eff[1] - vb) p_frag = self.get_fragment_projector(c_act) # Check number of electrons - nea = einsum('ix,ij,jx->', p_frag[0], dm1a, p_frag[0]) - neb = einsum('ix,ij,jx->', p_frag[1], dm1b, p_frag[1]) + nea = einsum("ix,ij,jx->", p_frag[0], dm1a, p_frag[0]) + neb = einsum("ix,ij,jx->", p_frag[1], dm1b, p_frag[1]) self.log.info("Number of local electrons for DMET energy: %.8f %.8f", nea, neb) # Evaluate energy - e1b = (einsum('xj,xi,ij->', h1e_eff[0], p_frag[0], dm1a) - + einsum('xj,xi,ij->', h1e_eff[1], p_frag[1], dm1b)) - e2b = (einsum('xjkl,xi,ijkl->', gaa, p_frag[0], dm2aa) - + einsum('xjkl,xi,ijkl->', gbb, p_frag[1], dm2bb) - + einsum('xjkl,xi,ijkl->', gab, p_frag[0], dm2ab) - + einsum('ijxl,xk,ijkl->', gab, p_frag[1], dm2ab))/2 + e1b = einsum("xj,xi,ij->", h1e_eff[0], p_frag[0], dm1a) + einsum("xj,xi,ij->", h1e_eff[1], p_frag[1], dm1b) + e2b = ( + einsum("xjkl,xi,ijkl->", gaa, p_frag[0], dm2aa) + + einsum("xjkl,xi,ijkl->", gbb, p_frag[1], dm2bb) + + einsum("xjkl,xi,ijkl->", gab, p_frag[0], dm2ab) + + einsum("ijxl,xk,ijkl->", gab, p_frag[1], dm2ab) + ) / 2 self.log.debugv("E(DMET): E(1)= %s E(2)= %s", energy_string(e1b), energy_string(e2b)) - e_dmet = self.opts.sym_factor*(e1b + e2b) + e_dmet = self.opts.sym_factor * (e1b + e2b) self.log.debug("Fragment E(DMET)= %+16.8f Ha", e_dmet) - self.log.timing("Time for DMET energy: %s", time_string(timer()-t0)) + self.log.timing("Time for DMET energy: %s", time_string(timer() - t0)) return e_dmet # --- Symmetry @@ -216,8 +234,8 @@ def get_symmetry_error(self, frag, dm1=None): cya, cyb = spinalg.hstack_matrices(frag.c_frag, cy_env) dmya = dot(cya.T, ovlp, dma, ovlp, cya) dmyb = dot(cyb.T, ovlp, dmb, ovlp, cyb) - charge_err = abs(dmxa+dmxb-dmya-dmyb).max() - spin_err = abs(dmxa-dmxb-dmya+dmyb).max() + charge_err = abs(dmxa + dmxb - dmya - dmyb).max() + spin_err = abs(dmxa - dmxb - dmya + dmyb).max() return charge_err, spin_err # --- Overlap matrices diff --git a/vayesta/core/qemb/uqemb.py b/vayesta/core/qemb/uqemb.py index 46cce67f9..14b37b02b 100644 --- a/vayesta/core/qemb/uqemb.py +++ b/vayesta/core/qemb/uqemb.py @@ -19,6 +19,7 @@ from vayesta.core.qemb.rdm import make_rdm1_demo_uhf from vayesta.core.qemb.rdm import make_rdm2_demo_uhf + class UEmbedding(Embedding): """Spin unrestricted quantum embedding.""" @@ -29,9 +30,9 @@ class UEmbedding(Embedding): is_rhf = False is_uhf = True # Use instead: - spinsym = 'unrestricted' + spinsym = "unrestricted" - #def get_init_veff(self): + # def get_init_veff(self): # if self.opts.recalc_vhf: # self.log.debug("Recalculating HF potential from MF object.") # veff = self.mf.get_veff() @@ -43,7 +44,7 @@ class UEmbedding(Embedding): # e_hf = self.mf.energy_tot(vhf=veff) # return veff, e_hf - #def _mpi_bcast_mf(self, mf): + # def _mpi_bcast_mf(self, mf): # """Use mo_energy and mo_coeff from master MPI rank only.""" # # Check if all MPI ranks have the same mean-field MOs # #mo_energy = mpi.world.gather(mf.mo_energy) @@ -62,38 +63,33 @@ class UEmbedding(Embedding): @property def nmo(self): """Total number of molecular orbitals (MOs).""" - return (self.mo_coeff[0].shape[-1], - self.mo_coeff[1].shape[-1]) + return (self.mo_coeff[0].shape[-1], self.mo_coeff[1].shape[-1]) @property def nocc(self): """Number of occupied MOs.""" - return (np.count_nonzero(self.mo_occ[0] > 0), - np.count_nonzero(self.mo_occ[1] > 0)) + return (np.count_nonzero(self.mo_occ[0] > 0), np.count_nonzero(self.mo_occ[1] > 0)) @property def nvir(self): """Number of virtual MOs.""" - return (np.count_nonzero(self.mo_occ[0] == 0), - np.count_nonzero(self.mo_occ[1] == 0)) + return (np.count_nonzero(self.mo_occ[0] == 0), np.count_nonzero(self.mo_occ[1] == 0)) @property def mo_coeff_occ(self): """Occupied MO coefficients.""" - return (self.mo_coeff[0][:,:self.nocc[0]], - self.mo_coeff[1][:,:self.nocc[1]]) + return (self.mo_coeff[0][:, : self.nocc[0]], self.mo_coeff[1][:, : self.nocc[1]]) @property def mo_coeff_vir(self): """Virtual MO coefficients.""" - return (self.mo_coeff[0][:,self.nocc[0]:], - self.mo_coeff[1][:,self.nocc[1]:]) + return (self.mo_coeff[0][:, self.nocc[0] :], self.mo_coeff[1][:, self.nocc[1] :]) - def _check_orthonormal(self, *mo_coeff, mo_name='', **kwargs): + def _check_orthonormal(self, *mo_coeff, mo_name="", **kwargs): mo_coeff = spinalg.hstack_matrices(*mo_coeff) results = [] - for s, spin in enumerate(('alpha', ' beta')): - name_s = '-'.join([spin, mo_name]) + for s, spin in enumerate(("alpha", " beta")): + name_s = "-".join([spin, mo_name]) res_s = super()._check_orthonormal(mo_coeff[s], mo_name=name_s, **kwargs) results.append(res_s) return tuple(zip(*results)) @@ -108,11 +104,12 @@ def get_exxdiv(self): v_exxdiv: array Divergent exact-exchange potential correction in AO basis. """ - if not self.has_exxdiv: return 0, None + if not self.has_exxdiv: + return 0, None ovlp = self.get_ovlp() - sca = np.dot(ovlp, self.mo_coeff[0][:,:self.nocc[0]]) - scb = np.dot(ovlp, self.mo_coeff[1][:,:self.nocc[1]]) - nocc = (self.nocc[0] + self.nocc[1])/2 + sca = np.dot(ovlp, self.mo_coeff[0][:, : self.nocc[0]]) + scb = np.dot(ovlp, self.mo_coeff[1][:, : self.nocc[1]]) + nocc = (self.nocc[0] + self.nocc[1]) / 2 e_exxdiv = -self.madelung * nocc / self.ncells v_exxdiv_a = -self.madelung * np.dot(sca, sca.T) v_exxdiv_b = -self.madelung * np.dot(scb, scb.T) @@ -143,13 +140,13 @@ def get_eris_array_uhf(self, mo_coeff, mo_coeff2=None, compact=False): raise NotImplementedError cderia, cderia_neg = kao2gmo_cderi(self.kdf, (moa, mo2a)) cderib, cderib_neg = kao2gmo_cderi(self.kdf, (mob, mo2b)) - eris_aa = einsum('Lij,Lkl->ijkl', cderia.conj(), cderia) - eris_ab = einsum('Lij,Lkl->ijkl', cderia.conj(), cderib) - eris_bb = einsum('Lij,Lkl->ijkl', cderib.conj(), cderib) + eris_aa = einsum("Lij,Lkl->ijkl", cderia.conj(), cderia) + eris_ab = einsum("Lij,Lkl->ijkl", cderia.conj(), cderib) + eris_bb = einsum("Lij,Lkl->ijkl", cderib.conj(), cderib) if cderia_neg is not None: - eris_aa -= einsum('Lij,Lkl->ijkl', cderia_neg.conj(), cderia_neg) - eris_ab -= einsum('Lij,Lkl->ijkl', cderia_neg.conj(), cderib_neg) - eris_bb -= einsum('Lij,Lkl->ijkl', cderib_neg.conj(), cderib_neg) + eris_aa -= einsum("Lij,Lkl->ijkl", cderia_neg.conj(), cderia_neg) + eris_ab -= einsum("Lij,Lkl->ijkl", cderia_neg.conj(), cderib_neg) + eris_bb -= einsum("Lij,Lkl->ijkl", cderib_neg.conj(), cderib_neg) return (eris_aa, eris_ab, eris_bb) eris_aa = super().get_eris_array((moa, mo2a, moa, mo2a), compact=compact) @@ -183,9 +180,11 @@ def get_eris_object(self, postscf, fock=None): raise ValueError("Unknown post-HF method: %r", type(postscf)) # For MO energies, always use get_fock(): act = postscf.get_frozen_mask() - mo_act = (postscf.mo_coeff[0][:,act[0]], postscf.mo_coeff[1][:,act[1]]) - mo_energy = (einsum('ai,ab,bi->i', mo_act[0], self.get_fock()[0], mo_act[0]), - einsum('ai,ab,bi->i', mo_act[1], self.get_fock()[1], mo_act[1])) + mo_act = (postscf.mo_coeff[0][:, act[0]], postscf.mo_coeff[1][:, act[1]]) + mo_energy = ( + einsum("ai,ab,bi->i", mo_act[0], self.get_fock()[0], mo_act[0]), + einsum("ai,ab,bi->i", mo_act[1], self.get_fock()[1], mo_act[1]), + ) e_hf = self.mf.e_tot # 1) Fold MOs into k-point sampled primitive cell, to perform efficient AO->MO transformation: @@ -206,9 +205,11 @@ def build_screened_eris(self, *args, **kwargs): def update_mf(self, mo_coeff, mo_energy=None, veff=None): """Update underlying mean-field object.""" # Chech orthonormal MOs - if not (np.allclose(dot(mo_coeff[0].T, self.get_ovlp(), mo_coeff[0]) - np.eye(mo_coeff[0].shape[-1]), 0) - and np.allclose(dot(mo_coeff[1].T, self.get_ovlp(), mo_coeff[1]) - np.eye(mo_coeff[1].shape[-1]), 0)): - raise ValueError("MO coefficients not orthonormal!") + if not ( + np.allclose(dot(mo_coeff[0].T, self.get_ovlp(), mo_coeff[0]) - np.eye(mo_coeff[0].shape[-1]), 0) + and np.allclose(dot(mo_coeff[1].T, self.get_ovlp(), mo_coeff[1]) - np.eye(mo_coeff[1].shape[-1]), 0) + ): + raise ValueError("MO coefficients not orthonormal!") self.mf.mo_coeff = mo_coeff dm = self.mf.make_rdm1(mo_coeff=mo_coeff) if veff is None: @@ -217,8 +218,10 @@ def update_mf(self, mo_coeff, mo_energy=None, veff=None): if mo_energy is None: # Use diagonal of Fock matrix as MO energies fock = self.get_fock() - mo_energy = (einsum('ai,ab,bi->i', mo_coeff[0], fock[0], mo_coeff[0]), - einsum('ai,ab,bi->i', mo_coeff[1], fock[1], mo_coeff[1])) + mo_energy = ( + einsum("ai,ab,bi->i", mo_coeff[0], fock[0], mo_coeff[0]), + einsum("ai,ab,bi->i", mo_coeff[1], fock[1], mo_coeff[1]), + ) self.mf.mo_energy = mo_energy self.mf.e_tot = self.mf.energy_tot(dm=dm, h1e=self.get_hcore(), vhf=veff) @@ -229,13 +232,23 @@ def check_fragment_symmetry(self, dm1, charge_tol=1e-6, spin_tol=1e-6): for child in children: charge_err, spin_err = parent.get_tsymmetry_error(child, dm1=dm1) if (charge_err > charge_tol) or (spin_err > spin_tol): - raise RuntimeError("%s and %s not symmetric: charge error= %.3e spin error= %.3e !" - % (parent.name, child.name, charge_err, spin_err)) - self.log.debugv("Symmetry between %s and %s: charge error= %.3e spin error= %.3e", parent.name, child.name, charge_err, spin_err) + raise RuntimeError( + "%s and %s not symmetric: charge error= %.3e spin error= %.3e !" + % (parent.name, child.name, charge_err, spin_err) + ) + self.log.debugv( + "Symmetry between %s and %s: charge error= %.3e spin error= %.3e", + parent.name, + child.name, + charge_err, + spin_err, + ) def _check_fragment_nelectron(self): - nelec_frags = (sum([f.sym_factor*f.nelectron[0] for f in self.loop()]), - sum([f.sym_factor*f.nelectron[1] for f in self.loop()])) + nelec_frags = ( + sum([f.sym_factor * f.nelectron[0] for f in self.loop()]), + sum([f.sym_factor * f.nelectron[1] for f in self.loop()]), + ) self.log.info("Total number of mean-field electrons over all fragments= %.8f , %.8f", *nelec_frags) if abs(nelec_frags[0] - np.rint(nelec_frags[0])) > 1e-4 or abs(nelec_frags[1] - np.rint(nelec_frags[1])) > 1e-4: self.log.warning("Number of electrons not integer!") @@ -254,14 +267,14 @@ def make_rdm1_demo(self, *args, **kwargs): def make_rdm2_demo(self, *args, **kwargs): return make_rdm2_demo_uhf(self, *args, **kwargs) - def pop_analysis(self, dm1, mo_coeff=None, local_orbitals='lowdin', write=True, minao='auto', mpi_rank=0, **kwargs): + def pop_analysis(self, dm1, mo_coeff=None, local_orbitals="lowdin", write=True, minao="auto", mpi_rank=0, **kwargs): # IAO / PAOs are spin dependent - we need to build them here: - if isinstance(local_orbitals, str) and local_orbitals.lower() == 'iao+pao': - local_orbitals = self.get_lo_coeff('iao+pao', minao=minao) + if isinstance(local_orbitals, str) and local_orbitals.lower() == "iao+pao": + local_orbitals = self.get_lo_coeff("iao+pao", minao=minao) pop = [] - for s, spin in enumerate(('alpha', 'beta')): - mo = (mo_coeff[s] if mo_coeff is not None else None) - lo = (local_orbitals if isinstance(local_orbitals, str) else local_orbitals[s]) + for s, spin in enumerate(("alpha", "beta")): + mo = mo_coeff[s] if mo_coeff is not None else None + lo = local_orbitals if isinstance(local_orbitals, str) else local_orbitals[s] pop.append(super().pop_analysis(dm1[s], mo_coeff=mo, local_orbitals=lo, write=False, **kwargs)) if write and (mpi.rank == mpi_rank): self.write_population(pop, **kwargs) @@ -271,8 +284,8 @@ def get_atomic_charges(self, pop): charges = np.zeros(self.mol.natm) spins = np.zeros(self.mol.natm) for i, label in enumerate(self.mol.ao_labels(fmt=None)): - charges[label[0]] -= (pop[0][i] + pop[1][i]) - spins[label[0]] += (pop[0][i] - pop[1][i]) + charges[label[0]] -= pop[0][i] + pop[1][i] + spins[label[0]] += pop[0][i] - pop[1][i] charges += self.mol.atom_charges() return charges, spins diff --git a/vayesta/core/scmf/__init__.py b/vayesta/core/scmf/__init__.py index 2bd41bed6..7bf3bf655 100644 --- a/vayesta/core/scmf/__init__.py +++ b/vayesta/core/scmf/__init__.py @@ -9,6 +9,7 @@ def PDMET(emb, *args, **kwargs): return PDMET_RHF(emb, *args, **kwargs) return PDMET_UHF(emb, *args, **kwargs) + def Brueckner(emb, *args, **kwargs): if emb.is_rhf: return Brueckner_RHF(emb, *args, **kwargs) diff --git a/vayesta/core/scmf/brueckner.py b/vayesta/core/scmf/brueckner.py index 2e221dc9b..c05e1ffc3 100644 --- a/vayesta/core/scmf/brueckner.py +++ b/vayesta/core/scmf/brueckner.py @@ -1,20 +1,20 @@ import numpy as np import scipy import scipy.linalg -#from vayesta.misc import PCDIIS + +# from vayesta.misc import PCDIIS from vayesta.core.util import dot, fix_orbital_sign from vayesta.core.scmf.scmf import SCMF class Brueckner_RHF(SCMF): - name = "Brueckner" - def __init__(self, *args, diis_obj='dm1', **kwargs): + def __init__(self, *args, diis_obj="dm1", **kwargs): super().__init__(*args, **kwargs) self.diis_obj = diis_obj.lower() - #def get_diis(self): + # def get_diis(self): # """PC-DIIS""" # nocc = np.count_nonzero(self.mf.mo_occ > 0) # diis = PCDIIS(self._mo_orig[:,:nocc].copy()) @@ -29,52 +29,57 @@ def update_mo_coeff(self, mf, diis=None): t1 = self.get_t1() self.log.debug("Norm of T1: L(2)= %.3e L(inf)= %.3e", np.linalg.norm(t1), abs(t1).max()) nocc, nvir = t1.shape - nmo = (nocc + nvir) + nmo = nocc + nvir occ, vir = np.s_[:nocc], np.s_[nocc:] ovlp = self.emb.get_ovlp() # Perform DIIS in original MO basis, then transform back: - if diis and self.diis_obj == 't1': - ro = dot(mf.mo_coeff[:,occ].T, ovlp, self._mo_orig) - rv = dot(mf.mo_coeff[:,vir].T, ovlp, self._mo_orig) + if diis and self.diis_obj == "t1": + ro = dot(mf.mo_coeff[:, occ].T, ovlp, self._mo_orig) + rv = dot(mf.mo_coeff[:, vir].T, ovlp, self._mo_orig) t1 = dot(ro.T, t1, rv) t1 = diis.update(t1, xerr=t1) ## Transform back t1 = dot(ro, t1, rv.T) - mo_change = (1-self.damping)*np.dot(mf.mo_coeff[:,vir], t1.T) + mo_change = (1 - self.damping) * np.dot(mf.mo_coeff[:, vir], t1.T) self.log.debug("Change of occupied Brueckner orbitals= %.3e", np.linalg.norm(mo_change)) - bmo_occ = (mf.mo_coeff[:,occ] + mo_change) + bmo_occ = mf.mo_coeff[:, occ] + mo_change # Orthogonalize occupied orbitals # If there was no AO-overlap matrix: bmo_occ = np.linalg.qr(bmo_occ)[0] dm_occ = np.dot(bmo_occ, bmo_occ.T) e, v = scipy.linalg.eigh(dm_occ, b=ovlp, type=2) - bmo_occ = v[:,-nocc:] + bmo_occ = v[:, -nocc:] # DIIS of occupied density - if diis and self.diis_obj == 'dm1': + if diis and self.diis_obj == "dm1": dm_occ = np.dot(bmo_occ, bmo_occ.T) r = np.dot(ovlp, self._mo_orig) dm_occ = dot(r.T, dm_occ, r) dm_occ = diis.update(dm_occ) e, v = np.linalg.eigh(dm_occ) - bmo_occ = np.dot(self._mo_orig, v)[:,-nocc:] + bmo_occ = np.dot(self._mo_orig, v)[:, -nocc:] # Virtual space - dm_vir = (np.linalg.inv(ovlp) - np.dot(bmo_occ, bmo_occ.T)) + dm_vir = np.linalg.inv(ovlp) - np.dot(bmo_occ, bmo_occ.T) e, v = scipy.linalg.eigh(dm_vir, b=ovlp, type=2) - bmo_vir = v[:,-nvir:] + bmo_vir = v[:, -nvir:] assert (bmo_occ.shape[-1] == nocc) and (bmo_vir.shape[-1] == nvir) mo_coeff = np.hstack((bmo_occ, bmo_vir)) mo_coeff = fix_orbital_sign(mo_coeff)[0] return mo_coeff -class Brueckner_UHF(Brueckner_RHF): +class Brueckner_UHF(Brueckner_RHF): def update_mo_coeff(self, mf, diis=None): t1a, t1b = self.get_t1() - self.log.debug("Norm of alpha/beta-T1 L(2)= %.3e %.3e L(inf)= %.3e %.3e", - np.linalg.norm(t1a), np.linalg.norm(t1b), abs(t1a).max(), abs(t1b).max()) + self.log.debug( + "Norm of alpha/beta-T1 L(2)= %.3e %.3e L(inf)= %.3e %.3e", + np.linalg.norm(t1a), + np.linalg.norm(t1b), + abs(t1a).max(), + abs(t1b).max(), + ) nocca, nvira = t1a.shape noccb, nvirb = t1b.shape nmoa, nmob = nocca + nvira, noccb + nvirb @@ -83,26 +88,29 @@ def update_mo_coeff(self, mf, diis=None): ovlp = self.emb.get_ovlp() # Perform DIIS in original MO basis, then transform back: - if diis and self.diis_obj == 't1': - roa = dot(mf.mo_coeff[0][:,occa].T, ovlp, self._mo_orig[0]) - rob = dot(mf.mo_coeff[1][:,occb].T, ovlp, self._mo_orig[1]) - rva = dot(mf.mo_coeff[0][:,vira].T, ovlp, self._mo_orig[0]) - rvb = dot(mf.mo_coeff[1][:,virb].T, ovlp, self._mo_orig[1]) + if diis and self.diis_obj == "t1": + roa = dot(mf.mo_coeff[0][:, occa].T, ovlp, self._mo_orig[0]) + rob = dot(mf.mo_coeff[1][:, occb].T, ovlp, self._mo_orig[1]) + rva = dot(mf.mo_coeff[0][:, vira].T, ovlp, self._mo_orig[0]) + rvb = dot(mf.mo_coeff[1][:, virb].T, ovlp, self._mo_orig[1]) t1a = dot(roa.T, t1a, rva) t1b = dot(rob.T, t1b, rvb) - t1a, t1b = diis.update(np.asarry((t1a,t1b)), xerr=np.asarray((t1a,t1b))) - #t1b = diis.update(t1b, xerr=t1b) + t1a, t1b = diis.update(np.asarry((t1a, t1b)), xerr=np.asarray((t1a, t1b))) + # t1b = diis.update(t1b, xerr=t1b) ## Transform back t1a = dot(roa, t1a, rva.T) t1b = dot(rob, t1b, rvb.T) - mo_change_a = (1-self.damping)*np.dot(mf.mo_coeff[0][:,vira], t1a.T) - mo_change_b = (1-self.damping)*np.dot(mf.mo_coeff[1][:,virb], t1b.T) - self.log.debug("Change of alpha/beta occupied Brueckner orbitals= %.3e %.3e", - np.linalg.norm(mo_change_a), np.linalg.norm(mo_change_b)) - bmo_occ_a = (mf.mo_coeff[0][:,occa] + mo_change_a) - bmo_occ_b = (mf.mo_coeff[1][:,occb] + mo_change_b) + mo_change_a = (1 - self.damping) * np.dot(mf.mo_coeff[0][:, vira], t1a.T) + mo_change_b = (1 - self.damping) * np.dot(mf.mo_coeff[1][:, virb], t1b.T) + self.log.debug( + "Change of alpha/beta occupied Brueckner orbitals= %.3e %.3e", + np.linalg.norm(mo_change_a), + np.linalg.norm(mo_change_b), + ) + bmo_occ_a = mf.mo_coeff[0][:, occa] + mo_change_a + bmo_occ_b = mf.mo_coeff[1][:, occb] + mo_change_b # Orthogonalize occupied orbitals # If there was no AO-overlap matrix: bmo_occ = np.linalg.qr(bmo_occ)[0] @@ -110,32 +118,32 @@ def update_mo_coeff(self, mf, diis=None): dm_occ_b = np.dot(bmo_occ_b, bmo_occ_b.T) ea, va = scipy.linalg.eigh(dm_occ_a, b=ovlp, type=2) eb, vb = scipy.linalg.eigh(dm_occ_b, b=ovlp, type=2) - bmo_occ_a = va[:,-nocca:] - bmo_occ_b = vb[:,-noccb:] + bmo_occ_a = va[:, -nocca:] + bmo_occ_b = vb[:, -noccb:] # DIIS of occupied density - if diis and self.diis_obj == 'dm1': + if diis and self.diis_obj == "dm1": dm_occ_a = np.dot(bmo_occ_a, bmo_occ_a.T) dm_occ_b = np.dot(bmo_occ_b, bmo_occ_b.T) ra = np.dot(ovlp, self._mo_orig[0]) rb = np.dot(ovlp, self._mo_orig[1]) dm_occ_a = dot(ra.T, dm_occ_a, ra) dm_occ_b = dot(rb.T, dm_occ_b, rb) - #dm_occ_a = diis.update(dm_occ_a) - #dm_occ_b = diis.update(dm_occ_b) + # dm_occ_a = diis.update(dm_occ_a) + # dm_occ_b = diis.update(dm_occ_b) dm_occ_a, dm_occ_b = diis.update(np.asarray((dm_occ_a, dm_occ_b))) ea, va = np.linalg.eigh(dm_occ_a) eb, vb = np.linalg.eigh(dm_occ_b) - bmo_occ_a = np.dot(self._mo_orig[0], va)[:,-nocca:] - bmo_occ_b = np.dot(self._mo_orig[1], vb)[:,-noccb:] + bmo_occ_a = np.dot(self._mo_orig[0], va)[:, -nocca:] + bmo_occ_b = np.dot(self._mo_orig[1], vb)[:, -noccb:] # Virtual space - dm_vir_a = (np.linalg.inv(ovlp) - np.dot(bmo_occ_a, bmo_occ_a.T)) - dm_vir_b = (np.linalg.inv(ovlp) - np.dot(bmo_occ_b, bmo_occ_b.T)) + dm_vir_a = np.linalg.inv(ovlp) - np.dot(bmo_occ_a, bmo_occ_a.T) + dm_vir_b = np.linalg.inv(ovlp) - np.dot(bmo_occ_b, bmo_occ_b.T) ea, va = scipy.linalg.eigh(dm_vir_a, b=ovlp, type=2) eb, vb = scipy.linalg.eigh(dm_vir_b, b=ovlp, type=2) - bmo_vir_a = va[:,-nvira:] - bmo_vir_b = vb[:,-nvirb:] + bmo_vir_a = va[:, -nvira:] + bmo_vir_b = vb[:, -nvirb:] assert (bmo_occ_a.shape[-1] == nocca) and (bmo_vir_a.shape[-1] == nvira) assert (bmo_occ_b.shape[-1] == noccb) and (bmo_vir_b.shape[-1] == nvirb) diff --git a/vayesta/core/scmf/pdmet.py b/vayesta/core/scmf/pdmet.py index 590a6bcff..ed245a7c5 100644 --- a/vayesta/core/scmf/pdmet.py +++ b/vayesta/core/scmf/pdmet.py @@ -4,19 +4,18 @@ class PDMET_RHF(SCMF): - name = "p-DMET" - def __init__(self, *args, dm_type='default', **kwargs): + def __init__(self, *args, dm_type="default", **kwargs): super().__init__(*args, **kwargs) self.dm_type = dm_type.lower() def get_rdm1(self): """DM1 in MO basis.""" dm_type = self.dm_type - if dm_type.startswith('default'): + if dm_type.startswith("default"): dm1 = self.emb.make_rdm1() - elif dm_type.startswith('demo'): + elif dm_type.startswith("demo"): dm1 = self.emb.make_rdm1_demo() else: raise NotImplementedError("dm_type= %r" % dm_type) @@ -34,9 +33,9 @@ def update_mo_coeff(self, mf, diis=None): if diis is not None: dm1 = diis.update(dm1) mo_occ, rot = np.linalg.eigh(dm1) - mo_occ, rot = mo_occ[::-1], rot[:,::-1] + mo_occ, rot = mo_occ[::-1], rot[:, ::-1] nocc = np.count_nonzero(mf.mo_occ > 0) - if abs(mo_occ[nocc-1] - mo_occ[nocc]) < 1e-8: + if abs(mo_occ[nocc - 1] - mo_occ[nocc]) < 1e-8: self.log.critical("p-DMET MO occupation numbers (occupied):\n%s", mo_occ[:nocc]) self.log.critical("p-DMET MO occupation numbers (virtual):\n%s", mo_occ[nocc:]) raise RuntimeError("Degeneracy in MO occupation!") @@ -49,22 +48,21 @@ def update_mo_coeff(self, mf, diis=None): class PDMET_UHF(PDMET_RHF): - def get_rdm1(self): """DM1 in MO basis.""" dm_type = self.dm_type - if dm_type.startswith('default'): + if dm_type.startswith("default"): dm1 = self.emb.make_rdm1() - elif dm_type.startswith('demo'): + elif dm_type.startswith("demo"): dm1 = self.emb.make_rdm1_demo() else: raise NotImplementedError("dm_type= %r" % dm_type) # Check electron number - nelec_err = abs(np.trace(dm1[0]+dm1[1]) - self.emb.mol.nelectron) + nelec_err = abs(np.trace(dm1[0] + dm1[1]) - self.emb.mol.nelectron) if nelec_err > 1e-5: self.log.warning("Large electron error in 1DM= %.3e", nelec_err) # Check spin - spin_err = abs(np.trace(dm1[0]-dm1[1]) - self.emb.mol.spin) + spin_err = abs(np.trace(dm1[0] - dm1[1]) - self.emb.mol.spin) if spin_err > 1e-5: self.log.warning("Large spin error in 1DM= %.3e", spin_err) return dm1 @@ -83,8 +81,8 @@ def update_mo_coeff(self, mf, diis=None): dma, dmb = diis.update(np.asarray((dma, dmb))) mo_occ_a, rot_a = np.linalg.eigh(dma) mo_occ_b, rot_b = np.linalg.eigh(dmb) - mo_occ_a, rot_a = mo_occ_a[::-1], rot_a[:,::-1] - mo_occ_b, rot_b = mo_occ_b[::-1], rot_b[:,::-1] + mo_occ_a, rot_a = mo_occ_a[::-1], rot_a[:, ::-1] + mo_occ_b, rot_b = mo_occ_b[::-1], rot_b[:, ::-1] nocc_a = np.count_nonzero(mf.mo_occ[0] > 0) nocc_b = np.count_nonzero(mf.mo_occ[1] > 0) @@ -93,8 +91,8 @@ def log_occupation(logger): logger("p-DMET MO occupation numbers (beta-occupied):\n%s", mo_occ_b[:nocc_b]) logger("p-DMET MO occupation numbers (alpha-virtual):\n%s", mo_occ_a[nocc_a:]) logger("p-DMET MO occupation numbers (beta-virtual):\n%s", mo_occ_b[nocc_b:]) - if min(abs(mo_occ_a[nocc_a-1] - mo_occ_a[nocc_a]), - abs(mo_occ_b[nocc_b-1] - mo_occ_b[nocc_b])) < 1e-8: + + if min(abs(mo_occ_a[nocc_a - 1] - mo_occ_a[nocc_a]), abs(mo_occ_b[nocc_b - 1] - mo_occ_b[nocc_b])) < 1e-8: log_occupation(self.log.critical) raise RuntimeError("Degeneracy in MO occupation!") log_occupation(self.log.debugv) diff --git a/vayesta/core/scmf/scmf.py b/vayesta/core/scmf/scmf.py index 9c14fb68d..cd4ed357a 100644 --- a/vayesta/core/scmf/scmf.py +++ b/vayesta/core/scmf/scmf.py @@ -7,13 +7,12 @@ class SCMF: - name = "SCMF" def __init__(self, emb, etol=1e-8, dtol=1e-6, maxiter=100, damping=0.0, diis=True): self.emb = emb - self.etol = (etol if etol is not None else np.inf) - self.dtol = (dtol if dtol is not None else np.inf) + self.etol = etol if etol is not None else np.inf + self.dtol = dtol if dtol is not None else np.inf self.maxiter = maxiter self.damping = damping self.diis = diis @@ -24,7 +23,7 @@ def __init__(self, emb, etol=1e-8, dtol=1e-6, maxiter=100, damping=0.0, diis=Tru self._mo_orig = self.mf.mo_coeff # Output self.converged = False - self.energies = [] # Total energy per iteration + self.energies = [] # Total energy per iteration @property def log(self): @@ -57,32 +56,32 @@ def update_mo_coeff(self, mf, diis=None): raise AbstractMethodError() def check_convergence(self, e_tot, dm1, e_last=None, dm1_last=None, etol=None, dtol=None): - if etol is None: etol = self.etol - if dtol is None: dtol = self.dtol + if etol is None: + etol = self.etol + if dtol is None: + dtol = self.dtol if e_last is not None: - de = (e_tot - e_last) + de = e_tot - e_last # RHF: if self.emb.is_rhf: - ddm = abs(dm1-dm1_last).max() / 2 + ddm = abs(dm1 - dm1_last).max() / 2 else: - # UHF: - ddm = max(abs(dm1[0]-dm1_last[0]).max(), - abs(dm1[1]-dm1_last[1]).max()) + # UHF: + ddm = max(abs(dm1[0] - dm1_last[0]).max(), abs(dm1[1] - dm1_last[1]).max()) else: de = ddm = np.inf - tighten = (1-self.damping) - if (abs(de) < tighten*etol) and (ddm < tighten*dtol): + tighten = 1 - self.damping + if (abs(de) < tighten * etol) and (ddm < tighten * dtol): return True, de, ddm return False, de, ddm def kernel(self, *args, **kwargs): - diis = (self.get_diis() if self.diis else None) + diis = self.get_diis() if self.diis else None e_last = dm1_last = None - for self.iteration in range(1, self.maxiter+1): - + for self.iteration in range(1, self.maxiter + 1): self.log.info("%s iteration %3d", self.name, self.iteration) - self.log.info("%s==============", len(self.name)*"=") + self.log.info("%s==============", len(self.name) * "=") if self.iteration > 1: self.emb.reset() @@ -91,7 +90,7 @@ def kernel(self, *args, **kwargs): res = self.kernel_orig(*args, **kwargs) e_mf = self.mf.e_tot e_corr = self.emb.get_e_corr() - e_tot = (e_mf + e_corr) + e_tot = e_mf + e_corr self.energies.append(e_tot) # Update MF diff --git a/vayesta/core/screening/screening_crpa.py b/vayesta/core/screening/screening_crpa.py index 0de9fd65d..71490e5eb 100644 --- a/vayesta/core/screening/screening_crpa.py +++ b/vayesta/core/screening/screening_crpa.py @@ -14,6 +14,7 @@ class cRPAError(RuntimeError): pass + def get_frag_W(mf, fragment, pcoupling=True, only_ov_screened=False, log=None): """Generates screened coulomb interaction due to screening at the level of cRPA. Note that this currently scales as O(N_frag N^6), so is not practical without further refinement. @@ -81,7 +82,7 @@ def get_frag_deltaW(mf, fragment, pcoupling=True, only_ov_screened=False, log=No else: # Have a factor of -2 due to negative value of RPA dd response, and summation of # the excitation and deexcitation branches of the dd response. - static_fac = - 1.0 * (crpa.freqs_ss ** (-1)) + static_fac = -1.0 * (crpa.freqs_ss ** (-1)) delta_w = ( einsum("npq,n,nrs->pqrs", l_a, static_fac, l_a) + einsum("nqp,n,nsr->pqrs", l_a, static_fac, l_a), @@ -90,6 +91,7 @@ def get_frag_deltaW(mf, fragment, pcoupling=True, only_ov_screened=False, log=No ) return delta_w, crpa + def set_up_W_crpa(mf, fragment, pcoupling=True, only_ov_screened=False, log=None): is_rhf = np.ndim(mf.mo_coeff[1]) == 1 if not hasattr(mf, "with_df"): @@ -138,8 +140,8 @@ def set_up_W_crpa(mf, fragment, pcoupling=True, only_ov_screened=False, log=None # First, generate epsilon couplings between cluster and crpa spaces. eps_fb = [einsum("p,qp,rp->qr", e, l, nl) for e, l, nl in zip(eps, rot_loc, crpa.ov_rot)] # Then generate X and Y values for this correction. - x_crpa = [(p + m)/2 for p, m in zip(crpa.XpY_ss, crpa.XmY_ss)] - y_crpa = [(p - m)/2 for p, m in zip(crpa.XpY_ss, crpa.XmY_ss)] + x_crpa = [(p + m) / 2 for p, m in zip(crpa.XpY_ss, crpa.XmY_ss)] + y_crpa = [(p - m) / 2 for p, m in zip(crpa.XpY_ss, crpa.XmY_ss)] # Contract with epsilon values a_fb = [dot(e, x) for x, e in zip(x_crpa, eps_fb)] b_fb = [dot(e, y) for y, e in zip(y_crpa, eps_fb)] @@ -149,28 +151,27 @@ def set_up_W_crpa(mf, fragment, pcoupling=True, only_ov_screened=False, log=None nv = fragment.cluster.nvir_active if isinstance(nv, int): nv = (nv, nv) - l_a[:, :no[0], no[0]:] += a_fb[0].T.reshape((a_fb[0].shape[-1], no[0], nv[0])) - l_b[:, :no[1], no[1]:] += a_fb[1].T.reshape((a_fb[1].shape[-1], no[1], nv[1])) + l_a[:, : no[0], no[0] :] += a_fb[0].T.reshape((a_fb[0].shape[-1], no[0], nv[0])) + l_b[:, : no[1], no[1] :] += a_fb[1].T.reshape((a_fb[1].shape[-1], no[1], nv[1])) - l_a[:, no[0]:, :no[0]] += b_fb[0].T.reshape((b_fb[0].shape[-1], no[0], nv[0])).transpose(0,2,1) - l_b[:, no[1]:, :no[1]] += b_fb[1].T.reshape((b_fb[1].shape[-1], no[1], nv[1])).transpose(0,2,1) + l_a[:, no[0] :, : no[0]] += b_fb[0].T.reshape((b_fb[0].shape[-1], no[0], nv[0])).transpose(0, 2, 1) + l_b[:, no[1] :, : no[1]] += b_fb[1].T.reshape((b_fb[1].shape[-1], no[1], nv[1])).transpose(0, 2, 1) if only_ov_screened: # Zero out all contributions screening oo or vv contributions. no = fragment.cluster.nocc_active if isinstance(no, int): no = (no, no) - l_a[:, no[0]:, no[0]:] = 0.0 - l_a[:, :no[0], :no[0]] = 0.0 - l_b[:, no[1]:, no[1]:] = 0.0 - l_b[:, :no[1], :no[1]] = 0.0 + l_a[:, no[0] :, no[0] :] = 0.0 + l_a[:, : no[0], : no[0]] = 0.0 + l_b[:, no[1] :, no[1] :] = 0.0 + l_b[:, : no[1], : no[1]] = 0.0 return l_a, l_b, crpa def get_crpa(orig_mf, f, log): - def construct_loc_rot(f): - """Constructs the rotation of the overall mean-field space into which """ + """Constructs the rotation of the overall mean-field space into which""" ro = f.get_overlap("cluster[occ]|mo[occ]") rv = f.get_overlap("cluster[vir]|mo[vir]") @@ -198,17 +199,16 @@ def construct_loc_rot(f): def ao2mo(mf, mo_coeff=None, ijslice=None): - """Get MO basis density-fitted integrals. - """ + """Get MO basis density-fitted integrals.""" if mo_coeff is None: mo_coeff = mf.mo_coeff nmo = mo_coeff.shape[1] naux = mf.with_df.get_naoaux() - mem_incore = (2 * nmo ** 2 * naux) * 8 / 1e6 + mem_incore = (2 * nmo**2 * naux) * 8 / 1e6 mem_now = lib.current_memory()[0] - mo = np.asarray(mo_coeff, order='F') + mo = np.asarray(mo_coeff, order="F") if ijslice is None: ijslice = (0, nmo, 0, nmo) @@ -216,8 +216,8 @@ def ao2mo(mf, mo_coeff=None, ijslice=None): Lpq = None if (mem_incore + mem_now < 0.99 * mf.max_memory) or mf.mol.incore_anyway: - Lpq = _ao2mo.nr_e2(mf.with_df._cderi, mo, ijslice, aosym='s2', out=Lpq) + Lpq = _ao2mo.nr_e2(mf.with_df._cderi, mo, ijslice, aosym="s2", out=Lpq) return Lpq.reshape(*finshape) else: - logger.warn(mf, 'Memory may not be enough!') + logger.warn(mf, "Memory may not be enough!") raise NotImplementedError diff --git a/vayesta/core/screening/screening_moment.py b/vayesta/core/screening/screening_moment.py index 7d6ce7a61..de1944a2e 100644 --- a/vayesta/core/screening/screening_moment.py +++ b/vayesta/core/screening/screening_moment.py @@ -44,20 +44,21 @@ def build_screened_eris(emb, fragments=None, cderi_ov=None, store_m0=True, npoin # --- Setup if fragments is None: fragments = emb.get_fragments(active=True, sym_parent=None, mpi_rank=mpi.rank) - fragments = [f for f in fragments if f.opts.screening == 'mrpa'] + fragments = [f for f in fragments if f.opts.screening == "mrpa"] if emb.df is None: raise NotImplementedError("Screened interactions require density-fitting.") - r_occs = [f.get_overlap('mo[occ]|cluster[occ]') for f in fragments] - r_virs = [f.get_overlap('mo[vir]|cluster[vir]') for f in fragments] + r_occs = [f.get_overlap("mo[occ]|cluster[occ]") for f in fragments] + r_virs = [f.get_overlap("mo[vir]|cluster[vir]") for f in fragments] target_rots, ovs_active = _get_target_rot(r_occs, r_virs) local_moments = calc_moms_RIRPA(emb.mf, target_rots, ovs_active, log, cderi_ov, npoints) # Could generate moments using N^6 moments instead, but just for debugging. - #local_moments, erpa = calc_moms_RPA(emb.mf, target_rots, ovs_active, log, cderi_ov, calc_e, npoints) + # local_moments, erpa = calc_moms_RPA(emb.mf, target_rots, ovs_active, log, cderi_ov, calc_e, npoints) # Then construct the RPA coupling matrix A-B, given by the diagonal matrix of energy differences. no = np.array(sum(emb.mf.mo_occ.T > 0)) - if no.size == 1: no = np.array([int(no), int(no)]) + if no.size == 1: + no = np.array([int(no), int(no)]) norb = emb.mo_coeff[0].shape[0] nv = norb - no @@ -72,8 +73,8 @@ def get_eps_singlespin(no_, nv_, mo_energy): eps = (eps.T - mo_energy[:no_]).T eps = eps.reshape(-1) return eps - eps = np.concatenate([get_eps_singlespin(no[0], nv[0], mo_e[0]), - get_eps_singlespin(no[1], nv[1], mo_e[1])]) + + eps = np.concatenate([get_eps_singlespin(no[0], nv[0], mo_e[0]), get_eps_singlespin(no[1], nv[1], mo_e[1])]) # And use this to perform inversion to calculate interaction in cluster. seris_ov = [] @@ -85,7 +86,7 @@ def get_eps_singlespin(no_, nv_, mo_energy): if min(e) < 1e-4: log.warning("Small eigenvalue of local rpa moment in %s: %e", f.name, min(e)) - mominv = einsum("pn,n,qn->pq", c, e**(-1), c) + mominv = einsum("pn,n,qn->pq", c, e ** (-1), c) apb = dot(mominv, amb, mominv) # This is the renormalised coulomb kernel in the cluster. @@ -125,7 +126,7 @@ def calc_moms_RIRPA(mf, target_rots, ovs_active, log, cderi_ov, npoints): for nov, rot in zip(ovs_active, target_rots): # Computation costs O(N^2 N_clus^2) # Get corresponding section of overall moment, then project to just local contribution. - mom = dot(momzero_interact[n:n+sum(nov)], rot.T) + mom = dot(momzero_interact[n : n + sum(nov)], rot.T) # This isn't exactly symmetric due to numerical integration, so enforce here. mom = (mom + mom.T) / 2 local_moments += [mom] @@ -133,6 +134,7 @@ def calc_moms_RIRPA(mf, target_rots, ovs_active, log, cderi_ov, npoints): return local_moments + def calc_moms_RPA(mf, target_rots, ovs_active, log, cderi_ov, calc_e, npoints): rpa = ssRPA(mf, log=log) erpa = rpa.kernel() @@ -142,6 +144,7 @@ def calc_moms_RPA(mf, target_rots, ovs_active, log, cderi_ov, calc_e, npoints): local_moments += [dot(rot, mom0, rot.T)] return local_moments, erpa + def get_screened_eris_full(eris, seris_ov, copy=True, log=None): """Build full array of screened ERIs, given the bare ERIs and screening.""" @@ -152,22 +155,24 @@ def replace_ov(full, ov, spins): no1, no2 = ov.shape[0], ov.shape[2] o1, v1 = np.s_[:no1], np.s_[no1:] o2, v2 = np.s_[:no2], np.s_[no2:] - out[o1,v1,o2,v2] = ov - out[v1,o1,o2,v2] = ov.transpose([1, 0, 2, 3]) - out[o1,v1,v2,o2] = ov.transpose([0, 1, 3, 2]) - out[v1,o1,v2,o2] = ov.transpose([1, 0, 3, 2]) + out[o1, v1, o2, v2] = ov + out[v1, o1, o2, v2] = ov.transpose([1, 0, 2, 3]) + out[o1, v1, v2, o2] = ov.transpose([0, 1, 3, 2]) + out[v1, o1, v2, o2] = ov.transpose([1, 0, 3, 2]) return out if isinstance(eris, np.ndarray): eris = (eris, eris, eris) - seris = (replace_ov(eris[0], seris_ov[0], 'aa'), - replace_ov(eris[1], seris_ov[1], 'ab'), - replace_ov(eris[2], seris_ov[2], 'bb')) + seris = ( + replace_ov(eris[0], seris_ov[0], "aa"), + replace_ov(eris[1], seris_ov[1], "ab"), + replace_ov(eris[2], seris_ov[2], "bb"), + ) return seris -def get_screened_eris_ccsd(eris, seris_ov, add_restore_bare=True, log=None): +def get_screened_eris_ccsd(eris, seris_ov, add_restore_bare=True, log=None): if add_restore_bare: gaa = eris.ovov[:] gab = eris.ovOV[:] @@ -176,20 +181,22 @@ def get_screened_eris_ccsd(eris, seris_ov, add_restore_bare=True, log=None): saa, sab, sbb = seris_ov # Alpha-alpha eris.ovov = saa - eris.ovvo = saa.transpose([0,1,3,2]) + eris.ovvo = saa.transpose([0, 1, 3, 2]) # Alpha-beta eris.ovOV = sab - eris.ovVO = sab.transpose([0,1,3,2]) + eris.ovVO = sab.transpose([0, 1, 3, 2]) # Beta-beta eris.OVOV = sbb - eris.OVVO = sbb.transpose([0,1,3,2]) + eris.OVVO = sbb.transpose([0, 1, 3, 2]) # Beta-alpha - eris.OVvo = sab.transpose([2,3,1,0]) + eris.OVvo = sab.transpose([2, 3, 1, 0]) # Add restore_bare function to remove screening later on if add_restore_bare: + def get_bare(eris): return (gaa, gab, gbb) + def restore_bare(eris): eris = get_screened_eris_ccsd(eris, eris.get_bare(), add_restore_bare=False) del eris.get_bare, eris.restore_bare @@ -200,6 +207,7 @@ def restore_bare(eris): return eris + def _get_target_rot(r_active_occs, r_active_virs): """Given the definitions of our cluster spaces in terms of rotations of the occupied and virtual orbitals, define the equivalent rotation of the full-system particle-hole excitation space. @@ -240,13 +248,12 @@ def get_target_rot_spat(ro, rv): return rot, ov_active nfrag = len(r_active_occs) - assert(nfrag == len(r_active_virs)) + assert nfrag == len(r_active_virs) ovs_active = np.full((nfrag, 2), fill_value=0) target_rots = [] for i, (r_o, r_v) in enumerate(zip(r_active_occs, r_active_virs)): - if isinstance(r_o, np.ndarray): r_o = (r_o, r_o) r_v = (r_v, r_v) diff --git a/vayesta/core/spinalg.py b/vayesta/core/spinalg.py index 78adc624b..67c81cee4 100644 --- a/vayesta/core/spinalg.py +++ b/vayesta/core/spinalg.py @@ -4,7 +4,7 @@ import numpy as np from vayesta.core import util -__all__ = ['add_numbers', 'hstack_matrices'] +__all__ = ["add_numbers", "hstack_matrices"] def add_numbers(*args): @@ -13,23 +13,23 @@ def add_numbers(*args): return sum(args) # UHF if not np.any([np.isscalar(arg) for arg in args]): - return (sum([arg[0] for arg in args]), - sum([arg[1] for arg in args])) + return (sum([arg[0] for arg in args]), sum([arg[1] for arg in args])) raise ValueError + def hstack_matrices(*args, ignore_none=True): if ignore_none: args = [x for x in args if x is not None] - ndims = np.asarray([(arg[0].ndim+1) for arg in args]) + ndims = np.asarray([(arg[0].ndim + 1) for arg in args]) # RHF if np.all(ndims == 2): return util.hstack(*args) # UHF if np.all(ndims == 3): - return (util.hstack(*[arg[0] for arg in args]), - util.hstack(*[arg[1] for arg in args])) + return (util.hstack(*[arg[0] for arg in args]), util.hstack(*[arg[1] for arg in args])) raise ValueError("ndims= %r" % ndims) + def dot(*args, out=None): """Generalizes dot with or without spin channel: ij,jk->ik or Sij,Sjk->Sik @@ -41,13 +41,14 @@ def dot(*args, out=None): if maxdim == 2: return util.dot(*args, out=out) # Spin-dependent arguments present - assert (maxdim == 3) + assert maxdim == 3 if out is None: - out = 2*[None] + out = 2 * [None] args_a = [(x if np.ndim(x[0]) < 2 else x[0]) for x in args] args_b = [(x if np.ndim(x[1]) < 2 else x[1]) for x in args] return (util.dot(*args_a, out=out[0]), util.dot(*args_b, out=out[1])) + def eigh(a, b=None, *args, **kwargs): ndim = np.ndim(a[0]) + 1 # RHF @@ -56,27 +57,29 @@ def eigh(a, b=None, *args, **kwargs): # UHF if b is None or np.ndim(b[0]) == 1: b = (b, b) - results = (scipy.linalg.eigh(a[0], b=b[0], *args, **kwargs), - scipy.linalg.eigh(a[1], b=b[1], *args, **kwargs)) + results = (scipy.linalg.eigh(a[0], b=b[0], *args, **kwargs), scipy.linalg.eigh(a[1], b=b[1], *args, **kwargs)) return tuple(zip(*results)) + def transpose(a, axes=None): if np.ndim(a[0]) == 1: return np.transpose(a, axes=axes) return (transpose(a[0], axes=axes), transpose(a[1], axes=axes)) + T = transpose + def _guess_spinsym(a): if isinstance(a, (tuple, list)): - return 'unrestricted' - return 'restricted' + return "unrestricted" + return "restricted" -def _make_func(func, nargs=1): +def _make_func(func, nargs=1): def _func(a, *args, spinsym=None, **kwargs): spinsym = spinsym or _guess_spinsym(a) - if spinsym == 'restricted': + if spinsym == "restricted": return func(a, *args, **kwargs) if nargs == 1: return tuple(func(a[i], *args, **kwargs) for i in range(len(a))) @@ -89,6 +92,7 @@ def _func(a, *args, spinsym=None, **kwargs): b, c, args = args[0], args[1], args[2:] return tuple(func(a[i], b[i], c[i], *args, **kwargs) for i in range(len(a))) raise NotImplementedError + return _func diff --git a/vayesta/core/symmetry/group.py b/vayesta/core/symmetry/group.py index f4f8d444c..d0d1a1dc5 100644 --- a/vayesta/core/symmetry/group.py +++ b/vayesta/core/symmetry/group.py @@ -4,6 +4,7 @@ log = logging.getLogger(__name__) + class SymmetryGroup: """Detect symmetry group automatically (use spglib?).""" @@ -24,7 +25,7 @@ def nao(self): @property def dimension(self): - return getattr(self.mol, 'dimension', 0) + return getattr(self.mol, "dimension", 0) def compare_atoms(self, atom1, atom2, check_basis=None, check_label=None): """Compare atom symbol and (optionally) basis between atom1 and atom2.""" @@ -38,23 +39,27 @@ def compare_atoms(self, atom1, atom2, check_basis=None, check_label=None): else: type1 = self.mol.atom_pure_symbol(atom1) type2 = self.mol.atom_pure_symbol(atom2) - if (type1 != type2): + if type1 != type2: return False if not check_basis: return True bas1 = self.mol._basis[self.mol.atom_symbol(atom1)] bas2 = self.mol._basis[self.mol.atom_symbol(atom2)] - return (bas1 == bas2) + return bas1 == bas2 def get_closest_atom(self, coords): """pos in internal coordinates.""" - dists = np.linalg.norm(self.mol.atom_coords()-coords, axis=1) + dists = np.linalg.norm(self.mol.atom_coords() - coords, axis=1) idx = np.argmin(dists) return idx, dists[idx] - def add_rotation(self, order, axis, center, unit='ang'): - log.critical(("The specification of rotational symmetry between fragments has changed." - " Check examples/ewf/73-rotational-symmetry.py for the new syntax.")) + def add_rotation(self, order, axis, center, unit="ang"): + log.critical( + ( + "The specification of rotational symmetry between fragments has changed." + " Check examples/ewf/73-rotational-symmetry.py for the new syntax." + ) + ) raise NotImplementedError def set_translations(self, nimages): diff --git a/vayesta/core/symmetry/operation.py b/vayesta/core/symmetry/operation.py index f3d0527fb..ad76e5481 100644 --- a/vayesta/core/symmetry/operation.py +++ b/vayesta/core/symmetry/operation.py @@ -12,8 +12,8 @@ BOHR = 0.529177210903 -class SymmetryOperation: +class SymmetryOperation: def __init__(self, group): self.group = group log.debugv("Creating %s", self) @@ -41,7 +41,7 @@ def call_wrapper(self, a, *args, axis=0, **kwargs): """Common pre- and post-processing for all symmetries. Symmetry specific processing is performed in call_kernel.""" - if hasattr(axis, '__len__'): + if hasattr(axis, "__len__"): for ax in axis: a = self(a, *args, axis=ax, **kwargs) return a @@ -80,8 +80,12 @@ def assign(): r1 = self.apply_to_point(r0) atom1, dist = self.group.get_closest_atom(r1) if dist > self.xtol: - log.error("No symmetry related atom found for atom %d. Closest atom is %d with distance %.3e a.u.", - atom0, atom1, dist) + log.error( + "No symmetry related atom found for atom %d. Closest atom is %d with distance %.3e a.u.", + atom0, + atom1, + dist, + ) success = False elif not self.group.compare_atoms(atom0, atom1): log.error("Atom %d is not symmetry related to atom %d.", atom1, atom0) @@ -95,23 +99,23 @@ def assign(): if not assign(): return None, None - assert (not np.any(reorder == -1)) - assert (not np.any(inverse == -1)) + assert not np.any(reorder == -1) + assert not np.any(inverse == -1) assert np.all(np.arange(self.natom)[reorder][inverse] == np.arange(self.natom)) return reorder, inverse def get_ao_reorder(self, atom_reorder): if atom_reorder is None: return None, None - aoslice = self.mol.aoslice_by_atom()[:,2:] + aoslice = self.mol.aoslice_by_atom()[:, 2:] reorder = np.full((self.mol.nao,), -1) inverse = np.full((self.mol.nao,), -1) for atom0 in range(self.natom): atom1 = atom_reorder[atom0] - aos0 = list(range(aoslice[atom0,0], aoslice[atom0,1])) - aos1 = list(range(aoslice[atom1,0], aoslice[atom1,1])) - reorder[aos0[0]:aos0[-1]+1] = aos1 - inverse[aos1[0]:aos1[-1]+1] = aos0 + aos0 = list(range(aoslice[atom0, 0], aoslice[atom0, 1])) + aos1 = list(range(aoslice[atom1, 0], aoslice[atom1, 1])) + reorder[aos0[0] : aos0[-1] + 1] = aos1 + inverse[aos1[0] : aos1[-1] + 1] = aos0 assert not np.any(reorder == -1) assert not np.any(inverse == -1) assert np.all(np.arange(self.nao)[reorder][inverse] == np.arange(self.nao)) @@ -133,16 +137,15 @@ def rotate_angular_orbitals(self, a, rotmats): # It is possible that multiple shells are contained in a single 'bas'! nl = rot.shape[0] - assert (size % nl == 0) + assert size % nl == 0 for shell0 in range(0, size, nl): - shell = np.s_[ao_start+shell0:ao_start+shell0+nl] - b[shell] = einsum('x...,xy->y...', a[shell], rot) + shell = np.s_[ao_start + shell0 : ao_start + shell0 + nl] + b[shell] = einsum("x...,xy->y...", a[shell], rot) ao_start = ao_end return b class SymmetryIdentity(SymmetryOperation): - def __repr__(self): return "Identity" @@ -158,8 +161,7 @@ def get_atom_reorder(self): class SymmetryInversion(SymmetryOperation): - - def __init__(self, group, center=(0,0,0)): + def __init__(self, group, center=(0, 0, 0)): center = np.asarray(center, dtype=float) self.center = center @@ -174,20 +176,19 @@ def __repr__(self): return "Inversion(%g,%g,%g)" % tuple(self.center) def apply_to_point(self, r0): - return 2*self.center - r0 + return 2 * self.center - r0 def call_kernel(self, a): - rotmats = [(-1)**i * np.eye(n) for (i, n) in enumerate(range(1,19,2))] + rotmats = [(-1) ** i * np.eye(n) for (i, n) in enumerate(range(1, 19, 2))] a = self.rotate_angular_orbitals(a, rotmats) return a class SymmetryReflection(SymmetryOperation): - - def __init__(self, group, axis, center=(0,0,0)): + def __init__(self, group, axis, center=(0, 0, 0)): center = np.asarray(center, dtype=float) self.center = center - self.axis = np.asarray(axis)/np.linalg.norm(axis) + self.axis = np.asarray(axis) / np.linalg.norm(axis) super().__init__(group) self.atom_reorder = self.get_atom_reorder()[0] @@ -197,24 +198,24 @@ def __init__(self, group, axis, center=(0,0,0)): # A reflection can be decomposed into a C2-rotation + inversion # We use this to derive the angular transformation matrix: - rot = scipy.spatial.transform.Rotation.from_rotvec(self.axis*np.pi).as_matrix() + rot = scipy.spatial.transform.Rotation.from_rotvec(self.axis * np.pi).as_matrix() try: angular_rotmats = pyscf.symm.basis._momentum_rotation_matrices(self.mol, rot) except AttributeError: angular_rotmats = pyscf.symm.basis._ao_rotation_matrices(self.mol, rot) # Inversion of p,f,h,... shells: - self.angular_rotmats =[(-1)**i * x for (i, x) in enumerate(angular_rotmats)] + self.angular_rotmats = [(-1) ** i * x for (i, x) in enumerate(angular_rotmats)] def __repr__(self): return "Reflection(%g,%g,%g)" % tuple(self.axis) def as_matrix(self): """Householder matrix. Does not account for shifted origin!""" - return np.eye(3) - 2*np.outer(self.axis, self.axis) + return np.eye(3) - 2 * np.outer(self.axis, self.axis) def apply_to_point(self, r0): """Householder transformation.""" - r1 = r0 - 2*np.dot(np.outer(self.axis, self.axis), r0-self.center) + r1 = r0 - 2 * np.dot(np.outer(self.axis, self.axis), r0 - self.center) return r1 def call_kernel(self, a): @@ -223,8 +224,7 @@ def call_kernel(self, a): class SymmetryRotation(SymmetryOperation): - - def __init__(self, group, rotvec, center=(0,0,0)): + def __init__(self, group, rotvec, center=(0, 0, 0)): self.rotvec = np.asarray(rotvec, dtype=float) self.center = np.asarray(center, dtype=float) super().__init__(group) @@ -254,17 +254,16 @@ def call_kernel(self, a): class SymmetryTranslation(SymmetryOperation): - def __init__(self, group, vector, boundary=None, atom_reorder=None, ao_reorder=None): self.vector = np.asarray(vector, dtype=float) super().__init__(group) if boundary is None: - boundary = getattr(self.mol, 'boundary', 'PBC') + boundary = getattr(self.mol, "boundary", "PBC") if np.ndim(boundary) == 0: - boundary = 3*[boundary] + boundary = 3 * [boundary] elif np.ndim(boundary) == 1 and len(boundary) == 2: - boundary = [boundary[0], boundary[1], 'PBC'] + boundary = [boundary[0], boundary[1], "PBC"] self.boundary = boundary # Atom reorder @@ -273,12 +272,12 @@ def __init__(self, group, vector, boundary=None, atom_reorder=None, ao_reorder=N if atom_reorder is None: atom_reorder, _, self.atom_reorder_phases = self.get_atom_reorder() self.atom_reorder = atom_reorder - assert (self.atom_reorder is not None) + assert self.atom_reorder is not None # AO reorder if ao_reorder is None: ao_reorder, _, self.ao_reorder_phases = self.get_ao_reorder() self.ao_reorder = ao_reorder - assert (self.ao_reorder is not None) + assert self.ao_reorder is not None def __repr__(self): return "Translation(%f,%f,%f)" % tuple(self.vector) @@ -286,7 +285,7 @@ def __repr__(self): def call_kernel(self, a): if self.ao_reorder_phases is None: return a - return a * self.ao_reorder_phases[tuple([np.s_[:]] + (a.ndim-1)*[None])] + return a * self.ao_reorder_phases[tuple([np.s_[:]] + (a.ndim - 1) * [None])] def inverse(self): return type(self)(self.mol, -self.vector, boundary=self.boundary, atom_reorder=np.argsort(self.atom_reorder)) @@ -301,7 +300,7 @@ def inv_lattice_vectors(self): @property def boundary_phases(self): - return np.asarray([1 if (b.lower() == 'pbc') else -1 for b in self.boundary]) + return np.asarray([1 if (b.lower() == "pbc") else -1 for b in self.boundary]) @property def vector_xyz(self): @@ -332,14 +331,16 @@ def get_atom_reorder(self): def get_atom_at(pos): """pos in internal coordinates.""" - for dx, dy, dz in itertools.product([0,-1,1], repeat=3): - if self.group.dimension in (1, 2) and (dz != 0): continue - if self.group.dimension == 1 and (dy != 0): continue + for dx, dy, dz in itertools.product([0, -1, 1], repeat=3): + if self.group.dimension in (1, 2) and (dz != 0): + continue + if self.group.dimension == 1 and (dy != 0): + continue dr = np.asarray([dx, dy, dz]) - phase = np.product(self.boundary_phases[dr!=0]) + phase = np.product(self.boundary_phases[dr != 0]) dists = np.linalg.norm(atom_coords_abc + dr - pos, axis=1) idx = np.argmin(dists) - if (dists[idx] < self.xtol): + if dists[idx] < self.xtol: return idx, phase return None, None @@ -355,9 +356,9 @@ def get_atom_at(pos): reorder[atom1] = atom0 inverse[atom0] = atom1 phases[atom0] = phase - assert (not np.any(reorder == -1)) - assert (not np.any(inverse == -1)) - assert (not np.any(phases == 0)) + assert not np.any(reorder == -1) + assert not np.any(inverse == -1) + assert not np.any(phases == 0) assert np.all(np.arange(self.natom)[reorder][inverse] == np.arange(self.natom)) return reorder, inverse, phases @@ -368,7 +369,7 @@ def get_ao_reorder(self, atom_reorder=None, atom_reorder_phases=None): atom_reorder_phases = self.atom_reorder_phases if atom_reorder is None: return None, None, None - aoslice = self.mol.aoslice_by_atom()[:,2:] + aoslice = self.mol.aoslice_by_atom()[:, 2:] reorder = np.full((self.mol.nao,), -1) inverse = np.full((self.mol.nao,), -1) if atom_reorder_phases is not None: @@ -377,12 +378,12 @@ def get_ao_reorder(self, atom_reorder=None, atom_reorder_phases=None): phases = None for atom0 in range(self.natom): atom1 = atom_reorder[atom0] - aos0 = list(range(aoslice[atom0,0], aoslice[atom0,1])) - aos1 = list(range(aoslice[atom1,0], aoslice[atom1,1])) - reorder[aos0[0]:aos0[-1]+1] = aos1 - inverse[aos1[0]:aos1[-1]+1] = aos0 + aos0 = list(range(aoslice[atom0, 0], aoslice[atom0, 1])) + aos1 = list(range(aoslice[atom1, 0], aoslice[atom1, 1])) + reorder[aos0[0] : aos0[-1] + 1] = aos1 + inverse[aos1[0] : aos1[-1] + 1] = aos0 if atom_reorder_phases is not None: - phases[aos0[0]:aos0[-1]+1] = atom_reorder_phases[atom1] + phases[aos0[0] : aos0[-1] + 1] = atom_reorder_phases[atom1] assert not np.any(reorder == -1) assert not np.any(inverse == -1) if atom_reorder_phases is not None: diff --git a/vayesta/core/symmetry/symmetry.py b/vayesta/core/symmetry/symmetry.py index 90ede893b..154e4f18d 100644 --- a/vayesta/core/symmetry/symmetry.py +++ b/vayesta/core/symmetry/symmetry.py @@ -11,31 +11,32 @@ def unit_vector(vector): """Returns the unit vector of the vector.""" return vector / np.linalg.norm(vector) + def angle_between(v1, v2): """Returns the angle in radians between vectors 'v1' and 'v2':: - >>> angle_between((1, 0, 0), (0, 1, 0)) - 1.5707963267948966 - >>> angle_between((1, 0, 0), (1, 0, 0)) - 0.0 - >>> angle_between((1, 0, 0), (-1, 0, 0)) - 3.141592653589793 + >>> angle_between((1, 0, 0), (0, 1, 0)) + 1.5707963267948966 + >>> angle_between((1, 0, 0), (1, 0, 0)) + 0.0 + >>> angle_between((1, 0, 0), (-1, 0, 0)) + 3.141592653589793 """ u1 = unit_vector(v1) u2 = unit_vector(v2) return np.arccos(np.clip(np.dot(u1, u2), -1.0, 1.0)) -class Symmetry: +class Symmetry: def __init__(self, mf, log=log): self.mf = mf self.nsubcells = None self.log = log if self.has_pbc: - #self.nsubcells = self.find_subcells(respect_dm1=self.mf.make_rdm1()) + # self.nsubcells = self.find_subcells(respect_dm1=self.mf.make_rdm1()) # MF is FoldedSCF - if hasattr(mf, 'nsubcells'): + if hasattr(mf, "nsubcells"): self.nsubcells = mf.nsubcells self.log.info("Found %d x %d x %d primitive subcells in unit cell", *self.nsubcells) @@ -45,7 +46,7 @@ def mol(self): @property def cell(self): - if (self.pbcndims == 0): + if self.pbcndims == 0: raise AttributeError return self.mol @@ -55,28 +56,28 @@ def natom(self): @property def natom_unique(self): - mf = getattr(self.mf, 'kmf', self.mf) + mf = getattr(self.mf, "kmf", self.mf) return mf.mol.natm - def get_unique_atoms(self): #, r_tol=1e-5): + def get_unique_atoms(self): # , r_tol=1e-5): return list(range(self.natom_unique)) - #if self.nsubcells is None: + # if self.nsubcells is None: # return list(range(self.natom)) - #avecs = self.primitive_lattice_vectors() - #bvecs = np.linalg.inv(avecs.T) - #atoms = [] - #for atom, coords in enumerate(self.atom_coords()): + # avecs = self.primitive_lattice_vectors() + # bvecs = np.linalg.inv(avecs.T) + # atoms = [] + # for atom, coords in enumerate(self.atom_coords()): # coords = np.dot(coords, bvecs.T) # if np.any(coords >= (1.0-r_tol)): # continue # atoms.append(atom) - #return atoms + # return atoms def primitive_lattice_vectors(self): - return self.lattice_vectors() / self.nsubcells[:,None] + return self.lattice_vectors() / self.nsubcells[:, None] - def atom_coords(self, unit='Bohr'): + def atom_coords(self, unit="Bohr"): return self.mol.atom_coords(unit=unit) def lattice_vectors(self): @@ -94,7 +95,7 @@ def pbcndims(self): @property def has_pbc(self): - return (self.pbcndims > 0) + return self.pbcndims > 0 def compare_atoms(self, atom1, atom2, respect_labels=False, respect_basis=True): """Compare atom symbol and (optionally) basis between atom1 and atom2.""" @@ -116,7 +117,6 @@ def compare_atoms(self, atom1, atom2, respect_labels=False, respect_basis=True): return False return True - def find_subcells(self, respect_basis=True, respect_labels=False, respect_dm1=None, r_tol=1e-5, dm1_tol=1e-6): """Find subcells within cell, with unit cell vectors parallel to the supercell. @@ -151,13 +151,12 @@ def find_subcells(self, respect_basis=True, respect_labels=False, respect_dm1=No else: dm1s = respect_dm1 - coords_internal = np.einsum('ar,br->ab', coords, bvecs) + coords_internal = np.einsum("ar,br->ab", coords, bvecs) - checked = [] # Keep track of vectors which were already checked, to improve performance + checked = [] # Keep track of vectors which were already checked, to improve performance dvecs = [[] for i in range(3)] for atm1 in range(self.natom): - for atm2 in range(atm1+1, self.natom): - + for atm2 in range(atm1 + 1, self.natom): # 1) Compare atom symbol and basis equal = self.compare_atoms(atm1, atm2, respect_basis=respect_basis, respect_labels=False) if not equal: @@ -165,7 +164,7 @@ def find_subcells(self, respect_basis=True, respect_labels=False, respect_dm1=No pos1 = coords_internal[atm1] pos2 = coords_internal[atm2] - dvec = (pos2 - pos1) + dvec = pos2 - pos1 # 3) Check parallel to one and only lattice vector dim = np.argwhere(abs(dvec) > r_tol) @@ -175,8 +174,8 @@ def find_subcells(self, respect_basis=True, respect_labels=False, respect_dm1=No # Check if vector has been checked before if checked: - diff = (dvec[None] - np.asarray(checked)) - checked_before = np.allclose(np.amin(abs(diff), axis=0), 0) + diff = dvec[None] - np.asarray(checked) + checked_before = np.allclose(np.amin(abs(diff), axis=0), 0) if checked_before: continue checked.append(dvec) @@ -185,7 +184,7 @@ def find_subcells(self, respect_basis=True, respect_labels=False, respect_dm1=No if dvecs[dim]: found = False for i in range(1, 100): - diff = np.linalg.norm(dvec[None]/i - dvecs[dim], axis=1) + diff = np.linalg.norm(dvec[None] / i - dvecs[dim], axis=1) if np.any(diff < r_tol): found = True break @@ -194,7 +193,7 @@ def find_subcells(self, respect_basis=True, respect_labels=False, respect_dm1=No # 5) Check if dvec is valid symmetry for all atoms: tvec = np.dot(dvec, avecs) - reorder, _, phases = tsymmetry.reorder_atoms(self.cell, tvec, unit='Bohr') + reorder, _, phases = tsymmetry.reorder_atoms(self.cell, tvec, unit="Bohr") if reorder is None: continue @@ -204,7 +203,7 @@ def find_subcells(self, respect_basis=True, respect_labels=False, respect_dm1=No assert np.allclose(phases, 1) dmsym = True for dm1 in dm1s: - dm1t = dm1[reorder][:,reorder] + dm1t = dm1[reorder][:, reorder] if not np.allclose(dm1, dm1t, rtol=0, atol=dm1_tol): dmsym = False break @@ -216,14 +215,13 @@ def find_subcells(self, respect_basis=True, respect_labels=False, respect_dm1=No # Check that not more than a single vector was found for each dimension assert np.all([(len(dvecs[d]) <= 1) for d in range(3)]) - nsubcells = [(1/(dvecs[d][0][d]) if dvecs[d] else 1) for d in range(3)] + nsubcells = [(1 / (dvecs[d][0][d]) if dvecs[d] else 1) for d in range(3)] assert np.allclose(np.rint(nsubcells), nsubcells) nsubcells = np.rint(nsubcells).astype(int) return nsubcells - - #def find_primitive_cells_old(self, tol=1e-5): + # def find_primitive_cells_old(self, tol=1e-5): # if self.pbcndims == 0: # raise ValueError() @@ -236,7 +234,6 @@ def find_subcells(self, respect_basis=True, respect_labels=False, respect_dm1=No # #print(coords[3] - coords[1]) # #print(aavecs[3,1]) - # tvecs = aavecs.reshape(self.natom*self.natom, 3) # #tvecs = np.unique(tvecs, axis=0) @@ -274,12 +271,12 @@ def find_subcells(self, respect_basis=True, respect_labels=False, respect_dm1=No # print(tvecs_dim[1]) # print(tvecs_dim[2]) - # # Find most + # # Find most # for d in range(3): # pass -if __name__ == '__main__': +if __name__ == "__main__": from timeit import default_timer as timer import pyscf @@ -289,31 +286,30 @@ def find_subcells(self, respect_basis=True, respect_labels=False, respect_dm1=No import pyscf.pbc.tools cell = pyscf.pbc.gto.Cell() - cell.a = 3*np.eye(3) - #cell.a[0,2] += 2.0 - #cell.atom = ['He 0 0 0', 'He 0 0 1', 'He 0 0 2.0000000001', 'He 0 1 2', 'He 2 2 1', 'He 5 5 5'] - #cell.atom = ['He %f %f %f' % tuple(xyz) for xyz in np.random.rand(4,3)] - cell.atom = 'He 0 0 0' - cell.unit = 'Bohr' - cell.basis = 'def2-svp' + cell.a = 3 * np.eye(3) + # cell.a[0,2] += 2.0 + # cell.atom = ['He 0 0 0', 'He 0 0 1', 'He 0 0 2.0000000001', 'He 0 1 2', 'He 2 2 1', 'He 5 5 5'] + # cell.atom = ['He %f %f %f' % tuple(xyz) for xyz in np.random.rand(4,3)] + cell.atom = "He 0 0 0" + cell.unit = "Bohr" + cell.basis = "def2-svp" cell.build() - #cell.dimension = 2 - + # cell.dimension = 2 sc = [1, 2, 3] cell = pyscf.pbc.tools.super_cell(cell, sc) - #cell.atom += ['Ne 1 2 3'] - #cell.build() + # cell.atom += ['Ne 1 2 3'] + # cell.build() mf = pyscf.pbc.scf.RHF(cell) - mf = mf.density_fit(auxbasis='def2-svp-ri') + mf = mf.density_fit(auxbasis="def2-svp-ri") mf.kernel() dm1 = mf.make_rdm1() - dm1 += 1e-7*np.random.rand(*dm1.shape) + dm1 += 1e-7 * np.random.rand(*dm1.shape) sym = Symmetry(cell) t0 = timer() ncells = sym.find_primitive_ncells(respect_dm1=dm1) - print(timer()-t0) + print(timer() - t0) print(ncells) diff --git a/vayesta/core/symmetry/tsymmetry.py b/vayesta/core/symmetry/tsymmetry.py index e67d81392..16bef59b4 100644 --- a/vayesta/core/symmetry/tsymmetry.py +++ b/vayesta/core/symmetry/tsymmetry.py @@ -7,19 +7,21 @@ log = logging.getLogger(__name__) + def to_bohr(a, unit): - if unit[0].upper() == 'A': - return a/BOHR - if unit[0].upper() == 'B': + if unit[0].upper() == "A": + return a / BOHR + if unit[0].upper() == "B": return a raise ValueError("Unknown unit: %s" % unit) -def get_mesh_tvecs(cell, tvecs, unit='Ang'): + +def get_mesh_tvecs(cell, tvecs, unit="Ang"): rvecs = cell.lattice_vectors() - if (np.ndim(tvecs) == 1 and len(tvecs) == 3): + if np.ndim(tvecs) == 1 and len(tvecs) == 3: mesh = tvecs - tvecs = [rvecs[d]/mesh[d] for d in range(3)] - elif (np.ndim(tvecs) == 2 and tvecs.shape == (3,3)): + tvecs = [rvecs[d] / mesh[d] for d in range(3)] + elif np.ndim(tvecs) == 2 and tvecs.shape == (3, 3): for d in range(3): if np.all(tvecs[d] == 0): tvecs[d] = rvecs[d] @@ -34,17 +36,19 @@ def get_mesh_tvecs(cell, tvecs, unit='Ang'): raise ValueError("Unknown set of T-vectors: %r" % tvecs) return mesh, tvecs -def loop_tvecs(cell, tvecs, unit='Ang', include_origin=False): + +def loop_tvecs(cell, tvecs, unit="Ang", include_origin=False): mesh, tvecs = get_mesh_tvecs(cell, tvecs, unit) log.debugv("nx= %d ny= %d nz= %d", *mesh) log.debugv("tvecs=\n%r", tvecs) for dz, dy, dx in itertools.product(range(mesh[2]), range(mesh[1]), range(mesh[0])): if not include_origin and (abs(dx) + abs(dy) + abs(dz) == 0): continue - t = dx*tvecs[0] + dy*tvecs[1] + dz*tvecs[2] + t = dx * tvecs[0] + dy * tvecs[1] + dz * tvecs[2] yield (dx, dy, dz), t -def tsymmetric_atoms(cell, rvecs, xtol=1e-8, unit='Ang', check_element=True, check_basis=True): + +def tsymmetric_atoms(cell, rvecs, xtol=1e-8, unit="Ang", check_element=True, check_basis=True): """Get indices of translationally symmetric atoms. Parameters @@ -88,7 +92,7 @@ def tsymmetric_atoms(cell, rvecs, xtol=1e-8, unit='Ang', check_element=True, che continue # 3) Check distance modulo lattice vectors pos2 = np.dot(atom_coords[atm2], bvecs.T) - dist = (pos2 - pos1) + dist = pos2 - pos1 dist -= np.rint(dist) if np.linalg.norm(dist) < xtol: # atm1 and atm2 are symmetry equivalent @@ -101,6 +105,7 @@ def tsymmetric_atoms(cell, rvecs, xtol=1e-8, unit='Ang', check_element=True, che return indices + def compare_atoms(cell, atm1, atm2, check_basis=True): type1 = cell.atom_pure_symbol(atm1) type2 = cell.atom_pure_symbol(atm2) @@ -114,7 +119,8 @@ def compare_atoms(cell, atm1, atm2, check_basis=True): return False return True -def reorder_atoms(cell, tvec, boundary=None, unit='Ang', check_basis=True): + +def reorder_atoms(cell, tvec, boundary=None, unit="Ang", check_basis=True): """Reordering of atoms for a given translation. Parameters @@ -128,14 +134,14 @@ def reorder_atoms(cell, tvec, boundary=None, unit='Ang', check_basis=True): inverse: list """ if boundary is None: - if hasattr(cell, 'boundary'): + if hasattr(cell, "boundary"): boundary = cell.boundary else: - boundary = 'PBC' + boundary = "PBC" if np.ndim(boundary) == 0: - boundary = 3*[boundary] + boundary = 3 * [boundary] elif np.ndim(boundary) == 1 and len(boundary) == 2: - boundary = [boundary[0], boundary[1], 'PBC'] + boundary = [boundary[0], boundary[1], "PBC"] tvec = to_bohr(tvec, unit) @@ -149,9 +155,9 @@ def reorder_atoms(cell, tvec, boundary=None, unit='Ang', check_basis=True): log.debugv("%3d %f %f %f", atm, *coords) log.debugv("boundary= %r", boundary) for d in range(3): - if boundary[d].upper() == 'PBC': + if boundary[d].upper() == "PBC": boundary[d] = 1 - elif boundary[d].upper() == 'APBC': + elif boundary[d].upper() == "APBC": boundary[d] = -1 else: raise ValueError("Boundary= %s" % boundary) @@ -159,18 +165,20 @@ def reorder_atoms(cell, tvec, boundary=None, unit='Ang', check_basis=True): log.debugv("boundary= %r", boundary) def get_atom_at(pos, xtol=1e-8): - for dx, dy, dz in itertools.product([0,-1,1], repeat=3): - if cell.dimension in (1, 2) and (dz != 0): continue - if cell.dimension == 1 and (dy != 0): continue + for dx, dy, dz in itertools.product([0, -1, 1], repeat=3): + if cell.dimension in (1, 2) and (dz != 0): + continue + if cell.dimension == 1 and (dy != 0): + continue dr = np.asarray([dx, dy, dz]) - phase = np.product(boundary[dr!=0]) - #log.debugv("dx= %d dy= %d dz= %d phase= %d", dx, dy, dz, phase) - #print(atom_coords.shape, dr.shape, pos.shape) + phase = np.product(boundary[dr != 0]) + # log.debugv("dx= %d dy= %d dz= %d phase= %d", dx, dy, dz, phase) + # print(atom_coords.shape, dr.shape, pos.shape) dists = np.linalg.norm(atom_coords + dr - pos, axis=1) idx = np.argmin(dists) - if (dists[idx] < xtol): + if dists[idx] < xtol: return idx, phase - #log.debugv("atom %d not close with distance %f", idx, dists[idx]) + # log.debugv("atom %d not close with distance %f", idx, dists[idx]) return None, None natm = cell.natm @@ -194,21 +202,22 @@ def get_atom_at(pos, xtol=1e-8): return reorder, inverse, phases + def reorder_atoms2aos(cell, atom_reorder, atom_phases): if atom_reorder is None: return None, None, None - aoslice = cell.aoslice_by_atom()[:,2:] + aoslice = cell.aoslice_by_atom()[:, 2:] nao = cell.nao_nr() reorder = np.full((nao,), -1) inverse = np.full((nao,), -1) phases = np.full((nao,), 0) for atm0 in range(cell.natm): atm1 = atom_reorder[atm0] - aos0 = list(range(aoslice[atm0,0], aoslice[atm0,1])) - aos1 = list(range(aoslice[atm1,0], aoslice[atm1,1])) - reorder[aos0[0]:aos0[-1]+1] = aos1 - inverse[aos1[0]:aos1[-1]+1] = aos0 - phases[aos0[0]:aos0[-1]+1] = atom_phases[atm1] + aos0 = list(range(aoslice[atm0, 0], aoslice[atm0, 1])) + aos1 = list(range(aoslice[atm1, 0], aoslice[atm1, 1])) + reorder[aos0[0] : aos0[-1] + 1] = aos1 + inverse[aos1[0] : aos1[-1] + 1] = aos0 + phases[aos0[0] : aos0[-1] + 1] = atom_phases[atm1] assert not np.any(reorder == -1) assert not np.any(inverse == -1) assert not np.any(phases == 0) @@ -218,40 +227,43 @@ def reorder_atoms2aos(cell, atom_reorder, atom_phases): return reorder, inverse, phases -def reorder_aos(cell, tvec, unit='Ang'): +def reorder_aos(cell, tvec, unit="Ang"): atom_reorder, atom_inverse, atom_phases = reorder_atoms(cell, tvec, unit=unit) return reorder_atoms2aos(cell, atom_reorder, atom_phases) - #if atom_reorder is None: + # if atom_reorder is None: # return None, None, None - #aoslice = cell.aoslice_by_atom()[:,2:] - #nao = cell.nao_nr() - #reorder = np.full((nao,), -1) - #inverse = np.full((nao,), -1) - #phases = np.full((nao,), 0) - #for atm0 in range(cell.natm): + # aoslice = cell.aoslice_by_atom()[:,2:] + # nao = cell.nao_nr() + # reorder = np.full((nao,), -1) + # inverse = np.full((nao,), -1) + # phases = np.full((nao,), 0) + # for atm0 in range(cell.natm): # atm1 = atom_reorder[atm0] # aos0 = list(range(aoslice[atm0,0], aoslice[atm0,1])) # aos1 = list(range(aoslice[atm1,0], aoslice[atm1,1])) # reorder[aos0[0]:aos0[-1]+1] = aos1 # inverse[aos1[0]:aos1[-1]+1] = aos0 # phases[aos0[0]:aos0[-1]+1] = atom_phases[atm1] - #assert not np.any(reorder == -1) - #assert not np.any(inverse == -1) - #assert not np.any(phases == 0) + # assert not np.any(reorder == -1) + # assert not np.any(inverse == -1) + # assert not np.any(phases == 0) + + # assert np.all(np.arange(nao)[reorder][inverse] == np.arange(nao)) - #assert np.all(np.arange(nao)[reorder][inverse] == np.arange(nao)) + # return reorder, inverse, phases - #return reorder, inverse, phases def _make_reorder_op(reorder, phases): def reorder_op(a, axis=0): if isinstance(a, (tuple, list)): return tuple([reorder_op(x, axis=axis) for x in a]) - bc = tuple(axis*[None] + [slice(None, None, None)] + (a.ndim-axis-1)*[None]) + bc = tuple(axis * [None] + [slice(None, None, None)] + (a.ndim - axis - 1) * [None]) return np.take(a, reorder, axis=axis) * phases[bc] + return reorder_op -def get_tsymmetry_op(cell, tvec, unit='Ang'): + +def get_tsymmetry_op(cell, tvec, unit="Ang"): reorder, inverse, phases = reorder_aos(cell, tvec, unit=unit) if reorder is None: # Not a valid symmetry @@ -259,8 +271,10 @@ def get_tsymmetry_op(cell, tvec, unit='Ang'): tsym_op = _make_reorder_op(reorder, phases) return tsym_op -if __name__ == '__main__': + +if __name__ == "__main__": import vayesta + log = vayesta.log import pyscf @@ -268,19 +282,19 @@ def get_tsymmetry_op(cell, tvec, unit='Ang'): import pyscf.pbc.tools cell = pyscf.pbc.gto.Cell() - cell.atom='H 0 0 0, H 0 1 2' - cell.basis='sto-3g' + cell.atom = "H 0 0 0, H 0 1 2" + cell.basis = "sto-3g" cell.a = np.eye(3) cell.dimension = 1 cell.build() ncopy = 4 - cell = pyscf.pbc.tools.super_cell(cell, [ncopy,1,1]) + cell = pyscf.pbc.tools.super_cell(cell, [ncopy, 1, 1]) - reorder, inverse, phases = reorder_atoms(cell, cell.a[0]/ncopy, unit='B') + reorder, inverse, phases = reorder_atoms(cell, cell.a[0] / ncopy, unit="B") print(reorder) print(inverse) - reorder, inverse, phases = reorder_aos(cell, cell.a[0]/ncopy, unit='B') + reorder, inverse, phases = reorder_aos(cell, cell.a[0] / ncopy, unit="B") print(reorder) print(inverse) diff --git a/vayesta/core/types/bosonic_orbitals.py b/vayesta/core/types/bosonic_orbitals.py index 123b52e7e..77415b0d4 100644 --- a/vayesta/core/types/bosonic_orbitals.py +++ b/vayesta/core/types/bosonic_orbitals.py @@ -7,6 +7,7 @@ class BosonicOrbitals: and deexcitations in our original bosonic basis. Name subject to change... """ + def __init__(self, coeff_ex, coeff_dex=None, energy=None, labels=None): self.coeff_ex = np.asarray(coeff_ex, dtype=float) self.coeff_dex = coeff_dex @@ -22,8 +23,12 @@ def nex(self): return self.coeff_ex.shape[1] def copy(self): - return type(self)(coeff_ex=_copy(self.coeff_ex), coeff_dex=_copy(self.coeff_dex), energy=_copy(self.energy), - labels=_copy(self.labels)) + return type(self)( + coeff_ex=_copy(self.coeff_ex), + coeff_dex=_copy(self.coeff_dex), + energy=_copy(self.energy), + labels=_copy(self.labels), + ) def rotate(self, U, inplace=False): """Rotate bosonic basis by unitary U.""" @@ -69,29 +74,41 @@ def coeff_ex_3d(self): @property def coeff_dex_3d(self): - return None if self.coeff_dex is None else bcoeff_ov_to_o_v(self.coeff_dex, self.forbitals.nocc, self.forbitals.nvir) + return ( + None + if self.coeff_dex is None + else bcoeff_ov_to_o_v(self.coeff_dex, self.forbitals.nocc, self.forbitals.nvir) + ) @property def coeff_3d_ao(self): """Get bosonic coefficient in basis of ao excitations""" alpha, beta = bcoeff_mo2ao(self.coeff_ex_3d, self.forbitals.coeff_occ, self.forbitals.coeff_vir) if self.has_dex: - dexa, dexb = bcoeff_mo2ao(self.coeff_dex_3d, self.forbitals.coeff_occ, self.forbitals.coeff_vir, transpose=True) + dexa, dexb = bcoeff_mo2ao( + self.coeff_dex_3d, self.forbitals.coeff_occ, self.forbitals.coeff_vir, transpose=True + ) alpha += dexa beta += dexb return alpha, beta def copy(self): - return type(self)(forbitals=self.forbitals.copy(), coeff_ex=_copy(self.coeff_ex), - coeff_dex=_copy(self.coeff_dex), energy=_copy(self.energy), labels=_copy(self.labels)) + return type(self)( + forbitals=self.forbitals.copy(), + coeff_ex=_copy(self.coeff_ex), + coeff_dex=_copy(self.coeff_dex), + energy=_copy(self.energy), + labels=_copy(self.labels), + ) def fbasis_transform(self, trafo, inplace=False): - if not hasattr(trafo, '__len__'): + if not hasattr(trafo, "__len__"): trafo = (trafo, trafo) cp = self if inplace else self.copy() cp.forbitals.basis_transform(trafo[0], inplace=True) return cp + def bcoeff_ov_to_o_v(cbos, no, nv): noa, nob = no if isinstance(no, tuple) else (no, no) nva, nvb = nv if isinstance(nv, tuple) else (nv, nv) @@ -100,6 +117,7 @@ def bcoeff_ov_to_o_v(cbos, no, nv): ca, cb = cbos[:, :ova], cbos[:, ova:] return ca.reshape((nbos, noa, nva)), cb.reshape((nbos, nob, nvb)) + def bcoeff_mo2ao(cbos, co, cv, transpose=False): def _spinchannel_bcoeff_mo2ao(cbos, co, cv, transpose=False): """Convert bosonic coefficients from MO basis to AO basis.""" @@ -108,8 +126,9 @@ def _spinchannel_bcoeff_mo2ao(cbos, co, cv, transpose=False): cbos = cbos.transpose((0, 2, 1)) return cbos - return _spinchannel_bcoeff_mo2ao(cbos[0], co[0], cv[0], transpose=transpose), \ - _spinchannel_bcoeff_mo2ao(cbos[1], co[1], cv[1], transpose=transpose) + return _spinchannel_bcoeff_mo2ao(cbos[0], co[0], cv[0], transpose=transpose), _spinchannel_bcoeff_mo2ao( + cbos[1], co[1], cv[1], transpose=transpose + ) def _copy(x): diff --git a/vayesta/core/types/cluster.py b/vayesta/core/types/cluster.py index 958eba376..cf083dbd6 100644 --- a/vayesta/core/types/cluster.py +++ b/vayesta/core/types/cluster.py @@ -4,11 +4,10 @@ from vayesta.core.types.bosonic_orbitals import BosonicOrbitals from vayesta.core.spinalg import add_numbers, hstack_matrices -__all__ = ['Cluster', 'ClusterRHF', 'ClusterUHF'] +__all__ = ["Cluster", "ClusterRHF", "ClusterUHF"] class Cluster: - def __init__(self, active_orbitals, frozen_orbitals, bosons=None): self.active_orbitals = active_orbitals self.frozen_orbitals = frozen_orbitals @@ -18,7 +17,7 @@ def __init__(self, active_orbitals, frozen_orbitals, bosons=None): def from_coeffs(c_active_occ, c_active_vir, c_frozen_occ, c_frozen_vir): c_active = hstack_matrices(c_active_occ, c_active_vir) c_frozen = hstack_matrices(c_frozen_occ, c_frozen_vir) - is_rhf = (c_active_occ[0].ndim == 1) + is_rhf = c_active_occ[0].ndim == 1 if is_rhf: nocc_active = c_active_occ.shape[-1] nocc_frozen = c_frozen_occ.shape[-1] @@ -134,70 +133,84 @@ def basis_transform(self, trafo, inplace=False): class ClusterRHF(Cluster): - - spinsym = 'restricted' + spinsym = "restricted" def __repr__(self): - return '%s(norb_active= %d, norb_frozen= %d)' % (self.__class__.__name__, - self.norb_active, self.norb_frozen) + return "%s(norb_active= %d, norb_frozen= %d)" % (self.__class__.__name__, self.norb_active, self.norb_frozen) # Indices and slices def get_active_slice(self): - return np.s_[self.nocc_frozen : self.nocc_frozen+self.norb_active] + return np.s_[self.nocc_frozen : self.nocc_frozen + self.norb_active] def get_active_indices(self): - return list(range(self.nocc_frozen, self.nocc_frozen+self.norb_active)) + return list(range(self.nocc_frozen, self.nocc_frozen + self.norb_active)) def get_frozen_indices(self): - return list(range(self.nocc_frozen)) + list(range(self.norb_total-self.nvir_frozen, self.norb_total)) + return list(range(self.nocc_frozen)) + list(range(self.norb_total - self.nvir_frozen, self.norb_total)) def repr_size(self): lines = [] - fmt = (10*" " + 2*" %-15s" + " %-5s") + fmt = 10 * " " + 2 * " %-15s" + " %-5s" lines += [fmt % ("Active", "Frozen", "Total")] - lines += [fmt % (15*'-', 15*'-', 5*'-')] - fmt = ' %-8s' + 2*' %5d (%6.1f%%)' + ' %5d' - get_values = lambda a, f, n : (a, 100*a/n, f, 100*f/n, n) + lines += [fmt % (15 * "-", 15 * "-", 5 * "-")] + fmt = " %-8s" + 2 * " %5d (%6.1f%%)" + " %5d" + get_values = lambda a, f, n: (a, 100 * a / n, f, 100 * f / n, n) lines += [fmt % ("Occupied", *get_values(self.nocc_active, self.nocc_frozen, self.nocc_total))] - lines += [fmt % ("Virtual", *get_values(self.nvir_active, self.nvir_frozen, self.nvir_total))] - lines += [fmt % ("Total", *get_values(self.norb_active, self.norb_frozen, self.norb_total))] - return '\n'.join(lines) + lines += [fmt % ("Virtual", *get_values(self.nvir_active, self.nvir_frozen, self.nvir_total))] + lines += [fmt % ("Total", *get_values(self.norb_active, self.norb_frozen, self.norb_total))] + return "\n".join(lines) class ClusterUHF(Cluster): - - spinsym = 'unrestricted' + spinsym = "unrestricted" def __repr__(self): - return '%s(norb_active= (%d, %d), norb_frozen= (%d, %d))' % (self.__class__.__name__, - *self.norb_active, *self.norb_frozen) + return "%s(norb_active= (%d, %d), norb_frozen= (%d, %d))" % ( + self.__class__.__name__, + *self.norb_active, + *self.norb_frozen, + ) # Indices and slices def get_active_slice(self): - return (np.s_[self.nocc_frozen[0]:self.nocc_frozen[0]+self.norb_active[0]], - np.s_[self.nocc_frozen[1]:self.nocc_frozen[1]+self.norb_active[1]]) + return ( + np.s_[self.nocc_frozen[0] : self.nocc_frozen[0] + self.norb_active[0]], + np.s_[self.nocc_frozen[1] : self.nocc_frozen[1] + self.norb_active[1]], + ) def get_active_indices(self): - return (list(range(self.nocc_frozen[0], self.nocc_frozen[0]+self.norb_active[0])), - list(range(self.nocc_frozen[1], self.nocc_frozen[1]+self.norb_active[1]))) + return ( + list(range(self.nocc_frozen[0], self.nocc_frozen[0] + self.norb_active[0])), + list(range(self.nocc_frozen[1], self.nocc_frozen[1] + self.norb_active[1])), + ) def get_frozen_indices(self): - return (list(range(self.nocc_frozen[0])) - + list(range(self.norb_total[0]-self.nvir_frozen[0], self.norb_total[0])), - list(range(self.nocc_frozen[1])) - + list(range(self.norb_total[1]-self.nvir_frozen[1], self.norb_total[1]))) + return ( + list(range(self.nocc_frozen[0])) + + list(range(self.norb_total[0] - self.nvir_frozen[0], self.norb_total[0])), + list(range(self.nocc_frozen[1])) + + list(range(self.norb_total[1] - self.nvir_frozen[1], self.norb_total[1])), + ) def repr_size(self): lines = [] - fmt = (10*" " + 2*" %-22s" + " %-12s") + fmt = 10 * " " + 2 * " %-22s" + " %-12s" lines += [(fmt % ("Active", "Frozen", "Total")).rstrip()] - lines += [fmt % (22*'-', 22*'-', 12*'-')] - fmt = ' %-8s' + 2*' %5d, %5d (%6.1f%%)' + ' %5d, %5d' - get_values = lambda a, f, n : (a[0], a[1], 100*(a[0]+a[1])/(n[0]+n[1]), - f[0], f[1], 100*(f[0]+f[1])/(n[0]+n[1]), n[0], n[1]) + lines += [fmt % (22 * "-", 22 * "-", 12 * "-")] + fmt = " %-8s" + 2 * " %5d, %5d (%6.1f%%)" + " %5d, %5d" + get_values = lambda a, f, n: ( + a[0], + a[1], + 100 * (a[0] + a[1]) / (n[0] + n[1]), + f[0], + f[1], + 100 * (f[0] + f[1]) / (n[0] + n[1]), + n[0], + n[1], + ) lines += [fmt % ("Occupied", *get_values(self.nocc_active, self.nocc_frozen, self.nocc_total))] - lines += [fmt % ("Virtual", *get_values(self.nvir_active, self.nvir_frozen, self.nvir_total))] - lines += [fmt % ("Total", *get_values(self.norb_active, self.norb_frozen, self.norb_total))] - return '\n'.join(lines) + lines += [fmt % ("Virtual", *get_values(self.nvir_active, self.nvir_frozen, self.nvir_total))] + lines += [fmt % ("Total", *get_values(self.norb_active, self.norb_frozen, self.norb_total))] + return "\n".join(lines) diff --git a/vayesta/core/types/ebwf/__init__.py b/vayesta/core/types/ebwf/__init__.py index cd1f0c7cd..d615bba2f 100644 --- a/vayesta/core/types/ebwf/__init__.py +++ b/vayesta/core/types/ebwf/__init__.py @@ -1,7 +1,8 @@ from vayesta.core.types.ebwf.ebwf import EBWavefunction + try: - from vayesta.core.types.ebwf.ebcc import EBCC_WaveFunction, REBCC_WaveFunction, UEBCC_WaveFunction + from vayesta.core.types.ebwf.ebcc import EBCC_WaveFunction, REBCC_WaveFunction, UEBCC_WaveFunction except ImportError: - _has_ebcc = False + _has_ebcc = False else: - _has_ebcc = True + _has_ebcc = True diff --git a/vayesta/core/types/ebwf/ebcc.py b/vayesta/core/types/ebwf/ebcc.py index ea779b6cc..636492c3f 100644 --- a/vayesta/core/types/ebwf/ebcc.py +++ b/vayesta/core/types/ebwf/ebcc.py @@ -32,7 +32,6 @@ def EBCC_WaveFunction(mo, *args, **kwargs): # - ebcc includes an additional factor of 1/2 in the definition of T2aaaa, T2bbbb, L2aaaa, L2bbbb etc. # - ebcc stores lambda amplitudes ai, abij while pyscf stores them ia, ijab. class REBCC_WaveFunction(EBWavefunction, RCCSD_WaveFunction): - _spin_type = "R" _driver = ebcc.REBCC @@ -86,7 +85,8 @@ def l1(self): @l1.setter def l1(self, value): - if value is None: return + if value is None: + return if self.lambdas is None: self.lambdas = ebcc.util.Namespace() self.lambdas.l1 = value.T @@ -97,7 +97,8 @@ def l2(self): @l2.setter def l2(self, value): - if value is None: return + if value is None: + return if self.lambdas is None: self.lambdas = ebcc.util.Namespace() self.lambdas.l2 = value.transpose((2, 3, 0, 1)) @@ -126,28 +127,34 @@ def _pack_codegen_kwargs(self, *extra_kwargs, eris=False): kwargs.update(kw) return kwargs - def make_rdm1(self, t_as_lambda=False, with_mf=True, ao_basis=False, hermitise=True, **kwargs): - assert(not t_as_lambda and with_mf and not ao_basis) - return self._driver.make_rdm1_f(self, eris=False, amplitudes=self.amplitudes, lambdas=self.lambdas, - hermitise=True, **kwargs) + def make_rdm1(self, t_as_lambda=False, with_mf=True, ao_basis=False, hermitise=True, **kwargs): + assert not t_as_lambda and with_mf and not ao_basis + return self._driver.make_rdm1_f( + self, eris=False, amplitudes=self.amplitudes, lambdas=self.lambdas, hermitise=True, **kwargs + ) - def make_rdm2(self, t_as_lambda=False, with_dm1=True, ao_basis=False, approx_cumulant=False, hermitise=True, - **kwargs): - assert(not t_as_lambda and with_dm1 and not ao_basis and not approx_cumulant) - return self._driver.make_rdm2_f(self, eris=False, amplitudes=self.amplitudes, lambdas=self.lambdas, - hermitise=hermitise, **kwargs) + def make_rdm2( + self, t_as_lambda=False, with_dm1=True, ao_basis=False, approx_cumulant=False, hermitise=True, **kwargs + ): + assert not t_as_lambda and with_dm1 and not ao_basis and not approx_cumulant + return self._driver.make_rdm2_f( + self, eris=False, amplitudes=self.amplitudes, lambdas=self.lambdas, hermitise=hermitise, **kwargs + ) def make_rdm1_b(self, hermitise=True, **kwargs): - return self._driver.make_rdm1_b(self, eris=False, amplitudes=self.amplitudes, lambdas=self.lambdas, - hermitise=hermitise, **kwargs) + return self._driver.make_rdm1_b( + self, eris=False, amplitudes=self.amplitudes, lambdas=self.lambdas, hermitise=hermitise, **kwargs + ) def make_sing_b_dm(self, hermitise=True, **kwargs): - return self._driver.make_sing_b_dm(self, eris=False, amplitudes=self.amplitudes, lambdas=self.lambdas, - hermitise=hermitise, **kwargs) + return self._driver.make_sing_b_dm( + self, eris=False, amplitudes=self.amplitudes, lambdas=self.lambdas, hermitise=hermitise, **kwargs + ) def make_rdm_eb(self, hermitise=True, **kwargs): - dmeb = self._driver.make_eb_coup_rdm(self, eris=False, amplitudes=self.amplitudes, lambdas=self.lambdas, - hermitise=hermitise, **kwargs) + dmeb = self._driver.make_eb_coup_rdm( + self, eris=False, amplitudes=self.amplitudes, lambdas=self.lambdas, hermitise=hermitise, **kwargs + ) return (dmeb[0].transpose((1, 2, 0)) / 2, dmeb[0].transpose((1, 2, 0)) / 2) make_rdm1_f = make_rdm1 @@ -155,20 +162,27 @@ def make_rdm_eb(self, hermitise=True, **kwargs): def copy(self): proj = callif(spinalg.copy, self.projector) - return type(self)(self.mo.copy(), deepcopy(self.ansatz), deepcopy(self.amplitudes), deepcopy(self.lambdas), - None if self.mbos is None else self.mbos.copy(), proj) + return type(self)( + self.mo.copy(), + deepcopy(self.ansatz), + deepcopy(self.amplitudes), + deepcopy(self.lambdas), + None if self.mbos is None else self.mbos.copy(), + proj, + ) def as_ccsd(self): proj = callif(spinalg.copy, self.projector) - return type(self)(self.mo.copy(), "CCSD", deepcopy(self.amplitudes), deepcopy(self.lambdas), - self.mbos.copy(), proj) + return type(self)( + self.mo.copy(), "CCSD", deepcopy(self.amplitudes), deepcopy(self.lambdas), self.mbos.copy(), proj + ) def rotate_ov(self, *args, **kwargs): # Note that this is slightly dodgy until we implement rotation of the coupled amplitudes. if "t3" in self.amplitudes: # can't access log within wavefunction classes currently; this should be a warning. pass - #raise NotImplementedError("Only rotation of CCSD components is implemented.") + # raise NotImplementedError("Only rotation of CCSD components is implemented.") return super().rotate_ov(*args, **kwargs) @@ -242,7 +256,8 @@ def l1(self): @l1.setter def l1(self, value): - if value is None: return + if value is None: + return if self.lambdas is None: self.lambdas = ebcc.util.Namespace() self.lambdas.l1.aa = value[0].T @@ -275,7 +290,8 @@ def l2(self): @l2.setter def l2(self, value): - if value is None: return + if value is None: + return if self.lambdas is None: self.lambdas = ebcc.util.Namespace() self.lambdas.l2.aaaa = value[0].transpose(2, 3, 0, 1) / 2.0 @@ -316,7 +332,8 @@ def make_rdm2(self, *args, **kwargs): return dm2.aaaa, dm2.aabb, dm2.bbbb def make_rdm_eb(self, hermitise=True, **kwargs): - dmeb = self._driver.make_eb_coup_rdm(self, eris=False, amplitudes=self.amplitudes, lambdas=self.lambdas, - hermitise=hermitise, **kwargs) + dmeb = self._driver.make_eb_coup_rdm( + self, eris=False, amplitudes=self.amplitudes, lambdas=self.lambdas, hermitise=hermitise, **kwargs + ) return (dmeb.aa[0].transpose(1, 2, 0), dmeb.bb[0].transpose(1, 2, 0)) diff --git a/vayesta/core/types/ebwf/ebwf.py b/vayesta/core/types/ebwf/ebwf.py index 73e26360d..a92265e13 100644 --- a/vayesta/core/types/ebwf/ebwf.py +++ b/vayesta/core/types/ebwf/ebwf.py @@ -1,8 +1,8 @@ from vayesta.core.types.wf import WaveFunction from vayesta.core.util import AbstractMethodError -class EBWavefunction(WaveFunction): +class EBWavefunction(WaveFunction): def __init__(self, mo, mbos=None, projector=None): WaveFunction.__init__(self, mo, projector) self.mbos = mbos @@ -12,7 +12,13 @@ def nbos(self): return 0 if self.mbos is None else self.mbos.nbos def __repr__(self): - return "%s(norb= %r, nocc= %r, nvir=%r, nbos= %r)" % (self.__class__.__name__, self.norb, self.nocc, self.nvir, self.nbos) + return "%s(norb= %r, nocc= %r, nvir=%r, nbos= %r)" % ( + self.__class__.__name__, + self.norb, + self.nocc, + self.nvir, + self.nbos, + ) def make_rdm_eb(self, *args, **kwargs): raise AbstractMethodError diff --git a/vayesta/core/types/orbitals.py b/vayesta/core/types/orbitals.py index 4d57e79c2..b731b8efb 100644 --- a/vayesta/core/types/orbitals.py +++ b/vayesta/core/types/orbitals.py @@ -2,8 +2,12 @@ from vayesta.core.helper import pack_arrays, unpack_arrays __all__ = [ - 'Orbitals', 'SpatialOrbitals', 'SpinOrbitals', 'GeneralOrbitals', - ] + "Orbitals", + "SpatialOrbitals", + "SpinOrbitals", + "GeneralOrbitals", +] + class MolecularOrbitals: """Abstract base class""" @@ -24,22 +28,29 @@ def _copy(x): if isinstance(x, np.ndarray): return x.copy() raise ValueError - return type(self)(coeff=_copy(self.coeff), energy=_copy(self.energy), occ=_copy(self.occ), - labels=_copy(self.labels), maxocc=_copy(self.maxocc)) + + return type(self)( + coeff=_copy(self.coeff), + energy=_copy(self.energy), + occ=_copy(self.occ), + labels=_copy(self.labels), + maxocc=_copy(self.maxocc), + ) + def Orbitals(coeff, *args, **kwargs): if np.ndim(coeff[0]) == 2: return SpinOrbitals(coeff, *args, **kwargs) return SpatialOrbitals(coeff, *args, **kwargs) -class SpatialOrbitals(MolecularOrbitals): +class SpatialOrbitals(MolecularOrbitals): def __init__(self, coeff, energy=None, occ=None, labels=None, maxocc=2): self.coeff = np.asarray(coeff, dtype=float) self.energy = np.asarray(energy, dtype=float) if energy is not None else None self.maxocc = maxocc if isinstance(occ, (int, np.integer)): - occ = np.asarray(occ*[self.maxocc] + (self.norb-occ)*[0]) + occ = np.asarray(occ * [self.maxocc] + (self.norb - occ) * [0]) self.occ = np.asarray(occ, dtype=float) if occ is not None else None self.labels = labels @@ -68,17 +79,17 @@ def nelec(self): if self.occ is None: return None ne = self.occ.sum() - if abs(np.rint(ne)-ne) < 1e-14: + if abs(np.rint(ne) - ne) < 1e-14: return int(np.rint(ne)) return ne @property def coeff_occ(self): - return self.coeff[:,:self.nocc] + return self.coeff[:, : self.nocc] @property def coeff_vir(self): - return self.coeff[:,self.nocc:] + return self.coeff[:, self.nocc :] def basis_transform(self, trafo, inplace=False): cp = self if inplace else self.copy() @@ -108,19 +119,22 @@ def unpack(cls, packed): class SpinOrbitals(MolecularOrbitals): - def __init__(self, coeff, energy=None, occ=None, labels=None, maxocc=1): - if energy is None: energy = (None, None) - if occ is None: occ = (None, None) - if labels is None: labels = (None, None) - if np.isscalar(maxocc): maxocc = (maxocc, maxocc) + if energy is None: + energy = (None, None) + if occ is None: + occ = (None, None) + if labels is None: + labels = (None, None) + if np.isscalar(maxocc): + maxocc = (maxocc, maxocc) self.alpha = SpatialOrbitals(coeff[0], energy=energy[0], occ=occ[0], labels=labels[0], maxocc=maxocc[0]) self.beta = SpatialOrbitals(coeff[1], energy=energy[1], occ=occ[1], labels=labels[1], maxocc=maxocc[1]) @classmethod def from_spatial_orbitals(cls, orbitals): energy = (orbitals.energy, orbitals.energy) if orbitals.energy is not None else None - occ = (orbitals.occ/2, orbitals.occ/2) if orbitals.occ is not None else None + occ = (orbitals.occ / 2, orbitals.occ / 2) if orbitals.occ is not None else None labels = (orbitals.labels, orbitals.labels) if orbitals.labels is not None else None return cls((orbitals.coeff, orbitals.coeff), energy=energy, occ=occ, labels=labels) @@ -156,20 +170,31 @@ def nspin(self): return 2 def __getattr__(self, name): - if name in ('norb', 'nocc', 'nvir', 'nelec', 'energy', - 'coeff', 'coeff_occ', 'coeff_vir', 'occ', 'labels', 'maxocc'): + if name in ( + "norb", + "nocc", + "nvir", + "nelec", + "energy", + "coeff", + "coeff_occ", + "coeff_vir", + "occ", + "labels", + "maxocc", + ): return (getattr(self.alpha, name), getattr(self.beta, name)) raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name)) def __setattr__(self, name, value): - if name in ('energy', 'coeff', 'occ', 'labels'): + if name in ("energy", "coeff", "occ", "labels"): setattr(self.alpha, name, value[0]) setattr(self.beta, name, value[1]) return super().__setattr__(name, value) def basis_transform(self, trafo, inplace=False): - if not hasattr(trafo, '__len__'): + if not hasattr(trafo, "__len__"): trafo = (trafo, trafo) cp = self if inplace else self.copy() cp.alpha.basis_transform(trafo[0], inplace=True) @@ -196,7 +221,6 @@ def unpack(cls, packed): class GeneralOrbitals(SpatialOrbitals): - @property def nspin(self): return 3 @@ -209,16 +233,17 @@ def from_spatial_orbitals(cls, orbitals): def from_spin_orbitals(cls, orbitals): raise NotImplementedError -if __name__ == '__main__': + +if __name__ == "__main__": def test_spatial(nao=20, nocc=5, nvir=15): coeff = np.random.rand(nao, nao) energy = np.random.rand(nao) - occ = np.asarray(nocc*[2] + nvir*[0]) + occ = np.asarray(nocc * [2] + nvir * [0]) orbs = SpatialOrbitals(coeff, energy=energy, occ=occ) - #packed = orbs.pack() - #orbs2 = SpatialOrbitals.unpack(packed) + # packed = orbs.pack() + # orbs2 = SpatialOrbitals.unpack(packed) orbs2 = orbs.copy() assert np.all(orbs.coeff == orbs2.coeff) assert np.all(orbs.energy == orbs2.energy) @@ -231,7 +256,7 @@ def test_spin(nao=20, nocc=5, nvir=15): for s in range(2): coeff.append(np.random.rand(nao, nao)) energy.append(np.random.rand(nao)) - occ.append(np.asarray(nocc*[2] + nvir*[0])) + occ.append(np.asarray(nocc * [2] + nvir * [0])) orbs = SpinOrbitals(coeff, energy=energy, occ=occ) packed = orbs.pack() diff --git a/vayesta/core/types/wf/__init__.py b/vayesta/core/types/wf/__init__.py index 30313380e..7cf94f39c 100644 --- a/vayesta/core/types/wf/__init__.py +++ b/vayesta/core/types/wf/__init__.py @@ -9,18 +9,33 @@ from vayesta.core.types.wf.cisd import CISD_WaveFunction, RCISD_WaveFunction, UCISD_WaveFunction from vayesta.core.types.wf.ccsd import CCSD_WaveFunction, RCCSD_WaveFunction, UCCSD_WaveFunction from vayesta.core.types.wf.fci import FCI_WaveFunction, RFCI_WaveFunction, UFCI_WaveFunction + # WIP: from vayesta.core.types.wf.cisdtq import CISDTQ_WaveFunction, RCISDTQ_WaveFunction, UCISDTQ_WaveFunction from vayesta.core.types.wf.ccsdtq import CCSDTQ_WaveFunction, RCCSDTQ_WaveFunction, UCCSDTQ_WaveFunction __all__ = [ - 'WaveFunction', - 'HF_WaveFunction', 'RHF_WaveFunction', 'UHF_WaveFunction', - 'MP2_WaveFunction', 'RMP2_WaveFunction', 'UMP2_WaveFunction', - 'CISD_WaveFunction', 'RCISD_WaveFunction', 'UCISD_WaveFunction', - 'CCSD_WaveFunction', 'RCCSD_WaveFunction', 'UCCSD_WaveFunction', - 'FCI_WaveFunction', 'RFCI_WaveFunction', 'UFCI_WaveFunction', - 'CISDTQ_WaveFunction', 'RCISDTQ_WaveFunction', 'UCISDTQ_WaveFunction', - 'CCSDTQ_WaveFunction', 'RCCSDTQ_WaveFunction', 'UCCSDTQ_WaveFunction', - ] + "WaveFunction", + "HF_WaveFunction", + "RHF_WaveFunction", + "UHF_WaveFunction", + "MP2_WaveFunction", + "RMP2_WaveFunction", + "UMP2_WaveFunction", + "CISD_WaveFunction", + "RCISD_WaveFunction", + "UCISD_WaveFunction", + "CCSD_WaveFunction", + "RCCSD_WaveFunction", + "UCCSD_WaveFunction", + "FCI_WaveFunction", + "RFCI_WaveFunction", + "UFCI_WaveFunction", + "CISDTQ_WaveFunction", + "RCISDTQ_WaveFunction", + "UCISDTQ_WaveFunction", + "CCSDTQ_WaveFunction", + "RCCSDTQ_WaveFunction", + "UCCSDTQ_WaveFunction", +] diff --git a/vayesta/core/types/wf/ccsd.py b/vayesta/core/types/wf/ccsd.py index a4ac69082..036514291 100644 --- a/vayesta/core/types/wf/ccsd.py +++ b/vayesta/core/types/wf/ccsd.py @@ -1,15 +1,27 @@ import numpy as np + # TODO: Remove once unnecessary: from vayesta.core.vpyscf import ccsd_rdm from vayesta.core.vpyscf import uccsd_rdm + # import pyscf # import pyscf.cc from vayesta.core import spinalg from vayesta.core.util import NotCalculatedError, Object, callif, einsum, dot from vayesta.core.types import wf as wf_types from vayesta.core.types.orbitals import SpatialOrbitals -from vayesta.core.types.wf.project import (project_c1, project_c2, project_uc1, project_uc2, symmetrize_c2, - symmetrize_uc2, transform_c1, transform_c2, transform_uc1, transform_uc2) +from vayesta.core.types.wf.project import ( + project_c1, + project_c2, + project_uc1, + project_uc2, + symmetrize_c2, + symmetrize_uc2, + transform_c1, + transform_c2, + transform_uc1, + transform_uc2, +) from vayesta.core.helper import pack_arrays, unpack_arrays from scipy.linalg import block_diag @@ -23,10 +35,9 @@ def CCSD_WaveFunction(mo, t1, t2, **kwargs): class RCCSD_WaveFunction(wf_types.WaveFunction): - # TODO: Once standard PySCF accepts additional keyword arguments: - #_make_rdm1_backend = pyscf.cc.ccsd_rdm.make_rdm1 - #_make_rdm2_backend = pyscf.cc.ccsd_rdm.make_rdm2 + # _make_rdm1_backend = pyscf.cc.ccsd_rdm.make_rdm1 + # _make_rdm2_backend = pyscf.cc.ccsd_rdm.make_rdm2 _make_rdm1_backend = ccsd_rdm.make_rdm1 _make_rdm2_backend = ccsd_rdm.make_rdm2 @@ -40,20 +51,21 @@ def __init__(self, mo, t1, t2, l1=None, l2=None, projector=None): def make_rdm1(self, t_as_lambda=False, with_mf=True, ao_basis=False): if t_as_lambda: l1, l2 = self.t1, self.t2 - elif (self.l1 is None or self.l2 is None): + elif self.l1 is None or self.l2 is None: raise NotCalculatedError("Lambda-amplitudes required for RDM1.") else: l1, l2 = self.l1, self.l2 fakecc = Object() fakecc.mo_coeff = self.mo.coeff - dm1 = type(self)._make_rdm1_backend(fakecc, t1=self.t1, t2=self.t2, l1=l1, l2=l2, - with_frozen=False, ao_repr=ao_basis, with_mf=with_mf) + dm1 = type(self)._make_rdm1_backend( + fakecc, t1=self.t1, t2=self.t2, l1=l1, l2=l2, with_frozen=False, ao_repr=ao_basis, with_mf=with_mf + ) return dm1 def make_rdm2(self, t_as_lambda=False, with_dm1=True, ao_basis=False, approx_cumulant=True): if t_as_lambda: l1, l2 = self.t1, self.t2 - elif (self.l1 is None or self.l2 is None): + elif self.l1 is None or self.l2 is None: raise NotCalculatedError("Lambda-amplitudes required for RDM2.") else: l1, l2 = self.l1, self.l2 @@ -61,9 +73,10 @@ def make_rdm2(self, t_as_lambda=False, with_dm1=True, ao_basis=False, approx_cum fakecc.mo_coeff = self.mo.coeff fakecc.stdout = None fakecc.verbose = 0 - fakecc.max_memory = int(10e9) # 10 GB - dm2 = type(self)._make_rdm2_backend(fakecc, t1=self.t1, t2=self.t2, l1=l1, l2=l2, - with_frozen=False, ao_repr=ao_basis, with_dm1=with_dm1) + fakecc.max_memory = int(10e9) # 10 GB + dm2 = type(self)._make_rdm2_backend( + fakecc, t1=self.t1, t2=self.t2, l1=l1, l2=l2, with_frozen=False, ao_repr=ao_basis, with_dm1=with_dm1 + ) if not with_dm1: if not approx_cumulant: dm2nc = self.make_rdm2_non_cumulant(t_as_lambda=t_as_lambda, ao_basis=ao_basis) @@ -71,10 +84,10 @@ def make_rdm2(self, t_as_lambda=False, with_dm1=True, ao_basis=False, approx_cum dm2 -= dm2nc # UHF: else: - dm2 = tuple((dm2[i]-dm2nc[i]) for i in range(len(dm2nc))) - elif (approx_cumulant in (1, True)): + dm2 = tuple((dm2[i] - dm2nc[i]) for i in range(len(dm2nc))) + elif approx_cumulant in (1, True): pass - elif (approx_cumulant == 2): + elif approx_cumulant == 2: raise NotImplementedError else: raise ValueError @@ -82,7 +95,7 @@ def make_rdm2(self, t_as_lambda=False, with_dm1=True, ao_basis=False, approx_cum def make_rdm2_non_cumulant(self, t_as_lambda=False, ao_basis=False): dm1 = self.make_rdm1(t_as_lambda=t_as_lambda, with_mf=False, ao_basis=ao_basis) - dm2 = (einsum('ij,kl->ijkl', dm1, dm1) - einsum('ij,kl->iklj', dm1, dm1)/2) + dm2 = einsum("ij,kl->ijkl", dm1, dm1) - einsum("ij,kl->iklj", dm1, dm1) / 2 return dm2 def multiply(self, factor): @@ -121,7 +134,8 @@ def unpack(cls, packed): return cls(mo, t1, t2, l1=l1, l2=l2, projector=projector) def restore(self, projector=None, inplace=False, sym=True): - if projector is None: projector = self.projector + if projector is None: + projector = self.projector wf = self.project(projector.T, inplace=inplace) wf.projector = None if not sym: @@ -144,10 +158,12 @@ def as_unrestricted(self): if self.projector is not None: raise NotImplementedError mo = self.mo.to_spin_orbitals() + def _to_uccsd(t1, t2): t1, t2 = self.t1.copy, self.t2.copy() - t2aa = t2 - t2.transpose(0,1,3,2) + t2aa = t2 - t2.transpose(0, 1, 3, 2) return (t1, t1), (t2aa, t2, t2aa) + t1, t2 = _to_uccsd(self.t1, self.t2) l1 = l2 = None if self.l1 is not None and self.l2 is not None: @@ -161,8 +177,8 @@ def as_cisd(self, c0=1.0): """In intermediate normalization.""" if self.projector is not None: raise NotImplementedError - c1 = c0*self.t1 - c2 = c0*(self.t2 + einsum('ia,jb->ijab', self.t1, self.t1)) + c1 = c0 * self.t1 + c2 = c0 * (self.t2 + einsum("ia,jb->ijab", self.t1, self.t1)) return wf_types.RCISD_WaveFunction(self.mo, c0, c1, c2, projector=self.projector) def as_ccsd(self): @@ -209,10 +225,9 @@ def rotate_ov(self, to, tv, inplace=False): class UCCSD_WaveFunction(RCCSD_WaveFunction): - # TODO - #_make_rdm1_backend = pyscf.cc.uccsd_rdm.make_rdm1 - #_make_rdm2_backend = pyscf.cc.uccsd_rdm.make_rdm2 + # _make_rdm1_backend = pyscf.cc.uccsd_rdm.make_rdm1 + # _make_rdm2_backend = pyscf.cc.uccsd_rdm.make_rdm2 _make_rdm1_backend = uccsd_rdm.make_rdm1 _make_rdm2_backend = uccsd_rdm.make_rdm2 @@ -238,7 +253,7 @@ def t2ab(self): def t2ba(self): if len(self.t2) == 4: return self.t2[2] - return self.t2ab.transpose(1,0,3,2) + return self.t2ab.transpose(1, 0, 3, 2) @property def t2bb(self): @@ -266,7 +281,7 @@ def l2ab(self): def l2ba(self): if len(self.l2) == 4: return self.l2[2] - return self.l2ab.transpose(1,0,3,2) + return self.l2ab.transpose(1, 0, 3, 2) @property def l2bb(self): @@ -274,9 +289,9 @@ def l2bb(self): def make_rdm2_non_cumulant(self, t_as_lambda=False, ao_basis=False): dm1a, dm1b = self.make_rdm1(t_as_lambda=t_as_lambda, with_mf=False, ao_basis=ao_basis) - dm2aa = (einsum('ij,kl->ijkl', dm1a, dm1a) - einsum('ij,kl->iklj', dm1a, dm1a)) - dm2bb = (einsum('ij,kl->ijkl', dm1b, dm1b) - einsum('ij,kl->iklj', dm1b, dm1b)) - dm2ab = einsum('ij,kl->ijkl', dm1a, dm1b) + dm2aa = einsum("ij,kl->ijkl", dm1a, dm1a) - einsum("ij,kl->iklj", dm1a, dm1a) + dm2bb = einsum("ij,kl->ijkl", dm1b, dm1b) - einsum("ij,kl->iklj", dm1b, dm1b) + dm2ab = einsum("ij,kl->ijkl", dm1a, dm1b) dm2 = (dm2aa, dm2ab, dm2bb) return dm2 @@ -290,7 +305,8 @@ def project(self, projector, inplace=False): return wf def restore(self, projector=None, inplace=False, sym=True): - if projector is None: projector = self.projector + if projector is None: + projector = self.projector wf = self.project((projector[0].T, projector[1].T), inplace=inplace) wf.projector = None if not sym: @@ -307,18 +323,16 @@ def as_mp2(self): def as_cisd(self, c0=1.0): if self.projector is not None: raise NotImplementedError - c1a = c0*self.t1a - c1b = c0*self.t1b - c2aa = c0*(self.t2aa + einsum('ia,jb->ijab', self.t1a, self.t1a) - - einsum('ib,ja->ijab', self.t1a, self.t1a)) - c2bb = c0*(self.t2bb + einsum('ia,jb->ijab', self.t1b, self.t1b) - - einsum('ib,ja->ijab', self.t1b, self.t1b)) - c2ab = c0*(self.t2ab + einsum('ia,jb->ijab', self.t1a, self.t1b)) + c1a = c0 * self.t1a + c1b = c0 * self.t1b + c2aa = c0 * (self.t2aa + einsum("ia,jb->ijab", self.t1a, self.t1a) - einsum("ib,ja->ijab", self.t1a, self.t1a)) + c2bb = c0 * (self.t2bb + einsum("ia,jb->ijab", self.t1b, self.t1b) - einsum("ib,ja->ijab", self.t1b, self.t1b)) + c2ab = c0 * (self.t2ab + einsum("ia,jb->ijab", self.t1a, self.t1b)) c1 = (c1a, c1b) if len(self.t2) == 3: c2 = (c2aa, c2ab, c2bb) elif len(self.t2) == 4: - c2ba = c0*self.t2ba + einsum('ia,jb->ijab', c1b, c1a) + c2ba = c0 * self.t2ba + einsum("ia,jb->ijab", c1b, c1a) c2 = (c2aa, c2ab, c2ba, c2bb) return wf_types.UCISD_WaveFunction(self.mo, c0, c1, c2, projector=self.projector) @@ -326,12 +340,12 @@ def as_fci(self): raise NotImplementedError def multiply(self, factor): - self.t1 = spinalg.multiply(self.t1, len(self.t1)*[factor]) - self.t2 = spinalg.multiply(self.t2, len(self.t2)*[factor]) + self.t1 = spinalg.multiply(self.t1, len(self.t1) * [factor]) + self.t2 = spinalg.multiply(self.t2, len(self.t2) * [factor]) if self.l1 is not None: - self.l1 = spinalg.multiply(self.l1, len(self.l1)*[factor]) + self.l1 = spinalg.multiply(self.l1, len(self.l1) * [factor]) if self.l2 is not None: - self.l2 = spinalg.multiply(self.l2, len(self.l2)*[factor]) + self.l2 = spinalg.multiply(self.l2, len(self.l2) * [factor]) def rotate(self, t, inplace=False): """Rotate wavefunction representation to another basis. @@ -341,6 +355,7 @@ def rotate(self, t, inplace=False): # Allow support for same rotation for alpha and beta. if isinstance(t, np.ndarray) and t.ndim == 2: t = (t, t) + def get_components(tsp, occ): o = occ > 0 v = occ == 0 @@ -369,7 +384,7 @@ def rotate_ov(self, to, tv, inplace=False): """ wf = self if inplace else self.copy() if isinstance(to, np.ndarray) and len(to) == 2: - assert(isinstance(tv, np.ndarray) and len(tv) == 2) + assert isinstance(tv, np.ndarray) and len(tv) == 2 trafo = lambda c: dot(c, block_diag(to, tv)) else: trafo = [lambda c: dot(c, x) for x in (block_diag(to[0], tv[0]), block_diag(to[1], tv[1]))] @@ -383,7 +398,7 @@ def rotate_ov(self, to, tv, inplace=False): wf.l2 = transform_uc2(wf.l2, to, tv) return wf - #def pack(self, dtype=float): + # def pack(self, dtype=float): # """Pack into a single array of data type `dtype`. # Useful for communication via MPI.""" @@ -395,8 +410,8 @@ def rotate_ov(self, to, tv, inplace=False): # pack = pack_arrays(*data, dtype=dtype) # return pack - #@classmethod - #def unpack(cls, packed): + # @classmethod + # def unpack(cls, packed): # """Unpack from a single array of data type `dtype`. # Useful for communication via MPI.""" diff --git a/vayesta/core/types/wf/ccsdtq.py b/vayesta/core/types/wf/ccsdtq.py index c1e4e13ee..af2580292 100644 --- a/vayesta/core/types/wf/ccsdtq.py +++ b/vayesta/core/types/wf/ccsdtq.py @@ -35,10 +35,12 @@ def as_ccsd(self): def as_cisd(self, c0=1.0): return self.as_ccsd().as_cisd() + class UCCSDTQ_WaveFunction(RCCSDTQ_WaveFunction): def _check_amps(self): if not (isinstance(self.t3, tuple) and len(self.t3) == 4): raise ValueError("t4 definition in UCCSDTQ wfn requires tuple of (aaa, aba, bab, bbb) spin signatures") if not (isinstance(self.t4, tuple) and len(self.t4) == 5): raise ValueError( - "t4 definition in UCCSDTQ wfn requires tuple of (aaaa, aaab, abab, abbb, bbbb) spin signatures") + "t4 definition in UCCSDTQ wfn requires tuple of (aaaa, aaab, abab, abbb, bbbb) spin signatures" + ) diff --git a/vayesta/core/types/wf/cisd.py b/vayesta/core/types/wf/cisd.py index eda04f95f..cdd683a5f 100644 --- a/vayesta/core/types/wf/cisd.py +++ b/vayesta/core/types/wf/cisd.py @@ -4,8 +4,14 @@ from vayesta.core import spinalg from vayesta.core.util import callif, einsum from vayesta.core.types import wf as wf_types -from vayesta.core.types.wf.project import (project_c1, project_c2, project_uc1, project_uc2, symmetrize_c2, - symmetrize_uc2) +from vayesta.core.types.wf.project import ( + project_c1, + project_c2, + project_uc1, + project_uc2, + symmetrize_c2, + symmetrize_uc2, +) def CISD_WaveFunction(mo, c0, c1, c2, **kwargs): @@ -17,7 +23,6 @@ def CISD_WaveFunction(mo, c0, c1, c2, **kwargs): class RCISD_WaveFunction(wf_types.WaveFunction): - def __init__(self, mo, c0, c1, c2, projector=None): super().__init__(mo, projector=projector) self.c0 = c0 @@ -32,7 +37,8 @@ def project(self, projector, inplace=False): return wf def restore(self, projector=None, inplace=False, sym=True): - if projector is None: projector = self.projector + if projector is None: + projector = self.projector wf = self.project(projector.T, inplace=inplace) wf.projector = None if not sym: @@ -53,16 +59,16 @@ def as_mp2(self): def as_cisd(self, c0=None): if c0 is None: return self - c1 = self.c1 * c0/self.c0 - c2 = self.c2 * c0/self.c0 + c1 = self.c1 * c0 / self.c0 + c2 = self.c2 * c0 / self.c0 return RCISD_WaveFunction(self.mo, c0, c1, c2, projector=self.projector) def as_ccsd(self): proj = self.projector if proj is not None: self = self.restore() - t1 = self.c1/self.c0 - t2 = self.c2/self.c0 - einsum('ia,jb->ijab', t1, t1) + t1 = self.c1 / self.c0 + t2 = self.c2 / self.c0 - einsum("ia,jb->ijab", t1, t1) l1, l2 = t1, t2 wf = wf_types.RCCSD_WaveFunction(self.mo, t1, t2, l1=l1, l2=l2, projector=self.projector) if proj is not None: @@ -80,7 +86,6 @@ def as_fci(self): class UCISD_WaveFunction(RCISD_WaveFunction): - @property def c1a(self): return self.c1[0] @@ -101,7 +106,7 @@ def c2ab(self): def c2ba(self): if len(self.c2) == 4: return self.c2[2] - return self.c2ab.transpose(1,0,3,2) + return self.c2ab.transpose(1, 0, 3, 2) @property def c2bb(self): @@ -115,7 +120,8 @@ def project(self, projector, inplace=False): return wf def restore(self, projector=None, inplace=False, sym=True): - if projector is None: projector = self.projector + if projector is None: + projector = self.projector wf = self.project((projector[0].T, projector[1].T), inplace=inplace) wf.projector = None if not sym: @@ -129,16 +135,16 @@ def as_mp2(self): def as_cisd(self, c0=None): if c0 is None: return self - c1a = self.c1a * c0/self.c0 - c1b = self.c1b * c0/self.c0 - c2aa = self.c2aa * c0/self.c0 - c2ab = self.c2ab * c0/self.c0 - c2bb = self.c2bb * c0/self.c0 + c1a = self.c1a * c0 / self.c0 + c1b = self.c1b * c0 / self.c0 + c2aa = self.c2aa * c0 / self.c0 + c2ab = self.c2ab * c0 / self.c0 + c2bb = self.c2bb * c0 / self.c0 c1 = (c1a, c1b) if len(self.c2) == 3: c2 = (c2aa, c2ab, c2bb) elif len(self.c2) == 4: - c2ba = self.c2ba * c0/self.c0 + c2ba = self.c2ba * c0 / self.c0 c2 = (c2aa, c2ab, c2ba, c2bb) return wf_types.UCISD_WaveFunction(self.mo, c0, c1, c2, projector=self.projector) @@ -146,16 +152,16 @@ def as_ccsd(self): proj = self.projector if proj is not None: self = self.restore() - t1a = self.c1a/self.c0 - t1b = self.c1b/self.c0 + t1a = self.c1a / self.c0 + t1b = self.c1b / self.c0 t1 = (t1a, t1b) - t2aa = self.c2aa/self.c0 - einsum('ia,jb->ijab', t1a, t1a) + einsum('ib,ja->ijab', t1a, t1a) - t2bb = self.c2bb/self.c0 - einsum('ia,jb->ijab', t1b, t1b) + einsum('ib,ja->ijab', t1b, t1b) - t2ab = self.c2ab/self.c0 - einsum('ia,jb->ijab', t1a, t1b) + t2aa = self.c2aa / self.c0 - einsum("ia,jb->ijab", t1a, t1a) + einsum("ib,ja->ijab", t1a, t1a) + t2bb = self.c2bb / self.c0 - einsum("ia,jb->ijab", t1b, t1b) + einsum("ib,ja->ijab", t1b, t1b) + t2ab = self.c2ab / self.c0 - einsum("ia,jb->ijab", t1a, t1b) if len(self.c2) == 3: t2 = (t2aa, t2ab, t2bb) elif len(self.c2) == 4: - t2ba = self.c2ab/self.c0 - einsum('ia,jb->ijab', t1b, t1a) + t2ba = self.c2ab / self.c0 - einsum("ia,jb->ijab", t1b, t1a) t2 = (t2aa, t2ab, t2ba, t2bb) l1, l2 = t1, t2 wf = wf_types.UCCSD_WaveFunction(self.mo, t1, t2, l1=l1, l2=l2, projector=self.projector) diff --git a/vayesta/core/types/wf/cisdtq.py b/vayesta/core/types/wf/cisdtq.py index b7653fc13..f43ef045b 100644 --- a/vayesta/core/types/wf/cisdtq.py +++ b/vayesta/core/types/wf/cisdtq.py @@ -11,7 +11,6 @@ def CISDTQ_WaveFunction(mo, *args, **kwargs): class RCISDTQ_WaveFunction(wf_types.WaveFunction): - def __init__(self, mo, c0, c1, c2, c3, c4): super().__init__(mo) self.c0 = c0 @@ -37,7 +36,6 @@ def as_ccsdtq(self): class UCISDTQ_WaveFunction(wf_types.WaveFunction): - def __init__(self, mo, c0, c1, c2, c3, c4): super().__init__(mo) self.c0 = c0 @@ -49,7 +47,8 @@ def __init__(self, mo, c0, c1, c2, c3, c4): raise ValueError("c4 definition in UCISDTQ wfn requires tuple of (aaa, aba, bab, bbb) spin signatures") if not (isinstance(c4, tuple) and len(c4) == 5): raise ValueError( - "c4 definition in UCISDTQ wfn requires tuple of (aaaa, aaab, abab, abbb, bbbb) spin signatures") + "c4 definition in UCISDTQ wfn requires tuple of (aaaa, aaab, abab, abbb, bbbb) spin signatures" + ) def as_ccsdtq(self): c1 = tuple(c / self.c0 for c in self.c1) diff --git a/vayesta/core/types/wf/fci.py b/vayesta/core/types/wf/fci.py index c03ef9f25..163fbde2f 100644 --- a/vayesta/core/types/wf/fci.py +++ b/vayesta/core/types/wf/fci.py @@ -6,6 +6,7 @@ import scipy.sparse.linalg from vayesta.core import spinalg + def FCI_WaveFunction(mo, ci, **kwargs): if mo.nspin == 1: cls = RFCI_WaveFunction @@ -15,7 +16,6 @@ def FCI_WaveFunction(mo, ci, **kwargs): class RFCI_WaveFunction(wf_types.WaveFunction): - def __init__(self, mo, ci, projector=None): super().__init__(mo, projector=projector) self.ci = ci @@ -36,21 +36,21 @@ def make_rdm2(self, ao_basis=False, with_dm1=True, approx_cumulant=True): dm1, dm2 = pyscf.fci.direct_spin1.make_rdm12(self.ci, self.norb, self.nelec) if not with_dm1: if not approx_cumulant: - dm2 -= (einsum('ij,kl->ijkl', dm1, dm1) - einsum('ij,kl->iklj', dm1, dm1)/2) - elif (approx_cumulant in (1, True)): + dm2 -= einsum("ij,kl->ijkl", dm1, dm1) - einsum("ij,kl->iklj", dm1, dm1) / 2 + elif approx_cumulant in (1, True): dm1[np.diag_indices(self.nocc)] -= 1 for i in range(self.nocc): - dm2[i,i,:,:] -= 2*dm1 - dm2[:,:,i,i] -= 2*dm1 - dm2[:,i,i,:] += dm1 - dm2[i,:,:,i] += dm1 - elif (approx_cumulant == 2): + dm2[i, i, :, :] -= 2 * dm1 + dm2[:, :, i, i] -= 2 * dm1 + dm2[:, i, i, :] += dm1 + dm2[i, :, :, i] += dm1 + elif approx_cumulant == 2: raise NotImplementedError else: raise ValueError if not ao_basis: return dm2 - return einsum('ijkl,ai,bj,ck,dl->abcd', dm2, *(4*[self.mo.coeff])) + return einsum("ijkl,ai,bj,ck,dl->abcd", dm2, *(4 * [self.mo.coeff])) def project(self, projector, inplace=False): """Apply one-body projector to the FCI wavefunction using pyscf. @@ -75,7 +75,7 @@ def project_occ(self, projector, inplace=False): projector = np.pad(projector, ((0, self.nvir),)).T wf = self.project(projector, inplace) - wf.ci = (2*sum(np.diag(projector)) * ci0 - wf.ci) / c0 + wf.ci = (2 * sum(np.diag(projector)) * ci0 - wf.ci) / c0 # Now just have to divide each coefficient by its excitation level; this corresponds to action of # R^{-1} = (1 + \sum_{i\in occ} i i^+)^{-1} = (N_{elec} + 1 - \sum_{i\in occ} i^+ i)^{-1} @@ -93,8 +93,8 @@ def myop(ci): cishape = wf.ci.shape # Calculate excitation level+1 for all states using this operation. ex_lvl = myop(np.full_like(wf.ci, fill_value=1.0).reshape(-1)).reshape(cishape) - ex_lvl[0,0] = 1.0 - wf.ci = einsum("pq,pq->pq", ex_lvl**(-1), wf.ci) + ex_lvl[0, 0] = 1.0 + wf.ci = einsum("pq,pq->pq", ex_lvl ** (-1), wf.ci) return wf def _apply_onebody(self, proj, ci=None): @@ -106,7 +106,7 @@ def restore(self, projector=None, inplace=False): @property def c0(self): - return self.ci[0,0] + return self.ci[0, 0] def copy(self): proj = callif(spinalg.copy, self.projector) @@ -128,15 +128,15 @@ def as_cisd(self, c0=None): # Change to arrays, in case of empty slice t1addr = np.asarray(t1addr, dtype=int) - c1 = self.ci[0,t1addr] * t1sign - c2 = einsum('i,j,ij->ij', t1sign, t1sign, self.ci[t1addr[:,None],t1addr]) - c1 = c1.reshape(nocc,nvir) - c2 = c2.reshape(nocc,nvir,nocc,nvir).transpose(0,2,1,3) + c1 = self.ci[0, t1addr] * t1sign + c2 = einsum("i,j,ij->ij", t1sign, t1sign, self.ci[t1addr[:, None], t1addr]) + c1 = c1.reshape(nocc, nvir) + c2 = c2.reshape(nocc, nvir, nocc, nvir).transpose(0, 2, 1, 3) if c0 is None: c0 = self.c0 else: - c1 *= c0/self.c0 - c2 *= c0/self.c0 + c1 *= c0 / self.c0 + c2 *= c0 / self.c0 return wf_types.RCISD_WaveFunction(self.mo, c0, c1, c2, projector=self.projector) def as_cisdtq(self, c0=None): @@ -146,11 +146,11 @@ def as_cisdtq(self, c0=None): # For packed 2D arrays ij_pairs = int(nocc * (nocc - 1) / 2) ab_pairs = int(nvir * (nvir - 1) / 2) - ooidx = np.tril_indices(nocc, -1) # second index lower than first - vvidx = np.tril_indices(nvir, -1) # second index lower than first + ooidx = np.tril_indices(nocc, -1) # second index lower than first + vvidx = np.tril_indices(nvir, -1) # second index lower than first # For packed 3D arrays - oooidx = tril_indices_ndim(nocc, 3) # i > j > k - vvvidx = tril_indices_ndim(nvir, 3) # a > b > c + oooidx = tril_indices_ndim(nocc, 3) # i > j > k + vvvidx = tril_indices_ndim(nvir, 3) # a > b > c ijk_pairs = int(nocc * (nocc - 1) * (nocc - 2) / 6) abc_pairs = int(nvir * (nvir - 1) * (nvir - 2) / 6) @@ -165,48 +165,48 @@ def as_cisdtq(self, c0=None): t4addr = np.asarray(t4addr, dtype=int) # === C1 amplitudes === - # These functions extract out the indicies and signs of + # These functions extract out the indicies and signs of # the *same spin* excitations of a given rank from the FCI vector - # C1 are taken to be the beta -> beta excitations (which should be + # C1 are taken to be the beta -> beta excitations (which should be # the same as alpha -> alpha), by taking the first (alpha) index to be doubly occupied. - c1 = self.ci[0,t1addr] * t1sign + c1 = self.ci[0, t1addr] * t1sign c1 = c1.reshape((nocc, nvir)) # === C2 amplitudes === # For RHF, we want the (alpha, beta) -> (alpha, beta) excitation amplitudes. - # Therefore, we can just take single excitations of alpha and + # Therefore, we can just take single excitations of alpha and # combine with the single excitations of beta. - c2 = np.einsum('i,j,ij->ij', t1sign, t1sign, self.ci[t1addr[:, None], t1addr]) + c2 = np.einsum("i,j,ij->ij", t1sign, t1sign, self.ci[t1addr[:, None], t1addr]) c2 = c2.reshape((nocc, nvir, nocc, nvir)) c2 = c2.transpose(0, 2, 1, 3) # === C3 amplitudes === - # For the C3 amplitudes, we want to find the ijk -> abc amplitudes of + # For the C3 amplitudes, we want to find the ijk -> abc amplitudes of # spin signature (alpha, beta, alpha) -> (alpha, beta, alpha) - c3 = np.einsum('i,j,ij->ij', t2sign, t1sign, self.ci[t2addr[:, None], t1addr]) + c3 = np.einsum("i,j,ij->ij", t2sign, t1sign, self.ci[t2addr[:, None], t1addr]) c3 = decompress_axes("iiaajb", c3, shape=(nocc, nocc, nvir, nvir, nocc, nvir)) c3 = c3.transpose(0, 4, 1, 2, 5, 3) # === C4 amplitudes === # For the C4 amplitudes, ijkl -> abcd, we are going to store two different spin - # signatures: + # signatures: # (alpha, beta, alpha, beta) -> (alpha, beta, alpha, beta) and # (alpha, beta, alpha, alpha) -> (alpha, beta, alpha, alpha) - c4_abaa = np.einsum('i,j,ij->ij', t3sign, t1sign, self.ci[t3addr[:, None], t1addr]) + c4_abaa = np.einsum("i,j,ij->ij", t3sign, t1sign, self.ci[t3addr[:, None], t1addr]) c4_abaa = decompress_axes("iiiaaajb", c4_abaa, shape=(nocc, nocc, nocc, nvir, nvir, nvir, nocc, nvir)) c4_abaa = c4_abaa.transpose(0, 6, 2, 1, 3, 7, 5, 4) - c4_abab = np.einsum('i,j,ij->ij', t2sign, t2sign, self.ci[t2addr[:, None], t2addr]) + c4_abab = np.einsum("i,j,ij->ij", t2sign, t2sign, self.ci[t2addr[:, None], t2addr]) c4_abab = decompress_axes("iiaajjbb", c4_abab, shape=(nocc, nocc, nvir, nvir, nocc, nocc, nvir, nvir)) c4_abab = c4_abab.transpose(0, 4, 1, 5, 2, 6, 3, 7) if c0 is None: c0 = self.c0 else: - c1 *= c0/self.c0 - c2 *= c0/self.c0 - c3 *= c0/self.c0 - c4_abab *= c0/self.c0 - c4_abaa *= c0/self.c0 + c1 *= c0 / self.c0 + c2 *= c0 / self.c0 + c3 *= c0 / self.c0 + c4_abab *= c0 / self.c0 + c4_abaa *= c0 / self.c0 return wf_types.RCISDTQ_WaveFunction(self.mo, c0, c1, c2, c3, (c4_abaa, c4_abab)) @@ -221,53 +221,53 @@ def as_fci(self): class UFCI_WaveFunction(RFCI_WaveFunction): - def make_rdm1(self, ao_basis=False, with_mf=True): - assert (self.norb[0] == self.norb[1]) + assert self.norb[0] == self.norb[1] dm1 = pyscf.fci.direct_spin1.make_rdm1s(self.ci, self.norb[0], self.nelec) if not with_mf: dm1[0][np.diag_indices(self.nocc[0])] -= 1 dm1[1][np.diag_indices(self.nocc[1])] -= 1 if not ao_basis: return dm1 - return (dot(self.mo.coeff[0], dm1[0], self.mo.coeff[0].T), - dot(self.mo.coeff[1], dm1[1], self.mo.coeff[1].T)) + return (dot(self.mo.coeff[0], dm1[0], self.mo.coeff[0].T), dot(self.mo.coeff[1], dm1[1], self.mo.coeff[1].T)) def make_rdm2(self, ao_basis=False, with_dm1=True, approx_cumulant=True): - assert (self.norb[0] == self.norb[1]) + assert self.norb[0] == self.norb[1] dm1, dm2 = pyscf.fci.direct_spin1.make_rdm12s(self.ci, self.norb[0], self.nelec) if not with_dm1: dm1a, dm1b = dm1 dm2aa, dm2ab, dm2bb = dm2 if not approx_cumulant: - dm2aa -= (einsum('ij,kl->ijkl', dm1a, dm1a) - einsum('ij,kl->iklj', dm1a, dm1a)) - dm2bb -= (einsum('ij,kl->ijkl', dm1b, dm1b) - einsum('ij,kl->iklj', dm1b, dm1b)) - dm2ab -= einsum('ij,kl->ijkl', dm1a, dm1b) - elif (approx_cumulant in (1, True)): + dm2aa -= einsum("ij,kl->ijkl", dm1a, dm1a) - einsum("ij,kl->iklj", dm1a, dm1a) + dm2bb -= einsum("ij,kl->ijkl", dm1b, dm1b) - einsum("ij,kl->iklj", dm1b, dm1b) + dm2ab -= einsum("ij,kl->ijkl", dm1a, dm1b) + elif approx_cumulant in (1, True): dm1a[np.diag_indices(self.nocca)] -= 0.5 dm1b[np.diag_indices(self.noccb)] -= 0.5 for i in range(self.nocca): - dm2aa[i,i,:,:] -= dm1a - dm2aa[:,:,i,i] -= dm1a - dm2aa[:,i,i,:] += dm1a - dm2aa[i,:,:,i] += dm1a - dm2ab[i,i,:,:] -= dm1b + dm2aa[i, i, :, :] -= dm1a + dm2aa[:, :, i, i] -= dm1a + dm2aa[:, i, i, :] += dm1a + dm2aa[i, :, :, i] += dm1a + dm2ab[i, i, :, :] -= dm1b for i in range(self.noccb): - dm2bb[i,i,:,:] -= dm1b - dm2bb[:,:,i,i] -= dm1b - dm2bb[:,i,i,:] += dm1b - dm2bb[i,:,:,i] += dm1b - dm2ab[:,:,i,i] -= dm1a - elif (approx_cumulant == 2): + dm2bb[i, i, :, :] -= dm1b + dm2bb[:, :, i, i] -= dm1b + dm2bb[:, i, i, :] += dm1b + dm2bb[i, :, :, i] += dm1b + dm2ab[:, :, i, i] -= dm1a + elif approx_cumulant == 2: raise NotImplementedError else: raise ValueError if not ao_basis: return dm2 moa, mob = self.mo.coeff - return (einsum('ijkl,ai,bj,ck,dl->abcd', dm2[0], *(4*[moa])), - einsum('ijkl,ai,bj,ck,dl->abcd', dm2[1], *[moa, moa, mob, mob]), - einsum('ijkl,ai,bj,ck,dl->abcd', dm2[2], *(4*[mob]))) + return ( + einsum("ijkl,ai,bj,ck,dl->abcd", dm2[0], *(4 * [moa])), + einsum("ijkl,ai,bj,ck,dl->abcd", dm2[1], *[moa, moa, mob, mob]), + einsum("ijkl,ai,bj,ck,dl->abcd", dm2[2], *(4 * [mob])), + ) def project_occ(self, projector, inplace=False): """Apply projector onto the occupied indices of all CI coefficient tensors. @@ -292,7 +292,9 @@ def project_occ(self, projector, inplace=False): # which could be obtained straightforwardly by solving # Rx = a. # In practice, it is more stable to just compute the excitation level of each state and divide by it. - mf_vdensity_op = tuple([np.eye(self.norb[i]) - np.pad(np.eye(self.nocc[i]), ((0, self.nvir[i]),)) for i in [0, 1]]) + mf_vdensity_op = tuple( + [np.eye(self.norb[i]) - np.pad(np.eye(self.nocc[i]), ((0, self.nvir[i]),)) for i in [0, 1]] + ) # Set up sparse LinearOperator object to apply the hole counting operator to the FCI string. def myop(ci): @@ -301,13 +303,13 @@ def myop(ci): cishape = wf.ci.shape # Calculate excitation level+1 for all states using this operation. ex_lvl = myop(np.full_like(wf.ci, fill_value=1.0).reshape(-1)).reshape(cishape) - ex_lvl[0,0] = 1.0 - wf.ci = einsum("pq,pq->pq", ex_lvl**(-1), wf.ci) + ex_lvl[0, 0] = 1.0 + wf.ci = einsum("pq,pq->pq", ex_lvl ** (-1), wf.ci) return wf def _apply_onebody(self, proj, ci=None): ci = self.ci if ci is None else ci - assert(self.norb[0] == self.norb[1]) + assert self.norb[0] == self.norb[1] return pyscf.fci.direct_uhf.contract_1e(proj, ci, self.norb[0], self.nelec) def as_cisd(self, c0=None): @@ -329,28 +331,28 @@ def as_cisd(self, c0=None): na = pyscf.fci.cistring.num_strings(norba, nocca) nb = pyscf.fci.cistring.num_strings(norbb, noccb) - ci = self.ci.reshape(na,nb) - c1a = (self.ci[t1addra,0] * t1signa).reshape(nocca,nvira) - c1b = (self.ci[0,t1addrb] * t1signb).reshape(noccb,nvirb) + ci = self.ci.reshape(na, nb) + c1a = (self.ci[t1addra, 0] * t1signa).reshape(nocca, nvira) + c1b = (self.ci[0, t1addrb] * t1signb).reshape(noccb, nvirb) - nocca_comp = nocca*(nocca-1)//2 - noccb_comp = noccb*(noccb-1)//2 - nvira_comp = nvira*(nvira-1)//2 - nvirb_comp = nvirb*(nvirb-1)//2 - c2aa = (self.ci[t2addra,0] * t2signa).reshape(nocca_comp, nvira_comp) - c2bb = (self.ci[0,t2addrb] * t2signb).reshape(noccb_comp, nvirb_comp) + nocca_comp = nocca * (nocca - 1) // 2 + noccb_comp = noccb * (noccb - 1) // 2 + nvira_comp = nvira * (nvira - 1) // 2 + nvirb_comp = nvirb * (nvirb - 1) // 2 + c2aa = (self.ci[t2addra, 0] * t2signa).reshape(nocca_comp, nvira_comp) + c2bb = (self.ci[0, t2addrb] * t2signb).reshape(noccb_comp, nvirb_comp) c2aa = pyscf.cc.ccsd._unpack_4fold(c2aa, nocca, nvira) c2bb = pyscf.cc.ccsd._unpack_4fold(c2bb, noccb, nvirb) - c2ab = einsum('i,j,ij->ij', t1signa, t1signb, self.ci[t1addra[:,None],t1addrb]) - c2ab = c2ab.reshape(nocca,nvira,noccb,nvirb).transpose(0,2,1,3) + c2ab = einsum("i,j,ij->ij", t1signa, t1signb, self.ci[t1addra[:, None], t1addrb]) + c2ab = c2ab.reshape(nocca, nvira, noccb, nvirb).transpose(0, 2, 1, 3) if c0 is None: c0 = self.c0 else: - c1a *= c0/self.c0 - c1b *= c0/self.c0 - c2aa *= c0/self.c0 - c2ab *= c0/self.c0 - c2bb *= c0/self.c0 + c1a *= c0 / self.c0 + c1b *= c0 / self.c0 + c2aa *= c0 / self.c0 + c2ab *= c0 / self.c0 + c2bb *= c0 / self.c0 c1 = (c1a, c1b) c2 = (c2aa, c2ab, c2bb) return wf_types.UCISD_WaveFunction(self.mo, c0, c1, c2, projector=self.projector) @@ -358,26 +360,26 @@ def as_cisd(self, c0=None): def as_cisdtq(self, c0=None): if self.projector is not None: raise NotImplementedError - + norba, norbb = self.norb nocca, noccb = self.nocc nvira, nvirb = self.nvir - + ij_pairs_a = int(nocca * (nocca - 1) / 2) ab_pairs_a = int(nvira * (nvira - 1) / 2) ij_pairs_b = int(noccb * (noccb - 1) / 2) ab_pairs_b = int(nvirb * (nvirb - 1) / 2) - ooidx_a = np.tril_indices(nocca, -1) # second index lower than first - vvidx_a = np.tril_indices(nvira, -1) # second index lower than first - ooidx_b = np.tril_indices(noccb, -1) # second index lower than first - vvidx_b = np.tril_indices(nvirb, -1) # second index lower than first + ooidx_a = np.tril_indices(nocca, -1) # second index lower than first + vvidx_a = np.tril_indices(nvira, -1) # second index lower than first + ooidx_b = np.tril_indices(noccb, -1) # second index lower than first + vvidx_b = np.tril_indices(nvirb, -1) # second index lower than first # For packed 3D arrays - oooidx_a = tril_indices_ndim(nocca, 3) # i > j > k - vvvidx_a = tril_indices_ndim(nvira, 3) # a > b > c + oooidx_a = tril_indices_ndim(nocca, 3) # i > j > k + vvvidx_a = tril_indices_ndim(nvira, 3) # a > b > c ijk_pairs_a = int(nocca * (nocca - 1) * (nocca - 2) / 6) abc_pairs_a = int(nvira * (nvira - 1) * (nvira - 2) / 6) - oooidx_b = tril_indices_ndim(noccb, 3) # i > j > k - vvvidx_b = tril_indices_ndim(nvirb, 3) # a > b > c + oooidx_b = tril_indices_ndim(noccb, 3) # i > j > k + vvvidx_b = tril_indices_ndim(nvirb, 3) # a > b > c ijk_pairs_b = int(noccb * (noccb - 1) * (noccb - 2) / 6) abc_pairs_b = int(nvirb * (nvirb - 1) * (nvirb - 2) / 6) @@ -409,51 +411,51 @@ def as_cisdtq(self, c0=None): nb = pyscf.fci.cistring.num_strings(norbb, noccb) # C1 - c1_a = (self.ci[t1addra,0] * t1signa).reshape(nocca,nvira) - c1_b = (self.ci[0,t1addrb] * t1signb).reshape(noccb,nvirb) + c1_a = (self.ci[t1addra, 0] * t1signa).reshape(nocca, nvira) + c1_b = (self.ci[0, t1addrb] * t1signb).reshape(noccb, nvirb) # C2 - c2_aa = (self.ci[t2addra,0] * t2signa).reshape(ij_pairs_a, ab_pairs_a) + c2_aa = (self.ci[t2addra, 0] * t2signa).reshape(ij_pairs_a, ab_pairs_a) c2_aa = pyscf.cc.ccsd._unpack_4fold(c2_aa, nocca, nvira) - c2_bb = (self.ci[0,t2addrb] * t2signb).reshape(ij_pairs_b, ab_pairs_b) + c2_bb = (self.ci[0, t2addrb] * t2signb).reshape(ij_pairs_b, ab_pairs_b) c2_bb = pyscf.cc.ccsd._unpack_4fold(c2_bb, noccb, nvirb) - c2_ab = einsum('i,j,ij->ij', t1signa, t1signb, self.ci[t1addra[:, None], t1addrb]) + c2_ab = einsum("i,j,ij->ij", t1signa, t1signb, self.ci[t1addra[:, None], t1addrb]) c2_ab = c2_ab.reshape(nocca, nvira, noccb, nvirb) c2_ab = c2_ab.transpose(0, 2, 1, 3) # C3 - c3_aaa = (self.ci[t3addra,0] * t3signa).reshape(ijk_pairs_a, abc_pairs_a) + c3_aaa = (self.ci[t3addra, 0] * t3signa).reshape(ijk_pairs_a, abc_pairs_a) c3_aaa = decompress_axes("iiiaaa", c3_aaa, shape=(nocca, nocca, nocca, nvira, nvira, nvira)) - c3_bbb = (self.ci[0,t3addrb] * t3signb).reshape(ijk_pairs_b, abc_pairs_b) + c3_bbb = (self.ci[0, t3addrb] * t3signb).reshape(ijk_pairs_b, abc_pairs_b) c3_bbb = decompress_axes("iiiaaa", c3_bbb, shape=(noccb, noccb, noccb, nvirb, nvirb, nvirb)) - c3_aba = np.einsum('i,j,ij->ij', t2signa, t1signb, self.ci[t2addra[:, None], t1addrb]) + c3_aba = np.einsum("i,j,ij->ij", t2signa, t1signb, self.ci[t2addra[:, None], t1addrb]) c3_aba = decompress_axes("iiaajb", c3_aba, shape=(nocca, nocca, nvira, nvira, noccb, nvirb)) c3_aba = c3_aba.transpose(0, 4, 1, 2, 5, 3) - c3_bab = np.einsum('i,j,ij->ij', t1signa, t2signb, self.ci[t1addra[:, None], t2addrb]) + c3_bab = np.einsum("i,j,ij->ij", t1signa, t2signb, self.ci[t1addra[:, None], t2addrb]) c3_bab = decompress_axes("iajjbb", c3_bab, shape=(nocca, nvira, noccb, noccb, nvirb, nvirb)) c3_bab = c3_bab.transpose(2, 0, 3, 4, 1, 5) # C4 - c4_aaaa = (self.ci[t4addra,0] * t4signa).reshape(ijkl_pairs_a, abcd_pairs_a) + c4_aaaa = (self.ci[t4addra, 0] * t4signa).reshape(ijkl_pairs_a, abcd_pairs_a) c4_aaaa = decompress_axes("iiiiaaaa", c4_aaaa, shape=(nocca, nocca, nocca, nocca, nvira, nvira, nvira, nvira)) - c4_bbbb = (self.ci[0,t4addrb] * t4signb).reshape(ijkl_pairs_b, abcd_pairs_b) + c4_bbbb = (self.ci[0, t4addrb] * t4signb).reshape(ijkl_pairs_b, abcd_pairs_b) c4_bbbb = decompress_axes("iiiiaaaa", c4_bbbb, shape=(noccb, noccb, noccb, noccb, nvirb, nvirb, nvirb, nvirb)) - c4_aaab = np.einsum('i,j,ij->ij', t3signa, t1signb, self.ci[t3addra[:,None], t1addrb]) + c4_aaab = np.einsum("i,j,ij->ij", t3signa, t1signb, self.ci[t3addra[:, None], t1addrb]) c4_aaab = decompress_axes("iiiaaajb", c4_aaab, shape=(nocca, nocca, nocca, nvira, nvira, nvira, noccb, nvirb)) c4_aaab = c4_aaab.transpose(0, 1, 2, 6, 3, 4, 5, 7) - c4_abab = np.einsum('i,j,ij->ij', t2signa, t2signb, self.ci[t2addra[:,None], t2addrb]) + c4_abab = np.einsum("i,j,ij->ij", t2signa, t2signb, self.ci[t2addra[:, None], t2addrb]) c4_abab = decompress_axes("iiaajjbb", c4_abab, shape=(nocca, nocca, nvira, nvira, noccb, noccb, nvirb, nvirb)) c4_abab = c4_abab.transpose(0, 4, 1, 5, 2, 6, 3, 7) - c4_abbb = np.einsum('i,j,ij->ij', t1signa, t3signb, self.ci[t1addra[:,None], t3addrb]) + c4_abbb = np.einsum("i,j,ij->ij", t1signa, t3signb, self.ci[t1addra[:, None], t3addrb]) c4_abbb = decompress_axes("iajjjbbb", c4_abbb, shape=(nocca, nvira, noccb, noccb, noccb, nvirb, nvirb, nvirb)) c4_abbb = c4_abbb.transpose(0, 2, 3, 4, 1, 5, 6, 7) @@ -495,9 +497,11 @@ def __init__(self, mo, ci, dummy_orbs, projector=None): raise NotImplementedError("Only dummy virtual orbitals are supported.") norb = np.array(self.ndummy) + np.array(self.norb) if norb[0] != norb[1]: - raise RuntimeError("Including padded orbitals doesn't match the number of orbitals in each spin channel!" - " %d != %d (%d + %d != %d + %d)" % (norb[0], norb[1], self.ndummy[0], self.norb[0], - self.ndummy[1], self.norb[1])) + raise RuntimeError( + "Including padded orbitals doesn't match the number of orbitals in each spin channel!" + " %d != %d (%d + %d != %d + %d)" + % (norb[0], norb[1], self.ndummy[0], self.norb[0], self.ndummy[1], self.norb[1]) + ) @property def ndummy(self): @@ -524,7 +528,9 @@ def _phys_ind_orbs(self): return [np.array([i for i in range(y) if i not in x]) for x, y in zip(self.dummy_orbs, self.norb)] def _phys_ind_vir(self): - return [np.array([i for i in range(y) if i + z not in x]) for x, y, z in zip(self.dummy_orbs, self.nvir, self.nocc)] + return [ + np.array([i for i in range(y) if i + z not in x]) for x, y, z in zip(self.dummy_orbs, self.nvir, self.nocc) + ] def make_rdm1(self, ao_basis=False, *args, **kwargs): with replace_attr(self, mo=self.dummy_mo): @@ -581,7 +587,12 @@ def vsl(a, sl): c3 = (vsl(c3aaa, [va, va, va]), vsl(c3aba, [va, vb, va]), vsl(c3bab, [vb, va, vb]), vsl(c3bbb, [vb, vb, vb])) - c4 = (vsl(c4aaaa, [va, va, va, va]), vsl(c4aaab, [va, va, va, vb]), vsl(c4abab, [va, vb, va, vb]), - vsl(c4abbb, [va, vb, vb, vb]), vsl(c4bbbb, [vb, vb, vb, vb])) + c4 = ( + vsl(c4aaaa, [va, va, va, va]), + vsl(c4aaab, [va, va, va, vb]), + vsl(c4abab, [va, vb, va, vb]), + vsl(c4abbb, [va, vb, vb, vb]), + vsl(c4bbbb, [vb, vb, vb, vb]), + ) return wf_types.UCISDTQ_WaveFunction(self.mo, wf_cisdtq.c0, c1, c2, c3, c4) diff --git a/vayesta/core/types/wf/hf.py b/vayesta/core/types/wf/hf.py index 57a17ddc6..47c1310a6 100644 --- a/vayesta/core/types/wf/hf.py +++ b/vayesta/core/types/wf/hf.py @@ -12,18 +12,19 @@ def HF_WaveFunction(mo): class RHF_WaveFunction(wf_types.WaveFunction): - def make_rdm1(self, mo_coeff=None, mo_occ=None, ao_basis=True): - if mo_occ is None: mo_occ = self.mo.occ + if mo_occ is None: + mo_occ = self.mo.occ if not ao_basis: return np.diag(mo_occ) - if mo_coeff is None: mo_coeff = self.mo.coeff - occ = (mo_occ > 0) - return np.dot(mo_coeff[:,occ]*mo_occ[occ], mo_coeff[:,occ].T) + if mo_coeff is None: + mo_coeff = self.mo.coeff + occ = mo_occ > 0 + return np.dot(mo_coeff[:, occ] * mo_occ[occ], mo_coeff[:, occ].T) def make_rdm2(self, mo_coeff=None, mo_occ=None, ao_basis=True): dm1 = self.make_rdm1(mo_coeff=mo_coeff, mo_occ=mo_occ, ao_basis=ao_basis) - dm2 = einsum('ij,kl->ijkl', dm1, dm1) - einsum('ij,kl->iklj', dm1, dm1)/2 + dm2 = einsum("ij,kl->ijkl", dm1, dm1) - einsum("ij,kl->iklj", dm1, dm1) / 2 return dm2 def as_restricted(self): @@ -34,19 +35,20 @@ def as_unrestricted(self): class UHF_WaveFunction(RHF_WaveFunction): - def make_rdm1(self, mo_coeff=None, mo_occ=None, ao_basis=True): - if mo_coeff is None: mo_coeff = self.mo.coeff - if mo_occ is None: mo_occ = self.mo.occ + if mo_coeff is None: + mo_coeff = self.mo.coeff + if mo_occ is None: + mo_occ = self.mo.occ dm1a = super().make_rdm1(mo_coeff=mo_coeff[0], mo_occ=mo_occ[0], ao_basis=ao_basis) dm1b = super().make_rdm1(mo_coeff=mo_coeff[1], mo_occ=mo_occ[1], ao_basis=ao_basis) return (dm1a, dm1b) def make_rdm2(self, mo_coeff=None, mo_occ=None, ao_basis=True): dm1a, dm1b = self.make_rdm1(mo_coeff=mo_coeff, mo_occ=mo_occ, ao_basis=ao_basis) - dm2aa = einsum('ij,kl->ijkl', dm1a, dm1a) - einsum('ij,kl->iklj', dm1a, dm1a) - dm2bb = einsum('ij,kl->ijkl', dm1b, dm1b) - einsum('ij,kl->iklj', dm1b, dm1b) - dm2ab = einsum('ij,kl->ijkl', dm1a, dm1b) + dm2aa = einsum("ij,kl->ijkl", dm1a, dm1a) - einsum("ij,kl->iklj", dm1a, dm1a) + dm2bb = einsum("ij,kl->ijkl", dm1b, dm1b) - einsum("ij,kl->iklj", dm1b, dm1b) + dm2ab = einsum("ij,kl->ijkl", dm1a, dm1b) return (dm2aa, dm2ab, dm2bb) def as_restricted(self): diff --git a/vayesta/core/types/wf/mp2.py b/vayesta/core/types/wf/mp2.py index 5c097a3ca..7da5511be 100644 --- a/vayesta/core/types/wf/mp2.py +++ b/vayesta/core/types/wf/mp2.py @@ -16,44 +16,43 @@ def MP2_WaveFunction(mo, t2, **kwargs): class RMP2_WaveFunction(wf_types.WaveFunction): - def __init__(self, mo, t2, projector=None): super().__init__(mo, projector=projector) self.t2 = t2 def make_rdm1(self, with_mf=True, ao_basis=False): t2 = self.t2 - doo = -(2*einsum('ikab,jkab->ij', t2, t2) - einsum('ikab,kjab->ij', t2, t2)) - dvv = 2*einsum('ijac,ijbc->ab', t2, t2) - einsum('ijac,ijcb->ab', t2, t2) + doo = -(2 * einsum("ikab,jkab->ij", t2, t2) - einsum("ikab,kjab->ij", t2, t2)) + dvv = 2 * einsum("ijac,ijbc->ab", t2, t2) - einsum("ijac,ijcb->ab", t2, t2) if with_mf: doo += np.eye(self.nocc) dm1 = np.zeros((self.norb, self.norb)) - occ, vir = np.s_[:self.nocc], np.s_[self.nocc:] - dm1[occ,occ] = doo + doo.T - dm1[vir,vir] = dvv + dvv.T + occ, vir = np.s_[: self.nocc], np.s_[self.nocc :] + dm1[occ, occ] = doo + doo.T + dm1[vir, vir] = dvv + dvv.T if ao_basis: dm1 = dot(self.mo.coeff, dm1, self.mo.coeff.T) return dm1 def make_rdm2(self, with_dm1=True, ao_basis=False, approx_cumulant=True): dm2 = np.zeros((self.norb, self.norb, self.norb, self.norb)) - occ, vir = np.s_[:self.nocc], np.s_[self.nocc:] - dovov = 4*self.t2.transpose(0,2,1,3) - 2*self.t2.transpose(0,3,1,2) - dm2[occ,vir,occ,vir] = dovov - dm2[vir,occ,vir,occ] = dovov.transpose(1,0,3,2) + occ, vir = np.s_[: self.nocc], np.s_[self.nocc :] + dovov = 4 * self.t2.transpose(0, 2, 1, 3) - 2 * self.t2.transpose(0, 3, 1, 2) + dm2[occ, vir, occ, vir] = dovov + dm2[vir, occ, vir, occ] = dovov.transpose(1, 0, 3, 2) if with_dm1: dm1 = self.make_rdm1(with_mf=False) dm1[np.diag_indices(self.nocc)] += 1 for i in range(self.nocc): - dm2[i,i,:,:] += 2*dm1 - dm2[:,:,i,i] += 2*dm1 - dm2[:,i,i,:] -= dm1 - dm2[i,:,:,i] -= dm1 + dm2[i, i, :, :] += 2 * dm1 + dm2[:, :, i, i] += 2 * dm1 + dm2[:, i, i, :] -= dm1 + dm2[i, :, :, i] -= dm1 else: if int(approx_cumulant) != 1: raise NotImplementedError if ao_basis: - dm2 = einsum('ijkl,ai,bj,ck,dl->abcd', dm2, *(4*[self.mo.coeff])) + dm2 = einsum("ijkl,ai,bj,ck,dl->abcd", dm2, *(4 * [self.mo.coeff])) return dm2 def as_restricted(self): @@ -62,7 +61,7 @@ def as_restricted(self): def as_unrestricted(self): mo = self.mo.to_spin_orbitals() t2 = self.t2.copy() - t2aa = (t2 - t2.transpose(0,1,3,2)) + t2aa = t2 - t2.transpose(0, 1, 3, 2) t2 = (t2aa, t2, t2aa) return UMP2_WaveFunction(mo, t2) @@ -76,7 +75,8 @@ def project(self, projector, inplace=False): return wf def restore(self, projector=None, inplace=False, sym=True): - if projector is None: projector = self.projector + if projector is None: + projector = self.projector wf = self.project(projector.T, inplace=inplace) wf.projector = None if not sym: @@ -90,7 +90,7 @@ def as_mp2(self): def as_cisd(self, c0=1.0): nocc1 = self.t2.shape[0] c1 = np.zeros((nocc1, self.nvir)) - c2 = c0*self.t2 + c2 = c0 * self.t2 return wf_types.RCISD_WaveFunction(self.mo, c0, c1, c2, projector=self.projector) def as_ccsd(self): @@ -126,7 +126,6 @@ def unpack(cls, packed): class UMP2_WaveFunction(RMP2_WaveFunction): - @property def t2aa(self): return self.t2[0] @@ -139,7 +138,7 @@ def t2ab(self): def t2ba(self): if len(self.t2) == 4: return self.t2[2] - return self.t2ab.transpose(1,0,3,2) + return self.t2ab.transpose(1, 0, 3, 2) @property def t2bb(self): @@ -158,7 +157,8 @@ def project(self, projector, inplace=False): return wf def restore(self, projector=None, inplace=False, sym=True): - if projector is None: projector = self.projector + if projector is None: + projector = self.projector wf = self.project((projector[0].T, projector[1].T), inplace=inplace) wf.projector = None if not sym: @@ -172,27 +172,25 @@ def as_mp2(self): def as_cisd(self, c0=1.0): nocc1a = self.t2aa.shape[0] nocc1b = self.t2bb.shape[0] - c1 = (np.zeros((nocc1a, self.nvira)), - np.zeros((nocc1b, self.nvirb))) - c2aa = c0*self.t2aa - c2ab = c0*self.t2ab - c2bb = c0*self.t2bb + c1 = (np.zeros((nocc1a, self.nvira)), np.zeros((nocc1b, self.nvirb))) + c2aa = c0 * self.t2aa + c2ab = c0 * self.t2ab + c2bb = c0 * self.t2bb if len(self.t2) == 3: c2 = (c2aa, c2ab, c2bb) elif len(self.t2) == 4: - c2ba = c0*self.t2ba + c2ba = c0 * self.t2ba c2 = (c2aa, c2ab, c2ba, c2bb) return wf_types.UCISD_WaveFunction(self.mo, c0, c1, c2, projector=self.projector) def as_ccsd(self): nocc1a = self.t2aa.shape[0] nocc1b = self.t2bb.shape[0] - t1 = (np.zeros((nocc1a, self.nvira)), - np.zeros((nocc1b, self.nvirb))) + t1 = (np.zeros((nocc1a, self.nvira)), np.zeros((nocc1b, self.nvirb))) return wf_types.UCCSD_WaveFunction(self.mo, t1, self.t2, l1=t1, l2=self.t2, projector=self.projector) def as_fci(self): return NotImplementedError def multiply(self, factor): - self.t2 = spinalg.multiply(self.t2, len(self.t2)*[factor]) + self.t2 = spinalg.multiply(self.t2, len(self.t2) * [factor]) diff --git a/vayesta/core/types/wf/project.py b/vayesta/core/types/wf/project.py index c9de15a99..15bd3439a 100644 --- a/vayesta/core/types/wf/project.py +++ b/vayesta/core/types/wf/project.py @@ -5,37 +5,51 @@ def project_c1(c1, p): - if c1 is None: return None - if p is None: return c1 + if c1 is None: + return None + if p is None: + return c1 return np.dot(p, c1) + def project_c2(c2, p): - if c2 is None: return None - if p is None: return c2 + if c2 is None: + return None + if p is None: + return c2 return np.tensordot(p, c2, axes=1) + def project_uc1(c1, p): - if c1 is None: return None - if p is None: return c1 - return (project_c1(c1[0], p[0]), - project_c1(c1[1], p[1])) + if c1 is None: + return None + if p is None: + return c1 + return (project_c1(c1[0], p[0]), project_c1(c1[1], p[1])) + def project_uc2(c2, p): - if c2 is None: return None - if p is None: return c2 - c2ba = (c2[2] if len(c2) == 4 else c2[1].transpose(1,0,3,2)) - return (project_c2(c2[0], p[0]), - project_c2(c2[1], p[0]), - #einsum('xi,ij...->ix...', p[1], c2[1]), - project_c2(c2ba, p[1]), - project_c2(c2[-1], p[1])) + if c2 is None: + return None + if p is None: + return c2 + c2ba = c2[2] if len(c2) == 4 else c2[1].transpose(1, 0, 3, 2) + return ( + project_c2(c2[0], p[0]), + project_c2(c2[1], p[0]), + # einsum('xi,ij...->ix...', p[1], c2[1]), + project_c2(c2ba, p[1]), + project_c2(c2[-1], p[1]), + ) + def symmetrize_c2(c2, inplace=True): if not inplace: c2 = c2.copy() - c2 = (c2 + c2.transpose(1,0,3,2))/2 + c2 = (c2 + c2.transpose(1, 0, 3, 2)) / 2 return c2 + def symmetrize_uc2(c2, inplace=True): if not inplace: c2 = tuple(x.copy() for x in c2) @@ -44,27 +58,36 @@ def symmetrize_uc2(c2, inplace=True): # Mixed spin: c2ab = c2[1] if len(c2) == 4: - c2ab = (c2ab + c2[2].transpose(1,0,3,2))/2 + c2ab = (c2ab + c2[2].transpose(1, 0, 3, 2)) / 2 return (c2aa, c2ab, c2bb) + def transform_c1(c1, to, tv): - if c1 is None: return None + if c1 is None: + return None return dot(to.T, c1, tv) + def transform_c2(c2, to, tv, to2=None, tv2=None): - if c2 is None: return None - if to2 is None: to2 = to - if tv2 is None: tv2 = tv + if c2 is None: + return None + if to2 is None: + to2 = to + if tv2 is None: + tv2 = tv # Use einsum for now- tensordot would be faster but less readable. return einsum("ijab,iI,jJ,aA,bB->IJAB", c2, to, to2, tv, tv2) + def transform_uc1(c1, to, tv): - if c1 is None: return None - return (transform_c1(c1[0], to[0], tv[0]), - transform_c1(c1[1], to[1], tv[1])) + if c1 is None: + return None + return (transform_c1(c1[0], to[0], tv[0]), transform_c1(c1[1], to[1], tv[1])) + def transform_uc2(c2, to, tv): - if c2 is None: return None + if c2 is None: + return None c2aa = transform_c2(c2[0], to[0], tv[0]) c2ab = transform_c2(c2[1], to[0], tv[0], to[1], tv[1]) c2bb = transform_c2(c2[-1], to[1], tv[1]) diff --git a/vayesta/core/types/wf/t_to_c.py b/vayesta/core/types/wf/t_to_c.py index b412a1d0a..d34f418af 100644 --- a/vayesta/core/types/wf/t_to_c.py +++ b/vayesta/core/types/wf/t_to_c.py @@ -6,6 +6,7 @@ import numpy as np from vayesta.core.util import einsum + def t1_uhf(c1): c1_aa, c1_bb = c1 nocc = (c1_aa.shape[0], c1_bb.shape[0]) @@ -16,12 +17,14 @@ def t1_uhf(c1): t1_bb += einsum("ia->ia", c1_bb) return t1_aa, t1_bb + def t1_rhf(c1): nocc, nvir = c1.shape t1 = np.zeros((nocc, nvir), dtype=np.float64) t1 += einsum("ia->ia", c1) return t1 + def t2_uhf(t1, c2): t1_aa, t1_bb = t1 c2_aaaa, c2_abab, c2_bbbb = c2 @@ -40,6 +43,7 @@ def t2_uhf(t1, c2): t2_bbbb += einsum("ia,jb->ijab", t1_bb, t1_bb) * -1.0 return t2_aaaa, t2_abab, t2_bbbb + def t2_rhf(t1, c2): nocc, nvir = t1.shape t2 = np.zeros((nocc, nocc, nvir, nvir), dtype=np.float64) @@ -47,6 +51,7 @@ def t2_rhf(t1, c2): t2 += einsum("ia,jb->ijab", t1, t1) * -1.0 return t2 + def t3_uhf(t1, t2, c3): t1_aa, t1_bb = t1 t2_aaaa, t2_abab, t2_bbbb = t2 @@ -107,6 +112,7 @@ def t3_uhf(t1, t2, c3): t3_bbbbbb += einsum("jb,kica->ijkabc", t1_bb, x0) * -1.0 return t3_aaaaaa, t3_abaaba, t3_babbab, t3_bbbbbb + def t3_rhf(t1, t2, c3): nocc, nvir = t1.shape t3 = np.zeros((nocc, nocc, nocc, nvir, nvir, nvir), dtype=np.float64) @@ -123,6 +129,7 @@ def t3_rhf(t1, t2, c3): t3 += einsum("jb,ikca->ijkabc", t1, x0) * -1.0 return t3 + def t4_uhf(t1, t2, t3, c4): t1_aa, t1_bb = t1 t2_aaaa, t2_abab, t2_bbbb = t2 @@ -377,6 +384,7 @@ def t4_uhf(t1, t2, t3, c4): t4_bbbbbbbb += einsum("kc,lijdab->ijklabcd", t1_bb, x3) * -1.0 return t4_aaaaaaaa, t4_aaabaaab, t4_abababab, t4_abbbabbb, t4_bbbbbbbb + def t4_rhf(t1, t2, t3, c4): nocc, nvir = t1.shape c4_abaaabaa, c4_abababab = c4 @@ -411,7 +419,7 @@ def t4_rhf(t1, t2, t3, c4): t4_abababab += einsum("ia,jklbcd->ijklabcd", t1, x1) * -1.0 t4_abababab += einsum("kc,jilbad->ijklabcd", t1, x1) * -1.0 t4_abaaabaa = np.zeros((nocc, nocc, nocc, nocc, nvir, nvir, nvir, nvir), dtype=np.float64) - #t4_abaaabaa += einsum("ikljacdb->ijklabcd", c4_abaaabaa) # NOTE incorrect in generated eqns + # t4_abaaabaa += einsum("ikljacdb->ijklabcd", c4_abaaabaa) # NOTE incorrect in generated eqns t4_abaaabaa += einsum("ijklabcd->ijklabcd", c4_abaaabaa) t4_abaaabaa += einsum("ia,kjlcbd->ijklabcd", t1, t3) * -1.0 t4_abaaabaa += einsum("id,kjlabc->ijklabcd", t1, t3) * -1.0 diff --git a/vayesta/core/types/wf/wf.py b/vayesta/core/types/wf/wf.py index 81b1bab79..98c11df43 100644 --- a/vayesta/core/types/wf/wf.py +++ b/vayesta/core/types/wf/wf.py @@ -11,7 +11,6 @@ class WaveFunction: - def __init__(self, mo, projector=None): self.mo = mo self.projector = projector @@ -76,11 +75,12 @@ def from_pyscf(obj, **kwargs): # HF # TODO def eris_init(obj, mod): - if 'eris' in kwargs: - return kwargs['eris'] + if "eris" in kwargs: + return kwargs["eris"] eris = importlib.import_module(mod.__name__)._ChemistsERIs() eris._common_init_(obj) return eris + # MP2 if isinstance(obj, pyscf.mp.ump2.UMP2): eris = eris_init(obj, pyscf.mp.ump2) @@ -112,13 +112,13 @@ def eris_init(obj, mod): return wf.RCISD_WaveFunction(mo, c0, c1, c2) # FCI if isinstance(obj, pyscf.fci.direct_uhf.FCISolver): - mo = kwargs['mo'] + mo = kwargs["mo"] return wf.UFCI_WaveFunction(mo, obj.ci) if isinstance(obj, pyscf.fci.direct_spin1.FCISolver): - mo = kwargs['mo'] + mo = kwargs["mo"] if isinstance(mo, np.ndarray): nelec = sum(obj.nelec) - assert (nelec % 2 == 0) + assert nelec % 2 == 0 nocc = nelec // 2 mo = SpatialOrbitals(mo, occ=nocc) return wf.RFCI_WaveFunction(mo, obj.ci) diff --git a/vayesta/core/util.py b/vayesta/core/util.py index 13655b49d..38e3863c4 100644 --- a/vayesta/core/util.py +++ b/vayesta/core/util.py @@ -23,26 +23,52 @@ # util module can be imported as *, such that the following is imported: __all__ = [ - # General - 'Object', 'OptionsBase', 'brange', 'deprecated', 'cache', 'call_once', 'with_doc', - # NumPy replacements - 'dot', 'tril_indices_ndim', 'einsum', 'hstack', 'decompress_axes', - # Exceptions - 'AbstractMethodError', 'ConvergenceError', 'OrthonormalityError', 'ImaginaryPartError', - 'NotCalculatedError', - # String formatting - 'energy_string', 'time_string', 'memory_string', - # Time & memory - 'timer', 'log_time', 'get_used_memory', 'log_method', - # Other - 'getattr_recursive', 'setattr_recursive', - 'replace_attr', 'break_into_lines', 'fix_orbital_sign', 'split_into_blocks', - 'getif', 'callif', 'permutations_with_signs', - ] + # General + "Object", + "OptionsBase", + "brange", + "deprecated", + "cache", + "call_once", + "with_doc", + # NumPy replacements + "dot", + "tril_indices_ndim", + "einsum", + "hstack", + "decompress_axes", + # Exceptions + "AbstractMethodError", + "ConvergenceError", + "OrthonormalityError", + "ImaginaryPartError", + "NotCalculatedError", + # String formatting + "energy_string", + "time_string", + "memory_string", + # Time & memory + "timer", + "log_time", + "get_used_memory", + "log_method", + # Other + "getattr_recursive", + "setattr_recursive", + "replace_attr", + "break_into_lines", + "fix_orbital_sign", + "split_into_blocks", + "getif", + "callif", + "permutations_with_signs", +] + class Object: pass + def cache(maxsize_or_user_function=16, typed=False, copy=False): """Adds LRU cache to function or method. @@ -67,23 +93,29 @@ def cache(maxsize_or_user_function=16, typed=False, copy=False): return lru_cache(user_function) else: return lru_cache + def decorator(func): cached_func = lru_cache(func) + @functools.wraps(func) def wrapper(*args, **kwargs): return deepcopy(cached_func(*args, **kwargs)) + wrapper.cache_clear = cached_func.cache_clear wrapper.cache_info = cached_func.cache_info # Python 3.9+ - if hasattr(cached_func, 'cache_parameters'): + if hasattr(cached_func, "cache_parameters"): wrapper.cache_parameters = cached_func.cache_parameters return wrapper + return decorator + @functools.lru_cache(None) def call_once(func, *args, **kwargs): return func(*args, **kwargs) + def with_doc(doc): """Use this decorator to add doc string for function @@ -96,18 +128,22 @@ def func: func.__doc__ = doc """ if not isinstance(doc, str): - if hasattr(doc, '__doc__'): + if hasattr(doc, "__doc__"): doc = doc.__doc__ + def func_with_doc(func): func.__doc__ = doc return func + return func_with_doc + # --- NumPy + def tril_indices_ndim(n, dims, include_diagonal=False): """Return lower triangular indices for a multidimensional array. - + Copied from ebcc. """ @@ -123,19 +159,16 @@ def tril_indices_ndim(n, dims, include_diagonal=False): else: func = np.greater - slices = [ - tuple(slice(None) if i == j else np.newaxis for i in range(dims)) for j in range(dims) - ] + slices = [tuple(slice(None) if i == j else np.newaxis for i in range(dims)) for j in range(dims)] casted = [rng[ind] for rng, ind in zip(ranges, slices)] mask = functools.reduce(np.logical_and, [func(a, b) for a, b in zip(casted[:-1], casted[1:])]) - tril = tuple( - np.broadcast_to(inds, mask.shape)[mask] for inds in np.indices(mask.shape, sparse=True) - ) + tril = tuple(np.broadcast_to(inds, mask.shape)[mask] for inds in np.indices(mask.shape, sparse=True)) return tril + def decompress_axes(subscript, array_flat, shape, include_diagonal=False, symmetry=None): """Decompress an array that has dimensions flattened according to permutation symmetries in the signs. @@ -207,9 +240,7 @@ def decompress_axes(subscript, array_flat, shape, include_diagonal=False, symmet array[tuple(indices_perm)] = array_flat.reshape(shape) * np.prod(signs) # Reshape array to non-flattened format - array = array.reshape( - sum([(sizes[char],) * subscript.count(char) for char in sorted(set(subscript))], tuple()) - ) + array = array.reshape(sum([(sizes[char],) * subscript.count(char) for char in sorted(set(subscript))], tuple())) # Undo transpose: arg = np.argsort(arg) @@ -217,19 +248,21 @@ def decompress_axes(subscript, array_flat, shape, include_diagonal=False, symmet return array + def dot(*args, out=None, ignore_none=False): """Like NumPy's multi_dot, but variadic""" if ignore_none: args = [a for a in args if a is not None] return np.linalg.multi_dot(args, out=out) + def _einsum_replace_decorated_subscripts(subscripts): """Support for decorated indices: a!, b$, c3, d123. Characters in ',->.()[]{}' cannot be used as decorators. """ free = sorted(set(string.ascii_letters).difference(set(subscripts))) - keep = (string.ascii_letters + ' ,->.()[]{}') + keep = string.ascii_letters + " ,->.()[]{}" replaced = {} subscripts_out = [] for char in subscripts: @@ -237,85 +270,88 @@ def _einsum_replace_decorated_subscripts(subscripts): subscripts_out += char continue else: - last = (subscripts_out.pop() if len(subscripts_out) else '%') + last = subscripts_out.pop() if len(subscripts_out) else "%" if last not in string.ascii_letters: raise ValueError("Invalid subscripts: '%s'" % subscripts) - comb = (last + char) + comb = last + char if comb not in replaced: replaced[comb] = free.pop() subscripts_out += replaced[comb] - return ''.join(subscripts_out) + return "".join(subscripts_out) + def _ordered_einsum(einsumfunc, subscripts, *operands, **kwargs): """Support for parenthesis in einsum subscripts: '(ab,bc),cd->ad'.""" def resolve(subs, *ops): - #print('resolve called with %s and %d operands' % (subs, len(ops))) + # print('resolve called with %s and %d operands' % (subs, len(ops))) - idx_right = re.sub('[\]}]', ')', subs).find(')') - idx_left = re.sub('[\[{]', '(', subs[:idx_right]).rfind('(') + idx_right = re.sub("[\]}]", ")", subs).find(")") + idx_left = re.sub("[\[{]", "(", subs[:idx_right]).rfind("(") if idx_left == idx_right == -1: return einsumfunc(subs, *ops, **kwargs) - if (idx_left == -1 or idx_right == -1): + if idx_left == -1 or idx_right == -1: raise ValueError("Unmatched parenthesis: '%s'" % subs) - bracket_types = {'(': ')', '[': ']', '{': '}'} + bracket_types = {"(": ")", "[": "]", "{": "}"} if subs[idx_right] != bracket_types[subs[idx_left]]: raise ValueError("Unmatched parenthesis: '%s'" % subs) - subs_int = subs[idx_left+1:idx_right] + subs_int = subs[idx_left + 1 : idx_right] subs_left = subs[:idx_left] - subs_right = subs[idx_right+1:] + subs_right = subs[idx_right + 1 :] # Split operands - nops_left = subs_left.count(',') - nops_right = subs_right.count(',') - nops_int = subs_int.count(',') + 1 - ops_int = ops[nops_left:nops_left+nops_int] + nops_left = subs_left.count(",") + nops_right = subs_right.count(",") + nops_int = subs_int.count(",") + 1 + ops_int = ops[nops_left : nops_left + nops_int] ops_left = ops[:nops_left] - ops_right = ops[nops_left+nops_int:] + ops_right = ops[nops_left + nops_int :] - if '->' in subs_int: - subs_int_in, subs_int_out = subs_int.split('->') + if "->" in subs_int: + subs_int_in, subs_int_out = subs_int.split("->") else: subs_int_in = subs_int - #possible = subs_int_in.replace(',', '').replace(' ', '') - #subs_int_out = ''.join([x for x in possible if x in (subs_left + subs_right)]) - #subs_int = '->'.join([subs_int_in, subs_int_out]) + # possible = subs_int_in.replace(',', '').replace(' ', '') + # subs_int_out = ''.join([x for x in possible if x in (subs_left + subs_right)]) + # subs_int = '->'.join([subs_int_in, subs_int_out]) subs_int_out = np.core.einsumfunc._parse_einsum_input((subs_int_in, *ops_int))[1] # Perform intern einsum res_int = einsumfunc(subs_int, *ops_int, **kwargs) # Resolve recursively - subs_ext = subs_left + subs_int_out + subs_right + subs_ext = subs_left + subs_int_out + subs_right ops_ext = ops_left + (res_int,) + ops_right return resolve(subs_ext, *ops_ext) res = resolve(subscripts, *operands) return res + def einsum(subscripts, *operands, **kwargs): subscripts = _einsum_replace_decorated_subscripts(subscripts) - if np.any([x in subscripts for x in '()[]{}']): + if np.any([x in subscripts for x in "()[]{}"]): return _ordered_einsum(einsum, subscripts, *operands, **kwargs) - kwargs['optimize'] = kwargs.get('optimize', True) - driver = kwargs.get('driver', np.einsum) + kwargs["optimize"] = kwargs.get("optimize", True) + driver = kwargs.get("driver", np.einsum) try: res = driver(subscripts, *operands, **kwargs) # Better shape information in case of exception: except ValueError: modlog.fatal("einsum('%s',...) failed. shapes of arguments:", subscripts) for i, arg in enumerate(operands): - modlog.fatal('%d: %r', i, list(np.asarray(arg).shape)) + modlog.fatal("%d: %r", i, list(np.asarray(arg).shape)) raise # Unpack scalars (for optimize = True): if isinstance(res, np.ndarray) and res.ndim == 0: res = res[()] return res + def hstack(*args, ignore_none=True): """Like NumPy's hstack, but variadic, ignores any arguments which are None and improved error message.""" if ignore_none: @@ -325,9 +361,10 @@ def hstack(*args, ignore_none=True): except ValueError as e: modlog.critical("Exception while trying to stack the following objects:") for x in args: - modlog.critical("type= %r shape= %r", type(x), x.shape if hasattr(x, 'shape') else "None") + modlog.critical("type= %r shape= %r", type(x), x.shape if hasattr(x, "shape") else "None") raise e + def brange(*args, minstep=1, maxstep=None): """Similar to PySCF's prange, but returning a slice instead. @@ -349,59 +386,73 @@ def brange(*args, minstep=1, maxstep=None): if stop <= start: return if maxstep is None: - maxstep = (stop-start) + maxstep = stop - start step = int(np.clip(step, minstep, maxstep)) for i in range(start, stop, step): - blk = np.s_[i:min(i+step, stop)] + blk = np.s_[i : min(i + step, stop)] yield blk + def split_into_blocks(array, axis=0, blocksize=None, max_memory=int(1e9)): size = array.shape[axis] axis = axis % array.ndim if blocksize is None: mem = array.nbytes - nblocks = max(int(np.ceil(mem/max_memory)), 1) - blocksize = int(np.ceil(size/nblocks)) + nblocks = max(int(np.ceil(mem / max_memory)), 1) + blocksize = int(np.ceil(size / nblocks)) if blocksize >= size: yield slice(None), array return for i in range(0, size, blocksize): - blk = np.s_[i:min(i+blocksize, size)] - yield blk, array[axis*(slice(None), ) + (blk,)] + blk = np.s_[i : min(i + blocksize, size)] + yield blk, array[axis * (slice(None),) + (blk,)] + # --- Exceptions + class AbstractMethodError(NotImplementedError): pass + class ConvergenceError(RuntimeError): pass + class ImaginaryPartError(RuntimeError): pass + class OrthonormalityError(RuntimeError): pass + class NotCalculatedError(AttributeError): """Raise if a necessary attribute has not been calculated.""" + pass + class SymmetryError(RuntimeError): pass + # --- Energy -def energy_string(energy, unit='Ha'): - if unit == 'eV': + +def energy_string(energy, unit="Ha"): + if unit == "eV": energy *= 27.211386245988 - if unit: unit = ' %s' % unit - return '%+16.8f%s' % (energy, unit) + if unit: + unit = " %s" % unit + return "%+16.8f%s" % (energy, unit) + # --- Time and memory timer = default_timer + @contextmanager def log_time(logger, message, *args, mintime=None, **kwargs): """Log time to execute the body of a with-statement. @@ -419,26 +470,30 @@ def log_time(logger, message, *args, mintime=None, **kwargs): t0 = timer() yield t0 finally: - t = (timer()-t0) + t = timer() - t0 if logger and (mintime is None or t >= mintime): logger(message, *args, time_string(t), **kwargs) -def log_method(message='Time for %(classname).%(funcname): %s', log=None): + +def log_method(message="Time for %(classname).%(funcname): %s", log=None): def decorator(func): @functools.wraps(func) def wrapped(self, *args, **kwargs): nonlocal message, log - message = message.replace('%(classname)', type(self).__name__) - message = message.replace('%(funcname)', func.__name__) - log = log or getattr(self, 'log', False) or modlog + message = message.replace("%(classname)", type(self).__name__) + message = message.replace("%(funcname)", func.__name__) + log = log or getattr(self, "log", False) or modlog log.debugv("Entering method '%s'", func.__name__) with log_time(log.timing, message): res = func(self, *args, **kwargs) log.debugv("Exiting method '%s'", func.__name__) return res + return wrapped + return decorator + def time_string(seconds, show_zeros=False): """String representation of seconds.""" seconds, sign = abs(seconds), np.sign(seconds) @@ -450,26 +505,29 @@ def time_string(seconds, show_zeros=False): else: tstr = "%.1f s" % s if sign == -1: - tstr = '-%s' % tstr + tstr = "-%s" % tstr return tstr -MEMUNITS = {'b': 1, 'kb': 1e3, 'mb': 1e6, 'gb': 1e9, 'tb': 1e12} -def get_used_memory(unit='b'): +MEMUNITS = {"b": 1, "kb": 1e3, "mb": 1e6, "gb": 1e9, "tb": 1e12} + + +def get_used_memory(unit="b"): if psutil is not None: process = psutil.Process(os.getpid()) mem = process.memory_info().rss # in bytes # Fallback: use os module - elif sys.platform.startswith('linux'): + elif sys.platform.startswith("linux"): pagesize = os.sysconf("SC_PAGE_SIZE") with open("/proc/%s/statm" % os.getpid()) as f: - mem = int(f.readline().split()[1])*pagesize + mem = int(f.readline().split()[1]) * pagesize else: mem = 0 mem /= MEMUNITS[unit.lower()] return mem -def memory_string(nbytes, fmt='6.2f'): + +def memory_string(nbytes, fmt="6.2f"): """String representation of nbytes""" if isinstance(nbytes, np.ndarray) and nbytes.size > 1: nbytes = nbytes.nbytes @@ -490,37 +548,43 @@ def memory_string(nbytes, fmt='6.2f'): unit = "TB" return "{:{fmt}} {unit}".format(val, unit=unit, fmt=fmt) + # --- # Recursive get- and setattr + def getattr_recursive(obj, attr, *args): def _getattr(obj, attr): return getattr(obj, attr, *args) - return functools.reduce(_getattr, [obj] + attr.split('.')) + + return functools.reduce(_getattr, [obj] + attr.split(".")) + def setattr_recursive(obj, attr, val): - pre, _, post = attr.rpartition('.') + pre, _, post = attr.rpartition(".") return setattr(rgetattr(obj, pre) if pre else obj, post, val) + @contextmanager def replace_attr(obj, **kwargs): """Temporary replace attributes and methods of object.""" orig = {} try: for name, attr in kwargs.items(): - orig[name] = getattr(obj, name) # Save originals + orig[name] = getattr(obj, name) # Save originals if callable(attr): - setattr(obj, name, attr.__get__(obj)) # For functions: replace and bind as method + setattr(obj, name, attr.__get__(obj)) # For functions: replace and bind as method else: - setattr(obj, name, attr) # Just set otherwise + setattr(obj, name, attr) # Just set otherwise yield obj finally: # Restore originals for name, attr in orig.items(): setattr(obj, name, attr) -def break_into_lines(string, linelength=100, sep=None, newline='\n'): + +def break_into_lines(string, linelength=100, sep=None, newline="\n"): """Break a long string into multiple lines""" if len(string) <= linelength: return string @@ -531,13 +595,15 @@ def break_into_lines(string, linelength=100, sep=None, newline='\n'): # Start new line lines.append(s) else: - lines[-1] += ' ' + s + lines[-1] += " " + s return newline.join(lines) + def deprecated(message=None, replacement=None): """This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used.""" + def decorator(func): if message is not None: msg = message @@ -548,15 +614,18 @@ def decorator(func): @functools.wraps(func) def wrapped(*args, **kwargs): - if len(args) > 0 and hasattr(args[0], 'log'): + if len(args) > 0 and hasattr(args[0], "log"): log = args[0].log else: log = modlog log.deprecated(msg) return func(*args, **kwargs) + return wrapped + return decorator + @dataclasses.dataclass class OptionsBase: """Abstract base class for Option dataclasses. @@ -593,14 +662,14 @@ def items(self): @classmethod def get_default(cls, field): for x in dataclasses.fields(cls): - if (x.name == field): + if x.name == field: return x.default raise TypeError @classmethod def get_default_factory(cls, field): for x in dataclasses.fields(cls): - if (x.name == field): + if x.name == field: return x.default_factory raise TypeError @@ -625,7 +694,7 @@ def update(self, **kwargs): if key not in keys: continue if isinstance(val, dict) and isinstance(getattr(self, key), dict): - #getattr(self, key).update(val) + # getattr(self, key).update(val) setattr(self, key, {**getattr(self, key), **val}) else: setattr(self, key, val) @@ -640,6 +709,7 @@ def change_dict_defaults(cls, field, **kwargs): defaults = cls.get_default_factory(field)() return cls.dict_with_defaults(**{**defaults, **kwargs}) + def fix_orbital_sign(mo_coeff, inplace=True): # UHF if np.ndim(mo_coeff[0]) == 2: @@ -650,24 +720,27 @@ def fix_orbital_sign(mo_coeff, inplace=True): mo_coeff = mo_coeff.copy() absmax = np.argmax(abs(mo_coeff), axis=0) nmo = mo_coeff.shape[-1] - swap = mo_coeff[absmax,np.arange(nmo)] < 0 - mo_coeff[:,swap] *= -1 + swap = mo_coeff[absmax, np.arange(nmo)] < 0 + mo_coeff[:, swap] *= -1 signs = np.ones((nmo,), dtype=int) signs[swap] = -1 return mo_coeff, signs + def getif(obj, key, cond=lambda x: x is not None, default=None): """Returns obj[key] if cond(obj) else default.""" if cond(obj): return obj[key] return default + def callif(func, arg, cond=lambda x, **kw: x is not None, default=None, **kwargs): """Returns func(arg, **kwargs) if cond(arg, **kwargs) else default.""" if cond(arg, **kwargs): return func(arg, **kwargs) return default + def permutations_with_signs(seq): """Generate permutations of seq, yielding also a sign which is equal to +1 for an even number of swaps, and -1 for an odd number diff --git a/vayesta/core/vlog.py b/vayesta/core/vlog.py index 265c6c6f3..75dfedd95 100644 --- a/vayesta/core/vlog.py +++ b/vayesta/core/vlog.py @@ -27,23 +27,23 @@ """ LVL_PREFIX = { - "FATAL" : "FATAL", - "CRITICAL" : "CRITICAL", - "ERROR" : "ERROR", - "WARNING" : "WARNING", - "DEPRECATED" : "WARNING", - "OUT" : "OUTPUT", - "DEBUG" : "DEBUG", - "DEBUGV" : "DEBUG", - "TRACE" : "TRACE", - } + "FATAL": "FATAL", + "CRITICAL": "CRITICAL", + "ERROR": "ERROR", + "WARNING": "WARNING", + "DEPRECATED": "WARNING", + "OUT": "OUTPUT", + "DEBUG": "DEBUG", + "DEBUGV": "DEBUG", + "TRACE": "TRACE", +} class NoLogger: - def __getattr__(self, key): """Return function which does nothing.""" - return (lambda *args, **kwargs : None) + return lambda *args, **kwargs: None + class LevelRangeFilter(logging.Filter): """Only log events with level in interval [low, high).""" @@ -55,10 +55,11 @@ def __init__(self, *args, low=None, high=None, **kwargs): def filter(self, record): if self._low is None: - return (record.levelno < self._high) + return record.levelno < self._high if self._high is None: - return (self._low <= record.levelno) - return (self._low <= record.levelno < self._high) + return self._low <= record.levelno + return self._low <= record.levelno < self._high + class LevelIncludeFilter(logging.Filter): """Only log events with level in include.""" @@ -68,7 +69,8 @@ def __init__(self, *args, include, **kwargs): self._include = include def filter(self, record): - return (record.levelno in self._include) + return record.levelno in self._include + class LevelExcludeFilter(logging.Filter): """Only log events with level not in exlude.""" @@ -78,14 +80,23 @@ def __init__(self, *args, exclude, **kwargs): self._exclude = exclude def filter(self, record): - return (record.levelno not in self._exclude) + return record.levelno not in self._exclude + class VFormatter(logging.Formatter): """Formatter which adds a prefix column and indentation.""" - def __init__(self, *args, - show_level=True, show_mpi_rank=False, prefix_sep='|', - indent=False, indent_char=' ', indent_width=4, **kwargs): + def __init__( + self, + *args, + show_level=True, + show_mpi_rank=False, + prefix_sep="|", + indent=False, + indent_char=" ", + indent_width=4, + **kwargs, + ): super().__init__(*args, **kwargs) self.show_level = show_level @@ -95,7 +106,7 @@ def __init__(self, *args, if show_level: self.prefix_width += len(max(LVL_PREFIX.values(), key=len)) + 2 if show_mpi_rank: - self.prefix_width += len(str(mpi.size-1)) + 6 + self.prefix_width += len(str(mpi.size - 1)) + 6 self.prefix_sep = prefix_sep self.indent = indent @@ -108,10 +119,10 @@ def format(self, record): if self.show_level: prefix = LVL_PREFIX.get(record.levelname, "") if prefix: - prefix = '[%s]' % prefix + prefix = "[%s]" % prefix if self.show_mpi_rank: - prefix += '[MPI %d]' % mpi.rank - prefix = '%-*s%s' % (self.prefix_width, prefix, self.prefix_sep) + prefix += "[MPI %d]" % mpi.rank + prefix = "%-*s%s" % (self.prefix_width, prefix, self.prefix_sep) if self.indent: root = logging.getLogger() indent = root.indentLevel * self.indent_width * self.indent_char @@ -120,6 +131,7 @@ def format(self, record): lines = [((prefix + " " + line) if line else prefix) for line in lines] return "\n".join(lines) + class VStreamHandler(logging.StreamHandler): """Default stream handler with IndentedFormatter""" @@ -129,23 +141,26 @@ def __init__(self, stream, formatter=None, **kwargs): formatter = VFormatter() self.setFormatter(formatter) + class VFileHandler(logging.FileHandler): """Default file handler with IndentedFormatter""" - def __init__(self, filename, mode='a', formatter=None, add_mpi_rank=True, delay=True, **kwargs): + def __init__(self, filename, mode="a", formatter=None, add_mpi_rank=True, delay=True, **kwargs): filename = get_logname(filename, add_mpi_rank=add_mpi_rank) super().__init__(filename, mode=mode, delay=delay, **kwargs) if formatter is None: formatter = VFormatter() self.setFormatter(formatter) -def get_logname(name, add_mpi_rank=True, ext='txt'): + +def get_logname(name, add_mpi_rank=True, ext="txt"): if mpi and add_mpi_rank: - name = '%s.mpi%d' % (name, mpi.rank) - if ext and not name.endswith('.%s' % ext): - name = '%s.%s' % (name, ext) + name = "%s.mpi%d" % (name, mpi.rank) + if ext and not name.endswith(".%s" % ext): + name = "%s.%s" % (name, ext) return name + def init_logging(): """Call this to initialize and configure logging, when importing Vayesta. @@ -161,15 +176,20 @@ def init_logging(): def add_log_level(level, name, log_once=False): logging.addLevelName(level, name.upper()) setattr(logging, name.upper(), level) + def logForLevel(self, message, *args, **kwargs): if self.isEnabledFor(level): self._log(level, message, args, **kwargs) + if log_once: logForLevel = functools.lru_cache(None)(logForLevel) + def logToRoot(message, *args, **kwargs): logging.log(level, message, *args, **kwargs) + setattr(logging.getLoggerClass(), name, logForLevel) setattr(logging, name, logToRoot) + add_log_level(100, "fatal") add_log_level(30, "deprecated", log_once=True) add_log_level(25, "output") @@ -196,7 +216,6 @@ def changeIndentLevel(self, delta): return root.indentLevel class indent(contextlib.ContextDecorator): - def __init__(self, delta=1): self.delta = delta self.level_init = None @@ -204,7 +223,7 @@ def __init__(self, delta=1): def __enter__(self): self.level_init = self.root.indentLevel - self.root.indentLevel = max(self.level_init+self.delta, 0) + self.root.indentLevel = max(self.level_init + self.delta, 0) def __exit__(self, *args): self.root.indentLevel = self.level_init diff --git a/vayesta/core/vpyscf/ccsd_rdm.py b/vayesta/core/vpyscf/ccsd_rdm.py index de1b38f2b..bbd3d4378 100644 --- a/vayesta/core/vpyscf/ccsd_rdm.py +++ b/vayesta/core/vpyscf/ccsd_rdm.py @@ -28,138 +28,142 @@ # JCP 95, 2639 (1991); DOI:10.1063/1.460916 # + def _gamma1_intermediates(mycc, t1, t2, l1, l2): nocc, nvir = t1.shape - doo =-numpy.einsum('ja,ia->ij', t1, l1) - dvv = numpy.einsum('ia,ib->ab', t1, l1) - xtv = numpy.einsum('ie,me->im', t1, l1) - dvo = t1.T - numpy.einsum('im,ma->ai', xtv, t1) - theta = t2 * 2 - t2.transpose(0,1,3,2) - doo -= lib.einsum('jkab,ikab->ij', theta, l2) - dvv += lib.einsum('jica,jicb->ab', theta, l2) - xt1 = lib.einsum('mnef,inef->mi', l2, theta) - xt2 = lib.einsum('mnaf,mnef->ea', l2, theta) - dvo += numpy.einsum('imae,me->ai', theta, l1) - dvo -= numpy.einsum('mi,ma->ai', xt1, t1) - dvo -= numpy.einsum('ie,ae->ai', t1, xt2) + doo = -numpy.einsum("ja,ia->ij", t1, l1) + dvv = numpy.einsum("ia,ib->ab", t1, l1) + xtv = numpy.einsum("ie,me->im", t1, l1) + dvo = t1.T - numpy.einsum("im,ma->ai", xtv, t1) + theta = t2 * 2 - t2.transpose(0, 1, 3, 2) + doo -= lib.einsum("jkab,ikab->ij", theta, l2) + dvv += lib.einsum("jica,jicb->ab", theta, l2) + xt1 = lib.einsum("mnef,inef->mi", l2, theta) + xt2 = lib.einsum("mnaf,mnef->ea", l2, theta) + dvo += numpy.einsum("imae,me->ai", theta, l1) + dvo -= numpy.einsum("mi,ma->ai", xt1, t1) + dvo -= numpy.einsum("ie,ae->ai", t1, xt2) dov = l1 return doo, dov, dvo, dvv + # gamma2 intermediates in Chemist's notation def _gamma2_intermediates(mycc, t1, t2, l1, l2, compress_vvvv=False): f = lib.H5TmpFile() _gamma2_outcore(mycc, t1, t2, l1, l2, f, compress_vvvv) - d2 = (f['dovov'][:], f['dvvvv'][:], f['doooo'][:], f['doovv'][:], - f['dovvo'][:], None, f['dovvv'][:], f['dooov'][:]) + d2 = (f["dovov"][:], f["dvvvv"][:], f["doooo"][:], f["doovv"][:], f["dovvo"][:], None, f["dovvv"][:], f["dooov"][:]) return d2 + def _gamma2_outcore(mycc, t1, t2, l1, l2, h5fobj, compress_vvvv=False): log = logger.Logger(mycc.stdout, mycc.verbose) nocc, nvir = t1.shape - nvir_pair = nvir * (nvir+1) //2 + nvir_pair = nvir * (nvir + 1) // 2 dtype = numpy.result_type(t1, t2, l1, l2).char if compress_vvvv: - dvvvv = h5fobj.create_dataset('dvvvv', (nvir_pair,nvir_pair), dtype) + dvvvv = h5fobj.create_dataset("dvvvv", (nvir_pair, nvir_pair), dtype) else: - dvvvv = h5fobj.create_dataset('dvvvv', (nvir,nvir,nvir,nvir), dtype) - dovvo = h5fobj.create_dataset('dovvo', (nocc,nvir,nvir,nocc), dtype, - chunks=(nocc,1,nvir,nocc)) + dvvvv = h5fobj.create_dataset("dvvvv", (nvir, nvir, nvir, nvir), dtype) + dovvo = h5fobj.create_dataset("dovvo", (nocc, nvir, nvir, nocc), dtype, chunks=(nocc, 1, nvir, nocc)) fswap = lib.H5TmpFile() time1 = logger.process_clock(), logger.perf_counter() - pvOOv = lib.einsum('ikca,jkcb->aijb', l2, t2) - moo = numpy.einsum('dljd->jl', pvOOv) * 2 - mvv = numpy.einsum('blld->db', pvOOv) * 2 - gooov = lib.einsum('kc,cija->jkia', t1, pvOOv) - fswap['mvOOv'] = pvOOv + pvOOv = lib.einsum("ikca,jkcb->aijb", l2, t2) + moo = numpy.einsum("dljd->jl", pvOOv) * 2 + mvv = numpy.einsum("blld->db", pvOOv) * 2 + gooov = lib.einsum("kc,cija->jkia", t1, pvOOv) + fswap["mvOOv"] = pvOOv pvOOv = None - pvoOV = -lib.einsum('ikca,jkbc->aijb', l2, t2) - theta = t2 * 2 - t2.transpose(0,1,3,2) - pvoOV += lib.einsum('ikac,jkbc->aijb', l2, theta) - moo += numpy.einsum('dljd->jl', pvoOV) - mvv += numpy.einsum('blld->db', pvoOV) - gooov -= lib.einsum('jc,cika->jkia', t1, pvoOV) - fswap['mvoOV'] = pvoOV + pvoOV = -lib.einsum("ikca,jkbc->aijb", l2, t2) + theta = t2 * 2 - t2.transpose(0, 1, 3, 2) + pvoOV += lib.einsum("ikac,jkbc->aijb", l2, theta) + moo += numpy.einsum("dljd->jl", pvoOV) + mvv += numpy.einsum("blld->db", pvoOV) + gooov -= lib.einsum("jc,cika->jkia", t1, pvoOV) + fswap["mvoOV"] = pvoOV pvoOV = None - mia =(numpy.einsum('kc,ikac->ia', l1, t2) * 2 - - numpy.einsum('kc,ikca->ia', l1, t2)) - mab = numpy.einsum('kc,kb->cb', l1, t1) - mij = numpy.einsum('kc,jc->jk', l1, t1) + moo*.5 + mia = numpy.einsum("kc,ikac->ia", l1, t2) * 2 - numpy.einsum("kc,ikca->ia", l1, t2) + mab = numpy.einsum("kc,kb->cb", l1, t1) + mij = numpy.einsum("kc,jc->jk", l1, t1) + moo * 0.5 - tau = numpy.einsum('ia,jb->ijab', t1, t1) + tau = numpy.einsum("ia,jb->ijab", t1, t1) tau += t2 - goooo = lib.einsum('ijab,klab->ijkl', tau, l2)*.5 - h5fobj['doooo'] = (goooo.transpose(0,2,1,3)*2 - - goooo.transpose(0,3,1,2)).conj() + goooo = lib.einsum("ijab,klab->ijkl", tau, l2) * 0.5 + h5fobj["doooo"] = (goooo.transpose(0, 2, 1, 3) * 2 - goooo.transpose(0, 3, 1, 2)).conj() - gooov += numpy.einsum('ji,ka->jkia', -.5*moo, t1) - gooov += lib.einsum('la,jkil->jkia', 2*t1, goooo) - gooov -= lib.einsum('ib,jkba->jkia', l1, tau) + gooov += numpy.einsum("ji,ka->jkia", -0.5 * moo, t1) + gooov += lib.einsum("la,jkil->jkia", 2 * t1, goooo) + gooov -= lib.einsum("ib,jkba->jkia", l1, tau) gooov = gooov.conj() - gooov -= lib.einsum('jkba,ib->jkia', l2, t1) - h5fobj['dooov'] = gooov.transpose(0,2,1,3)*2 - gooov.transpose(1,2,0,3) + gooov -= lib.einsum("jkba,ib->jkia", l2, t1) + h5fobj["dooov"] = gooov.transpose(0, 2, 1, 3) * 2 - gooov.transpose(1, 2, 0, 3) tau = None - time1 = log.timer_debug1('rdm intermediates pass1', *time1) + time1 = log.timer_debug1("rdm intermediates pass1", *time1) - goovv = numpy.einsum('ia,jb->ijab', mia.conj(), t1.conj()) + goovv = numpy.einsum("ia,jb->ijab", mia.conj(), t1.conj()) max_memory = max(0, mycc.max_memory - lib.current_memory()[0]) - unit = nocc**2*nvir*6 - blksize = min(nocc, nvir, max(ccsd.BLKMIN, int(max_memory*.95e6/8/unit))) - doovv = h5fobj.create_dataset('doovv', (nocc,nocc,nvir,nvir), dtype, - chunks=(nocc,nocc,1,nvir)) - - log.debug1('rdm intermediates pass 2: block size = %d, nvir = %d in %d blocks', - blksize, nvir, int((nvir+blksize-1)/blksize)) + unit = nocc**2 * nvir * 6 + blksize = min(nocc, nvir, max(ccsd.BLKMIN, int(max_memory * 0.95e6 / 8 / unit))) + doovv = h5fobj.create_dataset("doovv", (nocc, nocc, nvir, nvir), dtype, chunks=(nocc, nocc, 1, nvir)) + + log.debug1( + "rdm intermediates pass 2: block size = %d, nvir = %d in %d blocks", + blksize, + nvir, + int((nvir + blksize - 1) / blksize), + ) for p0, p1 in lib.prange(0, nvir, blksize): - tau = numpy.einsum('ia,jb->ijab', t1[:,p0:p1], t1) - tau += t2[:,:,p0:p1] - tmpoovv = lib.einsum('ijkl,klab->ijab', goooo, tau) - tmpoovv -= lib.einsum('jk,ikab->ijab', mij, tau) - tmpoovv -= lib.einsum('cb,ijac->ijab', mab, t2[:,:,p0:p1]) - tmpoovv -= lib.einsum('bd,ijad->ijab', mvv*.5, tau) - tmpoovv += .5 * tau + tau = numpy.einsum("ia,jb->ijab", t1[:, p0:p1], t1) + tau += t2[:, :, p0:p1] + tmpoovv = lib.einsum("ijkl,klab->ijab", goooo, tau) + tmpoovv -= lib.einsum("jk,ikab->ijab", mij, tau) + tmpoovv -= lib.einsum("cb,ijac->ijab", mab, t2[:, :, p0:p1]) + tmpoovv -= lib.einsum("bd,ijad->ijab", mvv * 0.5, tau) + tmpoovv += 0.5 * tau tmpoovv = tmpoovv.conj() - tmpoovv += .5 * l2[:,:,p0:p1] - goovv[:,:,p0:p1] += tmpoovv - - pvOOv = fswap['mvOOv'][p0:p1] - pvoOV = fswap['mvoOV'][p0:p1] - gOvvO = lib.einsum('kiac,jc,kb->iabj', l2[:,:,p0:p1], t1, t1) - gOvvO += numpy.einsum('aijb->iabj', pvOOv) - govVO = numpy.einsum('ia,jb->iabj', l1[:,p0:p1], t1) - govVO -= lib.einsum('ikac,jc,kb->iabj', l2[:,:,p0:p1], t1, t1) - govVO += numpy.einsum('aijb->iabj', pvoOV) - dovvo[:,p0:p1] = 2*govVO + gOvvO - doovv[:,:,p0:p1] = (-2*gOvvO - govVO).transpose(3,0,1,2).conj() + tmpoovv += 0.5 * l2[:, :, p0:p1] + goovv[:, :, p0:p1] += tmpoovv + + pvOOv = fswap["mvOOv"][p0:p1] + pvoOV = fswap["mvoOV"][p0:p1] + gOvvO = lib.einsum("kiac,jc,kb->iabj", l2[:, :, p0:p1], t1, t1) + gOvvO += numpy.einsum("aijb->iabj", pvOOv) + govVO = numpy.einsum("ia,jb->iabj", l1[:, p0:p1], t1) + govVO -= lib.einsum("ikac,jc,kb->iabj", l2[:, :, p0:p1], t1, t1) + govVO += numpy.einsum("aijb->iabj", pvoOV) + dovvo[:, p0:p1] = 2 * govVO + gOvvO + doovv[:, :, p0:p1] = (-2 * gOvvO - govVO).transpose(3, 0, 1, 2).conj() gOvvO = govVO = None - tau -= t2[:,:,p0:p1] * .5 + tau -= t2[:, :, p0:p1] * 0.5 for q0, q1 in lib.prange(0, nvir, blksize): - goovv[:,:,q0:q1,:] += lib.einsum('dlib,jlda->ijab', pvOOv, tau[:,:,:,q0:q1]).conj() - goovv[:,:,:,q0:q1] -= lib.einsum('dlia,jldb->ijab', pvoOV, tau[:,:,:,q0:q1]).conj() - tmp = pvoOV[:,:,:,q0:q1] + pvOOv[:,:,:,q0:q1]*.5 - goovv[:,:,q0:q1,:] += lib.einsum('dlia,jlbd->ijab', tmp, t2[:,:,:,p0:p1]).conj() + goovv[:, :, q0:q1, :] += lib.einsum("dlib,jlda->ijab", pvOOv, tau[:, :, :, q0:q1]).conj() + goovv[:, :, :, q0:q1] -= lib.einsum("dlia,jldb->ijab", pvoOV, tau[:, :, :, q0:q1]).conj() + tmp = pvoOV[:, :, :, q0:q1] + pvOOv[:, :, :, q0:q1] * 0.5 + goovv[:, :, q0:q1, :] += lib.einsum("dlia,jlbd->ijab", tmp, t2[:, :, :, p0:p1]).conj() pvOOv = pvoOV = tau = None - time1 = log.timer_debug1('rdm intermediates pass2 [%d:%d]'%(p0, p1), *time1) - h5fobj['dovov'] = goovv.transpose(0,2,1,3) * 2 - goovv.transpose(1,2,0,3) + time1 = log.timer_debug1("rdm intermediates pass2 [%d:%d]" % (p0, p1), *time1) + h5fobj["dovov"] = goovv.transpose(0, 2, 1, 3) * 2 - goovv.transpose(1, 2, 0, 3) goovv = goooo = None max_memory = max(0, mycc.max_memory - lib.current_memory()[0]) - unit = max(nocc**2*nvir*2+nocc*nvir**2*3, - nvir**3*2+nocc*nvir**2*2+nocc**2*nvir*2) - blksize = min(nvir, max(ccsd.BLKMIN, int(max_memory*.9e6/8/unit))) - log.debug1('rdm intermediates pass 3: block size = %d, nvir = %d in %d blocks', - blksize, nocc, int((nvir+blksize-1)/blksize)) - dovvv = h5fobj.create_dataset('dovvv', (nocc,nvir,nvir,nvir), dtype, - chunks=(nocc,min(nocc,nvir),1,nvir)) + unit = max(nocc**2 * nvir * 2 + nocc * nvir**2 * 3, nvir**3 * 2 + nocc * nvir**2 * 2 + nocc**2 * nvir * 2) + blksize = min(nvir, max(ccsd.BLKMIN, int(max_memory * 0.9e6 / 8 / unit))) + log.debug1( + "rdm intermediates pass 3: block size = %d, nvir = %d in %d blocks", + blksize, + nocc, + int((nvir + blksize - 1) / blksize), + ) + dovvv = h5fobj.create_dataset("dovvv", (nocc, nvir, nvir, nvir), dtype, chunks=(nocc, min(nocc, nvir), 1, nvir)) time1 = logger.process_clock(), logger.perf_counter() for istep, (p0, p1) in enumerate(lib.prange(0, nvir, blksize)): - l2tmp = l2[:,:,p0:p1] - gvvvv = lib.einsum('ijab,ijcd->abcd', l2tmp, t2) - jabc = lib.einsum('ijab,ic->jabc', l2tmp, t1) - gvvvv += lib.einsum('jabc,jd->abcd', jabc, t1) + l2tmp = l2[:, :, p0:p1] + gvvvv = lib.einsum("ijab,ijcd->abcd", l2tmp, t2) + jabc = lib.einsum("ijab,ic->jabc", l2tmp, t1) + gvvvv += lib.einsum("jabc,jd->abcd", jabc, t1) l2tmp = jabc = None if compress_vvvv: @@ -168,54 +172,63 @@ def _gamma2_outcore(mycc, t1, t2, l1, l2, h5fobj, compress_vvvv=False): # dvvvv = (dvvvv+dvvvv.transpose(0,1,3,2)) * .5 # dvvvv = (dvvvv+dvvvv.transpose(1,0,2,3)) * .5 # now dvvvv == dvvvv.transpose(0,1,3,2) == dvvvv.transpose(1,0,3,2) - tmp = numpy.empty((nvir,nvir,nvir)) - tmpvvvv = numpy.empty((p1-p0,nvir,nvir_pair)) - for i in range(p1-p0): - vvv = gvvvv[i].conj().transpose(1,0,2) - tmp[:] = vvv - vvv.transpose(2,1,0)*.5 - lib.pack_tril(tmp+tmp.transpose(0,2,1), out=tmpvvvv[i]) + tmp = numpy.empty((nvir, nvir, nvir)) + tmpvvvv = numpy.empty((p1 - p0, nvir, nvir_pair)) + for i in range(p1 - p0): + vvv = gvvvv[i].conj().transpose(1, 0, 2) + tmp[:] = vvv - vvv.transpose(2, 1, 0) * 0.5 + lib.pack_tril(tmp + tmp.transpose(0, 2, 1), out=tmpvvvv[i]) # tril of (dvvvv[p0:p1,p0:p1]+dvvvv[p0:p1,p0:p1].T) for i in range(p0, p1): for j in range(p0, i): - tmpvvvv[i-p0,j] += tmpvvvv[j-p0,i] - tmpvvvv[i-p0,i] *= 2 + tmpvvvv[i - p0, j] += tmpvvvv[j - p0, i] + tmpvvvv[i - p0, i] *= 2 for i in range(p1, nvir): - off = i * (i+1) // 2 - dvvvv[off+p0:off+p1] = tmpvvvv[:,i] + off = i * (i + 1) // 2 + dvvvv[off + p0 : off + p1] = tmpvvvv[:, i] for i in range(p0, p1): - off = i * (i+1) // 2 + off = i * (i + 1) // 2 if p0 > 0: - tmpvvvv[i-p0,:p0] += dvvvv[off:off+p0] - dvvvv[off:off+i+1] = tmpvvvv[i-p0,:i+1] * .25 + tmpvvvv[i - p0, :p0] += dvvvv[off : off + p0] + dvvvv[off : off + i + 1] = tmpvvvv[i - p0, : i + 1] * 0.25 tmp = tmpvvvv = None else: for i in range(p0, p1): - vvv = gvvvv[i-p0].conj().transpose(1,0,2) - dvvvv[i] = vvv - vvv.transpose(2,1,0)*.5 + vvv = gvvvv[i - p0].conj().transpose(1, 0, 2) + dvvvv[i] = vvv - vvv.transpose(2, 1, 0) * 0.5 - gvovv = lib.einsum('adbc,id->aibc', gvvvv, -t1) + gvovv = lib.einsum("adbc,id->aibc", gvvvv, -t1) gvvvv = None - gvovv += lib.einsum('akic,kb->aibc', fswap['mvoOV'][p0:p1], t1) - gvovv -= lib.einsum('akib,kc->aibc', fswap['mvOOv'][p0:p1], t1) + gvovv += lib.einsum("akic,kb->aibc", fswap["mvoOV"][p0:p1], t1) + gvovv -= lib.einsum("akib,kc->aibc", fswap["mvOOv"][p0:p1], t1) - gvovv += lib.einsum('ja,jibc->aibc', l1[:,p0:p1], t2) - gvovv += lib.einsum('ja,jb,ic->aibc', l1[:,p0:p1], t1, t1) - gvovv += numpy.einsum('ba,ic->aibc', mvv[:,p0:p1]*.5, t1) + gvovv += lib.einsum("ja,jibc->aibc", l1[:, p0:p1], t2) + gvovv += lib.einsum("ja,jb,ic->aibc", l1[:, p0:p1], t1, t1) + gvovv += numpy.einsum("ba,ic->aibc", mvv[:, p0:p1] * 0.5, t1) gvovv = gvovv.conj() - gvovv += lib.einsum('ja,jibc->aibc', t1[:,p0:p1], l2) + gvovv += lib.einsum("ja,jibc->aibc", t1[:, p0:p1], l2) - dovvv[:,:,p0:p1] = gvovv.transpose(1,3,0,2)*2 - gvovv.transpose(1,2,0,3) + dovvv[:, :, p0:p1] = gvovv.transpose(1, 3, 0, 2) * 2 - gvovv.transpose(1, 2, 0, 3) gvovv = None - time1 = log.timer_debug1('rdm intermediates pass3 [%d:%d]'%(p0, p1), *time1) + time1 = log.timer_debug1("rdm intermediates pass3 [%d:%d]" % (p0, p1), *time1) fswap = None dvvov = None - return (h5fobj['dovov'], h5fobj['dvvvv'], h5fobj['doooo'], h5fobj['doovv'], - h5fobj['dovvo'], dvvov , h5fobj['dovvv'], h5fobj['dooov']) + return ( + h5fobj["dovov"], + h5fobj["dvvvv"], + h5fobj["doooo"], + h5fobj["doovv"], + h5fobj["dovvo"], + dvvov, + h5fobj["dovvv"], + h5fobj["dooov"], + ) + def make_rdm1(mycc, t1, t2, l1, l2, ao_repr=False, with_frozen=True, with_mf=True): - r''' + r""" Spin-traced one-particle density matrix in MO basis (the occupied-virtual blocks from the orbital response contribution are not included). @@ -224,130 +237,131 @@ def make_rdm1(mycc, t1, t2, l1, l2, ao_repr=False, with_frozen=True, with_mf=Tru The convention of 1-pdm is based on McWeeney's book, Eq (5.4.20). The contraction between 1-particle Hamiltonian and rdm1 is E = einsum('pq,qp', h1, rdm1) - ''' + """ d1 = _gamma1_intermediates(mycc, t1, t2, l1, l2) return _make_rdm1(mycc, d1, with_frozen=with_frozen, ao_repr=ao_repr, with_mf=with_mf) + def make_rdm2(mycc, t1, t2, l1, l2, ao_repr=False, with_frozen=True, with_dm1=True): - r''' + r""" Spin-traced two-particle density matrix in MO basis dm2[p,q,r,s] = \sum_{sigma,tau} Note the contraction between ERIs (in Chemist's notation) and rdm2 is E = einsum('pqrs,pqrs', eri, rdm2) - ''' + """ d1 = _gamma1_intermediates(mycc, t1, t2, l1, l2) f = lib.H5TmpFile() d2 = _gamma2_outcore(mycc, t1, t2, l1, l2, f, False) - return _make_rdm2(mycc, d1, d2, with_dm1=with_dm1, with_frozen=with_frozen, - ao_repr=ao_repr) + return _make_rdm2(mycc, d1, d2, with_dm1=with_dm1, with_frozen=with_frozen, ao_repr=ao_repr) + def _make_rdm1(mycc, d1, with_frozen=True, ao_repr=False, with_mf=True): - r'''dm1[p,q] = + + r"""dm1[p,q] = + The convention of 1-pdm is based on McWeeney's book, Eq (5.4.20). The contraction between 1-particle Hamiltonian and rdm1 is E = einsum('pq,qp', h1, rdm1) - ''' + """ doo, dov, dvo, dvv = d1 nocc, nvir = dov.shape nmo = nocc + nvir - dm1 = numpy.empty((nmo,nmo), dtype=doo.dtype) - dm1[:nocc,:nocc] = doo + doo.conj().T - dm1[:nocc,nocc:] = dov + dvo.conj().T - dm1[nocc:,:nocc] = dm1[:nocc,nocc:].conj().T - dm1[nocc:,nocc:] = dvv + dvv.conj().T + dm1 = numpy.empty((nmo, nmo), dtype=doo.dtype) + dm1[:nocc, :nocc] = doo + doo.conj().T + dm1[:nocc, nocc:] = dov + dvo.conj().T + dm1[nocc:, :nocc] = dm1[:nocc, nocc:].conj().T + dm1[nocc:, nocc:] = dvv + dvv.conj().T if with_mf: dm1[numpy.diag_indices(nocc)] += 2 if with_frozen and mycc.frozen is not None: nmo = mycc.mo_occ.size nocc = numpy.count_nonzero(mycc.mo_occ > 0) - rdm1 = numpy.zeros((nmo,nmo), dtype=dm1.dtype) + rdm1 = numpy.zeros((nmo, nmo), dtype=dm1.dtype) if with_mf: rdm1[numpy.diag_indices(nocc)] = 2 moidx = numpy.where(mycc.get_frozen_mask())[0] - rdm1[moidx[:,None],moidx] = dm1 + rdm1[moidx[:, None], moidx] = dm1 dm1 = rdm1 if ao_repr: mo = mycc.mo_coeff - dm1 = lib.einsum('pi,ij,qj->pq', mo, dm1, mo.conj()) + dm1 = lib.einsum("pi,ij,qj->pq", mo, dm1, mo.conj()) return dm1 + # Note vvvv part of 2pdm have been symmetrized. It does not correspond to # vvvv part of CI 2pdm def _make_rdm2(mycc, d1, d2, with_dm1=True, with_frozen=True, ao_repr=False): - r''' + r""" dm2[p,q,r,s] = \sum_{sigma,tau} Note the contraction between ERIs (in Chemist's notation) and rdm2 is E = einsum('pqrs,pqrs', eri, rdm2) - ''' + """ dovov, dvvvv, doooo, doovv, dovvo, dvvov, dovvv, dooov = d2 nocc, nvir = dovov.shape[:2] nmo = nocc + nvir - dm2 = numpy.empty((nmo,nmo,nmo,nmo), dtype=doovv.dtype) + dm2 = numpy.empty((nmo, nmo, nmo, nmo), dtype=doovv.dtype) dovov = numpy.asarray(dovov) - dm2[:nocc,nocc:,:nocc,nocc:] = dovov - dm2[:nocc,nocc:,:nocc,nocc:]+= dovov.transpose(2,3,0,1) - dm2[nocc:,:nocc,nocc:,:nocc] = dm2[:nocc,nocc:,:nocc,nocc:].transpose(1,0,3,2).conj() + dm2[:nocc, nocc:, :nocc, nocc:] = dovov + dm2[:nocc, nocc:, :nocc, nocc:] += dovov.transpose(2, 3, 0, 1) + dm2[nocc:, :nocc, nocc:, :nocc] = dm2[:nocc, nocc:, :nocc, nocc:].transpose(1, 0, 3, 2).conj() dovov = None doovv = numpy.asarray(doovv) - dm2[:nocc,:nocc,nocc:,nocc:] = doovv - dm2[:nocc,:nocc,nocc:,nocc:]+= doovv.transpose(1,0,3,2).conj() - dm2[nocc:,nocc:,:nocc,:nocc] = dm2[:nocc,:nocc,nocc:,nocc:].transpose(2,3,0,1) + dm2[:nocc, :nocc, nocc:, nocc:] = doovv + dm2[:nocc, :nocc, nocc:, nocc:] += doovv.transpose(1, 0, 3, 2).conj() + dm2[nocc:, nocc:, :nocc, :nocc] = dm2[:nocc, :nocc, nocc:, nocc:].transpose(2, 3, 0, 1) doovv = None dovvo = numpy.asarray(dovvo) - dm2[:nocc,nocc:,nocc:,:nocc] = dovvo - dm2[:nocc,nocc:,nocc:,:nocc]+= dovvo.transpose(3,2,1,0).conj() - dm2[nocc:,:nocc,:nocc,nocc:] = dm2[:nocc,nocc:,nocc:,:nocc].transpose(1,0,3,2).conj() + dm2[:nocc, nocc:, nocc:, :nocc] = dovvo + dm2[:nocc, nocc:, nocc:, :nocc] += dovvo.transpose(3, 2, 1, 0).conj() + dm2[nocc:, :nocc, :nocc, nocc:] = dm2[:nocc, nocc:, nocc:, :nocc].transpose(1, 0, 3, 2).conj() dovvo = None if dvvvv.ndim == 2: # To handle the case of compressed vvvv, which is used in nuclear gradients dvvvv = ao2mo.restore(1, dvvvv, nvir) - dm2[nocc:,nocc:,nocc:,nocc:] = dvvvv - dm2[nocc:,nocc:,nocc:,nocc:]*= 4 + dm2[nocc:, nocc:, nocc:, nocc:] = dvvvv + dm2[nocc:, nocc:, nocc:, nocc:] *= 4 else: dvvvv = numpy.asarray(dvvvv) - dm2[nocc:,nocc:,nocc:,nocc:] = dvvvv - dm2[nocc:,nocc:,nocc:,nocc:]+= dvvvv.transpose(1,0,3,2).conj() - dm2[nocc:,nocc:,nocc:,nocc:]*= 2 + dm2[nocc:, nocc:, nocc:, nocc:] = dvvvv + dm2[nocc:, nocc:, nocc:, nocc:] += dvvvv.transpose(1, 0, 3, 2).conj() + dm2[nocc:, nocc:, nocc:, nocc:] *= 2 dvvvv = None doooo = numpy.asarray(doooo) - dm2[:nocc,:nocc,:nocc,:nocc] = doooo - dm2[:nocc,:nocc,:nocc,:nocc]+= doooo.transpose(1,0,3,2).conj() - dm2[:nocc,:nocc,:nocc,:nocc]*= 2 + dm2[:nocc, :nocc, :nocc, :nocc] = doooo + dm2[:nocc, :nocc, :nocc, :nocc] += doooo.transpose(1, 0, 3, 2).conj() + dm2[:nocc, :nocc, :nocc, :nocc] *= 2 doooo = None dovvv = numpy.asarray(dovvv) - dm2[:nocc,nocc:,nocc:,nocc:] = dovvv - dm2[nocc:,nocc:,:nocc,nocc:] = dovvv.transpose(2,3,0,1) - dm2[nocc:,nocc:,nocc:,:nocc] = dovvv.transpose(3,2,1,0).conj() - dm2[nocc:,:nocc,nocc:,nocc:] = dovvv.transpose(1,0,3,2).conj() + dm2[:nocc, nocc:, nocc:, nocc:] = dovvv + dm2[nocc:, nocc:, :nocc, nocc:] = dovvv.transpose(2, 3, 0, 1) + dm2[nocc:, nocc:, nocc:, :nocc] = dovvv.transpose(3, 2, 1, 0).conj() + dm2[nocc:, :nocc, nocc:, nocc:] = dovvv.transpose(1, 0, 3, 2).conj() dovvv = None dooov = numpy.asarray(dooov) - dm2[:nocc,:nocc,:nocc,nocc:] = dooov - dm2[:nocc,nocc:,:nocc,:nocc] = dooov.transpose(2,3,0,1) - dm2[:nocc,:nocc,nocc:,:nocc] = dooov.transpose(1,0,3,2).conj() - dm2[nocc:,:nocc,:nocc,:nocc] = dooov.transpose(3,2,1,0).conj() + dm2[:nocc, :nocc, :nocc, nocc:] = dooov + dm2[:nocc, nocc:, :nocc, :nocc] = dooov.transpose(2, 3, 0, 1) + dm2[:nocc, :nocc, nocc:, :nocc] = dooov.transpose(1, 0, 3, 2).conj() + dm2[nocc:, :nocc, :nocc, :nocc] = dooov.transpose(3, 2, 1, 0).conj() if with_frozen and mycc.frozen is not None: nmo, nmo0 = mycc.mo_occ.size, nmo nocc = numpy.count_nonzero(mycc.mo_occ > 0) - rdm2 = numpy.zeros((nmo,nmo,nmo,nmo), dtype=dm2.dtype) + rdm2 = numpy.zeros((nmo, nmo, nmo, nmo), dtype=dm2.dtype) moidx = numpy.where(mycc.get_frozen_mask())[0] - idx = (moidx.reshape(-1,1) * nmo + moidx).ravel() - lib.takebak_2d(rdm2.reshape(nmo**2,nmo**2), - dm2.reshape(nmo0**2,nmo0**2), idx, idx) + idx = (moidx.reshape(-1, 1) * nmo + moidx).ravel() + lib.takebak_2d(rdm2.reshape(nmo**2, nmo**2), dm2.reshape(nmo0**2, nmo0**2), idx, idx) dm2 = rdm2 if with_dm1: @@ -355,33 +369,33 @@ def _make_rdm2(mycc, d1, d2, with_dm1=True, with_frozen=True, ao_repr=False): dm1[numpy.diag_indices(nocc)] -= 2 for i in range(nocc): - dm2[i,i,:,:] += dm1 * 2 - dm2[:,:,i,i] += dm1 * 2 - dm2[:,i,i,:] -= dm1 - dm2[i,:,:,i] -= dm1.T + dm2[i, i, :, :] += dm1 * 2 + dm2[:, :, i, i] += dm1 * 2 + dm2[:, i, i, :] -= dm1 + dm2[i, :, :, i] -= dm1.T for i in range(nocc): for j in range(nocc): - dm2[i,i,j,j] += 4 - dm2[i,j,j,i] -= 2 + dm2[i, i, j, j] += 4 + dm2[i, j, j, i] -= 2 # dm2 was computed as dm2[p,q,r,s] = < p^\dagger r^\dagger s q > in the # above. Transposing it so that it be contracted with ERIs (in Chemist's # notation): # E = einsum('pqrs,pqrs', eri, rdm2) - dm2 = dm2.transpose(1,0,3,2) + dm2 = dm2.transpose(1, 0, 3, 2) if ao_repr: - dm2 = _rdm2_mo2ao(dm2.transpose(1,0,3,2), mycc.mo_coeff) + dm2 = _rdm2_mo2ao(dm2.transpose(1, 0, 3, 2), mycc.mo_coeff) return dm2 def _rdm2_mo2ao(dm2, mo): mo_C = mo.conj() - return lib.einsum('ijkl,pi,qj,rk,sl->pqrs', dm2, mo, mo_C, mo, mo_C) + return lib.einsum("ijkl,pi,qj,rk,sl->pqrs", dm2, mo, mo_C, mo, mo_C) -if __name__ == '__main__': +if __name__ == "__main__": from functools import reduce from pyscf import gto from pyscf import scf @@ -394,98 +408,97 @@ def _rdm2_mo2ao(dm2, mo): nocc = 5 nmo = 12 nvir = nmo - nocc - eri0 = numpy.random.random((nmo,nmo,nmo,nmo)) + eri0 = numpy.random.random((nmo, nmo, nmo, nmo)) eri0 = ao2mo.restore(1, ao2mo.restore(8, eri0, nmo), nmo) - fock0 = numpy.random.random((nmo,nmo)) - fock0 = fock0 + fock0.T + numpy.diag(range(nmo))*2 - t1 = numpy.random.random((nocc,nvir)) - t2 = numpy.random.random((nocc,nocc,nvir,nvir)) - t2 = t2 + t2.transpose(1,0,3,2) - l1 = numpy.random.random((nocc,nvir)) - l2 = numpy.random.random((nocc,nocc,nvir,nvir)) - l2 = l2 + l2.transpose(1,0,3,2) - h1 = fock0 - (numpy.einsum('kkpq->pq', eri0[:nocc,:nocc])*2 - - numpy.einsum('pkkq->pq', eri0[:,:nocc,:nocc])) - - eris = lambda:None - eris.oooo = eri0[:nocc,:nocc,:nocc,:nocc].copy() - eris.ooov = eri0[:nocc,:nocc,:nocc,nocc:].copy() - eris.ovoo = eri0[:nocc,nocc:,:nocc,:nocc].copy() - eris.oovv = eri0[:nocc,:nocc,nocc:,nocc:].copy() - eris.ovov = eri0[:nocc,nocc:,:nocc,nocc:].copy() - eris.ovvo = eri0[:nocc,nocc:,nocc:,:nocc].copy() - eris.ovvv = eri0[:nocc,nocc:,nocc:,nocc:].copy() - eris.vvvv = eri0[nocc:,nocc:,nocc:,nocc:].copy() + fock0 = numpy.random.random((nmo, nmo)) + fock0 = fock0 + fock0.T + numpy.diag(range(nmo)) * 2 + t1 = numpy.random.random((nocc, nvir)) + t2 = numpy.random.random((nocc, nocc, nvir, nvir)) + t2 = t2 + t2.transpose(1, 0, 3, 2) + l1 = numpy.random.random((nocc, nvir)) + l2 = numpy.random.random((nocc, nocc, nvir, nvir)) + l2 = l2 + l2.transpose(1, 0, 3, 2) + h1 = fock0 - (numpy.einsum("kkpq->pq", eri0[:nocc, :nocc]) * 2 - numpy.einsum("pkkq->pq", eri0[:, :nocc, :nocc])) + + eris = lambda: None + eris.oooo = eri0[:nocc, :nocc, :nocc, :nocc].copy() + eris.ooov = eri0[:nocc, :nocc, :nocc, nocc:].copy() + eris.ovoo = eri0[:nocc, nocc:, :nocc, :nocc].copy() + eris.oovv = eri0[:nocc, :nocc, nocc:, nocc:].copy() + eris.ovov = eri0[:nocc, nocc:, :nocc, nocc:].copy() + eris.ovvo = eri0[:nocc, nocc:, nocc:, :nocc].copy() + eris.ovvv = eri0[:nocc, nocc:, nocc:, nocc:].copy() + eris.vvvv = eri0[nocc:, nocc:, nocc:, nocc:].copy() eris.fock = fock0 doo, dov, dvo, dvv = _gamma1_intermediates(mcc, t1, t2, l1, l2) - print((numpy.einsum('ij,ij', doo, fock0[:nocc,:nocc]))*2+20166.329861034799) - print((numpy.einsum('ab,ab', dvv, fock0[nocc:,nocc:]))*2-58078.964019246778) - print((numpy.einsum('ai,ia', dvo, fock0[:nocc,nocc:]))*2+74994.356886784764) - print((numpy.einsum('ia,ai', dov, fock0[nocc:,:nocc]))*2-34.010188025702391) + print((numpy.einsum("ij,ij", doo, fock0[:nocc, :nocc])) * 2 + 20166.329861034799) + print((numpy.einsum("ab,ab", dvv, fock0[nocc:, nocc:])) * 2 - 58078.964019246778) + print((numpy.einsum("ai,ia", dvo, fock0[:nocc, nocc:])) * 2 + 74994.356886784764) + print((numpy.einsum("ia,ai", dov, fock0[nocc:, :nocc])) * 2 - 34.010188025702391) fdm2 = lib.H5TmpFile() - dovov, dvvvv, doooo, doovv, dovvo, dvvov, dovvv, dooov = \ - _gamma2_outcore(mcc, t1, t2, l1, l2, fdm2, True) - print('dovov', lib.finger(numpy.array(dovov)) - -14384.907042073517) - print('dvvvv', lib.finger(numpy.array(dvvvv)) - -25.374007033024839) - print('doooo', lib.finger(numpy.array(doooo)) - 60.114594698129963) - print('doovv', lib.finger(numpy.array(doovv)) - -79.176348067958401) - print('dovvo', lib.finger(numpy.array(dovvo)) - 9.864134457251815) - print('dovvv', lib.finger(numpy.array(dovvv)) - -421.90333700061342) - print('dooov', lib.finger(numpy.array(dooov)) - -592.66863759586136) + dovov, dvvvv, doooo, doovv, dovvo, dvvov, dovvv, dooov = _gamma2_outcore(mcc, t1, t2, l1, l2, fdm2, True) + print("dovov", lib.finger(numpy.array(dovov)) - -14384.907042073517) + print("dvvvv", lib.finger(numpy.array(dvvvv)) - -25.374007033024839) + print("doooo", lib.finger(numpy.array(doooo)) - 60.114594698129963) + print("doovv", lib.finger(numpy.array(doovv)) - -79.176348067958401) + print("dovvo", lib.finger(numpy.array(dovvo)) - 9.864134457251815) + print("dovvv", lib.finger(numpy.array(dovvv)) - -421.90333700061342) + print("dooov", lib.finger(numpy.array(dooov)) - -592.66863759586136) fdm2 = None - dovov, dvvvv, doooo, doovv, dovvo, dvvov, dovvv, dooov = \ - _gamma2_intermediates(mcc, t1, t2, l1, l2) - print('dovov', lib.finger(numpy.array(dovov)) - -14384.907042073517) - print('dvvvv', lib.finger(numpy.array(dvvvv)) - 45.872344902116758) - print('doooo', lib.finger(numpy.array(doooo)) - 60.114594698129963) - print('doovv', lib.finger(numpy.array(doovv)) - -79.176348067958401) - print('dovvo', lib.finger(numpy.array(dovvo)) - 9.864134457251815) - print('dovvv', lib.finger(numpy.array(dovvv)) - -421.90333700061342) - print('dooov', lib.finger(numpy.array(dooov)) - -592.66863759586136) - - print('doooo',numpy.einsum('kilj,kilj', doooo, eris.oooo)*2-15939.9007625418) - print('dvvvv',numpy.einsum('acbd,acbd', dvvvv, eris.vvvv)*2-37581.823919588 ) - print('dooov',numpy.einsum('jkia,jkia', dooov, eris.ooov)*2-128470.009687716) - print('dovvv',numpy.einsum('icba,icba', dovvv, eris.ovvv)*2+166794.225195056) - print('dovov',numpy.einsum('iajb,iajb', dovov, eris.ovov)*2+719279.812916893) - print('dovvo',numpy.einsum('jbai,jbia', dovvo, eris.ovov)*2 - +numpy.einsum('jiab,jiba', doovv, eris.oovv)*2+53634.0012286654) + dovov, dvvvv, doooo, doovv, dovvo, dvvov, dovvv, dooov = _gamma2_intermediates(mcc, t1, t2, l1, l2) + print("dovov", lib.finger(numpy.array(dovov)) - -14384.907042073517) + print("dvvvv", lib.finger(numpy.array(dvvvv)) - 45.872344902116758) + print("doooo", lib.finger(numpy.array(doooo)) - 60.114594698129963) + print("doovv", lib.finger(numpy.array(doovv)) - -79.176348067958401) + print("dovvo", lib.finger(numpy.array(dovvo)) - 9.864134457251815) + print("dovvv", lib.finger(numpy.array(dovvv)) - -421.90333700061342) + print("dooov", lib.finger(numpy.array(dooov)) - -592.66863759586136) + + print("doooo", numpy.einsum("kilj,kilj", doooo, eris.oooo) * 2 - 15939.9007625418) + print("dvvvv", numpy.einsum("acbd,acbd", dvvvv, eris.vvvv) * 2 - 37581.823919588) + print("dooov", numpy.einsum("jkia,jkia", dooov, eris.ooov) * 2 - 128470.009687716) + print("dovvv", numpy.einsum("icba,icba", dovvv, eris.ovvv) * 2 + 166794.225195056) + print("dovov", numpy.einsum("iajb,iajb", dovov, eris.ovov) * 2 + 719279.812916893) + print( + "dovvo", + numpy.einsum("jbai,jbia", dovvo, eris.ovov) * 2 + + numpy.einsum("jiab,jiba", doovv, eris.oovv) * 2 + + 53634.0012286654, + ) dm1 = make_rdm1(mcc, t1, t2, l1, l2) dm2 = make_rdm2(mcc, t1, t2, l1, l2) - e2 = (numpy.einsum('ijkl,ijkl', doooo, eris.oooo)*2 + - numpy.einsum('acbd,acbd', dvvvv, eris.vvvv)*2 + - numpy.einsum('jkia,jkia', dooov, eris.ooov)*2 + - numpy.einsum('icba,icba', dovvv, eris.ovvv)*2 + - numpy.einsum('iajb,iajb', dovov, eris.ovov)*2 + - numpy.einsum('jbai,jbia', dovvo, eris.ovov)*2 + - numpy.einsum('ijab,ijab', doovv, eris.oovv)*2 + - numpy.einsum('ij,ij', doo, fock0[:nocc,:nocc])*2 + - numpy.einsum('ia,ia', dov, fock0[:nocc,nocc:])*2 + - numpy.einsum('ai,ai', dvo, fock0[nocc:,:nocc])*2 + - numpy.einsum('ab,ab', dvv, fock0[nocc:,nocc:])*2 + - fock0[:nocc].trace()*2 - - numpy.einsum('kkpq->pq', eri0[:nocc,:nocc,:nocc,:nocc]).trace()*2 + - numpy.einsum('pkkq->pq', eri0[:nocc,:nocc,:nocc,:nocc]).trace()) - print(e2+794721.197459942) - print(numpy.einsum('pqrs,pqrs', dm2, eri0)*.5 + - numpy.einsum('pq,qp', dm1, h1) - e2) - - print(numpy.allclose(dm2, dm2.transpose(1,0,3,2))) - print(numpy.allclose(dm2, dm2.transpose(2,3,0,1))) - - d1 = numpy.einsum('kkpq->qp', dm2) / 9 + e2 = ( + numpy.einsum("ijkl,ijkl", doooo, eris.oooo) * 2 + + numpy.einsum("acbd,acbd", dvvvv, eris.vvvv) * 2 + + numpy.einsum("jkia,jkia", dooov, eris.ooov) * 2 + + numpy.einsum("icba,icba", dovvv, eris.ovvv) * 2 + + numpy.einsum("iajb,iajb", dovov, eris.ovov) * 2 + + numpy.einsum("jbai,jbia", dovvo, eris.ovov) * 2 + + numpy.einsum("ijab,ijab", doovv, eris.oovv) * 2 + + numpy.einsum("ij,ij", doo, fock0[:nocc, :nocc]) * 2 + + numpy.einsum("ia,ia", dov, fock0[:nocc, nocc:]) * 2 + + numpy.einsum("ai,ai", dvo, fock0[nocc:, :nocc]) * 2 + + numpy.einsum("ab,ab", dvv, fock0[nocc:, nocc:]) * 2 + + fock0[:nocc].trace() * 2 + - numpy.einsum("kkpq->pq", eri0[:nocc, :nocc, :nocc, :nocc]).trace() * 2 + + numpy.einsum("pkkq->pq", eri0[:nocc, :nocc, :nocc, :nocc]).trace() + ) + print(e2 + 794721.197459942) + print(numpy.einsum("pqrs,pqrs", dm2, eri0) * 0.5 + numpy.einsum("pq,qp", dm1, h1) - e2) + + print(numpy.allclose(dm2, dm2.transpose(1, 0, 3, 2))) + print(numpy.allclose(dm2, dm2.transpose(2, 3, 0, 1))) + + d1 = numpy.einsum("kkpq->qp", dm2) / 9 print(numpy.allclose(d1, dm1)) mol = gto.Mole() - mol.atom = [ - [8 , (0. , 0. , 0.)], - [1 , (0. , -0.757 , 0.587)], - [1 , (0. , 0.757 , 0.587)]] - mol.basis = '631g' + mol.atom = [[8, (0.0, 0.0, 0.0)], [1, (0.0, -0.757, 0.587)], [1, (0.0, 0.757, 0.587)]] + mol.basis = "631g" mol.build() mf = scf.RHF(mol).run() @@ -496,10 +509,10 @@ def _rdm2_mo2ao(dm2, mo): dm1 = make_rdm1(mycc, t1, t2, l1, l2) dm2 = make_rdm2(mycc, t1, t2, l1, l2) nmo = mf.mo_coeff.shape[1] - eri = ao2mo.kernel(mf._eri, mf.mo_coeff, compact=False).reshape([nmo]*4) + eri = ao2mo.kernel(mf._eri, mf.mo_coeff, compact=False).reshape([nmo] * 4) hcore = mf.get_hcore() h1 = reduce(numpy.dot, (mf.mo_coeff.T, hcore, mf.mo_coeff)) - e1 = numpy.einsum('ij,ji', h1, dm1) - e1+= numpy.einsum('ijkl,ijkl', eri, dm2) * .5 - e1+= mol.energy_nuc() + e1 = numpy.einsum("ij,ji", h1, dm1) + e1 += numpy.einsum("ijkl,ijkl", eri, dm2) * 0.5 + e1 += mol.energy_nuc() print(e1 - mycc.e_tot) diff --git a/vayesta/core/vpyscf/uccsd_rdm.py b/vayesta/core/vpyscf/uccsd_rdm.py index 7e096973f..e7878bbce 100644 --- a/vayesta/core/vpyscf/uccsd_rdm.py +++ b/vayesta/core/vpyscf/uccsd_rdm.py @@ -21,10 +21,11 @@ from pyscf import lib from pyscf import ao2mo -#einsum = numpy.einsum +# einsum = numpy.einsum einsum = lib.einsum -#TODO: optimize memory use +# TODO: optimize memory use + def _gamma1_intermediates(cc, t1, t2, l1, l2): t1a, t1b = t1 @@ -34,46 +35,46 @@ def _gamma1_intermediates(cc, t1, t2, l1, l2): nocca, nvira = t1a.shape noccb, nvirb = t1b.shape - tmpA = einsum('imef,jmef->ij', l2ab, t2ab) - tmpA += einsum('imef,jmef->ij', l2aa, t2aa) * .5 + tmpA = einsum("imef,jmef->ij", l2ab, t2ab) + tmpA += einsum("imef,jmef->ij", l2aa, t2aa) * 0.5 - tmpB = einsum('mief,mjef->ij', l2ab, t2ab) - tmpB += einsum('imef,jmef->ij', l2bb, t2bb) * .5 + tmpB = einsum("mief,mjef->ij", l2ab, t2ab) + tmpB += einsum("imef,jmef->ij", l2bb, t2bb) * 0.5 - tmpC = einsum('mnae,mnbe->ab', t2ab, l2ab) - tmpC += einsum('mnae,mnbe->ab', t2aa, l2aa) * .5 + tmpC = einsum("mnae,mnbe->ab", t2ab, l2ab) + tmpC += einsum("mnae,mnbe->ab", t2aa, l2aa) * 0.5 - tmpD = einsum('mnea,mneb->ab', t2ab, l2ab) - tmpD += einsum('mnae,mnbe->ab', t2bb, l2bb) * .5 + tmpD = einsum("mnea,mneb->ab", t2ab, l2ab) + tmpD += einsum("mnae,mnbe->ab", t2bb, l2bb) * 0.5 - dooa = -einsum('ie,je->ij', l1a, t1a) + dooa = -einsum("ie,je->ij", l1a, t1a) dooa -= tmpA - doob = -einsum('ie,je->ij', l1b, t1b) + doob = -einsum("ie,je->ij", l1b, t1b) doob -= tmpB - dvva = einsum('ma,mb->ab', t1a, l1a) + dvva = einsum("ma,mb->ab", t1a, l1a) dvva += tmpC - dvvb = einsum('ma,mb->ab', t1b, l1b) + dvvb = einsum("ma,mb->ab", t1b, l1b) dvvb += tmpD xt1a = tmpA xt2a = tmpC - xt2a += einsum('ma,me->ae', t1a, l1a) + xt2a += einsum("ma,me->ae", t1a, l1a) xt1b = tmpB xt2b = tmpD - xt2b += einsum('ma,me->ae', t1b, l1b) + xt2b += einsum("ma,me->ae", t1b, l1b) - dvoa = numpy.einsum('imae,me->ai', t2aa, l1a) - dvoa += numpy.einsum('imae,me->ai', t2ab, l1b) - dvoa -= einsum('mi,ma->ai', xt1a, t1a) - dvoa -= einsum('ie,ae->ai', t1a, xt2a) + dvoa = numpy.einsum("imae,me->ai", t2aa, l1a) + dvoa += numpy.einsum("imae,me->ai", t2ab, l1b) + dvoa -= einsum("mi,ma->ai", xt1a, t1a) + dvoa -= einsum("ie,ae->ai", t1a, xt2a) dvoa += t1a.T - dvob = numpy.einsum('imae,me->ai', t2bb, l1b) - dvob += numpy.einsum('miea,me->ai', t2ab, l1a) - dvob -= einsum('mi,ma->ai', xt1b, t1b) - dvob -= einsum('ie,ae->ai', t1b, xt2b) + dvob = numpy.einsum("imae,me->ai", t2bb, l1b) + dvob += numpy.einsum("miea,me->ai", t2ab, l1a) + dvob -= einsum("mi,ma->ai", xt1b, t1b) + dvob -= einsum("ie,ae->ai", t1b, xt2b) dvob += t1b.T dova = l1a @@ -81,238 +82,239 @@ def _gamma1_intermediates(cc, t1, t2, l1, l2): return ((dooa, doob), (dova, dovb), (dvoa, dvob), (dvva, dvvb)) + # gamma2 intermediates in Chemist's notation -#TODO: hold d2 intermediates in h5fobj +# TODO: hold d2 intermediates in h5fobj def _gamma2_outcore(cc, t1, t2, l1, l2, h5fobj, compress_vvvv=False): t1a, t1b = t1 t2aa, t2ab, t2bb = t2 l1a, l1b = l1 l2aa, l2ab, l2bb = l2 - tauaa = t2aa + numpy.einsum('ia,jb->ijab', 2*t1a, t1a) - tauab = t2ab + numpy.einsum('ia,jb->ijab', t1a, t1b) - taubb = t2bb + numpy.einsum('ia,jb->ijab', 2*t1b, t1b) - miajb = einsum('ikac,kjcb->iajb', l2aa, t2aa) - miajb+= einsum('ikac,jkbc->iajb', l2ab, t2ab) - miaJB = einsum('ikac,kjcb->iajb', l2aa, t2ab) - miaJB+= einsum('ikac,kjcb->iajb', l2ab, t2bb) - mIAjb = einsum('kica,jkbc->iajb', l2bb, t2ab) - mIAjb+= einsum('kica,kjcb->iajb', l2ab, t2aa) - mIAJB = einsum('ikac,kjcb->iajb', l2bb, t2bb) - mIAJB+= einsum('kica,kjcb->iajb', l2ab, t2ab) - miAjB = einsum('ikca,jkcb->iajb', l2ab, t2ab) - mIaJb = einsum('kiac,kjbc->iajb', l2ab, t2ab) - - goovv = (l2aa.conj() + tauaa) * .25 - goOvV = (l2ab.conj() + tauab) * .5 - gOOVV = (l2bb.conj() + taubb) * .25 - - tmpa = einsum('kc,kica->ia', l1a, t2aa) - tmpa += einsum('kc,ikac->ia', l1b, t2ab) - tmpb = einsum('kc,kica->ia', l1b, t2bb) - tmpb += einsum('kc,kica->ia', l1a, t2ab) - goovv += einsum('ia,jb->ijab', tmpa, t1a) - goOvV += einsum('ia,jb->ijab', tmpa, t1b) * .5 - goOvV += einsum('ia,jb->jiba', tmpb, t1a) * .5 - gOOVV += einsum('ia,jb->ijab', tmpb, t1b) - - tmpa = einsum('kc,kb->cb', l1a, t1a) - tmpb = einsum('kc,kb->cb', l1b, t1b) - goovv += einsum('cb,ijca->ijab', tmpa, t2aa) * .5 - goOvV -= einsum('cb,ijac->ijab', tmpb, t2ab) * .5 - goOvV -= einsum('cb,jica->jiba', tmpa, t2ab) * .5 - gOOVV += einsum('cb,ijca->ijab', tmpb, t2bb) * .5 - tmpa = einsum('kc,jc->kj', l1a, t1a) - tmpb = einsum('kc,jc->kj', l1b, t1b) - goovv += einsum('kiab,kj->ijab', tauaa, tmpa) * .5 - goOvV -= einsum('ikab,kj->ijab', tauab , tmpb) * .5 - goOvV -= einsum('kiba,kj->jiba', tauab , tmpa) * .5 - gOOVV += einsum('kiab,kj->ijab', taubb, tmpb) * .5 - - tmpa = numpy.einsum('ldjd->lj', miajb) - tmpa += numpy.einsum('ldjd->lj', miAjB) - tmpb = numpy.einsum('ldjd->lj', mIAJB) - tmpb += numpy.einsum('ldjd->lj', mIaJb) - goovv -= einsum('lj,liba->ijab', tmpa, tauaa) * .25 - goOvV -= einsum('lj,ilab->ijab', tmpb, tauab) * .25 - goOvV -= einsum('lj,liba->jiba', tmpa, tauab) * .25 - gOOVV -= einsum('lj,liba->ijab', tmpb, taubb) * .25 - tmpa = numpy.einsum('ldlb->db', miajb) - tmpa += numpy.einsum('ldlb->db', mIaJb) - tmpb = numpy.einsum('ldlb->db', mIAJB) - tmpb += numpy.einsum('ldlb->db', miAjB) - goovv -= einsum('db,jida->ijab', tmpa, tauaa) * .25 - goOvV -= einsum('db,ijad->ijab', tmpb, tauab) * .25 - goOvV -= einsum('db,jida->jiba', tmpa, tauab) * .25 - gOOVV -= einsum('db,jida->ijab', tmpb, taubb) * .25 - - goovv -= einsum('ldia,ljbd->ijab', miajb, tauaa) * .5 - goovv += einsum('LDia,jLbD->ijab', mIAjb, t2ab ) * .5 - gOOVV -= einsum('ldia,ljbd->ijab', mIAJB, taubb) * .5 - gOOVV += einsum('ldia,ljdb->ijab', miaJB, t2ab ) * .5 - goOvV -= einsum('LDia,LJBD->iJaB', mIAjb, taubb) * .25 - goOvV += einsum('ldia,lJdB->iJaB', miajb, t2ab ) * .25 - goOvV -= einsum('ldIA,ljbd->jIbA', miaJB, tauaa) * .25 - goOvV += einsum('LDIA,jLbD->jIbA', mIAJB, t2ab ) * .25 - goOvV += einsum('lDiA,lJbD->iJbA', miAjB, tauab) * .5 - goOvV += einsum('LdIa,jd,LB->jIaB', mIaJb, t1a, t1b) * .5 - - tmpaa = einsum('klcd,ijcd->ijkl', l2aa, tauaa) * .25**2 - tmpbb = einsum('klcd,ijcd->ijkl', l2bb, taubb) * .25**2 - tmpabab = einsum('kLcD,iJcD->iJkL', l2ab, tauab) * .5 - goovv += einsum('ijkl,klab->ijab', tmpaa, tauaa) - goOvV += einsum('ijkl,klab->ijab', tmpabab, tauab) - gOOVV += einsum('ijkl,klab->ijab', tmpbb, taubb) + tauaa = t2aa + numpy.einsum("ia,jb->ijab", 2 * t1a, t1a) + tauab = t2ab + numpy.einsum("ia,jb->ijab", t1a, t1b) + taubb = t2bb + numpy.einsum("ia,jb->ijab", 2 * t1b, t1b) + miajb = einsum("ikac,kjcb->iajb", l2aa, t2aa) + miajb += einsum("ikac,jkbc->iajb", l2ab, t2ab) + miaJB = einsum("ikac,kjcb->iajb", l2aa, t2ab) + miaJB += einsum("ikac,kjcb->iajb", l2ab, t2bb) + mIAjb = einsum("kica,jkbc->iajb", l2bb, t2ab) + mIAjb += einsum("kica,kjcb->iajb", l2ab, t2aa) + mIAJB = einsum("ikac,kjcb->iajb", l2bb, t2bb) + mIAJB += einsum("kica,kjcb->iajb", l2ab, t2ab) + miAjB = einsum("ikca,jkcb->iajb", l2ab, t2ab) + mIaJb = einsum("kiac,kjbc->iajb", l2ab, t2ab) + + goovv = (l2aa.conj() + tauaa) * 0.25 + goOvV = (l2ab.conj() + tauab) * 0.5 + gOOVV = (l2bb.conj() + taubb) * 0.25 + + tmpa = einsum("kc,kica->ia", l1a, t2aa) + tmpa += einsum("kc,ikac->ia", l1b, t2ab) + tmpb = einsum("kc,kica->ia", l1b, t2bb) + tmpb += einsum("kc,kica->ia", l1a, t2ab) + goovv += einsum("ia,jb->ijab", tmpa, t1a) + goOvV += einsum("ia,jb->ijab", tmpa, t1b) * 0.5 + goOvV += einsum("ia,jb->jiba", tmpb, t1a) * 0.5 + gOOVV += einsum("ia,jb->ijab", tmpb, t1b) + + tmpa = einsum("kc,kb->cb", l1a, t1a) + tmpb = einsum("kc,kb->cb", l1b, t1b) + goovv += einsum("cb,ijca->ijab", tmpa, t2aa) * 0.5 + goOvV -= einsum("cb,ijac->ijab", tmpb, t2ab) * 0.5 + goOvV -= einsum("cb,jica->jiba", tmpa, t2ab) * 0.5 + gOOVV += einsum("cb,ijca->ijab", tmpb, t2bb) * 0.5 + tmpa = einsum("kc,jc->kj", l1a, t1a) + tmpb = einsum("kc,jc->kj", l1b, t1b) + goovv += einsum("kiab,kj->ijab", tauaa, tmpa) * 0.5 + goOvV -= einsum("ikab,kj->ijab", tauab, tmpb) * 0.5 + goOvV -= einsum("kiba,kj->jiba", tauab, tmpa) * 0.5 + gOOVV += einsum("kiab,kj->ijab", taubb, tmpb) * 0.5 + + tmpa = numpy.einsum("ldjd->lj", miajb) + tmpa += numpy.einsum("ldjd->lj", miAjB) + tmpb = numpy.einsum("ldjd->lj", mIAJB) + tmpb += numpy.einsum("ldjd->lj", mIaJb) + goovv -= einsum("lj,liba->ijab", tmpa, tauaa) * 0.25 + goOvV -= einsum("lj,ilab->ijab", tmpb, tauab) * 0.25 + goOvV -= einsum("lj,liba->jiba", tmpa, tauab) * 0.25 + gOOVV -= einsum("lj,liba->ijab", tmpb, taubb) * 0.25 + tmpa = numpy.einsum("ldlb->db", miajb) + tmpa += numpy.einsum("ldlb->db", mIaJb) + tmpb = numpy.einsum("ldlb->db", mIAJB) + tmpb += numpy.einsum("ldlb->db", miAjB) + goovv -= einsum("db,jida->ijab", tmpa, tauaa) * 0.25 + goOvV -= einsum("db,ijad->ijab", tmpb, tauab) * 0.25 + goOvV -= einsum("db,jida->jiba", tmpa, tauab) * 0.25 + gOOVV -= einsum("db,jida->ijab", tmpb, taubb) * 0.25 + + goovv -= einsum("ldia,ljbd->ijab", miajb, tauaa) * 0.5 + goovv += einsum("LDia,jLbD->ijab", mIAjb, t2ab) * 0.5 + gOOVV -= einsum("ldia,ljbd->ijab", mIAJB, taubb) * 0.5 + gOOVV += einsum("ldia,ljdb->ijab", miaJB, t2ab) * 0.5 + goOvV -= einsum("LDia,LJBD->iJaB", mIAjb, taubb) * 0.25 + goOvV += einsum("ldia,lJdB->iJaB", miajb, t2ab) * 0.25 + goOvV -= einsum("ldIA,ljbd->jIbA", miaJB, tauaa) * 0.25 + goOvV += einsum("LDIA,jLbD->jIbA", mIAJB, t2ab) * 0.25 + goOvV += einsum("lDiA,lJbD->iJbA", miAjB, tauab) * 0.5 + goOvV += einsum("LdIa,jd,LB->jIaB", mIaJb, t1a, t1b) * 0.5 + + tmpaa = einsum("klcd,ijcd->ijkl", l2aa, tauaa) * 0.25**2 + tmpbb = einsum("klcd,ijcd->ijkl", l2bb, taubb) * 0.25**2 + tmpabab = einsum("kLcD,iJcD->iJkL", l2ab, tauab) * 0.5 + goovv += einsum("ijkl,klab->ijab", tmpaa, tauaa) + goOvV += einsum("ijkl,klab->ijab", tmpabab, tauab) + gOOVV += einsum("ijkl,klab->ijab", tmpbb, taubb) goovv = goovv.conj() goOvV = goOvV.conj() gOOVV = gOOVV.conj() - gvvvv = einsum('ijab,ijcd->abcd', tauaa, l2aa) * .125 - gvVvV = einsum('ijab,ijcd->abcd', tauab, l2ab) * .25 - gVVVV = einsum('ijab,ijcd->abcd', taubb, l2bb) * .125 - - goooo = einsum('ijab,klab->ijkl', l2aa, tauaa) * .125 - goOoO = einsum('ijab,klab->ijkl', l2ab, tauab) * .25 - gOOOO = einsum('ijab,klab->ijkl', l2bb, taubb) * .125 - - gooov = einsum('jkba,ib->jkia', tauaa, -0.25 * l1a) - goOoV = einsum('jkba,ib->jkia', tauab, -0.5 * l1a) - gOoOv = einsum('kjab,ib->jkia', tauab, -0.5 * l1b) - gOOOV = einsum('jkba,ib->jkia', taubb, -0.25 * l1b) - - gooov += einsum('iljk,la->jkia', goooo, t1a) - goOoV += einsum('iljk,la->jkia', goOoO, t1b) * 2 - gOoOv += einsum('likj,la->jkia', goOoO, t1a) * 2 - gOOOV += einsum('iljk,la->jkia', gOOOO, t1b) - - tmpa = numpy.einsum('icjc->ij', miajb) * .25 - tmpa += numpy.einsum('icjc->ij', miAjB) * .25 - tmpb = numpy.einsum('icjc->ij', mIAJB) * .25 - tmpb += numpy.einsum('icjc->ij', mIaJb) * .25 - gooov -= einsum('ij,ka->jkia', tmpa, t1a) - goOoV -= einsum('ij,ka->jkia', tmpa, t1b) - gOoOv -= einsum('ij,ka->jkia', tmpb, t1a) - gOOOV -= einsum('ij,ka->jkia', tmpb, t1b) - - gooov += einsum('icja,kc->jkia', miajb, .5 * t1a) - goOoV += einsum('icja,kc->jkia', miAjB, .5 * t1b) - goOoV -= einsum('icJA,kc->kJiA', miaJB, .5 * t1a) - gOoOv += einsum('icja,kc->jkia', mIaJb, .5 * t1a) - gOoOv -= einsum('ICja,KC->KjIa', mIAjb, .5 * t1b) - gOOOV += einsum('icja,kc->jkia', mIAJB, .5 * t1b) + gvvvv = einsum("ijab,ijcd->abcd", tauaa, l2aa) * 0.125 + gvVvV = einsum("ijab,ijcd->abcd", tauab, l2ab) * 0.25 + gVVVV = einsum("ijab,ijcd->abcd", taubb, l2bb) * 0.125 + + goooo = einsum("ijab,klab->ijkl", l2aa, tauaa) * 0.125 + goOoO = einsum("ijab,klab->ijkl", l2ab, tauab) * 0.25 + gOOOO = einsum("ijab,klab->ijkl", l2bb, taubb) * 0.125 + + gooov = einsum("jkba,ib->jkia", tauaa, -0.25 * l1a) + goOoV = einsum("jkba,ib->jkia", tauab, -0.5 * l1a) + gOoOv = einsum("kjab,ib->jkia", tauab, -0.5 * l1b) + gOOOV = einsum("jkba,ib->jkia", taubb, -0.25 * l1b) + + gooov += einsum("iljk,la->jkia", goooo, t1a) + goOoV += einsum("iljk,la->jkia", goOoO, t1b) * 2 + gOoOv += einsum("likj,la->jkia", goOoO, t1a) * 2 + gOOOV += einsum("iljk,la->jkia", gOOOO, t1b) + + tmpa = numpy.einsum("icjc->ij", miajb) * 0.25 + tmpa += numpy.einsum("icjc->ij", miAjB) * 0.25 + tmpb = numpy.einsum("icjc->ij", mIAJB) * 0.25 + tmpb += numpy.einsum("icjc->ij", mIaJb) * 0.25 + gooov -= einsum("ij,ka->jkia", tmpa, t1a) + goOoV -= einsum("ij,ka->jkia", tmpa, t1b) + gOoOv -= einsum("ij,ka->jkia", tmpb, t1a) + gOOOV -= einsum("ij,ka->jkia", tmpb, t1b) + + gooov += einsum("icja,kc->jkia", miajb, 0.5 * t1a) + goOoV += einsum("icja,kc->jkia", miAjB, 0.5 * t1b) + goOoV -= einsum("icJA,kc->kJiA", miaJB, 0.5 * t1a) + gOoOv += einsum("icja,kc->jkia", mIaJb, 0.5 * t1a) + gOoOv -= einsum("ICja,KC->KjIa", mIAjb, 0.5 * t1b) + gOOOV += einsum("icja,kc->jkia", mIAJB, 0.5 * t1b) gooov = gooov.conj() goOoV = goOoV.conj() gOoOv = gOoOv.conj() gOOOV = gOOOV.conj() - gooov += einsum('jkab,ib->jkia', l2aa, .25*t1a) - goOoV -= einsum('jkba,ib->jkia', l2ab, .5 *t1a) - gOoOv -= einsum('kjab,ib->jkia', l2ab, .5 *t1b) - gOOOV += einsum('jkab,ib->jkia', l2bb, .25*t1b) - - govvv = einsum('ja,ijcb->iacb', .25 * l1a, tauaa) - goVvV = einsum('ja,ijcb->iacb', .5 * l1b, tauab) - gOvVv = einsum('ja,jibc->iacb', .5 * l1a, tauab) - gOVVV = einsum('ja,ijcb->iacb', .25 * l1b, taubb) - - govvv += einsum('bcad,id->iabc', gvvvv, t1a) - goVvV -= einsum('bcda,id->iabc', gvVvV, t1a) * 2 - gOvVv -= einsum('cbad,id->iabc', gvVvV, t1b) * 2 - gOVVV += einsum('bcad,id->iabc', gVVVV, t1b) - - tmpa = numpy.einsum('kakb->ab', miajb) * .25 - tmpa += numpy.einsum('kakb->ab', mIaJb) * .25 - tmpb = numpy.einsum('kakb->ab', mIAJB) * .25 - tmpb += numpy.einsum('kakb->ab', miAjB) * .25 - govvv += einsum('ab,ic->iacb', tmpa, t1a) - goVvV += einsum('ab,ic->iacb', tmpb, t1a) - gOvVv += einsum('ab,ic->iacb', tmpa, t1b) - gOVVV += einsum('ab,ic->iacb', tmpb, t1b) - - govvv += einsum('kaib,kc->iabc', miajb, .5 * t1a) - goVvV += einsum('KAib,KC->iAbC', mIAjb, .5 * t1b) - goVvV -= einsum('kAiB,kc->iAcB', miAjB, .5 * t1a) - gOvVv += einsum('kaIB,kc->IaBc', miaJB, .5 * t1a) - gOvVv -= einsum('KaIb,KC->IaCb', mIaJb, .5 * t1b) - gOVVV += einsum('kaib,kc->iabc', mIAJB, .5 * t1b) + gooov += einsum("jkab,ib->jkia", l2aa, 0.25 * t1a) + goOoV -= einsum("jkba,ib->jkia", l2ab, 0.5 * t1a) + gOoOv -= einsum("kjab,ib->jkia", l2ab, 0.5 * t1b) + gOOOV += einsum("jkab,ib->jkia", l2bb, 0.25 * t1b) + + govvv = einsum("ja,ijcb->iacb", 0.25 * l1a, tauaa) + goVvV = einsum("ja,ijcb->iacb", 0.5 * l1b, tauab) + gOvVv = einsum("ja,jibc->iacb", 0.5 * l1a, tauab) + gOVVV = einsum("ja,ijcb->iacb", 0.25 * l1b, taubb) + + govvv += einsum("bcad,id->iabc", gvvvv, t1a) + goVvV -= einsum("bcda,id->iabc", gvVvV, t1a) * 2 + gOvVv -= einsum("cbad,id->iabc", gvVvV, t1b) * 2 + gOVVV += einsum("bcad,id->iabc", gVVVV, t1b) + + tmpa = numpy.einsum("kakb->ab", miajb) * 0.25 + tmpa += numpy.einsum("kakb->ab", mIaJb) * 0.25 + tmpb = numpy.einsum("kakb->ab", mIAJB) * 0.25 + tmpb += numpy.einsum("kakb->ab", miAjB) * 0.25 + govvv += einsum("ab,ic->iacb", tmpa, t1a) + goVvV += einsum("ab,ic->iacb", tmpb, t1a) + gOvVv += einsum("ab,ic->iacb", tmpa, t1b) + gOVVV += einsum("ab,ic->iacb", tmpb, t1b) + + govvv += einsum("kaib,kc->iabc", miajb, 0.5 * t1a) + goVvV += einsum("KAib,KC->iAbC", mIAjb, 0.5 * t1b) + goVvV -= einsum("kAiB,kc->iAcB", miAjB, 0.5 * t1a) + gOvVv += einsum("kaIB,kc->IaBc", miaJB, 0.5 * t1a) + gOvVv -= einsum("KaIb,KC->IaCb", mIaJb, 0.5 * t1b) + gOVVV += einsum("kaib,kc->iabc", mIAJB, 0.5 * t1b) govvv = govvv.conj() goVvV = goVvV.conj() gOvVv = gOvVv.conj() gOVVV = gOVVV.conj() - govvv += einsum('ijbc,ja->iabc', l2aa, .25*t1a) - goVvV += einsum('iJbC,JA->iAbC', l2ab, .5 *t1b) - gOvVv += einsum('jIcB,ja->IaBc', l2ab, .5 *t1a) - gOVVV += einsum('ijbc,ja->iabc', l2bb, .25*t1b) - - govvo = einsum('ia,jb->ibaj', l1a, t1a) - goVvO = einsum('ia,jb->ibaj', l1a, t1b) - gOvVo = einsum('ia,jb->ibaj', l1b, t1a) - gOVVO = einsum('ia,jb->ibaj', l1b, t1b) - - govvo += numpy.einsum('iajb->ibaj', miajb) - goVvO += numpy.einsum('iajb->ibaj', miaJB) - gOvVo += numpy.einsum('iajb->ibaj', mIAjb) - gOVVO += numpy.einsum('iajb->ibaj', mIAJB) - goVoV = numpy.einsum('iajb->ibja', miAjB) - gOvOv = numpy.einsum('iajb->ibja', mIaJb) - - govvo -= einsum('ikac,jc,kb->ibaj', l2aa, t1a, t1a) - goVvO -= einsum('iKaC,JC,KB->iBaJ', l2ab, t1b, t1b) - gOvVo -= einsum('kIcA,jc,kb->IbAj', l2ab, t1a, t1a) - gOVVO -= einsum('ikac,jc,kb->ibaj', l2bb, t1b, t1b) - goVoV += einsum('iKcA,jc,KB->iBjA', l2ab, t1a, t1b) - gOvOv += einsum('kIaC,JC,kb->IbJa', l2ab, t1b, t1a) - - dovov = goovv.transpose(0,2,1,3) - goovv.transpose(0,3,1,2) - dvvvv = gvvvv.transpose(0,2,1,3) - gvvvv.transpose(0,3,1,2) - doooo = goooo.transpose(0,2,1,3) - goooo.transpose(0,3,1,2) - dovvv = govvv.transpose(0,2,1,3) - govvv.transpose(0,3,1,2) - dooov = gooov.transpose(0,2,1,3) - gooov.transpose(1,2,0,3) - dovvo = govvo.transpose(0,2,1,3) - dovov =(dovov + dovov.transpose(2,3,0,1)) * .5 - dvvvv = dvvvv + dvvvv.transpose(1,0,3,2).conj() - doooo = doooo + doooo.transpose(1,0,3,2).conj() - dovvo =(dovvo + dovvo.transpose(3,2,1,0).conj()) * .5 - doovv =-dovvo.transpose(0,3,2,1) + govvv += einsum("ijbc,ja->iabc", l2aa, 0.25 * t1a) + goVvV += einsum("iJbC,JA->iAbC", l2ab, 0.5 * t1b) + gOvVv += einsum("jIcB,ja->IaBc", l2ab, 0.5 * t1a) + gOVVV += einsum("ijbc,ja->iabc", l2bb, 0.25 * t1b) + + govvo = einsum("ia,jb->ibaj", l1a, t1a) + goVvO = einsum("ia,jb->ibaj", l1a, t1b) + gOvVo = einsum("ia,jb->ibaj", l1b, t1a) + gOVVO = einsum("ia,jb->ibaj", l1b, t1b) + + govvo += numpy.einsum("iajb->ibaj", miajb) + goVvO += numpy.einsum("iajb->ibaj", miaJB) + gOvVo += numpy.einsum("iajb->ibaj", mIAjb) + gOVVO += numpy.einsum("iajb->ibaj", mIAJB) + goVoV = numpy.einsum("iajb->ibja", miAjB) + gOvOv = numpy.einsum("iajb->ibja", mIaJb) + + govvo -= einsum("ikac,jc,kb->ibaj", l2aa, t1a, t1a) + goVvO -= einsum("iKaC,JC,KB->iBaJ", l2ab, t1b, t1b) + gOvVo -= einsum("kIcA,jc,kb->IbAj", l2ab, t1a, t1a) + gOVVO -= einsum("ikac,jc,kb->ibaj", l2bb, t1b, t1b) + goVoV += einsum("iKcA,jc,KB->iBjA", l2ab, t1a, t1b) + gOvOv += einsum("kIaC,JC,kb->IbJa", l2ab, t1b, t1a) + + dovov = goovv.transpose(0, 2, 1, 3) - goovv.transpose(0, 3, 1, 2) + dvvvv = gvvvv.transpose(0, 2, 1, 3) - gvvvv.transpose(0, 3, 1, 2) + doooo = goooo.transpose(0, 2, 1, 3) - goooo.transpose(0, 3, 1, 2) + dovvv = govvv.transpose(0, 2, 1, 3) - govvv.transpose(0, 3, 1, 2) + dooov = gooov.transpose(0, 2, 1, 3) - gooov.transpose(1, 2, 0, 3) + dovvo = govvo.transpose(0, 2, 1, 3) + dovov = (dovov + dovov.transpose(2, 3, 0, 1)) * 0.5 + dvvvv = dvvvv + dvvvv.transpose(1, 0, 3, 2).conj() + doooo = doooo + doooo.transpose(1, 0, 3, 2).conj() + dovvo = (dovvo + dovvo.transpose(3, 2, 1, 0).conj()) * 0.5 + doovv = -dovvo.transpose(0, 3, 2, 1) dvvov = None - dOVOV = gOOVV.transpose(0,2,1,3) - gOOVV.transpose(0,3,1,2) - dVVVV = gVVVV.transpose(0,2,1,3) - gVVVV.transpose(0,3,1,2) - dOOOO = gOOOO.transpose(0,2,1,3) - gOOOO.transpose(0,3,1,2) - dOVVV = gOVVV.transpose(0,2,1,3) - gOVVV.transpose(0,3,1,2) - dOOOV = gOOOV.transpose(0,2,1,3) - gOOOV.transpose(1,2,0,3) - dOVVO = gOVVO.transpose(0,2,1,3) - dOVOV =(dOVOV + dOVOV.transpose(2,3,0,1)) * .5 - dVVVV = dVVVV + dVVVV.transpose(1,0,3,2).conj() - dOOOO = dOOOO + dOOOO.transpose(1,0,3,2).conj() - dOVVO =(dOVVO + dOVVO.transpose(3,2,1,0).conj()) * .5 - dOOVV =-dOVVO.transpose(0,3,2,1) + dOVOV = gOOVV.transpose(0, 2, 1, 3) - gOOVV.transpose(0, 3, 1, 2) + dVVVV = gVVVV.transpose(0, 2, 1, 3) - gVVVV.transpose(0, 3, 1, 2) + dOOOO = gOOOO.transpose(0, 2, 1, 3) - gOOOO.transpose(0, 3, 1, 2) + dOVVV = gOVVV.transpose(0, 2, 1, 3) - gOVVV.transpose(0, 3, 1, 2) + dOOOV = gOOOV.transpose(0, 2, 1, 3) - gOOOV.transpose(1, 2, 0, 3) + dOVVO = gOVVO.transpose(0, 2, 1, 3) + dOVOV = (dOVOV + dOVOV.transpose(2, 3, 0, 1)) * 0.5 + dVVVV = dVVVV + dVVVV.transpose(1, 0, 3, 2).conj() + dOOOO = dOOOO + dOOOO.transpose(1, 0, 3, 2).conj() + dOVVO = (dOVVO + dOVVO.transpose(3, 2, 1, 0).conj()) * 0.5 + dOOVV = -dOVVO.transpose(0, 3, 2, 1) dVVOV = None - dovOV = goOvV.transpose(0,2,1,3) - dvvVV = gvVvV.transpose(0,2,1,3) * 2 - dooOO = goOoO.transpose(0,2,1,3) * 2 - dovVV = goVvV.transpose(0,2,1,3) - dooOV = goOoV.transpose(0,2,1,3) - dovVO = goVvO.transpose(0,2,1,3) - - dOVvv = gOvVv.transpose(0,2,1,3) - dOOov = gOoOv.transpose(0,2,1,3) - dOVvo = gOvVo.transpose(0,2,1,3) - dooVV = goVoV.transpose(0,2,1,3) - dOOvv = gOvOv.transpose(0,2,1,3) - - dvvVV = dvvVV + dvvVV.transpose(1,0,3,2).conj() - dooOO = dooOO + dooOO.transpose(1,0,3,2).conj() - dovVO = (dovVO + dOVvo.transpose(3,2,1,0).conj()) * .5 - dooVV =-(dooVV + dooVV.transpose(1,0,3,2).conj()) * .5 + dovOV = goOvV.transpose(0, 2, 1, 3) + dvvVV = gvVvV.transpose(0, 2, 1, 3) * 2 + dooOO = goOoO.transpose(0, 2, 1, 3) * 2 + dovVV = goVvV.transpose(0, 2, 1, 3) + dooOV = goOoV.transpose(0, 2, 1, 3) + dovVO = goVvO.transpose(0, 2, 1, 3) + + dOVvv = gOvVv.transpose(0, 2, 1, 3) + dOOov = gOoOv.transpose(0, 2, 1, 3) + dOVvo = gOvVo.transpose(0, 2, 1, 3) + dooVV = goVoV.transpose(0, 2, 1, 3) + dOOvv = gOvOv.transpose(0, 2, 1, 3) + + dvvVV = dvvVV + dvvVV.transpose(1, 0, 3, 2).conj() + dooOO = dooOO + dooOO.transpose(1, 0, 3, 2).conj() + dovVO = (dovVO + dOVvo.transpose(3, 2, 1, 0).conj()) * 0.5 + dooVV = -(dooVV + dooVV.transpose(1, 0, 3, 2).conj()) * 0.5 dvvOV = None dOVov = None dVVvv = None dOOoo = None - dOVvo = dovVO.transpose(3,2,1,0).conj() - dOOvv =-(dOOvv + dOOvv.transpose(1,0,3,2).conj()) * .5 + dOVvo = dovVO.transpose(3, 2, 1, 0).conj() + dOOvv = -(dOOvv + dOOvv.transpose(1, 0, 3, 2).conj()) * 0.5 dVVov = None if compress_vvvv: @@ -321,33 +323,37 @@ def _gamma2_outcore(cc, t1, t2, l1, l2, h5fobj, compress_vvvv=False): idxa = idxa[0] * nvira + idxa[1] idxb = numpy.tril_indices(nvirb) idxb = idxb[0] * nvirb + idxb[1] - dvvvv = dvvvv + dvvvv.transpose(1,0,2,3) - dvvvv = lib.take_2d(dvvvv.reshape(nvira**2,nvira**2), idxa, idxa) - dvvvv *= .5 - dvvVV = dvvVV + dvvVV.transpose(1,0,2,3) - dvvVV = lib.take_2d(dvvVV.reshape(nvira**2,nvirb**2), idxa, idxb) - dvvVV *= .5 - dVVVV = dVVVV + dVVVV.transpose(1,0,2,3) - dVVVV = lib.take_2d(dVVVV.reshape(nvirb**2,nvirb**2), idxb, idxb) - dVVVV *= .5 - - return ((dovov, dovOV, dOVov, dOVOV), - (dvvvv, dvvVV, dVVvv, dVVVV), - (doooo, dooOO, dOOoo, dOOOO), - (doovv, dooVV, dOOvv, dOOVV), - (dovvo, dovVO, dOVvo, dOVVO), - (dvvov, dvvOV, dVVov, dVVOV), - (dovvv, dovVV, dOVvv, dOVVV), - (dooov, dooOV, dOOov, dOOOV)) + dvvvv = dvvvv + dvvvv.transpose(1, 0, 2, 3) + dvvvv = lib.take_2d(dvvvv.reshape(nvira**2, nvira**2), idxa, idxa) + dvvvv *= 0.5 + dvvVV = dvvVV + dvvVV.transpose(1, 0, 2, 3) + dvvVV = lib.take_2d(dvvVV.reshape(nvira**2, nvirb**2), idxa, idxb) + dvvVV *= 0.5 + dVVVV = dVVVV + dVVVV.transpose(1, 0, 2, 3) + dVVVV = lib.take_2d(dVVVV.reshape(nvirb**2, nvirb**2), idxb, idxb) + dVVVV *= 0.5 + + return ( + (dovov, dovOV, dOVov, dOVOV), + (dvvvv, dvvVV, dVVvv, dVVVV), + (doooo, dooOO, dOOoo, dOOOO), + (doovv, dooVV, dOOvv, dOOVV), + (dovvo, dovVO, dOVvo, dOVVO), + (dvvov, dvvOV, dVVov, dVVOV), + (dovvv, dovVV, dOVvv, dOVVV), + (dooov, dooOV, dOOov, dOOOV), + ) + def _gamma2_intermediates(cc, t1, t2, l1, l2, compress_vvvv=False): - #TODO: h5fobj = lib.H5TmpFile() + # TODO: h5fobj = lib.H5TmpFile() h5fobj = None d2 = _gamma2_outcore(cc, t1, t2, l1, l2, h5fobj, compress_vvvv) return d2 + def make_rdm1(mycc, t1, t2, l1, l2, ao_repr=False, with_frozen=True, with_mf=True): - r''' + r""" One-particle spin density matrices dm1a, dm1b in MO basis (the occupied-virtual blocks due to the orbital response contribution are not included). @@ -356,13 +362,14 @@ def make_rdm1(mycc, t1, t2, l1, l2, ao_repr=False, with_frozen=True, with_mf=Tru dm1b[p,q] = The convention of 1-pdm is based on McWeeney's book, Eq (5.4.20). - ''' + """ d1 = _gamma1_intermediates(mycc, t1, t2, l1, l2) return _make_rdm1(mycc, d1, with_frozen=with_frozen, ao_repr=ao_repr, with_mf=with_mf) + # spin-orbital rdm2 in Chemist's notation def make_rdm2(mycc, t1, t2, l1, l2, ao_repr=False, with_frozen=True, with_dm1=True): - r''' + r""" Two-particle spin density matrices dm2aa, dm2ab, dm2bb in MO basis dm2aa[p,q,r,s] = @@ -382,11 +389,11 @@ def make_rdm2(mycc, t1, t2, l1, l2, ao_repr=False, with_frozen=True, with_dm1=Tr eri_ab[p,q,r,s] = ( p_alpha q_alpha | r_beta s_beta ) eri_ba[p,q,r,s] = ( p_beta q_beta | r_alpha s_alpha ) eri_bb[p,q,r,s] = ( p_beta q_beta | r_beta s_beta ) - ''' + """ d1 = _gamma1_intermediates(mycc, t1, t2, l1, l2) d2 = _gamma2_intermediates(mycc, t1, t2, l1, l2) - return _make_rdm2(mycc, d1, d2, with_dm1=with_dm1, with_frozen=with_frozen, - ao_repr=ao_repr) + return _make_rdm2(mycc, d1, d2, with_dm1=with_dm1, with_frozen=with_frozen, ao_repr=ao_repr) + def _make_rdm1(mycc, d1, with_frozen=True, ao_repr=False, with_mf=True): doo, dOO = d1[0] @@ -398,19 +405,19 @@ def _make_rdm1(mycc, d1, with_frozen=True, ao_repr=False, with_mf=True): nmoa = nocca + nvira nmob = noccb + nvirb - dm1a = numpy.empty((nmoa,nmoa), dtype=doo.dtype) - dm1a[:nocca,:nocca] = doo + doo.conj().T - dm1a[:nocca,nocca:] = dov + dvo.conj().T - dm1a[nocca:,:nocca] = dm1a[:nocca,nocca:].conj().T - dm1a[nocca:,nocca:] = dvv + dvv.conj().T - dm1a *= .5 - - dm1b = numpy.empty((nmob,nmob), dtype=dOO.dtype) - dm1b[:noccb,:noccb] = dOO + dOO.conj().T - dm1b[:noccb,noccb:] = dOV + dVO.conj().T - dm1b[noccb:,:noccb] = dm1b[:noccb,noccb:].conj().T - dm1b[noccb:,noccb:] = dVV + dVV.conj().T - dm1b *= .5 + dm1a = numpy.empty((nmoa, nmoa), dtype=doo.dtype) + dm1a[:nocca, :nocca] = doo + doo.conj().T + dm1a[:nocca, nocca:] = dov + dvo.conj().T + dm1a[nocca:, :nocca] = dm1a[:nocca, nocca:].conj().T + dm1a[nocca:, nocca:] = dvv + dvv.conj().T + dm1a *= 0.5 + + dm1b = numpy.empty((nmob, nmob), dtype=dOO.dtype) + dm1b[:noccb, :noccb] = dOO + dOO.conj().T + dm1b[:noccb, noccb:] = dOV + dVO.conj().T + dm1b[noccb:, :noccb] = dm1b[:noccb, noccb:].conj().T + dm1b[noccb:, noccb:] = dVV + dVV.conj().T + dm1b *= 0.5 if with_mf: dm1a[numpy.diag_indices(nocca)] += 1 dm1b[numpy.diag_indices(noccb)] += 1 @@ -420,25 +427,26 @@ def _make_rdm1(mycc, d1, with_frozen=True, ao_repr=False, with_mf=True): nmob = mycc.mo_occ[1].size nocca = numpy.count_nonzero(mycc.mo_occ[0] > 0) noccb = numpy.count_nonzero(mycc.mo_occ[1] > 0) - rdm1a = numpy.zeros((nmoa,nmoa), dtype=dm1a.dtype) - rdm1b = numpy.zeros((nmob,nmob), dtype=dm1b.dtype) + rdm1a = numpy.zeros((nmoa, nmoa), dtype=dm1a.dtype) + rdm1b = numpy.zeros((nmob, nmob), dtype=dm1b.dtype) if with_mf: rdm1a[numpy.diag_indices(nocca)] = 1 rdm1b[numpy.diag_indices(noccb)] = 1 moidx = mycc.get_frozen_mask() moidxa = numpy.where(moidx[0])[0] moidxb = numpy.where(moidx[1])[0] - rdm1a[moidxa[:,None],moidxa] = dm1a - rdm1b[moidxb[:,None],moidxb] = dm1b + rdm1a[moidxa[:, None], moidxa] = dm1a + rdm1b[moidxb[:, None], moidxb] = dm1b dm1a = rdm1a dm1b = rdm1b if ao_repr: mo_a, mo_b = mycc.mo_coeff - dm1a = lib.einsum('pi,ij,qj->pq', mo_a, dm1a, mo_a) - dm1b = lib.einsum('pi,ij,qj->pq', mo_b, dm1b, mo_b) + dm1a = lib.einsum("pi,ij,qj->pq", mo_a, dm1a, mo_a) + dm1b = lib.einsum("pi,ij,qj->pq", mo_b, dm1b, mo_b) return dm1a, dm1b + def _make_rdm2(mycc, d1, d2, with_dm1=True, with_frozen=True, ao_repr=False): dovov, dovOV, dOVov, dOVOV = d2[0] dvvvv, dvvVV, dVVvv, dVVVV = d2[1] @@ -452,110 +460,110 @@ def _make_rdm2(mycc, d1, d2, with_dm1=True, with_frozen=True, ao_repr=False): nmoa = nocca + nvira nmob = noccb + nvirb - dm2aa = numpy.empty((nmoa,nmoa,nmoa,nmoa), dtype=doovv.dtype) - dm2ab = numpy.empty((nmoa,nmoa,nmob,nmob), dtype=doovv.dtype) - dm2bb = numpy.empty((nmob,nmob,nmob,nmob), dtype=doovv.dtype) + dm2aa = numpy.empty((nmoa, nmoa, nmoa, nmoa), dtype=doovv.dtype) + dm2ab = numpy.empty((nmoa, nmoa, nmob, nmob), dtype=doovv.dtype) + dm2bb = numpy.empty((nmob, nmob, nmob, nmob), dtype=doovv.dtype) -# dm2aa + # dm2aa dovov = numpy.asarray(dovov) - dm2aa[:nocca,nocca:,:nocca,nocca:] = dovov - dm2aa[nocca:,:nocca,nocca:,:nocca] = dm2aa[:nocca,nocca:,:nocca,nocca:].transpose(1,0,3,2).conj() + dm2aa[:nocca, nocca:, :nocca, nocca:] = dovov + dm2aa[nocca:, :nocca, nocca:, :nocca] = dm2aa[:nocca, nocca:, :nocca, nocca:].transpose(1, 0, 3, 2).conj() dovov = None - #assert(abs(doovv+dovvo.transpose(0,3,2,1)).max() == 0) + # assert(abs(doovv+dovvo.transpose(0,3,2,1)).max() == 0) dovvo = numpy.asarray(dovvo) - dm2aa[:nocca,:nocca,nocca:,nocca:] =-dovvo.transpose(0,3,2,1) - dm2aa[nocca:,nocca:,:nocca,:nocca] = dm2aa[:nocca,:nocca,nocca:,nocca:].transpose(2,3,0,1) - dm2aa[:nocca,nocca:,nocca:,:nocca] = dovvo - dm2aa[nocca:,:nocca,:nocca,nocca:] = dm2aa[:nocca,nocca:,nocca:,:nocca].transpose(1,0,3,2).conj() + dm2aa[:nocca, :nocca, nocca:, nocca:] = -dovvo.transpose(0, 3, 2, 1) + dm2aa[nocca:, nocca:, :nocca, :nocca] = dm2aa[:nocca, :nocca, nocca:, nocca:].transpose(2, 3, 0, 1) + dm2aa[:nocca, nocca:, nocca:, :nocca] = dovvo + dm2aa[nocca:, :nocca, :nocca, nocca:] = dm2aa[:nocca, nocca:, nocca:, :nocca].transpose(1, 0, 3, 2).conj() dovvo = None if len(dvvvv.shape) == 2: dvvvv = ao2mo.restore(1, dvvvv, nvira) - dm2aa[nocca:,nocca:,nocca:,nocca:] = dvvvv - dm2aa[:nocca,:nocca,:nocca,:nocca] = doooo + dm2aa[nocca:, nocca:, nocca:, nocca:] = dvvvv + dm2aa[:nocca, :nocca, :nocca, :nocca] = doooo dovvv = numpy.asarray(dovvv) - dm2aa[:nocca,nocca:,nocca:,nocca:] = dovvv - dm2aa[nocca:,nocca:,:nocca,nocca:] = dovvv.transpose(2,3,0,1) - dm2aa[nocca:,nocca:,nocca:,:nocca] = dovvv.transpose(3,2,1,0).conj() - dm2aa[nocca:,:nocca,nocca:,nocca:] = dovvv.transpose(1,0,3,2).conj() + dm2aa[:nocca, nocca:, nocca:, nocca:] = dovvv + dm2aa[nocca:, nocca:, :nocca, nocca:] = dovvv.transpose(2, 3, 0, 1) + dm2aa[nocca:, nocca:, nocca:, :nocca] = dovvv.transpose(3, 2, 1, 0).conj() + dm2aa[nocca:, :nocca, nocca:, nocca:] = dovvv.transpose(1, 0, 3, 2).conj() dovvv = None dooov = numpy.asarray(dooov) - dm2aa[:nocca,:nocca,:nocca,nocca:] = dooov - dm2aa[:nocca,nocca:,:nocca,:nocca] = dooov.transpose(2,3,0,1) - dm2aa[:nocca,:nocca,nocca:,:nocca] = dooov.transpose(1,0,3,2).conj() - dm2aa[nocca:,:nocca,:nocca,:nocca] = dooov.transpose(3,2,1,0).conj() + dm2aa[:nocca, :nocca, :nocca, nocca:] = dooov + dm2aa[:nocca, nocca:, :nocca, :nocca] = dooov.transpose(2, 3, 0, 1) + dm2aa[:nocca, :nocca, nocca:, :nocca] = dooov.transpose(1, 0, 3, 2).conj() + dm2aa[nocca:, :nocca, :nocca, :nocca] = dooov.transpose(3, 2, 1, 0).conj() dooov = None -# dm2bb + # dm2bb dOVOV = numpy.asarray(dOVOV) - dm2bb[:noccb,noccb:,:noccb,noccb:] = dOVOV - dm2bb[noccb:,:noccb,noccb:,:noccb] = dm2bb[:noccb,noccb:,:noccb,noccb:].transpose(1,0,3,2).conj() + dm2bb[:noccb, noccb:, :noccb, noccb:] = dOVOV + dm2bb[noccb:, :noccb, noccb:, :noccb] = dm2bb[:noccb, noccb:, :noccb, noccb:].transpose(1, 0, 3, 2).conj() dOVOV = None dOVVO = numpy.asarray(dOVVO) - dm2bb[:noccb,:noccb,noccb:,noccb:] =-dOVVO.transpose(0,3,2,1) - dm2bb[noccb:,noccb:,:noccb,:noccb] = dm2bb[:noccb,:noccb,noccb:,noccb:].transpose(2,3,0,1) - dm2bb[:noccb,noccb:,noccb:,:noccb] = dOVVO - dm2bb[noccb:,:noccb,:noccb,noccb:] = dm2bb[:noccb,noccb:,noccb:,:noccb].transpose(1,0,3,2).conj() + dm2bb[:noccb, :noccb, noccb:, noccb:] = -dOVVO.transpose(0, 3, 2, 1) + dm2bb[noccb:, noccb:, :noccb, :noccb] = dm2bb[:noccb, :noccb, noccb:, noccb:].transpose(2, 3, 0, 1) + dm2bb[:noccb, noccb:, noccb:, :noccb] = dOVVO + dm2bb[noccb:, :noccb, :noccb, noccb:] = dm2bb[:noccb, noccb:, noccb:, :noccb].transpose(1, 0, 3, 2).conj() dOVVO = None if len(dVVVV.shape) == 2: dVVVV = ao2mo.restore(1, dVVVV, nvirb) - dm2bb[noccb:,noccb:,noccb:,noccb:] = dVVVV - dm2bb[:noccb,:noccb,:noccb,:noccb] = dOOOO + dm2bb[noccb:, noccb:, noccb:, noccb:] = dVVVV + dm2bb[:noccb, :noccb, :noccb, :noccb] = dOOOO dOVVV = numpy.asarray(dOVVV) - dm2bb[:noccb,noccb:,noccb:,noccb:] = dOVVV - dm2bb[noccb:,noccb:,:noccb,noccb:] = dOVVV.transpose(2,3,0,1) - dm2bb[noccb:,noccb:,noccb:,:noccb] = dOVVV.transpose(3,2,1,0).conj() - dm2bb[noccb:,:noccb,noccb:,noccb:] = dOVVV.transpose(1,0,3,2).conj() + dm2bb[:noccb, noccb:, noccb:, noccb:] = dOVVV + dm2bb[noccb:, noccb:, :noccb, noccb:] = dOVVV.transpose(2, 3, 0, 1) + dm2bb[noccb:, noccb:, noccb:, :noccb] = dOVVV.transpose(3, 2, 1, 0).conj() + dm2bb[noccb:, :noccb, noccb:, noccb:] = dOVVV.transpose(1, 0, 3, 2).conj() dOVVV = None dOOOV = numpy.asarray(dOOOV) - dm2bb[:noccb,:noccb,:noccb,noccb:] = dOOOV - dm2bb[:noccb,noccb:,:noccb,:noccb] = dOOOV.transpose(2,3,0,1) - dm2bb[:noccb,:noccb,noccb:,:noccb] = dOOOV.transpose(1,0,3,2).conj() - dm2bb[noccb:,:noccb,:noccb,:noccb] = dOOOV.transpose(3,2,1,0).conj() + dm2bb[:noccb, :noccb, :noccb, noccb:] = dOOOV + dm2bb[:noccb, noccb:, :noccb, :noccb] = dOOOV.transpose(2, 3, 0, 1) + dm2bb[:noccb, :noccb, noccb:, :noccb] = dOOOV.transpose(1, 0, 3, 2).conj() + dm2bb[noccb:, :noccb, :noccb, :noccb] = dOOOV.transpose(3, 2, 1, 0).conj() dOOOV = None -# dm2ab + # dm2ab dovOV = numpy.asarray(dovOV) - dm2ab[:nocca,nocca:,:noccb,noccb:] = dovOV - dm2ab[nocca:,:nocca,noccb:,:noccb] = dm2ab[:nocca,nocca:,:noccb,noccb:].transpose(1,0,3,2).conj() + dm2ab[:nocca, nocca:, :noccb, noccb:] = dovOV + dm2ab[nocca:, :nocca, noccb:, :noccb] = dm2ab[:nocca, nocca:, :noccb, noccb:].transpose(1, 0, 3, 2).conj() dovOV = None dovVO = numpy.asarray(dovVO) - dm2ab[:nocca,:nocca,noccb:,noccb:] = dooVV - dm2ab[nocca:,nocca:,:noccb,:noccb] = dOOvv.transpose(2,3,0,1) - dm2ab[:nocca,nocca:,noccb:,:noccb] = dovVO - dm2ab[nocca:,:nocca,:noccb,noccb:] = dovVO.transpose(1,0,3,2).conj() + dm2ab[:nocca, :nocca, noccb:, noccb:] = dooVV + dm2ab[nocca:, nocca:, :noccb, :noccb] = dOOvv.transpose(2, 3, 0, 1) + dm2ab[:nocca, nocca:, noccb:, :noccb] = dovVO + dm2ab[nocca:, :nocca, :noccb, noccb:] = dovVO.transpose(1, 0, 3, 2).conj() dovVO = None if len(dvvVV.shape) == 2: idxa = numpy.tril_indices(nvira) dvvVV1 = lib.unpack_tril(dvvVV) - dvvVV = numpy.empty((nvira,nvira,nvirb,nvirb)) + dvvVV = numpy.empty((nvira, nvira, nvirb, nvirb)) dvvVV[idxa] = dvvVV1 - dvvVV[idxa[1],idxa[0]] = dvvVV1 + dvvVV[idxa[1], idxa[0]] = dvvVV1 dvvVV1 = None - dm2ab[nocca:,nocca:,noccb:,noccb:] = dvvVV - dm2ab[:nocca,:nocca,:noccb,:noccb] = dooOO + dm2ab[nocca:, nocca:, noccb:, noccb:] = dvvVV + dm2ab[:nocca, :nocca, :noccb, :noccb] = dooOO dovVV = numpy.asarray(dovVV) - dm2ab[:nocca,nocca:,noccb:,noccb:] = dovVV - dm2ab[nocca:,nocca:,:noccb,noccb:] = dOVvv.transpose(2,3,0,1) - dm2ab[nocca:,nocca:,noccb:,:noccb] = dOVvv.transpose(3,2,1,0).conj() - dm2ab[nocca:,:nocca,noccb:,noccb:] = dovVV.transpose(1,0,3,2).conj() + dm2ab[:nocca, nocca:, noccb:, noccb:] = dovVV + dm2ab[nocca:, nocca:, :noccb, noccb:] = dOVvv.transpose(2, 3, 0, 1) + dm2ab[nocca:, nocca:, noccb:, :noccb] = dOVvv.transpose(3, 2, 1, 0).conj() + dm2ab[nocca:, :nocca, noccb:, noccb:] = dovVV.transpose(1, 0, 3, 2).conj() dovVV = None dooOV = numpy.asarray(dooOV) - dm2ab[:nocca,:nocca,:noccb,noccb:] = dooOV - dm2ab[:nocca,nocca:,:noccb,:noccb] = dOOov.transpose(2,3,0,1) - dm2ab[:nocca,:nocca,noccb:,:noccb] = dooOV.transpose(1,0,3,2).conj() - dm2ab[nocca:,:nocca,:noccb,:noccb] = dOOov.transpose(3,2,1,0).conj() + dm2ab[:nocca, :nocca, :noccb, noccb:] = dooOV + dm2ab[:nocca, nocca:, :noccb, :noccb] = dOOov.transpose(2, 3, 0, 1) + dm2ab[:nocca, :nocca, noccb:, :noccb] = dooOV.transpose(1, 0, 3, 2).conj() + dm2ab[nocca:, :nocca, :noccb, :noccb] = dOOov.transpose(3, 2, 1, 0).conj() dooOV = None if with_frozen and mycc.frozen is not None: @@ -566,20 +574,17 @@ def _make_rdm2(mycc, d1, d2, with_dm1=True, with_frozen=True, ao_repr=False): nocca = numpy.count_nonzero(mycc.mo_occ[0] > 0) noccb = numpy.count_nonzero(mycc.mo_occ[1] > 0) - rdm2aa = numpy.zeros((nmoa,nmoa,nmoa,nmoa), dtype=dm2aa.dtype) - rdm2ab = numpy.zeros((nmoa,nmoa,nmob,nmob), dtype=dm2ab.dtype) - rdm2bb = numpy.zeros((nmob,nmob,nmob,nmob), dtype=dm2bb.dtype) + rdm2aa = numpy.zeros((nmoa, nmoa, nmoa, nmoa), dtype=dm2aa.dtype) + rdm2ab = numpy.zeros((nmoa, nmoa, nmob, nmob), dtype=dm2ab.dtype) + rdm2bb = numpy.zeros((nmob, nmob, nmob, nmob), dtype=dm2bb.dtype) moidxa, moidxb = mycc.get_frozen_mask() moidxa = numpy.where(moidxa)[0] moidxb = numpy.where(moidxb)[0] - idxa = (moidxa.reshape(-1,1) * nmoa + moidxa).ravel() - idxb = (moidxb.reshape(-1,1) * nmob + moidxb).ravel() - lib.takebak_2d(rdm2aa.reshape(nmoa**2,nmoa**2), - dm2aa.reshape(nmoa0**2,nmoa0**2), idxa, idxa) - lib.takebak_2d(rdm2bb.reshape(nmob**2,nmob**2), - dm2bb.reshape(nmob0**2,nmob0**2), idxb, idxb) - lib.takebak_2d(rdm2ab.reshape(nmoa**2,nmob**2), - dm2ab.reshape(nmoa0**2,nmob0**2), idxa, idxb) + idxa = (moidxa.reshape(-1, 1) * nmoa + moidxa).ravel() + idxb = (moidxb.reshape(-1, 1) * nmob + moidxb).ravel() + lib.takebak_2d(rdm2aa.reshape(nmoa**2, nmoa**2), dm2aa.reshape(nmoa0**2, nmoa0**2), idxa, idxa) + lib.takebak_2d(rdm2bb.reshape(nmob**2, nmob**2), dm2bb.reshape(nmob0**2, nmob0**2), idxb, idxb) + lib.takebak_2d(rdm2ab.reshape(nmoa**2, nmob**2), dm2ab.reshape(nmoa0**2, nmob0**2), idxa, idxb) dm2aa, dm2ab, dm2bb = rdm2aa, rdm2ab, rdm2bb if with_dm1: @@ -588,36 +593,37 @@ def _make_rdm2(mycc, d1, d2, with_dm1=True, with_frozen=True, ao_repr=False): dm1b[numpy.diag_indices(noccb)] -= 1 for i in range(nocca): - dm2aa[i,i,:,:] += dm1a - dm2aa[:,:,i,i] += dm1a - dm2aa[:,i,i,:] -= dm1a - dm2aa[i,:,:,i] -= dm1a.T - dm2ab[i,i,:,:] += dm1b + dm2aa[i, i, :, :] += dm1a + dm2aa[:, :, i, i] += dm1a + dm2aa[:, i, i, :] -= dm1a + dm2aa[i, :, :, i] -= dm1a.T + dm2ab[i, i, :, :] += dm1b for i in range(noccb): - dm2bb[i,i,:,:] += dm1b - dm2bb[:,:,i,i] += dm1b - dm2bb[:,i,i,:] -= dm1b - dm2bb[i,:,:,i] -= dm1b.T - dm2ab[:,:,i,i] += dm1a + dm2bb[i, i, :, :] += dm1b + dm2bb[:, :, i, i] += dm1b + dm2bb[:, i, i, :] -= dm1b + dm2bb[i, :, :, i] -= dm1b.T + dm2ab[:, :, i, i] += dm1a for i in range(nocca): for j in range(nocca): - dm2aa[i,i,j,j] += 1 - dm2aa[i,j,j,i] -= 1 + dm2aa[i, i, j, j] += 1 + dm2aa[i, j, j, i] -= 1 for i in range(noccb): for j in range(noccb): - dm2bb[i,i,j,j] += 1 - dm2bb[i,j,j,i] -= 1 + dm2bb[i, i, j, j] += 1 + dm2bb[i, j, j, i] -= 1 for i in range(nocca): for j in range(noccb): - dm2ab[i,i,j,j] += 1 + dm2ab[i, i, j, j] += 1 - dm2aa = dm2aa.transpose(1,0,3,2) - dm2ab = dm2ab.transpose(1,0,3,2) - dm2bb = dm2bb.transpose(1,0,3,2) + dm2aa = dm2aa.transpose(1, 0, 3, 2) + dm2ab = dm2ab.transpose(1, 0, 3, 2) + dm2bb = dm2bb.transpose(1, 0, 3, 2) if ao_repr: from pyscf.cc import ccsd_rdm + dm2aa = ccsd_rdm._rdm2_mo2ao(dm2aa, mycc.mo_coeff[0]) dm2bb = ccsd_rdm._rdm2_mo2ao(dm2bb, mycc.mo_coeff[1]) dm2ab = _dm2ab_mo2ao(dm2ab, mycc.mo_coeff[0], mycc.mo_coeff[1]) @@ -625,22 +631,18 @@ def _make_rdm2(mycc, d1, d2, with_dm1=True, with_frozen=True, ao_repr=False): def _dm2ab_mo2ao(dm2, mo_a, mo_b): - return lib.einsum('ijkl,pi,qj,rk,sl->pqrs', dm2, mo_a, mo_a.conj(), - mo_b, mo_b.conj()) + return lib.einsum("ijkl,pi,qj,rk,sl->pqrs", dm2, mo_a, mo_a.conj(), mo_b, mo_b.conj()) -if __name__ == '__main__': +if __name__ == "__main__": from functools import reduce from pyscf import gto from pyscf import scf from pyscf.cc import uccsd mol = gto.Mole() - mol.atom = [ - [8 , (0. , 0. , 0.)], - [1 , (0. , -0.757 , 0.587)], - [1 , (0. , 0.757 , 0.587)]] - mol.basis = '631g' + mol.atom = [[8, (0.0, 0.0, 0.0)], [1, (0.0, -0.757, 0.587)], [1, (0.0, 0.757, 0.587)]] + mol.basis = "631g" mol.spin = 2 mol.build() mf = scf.UHF(mol).run() @@ -649,59 +651,58 @@ def _dm2ab_mo2ao(dm2, mo_a, mo_b): mycc.frozen = 2 ecc, t1, t2 = mycc.kernel() l1, l2 = mycc.solve_lambda() - dm1a,dm1b = make_rdm1(mycc, t1, t2, l1, l2) - dm2aa,dm2ab,dm2bb = make_rdm2(mycc, t1, t2, l1, l2) + dm1a, dm1b = make_rdm1(mycc, t1, t2, l1, l2) + dm2aa, dm2ab, dm2bb = make_rdm2(mycc, t1, t2, l1, l2) mo_a = mf.mo_coeff[0] mo_b = mf.mo_coeff[1] nmoa = mo_a.shape[1] nmob = mo_b.shape[1] - eriaa = ao2mo.kernel(mf._eri, mo_a, compact=False).reshape([nmoa]*4) - eribb = ao2mo.kernel(mf._eri, mo_b, compact=False).reshape([nmob]*4) - eriab = ao2mo.kernel(mf._eri, (mo_a,mo_a,mo_b,mo_b), compact=False) - eriab = eriab.reshape([nmoa,nmoa,nmob,nmob]) + eriaa = ao2mo.kernel(mf._eri, mo_a, compact=False).reshape([nmoa] * 4) + eribb = ao2mo.kernel(mf._eri, mo_b, compact=False).reshape([nmob] * 4) + eriab = ao2mo.kernel(mf._eri, (mo_a, mo_a, mo_b, mo_b), compact=False) + eriab = eriab.reshape([nmoa, nmoa, nmob, nmob]) hcore = mf.get_hcore() h1a = reduce(numpy.dot, (mo_a.T.conj(), hcore, mo_a)) h1b = reduce(numpy.dot, (mo_b.T.conj(), hcore, mo_b)) - e1 = numpy.einsum('ij,ji', h1a, dm1a) - e1+= numpy.einsum('ij,ji', h1b, dm1b) - e1+= numpy.einsum('ijkl,ijkl', eriaa, dm2aa) * .5 - e1+= numpy.einsum('ijkl,ijkl', eriab, dm2ab) - e1+= numpy.einsum('ijkl,ijkl', eribb, dm2bb) * .5 - e1+= mol.energy_nuc() + e1 = numpy.einsum("ij,ji", h1a, dm1a) + e1 += numpy.einsum("ij,ji", h1b, dm1b) + e1 += numpy.einsum("ijkl,ijkl", eriaa, dm2aa) * 0.5 + e1 += numpy.einsum("ijkl,ijkl", eriab, dm2ab) + e1 += numpy.einsum("ijkl,ijkl", eribb, dm2bb) * 0.5 + e1 += mol.energy_nuc() print(e1 - mycc.e_tot) from pyscf.fci import direct_uhf + mol = gto.Mole() mol.verbose = 0 mol.atom = [ - ['H', ( 1.,-1. , 0. )], - ['H', ( 0.,-1. ,-1. )], - ['H', ( 1.,-0.5 , 0. )], - ['H', ( 0.,-1. , 1. )], + ["H", (1.0, -1.0, 0.0)], + ["H", (0.0, -1.0, -1.0)], + ["H", (1.0, -0.5, 0.0)], + ["H", (0.0, -1.0, 1.0)], ] mol.charge = 2 mol.spin = 2 - mol.basis = '6-31g' + mol.basis = "6-31g" mol.build() - mf = scf.UHF(mol).run(init_guess='hcore', conv_tol=1.) + mf = scf.UHF(mol).run(init_guess="hcore", conv_tol=1.0) ehf0 = mf.e_tot - mol.energy_nuc() mycc = uccsd.UCCSD(mf).run() mycc.solve_lambda() eri_aa = ao2mo.kernel(mf._eri, mf.mo_coeff[0]) eri_bb = ao2mo.kernel(mf._eri, mf.mo_coeff[1]) - eri_ab = ao2mo.kernel(mf._eri, [mf.mo_coeff[0], mf.mo_coeff[0], - mf.mo_coeff[1], mf.mo_coeff[1]]) + eri_ab = ao2mo.kernel(mf._eri, [mf.mo_coeff[0], mf.mo_coeff[0], mf.mo_coeff[1], mf.mo_coeff[1]]) h1a = reduce(numpy.dot, (mf.mo_coeff[0].T, mf.get_hcore(), mf.mo_coeff[0])) h1b = reduce(numpy.dot, (mf.mo_coeff[1].T, mf.get_hcore(), mf.mo_coeff[1])) - efci, fcivec = direct_uhf.kernel((h1a,h1b), (eri_aa,eri_ab,eri_bb), - h1a.shape[0], mol.nelec) + efci, fcivec = direct_uhf.kernel((h1a, h1b), (eri_aa, eri_ab, eri_bb), h1a.shape[0], mol.nelec) dm1ref, dm2ref = direct_uhf.make_rdm12s(fcivec, h1a.shape[0], mol.nelec) t1, t2 = mycc.t1, mycc.t2 l1, l2 = mycc.l1, mycc.l2 rdm1 = make_rdm1(mycc, t1, t2, l1, l2) rdm2 = make_rdm2(mycc, t1, t2, l1, l2) - print('dm1a', abs(dm1ref[0] - rdm1[0]).max()) - print('dm1b', abs(dm1ref[1] - rdm1[1]).max()) - print('dm2aa', abs(dm2ref[0] - rdm2[0]).max()) - print('dm2ab', abs(dm2ref[1] - rdm2[1]).max()) - print('dm2bb', abs(dm2ref[2] - rdm2[2]).max()) + print("dm1a", abs(dm1ref[0] - rdm1[0]).max()) + print("dm1b", abs(dm1ref[1] - rdm1[1]).max()) + print("dm2aa", abs(dm2ref[0] - rdm2[0]).max()) + print("dm2ab", abs(dm2ref[1] - rdm2[1]).max()) + print("dm2bb", abs(dm2ref[2] - rdm2[2]).max()) diff --git a/vayesta/dmet/__init__.py b/vayesta/dmet/__init__.py index 575dd1a2b..155057d9a 100644 --- a/vayesta/dmet/__init__.py +++ b/vayesta/dmet/__init__.py @@ -18,6 +18,7 @@ log = logging.getLogger(__name__) + def DMET(mf, *args, **kwargs): """Determine restricted or unrestricted by inspection of mean-field object""" if isinstance(mf, pyscf.scf.uhf.UHF): diff --git a/vayesta/dmet/dmet.py b/vayesta/dmet/dmet.py index 131b6abd0..950a4b716 100644 --- a/vayesta/dmet/dmet.py +++ b/vayesta/dmet/dmet.py @@ -15,7 +15,8 @@ @dataclasses.dataclass class Options(Embedding.Options): """Options for DMET calculations.""" - iao_minao: str = 'auto' # Minimal basis for IAOs + + iao_minao: str = "auto" # Minimal basis for IAOs dm_with_frozen: bool = False # Add frozen parts to cluster DMs # -- Self-consistency maxiter: int = 30 @@ -27,21 +28,24 @@ class Options(Embedding.Options): mixing_variable: str = "hl rdm" oneshot: bool = False # --- Solver options - solver_options: dict = Embedding.Options.change_dict_defaults('solver_options', - # CCSD - solve_lambda=True) + solver_options: dict = Embedding.Options.change_dict_defaults( + "solver_options", + # CCSD + solve_lambda=True, + ) + @dataclasses.dataclass class DMETResults: cluster_sizes: np.ndarray = None e_corr: float = None -class DMET(Embedding): +class DMET(Embedding): Fragment = DMETFragment Options = Options - def __init__(self, mf, solver='CCSD', log=None, **kwargs): + def __init__(self, mf, solver="CCSD", log=None, **kwargs): t_start = timer() # If we're running in oneshot mode will only do a single iteration, regardless of this setting, but good to have # consistent settings. @@ -51,7 +55,7 @@ def __init__(self, mf, solver='CCSD', log=None, **kwargs): super().__init__(mf, solver=solver, log=log, **kwargs) self.log.info("Parameters of %s:", self.__class__.__name__) - self.log.info(break_into_lines(str(self.opts), newline='\n ')) + self.log.info(break_into_lines(str(self.opts), newline="\n ")) # --- Check input if not mf.converged: @@ -71,14 +75,13 @@ def e_tot(self): return self.e_mf + self.e_corr def __repr__(self): - keys = ['mf', 'solver'] - fmt = ('%s(' + len(keys) * '%s: %r, ')[:-2] + ')' + keys = ["mf", "solver"] + fmt = ("%s(" + len(keys) * "%s: %r, ")[:-2] + ")" values = [self.__dict__[k] for k in keys] return fmt % (self.__class__.__name__, *[x for y in zip(keys, values) for x in y]) def kernel(self): - """Run DMET calculation. - """ + """Run DMET calculation.""" t_start = timer() if self.nfrag == 0: @@ -86,9 +89,11 @@ def kernel(self): maxiter = self.opts.maxiter # View this as a single number for now. - if self.opts.bath_options['bathtype'] == 'mp2' and maxiter > 1: - raise NotImplementedError("MP2 bath calculation is currently ignoring the correlation potential, so does" - " not work properly for self-consistent calculations.") + if self.opts.bath_options["bathtype"] == "mp2" and maxiter > 1: + raise NotImplementedError( + "MP2 bath calculation is currently ignoring the correlation potential, so does" + " not work properly for self-consistent calculations." + ) fock = self.get_fock() if self.vcorr is None: @@ -175,8 +180,9 @@ def electron_err(cpt, construct_bath=False): break # If we've got to here we've found a bracket. [lo, hi] = sorted([cpt, new_cpt]) - cpt, res = scipy.optimize.brentq(electron_err, a=lo, b=hi, full_output=True, - xtol=self.opts.max_elec_err * nelec_mf) # self.opts.max_elec_err * nelec_mf) + cpt, res = scipy.optimize.brentq( + electron_err, a=lo, b=hi, full_output=True, xtol=self.opts.max_elec_err * nelec_mf + ) # self.opts.max_elec_err * nelec_mf) self.log.info("Converged chemical potential: {:6.4e}".format(cpt)) # Recalculate to ensure all fragments have up-to-date info. Brentq strangely seems to do an extra # calculation at the end... @@ -240,8 +246,9 @@ def calc_electron_number_defect(self, chempot, nelec_target, parent_fragments, n nelec_hl += frag.get_nelectron_hl() * nsym[x] self.hl_rdms = [f.get_frag_hl_dm() for f in parent_fragments] - self.log.info("Chemical Potential {:8.6e} gives Total electron deviation {:6.4e}".format( - chempot, nelec_hl - nelec_target)) + self.log.info( + "Chemical Potential {:8.6e} gives Total electron deviation {:6.4e}".format(chempot, nelec_hl - nelec_target) + ) return nelec_hl - nelec_target def update_vcorr(self, fock, curr_rdms): @@ -250,17 +257,16 @@ def update_vcorr(self, fock, curr_rdms): # Note that we want the total number of electrons, not just in fragments, and that this treats different spin # channels separately; for RHF the resultant problems are identical and so can just be solved once. # As such need to use the spin-dm, rather than spatial. - vcorr_new = perform_SDP_fit(self.mol.nelec[0], fock, self.get_impurity_coeffs(), [x / 2 for x in curr_rdms], - self.get_ovlp(), self.log) + vcorr_new = perform_SDP_fit( + self.mol.nelec[0], fock, self.get_impurity_coeffs(), [x / 2 for x in curr_rdms], self.get_ovlp(), self.log + ) return vcorr_new def get_impurity_coeffs(self): sym_parents = self.get_symmetry_parent_fragments() sym_children = self.get_symmetry_child_fragments() - return [ - [parent.c_frag] + [c.c_frag for c in children] for (parent, children) in zip(sym_parents, sym_children) - ] + return [[parent.c_frag] + [c.c_frag for c in children] for (parent, children) in zip(sym_parents, sym_children)] def print_results(self): # , results): self.log.info("Energies") @@ -269,10 +275,10 @@ def print_results(self): # , results): # for i, frag in enumerate(self.loop()): # e_corr = results["e_corr"][i] # self.log.output(fmt, 'E(corr)[' + frag.trimmed_name() + ']=', e_corr) - self.log.output(fmt, 'E(corr)=', self.e_corr) - self.log.output(fmt, 'E(MF)=', self.e_mf) - self.log.output(fmt, 'E(nuc)=', self.mol.energy_nuc()) - self.log.output(fmt, 'E(tot)=', self.e_tot) + self.log.output(fmt, "E(corr)=", self.e_corr) + self.log.output(fmt, "E(MF)=", self.e_mf) + self.log.output(fmt, "E(nuc)=", self.mol.energy_nuc()) + self.log.output(fmt, "E(tot)=", self.e_tot) def print_clusters(self): """Print fragments of calculations.""" @@ -293,6 +299,7 @@ def get_corrfunc(self, kind, dm1=None, dm2=None, **kwargs): dm2 = self.make_rdm2() return super().get_corrfunc(kind, dm1=dm1, dm2=dm2, **kwargs) + DMET.make_rdm1.__doc__ = DMET.make_rdm1_demo.__doc__ DMET.make_rdm2.__doc__ = DMET.make_rdm2_demo.__doc__ diff --git a/vayesta/dmet/fragment.py b/vayesta/dmet/fragment.py index cff53c87b..d23b85483 100644 --- a/vayesta/dmet/fragment.py +++ b/vayesta/dmet/fragment.py @@ -12,11 +12,11 @@ # We might want to move the useful things from here into core, since they seem pretty general. + class DMETFragmentExit(Exception): pass - class DMETFragment(Fragment): @dataclasses.dataclass class Results(Fragment.Results): @@ -27,7 +27,6 @@ class Results(Fragment.Results): dm2: np.ndarray = None def __init__(self, *args, **kwargs): - """ Parameters ---------- @@ -79,8 +78,13 @@ def kernel(self, solver=None, init_guess=None, seris_ov=None, construct_bath=Tru with log_time(self.log.info, ("Time for %s solver:" % solver) + " %s"): cluster_solver.kernel() self.hamil = cluster_solver.hamil - self._results = results = self.Results(fid=self.id, wf=cluster_solver.wf, n_active=self.cluster.norb_active, - dm1=cluster_solver.wf.make_rdm1(), dm2=cluster_solver.wf.make_rdm2()) + self._results = results = self.Results( + fid=self.id, + wf=cluster_solver.wf, + n_active=self.cluster.norb_active, + dm1=cluster_solver.wf.make_rdm1(), + dm2=cluster_solver.wf.make_rdm2(), + ) self.hamil = cluster_solver.hamil @@ -109,12 +113,12 @@ def get_dmet_energy_contrib(self, hamil=None): occ = np.s_[:nocc] # Calculate the effective onebody interaction within the cluster. f_act = np.linalg.multi_dot((c_act.T, self.mf.get_fock(), c_act)) - v_act = 2 * np.einsum('iipq->pq', eris[occ, occ]) - np.einsum('iqpi->pq', eris[occ, :, :, occ]) + v_act = 2 * np.einsum("iipq->pq", eris[occ, occ]) - np.einsum("iqpi->pq", eris[occ, :, :, occ]) h_eff = f_act - v_act h_bare = np.linalg.multi_dot((c_act.T, self.base.get_hcore(), c_act)) e1 = 0.5 * dot(P_imp, h_bare + h_eff, self.results.dm1).trace() - e2 = 0.5 * np.einsum('pt,tqrs,pqrs->', P_imp, eris, self.results.dm2) + e2 = 0.5 * np.einsum("pt,tqrs,pqrs->", P_imp, eris, self.results.dm2) # Code to generate the HF energy contribution for testing purposes. # mf_dm1 = np.linalg.multi_dot((c_act.T, self.base.get_ovlp(), self.mf.make_rdm1(),\ # self.base.get_ovlp(), c_act)) diff --git a/vayesta/dmet/pdmet.py b/vayesta/dmet/pdmet.py index a2a75e5e3..2a910f8fd 100644 --- a/vayesta/dmet/pdmet.py +++ b/vayesta/dmet/pdmet.py @@ -7,25 +7,26 @@ log = logging.getLogger(__name__) + def update_mf(mf, dm1, canonicalize=True, inplace=False, damping=0.0, diis=None): """p-DMET mean-field update.""" if not inplace: mf = copy.copy(mf) if damping > 0: - dm1 = (1-damping)*dm1 + damping*mf.make_rdm1() + dm1 = (1 - damping) * dm1 + damping * mf.make_rdm1() if diis: dm1 = diis.update(dm1) - symerr = abs(dm1-dm1.T).max() + symerr = abs(dm1 - dm1.T).max() if symerr > 1e-10: log.warning("Density-matrix not symmetric! L(inf)= %.3e", symerr) ovlp = mf.get_ovlp() mo_occ, mo_coeff = scipy.linalg.eigh(dm1, b=ovlp, type=2) - mo_occ, mo_coeff = mo_occ[::-1], mo_coeff[:,::-1] + mo_occ, mo_coeff = mo_occ[::-1], mo_coeff[:, ::-1] nocc = np.count_nonzero(mf.mo_occ > 0) log.info("p-DMET occupation numbers:\n%r", mo_occ) - occ_homo = mo_occ[nocc-1] + occ_homo = mo_occ[nocc - 1] occ_lumo = mo_occ[nocc] if abs(occ_homo - occ_lumo) < 1e-8: raise RuntimeError("Degeneracy in MO occupation.") @@ -33,14 +34,14 @@ def update_mf(mf, dm1, canonicalize=True, inplace=False, damping=0.0, diis=None) if canonicalize: fock = mf.get_fock() occ, vir = np.s_[:nocc], np.s_[nocc:] - foo = np.linalg.multi_dot((mo_coeff[:,occ].T, fock, mo_coeff[:,occ])) + foo = np.linalg.multi_dot((mo_coeff[:, occ].T, fock, mo_coeff[:, occ])) eo, ro = np.linalg.eigh(foo) log.debug("Occupied eigenvalues:\n%r", eo) - mo_coeff[:,occ] = np.dot(mo_coeff[:,occ], ro) - fvv = np.linalg.multi_dot((mo_coeff[:,vir].T, fock, mo_coeff[:,vir])) + mo_coeff[:, occ] = np.dot(mo_coeff[:, occ], ro) + fvv = np.linalg.multi_dot((mo_coeff[:, vir].T, fock, mo_coeff[:, vir])) ev, rv = np.linalg.eigh(fvv) log.debug("Virtual eigenvalues:\n%r", ev) - mo_coeff[:,vir] = np.dot(mo_coeff[:,vir], rv) + mo_coeff[:, vir] = np.dot(mo_coeff[:, vir], rv) mf.mo_coeff = mo_coeff mf.mo_energy = None @@ -48,7 +49,7 @@ def update_mf(mf, dm1, canonicalize=True, inplace=False, damping=0.0, diis=None) return mf -if __name__ == '__main__': +if __name__ == "__main__": import pyscf import pyscf.gto import pyscf.scf @@ -56,7 +57,7 @@ def update_mf(mf, dm1, canonicalize=True, inplace=False, damping=0.0, diis=None) logging.basicConfig(level=logging.DEBUG) - mol = pyscf.gto.Mole(atom='H 0 0 0 ; F 0 0 2', basis='cc-pvdz') + mol = pyscf.gto.Mole(atom="H 0 0 0 ; F 0 0 2", basis="cc-pvdz") mol.build() mf = pyscf.scf.RHF(mol) diff --git a/vayesta/dmet/sdp_sc.py b/vayesta/dmet/sdp_sc.py index d8a118907..7c9fae105 100644 --- a/vayesta/dmet/sdp_sc.py +++ b/vayesta/dmet/sdp_sc.py @@ -49,7 +49,8 @@ def perform_SDP_fit(nelec, fock, impurity_projectors, target_rdms, ovlp, log): # Want to construct the full correlation potential, in the AO basis. utot = sum( - [cp.matmul(aproj.T, cp.matmul(us[i], aproj)) for i, curr_proj in enumerate(AO_in_imps) for aproj in curr_proj]) + [cp.matmul(aproj.T, cp.matmul(us[i], aproj)) for i, curr_proj in enumerate(AO_in_imps) for aproj in curr_proj] + ) # import scipy # c_pao = np.linalg.inv(scipy.linalg.sqrtm(ovlp)) @@ -62,7 +63,8 @@ def perform_SDP_fit(nelec, fock, impurity_projectors, target_rdms, ovlp, log): # trace to orthogonal rotations (if you're using nonorthogonal fragment orbitals this will need a bit more thought). objective = cp.Minimize( sum([cp.trace(rdm1 @ ux) * ni for ni, rdm1, ux in zip(nsym, target_rdms, us)]) - - alpha * nelec + cp.trace(cp.matmul(z, np.linalg.inv(ovlp))) + - alpha * nelec + + cp.trace(cp.matmul(z, np.linalg.inv(ovlp))) ) prob = cp.Problem(objective, constraints) diff --git a/vayesta/dmet/udmet.py b/vayesta/dmet/udmet.py index aa3fd2868..9c2d0a20f 100644 --- a/vayesta/dmet/udmet.py +++ b/vayesta/dmet/udmet.py @@ -15,10 +15,16 @@ def update_vcorr(self, fock, curr_rdms): impurity_coeffs = self.get_impurity_coeffs() self.log.info("Now running DMET correlation potential fitting") # Note that we want the total number of electrons, not just in fragments, and that this uses spatial orbitals. - vcorr_new = np.array((perform_SDP_fit(self.mol.nelec[0], fock[0], impurity_coeffs[0], [x[0] for x in curr_rdms], - self.get_ovlp(), self.log), - perform_SDP_fit(self.mol.nelec[1], fock[1], impurity_coeffs[1], [x[1] for x in curr_rdms], - self.get_ovlp(), self.log))) + vcorr_new = np.array( + ( + perform_SDP_fit( + self.mol.nelec[0], fock[0], impurity_coeffs[0], [x[0] for x in curr_rdms], self.get_ovlp(), self.log + ), + perform_SDP_fit( + self.mol.nelec[1], fock[1], impurity_coeffs[1], [x[1] for x in curr_rdms], self.get_ovlp(), self.log + ), + ) + ) return vcorr_new @@ -27,10 +33,12 @@ def get_impurity_coeffs(self): sym_children = self.get_symmetry_child_fragments() return [ [ - [parent.c_frag[x]] + [c.c_frag[x] for c in children] for (parent, children) in zip(sym_parents, - sym_children)] + [parent.c_frag[x]] + [c.c_frag[x] for c in children] + for (parent, children) in zip(sym_parents, sym_children) + ] for x in [0, 1] ] + UDMET.make_rdm1.__doc__ = UDMET.make_rdm1_demo.__doc__ UDMET.make_rdm2.__doc__ = UDMET.make_rdm2_demo.__doc__ diff --git a/vayesta/dmet/ufragment.py b/vayesta/dmet/ufragment.py index adb39eaf1..c0599fa0c 100644 --- a/vayesta/dmet/ufragment.py +++ b/vayesta/dmet/ufragment.py @@ -4,8 +4,8 @@ from vayesta.core.qemb import UFragment from vayesta.dmet.fragment import DMETFragment -class UDMETFragment(UFragment, DMETFragment): +class UDMETFragment(UFragment, DMETFragment): def set_cas(self, *args, **kwargs): raise NotImplementedError() @@ -33,24 +33,34 @@ def get_dmet_energy_contrib(self, hamil=None): fock = self.base.get_fock() fa = dot(c_active[0].T, fock[0], c_active[0]) fb = dot(c_active[1].T, fock[1], c_active[1]) - oa = np.s_[:self.cluster.nocc_active[0]] - ob = np.s_[:self.cluster.nocc_active[1]] + oa = np.s_[: self.cluster.nocc_active[0]] + ob = np.s_[: self.cluster.nocc_active[1]] gaa, gab, gbb = eris - va = (einsum('iipq->pq', gaa[oa,oa]) + einsum('pqii->pq', gab[:,:,ob,ob]) # Coulomb - - einsum('ipqi->pq', gaa[oa,:,:,oa])) # Exchange - vb = (einsum('iipq->pq', gbb[ob,ob]) + einsum('iipq->pq', gab[oa,oa]) # Coulomb - - einsum('ipqi->pq', gbb[ob,:,:,ob])) # Exchange - h_eff = (fa-va, fb-vb) + va = ( + einsum("iipq->pq", gaa[oa, oa]) + + einsum("pqii->pq", gab[:, :, ob, ob]) # Coulomb + - einsum("ipqi->pq", gaa[oa, :, :, oa]) + ) # Exchange + vb = ( + einsum("iipq->pq", gbb[ob, ob]) + + einsum("iipq->pq", gab[oa, oa]) # Coulomb + - einsum("ipqi->pq", gbb[ob, :, :, ob]) + ) # Exchange + h_eff = (fa - va, fb - vb) h_bare = tuple([dot(c.T, self.base.get_hcore(), c) for c in c_active]) - e1 = 0.5 * (dot(p_imp_a, h_bare[0] + h_eff[0], self.results.dm1[0]).trace() + - dot(p_imp_b, h_bare[1] + h_eff[1], self.results.dm1[1]).trace()) + e1 = 0.5 * ( + dot(p_imp_a, h_bare[0] + h_eff[0], self.results.dm1[0]).trace() + + dot(p_imp_b, h_bare[1] + h_eff[1], self.results.dm1[1]).trace() + ) - e2 = 0.5 * (einsum("tp,pqrs,tqrs->", p_imp_a, eris[0], self.results.dm2[0]) + - einsum("tp,pqrs,tqrs->", p_imp_a, eris[1], self.results.dm2[1]) + - einsum("tr,pqrs,pqts->", p_imp_b, eris[1], self.results.dm2[1]) + - einsum("tp,pqrs,tqrs->", p_imp_b, eris[2], self.results.dm2[2])) + e2 = 0.5 * ( + einsum("tp,pqrs,tqrs->", p_imp_a, eris[0], self.results.dm2[0]) + + einsum("tp,pqrs,tqrs->", p_imp_a, eris[1], self.results.dm2[1]) + + einsum("tr,pqrs,pqts->", p_imp_b, eris[1], self.results.dm2[1]) + + einsum("tp,pqrs,tqrs->", p_imp_b, eris[2], self.results.dm2[2]) + ) # Code to generate the HF energy contribution for testing purposes. # mf_dm1 = np.linalg.multi_dot((c_act.T, self.base.get_ovlp(), self.mf.make_rdm1(),\ diff --git a/vayesta/dmet/updates.py b/vayesta/dmet/updates.py index 9b3afb39c..0bd541fc6 100644 --- a/vayesta/dmet/updates.py +++ b/vayesta/dmet/updates.py @@ -9,7 +9,6 @@ def __init__(self): self.prev_params = None def _flatten_params(self, params): - def get_shape(x): return [get_shape(y) if type(y) != np.ndarray else y.shape for y in x] @@ -30,7 +29,7 @@ def _unflatten_params(self, params): def get_nonflat(flat_params, shapes, x): if type(shapes[0]) == int: - return flat_params[x:x + np.product(shapes)].reshape(shapes), x + np.product(shapes) + return flat_params[x : x + np.product(shapes)].reshape(shapes), x + np.product(shapes) else: finres = [] for shape in shapes: @@ -39,7 +38,7 @@ def get_nonflat(flat_params, shapes, x): return finres, x nonflat_params, x = get_nonflat(params, self.param_shape, x) - assert (x == len(params)) + assert x == len(params) return nonflat_params diff --git a/vayesta/edmet/__init__.py b/vayesta/edmet/__init__.py index 0e190cc86..2143159a1 100644 --- a/vayesta/edmet/__init__.py +++ b/vayesta/edmet/__init__.py @@ -12,6 +12,7 @@ log = logging.getLogger(__name__) + def EDMET(mf, *args, **kwargs): """Determine restricted or unrestricted by inspection of mean-field object""" if isinstance(mf, pyscf.scf.uhf.UHF): diff --git a/vayesta/edmet/edmet.py b/vayesta/edmet/edmet.py index 3f3d70850..ce5e1b331 100644 --- a/vayesta/edmet/edmet.py +++ b/vayesta/edmet/edmet.py @@ -11,6 +11,7 @@ from vayesta.edmet.fragment import EDMETFragment, EDMETFragmentExit from vayesta.solver import check_solver_config + @dataclasses.dataclass class Options(RDMET.Options): maxiter: int = 1 @@ -21,8 +22,7 @@ class Options(RDMET.Options): boson_xc_kernel: bool = False bosonic_interaction: str = "xc" solver: str = "CCSD-S-1-1" - solver_options: dict = RDMET.Options.change_dict_defaults('solver_options', - polaritonic_shift=True) + solver_options: dict = RDMET.Options.change_dict_defaults("solver_options", polaritonic_shift=True) @dataclasses.dataclass @@ -30,12 +30,12 @@ class EDMETResults: cluster_sizes: np.ndarray = None e_corr: float = None -class EDMET(RDMET): +class EDMET(RDMET): Fragment = EDMETFragment Options = Options - def __init__(self, mf, solver='EBFCI', log=None, **kwargs): + def __init__(self, mf, solver="EBFCI", log=None, **kwargs): # If we aren't running in oneshot mode we need to calculate the dd moments. if not kwargs.get("oneshot", False): kwargs["make_dd_moments"] = True @@ -50,8 +50,8 @@ def with_df(self): @property def eps(self): eps = np.zeros((self.nocc, self.nvir)) - eps = eps + self.mo_energy[self.nocc:] - eps = (eps.T - self.mo_energy[:self.nocc]).T + eps = eps + self.mo_energy[self.nocc :] + eps = (eps.T - self.mo_energy[: self.nocc]).T eps = eps.reshape(-1) return eps, eps @@ -69,7 +69,6 @@ def check_solver(self, solver): check_solver_config(is_uhf, is_eb, solver, self.log) def kernel(self): - t_start = timer() if self.nfrag == 0: @@ -167,8 +166,9 @@ def electron_err(cpt): self.log.fatal("Could not find chemical potential bracket.") # If we've got to here we've found a bracket. [lo, hi] = sorted([cpt, new_cpt]) - cpt, res = scipy.optimize.brentq(electron_err, a=lo, b=hi, full_output=True, - xtol=self.opts.max_elec_err * nelec_mf) + cpt, res = scipy.optimize.brentq( + electron_err, a=lo, b=hi, full_output=True, xtol=self.opts.max_elec_err * nelec_mf + ) # self.opts.max_elec_err * nelec_mf) self.log.info("Converged chemical potential: %6.4e", cpt) # Recalculate to ensure all fragments have up-to-date info. Brentq strangely seems to do an extra @@ -193,7 +193,8 @@ def electron_err(cpt): " coupled-boson={:12.8f} \n" " nonlocal correlation energy={:12.8f} \n" " mean-field energy={:12.8f} \n" - " correlation energy={:12.8f}".format(e1, e2, efb, self.e_nonlocal, emf, self.e_corr)) + " correlation energy={:12.8f}".format(e1, e2, efb, self.e_nonlocal, emf, self.e_corr) + ) if self.opts.oneshot: break # Want to do coupled DIIS optimisation of high-level rdms and local dd response moments. @@ -224,7 +225,6 @@ def electron_err(cpt): self.log.info("All done.") def set_up_fragments(self, sym_parents, nsym): - # First, set up and run RPA. Note that our self-consistency only couples same-spin excitations so we can # solve a subset of the RPA equations. if self.with_df: @@ -241,12 +241,11 @@ def set_up_fragments(self, sym_parents, nsym): moms_interact = np.zeros_like(target_rot) # Get appropriate slices to obtain required active spaces. ovs_active = [f.ov_active_tot for f in sym_parents] - ovs_active_slices = [slice(sum(ovs_active[:i]), sum(ovs_active[:i + 1])) for i in - range(len(sym_parents))] + ovs_active_slices = [slice(sum(ovs_active[:i]), sum(ovs_active[: i + 1])) for i in range(len(sym_parents))] # Use interaction component of moment to generate bosonic degrees of freedom. rot_bos = [f.define_bosons(moms_interact[0, sl, :]) for (f, sl) in zip(sym_parents, ovs_active_slices)] nbos = [x.shape[0] for x in rot_bos] - bos_slices = [slice(sum(nbos[:i]), sum(nbos[:i + 1])) for i in range(len(sym_parents))] + bos_slices = [slice(sum(nbos[:i]), sum(nbos[: i + 1])) for i in range(len(sym_parents))] if sum(nbos) > 0: # Calculate zeroth moment of bosonic degrees of freedom. mom0_bos, est_errors = rpa.kernel_moms(0, np.concatenate(rot_bos, axis=0), npoints=48) @@ -276,8 +275,7 @@ def set_up_fragments(self, sym_parents, nsym): e_nonlocal -= f.construct_boson_hamil(mom0_bos, eps, self.xc_kernel) * nc self.e_nonlocal = e_nonlocal - def calc_electron_number_defect(self, chempot, nelec_target, parent_fragments, nsym, - construct_bath=True): + def calc_electron_number_defect(self, chempot, nelec_target, parent_fragments, nsym, construct_bath=True): self.log.info("Running chemical potential={:8.6e}".format(chempot)) # Save original one-body hamiltonian calculation. saved_hcore = self.mf.get_hcore @@ -314,8 +312,9 @@ def calc_electron_number_defect(self, chempot, nelec_target, parent_fragments, n self.hl_rdms = [f.get_frag_hl_dm() for f in parent_fragments] self.hl_dd0 = hl_dd0 self.hl_dd1 = hl_dd1 - self.log.info("Chemical Potential {:8.6e} gives Total electron deviation {:6.4e}".format( - chempot, nelec_hl - nelec_target)) + self.log.info( + "Chemical Potential {:8.6e} gives Total electron deviation {:6.4e}".format(chempot, nelec_hl - nelec_target) + ) return nelec_hl - nelec_target def get_updated_correlation_kernel(self, curr_dd0, curr_dd1, sym_parents, sym_children): @@ -329,11 +328,13 @@ def get_updated_correlation_kernel(self, curr_dd0, curr_dd1, sym_parents, sym_ch def combine(old, new): return [[np.concatenate([a, b], axis=0) for a, b in zip(x, y)] for (x, y) in zip(old, new)] + else: k = [np.zeros([self.nao] * 4) for x in range(3)] def combine(old, new): return [old[x] + new[x] for x in range(3)] + for d0, d1, parent, children in zip(curr_dd0, curr_dd1, sym_parents, sym_children): local_contrib = parent.construct_correlation_kernel_contrib(eps, d0, d1, eris=None) contrib = parent.get_correlation_kernel_contrib(local_contrib) @@ -353,8 +354,10 @@ def improve_nl_energy(self, use_plasmon=True, deg=5): elocs = [x.calc_exact_ac(eps, use_plasmon, deg) for x in self.fragments] new_correction = etot - sum(elocs) - self.log.info("Numerical integration of the adiabatic connection modified nonlocal energy estimate by %6.4e", - new_correction - orig_correction) + self.log.info( + "Numerical integration of the adiabatic connection modified nonlocal energy estimate by %6.4e", + new_correction - orig_correction, + ) return self.e_tot + new_correction - orig_correction def run_exact_full_ac(self, xc_kernel=None, deg=5, calc_local=False, cluster_constrain=False, npoints=48): @@ -377,10 +380,15 @@ def run_exact_full_ac(self, xc_kernel=None, deg=5, calc_local=False, cluster_con if self.with_df: # Set up for RIRPA zeroth moment calculation. rpa = ssRIRPA(self.mf, xc, self.log) - local_rot = [np.concatenate([x.get_rot_to_mf_ov(), x.r_bos], axis=0) for x in self.fragments] if calc_local else None + local_rot = ( + [np.concatenate([x.get_rot_to_mf_ov(), x.r_bos], axis=0) for x in self.fragments] + if calc_local + else None + ) frag_proj = [x.get_fragment_projector_ov() for x in self.fragments] if calc_local else None - return rpa.direct_AC_integration(local_rot, frag_proj, deg=deg, npoints=npoints, - cluster_constrain=cluster_constrain) + return rpa.direct_AC_integration( + local_rot, frag_proj, deg=deg, npoints=npoints, cluster_constrain=cluster_constrain + ) else: raise NotImplementedError @@ -388,4 +396,5 @@ def _reset(self): super()._reset() self._e_nonlocal = None + REDMET = EDMET diff --git a/vayesta/edmet/fragment.py b/vayesta/edmet/fragment.py index f8b5aa7e6..e4488736d 100644 --- a/vayesta/edmet/fragment.py +++ b/vayesta/edmet/fragment.py @@ -13,6 +13,7 @@ from pyscf import __config__ + class EDMETFragmentExit(Exception): pass @@ -28,7 +29,6 @@ class Options(DMETFragment.Options): class EDMETFragment(DMETFragment): - Options = Options @dataclasses.dataclass @@ -69,8 +69,9 @@ def nbos(self): @property def r_bos(self): if self.sym_parent is not None: - raise RuntimeError("Symmetry transformation for EDMET bosons in particle-hole basis is not yet implemented." - ) + raise RuntimeError( + "Symmetry transformation for EDMET bosons in particle-hole basis is not yet implemented." + ) return self._r_bos @r_bos.setter @@ -87,8 +88,8 @@ def r_bos_ao(self): r_bos = self.r_bos co = self.base.mo_coeff_occ cv = self.base.mo_coeff_vir - r_bosa = r_bos[:, :self.ov_mf].reshape((self.nbos, self.base.nocc, self.base.nvir)) - r_bosb = r_bos[:, self.ov_mf:].reshape((self.nbos, self.base.nocc, self.base.nvir)) + r_bosa = r_bos[:, : self.ov_mf].reshape((self.nbos, self.base.nocc, self.base.nvir)) + r_bosb = r_bos[:, self.ov_mf :].reshape((self.nbos, self.base.nocc, self.base.nvir)) return (einsum("nia,pi,qa->npq", r_bosa, co, cv), einsum("nia,pi,qa->npq", r_bosb, co, cv)) @@ -133,19 +134,20 @@ def get_cv_active(self): return cv, cv def get_rot_to_mf_ov(self): - ro = self.get_overlap('mo[occ]|cluster[occ]') - rv = self.get_overlap('mo[vir]|cluster[vir]') + ro = self.get_overlap("mo[occ]|cluster[occ]") + rv = self.get_overlap("mo[vir]|cluster[vir]") spat_rot = einsum("iJ,aB->iaJB", ro, rv).reshape((self.ov_mf, self.ov_active)).T res = np.zeros((2 * self.ov_active, 2 * self.ov_mf)) - res[:self.ov_active, :self.ov_mf] = spat_rot - res[self.ov_active:2 * self.ov_active, self.ov_mf:2 * self.ov_mf] = spat_rot + res[: self.ov_active, : self.ov_mf] = spat_rot + res[self.ov_active : 2 * self.ov_active, self.ov_mf : 2 * self.ov_mf] = spat_rot return res def get_fragment_projector_ov(self, proj="o", inc_bosons=False): """In space of cluster p-h excitations, generate the projector to the impurity portion of the occupied index.""" if not ("o" in proj or "v" in proj): - raise ValueError("Must project the occupied and/or virtual index to the fragment. Please specify at least " - "one") + raise ValueError( + "Must project the occupied and/or virtual index to the fragment. Please specify at least " "one" + ) nex = self.ov_active_tot if inc_bosons: @@ -154,8 +156,8 @@ def get_fragment_projector_ov(self, proj="o", inc_bosons=False): def get_ov_projector(po, pv): p_ov_spat = einsum("ij,ab->iajb", po, pv).reshape((self.ov_active, self.ov_active)) p_ov = np.zeros((nex, nex)) - p_ov[:self.ov_active, :self.ov_active] = p_ov_spat - p_ov[self.ov_active:2 * self.ov_active, self.ov_active:2 * self.ov_active] = p_ov_spat + p_ov[: self.ov_active, : self.ov_active] = p_ov_spat + p_ov[self.ov_active : 2 * self.ov_active, self.ov_active : 2 * self.ov_active] = p_ov_spat return p_ov p_ov = np.zeros((nex, nex)) @@ -197,12 +199,15 @@ def define_bosons(self, rpa_mom, rot_ov=None, tol=1e-8): want = s > tol nbos = min(sum(want), self.opts.max_bos) if nbos < len(s): - self.log.info("Zeroth moment matching generated %d cluster bosons.Largest discarded singular value: %4.2e.", - nbos, s[nbos:].max()) + self.log.info( + "Zeroth moment matching generated %d cluster bosons.Largest discarded singular value: %4.2e.", + nbos, + s[nbos:].max(), + ) else: self.log.info("Zeroth moment matching generated %d cluster bosons.", nbos) self.log.info("Fragment %s Quasiboson histogram", self.id_name) - self.log.info("------------------------------%s", "-"*len(self.id_name)) + self.log.info("------------------------------%s", "-" * len(self.id_name)) bins = np.hstack([-np.inf, np.logspace(0, -12, 13)[::-1], np.inf]) self.log.info(helper.make_horizontal_histogram(s, bins=bins)) # Calculate the relevant components of the zeroth moment- we don't want to recalculate these. @@ -233,7 +238,7 @@ def construct_boson_hamil(self, eta0_bos, eps, xc_kernel): raise RuntimeError self.a_bos = a_bos - if self.nbos >0: + if self.nbos > 0: self.bos_freqs, x, y = bogoliubov_decouple(a_bos + b_bos, a_bos - b_bos) couplings_aa = einsum("npq,nm->mpq", couplings_aa, x) + einsum("npq,nm->mqp", couplings_aa, y) couplings_bb = np.einsum("npq,nm->mpq", couplings_bb, x) + np.einsum("npq,nm->mqp", couplings_bb, y) @@ -246,7 +251,7 @@ def construct_boson_hamil(self, eta0_bos, eps, xc_kernel): return self.loc_erpa def store_cluster_rpa(self, eta0_bos, eps, xc_kernel): - """This function just stores all required information for the """ + """This function just stores all required information for the""" self.eta0_bos = np.dot(eta0_bos, self.r_bos.T) ov_rot = self.get_rot_to_mf_ov() # Get couplings between all fermionic and boson degrees of freedom. @@ -260,10 +265,10 @@ def store_cluster_rpa(self, eta0_bos, eps, xc_kernel): # This is the bare amb. amb = eps_loc + xc_amb eta0 = np.zeros_like(apb) - eta0[:self.ov_active_tot, :self.ov_active_tot] = self.eta0_ferm - eta0[:self.ov_active_tot, self.ov_active_tot:] = self.eta0_coupling - eta0[self.ov_active_tot:, :self.ov_active_tot] = self.eta0_coupling.T - eta0[self.ov_active_tot:, self.ov_active_tot:] = self.eta0_bos + eta0[: self.ov_active_tot, : self.ov_active_tot] = self.eta0_ferm + eta0[: self.ov_active_tot, self.ov_active_tot :] = self.eta0_coupling + eta0[self.ov_active_tot :, : self.ov_active_tot] = self.eta0_coupling.T + eta0[self.ov_active_tot :, self.ov_active_tot :] = self.eta0_bos # Need to generate projector from our RPA excitation space to the local fragment degrees of freedom. fproj_ov = self.get_fragment_projector_ov() @@ -271,11 +276,10 @@ def store_cluster_rpa(self, eta0_bos, eps, xc_kernel): # - einsum("pq,qp->", fproj_ov, eps_loc[:self.ov_active_tot, :self.ov_active_tot]) \ # - einsum("pq,qp->", fproj_ov, eris[:self.ov_active_tot, :self.ov_active_tot])) / 2.0 xc_b = (xc_apb - xc_amb) / 2.0 - self.loc_erpa = (einsum("pq,qr,rp->", fproj_ov, eta0[:self.ov_active_tot], - (apb - (xc_b / 2.0))[:, :self.ov_active_tot]) - - einsum("pq,qp->", fproj_ov, - ((apb + amb - xc_b) / 2)[:self.ov_active_tot, :self.ov_active_tot]) - ) / 2.0 + self.loc_erpa = ( + einsum("pq,qr,rp->", fproj_ov, eta0[: self.ov_active_tot], (apb - (xc_b / 2.0))[:, : self.ov_active_tot]) + - einsum("pq,qp->", fproj_ov, ((apb + amb - xc_b) / 2)[: self.ov_active_tot, : self.ov_active_tot]) + ) / 2.0 # loc_erpa = (einsum("pq,qr,rp->", fproj_ov, eta0[:self.ov_active_tot], eris[:, :self.ov_active_tot]) # - einsum("pq,qp->", fproj_ov, eris[:self.ov_active_tot, :self.ov_active_tot])) / 4.0 @@ -283,10 +287,12 @@ def store_cluster_rpa(self, eta0_bos, eps, xc_kernel): renorm_amb = dot(eta0, apb, eta0) self.amb_renorm_effect = renorm_amb - amb - maxdev = abs(amb - renorm_amb)[:self.ov_active_tot, :self.ov_active_tot].max() + maxdev = abs(amb - renorm_amb)[: self.ov_active_tot, : self.ov_active_tot].max() if maxdev > 1e-6: - self.log.error("Maximum deviation in irreducible polarisation propagator=%6.4e", - abs(amb - renorm_amb)[:self.ov_active_tot, :self.ov_active_tot].max()) + self.log.error( + "Maximum deviation in irreducible polarisation propagator=%6.4e", + abs(amb - renorm_amb)[: self.ov_active_tot, : self.ov_active_tot].max(), + ) # If have xc kernel from previous iteration want to deduct contribution from this cluster; otherwise bosons # will contain a double-counted representation of the already captured correlation in the cluster. @@ -314,21 +320,25 @@ def _get_boson_hamil(self, apb, amb): couplings_aa = np.zeros((self.nbos, nactive_a, nactive_a)) couplings_bb = np.zeros((self.nbos, nactive_b, nactive_b)) - couplings_aa[:, :self.cluster.nocc_active, self.cluster.nocc_active:] = a[2 * self.ov_active:, - :self.ov_active].reshape( - self.nbos, self.cluster.nocc_active, self.cluster.nvir_active) - couplings_aa[:, self.cluster.nocc_active:, :self.cluster.nocc_active] = b[2 * self.ov_active:, - :self.ov_active].reshape( - self.nbos, self.cluster.nocc_active, self.cluster.nvir_active).transpose([0, 2, 1]) - couplings_bb[:, :self.cluster.nocc_active, self.cluster.nocc_active:] = \ - a[2 * self.ov_active:, self.ov_active:2 * self.ov_active].reshape( - self.nbos, self.cluster.nocc_active, self.cluster.nvir_active) - couplings_bb[:, self.cluster.nocc_active:, :self.cluster.nocc_active] = \ - b[2 * self.ov_active:, self.ov_active:2 * self.ov_active].reshape( - self.nbos, self.cluster.nocc_active, self.cluster.nvir_active).transpose([0, 2, 1]) - - a_bos = a[2 * self.ov_active:, 2 * self.ov_active:] - b_bos = b[2 * self.ov_active:, 2 * self.ov_active:] + couplings_aa[:, : self.cluster.nocc_active, self.cluster.nocc_active :] = a[ + 2 * self.ov_active :, : self.ov_active + ].reshape(self.nbos, self.cluster.nocc_active, self.cluster.nvir_active) + couplings_aa[:, self.cluster.nocc_active :, : self.cluster.nocc_active] = ( + b[2 * self.ov_active :, : self.ov_active] + .reshape(self.nbos, self.cluster.nocc_active, self.cluster.nvir_active) + .transpose([0, 2, 1]) + ) + couplings_bb[:, : self.cluster.nocc_active, self.cluster.nocc_active :] = a[ + 2 * self.ov_active :, self.ov_active : 2 * self.ov_active + ].reshape(self.nbos, self.cluster.nocc_active, self.cluster.nvir_active) + couplings_bb[:, self.cluster.nocc_active :, : self.cluster.nocc_active] = ( + b[2 * self.ov_active :, self.ov_active : 2 * self.ov_active] + .reshape(self.nbos, self.cluster.nocc_active, self.cluster.nvir_active) + .transpose([0, 2, 1]) + ) + + a_bos = a[2 * self.ov_active :, 2 * self.ov_active :] + b_bos = b[2 * self.ov_active :, 2 * self.ov_active :] return couplings_aa, couplings_bb, a_bos, b_bos @@ -343,7 +353,7 @@ def conv_to_aos(r): r = r.reshape((-1, self.base.nocc, self.base.nvir)) return einsum("nia,pi,qa->npq", r, self.base.mo_coeff_occ, self.base.mo_coeff_vir) - rota, rotb = rot[:, :self.ov_mf], rot[:, self.ov_mf:2 * self.ov_mf] + rota, rotb = rot[:, : self.ov_mf], rot[:, self.ov_mf : 2 * self.ov_mf] if hasattr(self.base.mf, "with_df"): rota, rotb = conv_to_aos(rota), conv_to_aos(rotb) @@ -357,8 +367,9 @@ def conv_to_aos(r): else: # This is painful to do for each fragment, but comes from working with 4-index eris. eris = self.base.get_eris_array(self.mf.mo_coeff) - eris = eris[:self.base.nocc, self.base.nocc:, :self.base.nocc, self.base.nocc:].reshape( - (self.ov_mf, self.ov_mf)) + eris = eris[: self.base.nocc, self.base.nocc :, : self.base.nocc, self.base.nocc :].reshape( + (self.ov_mf, self.ov_mf) + ) return dot(rota + rotb, eris, rota.T + rotb.T) def conv_to_aos(self, ra, rb): @@ -370,11 +381,11 @@ def conv_to_aos(r): return conv_to_aos(ra), conv_to_aos(rb) def get_xc_couplings(self, xc_kernel, rot): - ov_mf = self.ov_mf - if isinstance(ov_mf, int): ov_mf = (ov_mf, ov_mf) + if isinstance(ov_mf, int): + ov_mf = (ov_mf, ov_mf) - rota, rotb = rot[:, :ov_mf[0]], rot[:, ov_mf[0]:sum(ov_mf)] + rota, rotb = rot[:, : ov_mf[0]], rot[:, ov_mf[0] : sum(ov_mf)] rota, rotb = self.conv_to_aos(rota, rotb) if self.base.with_df: @@ -393,14 +404,18 @@ def get_xc_couplings(self, xc_kernel, rot): else: # Have full-rank expression for xc kernel, but separate spin channels. acontrib = einsum("lpq,pqrs,mrs->lm", rota, xc_kernel[1], rotb) - acontrib += acontrib.T + einsum("lpq,pqrs,mrs->lm", rota, xc_kernel[0], rota) + einsum("lpq,pqrs,mrs->lm", - rotb, xc_kernel[2], - rotb) + acontrib += ( + acontrib.T + + einsum("lpq,pqrs,mrs->lm", rota, xc_kernel[0], rota) + + einsum("lpq,pqrs,mrs->lm", rotb, xc_kernel[2], rotb) + ) bcontrib = einsum("lpq,pqrs,msr->lm", rota, xc_kernel[1], rotb) - bcontrib += bcontrib.T + einsum("lpq,pqrs,msr->lm", rota, xc_kernel[0], rota) + einsum("lpq,pqrs,msr->lm", - rotb, xc_kernel[2], - rotb) + bcontrib += ( + bcontrib.T + + einsum("lpq,pqrs,msr->lm", rota, xc_kernel[0], rota) + + einsum("lpq,pqrs,msr->lm", rotb, xc_kernel[2], rotb) + ) apb = acontrib + bcontrib amb = acontrib - bcontrib @@ -445,10 +460,12 @@ def proj_hamil_qba(self, exchange_between_bos=True): # V_n <= C_{nia}f_{ia} bos_nonconserv = einsum("npq,pq->n", r_bos_aoa, fa) + einsum("npq,pq->n", r_bos_aob, fb) # \Omega_n <= C_{mia}C_{nib}f_{ab} - C_{mia}C_{nja}f_{ij} - a_bos = einsum("npq,msr,qr,ps->nm", r_bos_aoa, r_bos_aoa, fa, ovlp) + \ - einsum("npq,msr,qr,ps->nm", r_bos_aob, r_bos_aob, fb, ovlp) - a_bos -= einsum("npq,mrs,pr,qs->nm", r_bos_aoa, r_bos_aoa, fa, ovlp) + \ - einsum("npq,mrs,pr,qs->nm", r_bos_aob, r_bos_aob, fb, ovlp) + a_bos = einsum("npq,msr,qr,ps->nm", r_bos_aoa, r_bos_aoa, fa, ovlp) + einsum( + "npq,msr,qr,ps->nm", r_bos_aob, r_bos_aob, fb, ovlp + ) + a_bos -= einsum("npq,mrs,pr,qs->nm", r_bos_aoa, r_bos_aoa, fa, ovlp) + einsum( + "npq,mrs,pr,qs->nm", r_bos_aob, r_bos_aob, fb, ovlp + ) # Write this as a single function for both spin channels, to avoid chance of typos def get_fock_couplings_spin_channel(r_bos_ao, f, co, cv, no, nv): @@ -456,18 +473,21 @@ def get_fock_couplings_spin_channel(r_bos_ao, f, co, cv, no, nv): # No o->v excitation fock contribution. # v->o excitation within active space. # V_{nai} <= C_{nic}f_{ac} - C_{nka}f_{ik} - couplings[:, no:, :no] = einsum("npc,qc,pi,qa->nai", r_bos_ao, f, dot(ovlp, co), cv) - \ - einsum("nkq,pk,pi,qa->nai", r_bos_ao, f, co, dot(ovlp, cv)) + couplings[:, no:, :no] = einsum("npc,qc,pi,qa->nai", r_bos_ao, f, dot(ovlp, co), cv) - einsum( + "nkq,pk,pi,qa->nai", r_bos_ao, f, co, dot(ovlp, cv) + ) # o->o excitation within active space. Note that we're constructing the non-normal ordered parameterisation # here, so all signs are flipped for o-o component. # V_{nij} <= -(\delta_{ij}C_{nkc}f_{ck} - C_{njc}f_{ic}) fac = einsum("nck,ck->n", r_bos_ao, f) - couplings[:, :no, :no] = -einsum("pq,n->npq", np.eye(no), fac) + \ - einsum("npc,qc,qi,pj->nij", r_bos_ao, f, co, dot(ovlp, co)) + couplings[:, :no, :no] = -einsum("pq,n->npq", np.eye(no), fac) + einsum( + "npc,qc,qi,pj->nij", r_bos_ao, f, co, dot(ovlp, co) + ) # v->v excitation within active space. # V_{nab} <= C_{nic}f_{ac} C_{nka}f_{ik} - couplings[:, no:, no:] = einsum("pq,n->npq", np.eye(nv), fac) - \ - einsum("nkp,kq,pa,qb->nab", r_bos_ao, f, dot(ovlp, cv), cv) + couplings[:, no:, no:] = einsum("pq,n->npq", np.eye(nv), fac) - einsum( + "nkp,kq,pa,qb->nab", r_bos_ao, f, dot(ovlp, cv), cv + ) return couplings fcouplings_aa = get_fock_couplings_spin_channel(r_bos_aoa, fa, coa, cva, noa, nva) @@ -485,9 +505,9 @@ def get_fock_couplings_spin_channel(r_bos_ao, f, co, cv, no, nv): if self.base.with_df: if exchange_between_bos: - blk_prefactor = self.nbos * (self.mf.mol.nao ** 2) + blk_prefactor = self.nbos * (self.mf.mol.nao**2) else: - blk_prefactor = self.mf.mol.nao ** 2 + blk_prefactor = self.mf.mol.nao**2 # Limit ourselves to only use quarter the maximum memory for the single largest array. blksize = max(1, int(__config__.MAX_MEMORY / (4 * 8.0 * blk_prefactor))) if blksize > self.mf.with_df.get_naoaux(): @@ -543,16 +563,16 @@ def get_fock_couplings_spin_channel(r_bos_ao, f, co, cv, no, nv): # ccouplings_bb -= einsum("nip,njq,mpq->mji", lb_singl, lb_singl, r_bos_aob) # N^3 ccouplings_aa -= np.tensordot( - la_singl, - np.tensordot(la_singl, r_bos_aoa, ([2], [2])), # njq,mpq->njmp - ([0, 2], [0, 3]) - ).transpose([2, 1, 0]) # nip,njmp->ijm->mji + la_singl, np.tensordot(la_singl, r_bos_aoa, ([2], [2])), ([0, 2], [0, 3]) # njq,mpq->njmp + ).transpose( + [2, 1, 0] + ) # nip,njmp->ijm->mji ccouplings_bb -= np.tensordot( - lb_singl, - np.tensordot(lb_singl, r_bos_aob, ([2], [2])), # njq,mpq->njmp - ([0, 2], [0, 3]) - ).transpose([2, 1, 0]) # nip,njmp->ijm->mji + lb_singl, np.tensordot(lb_singl, r_bos_aob, ([2], [2])), ([0, 2], [0, 3]) # njq,mpq->njmp + ).transpose( + [2, 1, 0] + ) # nip,njmp->ijm->mji # print("!!4!!", # abs(einsum("nip,njq,mpq->mji", la_singl, la_singl, r_bos_aoa) - np.tensordot( @@ -583,17 +603,22 @@ def get_fock_couplings_spin_channel(r_bos_ao, f, co, cv, no, nv): # einsum("npq,mpr->nqrm", l_, r_bos_aob), # einsum("npq,lrq->nprl", l_, r_bos_aob)) - a_bos -= np.tensordot(np.tensordot(l_, r_bos_aoa, ([1], [1])), # npq,mpr->nqmr - np.tensordot(l_, r_bos_aoa, ([2], [2])), # npq,mrq->npmr - ([0, 1, 3], [0, 3, 1])) # nqmr,nrlq->ml - a_bos -= np.tensordot(np.tensordot(l_, r_bos_aob, ([1], [1])), # npq,mpr->nqmr - np.tensordot(l_, r_bos_aob, ([2], [2])), # npq,mrq->npmr - ([0, 1, 3], [0, 3, 1])) # nqmr,nrlq->ml + a_bos -= np.tensordot( + np.tensordot(l_, r_bos_aoa, ([1], [1])), # npq,mpr->nqmr + np.tensordot(l_, r_bos_aoa, ([2], [2])), # npq,mrq->npmr + ([0, 1, 3], [0, 3, 1]), + ) # nqmr,nrlq->ml + a_bos -= np.tensordot( + np.tensordot(l_, r_bos_aob, ([1], [1])), # npq,mpr->nqmr + np.tensordot(l_, r_bos_aob, ([2], [2])), # npq,mrq->npmr + ([0, 1, 3], [0, 3, 1]), + ) # nqmr,nrlq->ml t_bos_exchange += timer() - t_bosex_start else: - raise NotImplementedError("Explicit QBA Hamiltonian construction is currently only implemented for use with" - "density fitting.") + raise NotImplementedError( + "Explicit QBA Hamiltonian construction is currently only implemented for use with" "density fitting." + ) couplings_aa = fcouplings_aa + ccouplings_aa couplings_bb = fcouplings_bb + ccouplings_bb @@ -612,20 +637,28 @@ def get_fock_couplings_spin_channel(r_bos_ao, f, co, cv, no, nv): # Decouple bosons here. self.bos_freqs, c = np.linalg.eigh(a_bos) - + self.couplings = (einsum("nm,npq->mqp", c, couplings_aa), einsum("nm,npq->mqp", c, couplings_bb)) # ccouplings[n,p,q] = C_{nia}; can use this for energy evaluation later. self.energy_couplings = (einsum("nm,npq->mqp", c, ccouplings_aa), einsum("nm,npq->mqp", c, ccouplings_bb)) - self.log.info("Time for Bosonic Hamiltonian Projection into fragment %d: %s", self.id, - time_string(timer() - t0)) + self.log.info( + "Time for Bosonic Hamiltonian Projection into fragment %d: %s", self.id, time_string(timer() - t0) + ) if exchange_between_bos: - self.log.info(" %s for fock components, %s for N^3 scaling coulombic components and %s for N^4 " - "bosonic exchange.", time_string(t_fock), time_string(t_coulomb), - time_string(t_bos_exchange)) + self.log.info( + " %s for fock components, %s for N^3 scaling coulombic components and %s for N^4 " + "bosonic exchange.", + time_string(t_fock), + time_string(t_coulomb), + time_string(t_bos_exchange), + ) else: - self.log.info(" %s for fock components, and %s for N^3 scaling coulombic components.", - time_string(t_fock), time_string(t_coulomb)) + self.log.info( + " %s for fock components, and %s for N^3 scaling coulombic components.", + time_string(t_fock), + time_string(t_coulomb), + ) def check_qba_approx(self, rdm1): """Given boson and cluster coefficient definitions, checks deviation from exact bosonic commutation relations @@ -637,8 +670,8 @@ def check_qba_approx(self, rdm1): """ r_bos_a, r_bos_b = self.get_rbos_split() - r_o = self.get_overlap('mo[occ]|cluster[occ]') - r_v = self.get_overlap('mo[vir]|cluster[vir]') + r_o = self.get_overlap("mo[occ]|cluster[occ]") + r_v = self.get_overlap("mo[vir]|cluster[vir]") if not self.base.is_uhf: r_o = (r_o, r_o) r_v = (r_v, r_v) @@ -652,20 +685,22 @@ def check_qba_approx(self, rdm1): vdev_b = einsum("nia,mib,ac,bd->nmcd", r_bos_b, r_bos_b, r_v[1], r_v[1]) no_a, no_b = r_o[0].shape[1], r_o[1].shape[1] - dev = einsum("nmij,ij->nm", odev_a, np.eye(no_a) - rdm1[0][:no_a, :no_a]) + \ - einsum("nmij,ij->nm", odev_b, np.eye(no_b) - rdm1[1][:no_b, :no_b]) + \ - einsum("nmab,ab->nm", vdev_a, rdm1[0][no_a:, no_a:]) + \ - einsum("nmab,ab->nm", vdev_b, rdm1[1][no_b:, no_b:]) + dev = ( + einsum("nmij,ij->nm", odev_a, np.eye(no_a) - rdm1[0][:no_a, :no_a]) + + einsum("nmij,ij->nm", odev_b, np.eye(no_b) - rdm1[1][:no_b, :no_b]) + + einsum("nmab,ab->nm", vdev_a, rdm1[0][no_a:, no_a:]) + + einsum("nmab,ab->nm", vdev_b, rdm1[1][no_b:, no_b:]) + ) self.log.info("Maximum neglected local density fluctuation in quasi-boson commutation=%6.4e", abs(dev.max())) def get_rbos_split(self): - r_bos_a = self.r_bos[:, :self.ov_mf] - r_bos_b = self.r_bos[:, self.ov_mf:] + r_bos_a = self.r_bos[:, : self.ov_mf] + r_bos_b = self.r_bos[:, self.ov_mf :] return r_bos_a.reshape((self.nbos, self.base.nocc, self.base.nvir)), r_bos_b.reshape( - (self.nbos, self.base.nocc, self.base.nvir)) + (self.nbos, self.base.nocc, self.base.nvir) + ) - def kernel(self, solver=None, eris=None, construct_bath=False, - chempot=None): + def kernel(self, solver=None, eris=None, construct_bath=False, chempot=None): """Solve the fragment with the specified solver and chemical potential.""" solver = solver or self.solver @@ -674,11 +709,11 @@ def kernel(self, solver=None, eris=None, construct_bath=False, cluster_solver = self.get_solver(solver) # Chemical potential if chempot is not None: - px = self.get_fragment_projector(self.cluster.c_active) + px = self.get_fragment_projector(self.cluster.c_active) if isinstance(px, tuple): - cluster_solver.v_ext = (-chempot*px[0], -chempot*px[1]) + cluster_solver.v_ext = (-chempot * px[0], -chempot * px[1]) else: - cluster_solver.v_ext = -chempot*px + cluster_solver.v_ext = -chempot * px with log_time(self.log.info, ("Time for %s solver:" % solver) + " %s"): cluster_solver.kernel() @@ -690,13 +725,14 @@ def kernel(self, solver=None, eris=None, construct_bath=False, if self.nbos > 0: self.check_qba_approx(dm1) dm_eb = wf.make_rdm_eb() - self._results = results = self.Results(fid=self.id, n_active=self.cluster.norb_active, - converged=True, wf=wf, dm1=dm1, dm2=dm2, dm_eb=dm_eb) + self._results = results = self.Results( + fid=self.id, n_active=self.cluster.norb_active, converged=True, wf=wf, dm1=dm1, dm2=dm2, dm_eb=dm_eb + ) results.e1, results.e2, results.e_fb = self.get_edmet_energy_contrib() if self.opts.make_dd_moments: - r_o = self.get_overlap('cluster[occ]|frag') - r_v = self.get_overlap('cluster[vir]|frag') + r_o = self.get_overlap("cluster[occ]|frag") + r_v = self.get_overlap("cluster[vir]|frag") if isinstance(r_o, tuple): r = tuple([np.concatenate([x, y], axis=0) for x, y in zip(r_o, r_v)]) else: @@ -737,16 +773,17 @@ def get_edmet_energy_contrib(self, hamil=None): # dm_eb -> <0|b^+ p^+ q|0> in P[p,q,b]. # couplings -> C_{nia} in couplings[n,p,q]. # Want C_{njb} ( - ) so our energy is: - efb = 0.25 * (einsum("qr,npq,rpn", p_imp[0], couplings[0], dm_eb[0] - dm_eb[0].transpose(1, 0, 2)) + - einsum("qr,npq,rpn", p_imp[1], couplings[1], dm_eb[1] - dm_eb[1].transpose(1, 0, 2)) - ) + efb = 0.25 * ( + einsum("qr,npq,rpn", p_imp[0], couplings[0], dm_eb[0] - dm_eb[0].transpose(1, 0, 2)) + + einsum("qr,npq,rpn", p_imp[1], couplings[1], dm_eb[1] - dm_eb[1].transpose(1, 0, 2)) + ) self.delta = efb else: efb = 0.5 * ( - np.einsum("pr,npq,rqn", p_imp[0], couplings[0], dm_eb[0]) + - np.einsum("qr,npq,prn", p_imp[0], couplings[0], dm_eb[0]) + - np.einsum("pr,npq,rqn", p_imp[1], couplings[1], dm_eb[1]) + - np.einsum("qr,npq,prn", p_imp[1], couplings[1], dm_eb[1]) + np.einsum("pr,npq,rqn", p_imp[0], couplings[0], dm_eb[0]) + + np.einsum("qr,npq,prn", p_imp[0], couplings[0], dm_eb[0]) + + np.einsum("pr,npq,rqn", p_imp[1], couplings[1], dm_eb[1]) + + np.einsum("qr,npq,prn", p_imp[1], couplings[1], dm_eb[1]) ) return e1, e2, efb @@ -760,10 +797,12 @@ def construct_correlation_kernel_contrib(self, epsilon, m0_new, m1_new, eris=Non new_amb, new_apb = self.get_composite_moments(m0_new, m1_new) - r_occ = self.get_overlap('mo[occ]|cluster[occ]') - r_vir = self.get_overlap('mo[vir]|cluster[vir]') - if not isinstance(r_occ, tuple): r_occ = (r_occ, r_occ) - if not isinstance(r_vir, tuple): r_vir = (r_vir, r_vir) + r_occ = self.get_overlap("mo[occ]|cluster[occ]") + r_vir = self.get_overlap("mo[vir]|cluster[vir]") + if not isinstance(r_occ, tuple): + r_occ = (r_occ, r_occ) + if not isinstance(r_vir, tuple): + r_vir = (r_vir, r_vir) ov_a, ov_b = self.ov_active_ab no_a, no_b = self.nocc_ab nv_a, nv_b = self.nvir_ab @@ -808,11 +847,19 @@ def get_fermionic_spat_contrib(acon, bcon, no_l, nv_l, no_r, nv_r): fermionic[:no_l, no_l:, no_r:, :no_r] = bcon.reshape(f_shape).transpose((0, 1, 3, 2)) fermionic = fermionic + fermionic.transpose((1, 0, 3, 2)) return fermionic + ferm_aa = get_fermionic_spat_contrib(new_xc_a[:ov_a, :ov_a], new_xc_b[:ov_a, :ov_a], no_a, nv_a, no_a, nv_a) - ferm_ab = get_fermionic_spat_contrib(new_xc_a[:ov_a, ov_a:ov_a + ov_b], new_xc_b[:ov_a, ov_a:ov_a+ov_b], - no_a, nv_a, no_b, nv_b) - ferm_bb = get_fermionic_spat_contrib(new_xc_a[ov_a:ov_a+ov_b, ov_a:ov_a+ov_b], - new_xc_b[ov_a:ov_a+ov_b, ov_a:ov_a+ov_b], no_b, nv_b, no_b, nv_b) + ferm_ab = get_fermionic_spat_contrib( + new_xc_a[:ov_a, ov_a : ov_a + ov_b], new_xc_b[:ov_a, ov_a : ov_a + ov_b], no_a, nv_a, no_b, nv_b + ) + ferm_bb = get_fermionic_spat_contrib( + new_xc_a[ov_a : ov_a + ov_b, ov_a : ov_a + ov_b], + new_xc_b[ov_a : ov_a + ov_b, ov_a : ov_a + ov_b], + no_b, + nv_b, + no_b, + nv_b, + ) def get_fb_spat_contrib(acon, bcon, no, nv): fb_shape = (no, nv, self.nbos) @@ -822,9 +869,10 @@ def get_fb_spat_contrib(acon, bcon, no, nv): return fermbos if self.opts.boson_xc_kernel: - fb_a = get_fb_spat_contrib(new_xc_a[:ov_a, ov_a+ov_b:], new_xc_b[:ov_a, ov_a+ov_b:], no_a, nv_a) - fb_b = get_fb_spat_contrib(new_xc_a[ov_a:ov_a+ov_b, ov_a+ov_b:], new_xc_b[ov_a:ov_a+ov_b, ov_a+ov_b:], - no_b, nv_b) + fb_a = get_fb_spat_contrib(new_xc_a[:ov_a, ov_a + ov_b :], new_xc_b[:ov_a, ov_a + ov_b :], no_a, nv_a) + fb_b = get_fb_spat_contrib( + new_xc_a[ov_a : ov_a + ov_b, ov_a + ov_b :], new_xc_b[ov_a : ov_a + ov_b, ov_a + ov_b :], no_b, nv_b + ) else: fb_a = np.zeros((no_a + nv_a,) * 2 + (0,)) fb_b = np.zeros((no_b + nv_b,) * 2 + (0,)) @@ -837,37 +885,37 @@ def construct_low_rank_rep(vaa, vab, vbb, v_fb_a, v_fb_b): use symmetric decomposition...""" na, nb = vaa.shape[0], vbb.shape[0] nbos = v_fb_a.shape[2] - nferm_tot = na ** 2 + nb ** 2 + nferm_tot = na**2 + nb**2 - vaa = vaa.reshape((na ** 2, na ** 2)) - vbb = vbb.reshape((nb ** 2, nb ** 2)) - vab = vab.reshape((na ** 2, nb ** 2)) + vaa = vaa.reshape((na**2, na**2)) + vbb = vbb.reshape((nb**2, nb**2)) + vab = vab.reshape((na**2, nb**2)) - v_fb_a_ex = v_fb_a.reshape((na ** 2, nbos)) - v_fb_b_ex = v_fb_b.reshape((nb ** 2, nbos)) + v_fb_a_ex = v_fb_a.reshape((na**2, nbos)) + v_fb_b_ex = v_fb_b.reshape((nb**2, nbos)) - v_fb_a_dex = v_fb_a.transpose((1, 0, 2)).reshape((na ** 2, nbos)) - v_fb_b_dex = v_fb_b.transpose((1, 0, 2)).reshape((nb ** 2, nbos)) + v_fb_a_dex = v_fb_a.transpose((1, 0, 2)).reshape((na**2, nbos)) + v_fb_b_dex = v_fb_b.transpose((1, 0, 2)).reshape((nb**2, nbos)) - fullv = np.zeros((na ** 2 + nb ** 2 + 2 * nbos,) * 2) - fullv[:na ** 2, :na ** 2] = vaa - fullv[na ** 2:nferm_tot, na ** 2:nferm_tot] = vbb - fullv[:na ** 2, na ** 2:nferm_tot] = vab - fullv[na ** 2:nferm_tot, :na ** 2] = vab.T + fullv = np.zeros((na**2 + nb**2 + 2 * nbos,) * 2) + fullv[: na**2, : na**2] = vaa + fullv[na**2 : nferm_tot, na**2 : nferm_tot] = vbb + fullv[: na**2, na**2 : nferm_tot] = vab + fullv[na**2 : nferm_tot, : na**2] = vab.T # Component coupling to bosonic excitations. - fullv[:na ** 2, nferm_tot:nferm_tot + nbos] = v_fb_a_ex - fullv[na ** 2:nferm_tot, nferm_tot:nferm_tot + nbos] = v_fb_b_ex + fullv[: na**2, nferm_tot : nferm_tot + nbos] = v_fb_a_ex + fullv[na**2 : nferm_tot, nferm_tot : nferm_tot + nbos] = v_fb_b_ex - fullv[nferm_tot:nferm_tot + nbos, :na ** 2] = v_fb_a_ex.T - fullv[nferm_tot:nferm_tot + nbos, na ** 2:nferm_tot] = v_fb_b_ex.T + fullv[nferm_tot : nferm_tot + nbos, : na**2] = v_fb_a_ex.T + fullv[nferm_tot : nferm_tot + nbos, na**2 : nferm_tot] = v_fb_b_ex.T # Component coupling to bosonic excitations. - fullv[:na ** 2, nferm_tot + nbos:] = v_fb_a_dex - fullv[na ** 2:nferm_tot, nferm_tot + nbos:] = v_fb_b_dex + fullv[: na**2, nferm_tot + nbos :] = v_fb_a_dex + fullv[na**2 : nferm_tot, nferm_tot + nbos :] = v_fb_b_dex - fullv[nferm_tot + nbos:, :na ** 2] = v_fb_a_dex.T - fullv[nferm_tot + nbos:, na ** 2:nferm_tot] = v_fb_b_dex.T + fullv[nferm_tot + nbos :, : na**2] = v_fb_a_dex.T + fullv[nferm_tot + nbos :, na**2 : nferm_tot] = v_fb_b_dex.T u, s, v = np.linalg.svd(fullv, full_matrices=False) want = s > svdtol @@ -876,12 +924,13 @@ def construct_low_rank_rep(vaa, vab, vbb, v_fb_a, v_fb_b): repr_l = einsum("n,np->np", s[:nwant] ** (0.5), v[:nwant]) repr_r = einsum("n,pn->np", s[:nwant] ** (0.5), u[:, :nwant]) - repf_a = (repr_l[:, :na ** 2].reshape((nwant, na, na)), - repr_r[:, :na ** 2].reshape((nwant, na, na))) - repf_b = (repr_l[:, na ** 2:nferm_tot].reshape((nwant, nb, nb)), - repr_r[:, na ** 2:nferm_tot].reshape((nwant, nb, nb))) - repbos_ex = (repr_l[:, nferm_tot:nferm_tot + nbos], repr_r[:, nferm_tot:nferm_tot + nbos]) - repbos_dex = (repr_l[:, nferm_tot + nbos:], repr_r[:, nferm_tot + nbos:]) + repf_a = (repr_l[:, : na**2].reshape((nwant, na, na)), repr_r[:, : na**2].reshape((nwant, na, na))) + repf_b = ( + repr_l[:, na**2 : nferm_tot].reshape((nwant, nb, nb)), + repr_r[:, na**2 : nferm_tot].reshape((nwant, nb, nb)), + ) + repbos_ex = (repr_l[:, nferm_tot : nferm_tot + nbos], repr_r[:, nferm_tot : nferm_tot + nbos]) + repbos_dex = (repr_l[:, nferm_tot + nbos :], repr_r[:, nferm_tot + nbos :]) return repf_a, repf_b, repbos_ex, repbos_dex return construct_low_rank_rep(ferm_aa, ferm_ab, ferm_bb, fb_a, fb_b) @@ -901,10 +950,19 @@ def get_correlation_kernel_contrib(self, contrib): r_ao_bosa, r_ao_bosb = self.r_ao_bos bos_contrib = [ - (einsum("nz,zpq->npq", repbos_ex[0], r_ao_bosa) + einsum("nz,zpq->nqp", repbos_dex[0], r_ao_bosa), - einsum("nz,zpq->npq", repbos_ex[1], r_ao_bosa) + einsum("nz,zpq->nqp", repbos_dex[1], r_ao_bosa)), - (einsum("nz,zpq->npq", repbos_ex[0], r_ao_bosb) + einsum("nz,zpq->nqp", repbos_dex[0], r_ao_bosb), - einsum("nz,zpq->npq", repbos_ex[1], r_ao_bosb) + einsum("nz,zpq->nqp", repbos_dex[1], r_ao_bosb))] + ( + einsum("nz,zpq->npq", repbos_ex[0], r_ao_bosa) + + einsum("nz,zpq->nqp", repbos_dex[0], r_ao_bosa), + einsum("nz,zpq->npq", repbos_ex[1], r_ao_bosa) + + einsum("nz,zpq->nqp", repbos_dex[1], r_ao_bosa), + ), + ( + einsum("nz,zpq->npq", repbos_ex[0], r_ao_bosb) + + einsum("nz,zpq->nqp", repbos_dex[0], r_ao_bosb), + einsum("nz,zpq->npq", repbos_ex[1], r_ao_bosb) + + einsum("nz,zpq->nqp", repbos_dex[1], r_ao_bosb), + ), + ] res = [tuple([z1 + z2 for z1, z2 in zip(x, y)]) for x, y in zip(res, bos_contrib)] self.prev_xc_contrib = res return res @@ -953,8 +1011,10 @@ def get_composite_moments(self, m0_new, m1_new): def get_updated(orig, update, rot_ovf, rot_fov): """Given the original value of a block, the updated solver value, and rotations between appropriate spaces generate the updated value of the appropriate block.""" - if not isinstance(rot_ovf, tuple): rot_ovf = (rot_ovf, rot_ovf) - if not isinstance(rot_fov, tuple): rot_fov = (rot_fov, rot_fov) + if not isinstance(rot_ovf, tuple): + rot_ovf = (rot_ovf, rot_ovf) + if not isinstance(rot_fov, tuple): + rot_fov = (rot_fov, rot_fov) # Generate difference in local, two-point excitation basis. diff = update - np.linalg.multi_dot([rot_ovf[0], orig, rot_ovf[1].T]) return orig + np.linalg.multi_dot([rot_fov[0], diff, rot_fov[1].T]) @@ -963,12 +1023,13 @@ def get_updated_spincomponents(orig, update, rot_ov_frag, rot_frag_ov): newmat = orig.copy() newmat[:ov_a, :ov_a] = get_updated(newmat[:ov_a, :ov_a], update[0], rot_ov_frag[0], rot_frag_ov[0]) - newmat[:ov_a, ov_a:ov_a + ov_b] = get_updated(newmat[:ov_a, ov_a:ov_a + ov_b], update[1], rot_ov_frag, - rot_frag_ov) - newmat[ov_a:ov_a + ov_b, :ov_a] = newmat[:ov_a, ov_a:ov_a + ov_b].T - newmat[ov_a:ov_a + ov_b, ov_a:ov_a + ov_b] = get_updated(newmat[ov_a:ov_a + ov_b, ov_a:ov_a + ov_b], - update[2], - rot_ov_frag[1], rot_frag_ov[1]) + newmat[:ov_a, ov_a : ov_a + ov_b] = get_updated( + newmat[:ov_a, ov_a : ov_a + ov_b], update[1], rot_ov_frag, rot_frag_ov + ) + newmat[ov_a : ov_a + ov_b, :ov_a] = newmat[:ov_a, ov_a : ov_a + ov_b].T + newmat[ov_a : ov_a + ov_b, ov_a : ov_a + ov_b] = get_updated( + newmat[ov_a : ov_a + ov_b, ov_a : ov_a + ov_b], update[2], rot_ov_frag[1], rot_frag_ov[1] + ) return newmat new_amb = get_updated_spincomponents(amb_orig, m1_new, rot_ov_frag, rot_frag_ov) @@ -982,8 +1043,8 @@ def get_rot_ov_frag(self): """Get rotations between the relevant space for fragment two-point excitations and the cluster active occupied- virtual excitations.""" - occ_frag_rot = self.get_overlap('cluster[occ]|frag') - vir_frag_rot = self.get_overlap('cluster[vir]|frag') + occ_frag_rot = self.get_overlap("cluster[occ]|frag") + vir_frag_rot = self.get_overlap("cluster[vir]|frag") ov_loc = self.ov_active if self.opts.old_sc_condition: # Then get projectors to local quantities in ov-basis. Note this needs to be stacked to apply to each spin @@ -1001,7 +1062,7 @@ def get_rot_ov_frag(self): for p in range(self.n_frag): for q in range(p + 1): proj_to_order[p, q, p, q] = proj_to_order[q, p, p, q] = 1.0 - proj_to_order = proj_to_order.reshape((self.n_frag ** 2, self.n_frag, self.n_frag)) + proj_to_order = proj_to_order.reshape((self.n_frag**2, self.n_frag, self.n_frag)) # Now restrict to triangular portion of array proj_to_order = pyscf.lib.pack_tril(proj_to_order) # proj_from_order = np.linalg.pinv(proj_to_order) @@ -1043,22 +1104,38 @@ def calc_contrib_partialint(alpha): eta0 = calc_eta0(alpha) eta0inv = np.linalg.inv(eta0) - return -(einsum("pq,qr,rp->", fproj_ov, eta0[:self.ov_active_tot], xc_apb[:, :self.ov_active_tot]) + - einsum("pq,qr,rp->", fproj_ov, eta0inv[:self.ov_active_tot], - xc_amb[:, :self.ov_active_tot])) / 4 + return ( + -( + einsum("pq,qr,rp->", fproj_ov, eta0[: self.ov_active_tot], xc_apb[:, : self.ov_active_tot]) + + einsum("pq,qr,rp->", fproj_ov, eta0inv[: self.ov_active_tot], xc_amb[:, : self.ov_active_tot]) + ) + / 4 + ) def calc_contrib_direct(alpha): eta0 = calc_eta0(alpha) eta0inv = np.linalg.inv(eta0) # This is just the contribution from the bare, standard coulomb interaction. - e_bare = einsum("pq,pr,rq->", fproj_ov, - (eta0 - np.eye(self.ov_active_tot + self.nbos))[:self.ov_active_tot], - eris[:, :self.ov_active_tot]) / 2 + e_bare = ( + einsum( + "pq,pr,rq->", + fproj_ov, + (eta0 - np.eye(self.ov_active_tot + self.nbos))[: self.ov_active_tot], + eris[:, : self.ov_active_tot], + ) + / 2 + ) # Need to account for renormalisation of bosonic interactions, which is included in cluster coulomb kernel. renorm = self.amb_renorm_effect / 2 - e_renorm = einsum("pq,pr,rq->", fproj_ov, - (eta0inv - np.eye(self.ov_active_tot + self.nbos))[:self.ov_active_tot], - renorm[:, :self.ov_active_tot]) / 2 + e_renorm = ( + einsum( + "pq,pr,rq->", + fproj_ov, + (eta0inv - np.eye(self.ov_active_tot + self.nbos))[: self.ov_active_tot], + renorm[:, : self.ov_active_tot], + ) + / 2 + ) return e_bare + e_renorm @@ -1071,11 +1148,16 @@ def run_ac_inter(func, deg=5): return sum([w * func(p) for w, p in zip(weights, points)]) if use_plasmon: - e_plasmon = (einsum("pq,qr,rp->", fproj_ov, self.eta0[:self.ov_active_tot], - self.apb[:, :self.ov_active_tot]) - ( - einsum("pq,qp->", fproj_ov, - (eps_loc + eris + self.amb_renorm_effect / 2)[:self.ov_active_tot, - :self.ov_active_tot]))) / 2 + e_plasmon = ( + einsum("pq,qr,rp->", fproj_ov, self.eta0[: self.ov_active_tot], self.apb[:, : self.ov_active_tot]) + - ( + einsum( + "pq,qp->", + fproj_ov, + (eps_loc + eris + self.amb_renorm_effect / 2)[: self.ov_active_tot, : self.ov_active_tot], + ) + ) + ) / 2 return e_plasmon + run_ac_inter(calc_contrib_partialint, deg) @@ -1109,8 +1191,7 @@ def calc_contrib_partialint(alpha): eta0 = calc_eta0(alpha) eta0inv = np.linalg.inv(eta0) - return -(einsum("pq,qp->", eta0, xc_apb) + - einsum("pq,qp->", eta0inv, xc_amb)) / 4 + return -(einsum("pq,qp->", eta0, xc_apb) + einsum("pq,qp->", eta0inv, xc_amb)) / 4 def calc_contrib_direct(alpha): eta0 = calc_eta0(alpha) @@ -1131,15 +1212,17 @@ def run_ac_inter(func, deg=5): weights /= 2 return sum([w * func(p) for w, p in zip(weights, points)]) - e_plasmon = (einsum("pq,qp->", self.eta0, self.apb) - - ((eps_loc + eris + self.amb_renorm_effect / 2).trace())) / 2 + e_plasmon = ( + einsum("pq,qp->", self.eta0, self.apb) - ((eps_loc + eris + self.amb_renorm_effect / 2).trace()) + ) / 2 e_plasmon = e_plasmon + run_ac_inter(calc_contrib_partialint, deg) e_direct = run_ac_inter(calc_contrib_direct, deg) - self.log.info("Difference between plasmon and direct AC total correlation energies: %6.4e", - e_plasmon - e_direct) + self.log.info( + "Difference between plasmon and direct AC total correlation energies: %6.4e", e_plasmon - e_direct + ) return e_plasmon, e_direct @@ -1178,7 +1261,7 @@ def split_ov_spin_components(self, mat): else: ova = ovb = ov - return mat[:ova, :ova], mat[:ova, ova:ova + ovb], mat[ova:ova + ovb, ova:ova + ovb] + return mat[:ova, :ova], mat[:ova, ova : ova + ovb], mat[ova : ova + ovb, ova : ova + ovb] def bogoliubov_decouple(apb, amb): diff --git a/vayesta/edmet/uedmet.py b/vayesta/edmet/uedmet.py index ede028ef5..f9fa19217 100644 --- a/vayesta/edmet/uedmet.py +++ b/vayesta/edmet/uedmet.py @@ -6,7 +6,6 @@ class UEDMET(REDMET, UDMET): - @property def eps(self): noa, nob = self.nocc diff --git a/vayesta/edmet/ufragment.py b/vayesta/edmet/ufragment.py index 80e16b41f..7ab3ee349 100644 --- a/vayesta/edmet/ufragment.py +++ b/vayesta/edmet/ufragment.py @@ -7,7 +7,6 @@ class UEDMETFragment(UDMETFragment, EDMETFragment): - @property def ov_active(self): no_a, no_b = self.cluster.nocc_active @@ -32,8 +31,8 @@ def r_bos_ao(self): co_a, co_b = self.base.mo_coeff_occ cv_a, cv_b = self.base.mo_coeff_vir - r_bosa = r_bos[:, :self.ov_mf[0]].reshape((self.nbos, self.base.nocc[0], self.base.nvir[0])) - r_bosb = r_bos[:, self.ov_mf[0]:].reshape((self.nbos, self.base.nocc[1], self.base.nvir[1])) + r_bosa = r_bos[:, : self.ov_mf[0]].reshape((self.nbos, self.base.nocc[0], self.base.nvir[0])) + r_bosb = r_bos[:, self.ov_mf[0] :].reshape((self.nbos, self.base.nocc[1], self.base.nvir[1])) return (einsum("nia,pi,qa->npq", r_bosa, co_a, cv_a), einsum("nia,pi,qa->npq", r_bosb, co_b, cv_b)) else: r_bos_ao = self.sym_parent.r_bos_ao @@ -51,20 +50,21 @@ def get_cv_active(self): return self.cluster.c_active_vir def get_rot_to_mf_ov(self): - r_o = self.get_overlap('mo[occ]|cluster[occ]') - r_v = self.get_overlap('mo[vir]|cluster[vir]') + r_o = self.get_overlap("mo[occ]|cluster[occ]") + r_v = self.get_overlap("mo[vir]|cluster[vir]") spat_rota = einsum("iJ,aB->iaJB", r_o[0], r_v[0]).reshape((self.ov_mf[0], self.ov_active[0])).T spat_rotb = einsum("iJ,aB->iaJB", r_o[1], r_v[1]).reshape((self.ov_mf[1], self.ov_active[1])).T res = np.zeros((sum(self.ov_active), sum(self.ov_mf))) - res[:self.ov_active[0], :self.ov_mf[0]] = spat_rota - res[self.ov_active[0]:, self.ov_mf[0]:] = spat_rotb + res[: self.ov_active[0], : self.ov_mf[0]] = spat_rota + res[self.ov_active[0] :, self.ov_mf[0] :] = spat_rotb return res def get_fragment_projector_ov(self, proj="o", inc_bosons=False): """In space of cluster p-h excitations, generate the projector to the .""" if not ("o" in proj or "v" in proj): - raise ValueError("Must project the occupied and/or virtual index to the fragment. Please specify at least " - "one") + raise ValueError( + "Must project the occupied and/or virtual index to the fragment. Please specify at least " "one" + ) nex = self.ov_active_tot if inc_bosons: @@ -74,8 +74,8 @@ def get_ov_projector(poa, pob, pva, pvb): p_ova = einsum("ij,ab->iajb", poa, pva).reshape((self.ov_active[0], self.ov_active[0])) p_ovb = einsum("ij,ab->iajb", pob, pvb).reshape((self.ov_active[1], self.ov_active[1])) p_ov = np.zeros((nex, nex)) - p_ov[:self.ov_active[0], :self.ov_active[0]] = p_ova - p_ov[self.ov_active[0]:self.ov_active_tot, self.ov_active[0]:self.ov_active_tot] = p_ovb + p_ov[: self.ov_active[0], : self.ov_active[0]] = p_ova + p_ov[self.ov_active[0] : self.ov_active_tot, self.ov_active[0] : self.ov_active_tot] = p_ovb return p_ov p_ov = np.zeros((nex, nex)) @@ -101,25 +101,27 @@ def _get_boson_hamil(self, apb, amb): couplings_aa = np.zeros((self.nbos, n_a, n_a)) couplings_bb = np.zeros((self.nbos, n_b, n_b)) - couplings_aa[:, :nocc_a, nocc_a:] = a[ov_a + ov_b:, :ov_a].reshape(self.nbos, nocc_a, nvir_a) - couplings_aa[:, nocc_a:, :nocc_a] = b[ov_a + ov_b:, :ov_a].reshape(self.nbos, nocc_a, nvir_a).transpose( - [0, 2, 1]) + couplings_aa[:, :nocc_a, nocc_a:] = a[ov_a + ov_b :, :ov_a].reshape(self.nbos, nocc_a, nvir_a) + couplings_aa[:, nocc_a:, :nocc_a] = ( + b[ov_a + ov_b :, :ov_a].reshape(self.nbos, nocc_a, nvir_a).transpose([0, 2, 1]) + ) - couplings_bb[:, :nocc_b, nocc_b:] = a[ov_a + ov_b:, ov_a:ov_a + ov_b].reshape(self.nbos, nocc_b, nvir_b) - couplings_bb[:, nocc_b:, :nocc_b] = b[ov_a + ov_b:, ov_a:ov_a + ov_b].reshape(self.nbos, nocc_b, - nvir_b).transpose( - [0, 2, 1]) + couplings_bb[:, :nocc_b, nocc_b:] = a[ov_a + ov_b :, ov_a : ov_a + ov_b].reshape(self.nbos, nocc_b, nvir_b) + couplings_bb[:, nocc_b:, :nocc_b] = ( + b[ov_a + ov_b :, ov_a : ov_a + ov_b].reshape(self.nbos, nocc_b, nvir_b).transpose([0, 2, 1]) + ) - a_bos = a[ov_a + ov_b:, ov_a + ov_b:] - b_bos = b[ov_a + ov_b:, ov_a + ov_b:] + a_bos = a[ov_a + ov_b :, ov_a + ov_b :] + b_bos = b[ov_a + ov_b :, ov_a + ov_b :] return couplings_aa, couplings_bb, a_bos, b_bos def conv_to_aos(self, ra, rb): ra = ra.reshape((-1, self.base.nocc[0], self.base.nvir[0])) rb = rb.reshape((-1, self.base.nocc[1], self.base.nvir[1])) - return einsum("nia,pi,qa->npq", ra, self.base.mo_coeff_occ[0], self.base.mo_coeff_vir[0]), \ - einsum("nia,pi,qa->npq", rb, self.base.mo_coeff_occ[1], self.base.mo_coeff_vir[1]) + return einsum("nia,pi,qa->npq", ra, self.base.mo_coeff_occ[0], self.base.mo_coeff_vir[0]), einsum( + "nia,pi,qa->npq", rb, self.base.mo_coeff_occ[1], self.base.mo_coeff_vir[1] + ) def get_eri_couplings(self, rot): """Obtain eri in a space defined by an arbitrary rotation of the mean-field particle-hole excitations of our @@ -129,7 +131,7 @@ def get_eri_couplings(self, rot): # Convert rots from full-space particle-hole excitations into AO pairs. - rota, rotb = rot[:, :self.ov_mf[0]], rot[:, self.ov_mf[0]:sum(self.ov_mf)] + rota, rotb = rot[:, : self.ov_mf[0]], rot[:, self.ov_mf[0] : sum(self.ov_mf)] if hasattr(self.base.mf, "with_df"): rota, rotb = self.conv_to_aos(rota, rotb) @@ -144,40 +146,56 @@ def get_eri_couplings(self, rot): # This is painful to do for each fragment, but comes from working with 4-index eris. c_act = self.mf.mo_coeff eris_aa, eris_ab, eris_bb = self.base.get_eris_array_uhf((c_act[0], c_act[1])) - eris_aa = eris_aa[:self.base.nocc[0], self.base.nocc[0]:, :self.base.nocc[0], self.base.nocc[0]:].reshape( - (self.ov_mf[0], self.ov_mf[0])) - eris_ab = eris_ab[:self.base.nocc[0], self.base.nocc[0]:, :self.base.nocc[1], self.base.nocc[1]:].reshape( - (self.ov_mf[0], self.ov_mf[1])) - eris_bb = eris_bb[:self.base.nocc[1], self.base.nocc[1]:, :self.base.nocc[1], self.base.nocc[1]:].reshape( - (self.ov_mf[1], self.ov_mf[1])) - - return dot(rota, eris_aa, rota.T) + dot(rota, eris_ab, rotb.T) + \ - dot(rotb, eris_ab.T, rota.T) + dot(rotb, eris_bb, rotb.T) + eris_aa = eris_aa[ + : self.base.nocc[0], self.base.nocc[0] :, : self.base.nocc[0], self.base.nocc[0] : + ].reshape((self.ov_mf[0], self.ov_mf[0])) + eris_ab = eris_ab[ + : self.base.nocc[0], self.base.nocc[0] :, : self.base.nocc[1], self.base.nocc[1] : + ].reshape((self.ov_mf[0], self.ov_mf[1])) + eris_bb = eris_bb[ + : self.base.nocc[1], self.base.nocc[1] :, : self.base.nocc[1], self.base.nocc[1] : + ].reshape((self.ov_mf[1], self.ov_mf[1])) + + return ( + dot(rota, eris_aa, rota.T) + + dot(rota, eris_ab, rotb.T) + + dot(rotb, eris_ab.T, rota.T) + + dot(rotb, eris_bb, rotb.T) + ) def get_rbos_split(self): - r_bos_a = self.r_bos[:, :self.ov_mf[0]] - r_bos_b = self.r_bos[:, self.ov_mf[0]:] + r_bos_a = self.r_bos[:, : self.ov_mf[0]] + r_bos_b = self.r_bos[:, self.ov_mf[0] :] return r_bos_a.reshape((self.nbos, self.base.nocc[0], self.base.nvir[1])), r_bos_b.reshape( - (self.nbos, self.base.nocc[0], self.base.nvir[1])) + (self.nbos, self.base.nocc[0], self.base.nvir[1]) + ) def get_rot_ov_frag(self): """Get rotations between the relevant space for fragment two-point excitations and the cluster active occupied- virtual excitations.""" - occ_frag_rot = self.get_overlap('cluster[occ]|frag') - vir_frag_rot = self.get_overlap('cluster[vir]|frag') + occ_frag_rot = self.get_overlap("cluster[occ]|frag") + vir_frag_rot = self.get_overlap("cluster[vir]|frag") if self.opts.old_sc_condition: # Then get projectors to local quantities in ov-basis. Note this needs to be stacked to apply to each spin # pairing separately. - rot_ov_frag = tuple([np.einsum("ip,ap->pia", x, y).reshape((-1, ov)) for x, y, ov in - zip(occ_frag_rot, vir_frag_rot, self.ov_active)]) + rot_ov_frag = tuple( + [ + np.einsum("ip,ap->pia", x, y).reshape((-1, ov)) + for x, y, ov in zip(occ_frag_rot, vir_frag_rot, self.ov_active) + ] + ) # Get pseudo-inverse to map from frag to loc. Since occupied-virtual excitations aren't spanning this # isn't a simple transpose. proj_to_order = np.eye(rot_ov_frag[0].shape[0]) else: # First, grab rotations from particle-hole excitations to fragment degrees of freedom, ignoring reordering - rot_ov_frag = tuple([np.einsum("ip,aq->pqia", x, y).reshape((-1, ov)) for x, y, ov in - zip(occ_frag_rot, vir_frag_rot, self.ov_active)]) + rot_ov_frag = tuple( + [ + np.einsum("ip,aq->pqia", x, y).reshape((-1, ov)) + for x, y, ov in zip(occ_frag_rot, vir_frag_rot, self.ov_active) + ] + ) # Set up matrix to map down to only a single index ordering. # Note that we current assume the number of alpha and beta orbitals is equal nf = self.n_frag[0] @@ -185,7 +203,7 @@ def get_rot_ov_frag(self): for p in range(nf): for q in range(p + 1): proj_to_order[p, q, p, q] = proj_to_order[q, p, p, q] = 1.0 - proj_to_order = proj_to_order.reshape((nf ** 2, nf, nf)) + proj_to_order = proj_to_order.reshape((nf**2, nf, nf)) # Now restrict to triangular portion of array proj_to_order = pyscf.lib.pack_tril(proj_to_order) # proj_from_order = np.linalg.pinv(proj_to_order) @@ -212,10 +230,19 @@ def get_correlation_kernel_contrib(self, contrib): r_ao_bosa, r_ao_bosb = self.r_ao_bos bos_contrib = [ - (einsum("nz,zpq->npq", repbos_ex[0], r_ao_bosa) + einsum("nz,zpq->nqp", repbos_dex[0], r_ao_bosa), - einsum("nz,zpq->npq", repbos_ex[1], r_ao_bosa) + einsum("nz,zpq->nqp", repbos_dex[1], r_ao_bosa)), - (einsum("nz,zpq->npq", repbos_ex[0], r_ao_bosb) + einsum("nz,zpq->nqp", repbos_dex[0], r_ao_bosb), - einsum("nz,zpq->npq", repbos_ex[1], r_ao_bosb) + einsum("nz,zpq->nqp", repbos_dex[1], r_ao_bosb))] + ( + einsum("nz,zpq->npq", repbos_ex[0], r_ao_bosa) + + einsum("nz,zpq->nqp", repbos_dex[0], r_ao_bosa), + einsum("nz,zpq->npq", repbos_ex[1], r_ao_bosa) + + einsum("nz,zpq->nqp", repbos_dex[1], r_ao_bosa), + ), + ( + einsum("nz,zpq->npq", repbos_ex[0], r_ao_bosb) + + einsum("nz,zpq->nqp", repbos_dex[0], r_ao_bosb), + einsum("nz,zpq->npq", repbos_ex[1], r_ao_bosb) + + einsum("nz,zpq->nqp", repbos_dex[1], r_ao_bosb), + ), + ] res = [tuple([z1 + z2 for z1, z2 in zip(x, y)]) for x, y in zip(res, bos_contrib)] self.prev_xc_contrib = res return res diff --git a/vayesta/ewf/__init__.py b/vayesta/ewf/__init__.py index c913f75a1..c0207b19c 100644 --- a/vayesta/ewf/__init__.py +++ b/vayesta/ewf/__init__.py @@ -5,14 +5,15 @@ import pyscf import pyscf.scf -import logging +import logging -#from .ewf import EWF as REWF +# from .ewf import EWF as REWF from vayesta.ewf.ewf import REWF from vayesta.ewf.uewf import UEWF log = logging.getLogger(__name__) + def EWF(mf, *args, **kwargs): """Determine restricted or unrestricted by inspection of mean-field object""" if isinstance(mf, pyscf.scf.uhf.UHF): diff --git a/vayesta/ewf/amplitudes.py b/vayesta/ewf/amplitudes.py index 54574bbc9..c501fe694 100644 --- a/vayesta/ewf/amplitudes.py +++ b/vayesta/ewf/amplitudes.py @@ -1,4 +1,3 @@ - import numpy as np from vayesta.core.util import NotCalculatedError, dot, einsum @@ -30,7 +29,7 @@ def get_global_t1_rhf(emb, get_lambda=False, mpi_target=None, ao_basis=False, fo cs_vir = np.dot(emb.mo_coeff_vir.T, ovlp) for x in emb.get_fragments(contributes=True, mpi_rank=mpi.rank, sym_parent=None): pwf = x.results.pwf.restore().as_ccsd() - if for_dm2 and x.solver == 'MP2': + if for_dm2 and x.solver == "MP2": continue t1x = pwf.l1 if (get_lambda and not x.opts.t_as_lambda) else pwf.t1 if t1x is None: @@ -46,6 +45,7 @@ def get_global_t1_rhf(emb, get_lambda=False, mpi_target=None, ao_basis=False, fo t1 = dot(emb.mo_coeff_occ, t1, emb.mo_coeff_vir.T) return t1 + def get_global_t2_rhf(emb, get_lambda=False, symmetrize=True, mpi_target=None, ao_basis=False, for_dm2=False): """Get global CCSD T2 amplitudes from fragment calculations. @@ -69,26 +69,29 @@ def get_global_t2_rhf(emb, get_lambda=False, symmetrize=True, mpi_target=None, a # Add fragment WFs in intermediate normalization for x in emb.get_fragments(contributes=True, mpi_rank=mpi.rank): emb.log.debugv("Now adding projected %s-amplitudes of fragment %s", ("L" if get_lambda else "T"), x) - ro = x.get_overlap('mo[occ]|cluster[occ]') - rv = x.get_overlap('mo[vir]|cluster[vir]') + ro = x.get_overlap("mo[occ]|cluster[occ]") + rv = x.get_overlap("mo[vir]|cluster[vir]") pwf = x.results.pwf.restore().as_ccsd() - if for_dm2 and x.solver == 'MP2': + if for_dm2 and x.solver == "MP2": # Lambda=0 for DM2(MP2) if get_lambda: continue - t2x = 2*pwf.t2 + t2x = 2 * pwf.t2 else: t2x = pwf.l2 if (get_lambda and not x.opts.t_as_lambda) else pwf.t2 if t2x is None: raise NotCalculatedError("Fragment %s" % x) - t2 += einsum('ijab,Ii,Jj,Aa,Bb->IJAB', t2x, ro, ro, rv, rv) + t2 += einsum("ijab,Ii,Jj,Aa,Bb->IJAB", t2x, ro, ro, rv, rv) # --- MPI if mpi: t2 = mpi.nreduce(t2, target=mpi_target, logfunc=emb.log.timingv) if ao_basis: - t2 = einsum('Ii,Jj,ijab,Aa,Bb->IJAB', emb.mo_coeff_occ, emb.mo_coeff_occ, t2, emb.mo_coeff_vir, emb.mo_coeff_vir) + t2 = einsum( + "Ii,Jj,ijab,Aa,Bb->IJAB", emb.mo_coeff_occ, emb.mo_coeff_occ, t2, emb.mo_coeff_vir, emb.mo_coeff_vir + ) return t2 + def get_global_t1_uhf(emb, get_lambda=False, mpi_target=None, ao_basis=False): """Get global CCSD T1 from fragment calculations. @@ -111,14 +114,14 @@ def get_global_t1_uhf(emb, get_lambda=False, mpi_target=None, ao_basis=False): # Add fragment WFs in intermediate normalization for x in emb.get_fragments(contributes=True, mpi_rank=mpi.rank): emb.log.debugv("Now adding projected %s-amplitudes of fragment %s", ("L" if get_lambda else "T"), x) - roa, rob = x.get_overlap('mo[occ]|cluster[occ]') - rva, rvb = x.get_overlap('mo[vir]|cluster[vir]') + roa, rob = x.get_overlap("mo[occ]|cluster[occ]") + rva, rvb = x.get_overlap("mo[vir]|cluster[vir]") pwf = x.results.pwf.restore().as_ccsd() t1xa, t1xb = pwf.l1 if (get_lambda and not x.opts.t_as_lambda) else pwf.t1 if t1xa is None: raise NotCalculatedError("Fragment %s" % x) - t1a += einsum('ia,Ii,Aa->IA', t1xa, roa, rva) - t1b += einsum('ia,Ii,Aa->IA', t1xb, rob, rvb) + t1a += einsum("ia,Ii,Aa->IA", t1xa, roa, rva) + t1b += einsum("ia,Ii,Aa->IA", t1xb, rob, rvb) # --- MPI if mpi: t1a, t1b = mpi.nreduce(t1a, t1b, target=mpi_target, logfunc=emb.log.timingv) @@ -127,6 +130,7 @@ def get_global_t1_uhf(emb, get_lambda=False, mpi_target=None, ao_basis=False): t1b = dot(emb.mo_coeff_occ[1], t1b, emb.mo_coeff_vir[1].T) return (t1a, t1b) + def get_global_t2_uhf(emb, get_lambda=False, symmetrize=True, mpi_target=None, ao_basis=False): """Get global CCSD T2 amplitudes from fragment calculations. @@ -150,22 +154,22 @@ def get_global_t2_uhf(emb, get_lambda=False, symmetrize=True, mpi_target=None, a # Add fragment WFs in intermediate normalization for x in emb.get_fragments(contributes=True, mpi_rank=mpi.rank): emb.log.debugv("Now adding projected %s-amplitudes of fragment %s", ("L" if get_lambda else "T"), x) - roa, rob = x.get_overlap('mo[occ]|cluster[occ]') - rva, rvb = x.get_overlap('mo[vir]|cluster[vir]') + roa, rob = x.get_overlap("mo[occ]|cluster[occ]") + rva, rvb = x.get_overlap("mo[vir]|cluster[vir]") pwf = x.results.pwf.restore().as_ccsd() t2xaa, t2xab, t2xbb = pwf.l2 if (get_lambda and not x.opts.t_as_lambda) else pwf.t2 if t2xaa is None: raise NotCalculatedError("Fragment %s" % x) - t2aa += einsum('ijab,Ii,Jj,Aa,Bb->IJAB', t2xaa, roa, roa, rva, rva) - t2ab += einsum('ijab,Ii,Jj,Aa,Bb->IJAB', t2xab, roa, rob, rva, rvb) - t2bb += einsum('ijab,Ii,Jj,Aa,Bb->IJAB', t2xbb, rob, rob, rvb, rvb) + t2aa += einsum("ijab,Ii,Jj,Aa,Bb->IJAB", t2xaa, roa, roa, rva, rva) + t2ab += einsum("ijab,Ii,Jj,Aa,Bb->IJAB", t2xab, roa, rob, rva, rvb) + t2bb += einsum("ijab,Ii,Jj,Aa,Bb->IJAB", t2xbb, rob, rob, rvb, rvb) # --- MPI if mpi: t2aa, t2ab, t2bb = mpi.nreduce(t2aa, t2ab, t2bb, target=mpi_target, logfunc=emb.log.timingv) if ao_basis: coa, cob = emb.mo_coeff_occ cva, cvb = emb.mo_coeff_vir - t2aa = einsum('Ii,Jj,ijab,Aa,Bb->IJAB', coa, coa, t2aa, cva, cva) - t2ab = einsum('Ii,Jj,ijab,Aa,Bb->IJAB', coa, cob, t2ab, cva, cvb) - t2bb = einsum('Ii,Jj,ijab,Aa,Bb->IJAB', cob, cob, t2bb, cvb, cvb) + t2aa = einsum("Ii,Jj,ijab,Aa,Bb->IJAB", coa, coa, t2aa, cva, cva) + t2ab = einsum("Ii,Jj,ijab,Aa,Bb->IJAB", coa, cob, t2ab, cva, cvb) + t2bb = einsum("Ii,Jj,ijab,Aa,Bb->IJAB", cob, cob, t2bb, cvb, cvb) return (t2aa, t2ab, t2bb) diff --git a/vayesta/ewf/ewf.py b/vayesta/ewf/ewf.py index 17cd8e6d6..d3ef71252 100644 --- a/vayesta/ewf/ewf.py +++ b/vayesta/ewf/ewf.py @@ -1,9 +1,21 @@ # --- Standard import dataclasses + # --- External import numpy as np -from vayesta.core.util import (NotCalculatedError, break_into_lines, cache, deprecated, dot, einsum, energy_string, - log_method, log_time, time_string, timer) +from vayesta.core.util import ( + NotCalculatedError, + break_into_lines, + cache, + deprecated, + dot, + einsum, + energy_string, + log_method, + log_time, + time_string, + timer, +) from vayesta.core.qemb import Embedding from vayesta.core.fragmentation import SAO_Fragmentation from vayesta.core.fragmentation import IAOPAO_Fragmentation @@ -22,25 +34,25 @@ @dataclasses.dataclass class Options(Embedding.Options): """Options for EWF calculations.""" + # --- Fragment settings - iao_minao : str = 'auto' # Minimal basis for IAOs + iao_minao: str = "auto" # Minimal basis for IAOs # --- Bath settings - bath_options: dict = Embedding.Options.change_dict_defaults('bath_options', - bathtype='mp2', threshold=1e-8) - #ewdmet_max_order: int = 1 + bath_options: dict = Embedding.Options.change_dict_defaults("bath_options", bathtype="mp2", threshold=1e-8) + # ewdmet_max_order: int = 1 # If multiple bno thresholds are to be calculated, we can project integrals and amplitudes from a previous larger cluster: - project_eris: bool = False # Project ERIs from a pervious larger cluster (corresponding to larger eta), can result in a loss of accuracy especially for large basis sets! - project_init_guess: bool = True # Project converted T1,T2 amplitudes from a previous larger cluster - energy_functional: str = 'wf' + project_eris: bool = False # Project ERIs from a pervious larger cluster (corresponding to larger eta), can result in a loss of accuracy especially for large basis sets! + project_init_guess: bool = True # Project converted T1,T2 amplitudes from a previous larger cluster + energy_functional: str = "wf" # Calculation modes calc_e_wf_corr: bool = True calc_e_dm_corr: bool = False # --- Solver settings - t_as_lambda: bool = None # If True, use T-amplitudes inplace of Lambda-amplitudes - store_wf_type: str = None # If set, fragment WFs will be converted to the respective type, before storing them + t_as_lambda: bool = None # If True, use T-amplitudes inplace of Lambda-amplitudes + store_wf_type: str = None # If set, fragment WFs will be converted to the respective type, before storing them # Counterpoise correction of BSSE bsse_correction: bool = True - bsse_rmax: float = 5.0 # In Angstrom + bsse_rmax: float = 5.0 # In Angstrom nelectron_target: int = None # --- Couple embedding problems (currently only CCSD) sc_mode: int = 0 @@ -50,11 +62,10 @@ class Options(Embedding.Options): class EWF(Embedding): - Fragment = Fragment Options = Options - def __init__(self, mf, solver='CCSD', log=None, **kwargs): + def __init__(self, mf, solver="CCSD", log=None, **kwargs): t0 = timer() super().__init__(mf, solver=solver, log=log, **kwargs) @@ -62,12 +73,12 @@ def __init__(self, mf, solver='CCSD', log=None, **kwargs): with self.log.indent(): # Options self.log.info("Parameters of %s:", self.__class__.__name__) - self.log.info(break_into_lines(str(self.opts), newline='\n ')) - self.log.info("Time for %s setup: %s", self.__class__.__name__, time_string(timer()-t0)) + self.log.info(break_into_lines(str(self.opts), newline="\n ")) + self.log.info("Time for %s setup: %s", self.__class__.__name__, time_string(timer() - t0)) def __repr__(self): - keys = ['mf', 'solver'] - fmt = ('%s(' + len(keys)*'%s: %r, ')[:-2] + ')' + keys = ["mf", "solver"] + fmt = ("%s(" + len(keys) * "%s: %r, ")[:-2] + ")" values = [self.__dict__[k] for k in keys] return fmt % (self.__class__.__name__, *[x for y in zip(keys, values) for x in y]) @@ -84,7 +95,7 @@ def fragmentation(self, *args, **kwargs): def tailor_all_fragments(self): for x in self.fragments: for y in self.fragments: - if (x == y): + if x == y: continue x.add_tailor_fragment(y) @@ -118,7 +129,7 @@ def kernel(self): x.reset() msg = "Making bath and clusters for %s%s" % (x, (" on MPI process %d" % mpi.rank) if mpi else "") self.log.info(msg) - self.log.info(len(msg)*"-") + self.log.info(len(msg) * "-") with self.log.indent(): if x._dmet_bath is None: # Make own bath: @@ -127,7 +138,7 @@ def kernel(self): # Copy bath (DMET, occupied, virtual) from other fragment: else: bath_parent = fragdict[x.flags.bath_parent_fragment_id] - for attr in ('_dmet_bath', '_bath_factory_occ', '_bath_factory_vir'): + for attr in ("_dmet_bath", "_bath_factory_occ", "_bath_factory_vir"): setattr(x, attr, getattr(bath_parent, attr)) if x._cluster is None: x.make_cluster() @@ -153,14 +164,14 @@ def kernel(self): for x in frags: msg = "Solving %s%s" % (x, (" on MPI process %d" % mpi.rank) if mpi else "") self.log.info(msg) - self.log.info(len(msg)*"-") + self.log.info(len(msg) * "-") with self.log.indent(): x.kernel() if mpi: mpi.world.Barrier() - if self.solver.lower() == 'dump': - self.log.output("Clusters dumped to file '%s'", self.opts.solver_options['dumpfile']) + if self.solver.lower() == "dump": + self.log.output("Clusters dumped to file '%s'", self.opts.solver_options["dumpfile"]) return # --- Check convergence of fragments @@ -171,16 +182,16 @@ def kernel(self): # --- Evaluate correlation energy and log information self.e_corr = self.get_e_corr() - self.log.output('E(MF)= %s', energy_string(self.e_mf)) - self.log.output('E(corr)= %s', energy_string(self.e_corr)) - self.log.output('E(tot)= %s', energy_string(self.e_tot)) - self.log.info("Total wall time: %s", time_string(timer()-t_start)) + self.log.output("E(MF)= %s", energy_string(self.e_mf)) + self.log.output("E(corr)= %s", energy_string(self.e_corr)) + self.log.output("E(tot)= %s", energy_string(self.e_tot)) + self.log.info("Total wall time: %s", time_string(timer() - t_start)) return self.e_tot def _all_converged(self, fragments): conv = True for fx in fragments: - conv = (conv and fx.results.converged) + conv = conv and fx.results.converged if mpi: conv = mpi.world.allreduce(conv, op=mpi.MPI.LAND) return conv @@ -196,6 +207,7 @@ def _all_converged(self, fragments): def get_global_l1(self, *args, t_as_lambda=None, **kwargs): get_lambda = True if not t_as_lambda else False return self.get_global_t1(*args, get_lambda=get_lambda, **kwargs) + def get_global_l2(self, *args, t_as_lambda=None, **kwargs): get_lambda = True if not t_as_lambda else False return self.get_global_t2(*args, get_lambda=get_lambda, **kwargs) @@ -205,16 +217,16 @@ def t1_diagnostic(self, warntol=0.02): for fx in self.get_fragments(active=True, mpi_rank=mpi.rank): wfx = fx.results.wf.as_ccsd() t1 = wfx.t1 - nelec = 2*t1.shape[0] + nelec = 2 * t1.shape[0] t1diag = np.linalg.norm(t1) / np.sqrt(nelec) if t1diag >= warntol: - self.log.warning("T1 diagnostic for %-20s %.5f", str(f)+':', t1diag) + self.log.warning("T1 diagnostic for %-20s %.5f", str(f) + ":", t1diag) else: - self.log.info("T1 diagnostic for %-20s %.5f", str(f)+':', t1diag) + self.log.info("T1 diagnostic for %-20s %.5f", str(f) + ":", t1diag) # Global t1 = self.get_global_t1(mpi_target=0) if mpi.is_master: - nelec = 2*t1.shape[0] + nelec = 2 * t1.shape[0] t1diag = np.linalg.norm(t1) / np.sqrt(nelec) if t1diag >= warntol: self.log.warning("Global T1 diagnostic: %.5f", t1diag) @@ -229,17 +241,17 @@ def t1_diagnostic(self, warntol=0.02): def make_rdm1(self, *args, **kwargs): if "cc" in self.solver.lower(): return self._make_rdm1_ccsd_global_wf(*args, **kwargs) - if self.solver.lower() == 'mp2': + if self.solver.lower() == "mp2": return self._make_rdm1_mp2_global_wf(*args, **kwargs) - if self.solver.lower() == 'fci': + if self.solver.lower() == "fci": return self.make_rdm1_demo(*args, **kwargs) raise NotImplementedError("make_rdm1 for solver '%s'" % self.solver) def make_rdm2(self, *args, **kwargs): - if self.solver.lower() == 'ccsd': + if self.solver.lower() == "ccsd": return self._make_rdm2_ccsd_proj_lambda(*args, **kwargs) - #return self._make_rdm2_ccsd(*args, **kwargs) - if self.solver.lower() == 'mp2': + # return self._make_rdm2_ccsd(*args, **kwargs) + if self.solver.lower() == "mp2": return self._make_rdm2_ccsd_proj_lambda(*args, t_as_lambda=True, **kwargs) raise NotImplementedError("make_rdm2 for solver '%s'" % self.solver) @@ -288,16 +300,16 @@ def _make_rdm2_ccsd_proj_lambda(self, *args, **kwargs): # Correlation def get_e_corr(self, functional=None, **kwargs): - functional = (functional or self.opts.energy_functional) + functional = functional or self.opts.energy_functional - if functional == 'projected': + if functional == "projected": self.log.warning("functional='projected' is deprecated; use functional='wf' instead.") - functional = 'wf' - if functional == 'wf': + functional = "wf" + if functional == "wf": return self.get_wf_corr_energy(**kwargs) - if functional == 'dm-t2only': + if functional == "dm-t2only": return self.get_dm_corr_energy(t_as_lambda=True, **kwargs) - if functional == 'dm': + if functional == "dm": return self.get_dm_corr_energy(**kwargs) raise ValueError("Unknown energy functional: '%s'" % functional) @@ -310,63 +322,67 @@ def get_wf_corr_energy(self): ex = x.results.e_corr else: wf = x.results.wf.as_cisd(c0=1.0) - px = x.get_overlap('frag|cluster-occ') + px = x.get_overlap("frag|cluster-occ") wf = wf.project(px) es, ed, ex = x.get_fragment_energy(wf.c1, wf.c2) - self.log.debug("%20s: E(S)= %s E(D)= %s E(tot)= %s", x, energy_string(es), energy_string(ed), energy_string(ex)) + self.log.debug( + "%20s: E(S)= %s E(D)= %s E(tot)= %s", x, energy_string(es), energy_string(ed), energy_string(ex) + ) e_corr += x.symmetry_factor * ex - return e_corr/self.ncells + return e_corr / self.ncells - def get_dm_corr_energy(self, dm1='global-wf', dm2='projected-lambda', t_as_lambda=None, with_exxdiv=None): + def get_dm_corr_energy(self, dm1="global-wf", dm2="projected-lambda", t_as_lambda=None, with_exxdiv=None): e1 = self.get_dm_corr_energy_e1(dm1=dm1, t_as_lambda=None, with_exxdiv=None) e2 = self.get_dm_corr_energy_e2(dm2=dm2, t_as_lambda=t_as_lambda) - e_corr = (e1 + e2) + e_corr = e1 + e2 self.log.debug("Ecorr(1)= %s Ecorr(2)= %s Ecorr= %s", *map(energy_string, (e1, e2, e_corr))) return e_corr - def get_dm_corr_energy_e1(self, dm1='global-wf', t_as_lambda=None, with_exxdiv=None): + def get_dm_corr_energy_e1(self, dm1="global-wf", t_as_lambda=None, with_exxdiv=None): # Correlation energy due to changes in 1-DM and non-cumulant 2-DM: - if dm1 == 'global-wf': + if dm1 == "global-wf": dm1 = self._make_rdm1_ccsd_global_wf(with_mf=False, t_as_lambda=t_as_lambda, ao_basis=True) - elif dm1 == '2p1l': + elif dm1 == "2p1l": dm1 = self._make_rdm1_ccsd(with_mf=False, t_as_lambda=t_as_lambda, ao_basis=True) - elif dm1 == '1p1l': + elif dm1 == "1p1l": dm1 = self._make_rdm1_ccsd_1p1l(with_mf=False, t_as_lambda=t_as_lambda, ao_basis=True) else: raise ValueError if with_exxdiv is None: if self.has_exxdiv: - with_exxdiv = np.all([x.solver == 'MP2' for x in self.fragments]) - any_mp2 = np.any([x.solver == 'MP2' for x in self.fragments]) - any_not_mp2 = np.any([x.solver != 'MP2' for x in self.fragments]) - if (any_mp2 and any_not_mp2): + with_exxdiv = np.all([x.solver == "MP2" for x in self.fragments]) + any_mp2 = np.any([x.solver == "MP2" for x in self.fragments]) + any_not_mp2 = np.any([x.solver != "MP2" for x in self.fragments]) + if any_mp2 and any_not_mp2: self.log.warning("Both MP2 and not MP2 solvers detected - unclear usage of exxdiv!") else: with_exxdiv = False fock = self.get_fock_for_energy(with_exxdiv=with_exxdiv) - e1 = np.sum(fock*dm1) - return e1/self.ncells + e1 = np.sum(fock * dm1) + return e1 / self.ncells @mpi.with_allreduce() - def get_dm_corr_energy_e2(self, dm2='projected-lambda', t_as_lambda=None): + def get_dm_corr_energy_e2(self, dm2="projected-lambda", t_as_lambda=None): """Correlation energy due to cumulant""" if t_as_lambda is None: t_as_lambda = self.opts.t_as_lambda - if dm2 == 'global-wf': + if dm2 == "global-wf": dm2 = self._make_rdm2_ccsd_global_wf(t_as_lambda=t_as_lambda, with_dm1=False) # TODO: AO basis, late DF contraction - if self.spinsym == 'restricted': + if self.spinsym == "restricted": g = self.get_eris_array(self.mo_coeff) - e2 = einsum('pqrs,pqrs', g, dm2)/2 + e2 = einsum("pqrs,pqrs", g, dm2) / 2 else: dm2aa, dm2ab, dm2bb = dm2 gaa, gab, gbb = self.get_eris_array_uhf(self.mo_coeff) - e2 = (einsum('pqrs,pqrs', gaa, dm2aa)/2 - + einsum('pqrs,pqrs', gbb, dm2bb)/2 - + einsum('pqrs,pqrs', gab, dm2ab)) - elif dm2 == 'projected-lambda': + e2 = ( + einsum("pqrs,pqrs", gaa, dm2aa) / 2 + + einsum("pqrs,pqrs", gbb, dm2bb) / 2 + + einsum("pqrs,pqrs", gab, dm2ab) + ) + elif dm2 == "projected-lambda": e2 = 0.0 for x in self.get_fragments(contributes=True, sym_parent=None, mpi_rank=mpi.rank): ex = x.results.e_corr_dm2cumulant @@ -375,7 +391,7 @@ def get_dm_corr_energy_e2(self, dm2='projected-lambda', t_as_lambda=None): e2 += x.symmetry_factor * x.sym_factor * ex else: raise ValueError("Unknown value for dm2: '%s'" % dm2) - return e2/self.ncells + return e2 / self.ncells def get_ccsd_corr_energy(self, full_wf=False): """Get projected correlation energy from partitioned CCSD WF. @@ -389,41 +405,39 @@ def get_ccsd_corr_energy(self, full_wf=False): # E(singles) fock = self.get_fock_for_energy(with_exxdiv=False) - fov = dot(self.mo_coeff_occ.T, fock, self.mo_coeff_vir) - e_singles = 2*np.sum(fov*t1) + fov = dot(self.mo_coeff_occ.T, fock, self.mo_coeff_vir) + e_singles = 2 * np.sum(fov * t1) # E(doubles) if full_wf: - c2 = (self.get_global_t2() + einsum('ia,jb->ijab', t1, t1)) + c2 = self.get_global_t2() + einsum("ia,jb->ijab", t1, t1) mos = (self.mo_coeff_occ, self.mo_coeff_vir, self.mo_coeff_vir, self.mo_coeff_occ) eris = self.get_eris_array(mos) - e_doubles = (2*einsum('ijab,iabj', c2, eris) - - einsum('ijab,ibaj', c2, eris)) + e_doubles = 2 * einsum("ijab,iabj", c2, eris) - einsum("ijab,ibaj", c2, eris) else: e_doubles = 0.0 for x in self.get_fragments(contributes=True, sym_parent=None, mpi_rank=mpi.rank): pwf = x.results.pwf.as_ccsd() - ro = x.get_overlap('mo-occ|cluster-occ') - rv = x.get_overlap('mo-vir|cluster-vir') + ro = x.get_overlap("mo-occ|cluster-occ") + rv = x.get_overlap("mo-vir|cluster-vir") - t1x = dot(ro.T, t1, rv) # N(frag) * N^2 - c2x = pwf.t2 + einsum('ia,jb->ijab', pwf.t1, t1x) + t1x = dot(ro.T, t1, rv) # N(frag) * N^2 + c2x = pwf.t2 + einsum("ia,jb->ijab", pwf.t1, t1x) noccx = x.cluster.nocc_active nvirx = x.cluster.nvir_active eris = x.hamil.get_eris_bare("ovvo") - px = x.get_overlap('frag|cluster-occ') - eris = einsum('xi,iabj->xabj', px, eris) + px = x.get_overlap("frag|cluster-occ") + eris = einsum("xi,iabj->xabj", px, eris) wx = x.symmetry_factor * x.sym_factor - e_doubles += wx*(2*einsum('ijab,iabj', c2x, eris) - - einsum('ijab,ibaj', c2x, eris)) + e_doubles += wx * (2 * einsum("ijab,iabj", c2x, eris) - einsum("ijab,ibaj", c2x, eris)) if mpi: e_doubles = mpi.world.allreduce(e_doubles) - self.log.timing("Time for E(CCSD)= %s", time_string(timer()-t0)) - e_corr = (e_singles + e_doubles) + self.log.timing("Time for E(CCSD)= %s", time_string(timer() - t0)) + e_corr = e_singles + e_doubles return e_corr / self.ncells # Total energy @@ -479,13 +493,13 @@ def get_fbc_energy(self, occupied=True, virtual=True): for fx in self.get_fragments(contributes=True, sym_parent=None, flags=dict(is_envelop=True), mpi_rank=mpi.rank): ex = 0 if occupied: - get_fbc = getattr(fx._bath_factory_occ, 'get_finite_bath_correction', False) + get_fbc = getattr(fx._bath_factory_occ, "get_finite_bath_correction", False) if get_fbc: ex += get_fbc(fx.cluster.c_active_occ, fx.cluster.c_frozen_occ) else: self.log.warning("%s does not have occupied BNOs - skipping fragment for FBC energy.", fx) if virtual: - get_fbc = getattr(fx._bath_factory_vir, 'get_finite_bath_correction', False) + get_fbc = getattr(fx._bath_factory_vir, "get_finite_bath_correction", False) if get_fbc: ex += get_fbc(fx.cluster.c_active_vir, fx.cluster.c_frozen_vir) else: @@ -549,7 +563,7 @@ def _get_dm_corr_energy_old(self, global_dm1=True, global_dm2=False): # Calculate global 2RDM and contract with ERIs rdm2 = self._make_rdm2_ccsd_global_wf(t_as_lambda=t_as_lambda, with_dm1=False) eris = self.get_eris_array(self.mo_coeff) - e2 = einsum('pqrs,pqrs', eris, rdm2)/2 + e2 = einsum("pqrs,pqrs", eris, rdm2) / 2 else: # Fragment Local 2DM cumulant contribution e2 = self.get_dm_corr_energy_e2(t_as_lambda=t_as_lambda) * self.ncells @@ -559,16 +573,17 @@ def _get_dm_corr_energy_old(self, global_dm1=True, global_dm2=False): # --- Debugging def _debug_get_wf(self, kind): - if kind == 'random': + if kind == "random": return - if kind == 'exact': - if self.solver == 'CCSD': + if kind == "exact": + if self.solver == "CCSD": import pyscf import pyscf.cc from vayesta.core.types import WaveFunction + cc = pyscf.cc.CCSD(self.mf) cc.kernel() - if self.opts.solver_options['solve_lambda']: + if self.opts.solver_options["solve_lambda"]: cc.solve_lambda() wf = WaveFunction.from_pyscf(cc) else: diff --git a/vayesta/ewf/fragment.py b/vayesta/ewf/fragment.py index a0cf8d123..05ad8e17b 100644 --- a/vayesta/ewf/fragment.py +++ b/vayesta/ewf/fragment.py @@ -23,24 +23,26 @@ # Get MPI rank of fragment -get_fragment_mpi_rank = lambda *args : args[0].mpi_rank +get_fragment_mpi_rank = lambda *args: args[0].mpi_rank @dataclasses.dataclass class Options(BaseFragment.Options): # Inherited from Embedding # ------------------------ - t_as_lambda: bool = None # If True, use T-amplitudes inplace of Lambda-amplitudes + t_as_lambda: bool = None # If True, use T-amplitudes inplace of Lambda-amplitudes bsse_correction: bool = None bsse_rmax: float = None sc_mode: int = None - nelectron_target: float = None # If set, adjust bath chemical potential until electron number in fragment equals nelectron_target + nelectron_target: float = ( + None # If set, adjust bath chemical potential until electron number in fragment equals nelectron_target + ) nelectron_target_atol: float = 1e-6 nelectron_target_rtol: float = 1e-6 # Calculation modes calc_e_wf_corr: bool = None calc_e_dm_corr: bool = None - store_wf_type: str = None # If set, fragment WFs will be converted to the respective type, before storing them + store_wf_type: str = None # If set, fragment WFs will be converted to the respective type, before storing them # Fragment specific # ----------------- wf_factor: Optional[int] = None @@ -54,8 +56,8 @@ class Options(BaseFragment.Options): # --- Couple embedding problems (currently only CCSD and MPI) # coupled_iterations: bool = None # Now accessible through solver=coupledCCSD setting. -class Fragment(BaseFragment): +class Fragment(BaseFragment): Options = Options @dataclasses.dataclass @@ -65,7 +67,6 @@ class Flags(BaseFragment.Flags): # Whether to perform additional checks on external corrections test_extcorr: bool = False - @dataclasses.dataclass class Results(BaseFragment.Results): e_corr_dm2cumulant: float = None @@ -85,7 +86,6 @@ def dm2(self): return self.wf.make_rdm2() def __init__(self, *args, **kwargs): - """ Parameters ---------- @@ -105,10 +105,10 @@ def _reset(self, *args, **kwargs): # Need to unset these so can be regenerated each iteration. self.opts.c_cas_occ = self.opts.c_cas_vir = None - def set_cas(self, iaos=None, c_occ=None, c_vir=None, minao='auto', dmet_threshold=None): + def set_cas(self, iaos=None, c_occ=None, c_vir=None, minao="auto", dmet_threshold=None): """Set complete active space for tailored CCSD and active-space CC methods.""" if dmet_threshold is None: - dmet_threshold = 2*self.opts.bath_options['dmet_threshold'] + dmet_threshold = 2 * self.opts.bath_options["dmet_threshold"] if iaos is not None: # Create new IAO fragmentation frag = IAO_Fragmentation(self.base, minao=minao) @@ -119,8 +119,8 @@ def set_cas(self, iaos=None, c_occ=None, c_vir=None, minao='auto', dmet_threshol c_env = frag.get_env_coeff(indices) bath = DMET_Bath(self, dmet_threshold=dmet_threshold) c_dmet = bath.make_dmet_bath(c_env)[0] - tol = self.opts.bath_options['occupation_tolerance'] - c_iao_occ, c_iao_vir = self.diagonalize_cluster_dm(c_iao, c_dmet, tol=2*tol) + tol = self.opts.bath_options["occupation_tolerance"] + c_iao_occ, c_iao_vir = self.diagonalize_cluster_dm(c_iao, c_dmet, tol=2 * tol) else: c_iao_occ = c_iao_vir = None @@ -130,7 +130,9 @@ def set_cas(self, iaos=None, c_occ=None, c_vir=None, minao='auto', dmet_threshol self.opts.c_cas_vir = c_cas_vir return c_cas_occ, c_cas_vir - def add_external_corrections(self, fragments, correction_type='tailor', projectors=1, test_extcorr=False, low_level_coul=True): + def add_external_corrections( + self, fragments, correction_type="tailor", projectors=1, test_extcorr=False, low_level_coul=True + ): """Add tailoring or external correction from other fragment solutions to CCSD solver. Parameters @@ -156,20 +158,23 @@ def add_external_corrections(self, fragments, correction_type='tailor', projecto minor differences. Default: True """ - if correction_type not in ('tailor', 'delta-tailor', 'external'): + if correction_type not in ("tailor", "delta-tailor", "external"): raise ValueError if self.solver == "CCSD": # Automatically update this cluster to use external correction solver. self.solver = "extCCSD" self.check_solver(self.solver) - if self.solver != 'extCCSD': + if self.solver != "extCCSD": raise RuntimeError - if (not low_level_coul) and correction_type != 'external': - raise ValueError("low_level_coul optional argument only meaningful with 'external' correction of fragments.") - if np.any([(getattr_recursive(f, 'results.wf', None) is None and not f.opts.auxiliary) for f in fragments]): - raise ValueError("Fragments for external correction need to be already solved or defined as auxiliary fragments.") - self.flags.external_corrections.extend( - [(f.id, correction_type, projectors, low_level_coul) for f in fragments]) + if (not low_level_coul) and correction_type != "external": + raise ValueError( + "low_level_coul optional argument only meaningful with 'external' correction of fragments." + ) + if np.any([(getattr_recursive(f, "results.wf", None) is None and not f.opts.auxiliary) for f in fragments]): + raise ValueError( + "Fragments for external correction need to be already solved or defined as auxiliary fragments." + ) + self.flags.external_corrections.extend([(f.id, correction_type, projectors, low_level_coul) for f in fragments]) self.flags.test_extcorr = test_extcorr def clear_external_corrections(self): @@ -182,14 +187,13 @@ def get_init_guess(self, init_guess, solver, cluster): return {} def kernel(self, solver=None, init_guess=None): - solver = solver or self.solver self.check_solver(solver) if self.cluster is None: raise RuntimeError cluster = self.cluster - if solver == 'HF': + if solver == "HF": return None init_guess = self.get_init_guess(init_guess, solver, cluster) @@ -201,11 +205,15 @@ def kernel(self, solver=None, init_guess=None): # --- Chemical potential cpt_frag = self.base.opts.global_frag_chempot if self.opts.nelectron_target is not None: - cluster_solver.optimize_cpt(self.opts.nelectron_target, c_frag=self.c_proj, atol=self.opts.nelectron_target_atol, - rtol=self.opts.nelectron_target_rtol) + cluster_solver.optimize_cpt( + self.opts.nelectron_target, + c_frag=self.c_proj, + atol=self.opts.nelectron_target_atol, + rtol=self.opts.nelectron_target_rtol, + ) elif cpt_frag: # Add chemical potential to fragment space - r = self.get_overlap('cluster|frag') + r = self.get_overlap("cluster|frag") if self.base.is_rhf: p_frag = np.dot(r, r.T) cluster_solver.v_ext = cpt_frag * p_frag @@ -215,7 +223,7 @@ def kernel(self, solver=None, init_guess=None): # --- Coupled fragments. # TODO rework this functionality to combine with external corrections/tailoring. - if solver == 'coupledCCSD': + if solver == "coupledCCSD": if not mpi: raise RuntimeError("coupled_iterations requires MPI.") if len(self.base.fragments) != len(mpi): @@ -228,12 +236,12 @@ def kernel(self, solver=None, init_guess=None): cluster_solver.kernel() # Special debug "solver" else: - if self.base.opts._debug_wf == 'random': + if self.base.opts._debug_wf == "random": cluster_solver._debug_random_wf() else: cluster_solver._debug_exact_wf(self.base._debug_wf) - if solver.lower() == 'dump': + if solver.lower() == "dump": return wf = cluster_solver.wf @@ -242,7 +250,7 @@ def kernel(self, solver=None, init_guess=None): wf.multiply(self.opts.wf_factor) # Convert WF to different type [optional] if self.opts.store_wf_type is not None: - wf = getattr(wf, 'as_%s' % self.opts.store_wf_type.lower())() + wf = getattr(wf, "as_%s" % self.opts.store_wf_type.lower())() # ---Make T-projected WF pwf = wf # Projection of FCI wave function is not implemented - convert to CISD @@ -251,16 +259,23 @@ def kernel(self, solver=None, init_guess=None): # Projection of CCSDTQ wave function is not implemented - convert to CCSD elif isinstance(wf, (RCCSDTQ_WaveFunction, UCCSDTQ_WaveFunction)): pwf = wf.as_ccsd() - proj = self.get_overlap('proj|cluster-occ') + proj = self.get_overlap("proj|cluster-occ") pwf = pwf.project(proj, inplace=False) # Moments - + moms = cluster_solver.hole_moments, cluster_solver.particle_moments - + # --- Add to results data class - self._results = results = self.Results(fid=self.id, n_active=cluster.norb_active, - converged=cluster_solver.converged, wf=wf, pwf=pwf, moms=moms, e_corr_rpa=e_corr_rpa) + self._results = results = self.Results( + fid=self.id, + n_active=cluster.norb_active, + converged=cluster_solver.converged, + wf=wf, + pwf=pwf, + moms=moms, + e_corr_rpa=e_corr_rpa, + ) self.hamil = cluster_solver.hamil @@ -269,8 +284,9 @@ def kernel(self, solver=None, init_guess=None): ci = wf.as_cisd(c0=1.0) ci = ci.project(proj) es, ed, results.e_corr = self.get_fragment_energy(ci.c1, ci.c2, hamil=self.hamil) - self.log.debug("E(S)= %s E(D)= %s E(tot)= %s", energy_string(es), energy_string(ed), - energy_string(results.e_corr)) + self.log.debug( + "E(S)= %s E(D)= %s E(tot)= %s", energy_string(es), energy_string(ed), energy_string(results.e_corr) + ) if self.opts.calc_e_dm_corr: results.e_corr_dm2cumulant = self.make_fragment_dm2cumulant_energy(hamil=self.hamil) return results @@ -281,14 +297,17 @@ def get_solver_options(self, solver): # (conv_tol, max_cycle, solve_lambda,...) solver_opts = {key: val for (key, val) in self.opts.solver_options.items() if val is not None} pass_through = [] - if 'CCSD' in solver.upper(): - pass_through += ['sc_mode', 'dm_with_frozen'] + if "CCSD" in solver.upper(): + pass_through += ["sc_mode", "dm_with_frozen"] for attr in pass_through: self.log.debugv("Passing fragment option %s to solver.", attr) solver_opts[attr] = getattr(self.opts, attr) - has_actspace = ((solver == "TCCSD") or ("CCSDt'" in solver) or - (solver.upper() == "EBCC" and self.opts.solver_options['ansatz'] == "CCSDt'")) + has_actspace = ( + (solver == "TCCSD") + or ("CCSDt'" in solver) + or (solver.upper() == "EBCC" and self.opts.solver_options["ansatz"] == "CCSDt'") + ) if has_actspace: # Set CAS orbitals if self.opts.c_cas_occ is None: @@ -297,14 +316,14 @@ def get_solver_options(self, solver): if self.opts.c_cas_vir is None: self.log.warning("Virtual CAS orbitals not set. Setting to virtual DMET cluster orbitals.") self.opts.c_cas_vir = self._dmet_bath.c_cluster_vir - solver_opts['c_cas_occ'] = self.opts.c_cas_occ - solver_opts['c_cas_vir'] = self.opts.c_cas_vir + solver_opts["c_cas_occ"] = self.opts.c_cas_occ + solver_opts["c_cas_vir"] = self.opts.c_cas_vir if solver == "TCCSD": - solver_opts['tcc_fci_opts'] = self.opts.tcc_fci_opts - elif solver.upper() == 'DUMP': - solver_opts['filename'] = self.opts.solver_options['dumpfile'] - solver_opts['external_corrections'] = self.flags.external_corrections - solver_opts['test_extcorr'] = self.flags.test_extcorr + solver_opts["tcc_fci_opts"] = self.opts.tcc_fci_opts + elif solver.upper() == "DUMP": + solver_opts["filename"] = self.opts.solver_options["dumpfile"] + solver_opts["external_corrections"] = self.flags.external_corrections + solver_opts["test_extcorr"] = self.flags.test_extcorr return solver_opts # --- Expectation values @@ -312,7 +331,7 @@ def get_solver_options(self, solver): # --- Energies - def get_fragment_energy(self, c1, c2, hamil=None, fock=None, c2ba_order='ba', axis1='fragment'): + def get_fragment_energy(self, c1, c2, hamil=None, fock=None, c2ba_order="ba", axis1="fragment"): """Calculate fragment correlation energy contribution from projected C1, C2. Parameters @@ -336,18 +355,18 @@ def get_fragment_energy(self, c1, c2, hamil=None, fock=None, c2ba_order='ba', ax e_corr : float Total fragment correlation energy contribution. """ - if axis1 == 'fragment': - px = self.get_overlap('proj|cluster-occ') + if axis1 == "fragment": + px = self.get_overlap("proj|cluster-occ") # --- Singles energy (zero for HF-reference) if c1 is not None: if fock is None: fock = self.base.get_fock_for_energy() fov = dot(self.cluster.c_active_occ.T, fock, self.cluster.c_active_vir) - if axis1 == 'fragment': - e_singles = 2*einsum('ia,xi,xa->', fov, px, c1) + if axis1 == "fragment": + e_singles = 2 * einsum("ia,xi,xa->", fov, px, c1) else: - e_singles = 2*np.sum(fov*c1) + e_singles = 2 * np.sum(fov * c1) else: e_singles = 0 # --- Doubles energy @@ -356,19 +375,16 @@ def get_fragment_energy(self, c1, c2, hamil=None, fock=None, c2ba_order='ba', ax # This automatically either slices a stored ERI tensor or calculates it on the fly. g_ovvo = hamil.get_eris_bare(block="ovvo") - if axis1 == 'fragment': - e_doubles = (2*einsum('xi,xjab,iabj', px, c2, g_ovvo) - - einsum('xi,xjab,ibaj', px, c2, g_ovvo)) + if axis1 == "fragment": + e_doubles = 2 * einsum("xi,xjab,iabj", px, c2, g_ovvo) - einsum("xi,xjab,ibaj", px, c2, g_ovvo) else: - e_doubles = (2*einsum('ijab,iabj', c2, g_ovvo) - - einsum('ijab,ibaj', c2, g_ovvo)) + e_doubles = 2 * einsum("ijab,iabj", c2, g_ovvo) - einsum("ijab,ibaj", c2, g_ovvo) - e_singles = (self.sym_factor * e_singles) - e_doubles = (self.sym_factor * e_doubles) - e_corr = (e_singles + e_doubles) + e_singles = self.sym_factor * e_singles + e_doubles = self.sym_factor * e_doubles + e_corr = e_singles + e_doubles return e_singles, e_doubles, e_corr - # --- Density-matrices def _ccsd_amplitudes_for_dm(self, t_as_lambda=False, sym_t2=True): @@ -397,12 +413,12 @@ def _get_projected_gamma1_intermediates(self, t_as_lambda=False, sym_t2=True): def _get_projected_gamma2_intermediates(self, t_as_lambda=False, sym_t2=True): """Intermediates for 2-DM, projected in Lambda-amplitudes and linear T-term.""" t1, t2, l1, l2, t1x, t2x, l1x, l2x = self._ccsd_amplitudes_for_dm(t_as_lambda=t_as_lambda, sym_t2=sym_t2) - cc = self.mf # Only attributes stdout, verbose, and max_memory are needed, just use mean-field object + cc = self.mf # Only attributes stdout, verbose, and max_memory are needed, just use mean-field object dovov, *d2rest = pyscf.cc.ccsd_rdm._gamma2_intermediates(cc, t1, t2, l1x, l2x) # Correct D2[ovov] part (first element of d2 tuple) - dtau = ((t2x-t2) + einsum('ia,jb->ijab', (t1x-t1), t1)) - dovov += dtau.transpose(0,2,1,3) - dovov -= dtau.transpose(0,3,1,2)/2 + dtau = (t2x - t2) + einsum("ia,jb->ijab", (t1x - t1), t1) + dovov += dtau.transpose(0, 2, 1, 3) + dovov -= dtau.transpose(0, 3, 1, 2) / 2 d2 = (dovov, *d2rest) return d2 @@ -414,44 +430,49 @@ def make_fragment_dm1(self, t_as_lambda=False, sym_t2=True): dm1 = pyscf.cc.ccsd_rdm._make_rdm1(None, d1, with_frozen=False, with_mf=False) return dm1 - def make_fragment_dm2cumulant(self, t_as_lambda=False, sym_t2=True, sym_dm2=True, full_shape=True, - approx_cumulant=True): + def make_fragment_dm2cumulant( + self, t_as_lambda=False, sym_t2=True, sym_dm2=True, full_shape=True, approx_cumulant=True + ): """Currently MP2/CCSD only""" - if self.solver == 'MP2': + if self.solver == "MP2": if approx_cumulant not in (1, True): raise NotImplementedError t2x = self.results.pwf.restore(sym=sym_t2).as_ccsd().t2 - dovov = 2*(2*t2x - t2x.transpose(0,1,3,2)).transpose(0,2,1,3) + dovov = 2 * (2 * t2x - t2x.transpose(0, 1, 3, 2)).transpose(0, 2, 1, 3) if not full_shape: return dovov nocc, nvir = dovov.shape[:2] - norb = nocc+nvir - dm2 = np.zeros(4*[norb]) + norb = nocc + nvir + dm2 = np.zeros(4 * [norb]) occ, vir = np.s_[:nocc], np.s_[nocc:] - dm2[occ,vir,occ,vir] = dovov - dm2[vir,occ,vir,occ] = dovov.transpose(1,0,3,2) + dm2[occ, vir, occ, vir] = dovov + dm2[vir, occ, vir, occ] = dovov.transpose(1, 0, 3, 2) return dm2 cc = d1 = None d2 = self._get_projected_gamma2_intermediates(t_as_lambda=t_as_lambda, sym_t2=sym_t2) dm2 = pyscf.cc.ccsd_rdm._make_rdm2(cc, d1, d2, with_dm1=False, with_frozen=False) - if (approx_cumulant == 2): + if approx_cumulant == 2: raise NotImplementedError - elif (approx_cumulant in (1, True)): + elif approx_cumulant in (1, True): pass elif not approx_cumulant: # Remove dm1(cc)^2 dm1x = self.make_fragment_dm1(t_as_lambda=t_as_lambda, sym_t2=sym_t2) dm1 = self.results.wf.make_rdm1(with_mf=False) - dm2 -= (einsum('ij,kl->ijkl', dm1, dm1x)/2 + einsum('ij,kl->ijkl', dm1x, dm1)/2 - - einsum('ij,kl->iklj', dm1, dm1x)/4 - einsum('ij,kl->iklj', dm1x, dm1)/4) - - if (sym_dm2 and not sym_t2): - dm2 = (dm2 + dm2.transpose(1,0,3,2) + dm2.transpose(2,3,0,1) + dm2.transpose(3,2,1,0))/4 + dm2 -= ( + einsum("ij,kl->ijkl", dm1, dm1x) / 2 + + einsum("ij,kl->ijkl", dm1x, dm1) / 2 + - einsum("ij,kl->iklj", dm1, dm1x) / 4 + - einsum("ij,kl->iklj", dm1x, dm1) / 4 + ) + + if sym_dm2 and not sym_t2: + dm2 = (dm2 + dm2.transpose(1, 0, 3, 2) + dm2.transpose(2, 3, 0, 1) + dm2.transpose(3, 2, 1, 0)) / 4 return dm2 - #def make_partial_dm1_energy(self, t_as_lambda=False): + # def make_partial_dm1_energy(self, t_as_lambda=False): # dm1 = self.make_partial_dm1(t_as_lambda=t_as_lambda) # c_act = self.cluster.c_active # fock = np.linalg.multi_dot((c_act.T, self.base.get_fock(), c_act)) @@ -474,63 +495,65 @@ def make_fragment_dm2cumulant_energy(self, hamil=None, t_as_lambda=False, sym_t2 if self.solver == "MP2": # This is just ovov shape in this case. TODO neater way to handle this? - dm2 = self.make_fragment_dm2cumulant(t_as_lambda=t_as_lambda, sym_t2=sym_t2, - approx_cumulant=approx_cumulant, - full_shape=False) - return 2 * einsum('ijkl,ijkl->', hamil.get_eris_bare("ovov"), dm2)/2 + dm2 = self.make_fragment_dm2cumulant( + t_as_lambda=t_as_lambda, sym_t2=sym_t2, approx_cumulant=approx_cumulant, full_shape=False + ) + return 2 * einsum("ijkl,ijkl->", hamil.get_eris_bare("ovov"), dm2) / 2 elif approx_cumulant: # Working hypothesis: this branch will effectively always uses `approx_cumulant=True`. eris = hamil.get_dummy_eri_object(force_bare=True, with_vext=False) d2 = self._get_projected_gamma2_intermediates(t_as_lambda=t_as_lambda, sym_t2=sym_t2) - return vayesta.core.ao2mo.helper.contract_dm2intermeds_eris_rhf(d2, eris)/2 + return vayesta.core.ao2mo.helper.contract_dm2intermeds_eris_rhf(d2, eris) / 2 else: - dm2 = self.make_fragment_dm2cumulant(t_as_lambda=t_as_lambda, sym_t2=sym_t2, - approx_cumulant=approx_cumulant, - full_shape=True) - e_dm2 = einsum('ijkl,ijkl->', hamil.get_eris_bare(), dm2)/2 + dm2 = self.make_fragment_dm2cumulant( + t_as_lambda=t_as_lambda, sym_t2=sym_t2, approx_cumulant=approx_cumulant, full_shape=True + ) + e_dm2 = einsum("ijkl,ijkl->", hamil.get_eris_bare(), dm2) / 2 return e_dm2 # --- Other # --------- - def get_fragment_bsse(self, rmax=None, nimages=5, unit='A'): + def get_fragment_bsse(self, rmax=None, nimages=5, unit="A"): self.log.info("Counterpoise Calculation") self.log.info("************************") if rmax is None: rmax = self.opts.bsse_rmax # Atomic calculation with atomic basis functions: - #mol = self.mol.copy() - #atom = mol.atom[self.atoms] - #self.log.debugv("Keeping atoms %r", atom) - #mol.atom = atom - #mol.a = None - #mol.build(False, False) + # mol = self.mol.copy() + # atom = mol.atom[self.atoms] + # self.log.debugv("Keeping atoms %r", atom) + # mol.atom = atom + # mol.a = None + # mol.build(False, False) natom0, e_mf0, e_cm0, dm = self.counterpoise_calculation(rmax=0.0, nimages=0) assert natom0 == len(self.atoms) self.log.debugv("Counterpoise: E(atom)= % 16.8f Ha", e_cm0) - #natom_list = [] - #e_mf_list = [] - #e_cm_list = [] - r_values = np.hstack((np.arange(1.0, int(rmax)+1, 1.0), rmax)) - #for r in r_values: + # natom_list = [] + # e_mf_list = [] + # e_cm_list = [] + r_values = np.hstack((np.arange(1.0, int(rmax) + 1, 1.0), rmax)) + # for r in r_values: r = rmax natom, e_mf, e_cm, dm = self.counterpoise_calculation(rmax=r, dm0=dm) - self.log.debugv("Counterpoise: n(atom)= %3d E(mf)= %16.8f Ha E(%s)= % 16.8f Ha", natom, e_mf, self.solver, e_cm) + self.log.debugv( + "Counterpoise: n(atom)= %3d E(mf)= %16.8f Ha E(%s)= % 16.8f Ha", natom, e_mf, self.solver, e_cm + ) - e_bsse = self.sym_factor*(e_cm - e_cm0) + e_bsse = self.sym_factor * (e_cm - e_cm0) self.log.debugv("Counterpoise: E(BSSE)= % 16.8f Ha", e_bsse) return e_bsse - def counterpoise_calculation(self, rmax, dm0=None, nimages=5, unit='A'): - mol = self.make_counterpoise_mol(rmax, nimages=nimages, unit=unit, output='pyscf-cp.txt') + def counterpoise_calculation(self, rmax, dm0=None, nimages=5, unit="A"): + mol = self.make_counterpoise_mol(rmax, nimages=nimages, unit=unit, output="pyscf-cp.txt") # Mean-field - #mf = type(self.mf)(mol) + # mf = type(self.mf)(mol) mf = pyscf.scf.RHF(mol) mf.conv_tol = self.mf.conv_tol - #if self.mf.with_df is not None: + # if self.mf.with_df is not None: # self.log.debugv("Setting GDF") # self.log.debugv("%s", type(self.mf.with_df)) # # ONLY GDF SO FAR! @@ -540,11 +563,11 @@ def counterpoise_calculation(self, rmax, dm0=None, nimages=5, unit='A'): elif self.mf.with_df is not None: auxbasis = self.mf.with_df.auxbasis else: - auxbasis=None + auxbasis = None if auxbasis: mf = mf.density_fit(auxbasis=auxbasis) # TODO: - #use dm0 as starting point + # use dm0 as starting point mf.kernel() dm0 = mf.make_rdm1() # Embedded calculation with same options diff --git a/vayesta/ewf/helper.py b/vayesta/ewf/helper.py index 460db23ec..f31e91e75 100644 --- a/vayesta/ewf/helper.py +++ b/vayesta/ewf/helper.py @@ -28,12 +28,12 @@ def orthogonalize_mo(c, s, tol=1e-6): assert np.allclose(s, s.T) l = np.linalg.cholesky(s) c2 = np.dot(l.T, c) - #chi = np.linalg.multi_dot((c.T, s, c)) + # chi = np.linalg.multi_dot((c.T, s, c)) chi = np.dot(c2.T, c2) - chi = (chi + chi.T)/2 + chi = (chi + chi.T) / 2 e, v = np.linalg.eigh(chi) assert np.all(e > 0) - r = einsum("ai,i,bi->ab", v, 1/np.sqrt(e), v) + r = einsum("ai,i,bi->ab", v, 1 / np.sqrt(e), v) c_out = np.dot(c, r) chi_out = np.linalg.multi_dot((c_out.T, s, c_out)) # Check orthogonality within tol diff --git a/vayesta/ewf/icmp2.py b/vayesta/ewf/icmp2.py index 15afc63eb..b4aa6e7c2 100644 --- a/vayesta/ewf/icmp2.py +++ b/vayesta/ewf/icmp2.py @@ -7,49 +7,60 @@ from vayesta.core.util import dot, einsum, energy_string, log_time from vayesta.mpi import mpi + class ClusterRHF: """Helper class""" def __init__(self, fragment, coll): # The following attributes can be used from the symmetry parent, without modification: f0id = fragment.get_symmetry_parent().id - self.p_frag = coll[f0id, 'p_frag'] - self.e_occ = coll[f0id, 'e_occ'] - self.e_vir = coll[f0id, 'e_vir'] + self.p_frag = coll[f0id, "p_frag"] + self.e_occ = coll[f0id, "e_occ"] + self.e_vir = coll[f0id, "e_vir"] # These attributes are different for every fragment: fid = fragment.id - self.c_vir = coll[fid, 'c_vir'] - self.cderi = coll[fid, 'cderi'] - if (fid, 'cderi_neg') in coll: - self.cderi_neg = coll[fid, 'cderi_neg'] + self.c_vir = coll[fid, "c_vir"] + self.cderi = coll[fid, "cderi"] + if (fid, "cderi_neg") in coll: + self.cderi_neg = coll[fid, "cderi_neg"] else: self.cderi_neg = None + class ClusterUHF: """Helper class""" def __init__(self, fragment, coll): # From symmetry parent: f0id = fragment.get_symmetry_parent().id - self.p_frag = (coll[f0id, 'p_frag_a'], coll[f0id, 'p_frag_b']) - self.e_occ = (coll[f0id, 'e_occ_a'], coll[f0id, 'e_occ_b']) - self.e_vir = (coll[f0id, 'e_vir_a'], coll[f0id, 'e_vir_b']) + self.p_frag = (coll[f0id, "p_frag_a"], coll[f0id, "p_frag_b"]) + self.e_occ = (coll[f0id, "e_occ_a"], coll[f0id, "e_occ_b"]) + self.e_vir = (coll[f0id, "e_vir_a"], coll[f0id, "e_vir_b"]) # Own: fid = fragment.id - self.c_vir = (coll[fid, 'c_vir_a'], coll[fid, 'c_vir_b']) - self.cderi = (coll[fid, 'cderi_a'], coll[fid, 'cderi_b']) - if (fid, 'cderi_a_neg') in coll: - self.cderi_neg = (coll[fid, 'cderi_a_neg'], coll[fid, 'cderi_b_neg']) + self.c_vir = (coll[fid, "c_vir_a"], coll[fid, "c_vir_b"]) + self.cderi = (coll[fid, "cderi_a"], coll[fid, "cderi_b"]) + if (fid, "cderi_a_neg") in coll: + self.cderi_neg = (coll[fid, "cderi_a_neg"], coll[fid, "cderi_b_neg"]) else: self.cderi_neg = None def _get_icmp2_fragments(emb, **kwargs): - return emb.get_fragments(contributes=True, flags=dict(is_envelop=True), **kwargs) - - -def get_intercluster_mp2_energy_rhf(emb, bno_threshold_occ=None, bno_threshold_vir=1e-9, - direct=True, exchange=True, fragments=None, project_dc='occ', vers=1, diagonal=True): + return emb.get_fragments(contributes=True, flags=dict(is_envelop=True), **kwargs) + + +def get_intercluster_mp2_energy_rhf( + emb, + bno_threshold_occ=None, + bno_threshold_vir=1e-9, + direct=True, + exchange=True, + fragments=None, + project_dc="occ", + vers=1, + diagonal=True, +): """Get long-range, inter-cluster energy contribution on the MP2 level. This constructs T2 amplitudes over two clusters, X and Y, as @@ -79,7 +90,7 @@ def get_intercluster_mp2_energy_rhf(emb, bno_threshold_occ=None, bno_threshold_v Intercluster MP2 energy contribution. """ - if project_dc not in ('occ', 'vir', 'both', None): + if project_dc not in ("occ", "vir", "both", None): raise ValueError() if not emb.has_df: raise RuntimeError("Intercluster MP2 energy requires density-fitting.") @@ -125,26 +136,28 @@ def get_intercluster_mp2_energy_rhf(emb, bno_threshold_occ=None, bno_threshold_v c_bath_vir = x.bath.get_virtual_bath(bno_threshold=bno_threshold_vir, verbose=False)[0] c_vir = x.canonicalize_mo(x.bath.c_cluster_vir, c_bath_vir)[0] # Three-center integrals: - cderi, cderi_neg = emb.get_cderi((c_occ, c_vir)) # TODO: Reuse BNO + cderi, cderi_neg = emb.get_cderi((c_occ, c_vir)) # TODO: Reuse BNO # Store required quantities: - coll[x.id, 'p_frag'] = dot(x.c_proj.T, ovlp, c_occ) - coll[x.id, 'c_vir'] = c_vir - coll[x.id, 'e_occ'] = x.get_fragment_mo_energy(c_occ) - coll[x.id, 'e_vir'] = x.get_fragment_mo_energy(c_vir) - coll[x.id, 'cderi'] = cderi + coll[x.id, "p_frag"] = dot(x.c_proj.T, ovlp, c_occ) + coll[x.id, "c_vir"] = c_vir + coll[x.id, "e_occ"] = x.get_fragment_mo_energy(c_occ) + coll[x.id, "e_vir"] = x.get_fragment_mo_energy(c_vir) + coll[x.id, "cderi"] = cderi # TODO: Test 2D if cderi_neg is not None: - coll[x.id, 'cderi_neg'] = cderi_neg + coll[x.id, "cderi_neg"] = cderi_neg # Fragments Y, which are symmetry related to X for y in _get_icmp2_fragments(emb, sym_parent=x): sym_op = y.get_symmetry_operation() - coll[y.id, 'c_vir'] = sym_op(c_vir) + coll[y.id, "c_vir"] = sym_op(c_vir) # TODO: Why do we need to invert the atom reordering with argsort? - sym_op_aux = type(sym_op)(auxsym, vector=sym_op.vector, atom_reorder=np.argsort(sym_op.atom_reorder)) - coll[y.id, 'cderi'] = sym_op_aux(cderi) - #cderi_y2 = emb.get_cderi((sym_op(c_occ), sym_op(c_vir)))[0] + sym_op_aux = type(sym_op)( + auxsym, vector=sym_op.vector, atom_reorder=np.argsort(sym_op.atom_reorder) + ) + coll[y.id, "cderi"] = sym_op_aux(cderi) + # cderi_y2 = emb.get_cderi((sym_op(c_occ), sym_op(c_vir)))[0] if cderi_neg is not None: - coll[y.id, 'cderi_neg'] = cderi_neg + coll[y.id, "cderi_neg"] = cderi_neg # Convert into remote memory access (RMA) dictionary: if mpi: coll = mpi.create_rma_dict(coll) @@ -152,14 +165,14 @@ def get_intercluster_mp2_energy_rhf(emb, bno_threshold_occ=None, bno_threshold_v for ix, x in enumerate(_get_icmp2_fragments(emb, fragments=fragments, mpi_rank=mpi.rank, sym_parent=None)): cx = ClusterRHF(x, coll) - eia_x = cx.e_occ[:,None] - cx.e_vir[None,:] + eia_x = cx.e_occ[:, None] - cx.e_vir[None, :] # Already contract these parts of P_dc and S_vir, to avoid having the n(AO)^2 overlap matrix in the n(Frag)^2 loop: - if project_dc in ('occ', 'both'): + if project_dc in ("occ", "both"): pdco0 = np.dot(x.cluster.c_active.T, ovlp) - if project_dc in ('vir', 'both'): + if project_dc in ("vir", "both"): pdcv0 = np.dot(x.cluster.c_active_vir.T, ovlp) - #if exchange: + # if exchange: svir0 = np.dot(cx.c_vir.T, ovlp) # Loop over all other fragments @@ -167,75 +180,75 @@ def get_intercluster_mp2_energy_rhf(emb, bno_threshold_occ=None, bno_threshold_v cy = ClusterRHF(y, coll) # TESTING - if diagonal == 'only' and x.id != y.id: + if diagonal == "only" and x.id != y.id: continue if not diagonal and x.id == y.id: continue - eia_y = cy.e_occ[:,None] - cy.e_vir[None,:] + eia_y = cy.e_occ[:, None] - cy.e_vir[None, :] # Make T2 # TODO: save memory by blocked loop # OR: write C function (also useful for BNO build) - eris = einsum('Lia,Ljb->ijab', cx.cderi, cy.cderi) # O(n(frag)^2) * O(naux) + eris = einsum("Lia,Ljb->ijab", cx.cderi, cy.cderi) # O(n(frag)^2) * O(naux) if cx.cderi_neg is not None: - eris -= einsum('Lia,Ljb->ijab', cx.cderi_neg, cy.cderi_neg) - eijab = (eia_x[:,None,:,None] + eia_y[None,:,None,:]) - t2 = (eris / eijab) + eris -= einsum("Lia,Ljb->ijab", cx.cderi_neg, cy.cderi_neg) + eijab = eia_x[:, None, :, None] + eia_y[None, :, None, :] + t2 = eris / eijab # Project i onto F(x) and j onto F(y): - t2 = einsum('xi,yj,ijab->xyab', cx.p_frag, cy.p_frag, t2) - eris = einsum('xi,yj,ijab->xyab', cx.p_frag, cy.p_frag, eris) + t2 = einsum("xi,yj,ijab->xyab", cx.p_frag, cy.p_frag, t2) + eris = einsum("xi,yj,ijab->xyab", cx.p_frag, cy.p_frag, eris) - #if exchange: + # if exchange: # # Overlap of virtual space between X and Y svir = np.dot(svir0, cy.c_vir) # Projector to remove double counting with intracluster energy ed = ex = 0 - if project_dc == 'occ': + if project_dc == "occ": pdco = np.dot(pdco0, y.c_proj) - pdco = (np.eye(pdco.shape[-1]) - dot(pdco.T, pdco)) + pdco = np.eye(pdco.shape[-1]) - dot(pdco.T, pdco) if direct: if vers == 1: - ed = 2*einsum('ijab,iJab,jJ->', t2, eris, pdco) + ed = 2 * einsum("ijab,iJab,jJ->", t2, eris, pdco) elif vers == 2: - ed = 2*einsum('ijab,iJAb,jJ,aC,AC->', t2, eris, pdco, svir, svir) + ed = 2 * einsum("ijab,iJAb,jJ,aC,AC->", t2, eris, pdco, svir, svir) if exchange: - ex = -einsum('ijaB,iJbA,jJ,aA,bB->', t2, eris, pdco, svir, svir) - elif project_dc == 'vir': + ex = -einsum("ijaB,iJbA,jJ,aA,bB->", t2, eris, pdco, svir, svir) + elif project_dc == "vir": pdcv = np.dot(pdcv0, cy.c_vir) - pdcv = (np.eye(pdcv.shape[-1]) - dot(pdcv.T, pdcv)) + pdcv = np.eye(pdcv.shape[-1]) - dot(pdcv.T, pdcv) if direct: if vers == 1: - ed = 2*einsum('ijab,ijaB,bB->', t2, eris, pdcv) + ed = 2 * einsum("ijab,ijaB,bB->", t2, eris, pdcv) elif vers == 2: - ed = 2*einsum('ijab,ijAB,bB,aC,AC->', t2, eris, pdcv, svir, svir) + ed = 2 * einsum("ijab,ijAB,bB,aC,AC->", t2, eris, pdcv, svir, svir) if exchange: - ex = -einsum('ijaB,ijbC,CA,aA,bB->', t2, eris, pdcv, svir, svir) - elif project_dc == 'both': + ex = -einsum("ijaB,ijbC,CA,aA,bB->", t2, eris, pdcv, svir, svir) + elif project_dc == "both": pdco = np.dot(pdco0, y.c_proj) - pdco = (np.eye(pdco.shape[-1]) - dot(pdco.T, pdco)) + pdco = np.eye(pdco.shape[-1]) - dot(pdco.T, pdco) pdcv = np.dot(pdcv0, cy.c_vir) - pdcv = (np.eye(pdcv.shape[-1]) - dot(pdcv.T, pdcv)) + pdcv = np.eye(pdcv.shape[-1]) - dot(pdcv.T, pdcv) if direct: - ed = 2*einsum('ijab,iJaB,jJ,bB->', t2, eris, pdco, pdcv) + ed = 2 * einsum("ijab,iJaB,jJ,bB->", t2, eris, pdco, pdcv) if exchange: - ex = -einsum('ijaB,iJbC,jJ,CA,aA,bB->', t2, eris, pdco, pdcv, svir, svir) + ex = -einsum("ijaB,iJbC,jJ,CA,aA,bB->", t2, eris, pdco, pdcv, svir, svir) elif project_dc is None: if direct: - ed = 2*einsum('ijab,ijab->', t2, eris) + ed = 2 * einsum("ijab,ijab->", t2, eris) if exchange: - ex = -einsum('ijaB,ijbA,aA,bB->', t2, eris, svir, svir) + ex = -einsum("ijaB,ijbA,aA,bB->", t2, eris, svir, svir) prefac = x.sym_factor * x.symmetry_factor * y.sym_factor e_direct += prefac * ed e_exchange += prefac * ex - if ix+iy == 0: + if ix + iy == 0: emb.log.debugv("Intercluster MP2 energies:") - xystr = '%s <- %s:' % (x.id_name, y.id_name) + xystr = "%s <- %s:" % (x.id_name, y.id_name) estr = energy_string - emb.log.debugv(" %-12s direct= %s exchange= %s total= %s", xystr, estr(ed), estr(ex), estr(ed+ex)) + emb.log.debugv(" %-12s direct= %s exchange= %s total= %s", xystr, estr(ed), estr(ex), estr(ed + ex)) if mpi: e_direct = mpi.world.allreduce(e_direct) @@ -244,13 +257,19 @@ def get_intercluster_mp2_energy_rhf(emb, bno_threshold_occ=None, bno_threshold_v e_exchange /= emb.ncells e_icmp2 = e_direct + e_exchange if mpi.is_master: - emb.log.info(" %-12s direct= %s exchange= %s total= %s", "Total:", estr(e_direct), estr(e_exchange), estr(e_icmp2)) - coll.clear() # Important in order to not run out of MPI communicators + emb.log.info( + " %-12s direct= %s exchange= %s total= %s", + "Total:", + estr(e_direct), + estr(e_exchange), + estr(e_icmp2), + ) + coll.clear() # Important in order to not run out of MPI communicators return e_icmp2 -def get_intercluster_mp2_energy_uhf(emb, bno_threshold=1e-9, direct=True, exchange=True, project_dc='vir'): +def get_intercluster_mp2_energy_uhf(emb, bno_threshold=1e-9, direct=True, exchange=True, project_dc="vir"): """Get long-range, inter-cluster energy contribution on the MP2 level. This constructs T2 amplitudes over two clusters, X and Y, as @@ -275,7 +294,7 @@ def get_intercluster_mp2_energy_uhf(emb, bno_threshold=1e-9, direct=True, exchan e_icmp2: float Intercluster MP2 energy contribution. """ - if project_dc != 'vir': + if project_dc != "vir": raise NotImplementedError e_direct = 0.0 @@ -297,34 +316,36 @@ def get_intercluster_mp2_energy_uhf(emb, bno_threshold=1e-9, direct=True, exchan coll = {} # Loop over symmetry unique fragments for x in _get_icmp2_fragments(emb, mpi_rank=mpi.rank, sym_parent=None): - #c_occ = x.bath.dmet_bath.c_cluster_occ + # c_occ = x.bath.dmet_bath.c_cluster_occ c_occ = x._dmet_bath.c_cluster_occ - coll[x.id, 'p_frag_a'] = dot(x.c_proj[0].T, ovlp, c_occ[0]) - coll[x.id, 'p_frag_b'] = dot(x.c_proj[1].T, ovlp, c_occ[1]) + coll[x.id, "p_frag_a"] = dot(x.c_proj[0].T, ovlp, c_occ[0]) + coll[x.id, "p_frag_b"] = dot(x.c_proj[1].T, ovlp, c_occ[1]) c_bath_vir = x._bath_factory_vir.get_bath(bno_threshold=bno_threshold, verbose=False)[0] c_vir = x.canonicalize_mo(x._dmet_bath.c_cluster_vir, c_bath_vir)[0] - coll[x.id, 'c_vir_a'], coll[x.id, 'c_vir_b'] = c_vir - coll[x.id, 'e_occ_a'], coll[x.id, 'e_occ_b'] = x.get_fragment_mo_energy(c_occ) - coll[x.id, 'e_vir_a'], coll[x.id, 'e_vir_b'] = x.get_fragment_mo_energy(c_vir) - cderi_a, cderi_a_neg = emb.get_cderi((c_occ[0], c_vir[0])) # TODO: Reuse BNO - cderi_b, cderi_b_neg = emb.get_cderi((c_occ[1], c_vir[1])) # TODO: Reuse BNO - coll[x.id, 'cderi_a'] = cderi_a - coll[x.id, 'cderi_b'] = cderi_b + coll[x.id, "c_vir_a"], coll[x.id, "c_vir_b"] = c_vir + coll[x.id, "e_occ_a"], coll[x.id, "e_occ_b"] = x.get_fragment_mo_energy(c_occ) + coll[x.id, "e_vir_a"], coll[x.id, "e_vir_b"] = x.get_fragment_mo_energy(c_vir) + cderi_a, cderi_a_neg = emb.get_cderi((c_occ[0], c_vir[0])) # TODO: Reuse BNO + cderi_b, cderi_b_neg = emb.get_cderi((c_occ[1], c_vir[1])) # TODO: Reuse BNO + coll[x.id, "cderi_a"] = cderi_a + coll[x.id, "cderi_b"] = cderi_b if cderi_a_neg is not None: - coll[x.id, 'cderi_a_neg'] = cderi_a_neg - coll[x.id, 'cderi_b_neg'] = cderi_b_neg + coll[x.id, "cderi_a_neg"] = cderi_a_neg + coll[x.id, "cderi_b_neg"] = cderi_b_neg # Symmetry related fragments for y in _get_icmp2_fragments(emb, sym_parent=x): sym_op = y.get_symmetry_operation() - coll[y.id, 'c_vir_a'] = sym_op(c_vir[0]) - coll[y.id, 'c_vir_b'] = sym_op(c_vir[1]) + coll[y.id, "c_vir_a"] = sym_op(c_vir[0]) + coll[y.id, "c_vir_b"] = sym_op(c_vir[1]) # TODO: Why do we need to invert the atom reordering with argsort? - sym_op_aux = type(sym_op)(auxsym, vector=sym_op.vector, atom_reorder=np.argsort(sym_op.atom_reorder)) - coll[y.id, 'cderi_a'] = sym_op_aux(cderi_a) - coll[y.id, 'cderi_b'] = sym_op_aux(cderi_b) + sym_op_aux = type(sym_op)( + auxsym, vector=sym_op.vector, atom_reorder=np.argsort(sym_op.atom_reorder) + ) + coll[y.id, "cderi_a"] = sym_op_aux(cderi_a) + coll[y.id, "cderi_b"] = sym_op_aux(cderi_b) if cderi_a_neg is not None: - coll[y.id, 'cderi_a_neg'] = cderi_a_neg - coll[y.id, 'cderi_b_neg'] = cderi_b_neg + coll[y.id, "cderi_a_neg"] = cderi_a_neg + coll[y.id, "cderi_b_neg"] = cderi_b_neg # Convert into remote memory access (RMA) dictionary: if mpi: coll = mpi.create_rma_dict(coll) @@ -332,8 +353,8 @@ def get_intercluster_mp2_energy_uhf(emb, bno_threshold=1e-9, direct=True, exchan for ix, x in enumerate(_get_icmp2_fragments(emb, mpi_rank=mpi.rank, sym_parent=None)): cx = ClusterUHF(x, coll) - eia_xa = cx.e_occ[0][:,None] - cx.e_vir[0][None,:] - eia_xb = cx.e_occ[1][:,None] - cx.e_vir[1][None,:] + eia_xa = cx.e_occ[0][:, None] - cx.e_vir[0][None, :] + eia_xb = cx.e_occ[1][:, None] - cx.e_vir[1][None, :] # Already contract these parts of P_dc and S_vir, to avoid having the n(AO)^2 overlap matrix in the n(Frag)^2 loop: pdcv0a = np.dot(x.cluster.c_active_vir[0].T, ovlp) @@ -346,40 +367,40 @@ def get_intercluster_mp2_energy_uhf(emb, bno_threshold=1e-9, direct=True, exchan for iy, y in enumerate(_get_icmp2_fragments(emb)): cy = ClusterUHF(y, coll) - eia_ya = cy.e_occ[0][:,None] - cy.e_vir[0][None,:] - eia_yb = cy.e_occ[1][:,None] - cy.e_vir[1][None,:] + eia_ya = cy.e_occ[0][:, None] - cy.e_vir[0][None, :] + eia_yb = cy.e_occ[1][:, None] - cy.e_vir[1][None, :] # Make T2 # TODO: save memory by blocked loop # OR: write C function (also useful for BNO build) - eris_aa = einsum('Lia,Ljb->ijab', cx.cderi[0], cy.cderi[0]) # O(n(frag)^2) * O(naux) - eris_ab = einsum('Lia,Ljb->ijab', cx.cderi[0], cy.cderi[1]) # O(n(frag)^2) * O(naux) - eris_ba = einsum('Lia,Ljb->ijab', cx.cderi[1], cy.cderi[0]) # O(n(frag)^2) * O(naux) - eris_bb = einsum('Lia,Ljb->ijab', cx.cderi[1], cy.cderi[1]) # O(n(frag)^2) * O(naux) + eris_aa = einsum("Lia,Ljb->ijab", cx.cderi[0], cy.cderi[0]) # O(n(frag)^2) * O(naux) + eris_ab = einsum("Lia,Ljb->ijab", cx.cderi[0], cy.cderi[1]) # O(n(frag)^2) * O(naux) + eris_ba = einsum("Lia,Ljb->ijab", cx.cderi[1], cy.cderi[0]) # O(n(frag)^2) * O(naux) + eris_bb = einsum("Lia,Ljb->ijab", cx.cderi[1], cy.cderi[1]) # O(n(frag)^2) * O(naux) if cx.cderi_neg is not None: - eris_aa -= einsum('Lia,Ljb->ijab', cx.cderi_neg[0], cy.cderi_neg[0]) - eris_ab -= einsum('Lia,Ljb->ijab', cx.cderi_neg[0], cy.cderi_neg[1]) - eris_ab -= einsum('Lia,Ljb->ijab', cx.cderi_neg[1], cy.cderi_neg[0]) - eris_bb -= einsum('Lia,Ljb->ijab', cx.cderi_neg[1], cy.cderi_neg[1]) + eris_aa -= einsum("Lia,Ljb->ijab", cx.cderi_neg[0], cy.cderi_neg[0]) + eris_ab -= einsum("Lia,Ljb->ijab", cx.cderi_neg[0], cy.cderi_neg[1]) + eris_ab -= einsum("Lia,Ljb->ijab", cx.cderi_neg[1], cy.cderi_neg[0]) + eris_bb -= einsum("Lia,Ljb->ijab", cx.cderi_neg[1], cy.cderi_neg[1]) # Alpha-alpha - eijab_aa = (eia_xa[:,None,:,None] + eia_ya[None,:,None,:]) - eijab_ab = (eia_xa[:,None,:,None] + eia_yb[None,:,None,:]) - eijab_ba = (eia_xb[:,None,:,None] + eia_ya[None,:,None,:]) - eijab_bb = (eia_xb[:,None,:,None] + eia_yb[None,:,None,:]) - t2aa = (eris_aa / eijab_aa) - t2ab = (eris_ab / eijab_ab) - t2ba = (eris_ba / eijab_ba) - t2bb = (eris_bb / eijab_bb) + eijab_aa = eia_xa[:, None, :, None] + eia_ya[None, :, None, :] + eijab_ab = eia_xa[:, None, :, None] + eia_yb[None, :, None, :] + eijab_ba = eia_xb[:, None, :, None] + eia_ya[None, :, None, :] + eijab_bb = eia_xb[:, None, :, None] + eia_yb[None, :, None, :] + t2aa = eris_aa / eijab_aa + t2ab = eris_ab / eijab_ab + t2ba = eris_ba / eijab_ba + t2bb = eris_bb / eijab_bb # Project i onto F(x) and j onto F(y): - t2aa = einsum('xi,yj,ijab->xyab', cx.p_frag[0], cy.p_frag[0], t2aa) - t2ab = einsum('xi,yj,ijab->xyab', cx.p_frag[0], cy.p_frag[1], t2ab) - t2ba = einsum('xi,yj,ijab->xyab', cx.p_frag[1], cy.p_frag[0], t2ba) - t2bb = einsum('xi,yj,ijab->xyab', cx.p_frag[1], cy.p_frag[1], t2bb) - eris_aa = einsum('xi,yj,ijab->xyab', cx.p_frag[0], cy.p_frag[0], eris_aa) - eris_ab = einsum('xi,yj,ijab->xyab', cx.p_frag[0], cy.p_frag[1], eris_ab) - eris_ba = einsum('xi,yj,ijab->xyab', cx.p_frag[1], cy.p_frag[0], eris_ba) - eris_bb = einsum('xi,yj,ijab->xyab', cx.p_frag[1], cy.p_frag[1], eris_bb) + t2aa = einsum("xi,yj,ijab->xyab", cx.p_frag[0], cy.p_frag[0], t2aa) + t2ab = einsum("xi,yj,ijab->xyab", cx.p_frag[0], cy.p_frag[1], t2ab) + t2ba = einsum("xi,yj,ijab->xyab", cx.p_frag[1], cy.p_frag[0], t2ba) + t2bb = einsum("xi,yj,ijab->xyab", cx.p_frag[1], cy.p_frag[1], t2bb) + eris_aa = einsum("xi,yj,ijab->xyab", cx.p_frag[0], cy.p_frag[0], eris_aa) + eris_ab = einsum("xi,yj,ijab->xyab", cx.p_frag[0], cy.p_frag[1], eris_ab) + eris_ba = einsum("xi,yj,ijab->xyab", cx.p_frag[1], cy.p_frag[0], eris_ba) + eris_bb = einsum("xi,yj,ijab->xyab", cx.p_frag[1], cy.p_frag[1], eris_bb) # Overlap of virtual space between X and Y if exchange: @@ -390,27 +411,27 @@ def get_intercluster_mp2_energy_uhf(emb, bno_threshold=1e-9, direct=True, exchan ed = ex = 0 pdcva = np.dot(pdcv0a, cy.c_vir[0]) pdcvb = np.dot(pdcv0b, cy.c_vir[1]) - pdcva = (np.eye(pdcva.shape[-1]) - dot(pdcva.T, pdcva)) - pdcvb = (np.eye(pdcvb.shape[-1]) - dot(pdcvb.T, pdcvb)) + pdcva = np.eye(pdcva.shape[-1]) - dot(pdcva.T, pdcva) + pdcvb = np.eye(pdcvb.shape[-1]) - dot(pdcvb.T, pdcvb) if direct: - ed += einsum('ijab,ijaB,bB->', t2aa, eris_aa, pdcva)/2 - ed += einsum('ijab,ijaB,bB->', t2ab, eris_ab, pdcvb)/2 - ed += einsum('ijab,ijaB,bB->', t2ba, eris_ba, pdcva)/2 - ed += einsum('ijab,ijaB,bB->', t2bb, eris_bb, pdcvb)/2 + ed += einsum("ijab,ijaB,bB->", t2aa, eris_aa, pdcva) / 2 + ed += einsum("ijab,ijaB,bB->", t2ab, eris_ab, pdcvb) / 2 + ed += einsum("ijab,ijaB,bB->", t2ba, eris_ba, pdcva) / 2 + ed += einsum("ijab,ijaB,bB->", t2bb, eris_bb, pdcvb) / 2 if exchange: - ex += -einsum('ijaB,ijbC,CA,aA,bB->', t2aa, eris_aa, pdcva, svira, svira)/2 - ex += -einsum('ijaB,ijbC,CA,aA,bB->', t2bb, eris_bb, pdcvb, svirb, svirb)/2 + ex += -einsum("ijaB,ijbC,CA,aA,bB->", t2aa, eris_aa, pdcva, svira, svira) / 2 + ex += -einsum("ijaB,ijbC,CA,aA,bB->", t2bb, eris_bb, pdcvb, svirb, svirb) / 2 prefac = x.sym_factor * x.symmetry_factor * y.sym_factor e_direct += prefac * ed e_exchange += prefac * ex - if ix+iy == 0: + if ix + iy == 0: emb.log.debugv("Intercluster MP2 energies:") - xystr = '%s <- %s:' % (x.id_name, y.id_name) + xystr = "%s <- %s:" % (x.id_name, y.id_name) estr = energy_string - emb.log.debugv(" %-12s direct= %s exchange= %s total= %s", xystr, estr(ed), estr(ex), estr(ed+ex)) + emb.log.debugv(" %-12s direct= %s exchange= %s total= %s", xystr, estr(ed), estr(ex), estr(ed + ex)) if mpi: e_direct = mpi.world.allreduce(e_direct) @@ -419,7 +440,13 @@ def get_intercluster_mp2_energy_uhf(emb, bno_threshold=1e-9, direct=True, exchan e_exchange /= emb.ncells e_icmp2 = e_direct + e_exchange if mpi.is_master: - emb.log.info(" %-12s direct= %s exchange= %s total= %s", "Total:", estr(e_direct), estr(e_exchange), estr(e_icmp2)) - coll.clear() # Important in order to not run out of MPI communicators + emb.log.info( + " %-12s direct= %s exchange= %s total= %s", + "Total:", + estr(e_direct), + estr(e_exchange), + estr(e_icmp2), + ) + coll.clear() # Important in order to not run out of MPI communicators return e_icmp2 diff --git a/vayesta/ewf/rdm.py b/vayesta/ewf/rdm.py index 62243d0fd..83f63a101 100644 --- a/vayesta/ewf/rdm.py +++ b/vayesta/ewf/rdm.py @@ -18,6 +18,7 @@ def _get_mockcc(mo_coeff, max_memory): cc.max_memory = max_memory return cc + def make_rdm1_ccsd(emb, ao_basis=False, t_as_lambda=False, symmetrize=True, with_mf=True, mpi_target=None, mp2=False): """Make one-particle reduced density-matrix from partitioned fragment CCSD wave functions. @@ -65,26 +66,26 @@ def make_rdm1_ccsd(emb, ao_basis=False, t_as_lambda=False, symmetrize=True, with # MPI loop for frag in emb.get_fragments(contributes=True, mpi_rank=mpi.rank): wfx = frag.results.pwf.as_ccsd() - th2x = (2*wfx.t2 - wfx.t2.transpose(0,1,3,2)) + th2x = 2 * wfx.t2 - wfx.t2.transpose(0, 1, 3, 2) l2x = wfx.t2 if t_as_lambda else wfx.l2 if l2x is None: raise NotCalculatedError("L2-amplitudes not calculated for %s" % frag) # Mean-field to cluster (occupied/virtual): - co = frag.get_overlap('mo[occ]|cluster[occ]') - cv = frag.get_overlap('mo[vir]|cluster[vir]') + co = frag.get_overlap("mo[occ]|cluster[occ]") + cv = frag.get_overlap("mo[vir]|cluster[vir]") # Mean-field to fragment (occupied): - fo = frag.get_overlap('mo[occ]|frag') - doo -= einsum('kiba,kjba,Ii,Jj->IJ', th2x, l2x, co, co) + fo = frag.get_overlap("mo[occ]|frag") + doo -= einsum("kiba,kjba,Ii,Jj->IJ", th2x, l2x, co, co) if not symmetrize: - dvv += einsum('ijca,ijcb,Aa,Bb->AB', th2x, l2x, cv, cv) + dvv += einsum("ijca,ijcb,Aa,Bb->AB", th2x, l2x, cv, cv) if not mp2: - dov += einsum('ijab,Ii,Jj,Aa,Bb,JB->IA', th2x, fo, co, cv, cv, l1) + dov += einsum("ijab,Ii,Jj,Aa,Bb,JB->IA", th2x, fo, co, cv, cv, l1) else: - dvv += einsum('jica,jicb,Aa,Bb->AB', th2x, l2x, cv, cv) / 2 - dvv += einsum('ijac,ijbc,Aa,Bb->AB', th2x, l2x, cv, cv) / 2 + dvv += einsum("jica,jicb,Aa,Bb->AB", th2x, l2x, cv, cv) / 2 + dvv += einsum("ijac,ijbc,Aa,Bb->AB", th2x, l2x, cv, cv) / 2 if not mp2: - dov += einsum('ijab,Ii,Jj,Aa,Bb,JB->IA', th2x, fo, co, cv, cv, l1) / 2 - dov += einsum('jiba,Jj,Ii,Aa,Bb,JB->IA', th2x, fo, co, cv, cv, l1) / 2 + dov += einsum("ijab,Ii,Jj,Aa,Bb,JB->IA", th2x, fo, co, cv, cv, l1) / 2 + dov += einsum("jiba,Jj,Ii,Aa,Bb,JB->IA", th2x, fo, co, cv, cv, l1) / 2 if mpi: if mp2: @@ -95,20 +96,20 @@ def make_rdm1_ccsd(emb, ao_basis=False, t_as_lambda=False, symmetrize=True, with return None if not mp2: - dov += einsum('IJ,JA->IA', doo, t1) - dov -= einsum('IB,AB->IA', t1, dvv) - dov += (t1 + l1 - einsum('IA,JA,JB->IB', t1, l1, t1)) - doo -= einsum('IA,JA->IJ', l1, t1) - dvv += einsum('IA,IB->AB', t1, l1) + dov += einsum("IJ,JA->IA", doo, t1) + dov -= einsum("IB,AB->IA", t1, dvv) + dov += t1 + l1 - einsum("IA,JA,JB->IB", t1, l1, t1) + doo -= einsum("IA,JA->IJ", l1, t1) + dvv += einsum("IA,IB->AB", t1, l1) - nmo = (nocc + nvir) + nmo = nocc + nvir occ, vir = np.s_[:nocc], np.s_[nocc:] dm1 = np.zeros((nmo, nmo)) - dm1[occ,occ] = (doo + doo.T) - dm1[vir,vir] = (dvv + dvv.T) + dm1[occ, occ] = doo + doo.T + dm1[vir, vir] = dvv + dvv.T if not mp2: - dm1[occ,vir] = dov - dm1[vir,occ] = dov.T + dm1[occ, vir] = dov + dm1[vir, occ] = dov.T if with_mf: dm1[np.diag_indices(nocc)] += 2.0 if ao_basis: @@ -116,6 +117,7 @@ def make_rdm1_ccsd(emb, ao_basis=False, t_as_lambda=False, symmetrize=True, with return dm1 + def make_rdm1_ccsd_proj_lambda(emb, ao_basis=False, t_as_lambda=False, with_mf=True, sym_t2=True, mpi_target=None): """Make one-particle reduced density-matrix from partitioned fragment CCSD wave functions. @@ -141,7 +143,7 @@ def make_rdm1_ccsd_proj_lambda(emb, ao_basis=False, t_as_lambda=False, with_mf=T # --- Loop over pairs of fragments and add projected density-matrix contributions: dm1 = np.zeros((emb.nmo, emb.nmo)) for x in emb.get_fragments(contributes=True, mpi_rank=mpi.rank): - rx = x.get_overlap('mo|cluster') + rx = x.get_overlap("mo|cluster") dm1x = x.make_fragment_dm1(t_as_lambda=t_as_lambda, sym_t2=sym_t2) dm1 += np.linalg.multi_dot((rx, dm1x, rx.T)) if mpi: @@ -154,8 +156,18 @@ def make_rdm1_ccsd_proj_lambda(emb, ao_basis=False, t_as_lambda=False, with_mf=T dm1 = dot(emb.mo_coeff, dm1, emb.mo_coeff.T) return dm1 -def make_rdm1_ccsd_global_wf(emb, t_as_lambda=False, with_t1=True, svd_tol=1e-3, ovlp_tol=None, use_sym=True, - late_t2_sym=True, mpi_target=None, slow=False): + +def make_rdm1_ccsd_global_wf( + emb, + t_as_lambda=False, + with_t1=True, + svd_tol=1e-3, + ovlp_tol=None, + use_sym=True, + late_t2_sym=True, + mpi_target=None, + slow=False, +): """Make one-particle reduced density-matrix from partitioned fragment CCSD wave functions. This replaces make_rdm1_ccsd_old. @@ -198,7 +210,7 @@ def make_rdm1_ccsd_global_wf(emb, t_as_lambda=False, with_t1=True, svd_tol=1e-3, if not with_t1: t1 = l1 = np.zeros_like(t1) mockcc = _get_mockcc(emb.mo_coeff, emb.mf.max_memory) - #dm1 = pyscf.cc.ccsd_rdm.make_rdm1(mockcc, t1=t1, t2=t2, l1=l1, l2=l2, with_mf=False) + # dm1 = pyscf.cc.ccsd_rdm.make_rdm1(mockcc, t1=t1, t2=t2, l1=l1, l2=l2, with_mf=False) dm1 = ccsd_rdm.make_rdm1(mockcc, t1=t1, t2=t2, l1=l1, l2=l2, with_mf=False) return dm1 @@ -220,8 +232,10 @@ def make_rdm1_ccsd_global_wf(emb, t_as_lambda=False, with_t1=True, svd_tol=1e-3, if mpi: # TODO: use L-amplitudes of cluster X and T-amplitudes, # Only send T-amplitudes via RMA? - #rma = {x.id: x.results.pwf.pack() for x in emb.get_fragments(active=True, mpi_rank=mpi.rank)} - rma = {x.id: x.results.pwf.pack() for x in emb.get_fragments(contributes=True, mpi_rank=mpi.rank, sym_parent=None)} + # rma = {x.id: x.results.pwf.pack() for x in emb.get_fragments(active=True, mpi_rank=mpi.rank)} + rma = { + x.id: x.results.pwf.pack() for x in emb.get_fragments(contributes=True, mpi_rank=mpi.rank, sym_parent=None) + } rma = mpi.create_rma_dict(rma) # --- Loop over pairs of fragments and add projected density-matrix contributions: @@ -235,22 +249,21 @@ def make_rdm1_ccsd_global_wf(emb, t_as_lambda=False, with_t1=True, svd_tol=1e-3, wfx = fx.results.pwf.as_ccsd() if not late_t2_sym: wfx = wfx.restore() - theta = (2*wfx.t2 - wfx.t2.transpose(0,1,3,2)) + theta = 2 * wfx.t2 - wfx.t2.transpose(0, 1, 3, 2) # Intermediates: leave left index in cluster-x basis: doox = np.zeros((fx.cluster.nocc_active, emb.nocc)) dvvx = np.zeros((fx.cluster.nvir_active, emb.nvir)) - cx_occ = fx.get_overlap('mo[occ]|cluster[occ]') - cx_vir = fx.get_overlap('mo[vir]|cluster[vir]') - cfx = fx.get_overlap('cluster[occ]|frag') - mfx = fx.get_overlap('mo[occ]|frag') + cx_occ = fx.get_overlap("mo[occ]|cluster[occ]") + cx_vir = fx.get_overlap("mo[vir]|cluster[vir]") + cfx = fx.get_overlap("cluster[occ]|frag") + mfx = fx.get_overlap("mo[occ]|frag") # Loop over fragments y: for fy_parent in emb.get_fragments(contributes=True, **symfilter): - if mpi: - if fy_parent.solver == 'MP2': + if fy_parent.solver == "MP2": wfy = RMP2_WaveFunction.unpack(rma[fy_parent.id]).as_ccsd() else: wfy = RCCSD_WaveFunction.unpack(rma[fy_parent.id]) @@ -258,11 +271,11 @@ def make_rdm1_ccsd_global_wf(emb, t_as_lambda=False, with_t1=True, svd_tol=1e-3, wfy = fy_parent.results.pwf.as_ccsd() if not late_t2_sym: wfy = wfy.restore() - cfy = fy_parent.get_overlap('cluster[occ]|frag') - - for fy, (cy_frag, cy_occ_ao, cy_vir_ao) in fy_parent.loop_symmetry_children((fy_parent.c_frag, - fy_parent.cluster.c_occ, fy_parent.cluster.c_vir), include_self=True, maxgen=maxgen): + cfy = fy_parent.get_overlap("cluster[occ]|frag") + for fy, (cy_frag, cy_occ_ao, cy_vir_ao) in fy_parent.loop_symmetry_children( + (fy_parent.c_frag, fy_parent.cluster.c_occ, fy_parent.cluster.c_vir), include_self=True, maxgen=maxgen + ): cy_occ = np.dot(cs_occ, cy_occ_ao) cy_vir = np.dot(cs_vir, cy_vir_ao) # Overlap between cluster x and cluster y: @@ -271,60 +284,63 @@ def make_rdm1_ccsd_global_wf(emb, t_as_lambda=False, with_t1=True, svd_tol=1e-3, mfy = np.dot(cs_occ, cy_frag) if svd_tol is not None: + def svd(a): nonlocal total_sv, kept_sv u, s, v = np.linalg.svd(a, full_matrices=False) if svd_tol is not None: - keep = (s >= svd_tol) + keep = s >= svd_tol total_sv += len(keep) kept_sv += sum(keep) - u, s, v = u[:,keep], s[keep], v[keep] + u, s, v = u[:, keep], s[keep], v[keep] return u, s, v + uxy_occ, sxy_occ, vxy_occ = svd(rxy_occ) uxy_vir, sxy_vir, vxy_vir = svd(rxy_vir) - uxy_occ *= np.sqrt(sxy_occ)[np.newaxis,:] - uxy_vir *= np.sqrt(sxy_vir)[np.newaxis,:] - vxy_occ *= np.sqrt(sxy_occ)[:,np.newaxis] - vxy_vir *= np.sqrt(sxy_vir)[:,np.newaxis] + uxy_occ *= np.sqrt(sxy_occ)[np.newaxis, :] + uxy_vir *= np.sqrt(sxy_vir)[np.newaxis, :] + vxy_occ *= np.sqrt(sxy_occ)[:, np.newaxis] + vxy_vir *= np.sqrt(sxy_vir)[:, np.newaxis] else: - nsv = (min(rxy_occ.shape[0], rxy_occ.shape[1]) - + min(rxy_vir.shape[0], rxy_vir.shape[1])) - total_sv = kept_sv = (total_sv + nsv) + nsv = min(rxy_occ.shape[0], rxy_occ.shape[1]) + min(rxy_vir.shape[0], rxy_vir.shape[1]) + total_sv = kept_sv = total_sv + nsv # --- If ovlp_tol is given, the cluster x-y pairs will be screened based on the # largest singular value of the occupied and virtual overlap matrices total_xy += 1 if ovlp_tol is not None: if svd_tol: - rxy_occ_norm = (sxy_occ[0] if len(sxy_occ) > 0 else 0.0) - rxy_vir_norm = (sxy_vir[0] if len(sxy_vir) > 0 else 0.0) + rxy_occ_norm = sxy_occ[0] if len(sxy_occ) > 0 else 0.0 + rxy_vir_norm = sxy_vir[0] if len(sxy_vir) > 0 else 0.0 else: rxy_occ_norm = np.linalg.norm(rxy_occ, ord=2) rxy_vir_norm = np.linalg.norm(rxy_vir, ord=2) - if (min(rxy_occ_norm, rxy_vir_norm) < ovlp_tol): + if min(rxy_occ_norm, rxy_vir_norm) < ovlp_tol: emb.log.debugv("Overlap of fragment pair %s - %s below %.2e; skipping pair.", fx, fy, ovlp_tol) continue kept_xy += 1 - l2 = wfy.t2 if (t_as_lambda or fy_parent.solver == 'MP2') else wfy.l2 + l2 = wfy.t2 if (t_as_lambda or fy_parent.solver == "MP2") else wfy.l2 if l2 is None: raise RuntimeError("No L2-amplitudes found for %s!" % fy) # Theta_jk^ab * l_ik^ab -> ij - #doox -= einsum('jkab,IKAB,kK,aA,bB,QI->jQ', theta, l2, rxy_occ, rxy_vir, rxy_vir, cy_occ) + # doox -= einsum('jkab,IKAB,kK,aA,bB,QI->jQ', theta, l2, rxy_occ, rxy_vir, rxy_vir, cy_occ) ## Theta_ji^ca * l_ji^cb -> ab - #dvvx += einsum('jica,JICB,jJ,iI,cC,QB->aQ', theta, l2, rxy_occ, rxy_occ, rxy_vir, cy_vir) + # dvvx += einsum('jica,JICB,jJ,iI,cC,QB->aQ', theta, l2, rxy_occ, rxy_occ, rxy_vir, cy_vir) # Only multiply with O(N)-scaling cy_occ/cy_vir in last step: if not late_t2_sym: if svd_tol is None: - tmp = einsum('(ijab,jJ,aA->iJAb),IJAB->iIbB', theta, rxy_occ, rxy_vir, l2) + tmp = einsum("(ijab,jJ,aA->iJAb),IJAB->iIbB", theta, rxy_occ, rxy_vir, l2) else: - tmp = einsum('(ijab,jS,aP->iSPb),(SJ,PA,IJAB->ISPB)->iIbB', theta, uxy_occ, uxy_vir, vxy_occ, vxy_vir, l2) - tmpo = -einsum('iIbB,bB->iI', tmp, rxy_vir) + tmp = einsum( + "(ijab,jS,aP->iSPb),(SJ,PA,IJAB->ISPB)->iIbB", theta, uxy_occ, uxy_vir, vxy_occ, vxy_vir, l2 + ) + tmpo = -einsum("iIbB,bB->iI", tmp, rxy_vir) doox += np.dot(tmpo, cy_occ.T) - tmpv = einsum('iIbB,iI->bB', tmp, rxy_occ) + tmpv = einsum("iIbB,iI->bB", tmp, rxy_occ) dvvx += np.dot(tmpv, cy_vir.T) else: # Calculate some overlap matrices: @@ -340,47 +356,47 @@ def svd(a): # --- Occupied # Deal with both virtual overlaps here: if svd_tol is None: - t2tmp = einsum('xjab,aA,bB->xjAB', theta, rxy_vir, rxy_vir) # frag * cluster^4 + t2tmp = einsum("xjab,aA,bB->xjAB", theta, rxy_vir, rxy_vir) # frag * cluster^4 l2tmp = l2 else: - t2tmp = einsum('xjab,aS,bP->xjSP', theta, uxy_vir, uxy_vir) - l2tmp = einsum('yjab,Sa,Pb->yjSP', l2, vxy_vir, vxy_vir) + t2tmp = einsum("xjab,aS,bP->xjSP", theta, uxy_vir, uxy_vir) + l2tmp = einsum("yjab,Sa,Pb->yjSP", l2, vxy_vir, vxy_vir) # T2 * L2 if svd_tol is None: - tmp = -einsum('(xjAB,jJ->xJAB),YJAB->xY', t2tmp, rxy_occ, l2tmp)/4 + tmp = -einsum("(xjAB,jJ->xJAB),YJAB->xY", t2tmp, rxy_occ, l2tmp) / 4 else: - tmp = -einsum('(xjAB,jS->xSAB),(SJ,YJAB->YSAB)->xY', t2tmp, uxy_occ, vxy_occ, l2tmp)/4 + tmp = -einsum("(xjAB,jS->xSAB),(SJ,YJAB->YSAB)->xY", t2tmp, uxy_occ, vxy_occ, l2tmp) / 4 doox += dot(cfx, tmp, mfy.T) # T2 * L2.T - tmp = -einsum('(xjAB,jY->xYAB),YIBA->xI', t2tmp, cfxy_occ, l2tmp)/4 + tmp = -einsum("(xjAB,jY->xYAB),YIBA->xI", t2tmp, cfxy_occ, l2tmp) / 4 doox += dot(cfx, tmp, cy_occ.T) # T2.T * L2 - tmp = -einsum('xiBA,(Jx,YJAB->YxAB)->iY', t2tmp, cfyx_occ, l2tmp)/4 + tmp = -einsum("xiBA,(Jx,YJAB->YxAB)->iY", t2tmp, cfyx_occ, l2tmp) / 4 doox += np.dot(tmp, mfy.T) # T2.T * L2.T - tmp = -einsum('xiBA,xY,YIBA->iI', t2tmp, ffxy, l2tmp)/4 + tmp = -einsum("xiBA,xY,YIBA->iI", t2tmp, ffxy, l2tmp) / 4 doox += np.dot(tmp, cy_occ.T) # --- Virtual # T2 * L2 and T2.T * L2.T if svd_tol is None: - t2tmp = einsum('xjab,xY,jJ->YJab', theta, ffxy, rxy_occ) - tmp = einsum('(YJab,aA->YJAb),YJAB->bB', t2tmp, rxy_vir, l2)/4 - tmp += einsum('(YIba,aA->YIbA),YIBA->bB', t2tmp, rxy_vir, l2)/4 + t2tmp = einsum("xjab,xY,jJ->YJab", theta, ffxy, rxy_occ) + tmp = einsum("(YJab,aA->YJAb),YJAB->bB", t2tmp, rxy_vir, l2) / 4 + tmp += einsum("(YIba,aA->YIbA),YIBA->bB", t2tmp, rxy_vir, l2) / 4 else: - t2tmp = einsum('xjab,jS->xSab', theta, uxy_occ) - l2tmp = einsum('YJAB,SJ,xY->xSAB', l2, vxy_occ, ffxy) - tmp = einsum('(xSab,aP->xSPb),(PA,xSAB->xSPB)->bB', t2tmp, uxy_vir, vxy_vir, l2tmp)/4 - tmp += einsum('(xSba,aP->xSbP),(PA,xSBA->xSBP)->bB', t2tmp, uxy_vir, vxy_vir, l2tmp)/4 + t2tmp = einsum("xjab,jS->xSab", theta, uxy_occ) + l2tmp = einsum("YJAB,SJ,xY->xSAB", l2, vxy_occ, ffxy) + tmp = einsum("(xSab,aP->xSPb),(PA,xSAB->xSPB)->bB", t2tmp, uxy_vir, vxy_vir, l2tmp) / 4 + tmp += einsum("(xSba,aP->xSbP),(PA,xSBA->xSBP)->bB", t2tmp, uxy_vir, vxy_vir, l2tmp) / 4 # T2 * L2.T and T2.T * L2 - t2tmp = einsum('xjab,jY->xYab', theta, cfxy_occ) - l2tmp = einsum('Jx,YJAB->YxAB', cfyx_occ, l2) + t2tmp = einsum("xjab,jY->xYab", theta, cfxy_occ) + l2tmp = einsum("Jx,YJAB->YxAB", cfyx_occ, l2) if svd_tol is None: - tmp += einsum('(xYab,aA->xYAb),YxBA->bB', t2tmp, rxy_vir, l2tmp)/4 - tmp += einsum('(xYba,aA->xYbA),YxAB->bB', t2tmp, rxy_vir, l2tmp)/4 + tmp += einsum("(xYab,aA->xYAb),YxBA->bB", t2tmp, rxy_vir, l2tmp) / 4 + tmp += einsum("(xYba,aA->xYbA),YxAB->bB", t2tmp, rxy_vir, l2tmp) / 4 else: - tmp += einsum('(xYab,aS->xYSb),(SA,YxBA->YxBS)->bB', t2tmp, uxy_vir, vxy_vir, l2tmp)/4 - tmp += einsum('(xYba,aS->xYbS),(SA,YxAB->YxSB)->bB', t2tmp, uxy_vir, vxy_vir, l2tmp)/4 + tmp += einsum("(xYab,aS->xYSb),(SA,YxBA->YxBS)->bB", t2tmp, uxy_vir, vxy_vir, l2tmp) / 4 + tmp += einsum("(xYba,aS->xYbS),(SA,YxAB->YxSB)->bB", t2tmp, uxy_vir, vxy_vir, l2tmp) / 4 dvvx += np.dot(tmp, cy_vir.T) doo += np.dot(cx_occ, doox) @@ -392,7 +408,8 @@ def svd(a): dvvx = dot(dvvx, emb.mo_coeff_vir.T) # Loop over symmetry children of x: for fx2, (cx2_occ, cx2_vir, doox2, dvvx2) in fx.loop_symmetry_children( - (fx.cluster.c_occ, fx.cluster.c_vir, doox, dvvx), axes=[0,0,1,1]): + (fx.cluster.c_occ, fx.cluster.c_vir, doox, dvvx), axes=[0, 0, 1, 1] + ): doo += dot(cs_occ, cx2_occ, doox2, cs_occ.T) dvv += dot(cs_vir, cx2_vir, dvvx2, cs_vir.T) @@ -400,16 +417,17 @@ def svd(a): if with_t1: l1x = dot(cx_occ.T, l1, cx_vir) if not late_t2_sym: - dovx = einsum('ijab,jb->ia', theta, l1x) + dovx = einsum("ijab,jb->ia", theta, l1x) else: - dovx1 = einsum('xjab,jb->xa', theta, l1x)/2 - dovx2 = einsum('xiba,(jx,jb->xb)->ia', theta, cfx, l1x)/2 + dovx1 = einsum("xjab,jb->xa", theta, l1x) / 2 + dovx2 = einsum("xiba,(jx,jb->xb)->ia", theta, cfx, l1x) / 2 for fx2, (cx2_frag, cx2_occ, cx2_vir) in fx.loop_symmetry_children( - (fx.c_frag, fx.cluster.c_occ, fx.cluster.c_vir), include_self=True, maxgen=maxgen): + (fx.c_frag, fx.cluster.c_occ, fx.cluster.c_vir), include_self=True, maxgen=maxgen + ): cx2_occ = np.dot(cs_occ, cx2_occ) cx2_vir = np.dot(cs_vir, cx2_vir) if not late_t2_sym: - dov += einsum('ia,Ii,Aa->IA', dovx, cx2_occ, cx2_vir) + dov += einsum("ia,Ii,Aa->IA", dovx, cx2_occ, cx2_vir) else: dov += dot(cs_occ, cx2_frag, dovx1, cx2_vir.T) dov += dot(cx2_occ, dovx2, cx2_vir.T) @@ -422,24 +440,24 @@ def svd(a): return None if with_t1: - dov += (t1 + l1 - einsum('ie,me,ma->ia', t1, l1, t1)) - dov += einsum('im,ma->ia', doo, t1) - dov -= einsum('ie,ae->ia', t1, dvv) - doo -= einsum('ja,ia->ij', t1, l1) - dvv += einsum('ia,ib->ab', t1, l1) + dov += t1 + l1 - einsum("ie,me,ma->ia", t1, l1, t1) + dov += einsum("im,ma->ia", doo, t1) + dov -= einsum("ie,ae->ia", t1, dvv) + doo -= einsum("ja,ia->ij", t1, l1) + dvv += einsum("ia,ib->ab", t1, l1) # --- Combine full DM: - occ, vir = np.s_[:emb.nocc], np.s_[emb.nocc:] + occ, vir = np.s_[: emb.nocc], np.s_[emb.nocc :] dm1 = np.zeros((emb.nmo, emb.nmo)) - dm1[occ,occ] = (doo + doo.T) - dm1[vir,vir] = (dvv + dvv.T) + dm1[occ, occ] = doo + doo.T + dm1[vir, vir] = dvv + dvv.T if with_t1: - dm1[occ,vir] = dov - dm1[vir,occ] = dov.T + dm1[occ, vir] = dov + dm1[vir, occ] = dov.T # --- Some information: - emb.log.debug("Cluster-pairs: total= %d kept= %d (%.1f%%)", total_xy, kept_xy, 100*kept_xy/total_xy) - emb.log.debug("Singular values: total= %d kept= %d (%.1f%%)", total_sv, kept_sv, 100*kept_sv/total_sv) + emb.log.debug("Cluster-pairs: total= %d kept= %d (%.1f%%)", total_xy, kept_xy, 100 * kept_xy / total_xy) + emb.log.debug("Singular values: total= %d kept= %d (%.1f%%)", total_sv, kept_sv, 100 * kept_sv / total_sv) return dm1 @@ -447,6 +465,7 @@ def svd(a): # --- Two-particle # ---------------- + def make_rdm2_ccsd_global_wf(emb, ao_basis=False, symmetrize=True, t_as_lambda=None, slow=True, with_dm1=True): """Recreate global two-particle reduced density-matrix from fragment calculations. @@ -468,25 +487,34 @@ def make_rdm2_ccsd_global_wf(emb, ao_basis=False, symmetrize=True, t_as_lambda=N Two-particle reduced density matrix in AO (if `ao_basis=True`) or MO basis (default). """ if slow: - if with_dm1 and np.any([(x.solver == 'MP2') for x in emb.get_fragments(contributes=True)]): + if with_dm1 and np.any([(x.solver == "MP2") for x in emb.get_fragments(contributes=True)]): raise NotImplementedError t1 = emb.get_global_t1(for_dm2=True) t2 = emb.get_global_t2(for_dm2=True) l1 = emb.get_global_l1(for_dm2=True) if not t_as_lambda else t1 l2 = emb.get_global_l2(for_dm2=True) if not t_as_lambda else t2 mockcc = _get_mockcc(emb.mo_coeff, emb.mf.max_memory) - #dm2 = pyscf.cc.ccsd_rdm.make_rdm2(mockcc, t1=t1, t2=t2, l1=l1, l2=l2, with_frozen=False, with_dm1=with_dm1) + # dm2 = pyscf.cc.ccsd_rdm.make_rdm2(mockcc, t1=t1, t2=t2, l1=l1, l2=l2, with_frozen=False, with_dm1=with_dm1) dm2 = ccsd_rdm.make_rdm2(mockcc, t1=t1, t2=t2, l1=l1, l2=l2, with_frozen=False, with_dm1=with_dm1) else: raise NotImplementedError if symmetrize: - dm2 = (dm2 + dm2.transpose(1,0,3,2))/2 + dm2 = (dm2 + dm2.transpose(1, 0, 3, 2)) / 2 if ao_basis: - dm2 = einsum('ijkl,pi,qj,rk,sl->pqrs', dm2, *(4*[emb.mo_coeff])) + dm2 = einsum("ijkl,pi,qj,rk,sl->pqrs", dm2, *(4 * [emb.mo_coeff])) return dm2 -def make_rdm2_ccsd_proj_lambda(emb, with_dm1=True, ao_basis=False, t_as_lambda=None, sym_t2=True, sym_dm2=True, - approx_cumulant=True, mpi_target=None): + +def make_rdm2_ccsd_proj_lambda( + emb, + with_dm1=True, + ao_basis=False, + t_as_lambda=None, + sym_t2=True, + sym_dm2=True, + approx_cumulant=True, + mpi_target=None, +): """Make two-particle reduced density-matrix from partitioned fragment CCSD wave functions. Without 1DM! @@ -514,9 +542,9 @@ def make_rdm2_ccsd_proj_lambda(emb, with_dm1=True, ao_basis=False, t_as_lambda=N dm2 = np.zeros((emb.nmo, emb.nmo, emb.nmo, emb.nmo)) ovlp = emb.get_ovlp() for x in emb.get_fragments(contributes=True, mpi_rank=mpi.rank): - rx = x.get_overlap('mo|cluster') + rx = x.get_overlap("mo|cluster") dm2x = x.make_fragment_dm2cumulant(t_as_lambda=t_as_lambda, sym_t2=sym_t2, approx_cumulant=approx_cumulant) - dm2 += einsum('ijkl,Ii,Jj,Kk,Ll->IJKL', dm2x, rx, rx, rx, rx) + dm2 += einsum("ijkl,Ii,Jj,Kk,Ll->IJKL", dm2x, rx, rx, rx, rx) if mpi: dm2 = mpi.nreduce(dm2, target=mpi_target, logfunc=emb.log.timingv) if mpi_target not in (None, mpi.rank): @@ -527,22 +555,22 @@ def make_rdm2_ccsd_proj_lambda(emb, with_dm1=True, ao_basis=False, t_as_lambda=N else: dm1 = with_dm1 if not approx_cumulant: - dm2 += (einsum('ij,kl->ijkl', dm1, dm1) - einsum('ij,kl->iklj', dm1, dm1)/2) + dm2 += einsum("ij,kl->ijkl", dm1, dm1) - einsum("ij,kl->iklj", dm1, dm1) / 2 # Remove half of the mean-field contribution # (in PySCF the entire MF is removed and afterwards half is added back in a (i,j) loop) - elif (approx_cumulant in (1, True)): + elif approx_cumulant in (1, True): dm1 = dm1.copy() dm1[np.diag_indices(emb.nocc)] -= 1 for i in range(emb.nocc): - dm2[i,i,:,:] += dm1 * 2 - dm2[:,:,i,i] += dm1 * 2 - dm2[:,i,i,:] -= dm1 - dm2[i,:,:,i] -= dm1.T + dm2[i, i, :, :] += dm1 * 2 + dm2[:, :, i, i] += dm1 * 2 + dm2[:, i, i, :] -= dm1 + dm2[i, :, :, i] -= dm1.T elif approx_cumulant == 2: raise NotImplementedError else: raise ValueError if ao_basis: - dm2 = einsum('ijkl,pi,qj,rk,sl->pqrs', dm2, *(4*[emb.mo_coeff])) + dm2 = einsum("ijkl,pi,qj,rk,sl->pqrs", dm2, *(4 * [emb.mo_coeff])) return dm2 diff --git a/vayesta/ewf/uewf.py b/vayesta/ewf/uewf.py index 696bac58e..17c87c90b 100644 --- a/vayesta/ewf/uewf.py +++ b/vayesta/ewf/uewf.py @@ -11,6 +11,7 @@ # Amplitudes from vayesta.ewf.amplitudes import get_global_t1_uhf from vayesta.ewf.amplitudes import get_global_t2_uhf + # Density-matrices from vayesta.ewf.urdm import make_rdm1_ccsd from vayesta.ewf.urdm import make_rdm1_ccsd_global_wf @@ -20,7 +21,6 @@ class UEWF(REWF, UEmbedding): - Fragment = Fragment # --- CC Amplitudes @@ -38,18 +38,16 @@ def t1_diagnostic(self, warn_tol=0.02): self.log.error("No T1 amplitudes found for %s.", f) continue nelec = t1[0].shape[0] + t1[1].shape[0] - t1diag = (np.linalg.norm(t1[0]) / np.sqrt(nelec), - np.linalg.norm(t1[1]) / np.sqrt(nelec)) + t1diag = (np.linalg.norm(t1[0]) / np.sqrt(nelec), np.linalg.norm(t1[1]) / np.sqrt(nelec)) if max(t1diag) > warn_tol: - self.log.warning("T1 diagnostic for %-20s alpha= %.5f beta= %.5f", str(f)+':', *t1diag) + self.log.warning("T1 diagnostic for %-20s alpha= %.5f beta= %.5f", str(f) + ":", *t1diag) else: - self.log.info("T1 diagnostic for %-20s alpha= %.5f beta= %.5f", str(f)+':', *t1diag) + self.log.info("T1 diagnostic for %-20s alpha= %.5f beta= %.5f", str(f) + ":", *t1diag) # Global t1 = self.get_global_t1(mpi_target=0) if mpi.is_master: nelec = t1[0].shape[0] + t1[1].shape[0] - t1diag = (np.linalg.norm(t1[0]) / np.sqrt(nelec), - np.linalg.norm(t1[1]) / np.sqrt(nelec)) + t1diag = (np.linalg.norm(t1[0]) / np.sqrt(nelec), np.linalg.norm(t1[1]) / np.sqrt(nelec)) if max(t1diag) > warn_tol: self.log.warning("Global T1 diagnostic: alpha= %.5f beta= %.5f", *t1diag) else: @@ -136,21 +134,23 @@ def _get_dm_corr_energy_old(self, global_dm1=True, global_dm2=False, t_as_lambda # --- Core Hamiltonian + Non-cumulant 2DM contribution fa, fb = self.get_fock_for_energy(with_exxdiv=False) - e1 = (einsum('pi,pq,qj,ij->', self.mo_coeff[0], fa, self.mo_coeff[0], dm1a) - + einsum('pi,pq,qj,ij->', self.mo_coeff[1], fb, self.mo_coeff[1], dm1b))/self.ncells + e1 = ( + einsum("pi,pq,qj,ij->", self.mo_coeff[0], fa, self.mo_coeff[0], dm1a) + + einsum("pi,pq,qj,ij->", self.mo_coeff[1], fb, self.mo_coeff[1], dm1b) + ) / self.ncells # --- Cumulant 2-DM contribution # Use global 2-DM if global_dm2: dm2aa, dm2ab, dm2bb = self._make_rdm2_ccsd_global_wf(t_as_lambda=t_as_lambda, with_dm1=False) eriaa = self.get_eris_array(self.mo_coeff[0]) - e2 = einsum('pqrs,pqrs', eriaa, dm2aa) / 2 - eriab = self.get_eris_array(2*[self.mo_coeff[0]] + 2*[self.mo_coeff[1]]) - e2 += einsum('pqrs,pqrs', eriab, dm2ab) + e2 = einsum("pqrs,pqrs", eriaa, dm2aa) / 2 + eriab = self.get_eris_array(2 * [self.mo_coeff[0]] + 2 * [self.mo_coeff[1]]) + e2 += einsum("pqrs,pqrs", eriab, dm2ab) eribb = self.get_eris_array(self.mo_coeff[1]) - e2 += einsum('pqrs,pqrs', eribb, dm2bb) / 2 + e2 += einsum("pqrs,pqrs", eribb, dm2bb) / 2 # Use fragment-local 2-DM else: e2 = self.get_dm_corr_energy_e2(t_as_lambda=t_as_lambda) - e_corr = (e1 + e2) + e_corr = e1 + e2 return e_corr diff --git a/vayesta/ewf/ufragment.py b/vayesta/ewf/ufragment.py index 6d4fb381d..8b57f61f0 100644 --- a/vayesta/ewf/ufragment.py +++ b/vayesta/ewf/ufragment.py @@ -10,17 +10,16 @@ class Fragment(RFragment, BaseFragment): - - def set_cas(self, iaos=None, c_occ=None, c_vir=None, minao='auto', dmet_threshold=None): + def set_cas(self, iaos=None, c_occ=None, c_vir=None, minao="auto", dmet_threshold=None): """Set complete active space for tailored CCSD and active-space CC methods.""" if iaos is not None: - raise NotImplementedError("Unrestricted IAO-based CAS not implemented yet.") + raise NotImplementedError("Unrestricted IAO-based CAS not implemented yet.") self.opts.c_cas_occ = c_occ self.opts.c_cas_vir = c_vir return c_occ, c_vir - def get_fragment_energy(self, c1, c2, hamil=None, fock=None, axis1='fragment', c2ba_order='ba'): + def get_fragment_energy(self, c1, c2, hamil=None, fock=None, axis1="fragment", c2ba_order="ba"): """Calculate fragment correlation energy contribution from projected C1, C2. Parameters @@ -47,17 +46,17 @@ def get_fragment_energy(self, c1, c2, hamil=None, fock=None, axis1='fragment', c nocc = (c2[0].shape[1], c2[-1].shape[1]) nvir = (c2[0].shape[2], c2[-1].shape[2]) self.log.debugv("nocc= %d, %d nvir= %d, %d", *nocc, *nvir) - oa, ob = np.s_[:nocc[0]], np.s_[:nocc[1]] - va, vb = np.s_[nocc[0]:], np.s_[nocc[1]:] - if axis1 == 'fragment': - pxa, pxb = self.get_overlap('proj|cluster-occ') + oa, ob = np.s_[: nocc[0]], np.s_[: nocc[1]] + va, vb = np.s_[nocc[0] :], np.s_[nocc[1] :] + if axis1 == "fragment": + pxa, pxb = self.get_overlap("proj|cluster-occ") # --- Singles energy (zero for HF-reference) if c1 is not None: - #if hasattr(eris, 'fock'): + # if hasattr(eris, 'fock'): # fa = eris.fock[0][oa,va] # fb = eris.fock[1][ob,vb] - #else: + # else: # fock = self.base.get_fock() # fa = dot(self.c_active_occ[0].T, fock[0], self.c_active_vir[0]) # fb = dot(self.c_active_occ[1].T, fock[1], self.c_active_vir[1]) @@ -65,13 +64,12 @@ def get_fragment_energy(self, c1, c2, hamil=None, fock=None, axis1='fragment', c fock = self.base.get_fock_for_energy() fova = dot(self.cluster.c_active_occ[0].T, fock[0], self.cluster.c_active_vir[0]) fovb = dot(self.cluster.c_active_occ[1].T, fock[1], self.cluster.c_active_vir[1]) - assert (len(c1) == 2) + assert len(c1) == 2 ca, cb = c1 - if axis1 == 'fragment': - e_singles = (einsum('ia,xi,xa->', fova, pxa, ca) - + einsum('ia,xi,xa->', fovb, pxb, cb)) + if axis1 == "fragment": + e_singles = einsum("ia,xi,xa->", fova, pxa, ca) + einsum("ia,xi,xa->", fovb, pxb, cb) else: - e_singles = np.sum(fova*ca) + np.sum(fovb*cb) + e_singles = np.sum(fova * ca) + np.sum(fovb * cb) else: e_singles = 0 # Doubles energy @@ -82,29 +80,33 @@ def get_fragment_energy(self, c1, c2, hamil=None, fock=None, axis1='fragment', c gab = hamil.get_eris_bare(block="ovOV") gbb = hamil.get_eris_bare(block="OVOV") - if axis1 == 'fragment': + if axis1 == "fragment": assert len(c2) == 4 caa, cab, cba, cbb = c2 - if c2ba_order == 'ab': - cba = cba.transpose(1,0,3,2) - e_doubles = (einsum('xi,xjab,iajb', pxa, caa, gaa)/4 - - einsum('xi,xjab,ibja', pxa, caa, gaa)/4 - + einsum('xi,xjab,iajb', pxb, cbb, gbb)/4 - - einsum('xi,xjab,ibja', pxb, cbb, gbb)/4 - + einsum('xi,xjab,iajb', pxa, cab, gab)/2 - + einsum('xi,xjab,jbia', pxb, cba, gab)/2) + if c2ba_order == "ab": + cba = cba.transpose(1, 0, 3, 2) + e_doubles = ( + einsum("xi,xjab,iajb", pxa, caa, gaa) / 4 + - einsum("xi,xjab,ibja", pxa, caa, gaa) / 4 + + einsum("xi,xjab,iajb", pxb, cbb, gbb) / 4 + - einsum("xi,xjab,ibja", pxb, cbb, gbb) / 4 + + einsum("xi,xjab,iajb", pxa, cab, gab) / 2 + + einsum("xi,xjab,jbia", pxb, cba, gab) / 2 + ) else: assert len(c2) == 3 caa, cab, cbb = c2 - e_doubles = (einsum('ijab,iajb', caa, gaa)/4 - - einsum('ijab,ibja', caa, gaa)/4 - + einsum('ijab,iajb', cbb, gbb)/4 - - einsum('ijab,ibja', cbb, gbb)/4 - + einsum('ijab,iajb', cab, gab)) - - e_singles = (self.sym_factor * e_singles) - e_doubles = (self.sym_factor * e_doubles) - e_corr = (e_singles + e_doubles) + e_doubles = ( + einsum("ijab,iajb", caa, gaa) / 4 + - einsum("ijab,ibja", caa, gaa) / 4 + + einsum("ijab,iajb", cbb, gbb) / 4 + - einsum("ijab,ibja", cbb, gbb) / 4 + + einsum("ijab,iajb", cab, gab) + ) + + e_singles = self.sym_factor * e_singles + e_doubles = self.sym_factor * e_doubles + e_corr = e_singles + e_doubles return e_singles, e_doubles, e_corr @with_doc(RFragment._get_projected_gamma1_intermediates) @@ -115,21 +117,24 @@ def _get_projected_gamma1_intermediates(self, t_as_lambda=None, sym_t2=True): def _get_projected_gamma2_intermediates(self, t_as_lambda=None, sym_t2=True): t1, t2, l1, l2, t1x, t2x, l1x, l2x = self._ccsd_amplitudes_for_dm(t_as_lambda=t_as_lambda, sym_t2=sym_t2) # Only incore for UCCSD: - #d2 = pyscf.cc.uccsd_rdm._gamma2_intermediates(None, t1, t2, l1x, l2x) + # d2 = pyscf.cc.uccsd_rdm._gamma2_intermediates(None, t1, t2, l1x, l2x) d2ovov, *d2 = pyscf.cc.uccsd_rdm._gamma2_intermediates(None, t1, t2, l1x, l2x) # Correction of unprojected terms (which do not involve L1/L2): # dovov: - dtau = (t2x[0]-t2[0] + einsum('ia,jb->ijab', t1x[0]-t1[0], 2*t1[0]))/4 - d2ovov[0][:] += dtau.transpose(0,2,1,3) - d2ovov[0][:] -= dtau.transpose(0,3,1,2) + dtau = (t2x[0] - t2[0] + einsum("ia,jb->ijab", t1x[0] - t1[0], 2 * t1[0])) / 4 + d2ovov[0][:] += dtau.transpose(0, 2, 1, 3) + d2ovov[0][:] -= dtau.transpose(0, 3, 1, 2) # dovOV (symmetrize between t1x[0] and t1x[1]; t2x[1] should already be symmetrized): - dtau = ((t2x[1]-t2[1]) + einsum('ia,jb->ijab', t1x[0]-t1[0], t1[1]/2) - + einsum('ia,jb->ijab', t1[0]/2, t1x[1]-t1[1]))/2 - d2ovov[1][:] += dtau.transpose(0,2,1,3) + dtau = ( + (t2x[1] - t2[1]) + + einsum("ia,jb->ijab", t1x[0] - t1[0], t1[1] / 2) + + einsum("ia,jb->ijab", t1[0] / 2, t1x[1] - t1[1]) + ) / 2 + d2ovov[1][:] += dtau.transpose(0, 2, 1, 3) # dOVOV: - dtau = (t2x[2]-t2[2] + einsum('ia,jb->ijab', t1x[1]-t1[1], 2*t1[1]))/4 - d2ovov[3][:] += dtau.transpose(0,2,1,3) - d2ovov[3][:] -= dtau.transpose(0,3,1,2) + dtau = (t2x[2] - t2[2] + einsum("ia,jb->ijab", t1x[1] - t1[1], 2 * t1[1])) / 4 + d2ovov[3][:] += dtau.transpose(0, 2, 1, 3) + d2ovov[3][:] -= dtau.transpose(0, 3, 1, 2) d2 = (d2ovov, *d2) return d2 @@ -137,11 +142,11 @@ def make_fragment_dm2cumulant(self, t_as_lambda=None, sym_t2=True, approx_cumula if int(approx_cumulant) != 1: raise NotImplementedError - if self.solver == 'MP2': + if self.solver == "MP2": t2xaa, t2xab, t2xbb = self.results.pwf.restore(sym=sym_t2).as_ccsd().t2 - dovov = t2xaa.transpose(0,2,1,3) - dovOV = t2xab.transpose(0,2,1,3) - dOVOV = t2xbb.transpose(0,2,1,3) + dovov = t2xaa.transpose(0, 2, 1, 3) + dovOV = t2xab.transpose(0, 2, 1, 3) + dOVOV = t2xbb.transpose(0, 2, 1, 3) if not full_shape: return (dovov, dovOV, dOVOV) nocca, nvira, noccb, nvirb = dovOV.shape @@ -149,15 +154,15 @@ def make_fragment_dm2cumulant(self, t_as_lambda=None, sym_t2=True, approx_cumula norbb = noccb + nvirb oa, va = np.s_[:nocca], np.s_[nocca:] ob, vb = np.s_[:noccb], np.s_[noccb:] - dm2aa = np.zeros(4*[norba]) - dm2ab = np.zeros(2*[norba] + 2*[norbb]) - dm2bb = np.zeros(4*[norbb]) - dm2aa[oa,va,oa,va] = dovov - dm2aa[va,oa,va,oa] = dovov.transpose(1,0,3,2) - dm2ab[oa,va,ob,vb] = dovOV - dm2ab[va,oa,vb,ob] = dovOV.transpose(1,0,3,2) - dm2bb[ob,vb,ob,vb] = dOVOV - dm2bb[vb,ob,vb,ob] = dOVOV.transpose(1,0,3,2) + dm2aa = np.zeros(4 * [norba]) + dm2ab = np.zeros(2 * [norba] + 2 * [norbb]) + dm2bb = np.zeros(4 * [norbb]) + dm2aa[oa, va, oa, va] = dovov + dm2aa[va, oa, va, oa] = dovov.transpose(1, 0, 3, 2) + dm2ab[oa, va, ob, vb] = dovOV + dm2ab[va, oa, vb, ob] = dovOV.transpose(1, 0, 3, 2) + dm2bb[ob, vb, ob, vb] = dOVOV + dm2bb[vb, ob, vb, ob] = dOVOV.transpose(1, 0, 3, 2) return (dm2aa, dm2ab, dm2bb) cc = d1 = None @@ -170,25 +175,35 @@ def make_fragment_dm2cumulant_energy(self, hamil=None, t_as_lambda=None, sym_t2= if hamil is None: hamil = self.hamil if self.solver == "MP2": - dm2 = self.make_fragment_dm2cumulant(t_as_lambda=t_as_lambda, sym_t2=sym_t2, - approx_cumulant=approx_cumulant, full_shape=False) + dm2 = self.make_fragment_dm2cumulant( + t_as_lambda=t_as_lambda, sym_t2=sym_t2, approx_cumulant=approx_cumulant, full_shape=False + ) dm2aa, dm2ab, dm2bb = dm2 gaa = hamil.get_eris_bare(block="ovov") gab = hamil.get_eris_bare(block="ovOV") gbb = hamil.get_eris_bare(block="OVOV") - return 2.0 * (einsum('ijkl,ijkl->', gaa, dm2aa) - + einsum('ijkl,ijkl->', gab, dm2ab) * 2 - + einsum('ijkl,ijkl->', gbb, dm2bb)) / 2 + return ( + 2.0 + * ( + einsum("ijkl,ijkl->", gaa, dm2aa) + + einsum("ijkl,ijkl->", gab, dm2ab) * 2 + + einsum("ijkl,ijkl->", gbb, dm2bb) + ) + / 2 + ) elif approx_cumulant: # Working hypothesis: this branch will effectively always uses `approx_cumulant=True`. eris = hamil.get_dummy_eri_object(force_bare=True, with_vext=False) d2 = self._get_projected_gamma2_intermediates(t_as_lambda=t_as_lambda, sym_t2=sym_t2) - return vayesta.core.ao2mo.helper.contract_dm2intermeds_eris_uhf(d2, eris)/2 + return vayesta.core.ao2mo.helper.contract_dm2intermeds_eris_uhf(d2, eris) / 2 else: - dm2 = self.make_fragment_dm2cumulant(t_as_lambda=t_as_lambda, sym_t2=sym_t2, - approx_cumulant=approx_cumulant, full_shape=True) + dm2 = self.make_fragment_dm2cumulant( + t_as_lambda=t_as_lambda, sym_t2=sym_t2, approx_cumulant=approx_cumulant, full_shape=True + ) dm2aa, dm2ab, dm2bb = dm2 gaa, gab, gbb = hamil.get_eris_bare() - return (einsum('ijkl,ijkl->', gaa, dm2aa) - + einsum('ijkl,ijkl->', gab, dm2ab)*2 - + einsum('ijkl,ijkl->', gbb, dm2bb))/2 + return ( + einsum("ijkl,ijkl->", gaa, dm2aa) + + einsum("ijkl,ijkl->", gab, dm2ab) * 2 + + einsum("ijkl,ijkl->", gbb, dm2bb) + ) / 2 diff --git a/vayesta/ewf/urdm.py b/vayesta/ewf/urdm.py index 968474712..3577f8be7 100644 --- a/vayesta/ewf/urdm.py +++ b/vayesta/ewf/urdm.py @@ -9,8 +9,9 @@ from vayesta.mpi import mpi -def make_rdm1_ccsd(emb, ao_basis=False, t_as_lambda=None, symmetrize=True, with_mf=True, mpi_target=None, mp2=False, - ba_order='ba'): +def make_rdm1_ccsd( + emb, ao_basis=False, t_as_lambda=None, symmetrize=True, with_mf=True, mpi_target=None, mp2=False, ba_order="ba" +): """Make one-particle reduced density-matrix from partitioned fragment CCSD wave functions. MPI parallelized. @@ -59,66 +60,66 @@ def make_rdm1_ccsd(emb, ao_basis=False, t_as_lambda=None, symmetrize=True, with_ wfx = frag.results.pwf.as_ccsd() t2xaa, t2xab, t2xba, t2xbb = wfx.t2 l2xaa, l2xab, l2xba, l2xbb = wfx.l2 if not t_as_lambda else wfx.t2 - if ba_order == 'ab': - t2xba = t2xba.transpose(1,0,3,2) - l2xba = l2xba.transpose(1,0,3,2) + if ba_order == "ab": + t2xba = t2xba.transpose(1, 0, 3, 2) + l2xba = l2xba.transpose(1, 0, 3, 2) # Mean-field to cluster (occupied/virtual): - coa, cob = frag.get_overlap('mo[occ]|cluster[occ]') - cva, cvb = frag.get_overlap('mo[vir]|cluster[vir]') + coa, cob = frag.get_overlap("mo[occ]|cluster[occ]") + cva, cvb = frag.get_overlap("mo[vir]|cluster[vir]") # Mean-field to fragment (occupied): - foa, fob = frag.get_overlap('mo[occ]|frag') + foa, fob = frag.get_overlap("mo[occ]|frag") # D(occ,occ) and D(vir,vir) # aa/bb -> dooa/doob - dooa -= einsum('kiab,kjab,Ii,Jj->IJ', l2xaa, t2xaa, coa, coa) / 2 - doob -= einsum('kiab,kjab,Ii,Jj->IJ', l2xbb, t2xbb, cob, cob) / 2 + dooa -= einsum("kiab,kjab,Ii,Jj->IJ", l2xaa, t2xaa, coa, coa) / 2 + doob -= einsum("kiab,kjab,Ii,Jj->IJ", l2xbb, t2xbb, cob, cob) / 2 # ba/ab -> dooa/doob - dooa -= einsum('kiab,kjab,Ii,Jj->IJ', l2xba, t2xba, coa, coa) - doob -= einsum('kiab,kjab,Ii,Jj->IJ', l2xab, t2xab, cob, cob) + dooa -= einsum("kiab,kjab,Ii,Jj->IJ", l2xba, t2xba, coa, coa) + doob -= einsum("kiab,kjab,Ii,Jj->IJ", l2xab, t2xab, cob, cob) # aa/bb - > dvva/dvvb - dvva += einsum('ijac,ijbc,Aa,Bb->AB', t2xaa, l2xaa, cva, cva) / 2 - dvvb += einsum('ijac,ijbc,Aa,Bb->AB', t2xbb, l2xbb, cvb, cvb) / 2 + dvva += einsum("ijac,ijbc,Aa,Bb->AB", t2xaa, l2xaa, cva, cva) / 2 + dvvb += einsum("ijac,ijbc,Aa,Bb->AB", t2xbb, l2xbb, cvb, cvb) / 2 # We cannot symmetrize the ba/ab -> dooa/doob part between t/l2xab and t/l2xba (and keep a single fragment loop); # instead dooa only uses the "ba" amplitudes and doob only the "ab" amplitudes. # In order to ensure the correct number of alpha and beta electrons, we should use the same choice for the # virtual-virtual parts (even though here we could symmetrize them as: - #dvva += einsum('ijac,ijbc,Aa,Bb->AB', t2xba, l2xba, cva, cva) / 2 - #dvva += einsum('ijac,ijbc,Aa,Bb->AB', t2xab, l2xab, cva, cva) / 2 - #dvvb += einsum('ijca,ijcb,Aa,Bb->AB', t2xab, l2xab, cvb, cvb) / 2 - #dvvb += einsum('ijca,ijcb,Aa,Bb->AB', t2xba, l2xba, cvb, cvb) / 2 + # dvva += einsum('ijac,ijbc,Aa,Bb->AB', t2xba, l2xba, cva, cva) / 2 + # dvva += einsum('ijac,ijbc,Aa,Bb->AB', t2xab, l2xab, cva, cva) / 2 + # dvvb += einsum('ijca,ijcb,Aa,Bb->AB', t2xab, l2xab, cvb, cvb) / 2 + # dvvb += einsum('ijca,ijcb,Aa,Bb->AB', t2xba, l2xba, cvb, cvb) / 2 # ba/ab -> dooa/doob - dvva += einsum('ijca,ijcb,Aa,Bb->AB', t2xba, l2xba, cva, cva) - dvvb += einsum('ijca,ijcb,Aa,Bb->AB', t2xab, l2xab, cvb, cvb) + dvva += einsum("ijca,ijcb,Aa,Bb->AB", t2xba, l2xba, cva, cva) + dvvb += einsum("ijca,ijcb,Aa,Bb->AB", t2xab, l2xab, cvb, cvb) # D(occ,vir) if not mp2: if not symmetrize: # aa/bb - dova += einsum('ijab,Ii,Jj,Aa,Bb,JB->IA', t2xaa, foa, coa, cva, cva, l1a) - dovb += einsum('ijab,Ii,Jj,Aa,Bb,JB->IA', t2xbb, fob, cob, cvb, cvb, l1b) + dova += einsum("ijab,Ii,Jj,Aa,Bb,JB->IA", t2xaa, foa, coa, cva, cva, l1a) + dovb += einsum("ijab,Ii,Jj,Aa,Bb,JB->IA", t2xbb, fob, cob, cvb, cvb, l1b) # ab/ba - dova += einsum('ijab,Ii,Jj,Aa,Bb,JB->IA', t2xab, foa, cob, cva, cvb, l1b) - dovb += einsum('ijab,Ii,Jj,Aa,Bb,JB->IA', t2xba, fob, coa, cvb, cva, l1a) + dova += einsum("ijab,Ii,Jj,Aa,Bb,JB->IA", t2xab, foa, cob, cva, cvb, l1b) + dovb += einsum("ijab,Ii,Jj,Aa,Bb,JB->IA", t2xba, fob, coa, cvb, cva, l1a) else: # aa/bb - dova += einsum('ijab,Ii,Jj,Aa,Bb,JB->IA', t2xaa, foa, coa, cva, cva, l1a) / 2 - dova += einsum('jiba,Jj,Ii,Aa,Bb,JB->IA', t2xaa, foa, coa, cva, cva, l1a) / 2 - dovb += einsum('ijab,Ii,Jj,Aa,Bb,JB->IA', t2xbb, fob, cob, cvb, cvb, l1b) / 2 - dovb += einsum('jiba,Jj,Ii,Aa,Bb,JB->IA', t2xbb, fob, cob, cvb, cvb, l1b) / 2 + dova += einsum("ijab,Ii,Jj,Aa,Bb,JB->IA", t2xaa, foa, coa, cva, cva, l1a) / 2 + dova += einsum("jiba,Jj,Ii,Aa,Bb,JB->IA", t2xaa, foa, coa, cva, cva, l1a) / 2 + dovb += einsum("ijab,Ii,Jj,Aa,Bb,JB->IA", t2xbb, fob, cob, cvb, cvb, l1b) / 2 + dovb += einsum("jiba,Jj,Ii,Aa,Bb,JB->IA", t2xbb, fob, cob, cvb, cvb, l1b) / 2 # ab/baAA (here we can use t2xab and t2xba in a symmetric fashion: - dova += einsum('ijab,Ii,Jj,Aa,Bb,JB->IA', t2xab, foa, cob, cva, cvb, l1b) / 2 - dova += einsum('jiba,Jj,Ii,Aa,Bb,JB->IA', t2xba, fob, coa, cva, cvb, l1b) / 2 - dovb += einsum('ijab,Ii,Jj,Aa,Bb,JB->IA', t2xba, fob, coa, cvb, cva, l1a) / 2 - dovb += einsum('jiba,Jj,Ii,Aa,Bb,JB->IA', t2xab, foa, cob, cvb, cva, l1a) / 2 + dova += einsum("ijab,Ii,Jj,Aa,Bb,JB->IA", t2xab, foa, cob, cva, cvb, l1b) / 2 + dova += einsum("jiba,Jj,Ii,Aa,Bb,JB->IA", t2xba, fob, coa, cva, cvb, l1b) / 2 + dovb += einsum("ijab,Ii,Jj,Aa,Bb,JB->IA", t2xba, fob, coa, cvb, cva, l1a) / 2 + dovb += einsum("jiba,Jj,Ii,Aa,Bb,JB->IA", t2xab, foa, cob, cvb, cva, l1a) / 2 # MPI reduce here; the remaining terms involve L1/T1 only if mpi: if mp2: - dooa, doob, dvva, dvvb = mpi.nreduce( - dooa, doob, dvva, dvvb, target=mpi_target, logfunc=emb.log.timingv) + dooa, doob, dvva, dvvb = mpi.nreduce(dooa, doob, dvva, dvvb, target=mpi_target, logfunc=emb.log.timingv) else: dooa, doob, dova, dovb, dvva, dvvb = mpi.nreduce( - dooa, doob, dova, dovb, dvva, dvvb, target=mpi_target, logfunc=emb.log.timingv) + dooa, doob, dova, dovb, dvva, dvvb, target=mpi_target, logfunc=emb.log.timingv + ) if mpi_target not in (None, mpi.rank): return None @@ -126,39 +127,39 @@ def make_rdm1_ccsd(emb, ao_basis=False, t_as_lambda=None, symmetrize=True, with_ # as the t1*l1 term needs to be added to dvv first # Note the + sign as we use dooa/b, rather than xt1a/b (see PySCF) if not mp2: - dova += einsum('IJ,IA->JA', dooa, t1a) - dovb += einsum('IJ,IA->JA', doob, t1b) + dova += einsum("IJ,IA->JA", dooa, t1a) + dovb += einsum("IJ,IA->JA", doob, t1b) - dooa -= einsum('IA,JA->IJ', l1a, t1a) - doob -= einsum('IA,JA->IJ', l1b, t1b) - dvva += einsum('IA,IB->AB', t1a, l1a) - dvvb += einsum('IA,IB->AB', t1b, l1b) + dooa -= einsum("IA,JA->IJ", l1a, t1a) + doob -= einsum("IA,JA->IJ", l1b, t1b) + dvva += einsum("IA,IB->AB", t1a, l1a) + dvvb += einsum("IA,IB->AB", t1b, l1b) - dova -= einsum('IB,AB->IA', t1a, dvva) - dovb -= einsum('IB,AB->IA', t1b, dvvb) + dova -= einsum("IB,AB->IA", t1a, dvva) + dovb -= einsum("IB,AB->IA", t1b, dvvb) - dova += (t1a + l1a) - dovb += (t1b + l1b) + dova += t1a + l1a + dovb += t1b + l1b # Alpha occ, vir = np.s_[:nocca], np.s_[nocca:] - nmo = (nocca + nvira) + nmo = nocca + nvira dm1a = np.zeros((nmo, nmo)) - dm1a[occ,occ] = (dooa + dooa.T) - dm1a[vir,vir] = (dvva + dvva.T) + dm1a[occ, occ] = dooa + dooa.T + dm1a[vir, vir] = dvva + dvva.T if not mp2: - dm1a[occ,vir] = dova - dm1a[vir,occ] = dova.T + dm1a[occ, vir] = dova + dm1a[vir, occ] = dova.T dm1a /= 2.0 # Beta occ, vir = np.s_[:noccb], np.s_[noccb:] - nmo = (noccb + nvirb) + nmo = noccb + nvirb dm1b = np.zeros((nmo, nmo)) - dm1b[occ,occ] = (doob + doob.T) - dm1b[vir,vir] = (dvvb + dvvb.T) + dm1b[occ, occ] = doob + doob.T + dm1b[vir, vir] = dvvb + dvvb.T if not mp2: - dm1b[occ,vir] = dovb - dm1b[vir,occ] = dovb.T + dm1b[occ, vir] = dovb + dm1b[vir, occ] = dovb.T dm1b /= 2.0 if with_mf: @@ -170,8 +171,18 @@ def make_rdm1_ccsd(emb, ao_basis=False, t_as_lambda=None, symmetrize=True, with_ return (dm1a, dm1b) -def make_rdm1_ccsd_global_wf(emb, t_as_lambda=None, with_t1=True, svd_tol=1e-3, ovlp_tol=None, use_sym=True, - late_t2_sym=True, mpi_target=None, slow=False): + +def make_rdm1_ccsd_global_wf( + emb, + t_as_lambda=None, + with_t1=True, + svd_tol=1e-3, + ovlp_tol=None, + use_sym=True, + late_t2_sym=True, + mpi_target=None, + slow=False, +): """Make one-particle reduced density-matrix from partitioned fragment CCSD wave functions. NOT MPI READY @@ -201,7 +212,7 @@ def make_rdm1_ccsd_global_wf(emb, t_as_lambda=None, with_t1=True, svd_tol=1e-3, if not with_t1: t1 = l1 = (np.zeros_like(t1[0]), np.zeros_like(t1[1])) mockcc = _get_mockcc(emb.mo_coeff, emb.mf.max_memory) - #dm1 = pyscf.cc.uccsd_rdm.make_rdm1(mockcc, t1=t1, t2=t2, l1=l1, l2=l2, with_mf=False) + # dm1 = pyscf.cc.uccsd_rdm.make_rdm1(mockcc, t1=t1, t2=t2, l1=l1, l2=l2, with_mf=False) dm1a, dm1b = uccsd_rdm.make_rdm1(mockcc, t1=t1, t2=t2, l1=l1, l2=l2, with_mf=False) return dm1a, dm1b @@ -261,13 +272,13 @@ def make_rdm1_ccsd_global_wf(emb, t_as_lambda=None, with_t1=True, svd_tol=1e-3, dvoxa = np.zeros((x.cluster.nvir_active[0], x.cluster.nocc_active[0])) dvoxb = np.zeros((x.cluster.nvir_active[1], x.cluster.nocc_active[1])) - cx_occ_a, cx_occ_b = x.get_overlap('mo[occ]|cluster[occ]') - cx_vir_a, cx_vir_b = x.get_overlap('mo[vir]|cluster[vir]') + cx_occ_a, cx_occ_b = x.get_overlap("mo[occ]|cluster[occ]") + cx_vir_a, cx_vir_b = x.get_overlap("mo[vir]|cluster[vir]") # Loop over ALL fragments y: for y in emb.get_fragments(contributes=True): if mpi: - if y.solver == 'MP2': + if y.solver == "MP2": wfy = UMP2_WaveFunction.unpack(rma[y.id]).as_ccsd() else: wfy = UCCSD_WaveFunction.unpack(rma[y.id]) @@ -278,64 +289,63 @@ def make_rdm1_ccsd_global_wf(emb, t_as_lambda=None, with_t1=True, svd_tol=1e-3, # Constructing these overlap matrices scales as N(AO)^2, # however they are cashed and will only be calculated N(frag) times - cy_occ_a, cy_occ_b = y.get_overlap('mo[occ]|cluster[occ]') - cy_vir_a, cy_vir_b = y.get_overlap('mo[vir]|cluster[vir]') + cy_occ_a, cy_occ_b = y.get_overlap("mo[occ]|cluster[occ]") + cy_vir_a, cy_vir_b = y.get_overlap("mo[vir]|cluster[vir]") # Overlap between cluster-x and cluster-y: rxy_occ_a, rxy_occ_b = np.dot(cx_occ_a.T, cy_occ_a), np.dot(cx_occ_b.T, cy_occ_b) rxy_vir_a, rxy_vir_b = np.dot(cx_vir_a.T, cy_vir_a), np.dot(cx_vir_b.T, cy_vir_b) if svd_tol is not None: + def svd(a): nonlocal total_sv, kept_sv u, s, v = np.linalg.svd(a, full_matrices=False) if svd_tol is not None: - keep = (s >= svd_tol) + keep = s >= svd_tol total_sv += len(keep) kept_sv += sum(keep) - u, s, v = u[:,keep], s[keep], v[keep] + u, s, v = u[:, keep], s[keep], v[keep] return u, s, v uxy_occ_a, sxy_occ_a, vxy_occ_a = svd(rxy_occ_a) uxy_vir_a, sxy_vir_a, vxy_vir_a = svd(rxy_vir_a) - uxy_occ_a *= np.sqrt(sxy_occ_a)[np.newaxis,:] - uxy_vir_a *= np.sqrt(sxy_vir_a)[np.newaxis,:] - vxy_occ_a *= np.sqrt(sxy_occ_a)[:,np.newaxis] - vxy_vir_a *= np.sqrt(sxy_vir_a)[:,np.newaxis] + uxy_occ_a *= np.sqrt(sxy_occ_a)[np.newaxis, :] + uxy_vir_a *= np.sqrt(sxy_vir_a)[np.newaxis, :] + vxy_occ_a *= np.sqrt(sxy_occ_a)[:, np.newaxis] + vxy_vir_a *= np.sqrt(sxy_vir_a)[:, np.newaxis] uxy_occ_b, sxy_occ_b, vxy_occ_b = svd(rxy_occ_b) uxy_vir_b, sxy_vir_b, vxy_vir_b = svd(rxy_vir_b) - uxy_occ_b *= np.sqrt(sxy_occ_b)[np.newaxis,:] - uxy_vir_b *= np.sqrt(sxy_vir_b)[np.newaxis,:] - vxy_occ_b *= np.sqrt(sxy_occ_b)[:,np.newaxis] - vxy_vir_b *= np.sqrt(sxy_vir_b)[:,np.newaxis] + uxy_occ_b *= np.sqrt(sxy_occ_b)[np.newaxis, :] + uxy_vir_b *= np.sqrt(sxy_vir_b)[np.newaxis, :] + vxy_occ_b *= np.sqrt(sxy_occ_b)[:, np.newaxis] + vxy_vir_b *= np.sqrt(sxy_vir_b)[:, np.newaxis] else: - nsv = (min(rxy_occ_a.shape[0], rxy_occ_a.shape[1]) - + min(rxy_vir_a.shape[0], rxy_vir_a.shape[1])) - total_sv = kept_sv = (total_sv + nsv) - nsv = (min(rxy_occ_b.shape[0], rxy_occ_b.shape[1]) - + min(rxy_vir_b.shape[0], rxy_vir_b.shape[1])) - total_sv = kept_sv = (total_sv + nsv) + nsv = min(rxy_occ_a.shape[0], rxy_occ_a.shape[1]) + min(rxy_vir_a.shape[0], rxy_vir_a.shape[1]) + total_sv = kept_sv = total_sv + nsv + nsv = min(rxy_occ_b.shape[0], rxy_occ_b.shape[1]) + min(rxy_vir_b.shape[0], rxy_vir_b.shape[1]) + total_sv = kept_sv = total_sv + nsv # --- If ovlp_tol is given, the cluster x-y pairs will be screened based on the # largest singular value of the occupied and virtual overlap matrices total_xy += 1 if ovlp_tol is not None: if svd_tol: - rxy_occ_a_norm = (sxy_occ_a[0] if len(sxy_occ_a) > 0 else 0.0) - rxy_occ_b_norm = (sxy_occ_b[0] if len(sxy_occ_b) > 0 else 0.0) - rxy_vir_a_norm = (sxy_vir_a[0] if len(sxy_vir_a) > 0 else 0.0) - rxy_vir_b_norm = (sxy_vir_b[0] if len(sxy_vir_b) > 0 else 0.0) + rxy_occ_a_norm = sxy_occ_a[0] if len(sxy_occ_a) > 0 else 0.0 + rxy_occ_b_norm = sxy_occ_b[0] if len(sxy_occ_b) > 0 else 0.0 + rxy_vir_a_norm = sxy_vir_a[0] if len(sxy_vir_a) > 0 else 0.0 + rxy_vir_b_norm = sxy_vir_b[0] if len(sxy_vir_b) > 0 else 0.0 else: rxy_occ_a_norm = np.linalg.norm(rxy_occ_a, ord=2) rxy_occ_b_norm = np.linalg.norm(rxy_occ_b, ord=2) rxy_vir_a_norm = np.linalg.norm(rxy_vir_a, ord=2) rxy_vir_b_norm = np.linalg.norm(rxy_vir_b, ord=2) - if (min(max(rxy_occ_a_norm, rxy_occ_b_norm), max(rxy_vir_a_norm, rxy_vir_b_norm)) < ovlp_tol): + if min(max(rxy_occ_a_norm, rxy_occ_b_norm), max(rxy_vir_a_norm, rxy_vir_b_norm)) < ovlp_tol: emb.log.debugv("Overlap of fragment pair %s - %s below %.2e; skipping pair.", x, y, ovlp_tol) continue kept_xy += 1 - tasl = (t_as_lambda or y.opts.t_as_lambda or y.solver == 'MP2') + tasl = t_as_lambda or y.opts.t_as_lambda or y.solver == "MP2" if not late_t2_sym: l2aa, l2ab, l2bb = wfy.t2 if tasl else wfy.l2 else: @@ -347,68 +357,72 @@ def svd(a): # Only multiply with O(N)-scaling cy_occ/cy_vir in last step: if not late_t2_sym: if svd_tol is None: -# # OO block -# dooxa -= einsum('(ijab,jJ,aA,bB),(qI,IJAB)->iq', t2aa, rxy_occ_a, rxy_vir_a, rxy_vir_a, cy_occ_a, l2aa) * 0.5 -# dooxa -= einsum('(ijab,jJ,aA,bB),(qI,IJAB)->iq', t2ab, rxy_occ_b, rxy_vir_a, rxy_vir_b, cy_occ_a, l2ab) -# dooxb -= einsum('(ijab,jJ,aA,bB),(qI,IJAB)->iq', t2bb, rxy_occ_b, rxy_vir_b, rxy_vir_b, cy_occ_b, l2bb) * 0.5 -# dooxb -= einsum('(ijab,iI,aA,bB),(qJ,IJAB)->jq', t2ab, rxy_occ_a, rxy_vir_a, rxy_vir_b, cy_occ_b, l2ab) - -# # VV block -# dvvxa += einsum('(ijab,iI,jJ,bB),(qA,IJAB)->aq', t2aa, rxy_occ_a, rxy_occ_a, rxy_vir_a, cy_vir_a, l2aa) * 0.5 -# dvvxa += einsum('(ijab,iI,jJ,bB),(qA,IJAB)->aq', t2ab, rxy_occ_a, rxy_occ_b, rxy_vir_b, cy_vir_a, l2ab) -# dvvxb += einsum('(ijab,iI,jJ,bB),(qA,IJAB)->aq', t2bb, rxy_occ_b, rxy_occ_b, rxy_vir_b, cy_vir_b, l2bb) * 0.5 -# dvvxb += einsum('(ijab,iI,jJ,aA),(qB,IJAB)->bq', t2ab, rxy_occ_a, rxy_occ_b, rxy_vir_a, cy_vir_b, l2ab) + # # OO block + # dooxa -= einsum('(ijab,jJ,aA,bB),(qI,IJAB)->iq', t2aa, rxy_occ_a, rxy_vir_a, rxy_vir_a, cy_occ_a, l2aa) * 0.5 + # dooxa -= einsum('(ijab,jJ,aA,bB),(qI,IJAB)->iq', t2ab, rxy_occ_b, rxy_vir_a, rxy_vir_b, cy_occ_a, l2ab) + # dooxb -= einsum('(ijab,jJ,aA,bB),(qI,IJAB)->iq', t2bb, rxy_occ_b, rxy_vir_b, rxy_vir_b, cy_occ_b, l2bb) * 0.5 + # dooxb -= einsum('(ijab,iI,aA,bB),(qJ,IJAB)->jq', t2ab, rxy_occ_a, rxy_vir_a, rxy_vir_b, cy_occ_b, l2ab) + + # # VV block + # dvvxa += einsum('(ijab,iI,jJ,bB),(qA,IJAB)->aq', t2aa, rxy_occ_a, rxy_occ_a, rxy_vir_a, cy_vir_a, l2aa) * 0.5 + # dvvxa += einsum('(ijab,iI,jJ,bB),(qA,IJAB)->aq', t2ab, rxy_occ_a, rxy_occ_b, rxy_vir_b, cy_vir_a, l2ab) + # dvvxb += einsum('(ijab,iI,jJ,bB),(qA,IJAB)->aq', t2bb, rxy_occ_b, rxy_occ_b, rxy_vir_b, cy_vir_b, l2bb) * 0.5 + # dvvxb += einsum('(ijab,iI,jJ,aA),(qB,IJAB)->bq', t2ab, rxy_occ_a, rxy_occ_b, rxy_vir_a, cy_vir_b, l2ab) # ------------------------------- - tlaa = einsum('(ijab,jJ,bB->iJaB),IJAB->iIaA', t2aa, rxy_occ_a, rxy_vir_a, l2aa) - tlbb = einsum('(ijab,jJ,bB->iJaB),IJAB->iIaA', t2bb, rxy_occ_b, rxy_vir_b, l2bb) + tlaa = einsum("(ijab,jJ,bB->iJaB),IJAB->iIaA", t2aa, rxy_occ_a, rxy_vir_a, l2aa) + tlbb = einsum("(ijab,jJ,bB->iJaB),IJAB->iIaA", t2bb, rxy_occ_b, rxy_vir_b, l2bb) - tlab1 = einsum('(ijab,jJ,bB->iJaB),IJAB->iIaA', t2ab, rxy_occ_b, rxy_vir_b, l2ab) - tlab2 = einsum('(ijab,iI,aA->IjAb),IJAB->jJbB', t2ab, rxy_occ_a, rxy_vir_a, l2ab) + tlab1 = einsum("(ijab,jJ,bB->iJaB),IJAB->iIaA", t2ab, rxy_occ_b, rxy_vir_b, l2ab) + tlab2 = einsum("(ijab,iI,aA->IjAb),IJAB->jJbB", t2ab, rxy_occ_a, rxy_vir_a, l2ab) else: -# # OO block -# dooxa -= 0.5 * einsum('(ijab,jX,aY,bZ),(XJ,YA,ZB,qI,IJAB)->iq', t2aa, uxy_occ_a, uxy_vir_a, uxy_vir_a, vxy_occ_a, vxy_vir_a, vxy_vir_a, cy_occ_a, l2aa) -# dooxa -= einsum('(ijab,jX,aY,bZ),(XJ,YA,ZB,qI,IJAB)->iq', t2ab, uxy_occ_b, uxy_vir_a, uxy_vir_b, vxy_occ_b, vxy_vir_a, vxy_vir_b, cy_occ_a, l2ab) -# dooxb -= 0.5 * einsum('(ijab,jX,aY,bZ),(XJ,YA,ZB,qI,IJAB)->iq', t2bb, uxy_occ_b, uxy_vir_b, uxy_vir_b, vxy_occ_b, vxy_vir_b, vxy_vir_b, cy_occ_b, l2bb) -# dooxb -= einsum('(ijab,iX,aY,bZ),(XI,YA,ZB,qJ,IJAB)->jq', t2ab, uxy_occ_a, uxy_vir_a, uxy_vir_b, vxy_occ_a, vxy_vir_a, vxy_vir_b, cy_occ_b, l2ab) - -# # VV block -# dvvxa += 0.5 * einsum('(ijab,iX,jY,bZ),(XI,YJ,ZB,qA,IJAB)->aq', t2aa, uxy_occ_a, uxy_occ_a, uxy_vir_a, vxy_occ_a, vxy_occ_a, vxy_vir_a, cy_vir_a, l2aa) -# dvvxa += einsum('(ijab,iX,jY,bZ),(XI,YJ,ZB,qA,IJAB)->aq', t2ab, uxy_occ_a, uxy_occ_b, uxy_vir_b, vxy_occ_a, vxy_occ_b, vxy_vir_b, cy_vir_a, l2ab) -# dvvxb += 0.5 * einsum('(ijab,iX,jY,bZ),(XI,YJ,ZB,qA,IJAB)->aq', t2bb, uxy_occ_b, uxy_occ_b, uxy_vir_b, vxy_occ_b, vxy_occ_b, vxy_vir_b, cy_vir_b, l2bb) -# dvvxb += einsum('(ijab,iX,jY,aZ),(XI,YJ,ZA,qB,IJAB)->bq', t2ab, uxy_occ_a, uxy_occ_b, uxy_vir_a, vxy_occ_a, vxy_occ_b, vxy_vir_a, cy_vir_b, l2ab) - - #-------------------------------- - - tlaa = einsum('(ijab,jX,bY->iXaY),(XJ,YB,IJAB)->iIaA', t2aa, uxy_occ_a, uxy_vir_a, - vxy_occ_a, vxy_vir_a, l2aa) - tlbb = einsum('(ijab,jX,bY->iXaY),(XJ,YB,IJAB)->iIaA', t2bb, uxy_occ_b, uxy_vir_b, - vxy_occ_b, vxy_vir_b, l2bb) - tlab1 = einsum('(ijab,jX,bY->iXaY),(XJ,YB,IJAB)->iIaA', t2ab, uxy_occ_b, uxy_vir_b, - vxy_occ_b, vxy_vir_b, l2ab) - tlab2 = einsum('(ijab,iX,aY->XjYb),(XI,YA,IJAB)->jJbB', t2ab, uxy_occ_a, uxy_vir_a, - vxy_occ_a, vxy_vir_a, l2ab) + # # OO block + # dooxa -= 0.5 * einsum('(ijab,jX,aY,bZ),(XJ,YA,ZB,qI,IJAB)->iq', t2aa, uxy_occ_a, uxy_vir_a, uxy_vir_a, vxy_occ_a, vxy_vir_a, vxy_vir_a, cy_occ_a, l2aa) + # dooxa -= einsum('(ijab,jX,aY,bZ),(XJ,YA,ZB,qI,IJAB)->iq', t2ab, uxy_occ_b, uxy_vir_a, uxy_vir_b, vxy_occ_b, vxy_vir_a, vxy_vir_b, cy_occ_a, l2ab) + # dooxb -= 0.5 * einsum('(ijab,jX,aY,bZ),(XJ,YA,ZB,qI,IJAB)->iq', t2bb, uxy_occ_b, uxy_vir_b, uxy_vir_b, vxy_occ_b, vxy_vir_b, vxy_vir_b, cy_occ_b, l2bb) + # dooxb -= einsum('(ijab,iX,aY,bZ),(XI,YA,ZB,qJ,IJAB)->jq', t2ab, uxy_occ_a, uxy_vir_a, uxy_vir_b, vxy_occ_a, vxy_vir_a, vxy_vir_b, cy_occ_b, l2ab) + + # # VV block + # dvvxa += 0.5 * einsum('(ijab,iX,jY,bZ),(XI,YJ,ZB,qA,IJAB)->aq', t2aa, uxy_occ_a, uxy_occ_a, uxy_vir_a, vxy_occ_a, vxy_occ_a, vxy_vir_a, cy_vir_a, l2aa) + # dvvxa += einsum('(ijab,iX,jY,bZ),(XI,YJ,ZB,qA,IJAB)->aq', t2ab, uxy_occ_a, uxy_occ_b, uxy_vir_b, vxy_occ_a, vxy_occ_b, vxy_vir_b, cy_vir_a, l2ab) + # dvvxb += 0.5 * einsum('(ijab,iX,jY,bZ),(XI,YJ,ZB,qA,IJAB)->aq', t2bb, uxy_occ_b, uxy_occ_b, uxy_vir_b, vxy_occ_b, vxy_occ_b, vxy_vir_b, cy_vir_b, l2bb) + # dvvxb += einsum('(ijab,iX,jY,aZ),(XI,YJ,ZA,qB,IJAB)->bq', t2ab, uxy_occ_a, uxy_occ_b, uxy_vir_a, vxy_occ_a, vxy_occ_b, vxy_vir_a, cy_vir_b, l2ab) + + # -------------------------------- + + tlaa = einsum( + "(ijab,jX,bY->iXaY),(XJ,YB,IJAB)->iIaA", t2aa, uxy_occ_a, uxy_vir_a, vxy_occ_a, vxy_vir_a, l2aa + ) + tlbb = einsum( + "(ijab,jX,bY->iXaY),(XJ,YB,IJAB)->iIaA", t2bb, uxy_occ_b, uxy_vir_b, vxy_occ_b, vxy_vir_b, l2bb + ) + tlab1 = einsum( + "(ijab,jX,bY->iXaY),(XJ,YB,IJAB)->iIaA", t2ab, uxy_occ_b, uxy_vir_b, vxy_occ_b, vxy_vir_b, l2ab + ) + tlab2 = einsum( + "(ijab,iX,aY->XjYb),(XI,YA,IJAB)->jJbB", t2ab, uxy_occ_a, uxy_vir_a, vxy_occ_a, vxy_vir_a, l2ab + ) # OO block - dooxa -= einsum('iIaA,aA,qI->iq', tlaa, rxy_vir_a, cy_occ_a) * 0.5 - dooxa -= einsum('iIaA,aA,qI->iq', tlab1, rxy_vir_a, cy_occ_a) - dooxb -= einsum('iIaA,aA,qI->iq', tlbb, rxy_vir_b, cy_occ_b) * 0.5 - dooxb -= einsum('iIaA,aA,qI->iq', tlab2, rxy_vir_b, cy_occ_b) + dooxa -= einsum("iIaA,aA,qI->iq", tlaa, rxy_vir_a, cy_occ_a) * 0.5 + dooxa -= einsum("iIaA,aA,qI->iq", tlab1, rxy_vir_a, cy_occ_a) + dooxb -= einsum("iIaA,aA,qI->iq", tlbb, rxy_vir_b, cy_occ_b) * 0.5 + dooxb -= einsum("iIaA,aA,qI->iq", tlab2, rxy_vir_b, cy_occ_b) # VV block - dvvxa += einsum('iIaA,iI,qA->aq', tlaa, rxy_occ_a, cy_vir_a) * 0.5 - dvvxa += einsum('iIaA,iI,qA->aq', tlab1, rxy_occ_a, cy_vir_a) - dvvxb += einsum('iIaA,iI,qA->aq', tlbb, rxy_occ_b, cy_vir_b) * 0.5 - dvvxb += einsum('iIaA,iI,qA->aq', tlab2, rxy_occ_b, cy_vir_b) + dvvxa += einsum("iIaA,iI,qA->aq", tlaa, rxy_occ_a, cy_vir_a) * 0.5 + dvvxa += einsum("iIaA,iI,qA->aq", tlab1, rxy_occ_a, cy_vir_a) + dvvxb += einsum("iIaA,iI,qA->aq", tlbb, rxy_occ_b, cy_vir_b) * 0.5 + dvvxb += einsum("iIaA,iI,qA->aq", tlab2, rxy_occ_b, cy_vir_b) else: # Calculate some overlap matrices: - cfxa, cfxb = x.get_overlap('cluster[occ]|frag') - cfya, cfyb = y.get_overlap('cluster[occ]|frag') - mfxa, mfxb = x.get_overlap('mo[occ]|frag') - mfya, mfyb = y.get_overlap('mo[occ]|frag') + cfxa, cfxb = x.get_overlap("cluster[occ]|frag") + cfya, cfyb = y.get_overlap("cluster[occ]|frag") + mfxa, mfxb = x.get_overlap("mo[occ]|frag") + mfya, mfyb = y.get_overlap("mo[occ]|frag") if svd_tol is None: cfxy_occ_a = dot(rxy_occ_a, cfya) cfxy_occ_b = dot(rxy_occ_b, cfyb) @@ -427,133 +441,131 @@ def svd(a): # --- Occupied # Deal with both virtual overlaps here: if svd_tol is None: - t2aatmp = einsum('xjab,aA,bB->xjAB', t2aa, rxy_vir_a, rxy_vir_a) # frag * cluster^4 - t2abtmp = einsum('xjab,aA,bB->xjAB', t2ab, rxy_vir_a, rxy_vir_b) - t2batmp = einsum('xjab,aA,bB->xjAB', t2ba, rxy_vir_b, rxy_vir_a) - t2bbtmp = einsum('xjab,aA,bB->xjAB', t2bb, rxy_vir_b, rxy_vir_b) + t2aatmp = einsum("xjab,aA,bB->xjAB", t2aa, rxy_vir_a, rxy_vir_a) # frag * cluster^4 + t2abtmp = einsum("xjab,aA,bB->xjAB", t2ab, rxy_vir_a, rxy_vir_b) + t2batmp = einsum("xjab,aA,bB->xjAB", t2ba, rxy_vir_b, rxy_vir_a) + t2bbtmp = einsum("xjab,aA,bB->xjAB", t2bb, rxy_vir_b, rxy_vir_b) l2aatmp, l2abtmp, l2batmp, l2bbtmp = l2aa, l2ab, l2ba, l2bb else: - t2aatmp = einsum('xjab,aS,bP->xjSP', t2aa, uxy_vir_a, uxy_vir_a) - t2abtmp = einsum('xjab,aS,bP->xjSP', t2ab, uxy_vir_a, uxy_vir_b) - t2batmp = einsum('xjab,aS,bP->xjSP', t2ba, uxy_vir_b, uxy_vir_a) - t2bbtmp = einsum('xjab,aS,bP->xjSP', t2bb, uxy_vir_b, uxy_vir_b) + t2aatmp = einsum("xjab,aS,bP->xjSP", t2aa, uxy_vir_a, uxy_vir_a) + t2abtmp = einsum("xjab,aS,bP->xjSP", t2ab, uxy_vir_a, uxy_vir_b) + t2batmp = einsum("xjab,aS,bP->xjSP", t2ba, uxy_vir_b, uxy_vir_a) + t2bbtmp = einsum("xjab,aS,bP->xjSP", t2bb, uxy_vir_b, uxy_vir_b) - l2aatmp = einsum('yjab,Sa,Pb->yjSP', l2aa, vxy_vir_a, vxy_vir_a) - l2abtmp = einsum('yjab,Sa,Pb->yjSP', l2ab, vxy_vir_a, vxy_vir_b) - l2batmp = einsum('yjab,Sa,Pb->yjSP', l2ba, vxy_vir_b, vxy_vir_a) - l2bbtmp = einsum('yjab,Sa,Pb->yjSP', l2bb, vxy_vir_b, vxy_vir_b) + l2aatmp = einsum("yjab,Sa,Pb->yjSP", l2aa, vxy_vir_a, vxy_vir_a) + l2abtmp = einsum("yjab,Sa,Pb->yjSP", l2ab, vxy_vir_a, vxy_vir_b) + l2batmp = einsum("yjab,Sa,Pb->yjSP", l2ba, vxy_vir_b, vxy_vir_a) + l2bbtmp = einsum("yjab,Sa,Pb->yjSP", l2bb, vxy_vir_b, vxy_vir_b) # T2 * L2 if svd_tol is None: - tmpa = -einsum('(xjAB,jJ->xJAB),YJAB->xY', t2aatmp, rxy_occ_a, l2aatmp)/8 - tmpa += -einsum('(xjAB,jJ->xJAB),YJAB->xY', t2abtmp, rxy_occ_b, l2abtmp)/4 - tmpb = -einsum('(xjAB,jJ->xJAB),YJAB->xY', t2bbtmp, rxy_occ_b, l2bbtmp)/8 - tmpb += -einsum('(xjAB,jJ->xJAB),YJAB->xY', t2batmp, rxy_occ_a, l2batmp)/4 + tmpa = -einsum("(xjAB,jJ->xJAB),YJAB->xY", t2aatmp, rxy_occ_a, l2aatmp) / 8 + tmpa += -einsum("(xjAB,jJ->xJAB),YJAB->xY", t2abtmp, rxy_occ_b, l2abtmp) / 4 + tmpb = -einsum("(xjAB,jJ->xJAB),YJAB->xY", t2bbtmp, rxy_occ_b, l2bbtmp) / 8 + tmpb += -einsum("(xjAB,jJ->xJAB),YJAB->xY", t2batmp, rxy_occ_a, l2batmp) / 4 else: - tmpa = -einsum('(xjAB,jS->xSAB),(SJ,YJAB->YSAB)->xY', t2aatmp, uxy_occ_a, vxy_occ_a, l2aatmp)/8 - tmpa += -einsum('(xjAB,jS->xSAB),(SJ,YJAB->YSAB)->xY', t2abtmp, uxy_occ_b, vxy_occ_b, l2abtmp)/4 - tmpb = -einsum('(xjAB,jS->xSAB),(SJ,YJAB->YSAB)->xY', t2bbtmp, uxy_occ_b, vxy_occ_b, l2bbtmp)/8 - tmpb += -einsum('(xjAB,jS->xSAB),(SJ,YJAB->YSAB)->xY', t2batmp, uxy_occ_a, vxy_occ_a, l2batmp)/4 + tmpa = -einsum("(xjAB,jS->xSAB),(SJ,YJAB->YSAB)->xY", t2aatmp, uxy_occ_a, vxy_occ_a, l2aatmp) / 8 + tmpa += -einsum("(xjAB,jS->xSAB),(SJ,YJAB->YSAB)->xY", t2abtmp, uxy_occ_b, vxy_occ_b, l2abtmp) / 4 + tmpb = -einsum("(xjAB,jS->xSAB),(SJ,YJAB->YSAB)->xY", t2bbtmp, uxy_occ_b, vxy_occ_b, l2bbtmp) / 8 + tmpb += -einsum("(xjAB,jS->xSAB),(SJ,YJAB->YSAB)->xY", t2batmp, uxy_occ_a, vxy_occ_a, l2batmp) / 4 dooxa += dot(cfxa, tmpa, mfya.T) dooxb += dot(cfxb, tmpb, mfyb.T) # T2 * L2.T - tmpa = -einsum('(xjAB,jY->xYAB),YIBA->xI', t2aatmp, cfxy_occ_a, l2aatmp)/8 - tmpa += -einsum('(xjAB,jY->xYAB),YIBA->xI', t2abtmp, cfxy_occ_b, l2batmp)/4 + tmpa = -einsum("(xjAB,jY->xYAB),YIBA->xI", t2aatmp, cfxy_occ_a, l2aatmp) / 8 + tmpa += -einsum("(xjAB,jY->xYAB),YIBA->xI", t2abtmp, cfxy_occ_b, l2batmp) / 4 dooxa += dot(cfxa, tmpa, cy_occ_a.T) - tmpb = -einsum('(xjAB,jY->xYAB),YIBA->xI', t2bbtmp, cfxy_occ_b, l2bbtmp)/8 - tmpb += -einsum('(xjAB,jY->xYAB),YIBA->xI', t2batmp, cfxy_occ_a, l2abtmp)/4 + tmpb = -einsum("(xjAB,jY->xYAB),YIBA->xI", t2bbtmp, cfxy_occ_b, l2bbtmp) / 8 + tmpb += -einsum("(xjAB,jY->xYAB),YIBA->xI", t2batmp, cfxy_occ_a, l2abtmp) / 4 dooxb += dot(cfxb, tmpb, cy_occ_b.T) # T2.T * L2 - tmpa = -einsum('xiBA,(Jx,YJAB->YxAB)->iY', t2aatmp, cfyx_occ_a, l2aatmp)/8 - tmpa += -einsum('xiBA,(Jx,YJAB->YxAB)->iY', t2batmp, cfyx_occ_b, l2abtmp)/4 + tmpa = -einsum("xiBA,(Jx,YJAB->YxAB)->iY", t2aatmp, cfyx_occ_a, l2aatmp) / 8 + tmpa += -einsum("xiBA,(Jx,YJAB->YxAB)->iY", t2batmp, cfyx_occ_b, l2abtmp) / 4 dooxa += np.dot(tmpa, mfya.T) - tmpb = -einsum('xiBA,(Jx,YJAB->YxAB)->iY', t2bbtmp, cfyx_occ_b, l2bbtmp)/8 - tmpb += -einsum('xiBA,(Jx,YJAB->YxAB)->iY', t2abtmp, cfyx_occ_a, l2batmp)/4 + tmpb = -einsum("xiBA,(Jx,YJAB->YxAB)->iY", t2bbtmp, cfyx_occ_b, l2bbtmp) / 8 + tmpb += -einsum("xiBA,(Jx,YJAB->YxAB)->iY", t2abtmp, cfyx_occ_a, l2batmp) / 4 dooxb += np.dot(tmpb, mfyb.T) # T2.T * L2.T - tmpa = -einsum('xiBA,xY,YIBA->iI', t2aatmp, ffxya, l2aatmp)/8 - tmpa += -einsum('xiBA,xY,YIBA->iI', t2batmp, ffxyb, l2batmp)/4 + tmpa = -einsum("xiBA,xY,YIBA->iI", t2aatmp, ffxya, l2aatmp) / 8 + tmpa += -einsum("xiBA,xY,YIBA->iI", t2batmp, ffxyb, l2batmp) / 4 dooxa += np.dot(tmpa, cy_occ_a.T) - tmpb = -einsum('xiBA,xY,YIBA->iI', t2bbtmp, ffxyb, l2bbtmp)/8 - tmpb += -einsum('xiBA,xY,YIBA->iI', t2abtmp, ffxya, l2abtmp)/4 + tmpb = -einsum("xiBA,xY,YIBA->iI", t2bbtmp, ffxyb, l2bbtmp) / 8 + tmpb += -einsum("xiBA,xY,YIBA->iI", t2abtmp, ffxya, l2abtmp) / 4 dooxb += np.dot(tmpb, cy_occ_b.T) # --- Virtual # T2 * L2 and T2.T * L2.T if svd_tol is None: - t2aatmp = einsum('xjab,xY,jJ->YJab', t2aa, ffxya, rxy_occ_a) - t2abtmp = einsum('xjab,xY,jJ->YJab', t2ab, ffxya, rxy_occ_b) - t2batmp = einsum('xjab,xY,jJ->YJab', t2ba, ffxyb, rxy_occ_a) - t2bbtmp = einsum('xjab,xY,jJ->YJab', t2bb, ffxyb, rxy_occ_b) - - tmpa = einsum('(YJab,aA->YJAb),YJAB->bB', t2aatmp, rxy_vir_a, l2aa)/8 - tmpa += einsum('(YJab,aA->YJAb),YJAB->bB', t2batmp, rxy_vir_b, l2ba)/4 - tmpb = einsum('(YJab,aA->YJAb),YJAB->bB', t2bbtmp, rxy_vir_b, l2bb)/8 - tmpb += einsum('(YJab,aA->YJAb),YJAB->bB', t2abtmp, rxy_vir_a, l2ab)/4 - - - tmpa += einsum('(YIba,aA->YIbA),YIBA->bB', t2aatmp, rxy_vir_a, l2aa)/8 - tmpa += einsum('(YIba,aA->YIbA),YIBA->bB', t2abtmp, rxy_vir_b, l2ab)/4 - tmpb += einsum('(YIba,aA->YIbA),YIBA->bB', t2bbtmp, rxy_vir_b, l2bb)/8 - tmpb += einsum('(YIba,aA->YIbA),YIBA->bB', t2batmp, rxy_vir_a, l2ba)/4 + t2aatmp = einsum("xjab,xY,jJ->YJab", t2aa, ffxya, rxy_occ_a) + t2abtmp = einsum("xjab,xY,jJ->YJab", t2ab, ffxya, rxy_occ_b) + t2batmp = einsum("xjab,xY,jJ->YJab", t2ba, ffxyb, rxy_occ_a) + t2bbtmp = einsum("xjab,xY,jJ->YJab", t2bb, ffxyb, rxy_occ_b) + + tmpa = einsum("(YJab,aA->YJAb),YJAB->bB", t2aatmp, rxy_vir_a, l2aa) / 8 + tmpa += einsum("(YJab,aA->YJAb),YJAB->bB", t2batmp, rxy_vir_b, l2ba) / 4 + tmpb = einsum("(YJab,aA->YJAb),YJAB->bB", t2bbtmp, rxy_vir_b, l2bb) / 8 + tmpb += einsum("(YJab,aA->YJAb),YJAB->bB", t2abtmp, rxy_vir_a, l2ab) / 4 + + tmpa += einsum("(YIba,aA->YIbA),YIBA->bB", t2aatmp, rxy_vir_a, l2aa) / 8 + tmpa += einsum("(YIba,aA->YIbA),YIBA->bB", t2abtmp, rxy_vir_b, l2ab) / 4 + tmpb += einsum("(YIba,aA->YIbA),YIBA->bB", t2bbtmp, rxy_vir_b, l2bb) / 8 + tmpb += einsum("(YIba,aA->YIbA),YIBA->bB", t2batmp, rxy_vir_a, l2ba) / 4 else: - t2aatmp = einsum('xjab,jS->xSab', t2aa, uxy_occ_a) - t2abtmp = einsum('xjab,jS->xSab', t2ab, uxy_occ_b) - t2batmp = einsum('xjab,jS->xSab', t2ba, uxy_occ_a) - t2bbtmp = einsum('xjab,jS->xSab', t2bb, uxy_occ_b) - - l2aatmp = einsum('YJAB,SJ,xY->xSAB', l2aa, vxy_occ_a, ffxya) - l2abtmp = einsum('YJAB,SJ,xY->xSAB', l2ab, vxy_occ_b, ffxya) - l2batmp = einsum('YJAB,SJ,xY->xSAB', l2ba, vxy_occ_a, ffxyb) - l2bbtmp = einsum('YJAB,SJ,xY->xSAB', l2bb, vxy_occ_b, ffxyb) - - tmpa = einsum('(xSab,aP->xSPb),(PA,xSAB->xSPB)->bB', t2aatmp, uxy_vir_a, vxy_vir_a, l2aatmp)/8 - tmpa += einsum('(xSab,aP->xSPb),(PA,xSAB->xSPB)->bB', t2batmp, uxy_vir_b, vxy_vir_b, l2batmp)/4 - tmpb = einsum('(xSab,aP->xSPb),(PA,xSAB->xSPB)->bB', t2bbtmp, uxy_vir_b, vxy_vir_b, l2bbtmp)/8 - tmpb += einsum('(xSab,aP->xSPb),(PA,xSAB->xSPB)->bB', t2abtmp, uxy_vir_a, vxy_vir_a, l2abtmp)/4 - - - tmpa += einsum('(xSba,aP->xSbP),(PA,xSBA->xSBP)->bB', t2aatmp, uxy_vir_a, vxy_vir_a, l2aatmp)/8 - tmpa += einsum('(xSba,aP->xSbP),(PA,xSBA->xSBP)->bB', t2abtmp, uxy_vir_b, vxy_vir_b, l2abtmp)/4 - tmpb += einsum('(xSba,aP->xSbP),(PA,xSBA->xSBP)->bB', t2bbtmp, uxy_vir_b, vxy_vir_b, l2bbtmp)/8 - tmpb += einsum('(xSba,aP->xSbP),(PA,xSBA->xSBP)->bB', t2batmp, uxy_vir_a, vxy_vir_a, l2batmp)/4 + t2aatmp = einsum("xjab,jS->xSab", t2aa, uxy_occ_a) + t2abtmp = einsum("xjab,jS->xSab", t2ab, uxy_occ_b) + t2batmp = einsum("xjab,jS->xSab", t2ba, uxy_occ_a) + t2bbtmp = einsum("xjab,jS->xSab", t2bb, uxy_occ_b) + + l2aatmp = einsum("YJAB,SJ,xY->xSAB", l2aa, vxy_occ_a, ffxya) + l2abtmp = einsum("YJAB,SJ,xY->xSAB", l2ab, vxy_occ_b, ffxya) + l2batmp = einsum("YJAB,SJ,xY->xSAB", l2ba, vxy_occ_a, ffxyb) + l2bbtmp = einsum("YJAB,SJ,xY->xSAB", l2bb, vxy_occ_b, ffxyb) + + tmpa = einsum("(xSab,aP->xSPb),(PA,xSAB->xSPB)->bB", t2aatmp, uxy_vir_a, vxy_vir_a, l2aatmp) / 8 + tmpa += einsum("(xSab,aP->xSPb),(PA,xSAB->xSPB)->bB", t2batmp, uxy_vir_b, vxy_vir_b, l2batmp) / 4 + tmpb = einsum("(xSab,aP->xSPb),(PA,xSAB->xSPB)->bB", t2bbtmp, uxy_vir_b, vxy_vir_b, l2bbtmp) / 8 + tmpb += einsum("(xSab,aP->xSPb),(PA,xSAB->xSPB)->bB", t2abtmp, uxy_vir_a, vxy_vir_a, l2abtmp) / 4 + + tmpa += einsum("(xSba,aP->xSbP),(PA,xSBA->xSBP)->bB", t2aatmp, uxy_vir_a, vxy_vir_a, l2aatmp) / 8 + tmpa += einsum("(xSba,aP->xSbP),(PA,xSBA->xSBP)->bB", t2abtmp, uxy_vir_b, vxy_vir_b, l2abtmp) / 4 + tmpb += einsum("(xSba,aP->xSbP),(PA,xSBA->xSBP)->bB", t2bbtmp, uxy_vir_b, vxy_vir_b, l2bbtmp) / 8 + tmpb += einsum("(xSba,aP->xSbP),(PA,xSBA->xSBP)->bB", t2batmp, uxy_vir_a, vxy_vir_a, l2batmp) / 4 # T2 * L2.T and T2.T * L2 - t2aatmp = einsum('xjab,jY->xYab', t2aa, cfxy_occ_a) - t2abtmp = einsum('xjab,jY->xYab', t2ab, cfxy_occ_b) - t2batmp = einsum('xjab,jY->xYab', t2ba, cfxy_occ_a) - t2bbtmp = einsum('xjab,jY->xYab', t2bb, cfxy_occ_b) + t2aatmp = einsum("xjab,jY->xYab", t2aa, cfxy_occ_a) + t2abtmp = einsum("xjab,jY->xYab", t2ab, cfxy_occ_b) + t2batmp = einsum("xjab,jY->xYab", t2ba, cfxy_occ_a) + t2bbtmp = einsum("xjab,jY->xYab", t2bb, cfxy_occ_b) - l2aatmp = einsum('Jx,YJAB->YxAB', cfyx_occ_a, l2aa) - l2abtmp = einsum('Jx,YJAB->YxAB', cfyx_occ_b, l2ab) - l2batmp = einsum('Jx,YJAB->YxAB', cfyx_occ_a, l2ba) - l2bbtmp = einsum('Jx,YJAB->YxAB', cfyx_occ_b, l2bb) + l2aatmp = einsum("Jx,YJAB->YxAB", cfyx_occ_a, l2aa) + l2abtmp = einsum("Jx,YJAB->YxAB", cfyx_occ_b, l2ab) + l2batmp = einsum("Jx,YJAB->YxAB", cfyx_occ_a, l2ba) + l2bbtmp = einsum("Jx,YJAB->YxAB", cfyx_occ_b, l2bb) if svd_tol is None: - tmpa += einsum('(xYab,aA->xYAb),YxBA->bB', t2aatmp, rxy_vir_a, l2aatmp)/8 - tmpa += einsum('(xYab,aA->xYAb),YxBA->bB', t2batmp, rxy_vir_b, l2abtmp)/4 - tmpb += einsum('(xYab,aA->xYAb),YxBA->bB', t2bbtmp, rxy_vir_b, l2bbtmp)/8 - tmpb += einsum('(xYab,aA->xYAb),YxBA->bB', t2abtmp, rxy_vir_a, l2batmp)/4 - - tmpa += einsum('(xYba,aA->xYbA),YxAB->bB', t2aatmp, rxy_vir_a, l2aatmp)/8 - tmpa += einsum('(xYba,aA->xYbA),YxAB->bB', t2abtmp, rxy_vir_b, l2batmp)/4 - tmpb += einsum('(xYba,aA->xYbA),YxAB->bB', t2bbtmp, rxy_vir_b, l2bbtmp)/8 - tmpb += einsum('(xYba,aA->xYbA),YxAB->bB', t2batmp, rxy_vir_a, l2abtmp)/4 + tmpa += einsum("(xYab,aA->xYAb),YxBA->bB", t2aatmp, rxy_vir_a, l2aatmp) / 8 + tmpa += einsum("(xYab,aA->xYAb),YxBA->bB", t2batmp, rxy_vir_b, l2abtmp) / 4 + tmpb += einsum("(xYab,aA->xYAb),YxBA->bB", t2bbtmp, rxy_vir_b, l2bbtmp) / 8 + tmpb += einsum("(xYab,aA->xYAb),YxBA->bB", t2abtmp, rxy_vir_a, l2batmp) / 4 + + tmpa += einsum("(xYba,aA->xYbA),YxAB->bB", t2aatmp, rxy_vir_a, l2aatmp) / 8 + tmpa += einsum("(xYba,aA->xYbA),YxAB->bB", t2abtmp, rxy_vir_b, l2batmp) / 4 + tmpb += einsum("(xYba,aA->xYbA),YxAB->bB", t2bbtmp, rxy_vir_b, l2bbtmp) / 8 + tmpb += einsum("(xYba,aA->xYbA),YxAB->bB", t2batmp, rxy_vir_a, l2abtmp) / 4 else: - tmpa += einsum('(xYab,aS->xYSb),(SA,YxBA->YxBS)->bB', t2aatmp, uxy_vir_a, vxy_vir_a, l2aatmp)/8 - tmpa += einsum('(xYab,aS->xYSb),(SA,YxBA->YxBS)->bB', t2batmp, uxy_vir_b, vxy_vir_b, l2abtmp)/4 - tmpb += einsum('(xYab,aS->xYSb),(SA,YxBA->YxBS)->bB', t2bbtmp, uxy_vir_b, vxy_vir_b, l2bbtmp)/8 - tmpb += einsum('(xYab,aS->xYSb),(SA,YxBA->YxBS)->bB', t2abtmp, uxy_vir_a, vxy_vir_a, l2batmp)/4 + tmpa += einsum("(xYab,aS->xYSb),(SA,YxBA->YxBS)->bB", t2aatmp, uxy_vir_a, vxy_vir_a, l2aatmp) / 8 + tmpa += einsum("(xYab,aS->xYSb),(SA,YxBA->YxBS)->bB", t2batmp, uxy_vir_b, vxy_vir_b, l2abtmp) / 4 + tmpb += einsum("(xYab,aS->xYSb),(SA,YxBA->YxBS)->bB", t2bbtmp, uxy_vir_b, vxy_vir_b, l2bbtmp) / 8 + tmpb += einsum("(xYab,aS->xYSb),(SA,YxBA->YxBS)->bB", t2abtmp, uxy_vir_a, vxy_vir_a, l2batmp) / 4 - tmpa += einsum('(xYba,aS->xYbS),(SA,YxAB->YxSB)->bB', t2aatmp, uxy_vir_a, vxy_vir_a, l2aatmp)/8 - tmpa += einsum('(xYba,aS->xYbS),(SA,YxAB->YxSB)->bB', t2abtmp, uxy_vir_b, vxy_vir_b, l2batmp)/4 - tmpb += einsum('(xYba,aS->xYbS),(SA,YxAB->YxSB)->bB', t2bbtmp, uxy_vir_b, vxy_vir_b, l2bbtmp)/8 - tmpb += einsum('(xYba,aS->xYbS),(SA,YxAB->YxSB)->bB', t2batmp, uxy_vir_a, vxy_vir_a, l2abtmp)/4 + tmpa += einsum("(xYba,aS->xYbS),(SA,YxAB->YxSB)->bB", t2aatmp, uxy_vir_a, vxy_vir_a, l2aatmp) / 8 + tmpa += einsum("(xYba,aS->xYbS),(SA,YxAB->YxSB)->bB", t2abtmp, uxy_vir_b, vxy_vir_b, l2batmp) / 4 + tmpb += einsum("(xYba,aS->xYbS),(SA,YxAB->YxSB)->bB", t2bbtmp, uxy_vir_b, vxy_vir_b, l2bbtmp) / 8 + tmpb += einsum("(xYba,aS->xYbS),(SA,YxAB->YxSB)->bB", t2batmp, uxy_vir_a, vxy_vir_a, l2abtmp) / 4 dvvxa += np.dot(tmpa, cy_vir_a.T) dvvxb += np.dot(tmpb, cy_vir_b.T) @@ -569,11 +581,27 @@ def svd(a): dooxb = dot(dooxb, emb.mo_coeff_occ[1].T) dvvxa = dot(dvvxa, emb.mo_coeff_vir[0].T) dvvxb = dot(dvvxb, emb.mo_coeff_vir[1].T) - #Loop over symmetry children of x: - arrays = (x.cluster.c_occ[0], x.cluster.c_occ[1], x.cluster.c_vir[0], x.cluster.c_vir[1], - dooxa, dooxb, dvvxa, dvvxb) - for x2, (cx2_occ_a, cx2_occ_b, cx2_vir_a, cx2_vir_b, dooxa2, dooxb2, dvvxa2, dvvxb2) in \ - x.loop_symmetry_children(arrays, axes=[0,0,0,0,1,1,1,1]): + # Loop over symmetry children of x: + arrays = ( + x.cluster.c_occ[0], + x.cluster.c_occ[1], + x.cluster.c_vir[0], + x.cluster.c_vir[1], + dooxa, + dooxb, + dvvxa, + dvvxb, + ) + for x2, ( + cx2_occ_a, + cx2_occ_b, + cx2_vir_a, + cx2_vir_b, + dooxa2, + dooxb2, + dvvxa2, + dvvxb2, + ) in x.loop_symmetry_children(arrays, axes=[0, 0, 0, 0, 1, 1, 1, 1]): dooa += dot(cs_occ_a, cx2_occ_a, dooxa2, cs_occ_a.T) doob += dot(cs_occ_b, cx2_occ_b, dooxb2, cs_occ_b.T) dvva += dot(cs_vir_a, cx2_vir_a, dvvxa2, cs_vir_a.T) @@ -584,42 +612,54 @@ def svd(a): l1xb = dot(cx_occ_b.T, l1b, cx_vir_b) if not late_t2_sym: # VO block - dvoxa += einsum('ijab,jb->ai', t2aa, l1xa) - dvoxa += einsum('ijab,jb->ai', t2ab, l1xb) - dvoxb += einsum('ijab,jb->ai', t2bb, l1xb) - dvoxb += einsum('ijab,ia->bj', t2ab, l1xa) + dvoxa += einsum("ijab,jb->ai", t2aa, l1xa) + dvoxa += einsum("ijab,jb->ai", t2ab, l1xb) + dvoxb += einsum("ijab,jb->ai", t2bb, l1xb) + dvoxb += einsum("ijab,ia->bj", t2ab, l1xa) - dvoa += einsum('ai,Ii,Aa->AI', dvoxa, cx_occ_a, cx_vir_a) - dvob += einsum('ai,Ii,Aa->AI', dvoxb, cx_occ_b, cx_vir_b) + dvoa += einsum("ai,Ii,Aa->AI", dvoxa, cx_occ_a, cx_vir_a) + dvob += einsum("ai,Ii,Aa->AI", dvoxb, cx_occ_b, cx_vir_b) else: - cfxa, cfxb = x.get_overlap('cluster[occ]|frag') - dvoxa1 = einsum('xjab,jb->ax', t2aa, l1xa)/2 - dvoxa1 += einsum('xjab,jb->ax', t2ab, l1xb)/2 - dvoxa2 = einsum('xiba,(jx,jb->xb)->ai', t2aa, cfxa, l1xa)/2 - dvoxa2 += einsum('xiba,(jx,jb->xb)->ai', t2ba, cfxb, l1xb)/2 + cfxa, cfxb = x.get_overlap("cluster[occ]|frag") + dvoxa1 = einsum("xjab,jb->ax", t2aa, l1xa) / 2 + dvoxa1 += einsum("xjab,jb->ax", t2ab, l1xb) / 2 + dvoxa2 = einsum("xiba,(jx,jb->xb)->ai", t2aa, cfxa, l1xa) / 2 + dvoxa2 += einsum("xiba,(jx,jb->xb)->ai", t2ba, cfxb, l1xb) / 2 dvoa += dot(cx_vir_a, dvoxa1, cfxa.T, cx_occ_a.T) dvoa += dot(cx_vir_a, dvoxa2, cx_occ_a.T) - dvoxb1 = einsum('xjab,jb->ax', t2bb, l1xb)/2 - dvoxb1 += einsum('xjab,jb->ax', t2ba, l1xa)/2 - dvoxb2 = einsum('xiba,(jx,jb->xb)->ai', t2bb, cfxb, l1xb)/2 - dvoxb2 += einsum('xiba,(jx,jb->xb)->ai', t2ab, cfxa, l1xa)/2 + dvoxb1 = einsum("xjab,jb->ax", t2bb, l1xb) / 2 + dvoxb1 += einsum("xjab,jb->ax", t2ba, l1xa) / 2 + dvoxb2 = einsum("xiba,(jx,jb->xb)->ai", t2bb, cfxb, l1xb) / 2 + dvoxb2 += einsum("xiba,(jx,jb->xb)->ai", t2ab, cfxa, l1xa) / 2 dvob += dot(cx_vir_b, dvoxb1, cfxb.T, cx_occ_b.T) dvob += dot(cx_vir_b, dvoxb2, cx_occ_b.T) if use_sym: - arrays = (x.c_frag[0], x.cluster.c_occ[0], x.cluster.c_vir[0], x.c_frag[1], x.cluster.c_occ[1], - x.cluster.c_vir[1]) - for x2, (cx2_frag_a, cx2_occ_a, cx2_vir_a, cx2_frag_b, cx2_occ_b, cx2_vir_b) in \ - x.loop_symmetry_children(arrays, include_self=False, maxgen=None): + arrays = ( + x.c_frag[0], + x.cluster.c_occ[0], + x.cluster.c_vir[0], + x.c_frag[1], + x.cluster.c_occ[1], + x.cluster.c_vir[1], + ) + for x2, ( + cx2_frag_a, + cx2_occ_a, + cx2_vir_a, + cx2_frag_b, + cx2_occ_b, + cx2_vir_b, + ) in x.loop_symmetry_children(arrays, include_self=False, maxgen=None): cx2_occ_a = np.dot(cs_occ_a, cx2_occ_a) cx2_vir_a = np.dot(cs_vir_a, cx2_vir_a) cx2_occ_b = np.dot(cs_occ_b, cx2_occ_b) cx2_vir_b = np.dot(cs_vir_b, cx2_vir_b) if not late_t2_sym: - dvoa += einsum('ai,Ii,Aa->AI', dvoxa, cx2_occ_a, cx2_vir_a) - dvob += einsum('ai,Ii,Aa->AI', dvoxb, cx2_occ_b, cx2_vir_b) + dvoa += einsum("ai,Ii,Aa->AI", dvoxa, cx2_occ_a, cx2_vir_a) + dvob += einsum("ai,Ii,Aa->AI", dvoxb, cx2_occ_b, cx2_vir_b) else: dvoa += dot(cx2_vir_a, dvoxa1, cx2_frag_a.T, cs_occ_a.T) dvoa += dot(cx2_vir_a, dvoxa2, cx2_occ_a.T) @@ -628,8 +668,9 @@ def svd(a): if mpi: rma.clear() - dooa, doob, dvoa, dvob, dvva, dvvb = mpi.nreduce(dooa, doob, dvoa, dvob, dvva, dvvb, - target=mpi_target, logfunc=emb.log.timingv) + dooa, doob, dvoa, dvob, dvva, dvvb = mpi.nreduce( + dooa, doob, dvoa, dvob, dvva, dvvb, target=mpi_target, logfunc=emb.log.timingv + ) # Make sure no more MPI calls are made after returning some ranks early! if mpi_target not in (None, mpi.rank): return None @@ -640,43 +681,43 @@ def svd(a): xt2a = dvva.copy() xt2b = dvvb.copy() - dooa -= einsum('ie,je->ij', l1a, t1a) - doob -= einsum('ie,je->ij', l1b, t1b) + dooa -= einsum("ie,je->ij", l1a, t1a) + doob -= einsum("ie,je->ij", l1b, t1b) - dvva += einsum('ma,mb->ab', t1a, l1a) - dvvb += einsum('ma,mb->ab', t1b, l1b) + dvva += einsum("ma,mb->ab", t1a, l1a) + dvvb += einsum("ma,mb->ab", t1b, l1b) - xt2a += einsum('ma,me->ae', t1a, l1a) - dvoa -= einsum('mi,ma->ai', xt1a, t1a) - dvoa -= einsum('ie,ae->ai', t1a, xt2a) + xt2a += einsum("ma,me->ae", t1a, l1a) + dvoa -= einsum("mi,ma->ai", xt1a, t1a) + dvoa -= einsum("ie,ae->ai", t1a, xt2a) dvoa += t1a.T + l1a.T - xt2b += einsum('ma,me->ae', t1b, l1b) - dvob -= einsum('mi,ma->ai', xt1b, t1b) - dvob -= einsum('ie,ae->ai', t1b, xt2b) + xt2b += einsum("ma,me->ae", t1b, l1b) + dvob -= einsum("mi,ma->ai", xt1b, t1b) + dvob -= einsum("ie,ae->ai", t1b, xt2b) dvob += t1b.T + l1b.T nmoa = nocca + nvira - dm1a = np.zeros((nmoa,nmoa)) - dm1a[:nocca,:nocca] = dooa + dooa.conj().T - dm1a[nocca:,nocca:] = dvva + dvva.conj().T + dm1a = np.zeros((nmoa, nmoa)) + dm1a[:nocca, :nocca] = dooa + dooa.conj().T + dm1a[nocca:, nocca:] = dvva + dvva.conj().T if with_t1: - dm1a[:nocca,nocca:] = dvoa.conj().T - dm1a[nocca:,:nocca] = (dm1a[:nocca,nocca:].conj().T) - dm1a = (dm1a + dm1a.T)/4 + dm1a[:nocca, nocca:] = dvoa.conj().T + dm1a[nocca:, :nocca] = dm1a[:nocca, nocca:].conj().T + dm1a = (dm1a + dm1a.T) / 4 nmob = noccb + nvirb dm1b = np.zeros((nmob, nmob)) - dm1b[:noccb,:noccb] = doob + doob.conj().T - dm1b[noccb:,noccb:] = dvvb + dvvb.conj().T + dm1b[:noccb, :noccb] = doob + doob.conj().T + dm1b[noccb:, noccb:] = dvvb + dvvb.conj().T if with_t1: - dm1b[:noccb,noccb:] = dvob.conj().T - dm1b[noccb:,:noccb] = (dm1b[:noccb,noccb:].conj().T) - dm1b = (dm1b + dm1b.T)/4 + dm1b[:noccb, noccb:] = dvob.conj().T + dm1b[noccb:, :noccb] = dm1b[:noccb, noccb:].conj().T + dm1b = (dm1b + dm1b.T) / 4 # --- Some information: - emb.log.debug("Cluster-pairs: total= %d kept= %d (%.1f%%)", total_xy, kept_xy, 100*kept_xy/total_xy) - emb.log.debug("Singular values total= %d kept= %d (%.1f%%)", total_sv, kept_sv, 100*kept_sv/total_sv) + emb.log.debug("Cluster-pairs: total= %d kept= %d (%.1f%%)", total_xy, kept_xy, 100 * kept_xy / total_xy) + emb.log.debug("Singular values total= %d kept= %d (%.1f%%)", total_sv, kept_sv, 100 * kept_sv / total_sv) return dm1a, dm1b @@ -708,18 +749,19 @@ def make_rdm2_ccsd_global_wf(emb, ao_basis=False, symmetrize=False, t_as_lambda= l1 = emb.get_global_l1() if not t_as_lambda else t1 l2 = emb.get_global_l2() if not t_as_lambda else t2 mockcc = _get_mockcc(emb.mo_coeff, emb.mf.max_memory) - #dm2 = pyscf.cc.uccsd_rdm.make_rdm2(mockcc, t1=t1, t2=t2, l1=l1, l2=l2, with_frozen=False, with_dm1=with_dm1) + # dm2 = pyscf.cc.uccsd_rdm.make_rdm2(mockcc, t1=t1, t2=t2, l1=l1, l2=l2, with_frozen=False, with_dm1=with_dm1) dm2 = uccsd_rdm.make_rdm2(mockcc, t1=t1, t2=t2, l1=l1, l2=l2, with_frozen=False, with_dm1=with_dm1) else: raise NotImplementedError() if ao_basis: raise NotImplementedError() - #dm2 = einsum('ijkl,pi,qj,rk,sl->pqrs', dm2, *(4*[emb.mo_coeff])) + # dm2 = einsum('ijkl,pi,qj,rk,sl->pqrs', dm2, *(4*[emb.mo_coeff])) if symmetrize: raise NotImplementedError() - #dm2 = (dm2 + dm2.transpose(1,0,3,2))/2 + # dm2 = (dm2 + dm2.transpose(1,0,3,2))/2 return dm2 + def make_rdm2_ccsd_proj_lambda(emb, ao_basis=False, t_as_lambda=None, with_dm1=True, mpi_target=None): """Make two-particle reduced density-matrix from partitioned fragment CCSD wave functions. @@ -746,16 +788,16 @@ def make_rdm2_ccsd_proj_lambda(emb, ao_basis=False, t_as_lambda=None, with_dm1=T """ # --- Loop over pairs of fragments and add projected density-matrix contributions: nmoa, nmob = emb.nmo - dm2aa = np.zeros(4*[nmoa]) - dm2ab = np.zeros(2*[nmoa]+2*[nmob]) - dm2bb = np.zeros(4*[nmob]) + dm2aa = np.zeros(4 * [nmoa]) + dm2ab = np.zeros(2 * [nmoa] + 2 * [nmob]) + dm2bb = np.zeros(4 * [nmob]) ovlp = emb.get_ovlp() for x in emb.get_fragments(contributes=True, mpi_rank=mpi.rank): dm2xaa, dm2xab, dm2xbb = x.make_fragment_dm2cumulant(t_as_lambda=t_as_lambda) - ra, rb = x.get_overlap('mo|cluster') - dm2aa += einsum('ijkl,Ii,Jj,Kk,Ll->IJKL', dm2xaa, ra, ra, ra, ra) - dm2ab += einsum('ijkl,Ii,Jj,Kk,Ll->IJKL', dm2xab, ra, ra, rb, rb) - dm2bb += einsum('ijkl,Ii,Jj,Kk,Ll->IJKL', dm2xbb, rb, rb, rb, rb) + ra, rb = x.get_overlap("mo|cluster") + dm2aa += einsum("ijkl,Ii,Jj,Kk,Ll->IJKL", dm2xaa, ra, ra, ra, ra) + dm2ab += einsum("ijkl,Ii,Jj,Kk,Ll->IJKL", dm2xab, ra, ra, rb, rb) + dm2bb += einsum("ijkl,Ii,Jj,Kk,Ll->IJKL", dm2xbb, rb, rb, rb, rb) if mpi: dm2aa, dm2ab, dm2bb = mpi.nreduce(dm2aa, dm2ab, dm2bb, target=mpi_target, logfunc=emb.log.timingv) if mpi_target not in (None, mpi.rank): @@ -769,20 +811,20 @@ def make_rdm2_ccsd_proj_lambda(emb, ao_basis=False, t_as_lambda=None, with_dm1=T dm1a[np.diag_indices(nocca)] -= 0.5 dm1b[np.diag_indices(noccb)] -= 0.5 for i in range(nocca): - dm2aa[i,i,:,:] += dm1a - dm2aa[:,:,i,i] += dm1a - dm2aa[:,i,i,:] -= dm1a - dm2aa[i,:,:,i] -= dm1a.T - dm2ab[i,i,:,:] += dm1b + dm2aa[i, i, :, :] += dm1a + dm2aa[:, :, i, i] += dm1a + dm2aa[:, i, i, :] -= dm1a + dm2aa[i, :, :, i] -= dm1a.T + dm2ab[i, i, :, :] += dm1b for i in range(noccb): - dm2bb[i,i,:,:] += dm1b - dm2bb[:,:,i,i] += dm1b - dm2bb[:,i,i,:] -= dm1b - dm2bb[i,:,:,i] -= dm1b.T - dm2ab[:,:,i,i] += dm1a + dm2bb[i, i, :, :] += dm1b + dm2bb[:, :, i, i] += dm1b + dm2bb[:, i, i, :] -= dm1b + dm2bb[i, :, :, i] -= dm1b.T + dm2ab[:, :, i, i] += dm1a if ao_basis: - dm2aa = einsum('ijkl,pi,qj,rk,sl->pqrs', dm2aa, *(4*[emb.mo_coeff[0]])) - dm2ab = einsum('ijkl,pi,qj,rk,sl->pqrs', dm2ab, *(2*[emb.mo_coeff[0]] + 2*[emb.mo_coeff[1]])) - dm2bb = einsum('ijkl,pi,qj,rk,sl->pqrs', dm2bb, *(4*[emb.mo_coeff[1]])) + dm2aa = einsum("ijkl,pi,qj,rk,sl->pqrs", dm2aa, *(4 * [emb.mo_coeff[0]])) + dm2ab = einsum("ijkl,pi,qj,rk,sl->pqrs", dm2ab, *(2 * [emb.mo_coeff[0]] + 2 * [emb.mo_coeff[1]])) + dm2bb = einsum("ijkl,pi,qj,rk,sl->pqrs", dm2bb, *(4 * [emb.mo_coeff[1]])) return (dm2aa, dm2ab, dm2bb) diff --git a/vayesta/lattmod/__init__.py b/vayesta/lattmod/__init__.py index 10953ca8e..c514a3398 100644 --- a/vayesta/lattmod/__init__.py +++ b/vayesta/lattmod/__init__.py @@ -5,6 +5,7 @@ from vayesta.lattmod.latt import LatticeRHF from vayesta.lattmod.latt import LatticeUHF + def LatticeMF(mol, *args, **kwargs): """Use RHF by default, unless spin is not zero.""" if mol.spin == 0: diff --git a/vayesta/lattmod/bethe.py b/vayesta/lattmod/bethe.py index e6c719283..86cd3642e 100644 --- a/vayesta/lattmod/bethe.py +++ b/vayesta/lattmod/bethe.py @@ -3,42 +3,43 @@ import scipy.integrate -def hubbard1d_bethe_energy(t, u, interval=(1e-14, 30.75*np.pi), **kwargs): +def hubbard1d_bethe_energy(t, u, interval=(1e-14, 30.75 * np.pi), **kwargs): """Exact total energy per site for the 1D Hubbard model in the thermodynamic limit. from DOI: 10.1103/PhysRevB.77.045133.""" - kwargs['limit'] = kwargs.get('limit', 100) + kwargs["limit"] = kwargs.get("limit", 100) def func(x): j0 = scipy.special.jv(0, x) j1 = scipy.special.jv(1, x) - eu = np.exp((x*u)/(2*t)) - f = j0*j1/(x*(1 + eu)) + eu = np.exp((x * u) / (2 * t)) + f = j0 * j1 / (x * (1 + eu)) return f e, *res = scipy.integrate.quad(func, *interval, **kwargs) - e = -4*t*e + e = -4 * t * e return e -def hubbard1d_bethe_docc(t, u, interval=(1e-14, 30.75*np.pi), **kwargs): + +def hubbard1d_bethe_docc(t, u, interval=(1e-14, 30.75 * np.pi), **kwargs): """Exact on-site double occupancy for the 1D Hubbard model in the thermodynamic limit.""" - kwargs['limit'] = kwargs.get('limit', 100) + kwargs["limit"] = kwargs.get("limit", 100) def func(x): j0 = scipy.special.jv(0, x) j1 = scipy.special.jv(1, x) - eu = np.exp((x*u)/(2*t)) - #f = j0*j1/x * (-eu)/(1+eu)**2 * x/(2*t) + eu = np.exp((x * u) / (2 * t)) + # f = j0*j1/x * (-eu)/(1+eu)**2 * x/(2*t) # Avoid float overflow: - emu = np.exp(-(x*u)/(2*t)) - f = j0*j1/x * (-1)/(2+emu+eu) * x/(2*t) + emu = np.exp(-(x * u) / (2 * t)) + f = j0 * j1 / x * (-1) / (2 + emu + eu) * x / (2 * t) return f e, *res = scipy.integrate.quad(func, *interval, **kwargs) - e = -4*t*e + e = -4 * t * e return e @@ -49,21 +50,22 @@ def hubbard1d_bethe_docc_numdiff(t, u, du=1e-10, order=2, **kwargs): Calculated from the numerical differentiation of the exact Bethe ansatz energy.""" if order == 1: - em1 = hubbard1d_bethe_energy(t, u-du, **kwargs) - ep1 = hubbard1d_bethe_energy(t, u+du, **kwargs) - docc = (1/2*ep1 - 1/2*em1) / du + em1 = hubbard1d_bethe_energy(t, u - du, **kwargs) + ep1 = hubbard1d_bethe_energy(t, u + du, **kwargs) + docc = (1 / 2 * ep1 - 1 / 2 * em1) / du elif order == 2: - em2 = hubbard1d_bethe_energy(t, u-2*du, **kwargs) - em1 = hubbard1d_bethe_energy(t, u- du, **kwargs) - ep1 = hubbard1d_bethe_energy(t, u+ du, **kwargs) - ep2 = hubbard1d_bethe_energy(t, u+2*du, **kwargs) - docc = (1/12*em2 - 2/3*em1 + 2/3*ep1 - 1/12*ep2) / du + em2 = hubbard1d_bethe_energy(t, u - 2 * du, **kwargs) + em1 = hubbard1d_bethe_energy(t, u - du, **kwargs) + ep1 = hubbard1d_bethe_energy(t, u + du, **kwargs) + ep2 = hubbard1d_bethe_energy(t, u + 2 * du, **kwargs) + docc = (1 / 12 * em2 - 2 / 3 * em1 + 2 / 3 * ep1 - 1 / 12 * ep2) / du else: raise NotImplementedError() return docc -if __name__ == '__main__': + +if __name__ == "__main__": t = 1.0 for u in range(0, 13): e = hubbard1d_bethe_energy(t, u) diff --git a/vayesta/lattmod/latt.py b/vayesta/lattmod/latt.py index 598790e3f..1574c50c2 100644 --- a/vayesta/lattmod/latt.py +++ b/vayesta/lattmod/latt.py @@ -13,6 +13,7 @@ log = logging.getLogger(__name__) + class LatticeMole(pyscf.pbc.gto.Cell): """For PySCF compatibility @@ -39,10 +40,11 @@ def __init__(self, nsite, nelectron=None, spin=0, order=None, incore_anyway=True """ super().__init__(verbose=verbose, output=output) self.nsite = nsite - if nelectron is None: nelectron = nsite + if nelectron is None: + nelectron = nsite self.nelectron = nelectron self.spin = spin - self._basis = {self.atom_symbol(i) : None for i in range(self.nsite)} + self._basis = {self.atom_symbol(i): None for i in range(self.nsite)} self._built = True self.incore_anyway = incore_anyway self.order = order @@ -59,17 +61,17 @@ def nao_nr(self): def ao_labels(self, fmt=True): if fmt: - return ['%s%d' % (self.atom_pure_symbol(i), i) for i in range(self.nsite)] + return ["%s%d" % (self.atom_pure_symbol(i), i) for i in range(self.nsite)] elif fmt is None: - return [(i, self.atom_pure_symbol(i) , '', '') for i in range(self.nsite)] + return [(i, self.atom_pure_symbol(i), "", "") for i in range(self.nsite)] def atom_symbol(self, site): - return '%s%d' % (self.atom_pure_symbol(site), site) + return "%s%d" % (self.atom_pure_symbol(site), site) def atom_pure_symbol(self, site): - return 'S' + return "S" - #def build(self): + # def build(self): # pass def search_ao_label(self): @@ -87,51 +89,55 @@ def __init__(self, nsite, nelectron=None, spin=0, hubbard_t=1.0, hubbard_u=0.0, def aoslice_by_atom(self): """One basis function per site ("atom").""" - aorange = np.stack(4*[np.arange(self.nsite)], axis=1) - aorange[:,1] += 1 - aorange[:,3] += 1 + aorange = np.stack(4 * [np.arange(self.nsite)], axis=1) + aorange[:, 1] += 1 + aorange[:, 3] += 1 return aorange def ao2mo(self, mo_coeffs, compact=False): - if compact: raise NotImplementedError() - if self.v_nn: raise NotImplementedError() + if compact: + raise NotImplementedError() + if self.v_nn: + raise NotImplementedError() if isinstance(mo_coeffs, np.ndarray) and np.ndim(mo_coeffs) == 2: - eris = self.hubbard_u*einsum('ai,aj,ak,al->ijkl', mo_coeffs, mo_coeffs, mo_coeffs, mo_coeffs) + eris = self.hubbard_u * einsum("ai,aj,ak,al->ijkl", mo_coeffs, mo_coeffs, mo_coeffs, mo_coeffs) else: - eris = self.hubbard_u*einsum('ai,aj,ak,al->ijkl', *mo_coeffs) - eris = eris.reshape(eris.shape[0]*eris.shape[1], eris.shape[2]*eris.shape[3]) + eris = self.hubbard_u * einsum("ai,aj,ak,al->ijkl", *mo_coeffs) + eris = eris.reshape(eris.shape[0] * eris.shape[1], eris.shape[2] * eris.shape[3]) return eris class Hubbard1D(Hubbard): """Hubbard model in 1D.""" - def __init__(self, nsite, nelectron=None, spin=0, hubbard_t=1.0, hubbard_u=0.0, v_nn=0.0, boundary='auto', **kwargs): + def __init__( + self, nsite, nelectron=None, spin=0, hubbard_t=1.0, hubbard_u=0.0, v_nn=0.0, boundary="auto", **kwargs + ): super().__init__(nsite, nelectron, spin, hubbard_t, hubbard_u, v_nn=v_nn, **kwargs) self.nsites = [nsite] self.dimension = 1 - if boundary == 'auto': - if (nsite % 4 == 2): - boundary = 'PBC' - elif (nsite % 4 == 0): - boundary = 'APBC' + if boundary == "auto": + if nsite % 4 == 2: + boundary = "PBC" + elif nsite % 4 == 0: + boundary = "APBC" else: raise ValueError() log.debug("Automatically chosen boundary condition: %s", boundary) self.boundary = boundary - if boundary.upper() == 'PBC': + if boundary.upper() == "PBC": bfac = 1 - elif boundary.upper() == 'APBC': + elif boundary.upper() == "APBC": bfac = -1 self.bfac = bfac h1e = np.zeros((nsite, nsite)) - for i in range(nsite-1): - h1e[i,i+1] = h1e[i+1,i] = -hubbard_t - h1e[nsite-1,0] = h1e[0,nsite-1] = bfac * -hubbard_t + for i in range(nsite - 1): + h1e[i, i + 1] = h1e[i + 1, i] = -hubbard_t + h1e[nsite - 1, 0] = h1e[0, nsite - 1] = bfac * -hubbard_t if self.order is not None: - h1e = h1e[self.order][:,self.order] + h1e = h1e[self.order][:, self.order] self.h1e = h1e def get_eri(self, hubbard_u=None, v_nn=None): @@ -140,32 +146,31 @@ def get_eri(self, hubbard_u=None, v_nn=None): if v_nn is None: v_nn = self.v_nn - eri = np.zeros(4*[self.nsite]) + eri = np.zeros(4 * [self.nsite]) np.fill_diagonal(eri, hubbard_u) # Nearest-neighbor interaction if v_nn: - for i in range(self.nsite-1): - eri[i,i,i+1,i+1] = eri[i+1,i+1,i,i] = v_nn - eri[self.nsite-1,self.nsite-1,0,0] = eri[0,0,self.nsite-1,self.nsite-1] = v_nn + for i in range(self.nsite - 1): + eri[i, i, i + 1, i + 1] = eri[i + 1, i + 1, i, i] = v_nn + eri[self.nsite - 1, self.nsite - 1, 0, 0] = eri[0, 0, self.nsite - 1, self.nsite - 1] = v_nn if self.order is not None: # Not tested: order = self.order - eri = eri[order][:,order][:,:,order][:,:,:,order] + eri = eri[order][:, order][:, :, order][:, :, :, order] return eri - def lattice_vectors(self): """Lattice vectors of 1D Hubbard model. An arbitrary value of 1 A is assumed between sites. The lattice vectors, however, are saved in units of Bohr. """ rvecs = np.eye(3) - rvecs[0,0] = self.nsite + rvecs[0, 0] = self.nsite return rvecs / BOHR def atom_coords(self): coords = np.zeros((self.nsite, 3)) - coords[:,0] = np.arange(self.nsite) + coords[:, 0] = np.arange(self.nsite) if self.order is not None: coords = coords[self.order] return coords / BOHR @@ -178,11 +183,21 @@ def get_index(self, i): idx = i return idx, fac -class Hubbard2D(Hubbard): - def __init__(self, nsites, nelectron=None, spin=0, hubbard_t=1.0, hubbard_u=0.0, boundary='auto', - tiles=(1, 1), order=None, **kwargs): - nsite = nsites[0]*nsites[1] +class Hubbard2D(Hubbard): + def __init__( + self, + nsites, + nelectron=None, + spin=0, + hubbard_t=1.0, + hubbard_u=0.0, + boundary="auto", + tiles=(1, 1), + order=None, + **kwargs, + ): + nsite = nsites[0] * nsites[1] if order is None and tiles != (1, 1): order = self.get_tiles_order(nsites, tiles) super().__init__(nsite, nelectron, spin, hubbard_t, hubbard_u, order=order, **kwargs) @@ -190,20 +205,20 @@ def __init__(self, nsites, nelectron=None, spin=0, hubbard_t=1.0, hubbard_u=0.0, self.nsites = nsites self.dimension = 2 - if isinstance(boundary, str) and boundary.lower() == 'auto': + if isinstance(boundary, str) and boundary.lower() == "auto": if nelectron == nsite: if self.nsites[0] % 4 == 0 and self.nsites[1] % 4 == 0: - boundary = ('PBC', 'APBC') + boundary = ("PBC", "APBC") elif self.nsites[0] % 4 == 0 and self.nsites[1] % 4 == 2: - boundary = ('APBC', 'APBC') + boundary = ("APBC", "APBC") # Also possible: - #boundary = ('APBC', 'PBC') + # boundary = ('APBC', 'PBC') elif self.nsites[0] % 4 == 2 and self.nsites[1] % 4 == 0: - boundary = ('APBC', 'APBC') + boundary = ("APBC", "APBC") # Also possible: - #boundary = ('PBC', 'APBC') + # boundary = ('PBC', 'APBC') elif self.nsites[0] % 4 == 2 and self.nsites[1] % 4 == 2: - boundary = ('PBC', 'APBC') + boundary = ("PBC", "APBC") else: raise NotImplementedError("Please specify boundary conditions.") else: @@ -212,32 +227,32 @@ def __init__(self, nsites, nelectron=None, spin=0, hubbard_t=1.0, hubbard_u=0.0, boundary = (boundary, boundary) self.boundary = boundary - bfac = 2*[None] + bfac = 2 * [None] for i in range(2): - if boundary[i].lower() == 'open': + if boundary[i].lower() == "open": bfac[i] = 0 - elif boundary[i].lower() in ('periodic', 'pbc'): + elif boundary[i].lower() in ("periodic", "pbc"): bfac[i] = +1 - elif boundary[i].lower() in ('anti-periodic', 'apbc'): + elif boundary[i].lower() in ("anti-periodic", "apbc"): bfac[i] = -1 else: - raise ValueError('Invalid boundary: %s' % boundary[i]) - log.debugv('boundary phases= %r', bfac) + raise ValueError("Invalid boundary: %s" % boundary[i]) + log.debugv("boundary phases= %r", bfac) self.bfac = bfac h1e = np.zeros((nsite, nsite)) for i in range(nsites[0]): for j in range(nsites[1]): idx, _ = self.get_index(i, j) - idx_l, fac_l = self.get_index(i, j-1) - idx_r, fac_r = self.get_index(i, j+1) - idx_u, fac_u = self.get_index(i-1, j) - idx_d, fac_d = self.get_index(i+1, j) - h1e[idx,idx_l] += fac_l * -hubbard_t - h1e[idx,idx_r] += fac_r * -hubbard_t - h1e[idx,idx_u] += fac_u * -hubbard_t - h1e[idx,idx_d] += fac_d * -hubbard_t + idx_l, fac_l = self.get_index(i, j - 1) + idx_r, fac_r = self.get_index(i, j + 1) + idx_u, fac_u = self.get_index(i - 1, j) + idx_d, fac_d = self.get_index(i + 1, j) + h1e[idx, idx_l] += fac_l * -hubbard_t + h1e[idx, idx_r] += fac_r * -hubbard_t + h1e[idx, idx_u] += fac_u * -hubbard_t + h1e[idx, idx_d] += fac_d * -hubbard_t if self.order is not None: - h1e = h1e[self.order][:,self.order] + h1e = h1e[self.order][:, self.order] self.h1e = h1e def get_index(self, i, j): @@ -247,7 +262,7 @@ def get_index(self, i, j): fac *= bfac[0] if j % self.nsites[1] != j: fac *= bfac[1] - idx = (i%self.nsites[0])*self.nsites[1] + (j%self.nsites[1]) + idx = (i % self.nsites[0]) * self.nsites[1] + (j % self.nsites[1]) return idx, fac def get_eri(self, hubbard_u=None, v_nn=None): @@ -256,7 +271,7 @@ def get_eri(self, hubbard_u=None, v_nn=None): if v_nn is None: v_nn = self.v_nn - eri = np.zeros(4*[self.nsite]) + eri = np.zeros(4 * [self.nsite]) np.fill_diagonal(eri, hubbard_u) # Nearest-neighbor interaction if v_nn: @@ -269,8 +284,8 @@ def lattice_vectors(self): An arbitrary value of 1 A is assumed between sites. The lattice vectors, however, are saved in units of Bohr. """ rvecs = np.eye(3) - rvecs[0,0] = self.nsites[0] - rvecs[1,1] = self.nsites[1] + rvecs[0, 0] = self.nsites[0] + rvecs[1, 1] = self.nsites[1] return rvecs / BOHR def atom_coords(self): @@ -282,39 +297,39 @@ def atom_coords(self): """ coords = np.zeros((self.nsite, 3)) for row in range(self.nsites[1]): - slc = np.s_[row*self.nsites[0]:(row+1)*self.nsites[0]] - coords[slc,0] = np.arange(self.nsites[0]) - coords[slc,1] = row + slc = np.s_[row * self.nsites[0] : (row + 1) * self.nsites[0]] + coords[slc, 0] = np.arange(self.nsites[0]) + coords[slc, 1] = row if self.order is not None: coords = coords[self.order] return coords / BOHR @staticmethod def get_tiles_order(nsites, tiles): - assert(nsites[0] % tiles[0] == 0) - assert(nsites[1] % tiles[1] == 0) + assert nsites[0] % tiles[0] == 0 + assert nsites[1] % tiles[1] == 0 ntiles = [nsites[0] // tiles[0], nsites[1] // tiles[1]] - tsize = tiles[0]*tiles[1] + tsize = tiles[0] * tiles[1] def get_xy(site): tile, pos = divmod(site, tsize) ty, tx = divmod(tile, ntiles[0]) py, px = divmod(pos, tiles[0]) - return tx*tiles[0]+px, ty*tiles[1]+py + return tx * tiles[0] + px, ty * tiles[1] + py - nsite = nsites[0]*nsites[1] + nsite = nsites[0] * nsites[1] order = [] for site in range(nsite): x, y = get_xy(site) - idx = y*nsites[0] + x + idx = y * nsites[0] + x order.append(idx) return order class HubbardDF: - def __init__(self, mol): - if mol.v_nn: raise NotImplementedError() + if mol.v_nn: + raise NotImplementedError() self.mol = mol self.blockdim = self.get_naoaux() @@ -335,7 +350,6 @@ def loop(self, blksize=None): class LatticeSCF: - def __init__(self, mol, *args, **kwargs): super().__init__(mol, *args, **kwargs) if self.mol.incore_anyway: @@ -357,25 +371,28 @@ def density_fit(self): self.with_df = HubbardDF(self.mol) return self -class LatticeRHF(LatticeSCF, pyscf.scf.hf.RHF): +class LatticeRHF(LatticeSCF, pyscf.scf.hf.RHF): def get_init_guess(self, mol=None, key=None): e, c = np.linalg.eigh(self.get_hcore()) nocc = self.mol.nelectron // 2 - dm = 2*np.dot(c[:,:nocc], c[:,:nocc].T) + dm = 2 * np.dot(c[:, :nocc], c[:, :nocc].T) return dm def get_jk(self, mol=None, dm=None, *args, **kwargs): - if mol is None: mol = self.mol - if dm is None: dm = self.make_rdm1() + if mol is None: + mol = self.mol + if dm is None: + dm = self.make_rdm1() if self.mol.v_nn is not None and mol.v_nn != 0: raise NotImplementedError() - j = np.diag(np.diag(dm))*mol.hubbard_u + j = np.diag(np.diag(dm)) * mol.hubbard_u k = j return j, k def check_lattice_symmetry(self, dm=None): - if dm is None: dm = self.make_rdm1() + if dm is None: + dm = self.make_rdm1() occ = np.diag(dm) if not np.all(np.isclose(occ[0], occ)): log.warning("Mean-field not lattice symmetric! Site occupations=\n%r", occ) @@ -384,36 +401,37 @@ def check_lattice_symmetry(self, dm=None): class LatticeUHF(LatticeSCF, pyscf.scf.uhf.UHF): - def get_init_guess(self, mol=None, key=None): e, c = np.linalg.eigh(self.get_hcore()) nocc = self.mol.nelec - dma = np.dot(c[:,:nocc[0]], c[:,:nocc[0]].T) - dmb = np.dot(c[:,:nocc[1]], c[:,:nocc[1]].T) + dma = np.dot(c[:, : nocc[0]], c[:, : nocc[0]].T) + dmb = np.dot(c[:, : nocc[1]], c[:, : nocc[1]].T) # Create small random offset to break symmetries. offset = np.full_like(dma.diagonal(), fill_value=1e-2) if self.mol.dimension == 1: for x in range(self.mol.nsites[0]): ind, fac = self.mol.get_index(x) - offset[ind] *= (-1) ** (x%2) + offset[ind] *= (-1) ** (x % 2) elif self.mol.dimension == 2: for x in range(self.mol.nsites[0]): for y in range(self.mol.nsites[1]): - ind, fac = self.mol.get_index(x,y) - offset[ind] *= (-1) ** (x%2 + y%2) + ind, fac = self.mol.get_index(x, y) + offset[ind] *= (-1) ** (x % 2 + y % 2) else: raise NotImplementedError("LatticeUHF only supports 1- and 2-D Hubbard models.") dma[np.diag_indices_from(dma)] += offset return (dma, dmb) def get_jk(self, mol=None, dm=None, *args, **kwargs): - if mol is None: mol = self.mol - if dm is None: dm = self.make_rdm1() + if mol is None: + mol = self.mol + if dm is None: + dm = self.make_rdm1() if self.mol.v_nn is not None and mol.v_nn != 0: raise NotImplementedError() dma, dmb = dm - ja = np.diag(np.diag(dma))*mol.hubbard_u - jb = np.diag(np.diag(dmb))*mol.hubbard_u + ja = np.diag(np.diag(dma)) * mol.hubbard_u + jb = np.diag(np.diag(dmb)) * mol.hubbard_u ka, kb = ja, jb return (ja, jb), (ka, kb) diff --git a/vayesta/libs/__init__.py b/vayesta/libs/__init__.py index 49e05a5de..9fe5b215e 100644 --- a/vayesta/libs/__init__.py +++ b/vayesta/libs/__init__.py @@ -5,10 +5,11 @@ log = logging.getLogger(__name__) + def load_library(libname, required=False): try: - if not libname.startswith('lib'): - libname = 'lib' + libname + if not libname.startswith("lib"): + libname = "lib" + libname path = os.path.dirname(__file__) return np.ctypeslib.load_library(libname, path) except OSError as e: @@ -18,4 +19,5 @@ def load_library(libname, required=False): log.debug("Library %s not found", libname) return None -libcore = load_library('core') + +libcore = load_library("core") diff --git a/vayesta/misc/brueckner.py b/vayesta/misc/brueckner.py index dddc25ca5..cf5a808c4 100644 --- a/vayesta/misc/brueckner.py +++ b/vayesta/misc/brueckner.py @@ -7,15 +7,16 @@ log = logging.getLogger(__name__) + def update_mo_coeff(mo_coeff, t1, ovlp=None, damping=0.0, diis=None): nocc, nvir = t1.shape nmo = mo_coeff.shape[-1] - assert nocc+nvir == nmo + assert nocc + nvir == nmo occ = np.s_[:nocc] vir = np.s_[nocc:] - delta_occ = (1-damping)*np.dot(mo_coeff[:,vir], t1.T) # qa,ai->qi + delta_occ = (1 - damping) * np.dot(mo_coeff[:, vir], t1.T) # qa,ai->qi log.debug("Change of occupied Brueckner orbitals= %.3e", np.linalg.norm(delta_occ)) - bmo_occ = mo_coeff[:,occ] + delta_occ + bmo_occ = mo_coeff[:, occ] + delta_occ # Orthogonalize occupied orbitals if ovlp is None: @@ -23,30 +24,31 @@ def update_mo_coeff(mo_coeff, t1, ovlp=None, damping=0.0, diis=None): else: dm_occ = np.dot(bmo_occ, bmo_occ.T) e, v = scipy.linalg.eigh(dm_occ, b=ovlp, type=2) - bmo_occ = v[:,-nocc:] + bmo_occ = v[:, -nocc:] # PCDIIS of occupied density if diis: dm_occ = np.dot(bmo_occ, bmo_occ.T) dm_occ = diis.update(dm_occ) e, v = scipy.linalg.eigh(dm_occ, b=ovlp, type=2) - bmo_occ = v[:,-nocc:] + bmo_occ = v[:, -nocc:] # Virtual space if ovlp is None: - dm_vir = (np.eye(nmo) - np.dot(bmo_occ, bmo_occ.T)) + dm_vir = np.eye(nmo) - np.dot(bmo_occ, bmo_occ.T) else: dm_vir = np.linalg.inv(ovlp) - np.dot(bmo_occ, bmo_occ.T) e, v = scipy.linalg.eigh(dm_vir, b=ovlp, type=2) - bmo_vir = v[:,-nvir:] + bmo_vir = v[:, -nvir:] assert bmo_occ.shape[-1] == nocc assert bmo_vir.shape[-1] == nvir if ovlp is None: ovlp = np.eye(nmo) bmo = np.hstack((bmo_occ, bmo_vir)) - assert np.allclose(np.linalg.multi_dot((bmo.T, ovlp, bmo))-np.eye(nmo), 0) + assert np.allclose(np.linalg.multi_dot((bmo.T, ovlp, bmo)) - np.eye(nmo), 0) return bmo_occ, bmo_vir + def update_mf(mf, t1, mo_coeff=None, inplace=False, canonicalize=True, damping=0.0, diis=None): """Update occupied MOs based on T1 amplitudes, to converge to Brueckner MOs. @@ -73,18 +75,20 @@ def update_mf(mf, t1, mo_coeff=None, inplace=False, canonicalize=True, damping=0 if not inplace: mf = copy.copy(mf) - if mo_coeff is None: mo_coeff = mf.mo_coeff + if mo_coeff is None: + mo_coeff = mf.mo_coeff nmo = mo_coeff.shape[-1] nocc = np.count_nonzero(mf.mo_occ > 0) - nvir = (nmo-nocc) + nvir = nmo - nocc assert t1.shape == (nocc, nvir) ovlp = mf.get_ovlp() - if np.allclose(ovlp, np.eye(ovlp.shape[-1])): ovlp = None + if np.allclose(ovlp, np.eye(ovlp.shape[-1])): + ovlp = None bmo_occ, bmo_vir = update_mo_coeff(mo_coeff, t1, ovlp, damping=damping, diis=diis) # Diagonalize one-electron Hamiltonian or Fock matrix within occupied and virtual space: if canonicalize: - if canonicalize == 'hcore': + if canonicalize == "hcore": h1e = mf.get_hcore() else: h1e = mf.get_fock() diff --git a/vayesta/misc/corrfunc.py b/vayesta/misc/corrfunc.py index 64f619e9c..67c46dd1f 100644 --- a/vayesta/misc/corrfunc.py +++ b/vayesta/misc/corrfunc.py @@ -11,6 +11,7 @@ def _get_proj_per_spin(p): return p, p raise ValueError() + def chargecharge(dm1, dm2, proj1=None, proj2=None, subtract_indep=True): if dm2 is None: return chargecharge_mf(dm1, proj1=proj1, proj2=proj2, subtract_indep=subtract_indep) @@ -18,50 +19,53 @@ def chargecharge(dm1, dm2, proj1=None, proj2=None, subtract_indep=True): if proj2 is None: proj2 = proj1 if proj1 is None: - corr = einsum('iijj->', dm2) + np.trace(dm1) + corr = einsum("iijj->", dm2) + np.trace(dm1) if subtract_indep: - corr -= np.trace(dm1)**2 + corr -= np.trace(dm1) ** 2 return corr - corr = einsum('(ij,ijkl),kl->', proj1, dm2, proj2) - corr += einsum('ij,ik,jk->', dm1, proj1, proj2) + corr = einsum("(ij,ijkl),kl->", proj1, dm2, proj2) + corr += einsum("ij,ik,jk->", dm1, proj1, proj2) if subtract_indep: corr -= np.sum(dm1 * proj1) * np.sum(dm1 * proj2) return corr + # --- Correlated: + def spin_z(dm1, proj=None): return 0.0 + def spin_z_unrestricted(dm1, proj=None): dm1a, dm1b = dm1 if proj is None: - sz = (np.trace(dm1a) - np.trace(dm1b))/2 + sz = (np.trace(dm1a) - np.trace(dm1b)) / 2 return sz pa, pb = _get_proj_per_spin(proj) - sz = (einsum('ij,ij->', dm1a, pa) - - einsum('ij,ij->', dm1b, pb))/2 + sz = (einsum("ij,ij->", dm1a, pa) - einsum("ij,ij->", dm1b, pb)) / 2 return sz + def spinspin_z(dm1, dm2, proj1=None, proj2=None): if dm2 is None: return spinspin_z_mf(dm1, proj1=proj1, proj2=proj2) - dm1a = dm1/2 - dm2aa = (dm2 - dm2.transpose(0,3,2,1)) / 6 - dm2ab = (dm2/2 - dm2aa) + dm1a = dm1 / 2 + dm2aa = (dm2 - dm2.transpose(0, 3, 2, 1)) / 6 + dm2ab = dm2 / 2 - dm2aa if proj2 is None: proj2 = proj1 if proj1 is None: - ssz = (einsum('iijj->', dm2aa) - einsum('iijj->', dm2ab))/2 - ssz += np.trace(dm1a)/2 + ssz = (einsum("iijj->", dm2aa) - einsum("iijj->", dm2ab)) / 2 + ssz += np.trace(dm1a) / 2 return ssz - ssz = (einsum('ijkl,ij,kl->', dm2aa, proj1, proj2) - - einsum('ijkl,ij,kl->', dm2ab, proj1, proj2))/2 - ssz += einsum('ij,ik,jk->', dm1a, proj1, proj2)/2 + ssz = (einsum("ijkl,ij,kl->", dm2aa, proj1, proj2) - einsum("ijkl,ij,kl->", dm2ab, proj1, proj2)) / 2 + ssz += einsum("ij,ik,jk->", dm1a, proj1, proj2) / 2 return ssz + def spinspin_z_unrestricted(dm1, dm2, proj1=None, proj2=None): if dm2 is None: return spinspin_z_mf_unrestricted(dm1, proj1=proj1, proj2=proj2) @@ -71,22 +75,24 @@ def spinspin_z_unrestricted(dm1, dm2, proj1=None, proj2=None): if proj2 is None: proj2 = proj1 if proj1 is None: - ssz = (einsum('iijj->', dm2aa)/4 + einsum('iijj->', dm2bb)/4 - - einsum('iijj->', dm2ab)/2) - ssz += (np.trace(dma) + np.trace(dmb))/4 + ssz = einsum("iijj->", dm2aa) / 4 + einsum("iijj->", dm2bb) / 4 - einsum("iijj->", dm2ab) / 2 + ssz += (np.trace(dma) + np.trace(dmb)) / 4 return ssz p1a, p1b = _get_proj_per_spin(proj1) p2a, p2b = _get_proj_per_spin(proj2) - ssz = (einsum('ijkl,ij,kl->', dm2aa, p1a, p2a)/4 - + einsum('ijkl,ij,kl->', dm2bb, p1b, p2b)/4 - - einsum('ijkl,ij,kl->', dm2ab, p1a, p2b)/4 - - einsum('ijkl,ij,kl->', dm2ab, p2a, p1b)/4) - ssz += (einsum('ij,ik,jk->', dm1a, p1a, p2a) - + einsum('ij,ik,jk->', dm1b, p1b, p2b))/4 + ssz = ( + einsum("ijkl,ij,kl->", dm2aa, p1a, p2a) / 4 + + einsum("ijkl,ij,kl->", dm2bb, p1b, p2b) / 4 + - einsum("ijkl,ij,kl->", dm2ab, p1a, p2b) / 4 + - einsum("ijkl,ij,kl->", dm2ab, p2a, p1b) / 4 + ) + ssz += (einsum("ij,ik,jk->", dm1a, p1a, p2a) + einsum("ij,ik,jk->", dm1b, p1b, p2b)) / 4 return ssz + # --- Mean-field: + def chargecharge_mf(dm1, proj1=None, proj2=None, subtract_indep=True): if proj2 is None: proj2 = proj1 @@ -94,65 +100,70 @@ def chargecharge_mf(dm1, proj1=None, proj2=None, subtract_indep=True): if subtract_indep: return 0 else: - return np.trace(dm1)**2 + return np.trace(dm1) ** 2 if subtract_indep: corr = 0 else: - corr = np.sum(dm1*proj1) * np.sum(dm1*proj2) - corr -= einsum('(ik,ij),(kl,lj)->', proj1, dm1, dm1, proj2)/2 - corr += einsum('ij,ik,jk->', dm1, proj1, proj2) + corr = np.sum(dm1 * proj1) * np.sum(dm1 * proj2) + corr -= einsum("(ik,ij),(kl,lj)->", proj1, dm1, dm1, proj2) / 2 + corr += einsum("ij,ik,jk->", dm1, proj1, proj2) return corr + def spinspin_z_mf(dm1, proj1=None, proj2=None): # TEMP: - dm1 = (dm1/2, dm1/2) + dm1 = (dm1 / 2, dm1 / 2) return spinspin_z_mf_unrestricted(dm1=dm1, proj1=proj1, proj2=proj2) - #if proj2 is None: + # if proj2 is None: # proj2 = proj1 - #if proj1 is None: + # if proj1 is None: # ssz = np.trace(dm1)/4 - einsum('ij,ij->', dm1, dm1)/8 # return ssz # TODO: - #ssz = (einsum('ij,kl,ij,kl->', dma, dma, p1a, p2a) + # ssz = (einsum('ij,kl,ij,kl->', dma, dma, p1a, p2a) # - einsum('il,jk,ij,kl->', dma, dma, p1a, p2a) # + einsum('ij,kl,ij,kl->', dmb, dmb, p1b, p2b) # - einsum('il,jk,ij,kl->', dmb, dmb, p1b, p2b) # - einsum('ij,kl,ij,kl->', dma, dmb, p1a, p2b) # - einsum('ij,kl,ij,kl->', dmb, dma, p1b, p2a))/4 - #ssz += (einsum('ij,ik,jk->', dma, p1a, p2a) + # ssz += (einsum('ij,ik,jk->', dma, p1a, p2a) # + einsum('ij,ik,jk->', dmb, p1b, p2b))/4 - #return ssz + # return ssz + def spinspin_z_mf_unrestricted(dm1, proj1=None, proj2=None): dma, dmb = dm1 if proj2 is None: proj2 = proj1 if proj1 is None: - ssz = (einsum('ii,jj->', dma, dma)/4 - - einsum('ij,ij->', dma, dma)/4 - + einsum('ii,jj->', dmb, dmb)/4 - - einsum('ij,ij->', dmb, dmb)/4 - - einsum('ii,jj->', dma, dmb)/2) - ssz += (np.trace(dma) + np.trace(dmb))/4 + ssz = ( + einsum("ii,jj->", dma, dma) / 4 + - einsum("ij,ij->", dma, dma) / 4 + + einsum("ii,jj->", dmb, dmb) / 4 + - einsum("ij,ij->", dmb, dmb) / 4 + - einsum("ii,jj->", dma, dmb) / 2 + ) + ssz += (np.trace(dma) + np.trace(dmb)) / 4 return ssz p1a, p1b = (proj1, proj1) if np.ndim(proj1[0]) == 1 else proj1 p2a, p2b = (proj2, proj2) if np.ndim(proj2[0]) == 1 else proj2 - ssz = (einsum('(ij,ij),(kl,kl)->', p1a, dma, dma, p2a) - - einsum('(ij,il),(jk,kl)->', p1a, dma, dma, p2a) - + einsum('(ij,ij),(kl,kl)->', p1b, dmb, dmb, p2b) - - einsum('(ij,il),(jk,kl)->', p1b, dmb, dmb, p2b) - - einsum('(ij,ij),(kl,kl)->', p1a, dma, dmb, p2b) - - einsum('(ij,ij),(kl,kl)->', p1b, dmb, dma, p2a))/4 - ssz += (einsum('ij,ik,jk->', dma, p1a, p2a) - + einsum('ij,ik,jk->', dmb, p1b, p2b))/4 + ssz = ( + einsum("(ij,ij),(kl,kl)->", p1a, dma, dma, p2a) + - einsum("(ij,il),(jk,kl)->", p1a, dma, dma, p2a) + + einsum("(ij,ij),(kl,kl)->", p1b, dmb, dmb, p2b) + - einsum("(ij,il),(jk,kl)->", p1b, dmb, dmb, p2b) + - einsum("(ij,ij),(kl,kl)->", p1a, dma, dmb, p2b) + - einsum("(ij,ij),(kl,kl)->", p1b, dmb, dma, p2a) + ) / 4 + ssz += (einsum("ij,ik,jk->", dma, p1a, p2a) + einsum("ij,ik,jk->", dmb, p1b, p2b)) / 4 return ssz -if __name__ == '__main__': +if __name__ == "__main__": import pyscf import pyscf.gto import pyscf.scf @@ -163,10 +174,10 @@ def spinspin_z_mf_unrestricted(dm1, proj1=None, proj2=None): H 0.0000 0.7572 -0.4692 H 0.0000 -0.7572 -0.4692 """ - mol.basis = 'cc-pVDZ' + mol.basis = "cc-pVDZ" mol.build() nmo = mol.nao - nocc = mol.nelectron//2 + nocc = mol.nelectron // 2 # RHF rhf = pyscf.scf.RHF(mol) @@ -176,12 +187,10 @@ def spinspin_z_mf_unrestricted(dm1, proj1=None, proj2=None): sz = spin_z(dm1) print(sz) - ssz = spinspin_z_rhf(dm1) print(ssz) - 1/0 - #print('RHF: = %.8f = %.8f' % (sz, ssz)) - + 1 / 0 + # print('RHF: = %.8f = %.8f' % (sz, ssz)) # UHF mol.charge = mol.spin = 1 @@ -203,7 +212,7 @@ def spinspin_z_mf_unrestricted(dm1, proj1=None, proj2=None): print(sz) sz = spin_z_unrestricted(dm1) print(sz) - #ssz = spinspin_z_uhf(uhf) - #print('UHF: = %.8f = %.8f' % (sz, ssz)) - #ssz = spinspin_z_unrestricted(uhf) - #print('UHF: = %.8f = %.8f' % (sz, ssz)) + # ssz = spinspin_z_uhf(uhf) + # print('UHF: = %.8f = %.8f' % (sz, ssz)) + # ssz = spinspin_z_unrestricted(uhf) + # print('UHF: = %.8f = %.8f' % (sz, ssz)) diff --git a/vayesta/misc/counterpoise.py b/vayesta/misc/counterpoise.py index b52ca87d9..7f2940b41 100644 --- a/vayesta/misc/counterpoise.py +++ b/vayesta/misc/counterpoise.py @@ -3,14 +3,15 @@ import numpy as np import pyscf -#import pyscf.scf + +# import pyscf.scf import pyscf.lib log = logging.getLogger(__name__) # TODO -#def get_cp_energy(emb, rmax, nimages=1, unit='A'): +# def get_cp_energy(emb, rmax, nimages=1, unit='A'): # """Get counterpoise correction for a given embedding problem.""" # mol = emb.kcell or emb.mol # @@ -27,7 +28,7 @@ # mf_cp.kernel() -def make_cp_mol(mol, atom, rmax, nimages=1, unit='A', **kwargs): +def make_cp_mol(mol, atom, rmax, nimages=1, unit="A", **kwargs): """Make molecule object for counterposise calculation. WARNING: This has only been tested for periodic systems so far! @@ -56,68 +57,73 @@ def make_cp_mol(mol, atom, rmax, nimages=1, unit='A', **kwargs): and with ghost atoms added depending on `rmax` and `nimages`. """ # Add physical atom - atoms = [(mol.atom_symbol(atom), mol.atom_coord(atom, unit='ANG'))] + atoms = [(mol.atom_symbol(atom), mol.atom_coord(atom, unit="ANG"))] log.debugv("Counterpoise: adding atom %6s at %8.5f %8.5f %8.5f A", atoms[0][0], *(atoms[0][1])) # If rmax > 0: Atomic calculation with additional basis functions # else: Atom only if rmax: - dim = getattr(mol, 'dimension', 0) + dim = getattr(mol, "dimension", 0) images = np.zeros(3, dtype=int) if dim == 0: - pass # Open boundary conditions - do not create images + pass # Open boundary conditions - do not create images elif dim == 1: images[0] = nimages elif dim == 2: images[:2] = nimages elif dim == 3: images[:] = nimages - log.debugv('Counterpoise images= %r', images) - center = mol.atom_coord(atom, unit='ANG') - log.debugv('Counterpoise center= %r %s', center, unit) + log.debugv("Counterpoise images= %r", images) + center = mol.atom_coord(atom, unit="ANG") + log.debugv("Counterpoise center= %r %s", center, unit) # Lattice vectors in Angstrom - if hasattr(mol, 'lattice_vectors'): + if hasattr(mol, "lattice_vectors"): amat = pyscf.lib.param.BOHR * mol.lattice_vectors() - log.debugv('Latt. vec.= %r', amat) - log.debugv('mol.unit= %r', mol.unit) - log.debugv('amat= %r', amat) + log.debugv("Latt. vec.= %r", amat) + log.debugv("mol.unit= %r", mol.unit) + log.debugv("amat= %r", amat) # Convert rmax to Angstrom - log.debugv('rmax= %.8f %s', rmax, unit) - if unit == 'A': + log.debugv("rmax= %.8f %s", rmax, unit) + if unit == "A": pass - elif unit == 'B': + elif unit == "B": rmax *= pyscf.lib.param.BOHR - elif unit.startswith('a'): + elif unit.startswith("a"): rmax *= np.linalg.norm(amat[int(unit[1])]) else: raise ValueError("Unknown unit: %r" % unit) - log.debugv('rmax= %.8f A', rmax) + log.debugv("rmax= %.8f A", rmax) # Add ghost atoms. Note that rx = ry = rz = 0 for open boundary conditions - for rx in range(-images[0], images[0]+1): - for ry in range(-images[1], images[1]+1): - for rz in range(-images[2], images[2]+1): + for rx in range(-images[0], images[0] + 1): + for ry in range(-images[1], images[1] + 1): + for rz in range(-images[2], images[2] + 1): for atm in range(mol.natm): symb = mol.atom_symbol(atm) - coord = mol.atom_coord(atm, unit='ANG') + coord = mol.atom_coord(atm, unit="ANG") # This is a fragment atom - already included above as real atom - if (abs(rx)+abs(ry)+abs(rz) == 0) and (atm == atom): - assert (symb == atoms[0][0]) + if (abs(rx) + abs(ry) + abs(rz) == 0) and (atm == atom): + assert symb == atoms[0][0] assert np.allclose(coord, atoms[0][1]) continue # This is either a non-fragment atom in the unit cell (rx = ry = rz = 0) or in a neighbor cell - if abs(rx)+abs(ry)+abs(rz) > 0: - coord += (rx*amat[0] + ry*amat[1] + rz*amat[2]) - if not symb.lower().startswith('ghost'): - symb = 'Ghost-' + symb + if abs(rx) + abs(ry) + abs(rz) > 0: + coord += rx * amat[0] + ry * amat[1] + rz * amat[2] + if not symb.lower().startswith("ghost"): + symb = "Ghost-" + symb distance = np.linalg.norm(coord - center) if (not rmax) or (distance <= rmax): - log.debugv("Counterpoise: adding atom %6s at %8.5f %8.5f %8.5f with distance %8.5f A", symb, *coord, distance) + log.debugv( + "Counterpoise: adding atom %6s at %8.5f %8.5f %8.5f with distance %8.5f A", + symb, + *coord, + distance, + ) atoms.append((symb, coord)) - log.info("Counterpoise with rmax %.3f A -> %3d ghost atoms", rmax, (len(atoms)-1)) + log.info("Counterpoise with rmax %.3f A -> %3d ghost atoms", rmax, (len(atoms) - 1)) mol_cp = mol.copy() mol_cp.atom = atoms - mol_cp.unit = 'ANG' + mol_cp.unit = "ANG" mol_cp.a = None mol_cp.dimension = 0 for key, val in kwargs.items(): diff --git a/vayesta/misc/cptbisect.py b/vayesta/misc/cptbisect.py index 75a3c26a7..812bdcf1b 100644 --- a/vayesta/misc/cptbisect.py +++ b/vayesta/misc/cptbisect.py @@ -5,7 +5,6 @@ class ChempotBisection: - def __init__(self, func, cpt_init=0.0, tol=1e-8, maxiter=30, robust=False, log=None): self.func = func self.cpt = cpt_init @@ -14,10 +13,9 @@ def __init__(self, func, cpt_init=0.0, tol=1e-8, maxiter=30, robust=False, log=N self.tol = tol self.maxiter = maxiter self.robust = robust - self.log = (log or vayesta.log) + self.log = log or vayesta.log def kernel(self, *args, **kwargs): - cpt1 = self.cpt err1 = self.func(cpt1, *args, **kwargs) if abs(err1) <= self.tol: @@ -25,9 +23,9 @@ def kernel(self, *args, **kwargs): return cpt1 # Linear extrapolation for bounds - cpt2 = cpt1 + np.sign(err1)*1e-3 + cpt2 = cpt1 + np.sign(err1) * 1e-3 err2 = self.func(cpt2, *args, **kwargs) - m = (err2-err1) / (cpt2-cpt1) + m = (err2 - err1) / (cpt2 - cpt1) self.log.debug("cpt1= %.8f err1= %.3e", cpt1, err1) self.log.debug("cpt2= %.8f err2= %.3e", cpt2, err2) self.log.debug("gradient m= %.3e", m) @@ -38,33 +36,35 @@ def kernel(self, *args, **kwargs): else: base, exp0 = 2, 1 for exp in range(exp0, 4): - cpt3 = cpt1 - (base**exp)*err1/m + cpt3 = cpt1 - (base**exp) * err1 / m err3 = self.func(cpt3, *args, **kwargs) self.log.debug("cpt3= %.8f err3= %.3e", cpt3, err3) if (err1 * err3) <= 0: - self.log.debug('Chemical potential in [%.8f, %.8f] (errors=[%.3e, %.3e])' % (cpt1, cpt3, err1, err3)) + self.log.debug("Chemical potential in [%.8f, %.8f] (errors=[%.3e, %.3e])" % (cpt1, cpt3, err1, err3)) break - self.log.info('Chemical potential not in [%.8f, %.8f] (errors=[%.3e, %.3e])' % (cpt1, cpt3, err1, err3)) + self.log.info("Chemical potential not in [%.8f, %.8f] (errors=[%.3e, %.3e])" % (cpt1, cpt3, err1, err3)) else: - raise ValueError('Chemical potential not in [%.8f, %.8f] (errors=[%.3e, %.3e])' % (cpt1, cpt3, err1, err3)) + raise ValueError("Chemical potential not in [%.8f, %.8f] (errors=[%.3e, %.3e])" % (cpt1, cpt3, err1, err3)) lower = min(cpt1, cpt3) upper = max(cpt1, cpt3) it = 1 + def iteration(cpt, *args, **kwargs): nonlocal it - if (cpt == cpt1): + if cpt == cpt1: err = err1 - elif (cpt == cpt3): + elif cpt == cpt3: err = err3 else: err = self.func(cpt, *args, **kwargs) - self.log.info('Chemical potential iteration= %3d cpt= %+12.8f err= %.3e', it, cpt, err) + self.log.info("Chemical potential iteration= %3d cpt= %+12.8f err= %.3e", it, cpt, err) self.cpt = cpt it += 1 if abs(err) <= self.tol: raise StopIteration return err + try: res = scipy.optimize.brentq(iteration, lower, upper) except StopIteration: @@ -72,10 +72,10 @@ def iteration(cpt, *args, **kwargs): return self.cpt -if __name__ == '__main__': +if __name__ == "__main__": def func(cpt): - return 0.1*cpt + 1 + return 0.1 * cpt + 1 bisect = ChempotBisection(func) cpt = bisect.kernel() diff --git a/vayesta/misc/cubefile.py b/vayesta/misc/cubefile.py index cf63c4337..bfdf6714a 100644 --- a/vayesta/misc/cubefile.py +++ b/vayesta/misc/cubefile.py @@ -17,10 +17,17 @@ class CubeFile: - - def __init__(self, cell, filename=None, gridsize=(100, 100, 100), resolution=None, - title=None, comment=None, fmt="%13.5E", - crop=None): + def __init__( + self, + cell, + filename=None, + gridsize=(100, 100, 100), + resolution=None, + title=None, + comment=None, + fmt="%13.5E", + crop=None, + ): """Initialize a cube file object. Data can be added using the `add_orbital` and `add_density` methods. This class can also be used as a context manager: @@ -69,18 +76,18 @@ def __init__(self, cell, filename=None, gridsize=(100, 100, 100), resolution=Non if crop is not None: a = self.a.copy() norm = np.linalg.norm(self.a, axis=1) - a[0] -= (crop.get('a0', 0) + crop.get('a1', 0)) * self.a[0]/norm[0] - a[1] -= (crop.get('b0', 0) + crop.get('b1', 0)) * self.a[1]/norm[1] - a[2] -= (crop.get('c0', 0) + crop.get('c1', 0)) * self.a[2]/norm[2] - self.origin += crop.get('a0', 0)*self.a[0]/norm[0] - self.origin += crop.get('b0', 0)*self.a[1]/norm[1] - self.origin += crop.get('c0', 0)*self.a[2]/norm[2] + a[0] -= (crop.get("a0", 0) + crop.get("a1", 0)) * self.a[0] / norm[0] + a[1] -= (crop.get("b0", 0) + crop.get("b1", 0)) * self.a[1] / norm[1] + a[2] -= (crop.get("c0", 0) + crop.get("c1", 0)) * self.a[2] / norm[2] + self.origin += crop.get("a0", 0) * self.a[0] / norm[0] + self.origin += crop.get("b0", 0) * self.a[1] / norm[1] + self.origin += crop.get("c0", 0) * self.a[2] / norm[2] self.a = a # Use resolution if provided, else gridsize if resolution is not None: - self.nx = min(np.ceil(abs(self.a[0,0]) * resolution).astype(int), 192) - self.ny = min(np.ceil(abs(self.a[1,1]) * resolution).astype(int), 192) - self.nz = min(np.ceil(abs(self.a[2,2]) * resolution).astype(int), 192) + self.nx = min(np.ceil(abs(self.a[0, 0]) * resolution).astype(int), 192) + self.ny = min(np.ceil(abs(self.a[1, 1]) * resolution).astype(int), 192) + self.nz = min(np.ceil(abs(self.a[2, 2]) * resolution).astype(int), 192) else: self.nx, self.ny, self.nz = gridsize self.title = title or "" @@ -92,7 +99,7 @@ def __init__(self, cell, filename=None, gridsize=(100, 100, 100), resolution=Non @property def has_pbc(self): - return hasattr(self.cell, 'lattice_vectors') + return hasattr(self.cell, "lattice_vectors") def get_box_and_origin(self): if self.has_pbc: @@ -101,24 +108,24 @@ def get_box_and_origin(self): else: coord = self.cell.atom_coords() margin = 3.0 - extent = np.max(coord, axis=0) - np.min(coord, axis=0) + 2*margin + extent = np.max(coord, axis=0) - np.min(coord, axis=0) + 2 * margin box = np.diag(extent) origin = np.asarray(np.min(coord, axis=0) - margin) return box, origin def get_coords(self): - xs = np.arange(self.nx) / (self.nx-1) - ys = np.arange(self.ny) / (self.ny-1) - zs = np.arange(self.nz) / (self.nz-1) + xs = np.arange(self.nx) / (self.nx - 1) + ys = np.arange(self.ny) / (self.ny - 1) + zs = np.arange(self.nz) / (self.nz - 1) coords = pyscf.lib.cartesian_prod([xs, ys, zs]) coords = np.dot(coords, self.a) - coords = np.asarray(coords, order='C') + self.origin + coords = np.asarray(coords, order="C") + self.origin return coords @property def ncoords(self): """Number of grod points.""" - return self.nx*self.ny*self.nz + return self.nx * self.ny * self.nz @property def nfields(self): @@ -126,13 +133,13 @@ def nfields(self): return len(self.fields) def save_state(self, filename): - cell, self.cell = self.cell, None # Do not pickle cell - pickle.dump(self, open(filename, 'wb')) - self.cell = cell # Restore self.cell + cell, self.cell = self.cell, None # Do not pickle cell + pickle.dump(self, open(filename, "wb")) + self.cell = cell # Restore self.cell @classmethod def load_state(cls, filename, cell=None): - self = pickle.load(open(filename, 'rb')) + self = pickle.load(open(filename, "rb")) self.cell = cell return self @@ -157,12 +164,13 @@ def add_orbital(self, coeff, dset_idx=None): be labelled as 'Orbital <dset_idx>' or similar. If set to `None`, the smallest unused, positive integer will be used. Default: None. """ - coeff = np.array(coeff) # Force copy - if coeff.ndim == 1: coeff = coeff[:,np.newaxis] - assert (coeff.ndim == 2) + coeff = np.array(coeff) # Force copy + if coeff.ndim == 1: + coeff = coeff[:, np.newaxis] + assert coeff.ndim == 2 for i, c in enumerate(coeff.T): - idx = dset_idx+i if dset_idx is not None else None - self.fields.append((c, 'orbital', idx)) + idx = dset_idx + i if dset_idx is not None else None + self.fields.append((c, "orbital", idx)) def add_density(self, dm, dset_idx=None): """Add one or more densities to the cube file. @@ -178,29 +186,30 @@ def add_density(self, dm, dset_idx=None): be labelled as 'Orbital <dset_idx>' or similar. If set to `None`, the smallest unused, positive integer will be used. Default: None. """ - dm = np.array(dm) # Force copy - if dm.ndim == 2: dm = dm[np.newaxis] - assert (dm.ndim == 3) + dm = np.array(dm) # Force copy + if dm.ndim == 2: + dm = dm[np.newaxis] + assert dm.ndim == 3 for i, d in enumerate(dm): - idx = dset_idx+i if dset_idx is not None else None - self.fields.append((d, 'density', idx)) + idx = dset_idx + i if dset_idx is not None else None + self.fields.append((d, "density", idx)) def add_mep(self, dm, dset_idx=None): # TODO raise NotImplementedError() def write(self, filename=None): - filename = (filename or self.filename) + filename = filename or self.filename # Create directories if necessary directory = os.path.dirname(filename) if directory: os.makedirs(directory, exist_ok=True) # Get dataset IDs dset_ids = [] - for (field, ftype, fid) in self.fields: + for field, ftype, fid in self.fields: if fid is None: if dset_ids: - fid = np.max(dset_ids)+1 + fid = np.max(dset_ids) + 1 else: fid = 1 dset_ids.append(fid) @@ -211,65 +220,61 @@ def write(self, filename=None): def write_header(self, filename, dset_ids=None): """Write header of cube-file.""" if self.nfields > 1 and dset_ids is None: - dset_ids = range(1, self.nfields+1) - with open(filename, 'w') as f: - f.write('%s\n' % self.title) - f.write('%s\n' % self.comment) + dset_ids = range(1, self.nfields + 1) + with open(filename, "w") as f: + f.write("%s\n" % self.title) + f.write("%s\n" % self.comment) if self.nfields > 1: - f.write('%5d' % -self.cell.natm) + f.write("%5d" % -self.cell.natm) else: - f.write('%5d' % self.cell.natm) - f.write('%12.6f%12.6f%12.6f' % tuple(self.origin)) + f.write("%5d" % self.cell.natm) + f.write("%12.6f%12.6f%12.6f" % tuple(self.origin)) if self.nfields > 1: - f.write('%5d' % self.nfields) - f.write('\n') + f.write("%5d" % self.nfields) + f.write("\n") # Lattice vectors - f.write('%5d%12.6f%12.6f%12.6f\n' % (self.nx, *(self.a[0]/(self.nx-1)))) - f.write('%5d%12.6f%12.6f%12.6f\n' % (self.ny, *(self.a[1]/(self.ny-1)))) - f.write('%5d%12.6f%12.6f%12.6f\n' % (self.nz, *(self.a[2]/(self.nz-1)))) + f.write("%5d%12.6f%12.6f%12.6f\n" % (self.nx, *(self.a[0] / (self.nx - 1)))) + f.write("%5d%12.6f%12.6f%12.6f\n" % (self.ny, *(self.a[1] / (self.ny - 1)))) + f.write("%5d%12.6f%12.6f%12.6f\n" % (self.nz, *(self.a[2] / (self.nz - 1)))) # Atoms for atm in range(self.cell.natm): sym = self.cell.atom_symbol(atm) - f.write('%5d%12.6f' % (pyscf.gto.charge(sym), 0.0)) - f.write('%12.6f%12.6f%12.6f\n' % tuple(self.cell.atom_coords()[atm])) + f.write("%5d%12.6f" % (pyscf.gto.charge(sym), 0.0)) + f.write("%12.6f%12.6f%12.6f\n" % tuple(self.cell.atom_coords()[atm])) # Data set indices if self.nfields > 1: - f.write('%5d' % self.nfields) + f.write("%5d" % self.nfields) for i in range(self.nfields): - f.write('%5d' % dset_ids[i]) - f.write('\n') + f.write("%5d" % dset_ids[i]) + f.write("\n") def write_fields(self, filename): """Write voxel data of registered fields in `self.fields` to cube-file.""" blksize = min(self.ncoords, 8000) - with open(filename, 'a') as f: + with open(filename, "a") as f: # Loop over x,y,z coordinates first, then fields! for blk0, blk1 in pyscf.lib.prange(0, self.ncoords, blksize): - data = np.zeros((blk1-blk0, self.nfields)) + data = np.zeros((blk1 - blk0, self.nfields)) blk = np.s_[blk0:blk1] - intor = 'PBCGTOval' if self.has_pbc else 'GTOval' + intor = "PBCGTOval" if self.has_pbc else "GTOval" ao = self.cell.eval_gto(intor, self.coords[blk]) for i, (field, ftype, _) in enumerate(self.fields): - if ftype == 'orbital': - data[:,i] = np.dot(ao, field) - elif ftype == 'density': - data[:,i] = numint.eval_rho(self.cell, ao, field) + if ftype == "orbital": + data[:, i] = np.dot(ao, field) + elif ftype == "density": + data[:, i] = numint.eval_rho(self.cell, ao, field) else: - raise ValueError('Unknown field type: %s' % ftype) + raise ValueError("Unknown field type: %s" % ftype) data = data.flatten() for d0, d1 in pyscf.lib.prange(0, len(data), 6): - f.write(((d1-d0)*self.fmt + '\n') % tuple(data[d0:d1])) + f.write(((d1 - d0) * self.fmt + "\n") % tuple(data[d0:d1])) + -if __name__ == '__main__': +if __name__ == "__main__": def make_graphene(a, c, atoms=["C", "C"], supercell=None): - amat = np.asarray([ - [a, 0, 0], - [a/2, a*np.sqrt(3.0)/2, 0], - [0, 0, c]]) - coords_internal = np.asarray([ - [2.0, 2.0, 3.0], - [4.0, 4.0, 3.0]])/6 + amat = np.asarray([[a, 0, 0], [a / 2, a * np.sqrt(3.0) / 2, 0], [0, 0, c]]) + coords_internal = np.asarray([[2.0, 2.0, 3.0], [4.0, 4.0, 3.0]]) / 6 coords = np.dot(coords_internal, amat) if supercell is None: @@ -281,26 +286,22 @@ def make_graphene(a, c, atoms=["C", "C"], supercell=None): for x in range(ncopy[0]): for y in range(ncopy[1]): for z in range(ncopy[2]): - shift = x*amat[0] + y*amat[1] + z*amat[2] - atom.append((atoms[0]+str(nr), coords[0]+shift)) - atom.append((atoms[1]+str(nr), coords[1]+shift)) + shift = x * amat[0] + y * amat[1] + z * amat[2] + atom.append((atoms[0] + str(nr), coords[0] + shift)) + atom.append((atoms[1] + str(nr), coords[1] + shift)) nr += 1 - amat = np.einsum('i,ij->ij', ncopy, amat) + amat = np.einsum("i,ij->ij", ncopy, amat) return amat, atom - from pyscf import pbc - cell = pbc.gto.Cell( - basis = 'gth-dzv', - pseudo = 'gth-pade', - dimension = 2, - verbose=10) - cell.a, cell.atom = make_graphene(2.46, 10.0, supercell=(2,2,1)) + + cell = pbc.gto.Cell(basis="gth-dzv", pseudo="gth-pade", dimension=2, verbose=10) + cell.a, cell.atom = make_graphene(2.46, 10.0, supercell=(2, 2, 1)) hf = pbc.scf.HF(cell) hf = hf.density_fit() hf.kernel() - with CubeFile(cell, "graphene.cube", crop={"c0" : 5.0, "c1" : 5.0}) as f: - f.add_orbital(hf.mo_coeff[:,0]) - f.add_orbital(hf.mo_coeff[:,6:10]) - f.add_density([hf.make_rdm1(), np.linalg.inv(hf.get_ovlp())-hf.make_rdm1()]) + with CubeFile(cell, "graphene.cube", crop={"c0": 5.0, "c1": 5.0}) as f: + f.add_orbital(hf.mo_coeff[:, 0]) + f.add_orbital(hf.mo_coeff[:, 6:10]) + f.add_density([hf.make_rdm1(), np.linalg.inv(hf.get_ovlp()) - hf.make_rdm1()]) diff --git a/vayesta/misc/gmtkn55/__init__.py b/vayesta/misc/gmtkn55/__init__.py index 0050dba6d..a3f94c896 100644 --- a/vayesta/misc/gmtkn55/__init__.py +++ b/vayesta/misc/gmtkn55/__init__.py @@ -8,31 +8,31 @@ moddir = Path(__file__).parent -class TestSet: +class TestSet: def __init__(self, path): path = moddir / path - dtype = [('key', object), ('charge', int), ('multiplicity', int)] - systems = np.loadtxt(path / 'list.txt', dtype=dtype) + dtype = [("key", object), ("charge", int), ("multiplicity", int)] + systems = np.loadtxt(path / "list.txt", dtype=dtype) self.systems = {} - dtype = [('atom', object), ('x', float), ('y', float), ('z', float)] + dtype = [("atom", object), ("x", float), ("y", float), ("z", float)] for key, charge, multiplicity in systems: # Get atomic structure - data = np.loadtxt(path / key / 'struc.xyz', skiprows=2, dtype=dtype) + data = np.loadtxt(path / key / "struc.xyz", skiprows=2, dtype=dtype) if data.ndim == 1: - structure = [(data['atom'][i], (data['x'][i], data['y'][i], data['z'][i])) for i in range(len(data))] + structure = [(data["atom"][i], (data["x"][i], data["y"][i], data["z"][i])) for i in range(len(data))] else: - structure = [(str(data['atom']), (float(data['x']), float(data['y']), float(data['z'])))] + structure = [(str(data["atom"]), (float(data["x"]), float(data["y"]), float(data["z"])))] # Check .CHRG file try: - chrg = int(np.loadtxt(path / key / '.CHRG', dtype=int)) - assert (chrg == charge) + chrg = int(np.loadtxt(path / key / ".CHRG", dtype=int)) + assert chrg == charge except OSError: print("File '.CHRG' not found for system %s." % key) # Check .UHF file try: - uhf = bool(np.loadtxt(path / key / '.UHF', dtype=bool)) + uhf = bool(np.loadtxt(path / key / ".UHF", dtype=bool)) assert uhf == (multiplicity > 1) except OSError: print("File '.UHF' not found for system %s." % key) @@ -40,7 +40,7 @@ def __init__(self, path): self.systems[key] = (structure, charge, multiplicity) def loop(self, min_atoms=0, max_atoms=np.inf, include_uhf=True, systems=None, **kwargs): - for key in (systems or self.systems): + for key in systems or self.systems: mol = self.get_mol(key, **kwargs) if mol.natm < min_atoms or mol.natm > max_atoms: continue @@ -55,17 +55,16 @@ def get_mol(self, key, **kwargs): if charge != 0: mol.charge = charge if multiplicity != 1: - mol.spin = (multiplicity - 1) + mol.spin = multiplicity - 1 for attr, val in kwargs.items(): setattr(mol, attr, val) mol.build() return mol -W411 = TestSet('W4-11') - +W411 = TestSet("W4-11") -if __name__ == '__main__': +if __name__ == "__main__": for key, mol in W411.loop(min_atoms=3, max_atoms=4, include_uhf=False): print(mol.natm, mol.spin) diff --git a/vayesta/misc/gto_helper.py b/vayesta/misc/gto_helper.py index b6e2fce6b..59fcc081d 100644 --- a/vayesta/misc/gto_helper.py +++ b/vayesta/misc/gto_helper.py @@ -4,7 +4,7 @@ def loop_neighbor_cells(lattice_vectors=None, dimension=3): - if (dimension == 0): + if dimension == 0: yield np.zeros(3) return dxs = (-1, 0, 1) @@ -18,6 +18,7 @@ def loop_neighbor_cells(lattice_vectors=None, dimension=3): yield np.asarray(dr) yield np.dot(dr, lattice_vectors) + def get_atom_distances(mol, point, dimension=None): """Get array containing the distances of all atoms to the specified point. @@ -31,7 +32,7 @@ def get_atom_distances(mol, point, dimension=None): Distances: Array(n(atom)) """ coords = mol.atom_coords() - if hasattr(mol, 'lattice_vectors'): + if hasattr(mol, "lattice_vectors"): latvec = mol.lattice_vectors() dim = dimension if dimension is not None else mol.dimension else: @@ -40,20 +41,22 @@ def get_atom_distances(mol, point, dimension=None): distances = [] for atm, r0 in enumerate(coords): - dists = [np.linalg.norm(point - (r0+dr)) for dr in loop_neighbor_cells(latvec, dim)] + dists = [np.linalg.norm(point - (r0 + dr)) for dr in loop_neighbor_cells(latvec, dim)] distances.append(np.amin(dists)) return np.asarray(distances) + def get_atom_shells(mol, point, dimension=None, decimals=5): distances = get_atom_distances(mol, point, dimension=dimension) drounded = distances.round(decimals) - sort = np.argsort(distances, kind='stable') + sort = np.argsort(distances, kind="stable") d_uniq, inv = np.unique(drounded[sort], return_inverse=True) shells = inv[np.argsort(sort)] return shells, distances + def make_counterpoise_fragments(mol, fragments, full_basis=True, add_rest_fragment=True, dump_input=True): - '''Make mol objects for counterpoise calculations. + """Make mol objects for counterpoise calculations. Parameters ---------- @@ -64,7 +67,7 @@ def make_counterpoise_fragments(mol, fragments, full_basis=True, add_rest_fragme Returns ------- fmols : list - ''' + """ GHOST_PREFIX = "GHOST-" atom = mol.format_atom(mol.atom, unit=1.0) atom_symbols = [mol.atom_symbol(atm_id) for atm_id in range(mol.natm)] @@ -118,8 +121,8 @@ def make_frag_mol(frag): return fmols -if __name__ == '__main__': +if __name__ == "__main__": import pyscf import pyscf.pbc import pyscf.pbc.gto @@ -136,11 +139,11 @@ def make_frag_mol(frag): shells, dists = get_atom_shells(cell, point) print("Periodic boundary conditions:") for i in range(cell.natm): - print('atom= %2d distance= %12.8f shell= %2d' % (i, dists[i], shells[i])) - #uniq, idx, counts = np.unique(shells, return_index=True, return_counts=True) + print("atom= %2d distance= %12.8f shell= %2d" % (i, dists[i], shells[i])) + # uniq, idx, counts = np.unique(shells, return_index=True, return_counts=True) mol = cell.to_mol() shells, dists = get_atom_shells(mol, point) print("Open boundary conditions:") for i in range(mol.natm): - print('atom= %2d distance= %12.8f shell= %2d' % (i, dists[i], shells[i])) + print("atom= %2d distance= %12.8f shell= %2d" % (i, dists[i], shells[i])) diff --git a/vayesta/misc/molecules/molecules.py b/vayesta/misc/molecules/molecules.py index 29a4b6447..efb5482ca 100644 --- a/vayesta/misc/molecules/molecules.py +++ b/vayesta/misc/molecules/molecules.py @@ -1,132 +1,143 @@ import os.path import numpy as np + def _load_datafile(filename, scale=1): datafile = os.path.join(os.path.dirname(__file__), os.path.join("data", filename)) data = np.loadtxt(datafile, dtype=[("atoms", object), ("coords", np.float64, (3,))]) atoms = data["atoms"] - coords = scale*data["coords"] + coords = scale * data["coords"] atom = [[atoms[i], coords[i]] for i in range(len(atoms))] return atom -def water(atoms=['O', 'H'], origin=(0, 0, 0), scale=1): + +def water(atoms=["O", "H"], origin=(0, 0, 0), scale=1): origin = np.asarray(origin) - atom = [[atoms[0], scale*np.asarray([0.0000, 0.0000, 0.1173]) - origin], - [atoms[1], scale*np.asarray([0.0000, 0.7572, -0.4692]) - origin], - [atoms[1], scale*np.asarray([0.0000, -0.7572, -0.4692]) - origin]] + atom = [ + [atoms[0], scale * np.asarray([0.0000, 0.0000, 0.1173]) - origin], + [atoms[1], scale * np.asarray([0.0000, 0.7572, -0.4692]) - origin], + [atoms[1], scale * np.asarray([0.0000, -0.7572, -0.4692]) - origin], + ] return atom -def alkane(n, atoms=['C', 'H'], cc_bond=1.54, ch_bond=1.09, scale=1.0, numbering=False): + +def alkane(n, atoms=["C", "H"], cc_bond=1.54, ch_bond=1.09, scale=1.0, numbering=False): """Alkane with idealized tetrahedral (sp3) coordination.""" - assert numbering in (False, 'atom', 'unit') + assert numbering in (False, "atom", "unit") index = 0 + def get_symbol(symbol): nonlocal index if not numbering: return symbol - if numbering == 'atom': - out = '%s%d' % (symbol, index) + if numbering == "atom": + out = "%s%d" % (symbol, index) index += 1 return out - return '%s%d' % (symbol, index) + return "%s%d" % (symbol, index) - dk = 1 if (numbering == 'atom') else 0 + dk = 1 if (numbering == "atom") else 0 - phi = np.arccos(-1.0/3) - cph = 1/np.sqrt(3.0) - sph = np.sin(phi/2.0) - dcy = scale*cc_bond * cph - dcz = scale*cc_bond * sph - dchs = scale*ch_bond * sph - dchc = scale*ch_bond * cph + phi = np.arccos(-1.0 / 3) + cph = 1 / np.sqrt(3.0) + sph = np.sin(phi / 2.0) + dcy = scale * cc_bond * cph + dcz = scale * cc_bond * sph + dchs = scale * ch_bond * sph + dchc = scale * ch_bond * cph x = 0.0 atom = [] for i in range(n): # Carbon atoms - sign = (-1)**i - y = sign * dcy/2 - z = i*dcz + sign = (-1) ** i + y = sign * dcy / 2 + z = i * dcz atom.append([get_symbol(atoms[0]), [x, y, z]]) # Hydrogen atoms on side dy = sign * dchc - atom.append([get_symbol(atoms[1]), [x+dchs, y+dy, z]]) - atom.append([get_symbol(atoms[1]), [x-dchs, y+dy, z]]) + atom.append([get_symbol(atoms[1]), [x + dchs, y + dy, z]]) + atom.append([get_symbol(atoms[1]), [x - dchs, y + dy, z]]) # Terminal hydrogen atom 1 - if (i == 0): - atom.append([get_symbol(atoms[1]), [0.0, y-dchc, z-dchs]]) + if i == 0: + atom.append([get_symbol(atoms[1]), [0.0, y - dchc, z - dchs]]) # Terminal hydrogen atom 2 # Do not use elif, since for n=1 (Methane), we need to add both terminal hydrogen atoms: - if (i == n-1): - atom.append([get_symbol(atoms[1]), [0.0, y-sign*dchc, z+dchs]]) - if numbering == 'unit': + if i == n - 1: + atom.append([get_symbol(atoms[1]), [0.0, y - sign * dchc, z + dchs]]) + if numbering == "unit": index += 1 return atom + def alkene(ncarbon, cc_bond=1.33, ch_bond=1.09): """Alkene with idealized trigonal planar (sp2) coordination.""" - if (ncarbon < 2): + if ncarbon < 2: raise ValueError - if (ncarbon % 2 == 1): + if ncarbon % 2 == 1: raise NotImplementedError - cos30 = np.sqrt(3)/2 - sin30 = 1/2 + cos30 = np.sqrt(3) / 2 + sin30 = 1 / 2 r0 = x0, y0, z0 = (0, 0, 0) - atom = [('H', (x0-ch_bond*cos30, y0+ch_bond*sin30, z0))] + atom = [("H", (x0 - ch_bond * cos30, y0 + ch_bond * sin30, z0))] for nc in range(ncarbon): - sign = (-1)**nc - atom += [('C', r0)] - atom += [('H', (x0, y0-sign*ch_bond, z0))] - if nc == (ncarbon-1): + sign = (-1) ** nc + atom += [("C", r0)] + atom += [("H", (x0, y0 - sign * ch_bond, z0))] + if nc == (ncarbon - 1): break - r0 = x0, y0, z0 = (x0+cc_bond*cos30, y0+sign*cc_bond*sin30, 0) - atom += [('H', (x0+ch_bond*cos30, y0+sign*ch_bond*sin30, z0))] + r0 = x0, y0, z0 = (x0 + cc_bond * cos30, y0 + sign * cc_bond * sin30, 0) + atom += [("H", (x0 + ch_bond * cos30, y0 + sign * ch_bond * sin30, z0))] return atom -def arene(n, atoms=['C', 'H'], cc_bond=1.39, ch_bond=1.09, unit=1.0): + +def arene(n, atoms=["C", "H"], cc_bond=1.39, ch_bond=1.09, unit=1.0): """Bond length for benzene.""" - r1 = unit*cc_bond/(2*np.sin(np.pi/n)) - r2 = unit*(r1 + ch_bond) + r1 = unit * cc_bond / (2 * np.sin(np.pi / n)) + r2 = unit * (r1 + ch_bond) atom = [] for i in range(n): - phi = 2*i*np.pi/n - atomidx = (2*i) % len(atoms) - atom.append((atoms[atomidx], (r1*np.cos(phi), r1*np.sin(phi), 0.0))) - atom.append((atoms[atomidx+1], (r2*np.cos(phi), r2*np.sin(phi), 0.0))) + phi = 2 * i * np.pi / n + atomidx = (2 * i) % len(atoms) + atom.append((atoms[atomidx], (r1 * np.cos(phi), r1 * np.sin(phi), 0.0))) + atom.append((atoms[atomidx + 1], (r2 * np.cos(phi), r2 * np.sin(phi), 0.0))) return atom + def no2(): atom = [ - ('N', (0.0000, 0.0000, 0.0000)), - ('O', (0.0000, 1.0989, 0.4653)), - ('O', (0.0000, -1.0989, 0.4653)), - ] + ("N", (0.0000, 0.0000, 0.0000)), + ("O", (0.0000, 1.0989, 0.4653)), + ("O", (0.0000, -1.0989, 0.4653)), + ] return atom + def ethanol(oh_bond=None, scale=1): - atom = _load_datafile('ethanol.dat', scale=scale) + atom = _load_datafile("ethanol.dat", scale=scale) if oh_bond is not None: pos_o = atom[2][1] pos_h = atom[3][1] - voh = (pos_h - pos_o) + voh = pos_h - pos_o voh = voh / np.linalg.norm(voh) - pos_h = pos_o + oh_bond*voh + pos_h = pos_o + oh_bond * voh atom[3][1] = pos_h return atom + def ketene(cc_bond=None): - atom = _load_datafile('ketene.dat') + atom = _load_datafile("ketene.dat") if cc_bond is not None: pos_c1 = atom[0][1] pos_c2 = atom[1][1] - vcc = (pos_c2 - pos_c1) + vcc = pos_c2 - pos_c1 vcc = vcc / np.linalg.norm(vcc) new_c2 = pos_c1 + cc_bond * vcc new_o = atom[2][1] + (new_c2 - pos_c2) @@ -134,89 +145,105 @@ def ketene(cc_bond=None): atom[2][1] = new_o return atom + def ring(atom, natom, bond_length=None, radius=None, z=0.0, numbering=None): if radius is None: - r = bond_length/(2*np.sin(np.pi/natom)) + r = bond_length / (2 * np.sin(np.pi / natom)) else: r = radius atoms = [] if isinstance(atom, str): atom = [atom] for i in range(natom): - theta = i * (2*np.pi/natom) - atom_i = atom[i%len(atom)] + theta = i * (2 * np.pi / natom) + atom_i = atom[i % len(atom)] if numbering is not None: atom_i += str(int(numbering) + i) - atoms.append([atom_i, np.asarray([r*np.cos(theta), r*np.sin(theta), z])]) + atoms.append([atom_i, np.asarray([r * np.cos(theta), r * np.sin(theta), z])]) return atoms + def chain(atom, natom, bond_length, numbering=None): - '''Open boundary condition version of 1D ring''' + """Open boundary condition version of 1D ring""" atoms = [] if isinstance(atom, str): atom = [atom] for i in range(natom): - atom_i = atom[i%len(atom)] + atom_i = atom[i % len(atom)] if numbering is not None: atom_i += str(int(numbering) + i) - atoms.append([atom_i, np.asarray([i*bond_length, 0.0, 0.0])]) + atoms.append([atom_i, np.asarray([i * bond_length, 0.0, 0.0])]) return atoms + # --- From datafiles: + def acetic_acid(): - atom = _load_datafile('acetic.dat') + atom = _load_datafile("acetic.dat") return atom + def ferrocene_b3lyp(): - atom = _load_datafile('ferrocene.dat') + atom = _load_datafile("ferrocene.dat") return atom -def ferrocene(atoms=['Fe', 'C', 'H'], conformation='eclipsed', dFeCp=1.648, dCC=1.427, dCH=1.079, aCpH=0.52, numbering=None): + +def ferrocene( + atoms=["Fe", "C", "H"], conformation="eclipsed", dFeCp=1.648, dCC=1.427, dCH=1.079, aCpH=0.52, numbering=None +): """From https://pubs.acs.org/doi/pdf/10.1021/ct700152c""" - if conformation != 'eclipsed': + if conformation != "eclipsed": raise NotImplementedError - rHH = dCC/(2*np.sin(np.pi/5)) + dCH*np.cos(aCpH*np.pi/180) - zH = dCH*np.sin(aCpH*np.pi/180) + rHH = dCC / (2 * np.sin(np.pi / 5)) + dCH * np.cos(aCpH * np.pi / 180) + zH = dCH * np.sin(aCpH * np.pi / 180) - atom = [(atoms[0] + ('1' if numbering else ''), np.asarray((0, 0, 0)))] + atom = [(atoms[0] + ("1" if numbering else ""), np.asarray((0, 0, 0)))] atom += ring(atoms[1], 5, dCC, z=dFeCp, numbering=2 if numbering else None) - atom += ring(atoms[2], 5, radius=rHH, z=dFeCp-zH, numbering=7 if numbering else None) + atom += ring(atoms[2], 5, radius=rHH, z=dFeCp - zH, numbering=7 if numbering else None) atom += ring(atoms[1], 5, dCC, z=-dFeCp, numbering=12 if numbering else None) - atom += ring(atoms[2], 5, radius=rHH, z=-dFeCp+zH, numbering=17 if numbering else None) + atom += ring(atoms[2], 5, radius=rHH, z=-dFeCp + zH, numbering=17 if numbering else None) return atom + def propyl(): - atom = _load_datafile('propyl.dat') + atom = _load_datafile("propyl.dat") return atom + def phenyl(): - atom = _load_datafile('phenyl.dat') + atom = _load_datafile("phenyl.dat") return atom + def propanol(): - atom = _load_datafile('propanol.dat') + atom = _load_datafile("propanol.dat") return atom + def chloroethanol(): - atom = _load_datafile('chloroethanol.dat') + atom = _load_datafile("chloroethanol.dat") return atom + def neopentane(): """Structure from B3LYP//aug-cc-pVTZ.""" - atom = _load_datafile('neopentane.dat') + atom = _load_datafile("neopentane.dat") return atom + def boronene(): - atom = _load_datafile('boronene.dat') + atom = _load_datafile("boronene.dat") return atom + def coronene(): - atom = _load_datafile('coronene.dat') + atom = _load_datafile("coronene.dat") return atom + def glycine(): - atom = _load_datafile('glycine.dat') + atom = _load_datafile("glycine.dat") return atom diff --git a/vayesta/misc/pcdiis.py b/vayesta/misc/pcdiis.py index 8f9cdc50b..78004f2c9 100644 --- a/vayesta/misc/pcdiis.py +++ b/vayesta/misc/pcdiis.py @@ -11,7 +11,6 @@ def __init__(self, pref, *args, **kwargs): self.pref = pref super().__init__(*args, **kwargs) - def update(self, x): y = np.dot(x, self.pref) y_new = super().update(y) @@ -20,17 +19,17 @@ def update(self, x): return x_new -if __name__ == '__main__': +if __name__ == "__main__": import pyscf.gto import pyscf.scf - mol = pyscf.gto.Mole(atom='H 0 0 0 ; F 0 0 1', basis='cc-pVDZ') + mol = pyscf.gto.Mole(atom="H 0 0 0 ; F 0 0 1", basis="cc-pVDZ") mol.build() hf = pyscf.scf.RHF(mol) hf.kernel() nocc = np.count_nonzero(hf.mo_occ > 0) - c = hf.mo_coeff[:,:nocc] + c = hf.mo_coeff[:, :nocc] diis = PCDIIS(c) dm0 = hf.make_rdm1() dm1 = diis.update(dm0) diff --git a/vayesta/misc/solids/solids.py b/vayesta/misc/solids/solids.py index a57d5e2fd..2d18057ad 100644 --- a/vayesta/misc/solids/solids.py +++ b/vayesta/misc/solids/solids.py @@ -2,143 +2,115 @@ SQRT2 = np.sqrt(2) + def bcc(atoms, a): """Body-centered cubic. Li: 3.51 A """ - amat = a*np.eye(a) - coords = a * np.asarray([[0, 0, 0], [1, 1, 1]])/2 + amat = a * np.eye(a) + coords = a * np.asarray([[0, 0, 0], [1, 1, 1]]) / 2 atom = _make_atom(atoms, coords) return amat, atom -def diamond(atoms=['C', 'C'], a=3.57): + +def diamond(atoms=["C", "C"], a=3.57): """Silicon: a=5.431 A""" - amat = a * np.asarray([ - [0.5, 0.5, 0.0], - [0.0, 0.5, 0.5], - [0.5, 0.0, 0.5]]) - coords = a * np.asarray([[0, 0, 0], [1, 1, 1]])/4 + amat = a * np.asarray([[0.5, 0.5, 0.0], [0.0, 0.5, 0.5], [0.5, 0.0, 0.5]]) + coords = a * np.asarray([[0, 0, 0], [1, 1, 1]]) / 4 atom = _make_atom(atoms, coords) return amat, atom -def graphene(atoms=['C', 'C'], a=2.46, c=20.0): + +def graphene(atoms=["C", "C"], a=2.46, c=20.0): """ hBN: a=2.5A hBeO: a=2.68A (VASP)""" - amat = np.asarray([ - [a, 0, 0], - [a/2, a*np.sqrt(3.0)/2, 0], - [0, 0, c]]) - coords_internal = np.asarray([ - [2.0, 2.0, 3.0], - [4.0, 4.0, 3.0]])/6 + amat = np.asarray([[a, 0, 0], [a / 2, a * np.sqrt(3.0) / 2, 0], [0, 0, c]]) + coords_internal = np.asarray([[2.0, 2.0, 3.0], [4.0, 4.0, 3.0]]) / 6 coords = np.dot(coords_internal, amat) atom = _make_atom(atoms, coords) return amat, atom -def graphite(atoms=['C', 'C', 'C', 'C'], a=2.461, c=6.708): + +def graphite(atoms=["C", "C", "C", "C"], a=2.461, c=6.708): """a = 2.461 A , c = 6.708 A""" - amat = np.asarray([ - [a/2, -a*np.sqrt(3.0)/2, 0], - [a/2, +a*np.sqrt(3.0)/2, 0], - [0, 0, c]]) - coords_internal = np.asarray([ - [0, 0, 1.0/4], - [2.0/3, 1.0/3, 1.0/4], - [0, 0, 3.0/4], - [1.0/3, 2.0/3, 3.0/4]]) + amat = np.asarray([[a / 2, -a * np.sqrt(3.0) / 2, 0], [a / 2, +a * np.sqrt(3.0) / 2, 0], [0, 0, c]]) + coords_internal = np.asarray( + [[0, 0, 1.0 / 4], [2.0 / 3, 1.0 / 3, 1.0 / 4], [0, 0, 3.0 / 4], [1.0 / 3, 2.0 / 3, 3.0 / 4]] + ) coords = np.dot(coords_internal, amat) atom = _make_atom(atoms, coords) return amat, atom -def rocksalt(atoms=['Na', 'Cl'], a=5.6402, unitcell='primitive'): + +def rocksalt(atoms=["Na", "Cl"], a=5.6402, unitcell="primitive"): """ LiH: a=4.0834 LiF: a=4.0351 """ - if unitcell == 'primitive': - amat = a * np.asarray([ - [0.5, 0.5, 0.0], - [0.0, 0.5, 0.5], - [0.5, 0.0, 0.5]]) - internal = np.asarray([ - [0.0, 0.0, 0.0], - [0.5, 0.5, 0.5]]) + if unitcell == "primitive": + amat = a * np.asarray([[0.5, 0.5, 0.0], [0.0, 0.5, 0.5], [0.5, 0.0, 0.5]]) + internal = np.asarray([[0.0, 0.0, 0.0], [0.5, 0.5, 0.5]]) coords = np.dot(internal, amat) atom = _make_atom(atoms, coords) return amat, atom - if unitcell == 'primitive-af1': + if unitcell == "primitive-af1": # wrong: - #amat = a * np.asarray([ + # amat = a * np.asarray([ # [1/SQRT2, 0, 0], # [0, 1/SQRT2, 0], # [0, 0, 1]]) - #internal = np.asarray([ + # internal = np.asarray([ # [0.0, 0.0, 0.0], # [0.5, 0.5, 0.0], # [0.0, 0.0, 0.5], # [0.5, 0.5, 0.5]]) # arXiv:2207.12064v1: - amat = a * np.asarray([ - [0.5, 0, 0.5], - [0.5, 0, -0.5], - [0, 1, 0.0]]) - coords = a/2 * np.asarray([ - [0, 0, 0], - [1, 0, 0], - [1, 1, 0], - [0, 1, 0]]) - atom = _make_atom(2*atoms, coords) + amat = a * np.asarray([[0.5, 0, 0.5], [0.5, 0, -0.5], [0, 1, 0.0]]) + coords = a / 2 * np.asarray([[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]]) + atom = _make_atom(2 * atoms, coords) return amat, atom - if unitcell == 'primitive-af2': - amat = a * np.asarray([ - [1.00, 0.50, 0.50], - [0.50, 1.00, 0.50], - [0.50, 0.50, 1.00]]) - internal = np.asarray([ - [0.00, 0.00, 0.00], - [0.25, 0.25, 0.25], - [0.50, 0.50, 0.50], - [0.75, 0.75, 0.75]]) + if unitcell == "primitive-af2": + amat = a * np.asarray([[1.00, 0.50, 0.50], [0.50, 1.00, 0.50], [0.50, 0.50, 1.00]]) + internal = np.asarray([[0.00, 0.00, 0.00], [0.25, 0.25, 0.25], [0.50, 0.50, 0.50], [0.75, 0.75, 0.75]]) coords = np.dot(internal, amat) - atom = _make_atom(2*atoms, coords) + atom = _make_atom(2 * atoms, coords) return amat, atom - if unitcell == 'cubic': - amat = a*np.eye(3) - internal = np.asarray([ - # Atom 1: - [0, 0, 0], - [0, 1/2, 1/2], - [1/2, 0, 1/2], - [1/2, 1/2, 0], - # Atom 2: - [0, 0, 1/2], - [0, 1/2, 0], - [1/2, 0, 0], - [1/2, 1/2, 1/2]]) + if unitcell == "cubic": + amat = a * np.eye(3) + internal = np.asarray( + [ + # Atom 1: + [0, 0, 0], + [0, 1 / 2, 1 / 2], + [1 / 2, 0, 1 / 2], + [1 / 2, 1 / 2, 0], + # Atom 2: + [0, 0, 1 / 2], + [0, 1 / 2, 0], + [1 / 2, 0, 0], + [1 / 2, 1 / 2, 1 / 2], + ] + ) coords = np.dot(internal, amat) - atom = _make_atom(4*[atoms[0]]+4*[atoms[1]], coords) + atom = _make_atom(4 * [atoms[0]] + 4 * [atoms[1]], coords) return amat, atom raise ValueError -def perovskite(atoms=['Sr', 'Ti', 'O'], a=3.905): + +def perovskite(atoms=["Sr", "Ti", "O"], a=3.905): if len(atoms) == 3: - atoms = [atoms[0], atoms[1]] + 3*[atoms[2]] + atoms = [atoms[0], atoms[1]] + 3 * [atoms[2]] amat = a * np.eye(3) - coords = a*np.asarray([ - [0, 0, 0], - [1/2, 1/2, 1/2], - [0, 1/2, 1/2], - [1/2, 0, 1/2], - [1/2, 1/2, 0] - ]) + coords = a * np.asarray([[0, 0, 0], [1 / 2, 1 / 2, 1 / 2], [0, 1 / 2, 1 / 2], [1 / 2, 0, 1 / 2], [1 / 2, 1 / 2, 0]]) atom = _make_atom(atoms, coords) return amat, atom -def perovskite_tetragonal(atoms=['Sr', 'Ti', 'O'], a=5.507, c=7.796, u=0.241): + +def perovskite_tetragonal(atoms=["Sr", "Ti", "O"], a=5.507, c=7.796, u=0.241): """This is the crystallographic ('quadruple') cell, not the primitive ('double') cell. Lattice constants from PHYSICAL REVIEW MATERIALS 2, 013807 (2018). @@ -149,19 +121,19 @@ def perovskite_tetragonal(atoms=['Sr', 'Ti', 'O'], a=5.507, c=7.796, u=0.241): (see DOI: 10.1103/PhysRevB.83.134108) """ if a is None: - a = c/np.sqrt(2) + a = c / np.sqrt(2) if c is None: - c = np.sqrt(2)*a + c = np.sqrt(2) * a if len(atoms) == 3: - atoms = [atoms[0], atoms[1]] + 3*[atoms[2]] + atoms = [atoms[0], atoms[1]] + 3 * [atoms[2]] if len(atoms) == 5: - atoms = 4*atoms + atoms = 4 * atoms amat = a * np.eye(3) - amat[2,2] = c + amat[2, 2] = c - #coords_internal = a*np.asarray([ + # coords_internal = a*np.asarray([ # [0 , 0.5 , 0.25], # Sr # [0.5 , 0 , 0.75], # Sr # [0.5 , 0 , 0.25], # Sr @@ -184,33 +156,36 @@ def perovskite_tetragonal(atoms=['Sr', 'Ti', 'O'], a=5.507, c=7.796, u=0.241): # [u , 0.5-u , 0.5], # O # ]) # Order? - coords_internal = np.asarray([ - [0 , 0.5 , 0.25], # Sr - [0 , 0 , 0], # Ti - [0 , 0 , 0.25], # O (4a) - [u , 0.5+u , 0], # O - [0.5+u , u , 0.5], # O - [0.5 , 0 , 0.75], # Sr - [0.5 , 0.5 , 0.5], # Ti - [0.5 , 0.5 , 0.75], # O (4a) - [-u , 0.5-u , 0], # O - [0.5-u , -u , 0.5], # O - [0.5 , 0 , 0.25], # Sr - [0 , 0 , 0.5], # Ti - [0 , 0 , 0.75], # O (4a) - [0.5-u , u , 0], # O - [-u , 0.5+u , 0.5], # O - [0 , 0.5 , 0.75], # Sr - [0.5 , 0.5 , 0], # Ti - [0.5 , 0.5 , 0.25], # O (4a) - [0.5+u , -u , 0], # O - [u , 0.5-u , 0.5], # O - ]) + coords_internal = np.asarray( + [ + [0, 0.5, 0.25], # Sr + [0, 0, 0], # Ti + [0, 0, 0.25], # O (4a) + [u, 0.5 + u, 0], # O + [0.5 + u, u, 0.5], # O + [0.5, 0, 0.75], # Sr + [0.5, 0.5, 0.5], # Ti + [0.5, 0.5, 0.75], # O (4a) + [-u, 0.5 - u, 0], # O + [0.5 - u, -u, 0.5], # O + [0.5, 0, 0.25], # Sr + [0, 0, 0.5], # Ti + [0, 0, 0.75], # O (4a) + [0.5 - u, u, 0], # O + [-u, 0.5 + u, 0.5], # O + [0, 0.5, 0.75], # Sr + [0.5, 0.5, 0], # Ti + [0.5, 0.5, 0.25], # O (4a) + [0.5 + u, -u, 0], # O + [u, 0.5 - u, 0.5], # O + ] + ) coords = np.dot(coords_internal, amat) atom = _make_atom(atoms, coords) return amat, atom + def _make_atom(atoms, coords): atom = [] for i in range(len(atoms)): diff --git a/vayesta/misc/variational_embedding/calc_nonorth_couplings.py b/vayesta/misc/variational_embedding/calc_nonorth_couplings.py index 1e04463da..7da17c6f4 100644 --- a/vayesta/misc/variational_embedding/calc_nonorth_couplings.py +++ b/vayesta/misc/variational_embedding/calc_nonorth_couplings.py @@ -85,7 +85,6 @@ def calc_ci_elements(ci, h1e, h2e, ovlp, mo, nmo, nocc, nact, ncore, enuc=0.0): def calc_ci_elements_uhf(ci, h1e, h2e, ovlp, mo, nmo, nocc, nact, ncore, enuc=0.0): - h1e, h2e, ovlp = owndata(h1e), owndata(h2e), owndata(ovlp) mo = tuple([tuple([owndata(y) for y in x]) for x in mo]) diff --git a/vayesta/misc/variational_embedding/variational_params.py b/vayesta/misc/variational_embedding/variational_params.py index 9f6d20fd2..56b769f3d 100644 --- a/vayesta/misc/variational_embedding/variational_params.py +++ b/vayesta/misc/variational_embedding/variational_params.py @@ -4,6 +4,7 @@ from vayesta.core.types import RFCI_WaveFunction, SpatialOrbitals from .calc_nonorth_couplings import calc_full_couplings, calc_full_couplings_uhf, calc_ci_elements, calc_ci_elements_uhf + def get_emb_info(emb, wfs): h1e = emb.get_hcore() ovlp = emb.get_ovlp() @@ -17,9 +18,10 @@ def get_emb_info(emb, wfs): h2e = emb.get_eris_array(np.eye(emb.nao)).reshape((emb.nao**2, emb.nao**2)) nmo, nocc = emb.nao, emb.nocc nact = [x.mo.norb for x in wfs] - ncore = [[y - z for y,z in zip(emb.nocc, x.mo.nocc)] for x in wfs] + ncore = [[y - z for y, z in zip(emb.nocc, x.mo.nocc)] for x in wfs] return ovlp, h1e, h2e, nmo, nocc, nact, ncore + def optimise_full_varwav(emb, fs=None, lindep=1e-12, replace_wf=False): fullham, fullovlp = gen_loc_hams(emb, fs) h, s, nstates = to_single_ham(fullham, fullovlp, returnsizes=True) @@ -34,7 +36,7 @@ def optimise_full_varwav(emb, fs=None, lindep=1e-12, replace_wf=False): if replace_wf: for ns, f in zip(nstates, emb.fragments): if f.sym_parent is None: - c = ci0[i:i + ns].reshape(f.results.wf.ci.shape) + c = ci0[i : i + ns].reshape(f.results.wf.ci.shape) # Copy to avoid wiping any other copies. f.results.wf = f.results.wf.copy() f.results.wf.ci = c @@ -42,6 +44,7 @@ def optimise_full_varwav(emb, fs=None, lindep=1e-12, replace_wf=False): return w[0] + def gen_loc_hams(emb, fs=None): if fs is None: fs = emb.fragments @@ -60,26 +63,26 @@ def gen_loc_hams(emb, fs=None): fullovlp = np.zeros_like(fullham) for i in range(len(fs)): for j in range(i, len(fs)): - fullham[i, j], fullovlp[i, j] = coupl_func(h1e, h2e, ovlp, mos[i], mos[j], nmo, nocc, nact[i], - nact[j], ncore[i], ncore[j], enuc=emb.e_nuc) + fullham[i, j], fullovlp[i, j] = coupl_func( + h1e, h2e, ovlp, mos[i], mos[j], nmo, nocc, nact[i], nact[j], ncore[i], ncore[j], enuc=emb.e_nuc + ) fullham[j, i] = fullham[i, j].T fullovlp[j, i] = fullovlp[i, j].T return fullham, fullovlp def to_single_ham(fullham, fullovlp, returnsizes=False): - nstates = sum([x.shape[1] for x in fullham[0]]) h = np.zeros((nstates, nstates)) s = np.zeros_like(h) ix = 0 - for (hx, sx) in zip(fullham, fullovlp): + for hx, sx in zip(fullham, fullovlp): iy = 0 - for (hloc, sloc) in zip(hx, sx): + for hloc, sloc in zip(hx, sx): dx, dy = hloc.shape - h[ix:ix+dx, iy:iy+dy] = hloc - s[ix:ix + dx, iy:iy + dy] = sloc + h[ix : ix + dx, iy : iy + dy] = hloc + s[ix : ix + dx, iy : iy + dy] = sloc iy += dy ix += dx @@ -88,6 +91,7 @@ def to_single_ham(fullham, fullovlp, returnsizes=False): else: return h, s + def get_wf_couplings(emb, fs=None, wfs=None, mos=None, inc_mf=False): """Calculate the hamiltonian element between multiple FCI wavefunctions in different fragments. This requires the CI coefficients and the basis set in which they are defined. @@ -111,7 +115,7 @@ def get_wf_couplings(emb, fs=None, wfs=None, mos=None, inc_mf=False): if inc_mf: mfwf = np.zeros_like(ci[-1]) - mfwf[0,0] = 1.0 + mfwf[0, 0] = 1.0 ci += [mfwf] mos += [mos[-1]] diff --git a/vayesta/mpi/__init__.py b/vayesta/mpi/__init__.py index 7dd58f77d..25068b647 100644 --- a/vayesta/mpi/__init__.py +++ b/vayesta/mpi/__init__.py @@ -3,10 +3,11 @@ mpi = None + def init_mpi(use_mpi, required=True): global mpi if use_mpi: - mpi = MPI_Interface('mpi4py', required=required) + mpi = MPI_Interface("mpi4py", required=required) else: mpi = MPI_Interface(None) return mpi diff --git a/vayesta/mpi/interface.py b/vayesta/mpi/interface.py index c98fc4d46..20f8ad2ec 100644 --- a/vayesta/mpi/interface.py +++ b/vayesta/mpi/interface.py @@ -10,13 +10,13 @@ from vayesta.mpi.scf import gdf_with_mpi -NdArrayMetadata = namedtuple('NdArrayMetadata', ['shape', 'dtype']) +NdArrayMetadata = namedtuple("NdArrayMetadata", ["shape", "dtype"]) -class MPI_Interface: +class MPI_Interface: def __init__(self, mpi, required=False, log=None): self.log = log or logging.getLogger(__name__) - if mpi == 'mpi4py': + if mpi == "mpi4py": mpi = self._import_mpi4py(required=required) if mpi: self.MPI = mpi @@ -35,8 +35,10 @@ def __init__(self, mpi, required=False, log=None): def _import_mpi4py(self, required=True): try: import mpi4py + mpi4py.rc.threads = False from mpi4py import MPI as mpi + return mpi except (ModuleNotFoundError, ImportError) as e: if required: @@ -53,7 +55,7 @@ def __bool__(self): @property def enabled(self): - return (self.size > 1) + return self.size > 1 @property def disabled(self): @@ -61,7 +63,7 @@ def disabled(self): @property def is_master(self): - return (self.rank == 0) + return self.rank == 0 def get_new_tag(self): self._tag += 1 @@ -134,12 +136,15 @@ def decorator(func): # No MPI: if self.disabled: return func + @functools.wraps(func) def wrapper(*args, **kwargs): res = func(*args, **kwargs) res = self.world.reduce(res, **mpi_kwargs) return res + return wrapper + return decorator def with_allreduce(self, **mpi_kwargs): @@ -147,12 +152,15 @@ def decorator(func): # No MPI: if self.disabled: return func + @functools.wraps(func) def wrapper(*args, **kwargs): res = func(*args, **kwargs) res = self.world.allreduce(res, **mpi_kwargs) return res + return wrapper + return decorator def only_master(self): @@ -160,12 +168,15 @@ def decorator(func): # No MPI: if self.disabled: return func + @functools.wraps(func) def wrapper(*args, **kwargs): if not self.is_master: return None return func(*args, **kwargs) + return wrapper + return decorator # --- Function wrapper at fragment level @@ -178,6 +189,7 @@ def decorator(func): return func # With MPI: tag2 = self.get_new_tag() if tag is None else tag + @functools.wraps(func) def wrapper(*args, **kwargs): if callable(source): @@ -186,7 +198,7 @@ def wrapper(*args, **kwargs): src = source if self.rank == src: res = func(*args, **kwargs) - if (self.rank != dest): + if self.rank != dest: self.log.debugv("MPI[%d]<send>: func=%s dest=%d", self.rank, func.__name__, dest) self.world.send(res, dest=dest, tag=tag2, **mpi_kwargs) self.log.debugv("MPI[%d]<send>: done", self.rank) @@ -199,7 +211,9 @@ def wrapper(*args, **kwargs): else: self.log.debugv("MPI[%d] <do nothing> func=%s source=%d ", self.rank, func.__name__, src) return None + return wrapper + return decorator def create_rma_dict(self, dictionary): diff --git a/vayesta/mpi/rma.py b/vayesta/mpi/rma.py index b7acbb6cd..5a61203a2 100644 --- a/vayesta/mpi/rma.py +++ b/vayesta/mpi/rma.py @@ -7,7 +7,6 @@ class RMA_Dict: - def __init__(self, mpi): self.mpi = mpi self._writable = False @@ -23,7 +22,6 @@ def from_dict(cls, mpi, dictionary): return rma_dict class RMA_DictElement: - def __init__(self, collection, location, data=None, shape=None, dtype=None): self.collection = collection self.location = location @@ -33,36 +31,36 @@ def __init__(self, collection, location, data=None, shape=None, dtype=None): # Allocate RMA Window and put data self.win = None if self.dtype != type(None): - if (self.mpi.rank == self.location): + if self.mpi.rank == self.location: self.local_init(data) else: self.remote_init() def local_init(self, data): - #if data is not None: + # if data is not None: # winsize = (data.size * data.dtype.itemsize) - #else: + # else: # winsize = 0 - #self.win = self.mpi.MPI.Win.Allocate(winsize, comm=self.mpi.world) - #self.win = self.mpi.MPI.Win.Create(data, comm=self.mpi.world) - #if data is None: + # self.win = self.mpi.MPI.Win.Allocate(winsize, comm=self.mpi.world) + # self.win = self.mpi.MPI.Win.Create(data, comm=self.mpi.world) + # if data is None: # return - winsize = (data.size * data.dtype.itemsize) + winsize = data.size * data.dtype.itemsize self.win = self.mpi.MPI.Win.Allocate(winsize, comm=self.mpi.world) - assert (self.shape == data.shape) - assert (self.dtype == data.dtype) + assert self.shape == data.shape + assert self.dtype == data.dtype self.rma_lock() self.rma_put(data) self.rma_unlock() def remote_init(self): - #if self.dtype == type(None): + # if self.dtype == type(None): # return self.win = self.mpi.MPI.Win.Allocate(0, comm=self.mpi.world) - #self.win = self.mpi.MPI.Win.Create(None, comm=self.mpi.world) - #buf = np.empty(self.shape, dtype=self.dtype) - #self.win = self.mpi.MPI.Win.Create(buf, comm=self.mpi.world) + # self.win = self.mpi.MPI.Win.Create(None, comm=self.mpi.world) + # buf = np.empty(self.shape, dtype=self.dtype) + # self.win = self.mpi.MPI.Win.Create(buf, comm=self.mpi.world) @property def size(self): @@ -70,14 +68,14 @@ def size(self): return 0 return np.product(self.shape) - #@property - #def itemsize(self): + # @property + # def itemsize(self): # if self.dtype is type(None): # return 0 # return self.dtype.itemsize - #@property - #def winsize(self): + # @property + # def winsize(self): # return self.size * self.itemsize def get(self, shared_lock=True): @@ -118,19 +116,26 @@ def __getitem__(self, key): if not self.readable: raise AttributeError("Cannot read from ArrayCollection from inside with-statement.") # Is local access without going via MPI.Get safe? - #if key in self.local_data: + # if key in self.local_data: # return self.local_data[key] if self.mpi.disabled: return self._elements[key] element = self._elements[key] - log.debugv("RMA: origin= %d, target= %d, key= %r, shape= %r, dtype= %r", self.mpi.rank, element.location, key, element.shape, element.dtype) + log.debugv( + "RMA: origin= %d, target= %d, key= %r, shape= %r, dtype= %r", + self.mpi.rank, + element.location, + key, + element.shape, + element.dtype, + ) return element.get() def __setitem__(self, key, value): if not self._writable: raise AttributeError("Cannot write to ArrayCollection outside of with-statement.") if not isinstance(value, (np.ndarray, type(None))): - #value = np.asarray(value) + # value = np.asarray(value) raise ValueError("Invalid type= %r" % type(value)) if self.mpi.disabled: self._elements[key] = value @@ -165,11 +170,11 @@ def writable(self): def _get_metadata(self): """Get shapes and datatypes of local data.""" - #return {key: (getattr(val, 'shape', None), getattr(val, 'dtype', type(None))) for key, val in self.local_data.items()} + # return {key: (getattr(val, 'shape', None), getattr(val, 'dtype', type(None))) for key, val in self.local_data.items()} mdata = {} for key, val in self.local_data.items(): - shape = getattr(val, 'shape', None) - dtype = getattr(val, 'dtype', type(None)) + shape = getattr(val, "shape", None) + dtype = getattr(val, "dtype", type(None)) mdata[key] = (shape, dtype) return mdata @@ -181,12 +186,12 @@ def __len__(self): def keys(self): if not self.readable: - raise RuntimeError("Cannot access keys inside of with-statement.""") + raise RuntimeError("Cannot access keys inside of with-statement." "") return self._elements.keys() def values(self): if not self.readable: - raise RuntimeError("Cannot access values inside of with-statement.""") + raise RuntimeError("Cannot access values inside of with-statement." "") return self._elements.values() def get_location(self, key): @@ -205,11 +210,11 @@ def synchronize(self): self.mpi.world.Barrier() mdata = self._get_metadata() allmdata = self.mpi.world.allgather(mdata) - assert (len(allmdata) == len(self.mpi)) + assert len(allmdata) == len(self.mpi) elements = {} for rank, d in enumerate(allmdata): for key, mdata in d.items(): - #print("Rank %d has key: %r" % (rank, key)) + # print("Rank %d has key: %r" % (rank, key)) if key in elements: raise AttributeError("Key '%s' used multiple times. Keys need to be unique." % key) shape, dtype = mdata diff --git a/vayesta/mpi/scf.py b/vayesta/mpi/scf.py index ec954a3c7..748cafb9b 100644 --- a/vayesta/mpi/scf.py +++ b/vayesta/mpi/scf.py @@ -17,7 +17,7 @@ def scf_with_mpi(mpi, mf, mpi_rank=0, log=None): log = log or mpi.log or logging.getLogger(__name__) def mpi_kernel(self, *args, **kwargs): - df = getattr(self, 'with_df', None) + df = getattr(self, "with_df", None) if mpi.rank == mpi_rank: log.info("MPI rank= %3d is running SCF", mpi.rank) with log_time(log.timing, "Time for SCF: %s"): @@ -28,10 +28,10 @@ def mpi_kernel(self, *args, **kwargs): # Generate auxiliary cell, compensation basis etc,..., but not 3c integrals: if df is not None: # Molecules - if getattr(df, 'auxmol', False) is None: + if getattr(df, "auxmol", False) is None: df.auxmol = pyscf.df.addons.make_auxmol(df.mol, df.auxbasis) # Solids - elif getattr(df, 'auxcell', False) is None: + elif getattr(df, "auxcell", False) is None: df.build(with_j3c=False) log.info("MPI rank= %3d is waiting for SCF results", mpi.rank) mpi.world.barrier() @@ -56,7 +56,6 @@ def mpi_kernel(self, *args, **kwargs): def gdf_with_mpi(mpi, df, mpi_rank=0, log=None): - log = log or mpi.log or logging.getLogger(__name__) if not isinstance(df._cderi_to_save, str): @@ -66,7 +65,7 @@ def gdf_with_mpi(mpi, df, mpi_rank=0, log=None): pbc = isinstance(df, pyscf.pbc.df.GDF) - cderi_file = getattr(df._cderi_to_save, 'name', df._cderi_to_save) + cderi_file = getattr(df._cderi_to_save, "name", df._cderi_to_save) df._cderi_to_save = mpi.world.bcast(cderi_file, root=mpi_rank) log.debug("df._cderi_to_save= %s", df._cderi_to_save) diff --git a/vayesta/rpa/rirpa/NI_eval.py b/vayesta/rpa/rirpa/NI_eval.py index 96ece7d26..1a0c10176 100644 --- a/vayesta/rpa/rirpa/NI_eval.py +++ b/vayesta/rpa/rirpa/NI_eval.py @@ -116,10 +116,7 @@ def eval_diag_NI_approx_deriv2(self, a): def get_deriv2_contrib(freq, weight, a): deriv = self.eval_diag_deriv_contrib(freq) deriv2 = self.eval_diag_deriv2_contrib(freq) - return ( - 2 * (weight / a) * (freq / a) * deriv - + weight * (freq / a) ** 2 * deriv2 - ) + return 2 * (weight / a) * (freq / a) * deriv + weight * (freq / a) ** 2 * deriv2 return self._NI_eval_deriv(a, self.diag_shape, get_deriv2_contrib) @@ -131,13 +128,9 @@ def test_diag_derivs(self, a, delta=1e-6): ) grad_1 = self.eval_diag_deriv_contrib(freq) deriv2_1 = self.eval_diag_deriv2_contrib(freq) - grad_2 = ( - self.eval_diag_contrib(freq + delta / 2) - - self.eval_diag_contrib(freq - delta / 2) - ) / delta + grad_2 = (self.eval_diag_contrib(freq + delta / 2) - self.eval_diag_contrib(freq - delta / 2)) / delta deriv2_2 = ( - self.eval_diag_deriv_contrib(freq + delta / 2) - - self.eval_diag_deriv_contrib(freq - delta / 2) + self.eval_diag_deriv_contrib(freq + delta / 2) - self.eval_diag_deriv_contrib(freq - delta / 2) ) / delta self.log.info("Max Grad Error=%6.4e", abs(grad_1 - grad_2).max()) self.log.info("Max Deriv2 Error=%6.4e", abs(deriv2_1 - deriv2_2).max()) @@ -145,14 +138,8 @@ def test_diag_derivs(self, a, delta=1e-6): self.log.info("Testing ensemble gradients w.r.t variation of a:") grad_1 = self.eval_diag_NI_approx_grad(a) deriv2_1 = self.eval_diag_NI_approx_deriv2(a) - grad_2 = ( - self.eval_diag_NI_approx(a + delta / 2) - - self.eval_diag_NI_approx(a - delta / 2) - ) / delta - deriv2_2 = ( - self.eval_diag_NI_approx_grad(a + delta / 2) - - self.eval_diag_NI_approx_grad(a - delta / 2) - ) / delta + grad_2 = (self.eval_diag_NI_approx(a + delta / 2) - self.eval_diag_NI_approx(a - delta / 2)) / delta + deriv2_2 = (self.eval_diag_NI_approx_grad(a + delta / 2) - self.eval_diag_NI_approx_grad(a - delta / 2)) / delta self.log.info("Max Grad Error=%6.4e", abs(grad_1 - grad_2).max()) self.log.info("Max Deriv2 Error=%6.4e", abs(deriv2_1 - deriv2_2).max()) @@ -203,15 +190,12 @@ def find_good_start(ainit=1e-6, scale_fac=10.0, maxval=1e8, relevance_factor=5): # Did we find a root? opt_min = not res.converged if opt_min: - res = scipy.optimize.minimize_scalar( - lambda freq: abs(get_val(freq)), bounds=(mini, maxi), method="bounded" - ) + res = scipy.optimize.minimize_scalar(lambda freq: abs(get_val(freq)), bounds=(mini, maxi), method="bounded") if not res.success: raise NIException("Could not optimise `a' value.") solve = res.x self.log.info( - "Used minimisation to optimise quadrature grid: a= %.2e penalty value= %.2e " - "(smaller is better)", + "Used minimisation to optimise quadrature grid: a= %.2e penalty value= %.2e " "(smaller is better)", solve, res.fun, ) @@ -258,9 +242,7 @@ def kernel_adaptive(self): full_output=True, ) if not info.success: - raise NIException( - "Adaptive gaussian quadrature could not compute integral." - ) + raise NIException("Adaptive gaussian quadrature could not compute integral.") else: self.log.info( "Successfully computed integral via adaptive quadrature using %d evaluations with estimated error of %6.4e", @@ -318,9 +300,7 @@ def _NI_eval_w_error(self, a, res_shape, evaluator): a = scipy.linalg.norm(integral_quarter - integral) b = scipy.linalg.norm(integral_half - integral) error = self.calculate_error(a, b) - self.log.info( - "Numerical Integration performed with estimated L2 norm error %6.4e.", error - ) + self.log.info("Numerical Integration performed with estimated L2 norm error %6.4e.", error) return integral, error def calculate_error(self, a, b): @@ -359,9 +339,7 @@ def calculate_error(self, a, b): ) if not (any((real_roots > 0) & (real_roots < 1))): - self.log.critical( - "No real root found between 0 and 1 in NI error estimation; returning nan." - ) + self.log.critical("No real root found between 0 and 1 in NI error estimation; returning nan.") return np.nan else: # This defines the values of e^{-\beta n_p}, where we seek the value of \alpha e^{-4 \beta n_p} @@ -416,9 +394,7 @@ def npoints(self, value): "ill-conditioning in the quadrature construction. Watch out for floating-point overflows!" ) self._points, self._weights = np.polynomial.laguerre.laggauss(value) - self._weights = np.array( - [w * np.exp(p) for (p, w) in zip(self._points, self._weights)] - ) + self._weights = np.array([w * np.exp(p) for (p, w) in zip(self._points, self._weights)]) def get_quad(self, a): if a < 0: @@ -443,16 +419,8 @@ def gen_ClenCur_quad_semiinf(a, npoints): """Generate quadrature points and weights for Clenshaw-Curtis quadrature over semiinfinite range (0 to +inf)""" tvals = [(np.pi * j / (npoints + 1)) for j in range(1, npoints + 1)] points = [a / (np.tan(t / 2) ** 2) for t in tvals] - jsums = [ - sum( - [np.sin(j * t) * (1 - np.cos(j * np.pi)) / j for j in range(1, npoints + 1)] - ) - for t in tvals - ] - weights = [ - a * (4 * np.sin(t) / ((npoints + 1) * (1 - np.cos(t)) ** 2)) * s - for (t, s) in zip(tvals, jsums) - ] + jsums = [sum([np.sin(j * t) * (1 - np.cos(j * np.pi)) / j for j in range(1, npoints + 1)]) for t in tvals] + weights = [a * (4 * np.sin(t) / ((npoints + 1) * (1 - np.cos(t)) ** 2)) * s for (t, s) in zip(tvals, jsums)] return points, weights diff --git a/vayesta/rpa/rirpa/RIRPA.py b/vayesta/rpa/rirpa/RIRPA.py index 622a51866..a257a61db 100644 --- a/vayesta/rpa/rirpa/RIRPA.py +++ b/vayesta/rpa/rirpa/RIRPA.py @@ -147,7 +147,6 @@ def eps(self): eps = eps.reshape((self.ov,)) return eps - @property def D(self): eps = self.eps @@ -203,41 +202,31 @@ def kernel_moms(self, max_moment, target_rot=None, **kwargs): t_start = timer() if target_rot is None: - self.log.warning( - "Warning; generating full moment rather than local component. Will scale as O(N^5)." - ) + self.log.warning("Warning; generating full moment rather than local component. Will scale as O(N^5).") target_rot = np.eye(self.ov_tot) ri_decomps = self.get_compressed_MP() ri_mp, ri_apb, ri_amb = ri_decomps # First need to calculate zeroth moment. moments = np.zeros((max_moment + 1,) + target_rot.shape) - moments[0], err0 = self._kernel_mom0( - target_rot, ri_decomps=ri_decomps, **kwargs - ) + moments[0], err0 = self._kernel_mom0(target_rot, ri_decomps=ri_decomps, **kwargs) t_start_higher = timer() if max_moment > 0: # Grab mean. D = self.D - moments[1] = einsum("pq,q->pq", target_rot, D) + dot( - target_rot, ri_amb[0].T, ri_amb[1] - ) + moments[1] = einsum("pq,q->pq", target_rot, D) + dot(target_rot, ri_amb[0].T, ri_amb[1]) if max_moment > 1: for i in range(2, max_moment + 1): - moments[i] = einsum("pq,q->pq", moments[i - 2], D**2) + dot( - moments[i - 2], ri_mp[1].T, ri_mp[0] - ) + moments[i] = einsum("pq,q->pq", moments[i - 2], D**2) + dot(moments[i - 2], ri_mp[1].T, ri_mp[0]) self.record_memory() if max_moment > 0: self.log.info( "RIRPA Higher Moments wall time: %s", time_string(timer() - t_start_higher), ) - self.log.info( - "Overall RIRPA Moments wall time: %s", time_string(timer() - t_start) - ) + self.log.info("Overall RIRPA Moments wall time: %s", time_string(timer() - t_start)) return moments, err0 def _kernel_mom0( @@ -275,9 +264,7 @@ def _kernel_mom0( """ t_start = timer() if target_rot is None: - self.log.warning( - "Warning; generating full moment rather than local component. Will scale as O(N^5)." - ) + self.log.warning("Warning; generating full moment rather than local component. Will scale as O(N^5).") target_rot = np.eye(self.ov_tot) if ri_decomps is None: ri_mp, ri_apb, ri_amb = self.get_compressed_MP(alpha) @@ -329,23 +316,15 @@ def _kernel_mom0( # Use Cauchy-Schwartz to both obtain an upper bound on resulting mom0 error, and efficiently obtain upper bound # on norm of low-rank portion of P^{-1}. if upper_bound is not None: - pinv_norm = np.linalg.norm(self.D ** (-2)) + np.linalg.norm( - ri_apb_inv[0] - ) * np.linalg.norm(ri_apb_inv[1]) + pinv_norm = np.linalg.norm(self.D ** (-2)) + np.linalg.norm(ri_apb_inv[0]) * np.linalg.norm(ri_apb_inv[1]) mom0_ub = upper_bound * pinv_norm self.check_errors(mom0_ub, target_rot.size) else: mom0_ub = None - mom_lb = ( - self.test_eta0_error(mom0, target_rot, ri_apb, ri_amb) - if analytic_lower_bound - else None - ) + mom_lb = self.test_eta0_error(mom0, target_rot, ri_apb, ri_amb) if analytic_lower_bound else None - self.log.info( - "RIRPA Zeroth Moment wall time: %s", time_string(timer() - t_start) - ) + self.log.info("RIRPA Zeroth Moment wall time: %s", time_string(timer() - t_start)) return mom0, (mom0_ub, mom_lb) @@ -369,22 +348,15 @@ def test_eta0_error(self, mom0, target_rot, ri_apb, ri_amb): error = amb_exact - dot(mom0, apb, mom0.T) self.error = error e_norm = np.linalg.norm(error) - p_norm = np.linalg.norm(self.D) + np.linalg.norm(ri_apb[0]) * np.linalg.norm( - ri_apb[1] - ) - peta_norm = np.linalg.norm( - einsum("p,qp->pq", self.D, mom0) + dot(ri_apb[0].T, dot(ri_apb[1], mom0.T)) - ) + p_norm = np.linalg.norm(self.D) + np.linalg.norm(ri_apb[0]) * np.linalg.norm(ri_apb[1]) + peta_norm = np.linalg.norm(einsum("p,qp->pq", self.D, mom0) + dot(ri_apb[0].T, dot(ri_apb[1], mom0.T))) # Now to estimate resulting error estimate in eta0. try: - poly = np.polynomial.Polynomial( - [e_norm / p_norm, -2 * peta_norm / p_norm, 1] - ) + poly = np.polynomial.Polynomial([e_norm / p_norm, -2 * peta_norm / p_norm, 1]) roots = poly.roots() except np.linalg.LinAlgError: self.log.warning( - "Could not obtain eta0 error lower bound; this is usually due to vanishing norms: %e, " - "%e, %e.", + "Could not obtain eta0 error lower bound; this is usually due to vanishing norms: %e, " "%e, %e.", e_norm, p_norm, peta_norm, @@ -410,7 +382,6 @@ def kernel_trMPrt(self, npoints=48, ainit=10): return integral[0] + offset, err def kernel_energy(self, npoints=48, ainit=10, correction="linear"): - t_start = timer() e1, err = self.kernel_trMPrt(npoints, ainit) e2 = 0.0 @@ -418,28 +389,18 @@ def kernel_energy(self, npoints=48, ainit=10, correction="linear"): # Note that eri contribution to A and B is equal, so can get trace over one by dividing by two e3 = sum(self.D) + einsum("np,np->", ri_apb_eri, ri_apb_eri) / 2 if ri_apb_eri_neg is not None: - e3 -= einsum("np,np->", ri_apb_eri_neg, ri_apb_eri_neg) / 2 + e3 -= einsum("np,np->", ri_apb_eri_neg, ri_apb_eri_neg) / 2 err /= 2 if self.rixc is not None and correction is not None: if correction.lower() == "linear": ri_a_xc, ri_b_xc = self.get_ab_xc_ri() - eta0_xc, errs = self.kernel_moms( - 0, target_rot=ri_b_xc[0], npoints=npoints, ainit=ainit - ) + eta0_xc, errs = self.kernel_moms(0, target_rot=ri_b_xc[0], npoints=npoints, ainit=ainit) eta0_xc = eta0_xc[0] - err = tuple( - [ - (err**2 + x / 2**2) ** 0.5 if x is not None else None - for x in errs - ] - ) + err = tuple([(err**2 + x / 2**2) ** 0.5 if x is not None else None for x in errs]) val = np.dot(eta0_xc, ri_b_xc[1].T).trace() / 2 self.log.info("Approximated correlation energy contribution: %e", val) e2 -= val - e3 += ( - einsum("np,np->", ri_a_xc[0], ri_a_xc[1]) - - einsum("np,np->", ri_b_xc[0], ri_b_xc[1]) / 2 - ) + e3 += einsum("np,np->", ri_a_xc[0], ri_a_xc[1]) - einsum("np,np->", ri_b_xc[0], ri_b_xc[1]) / 2 elif correction.lower() == "xc_ac": pass self.e_corr_ss = 0.5 * (e1 + e2 - e3) @@ -464,12 +425,11 @@ def direct_AC_integration( fragment_projectors gives the projector within this local excitation space to the actual fragment.""" # Get the coulomb integrals. ri_eri, ri_eri_neg = self.get_apb_eri_ri() / np.sqrt(2) + # TODO use ri_eri_neg here. def get_eta_alpha(alpha, target_rot): newrirpa = self.__class__(self.mf, rixc=self.rixc, log=self.log) - moms, errs = newrirpa.kernel_moms( - 0, target_rot=target_rot, npoints=npoints, alpha=alpha - ) + moms, errs = newrirpa.kernel_moms(0, target_rot=target_rot, npoints=npoints, alpha=alpha) return moms[0] def run_ac_inter(func, deg=5): @@ -481,15 +441,16 @@ def run_ac_inter(func, deg=5): return sum([w * func(p) for w, p in zip(weights, points)]) if ri_eri_neg is not None: - raise NotImplementedError("Use of negative CDERI contributions with direct integration for the RIRPA " - "correlation energy not yet supported.") + raise NotImplementedError( + "Use of negative CDERI contributions with direct integration for the RIRPA " + "correlation energy not yet supported." + ) naux_eri = ri_eri.shape[0] if local_rot is None or fragment_projectors is None: lrot = ri_eri rrot = ri_eri else: - if cluster_constrain: lrot = np.concatenate(local_rot, axis=0) nloc_cum = np.cumsum([x.shape[0] for x in local_rot]) @@ -498,18 +459,12 @@ def run_ac_inter(func, deg=5): def get_contrib(rot, proj): lloc = dot(rot, ri_eri.T) - return dot( - lloc, lloc[: proj.shape[0]].T, proj, rot[: proj.shape[0]] - ) + return dot(lloc, lloc[: proj.shape[0]].T, proj, rot[: proj.shape[0]]) # return dot(rot[:proj.shape[0]].T, proj, lloc[:proj.shape[0]], lloc.T) else: - lrot = np.concatenate( - [ - dot(x[: p.shape[0]], ri_eri.T, ri_eri) - for x, p in zip(local_rot, fragment_projectors) - ], + [dot(x[: p.shape[0]], ri_eri.T, ri_eri) for x, p in zip(local_rot, fragment_projectors)], axis=0, ) nloc_cum = np.cumsum([x.shape[0] for x in fragment_projectors]) @@ -576,20 +531,10 @@ def precond(x, e, *args): if nosym: # Ensure left isn't in our kwargs. kwargs.pop("left", None) - e, c_l, c_r = pyscf.lib.eig( - hop, - x0, - precond, - max_space=max_space, - nroots=nroots, - left=True, - **kwargs - ) + e, c_l, c_r = pyscf.lib.eig(hop, x0, precond, max_space=max_space, nroots=nroots, left=True, **kwargs) return e, np.array(c_l), np.array(c_r) else: - e, c = pyscf.lib.davidson( - hop, x0, precond, max_space=max_space, nroots=nroots, **kwargs - ) + e, c = pyscf.lib.davidson(hop, x0, precond, max_space=max_space, nroots=nroots, **kwargs) return e, np.array(c) # Since A+B and A-B are symmetric can get eigenvalues straightforwardly. @@ -603,9 +548,7 @@ def precond(x, e, *args): raise RuntimeError("RPA approximation broken down!") # MP is asymmetric, so need to take care to obtain actual eigenvalues. # Use Davidson to obtain accurate right eigenvectors... - e_mp_r, c_l_approx, c_r = get_lowest_eigenvals( - self.D**2, *ri_mp, c0, nroots=nroots, nosym=True - ) + e_mp_r, c_l_approx, c_r = get_lowest_eigenvals(self.D**2, *ri_mp, c0, nroots=nroots, nosym=True) if not calc_xy: return e_mp_r ** (0.5) @@ -622,9 +565,7 @@ def precond(x, e, *args): if nroots > 1: c_l = np.dot(np.linalg.inv(ovlp), c_l) # Now diagonalise in corresponding subspace to get eigenvalues. - subspace = einsum("np,p,mp->nm", c_l, self.D**2, c_r) + einsum( - "np,yp,yq,mq->nm", c_l, *ri_mp, c_r - ) + subspace = einsum("np,p,mp->nm", c_l, self.D**2, c_r) + einsum("np,yp,yq,mq->nm", c_l, *ri_mp, c_r) e, c_sub = np.linalg.eig(subspace) # Now fold these eigenvectors into our definitions, xpy = np.dot(c_sub.T, c_r) @@ -637,9 +578,7 @@ def precond(x, e, *args): else: xpy = c_r / (ovlp ** (0.5)) xmy = c_l / (ovlp ** (0.5)) - e = einsum("p,p,p->", xmy, self.D**2, xpy) + einsum( - "p,yp,yq,q->", xmy, *ri_mp, xpy - ) + e = einsum("p,p,p->", xmy, self.D**2, xpy) + einsum("p,yp,yq,q->", xmy, *ri_mp, xpy) return e ** (0.5), xpy, xmy @@ -690,8 +629,10 @@ def construct_RI_AB(self): amb_lhs += [np.concatenate([ri_a_xc[0], ri_b_xc[0]], axis=0)] amb_rhs += [np.concatenate([ri_a_xc[1], -ri_b_xc[1]], axis=0)] - return (tuple([np.concatenate(x, axis=0) for x in [apb_lhs, apb_rhs]]), - tuple([np.concatenate(x, axis=0) for x in [amb_lhs, amb_rhs]])) + return ( + tuple([np.concatenate(x, axis=0) for x in [apb_lhs, apb_rhs]]), + tuple([np.concatenate(x, axis=0) for x in [amb_lhs, amb_rhs]]), + ) def compress_low_rank(self, ri_l, ri_r, name=None): return compress_low_rank(ri_l, ri_r, tol=self.svd_tol, log=self.log, name=name) @@ -705,7 +646,7 @@ def get_apb_eri_ri(self): lov_neg = lov_neg.reshape((lov_neg.shape[0], -1)) # Need to include factor of two since eris appear in both A and B. - ri_apb_eri = np.sqrt(2) * np.concatenate([lov, lov], axis = 1) + ri_apb_eri = np.sqrt(2) * np.concatenate([lov, lov], axis=1) ri_neg_apb_eri = None if lov_neg is not None: @@ -717,29 +658,21 @@ def get_ab_xc_ri(self): # Have low-rank representation for interactions over and above coulomb interaction. # Note that this is usually asymmetric, as correction is non-PSD. ri_a_aa = [ - einsum("npq,pi,qa->nia", x, self.mo_coeff_occ, self.mo_coeff_vir).reshape( - (-1, self.ov) - ) + einsum("npq,pi,qa->nia", x, self.mo_coeff_occ, self.mo_coeff_vir).reshape((-1, self.ov)) for x in self.rixc[0] ] ri_a_bb = [ - einsum("npq,pi,qa->nia", x, self.mo_coeff_occ, self.mo_coeff_vir).reshape( - (-1, self.ov) - ) + einsum("npq,pi,qa->nia", x, self.mo_coeff_occ, self.mo_coeff_vir).reshape((-1, self.ov)) for x in self.rixc[1] ] ri_b_aa = [ ri_a_aa[0], - einsum( - "npq,qi,pa->nia", self.rixc[0][1], self.mo_coeff_occ, self.mo_coeff_vir - ).reshape((-1, self.ov)), + einsum("npq,qi,pa->nia", self.rixc[0][1], self.mo_coeff_occ, self.mo_coeff_vir).reshape((-1, self.ov)), ] ri_b_bb = [ ri_a_bb[0], - einsum( - "npq,qi,pa->nia", self.rixc[1][1], self.mo_coeff_occ, self.mo_coeff_vir - ).reshape((-1, self.ov)), + einsum("npq,qi,pa->nia", self.rixc[1][1], self.mo_coeff_occ, self.mo_coeff_vir).reshape((-1, self.ov)), ] ri_a_xc = [np.concatenate([x, y], axis=1) for x, y in zip(ri_a_aa, ri_a_bb)] @@ -789,10 +722,7 @@ def get_log_qval(freq): log_qvals = [get_log_qval(x) for x in freqs] def get_log_specvals(freq): - return sum( - np.log(fullrpa.freqs_ss**2 + freq**2) - - np.log(self.D**2 + freq**2) - ) + return sum(np.log(fullrpa.freqs_ss**2 + freq**2) - np.log(self.D**2 + freq**2)) log_specvals = [get_log_specvals(x) for x in freqs] @@ -801,6 +731,7 @@ def get_log_specvals(freq): def record_memory(self): self.log.info(" %s", memory_string()) + def construct_product_RI(D, ri_1, ri_2): """Given two matrices expressed as low-rank modifications, cderi_1 and cderi_2, of some full-rank matrix D, construct the RI expression for the deviation of their product from D**2. @@ -819,12 +750,8 @@ def construct_product_RI(D, ri_1, ri_2): U = np.dot(ri_1_R, ri_2_L.T) - ri_L = np.concatenate( - [ri_1_L, einsum("p,np->np", D, ri_2_L) + np.dot(U.T, ri_1_L) / 2], axis=0 - ) - ri_R = np.concatenate( - [einsum("p,np->np", D, ri_1_R) + np.dot(U, ri_2_R) / 2, ri_2_R], axis=0 - ) + ri_L = np.concatenate([ri_1_L, einsum("p,np->np", D, ri_2_L) + np.dot(U.T, ri_1_L) / 2], axis=0) + ri_R = np.concatenate([einsum("p,np->np", D, ri_1_R) + np.dot(U, ri_2_R) / 2, ri_2_R], axis=0) return ri_L, ri_R @@ -844,9 +771,7 @@ def construct_inverse_RI(D, ri): urt_l = einsum("nm,m->nm", u, s ** (0.5)) urt_r = einsum("n,nm->nm", s ** (0.5), v) # Evaluate the resulting RI - return einsum("p,np,nm->mp", D ** (-1), ri_L, urt_l), einsum( - "p,np,nm->mp", D ** (-1), ri_R, urt_r.T - ) + return einsum("p,np,nm->mp", D ** (-1), ri_L, urt_l), einsum("p,np,nm->mp", D ** (-1), ri_R, urt_r.T) def compress_low_rank(ri_l, ri_r, tol=1e-12, log=None, name=None): diff --git a/vayesta/rpa/rirpa/RIURPA.py b/vayesta/rpa/rirpa/RIURPA.py index 2c1f3b4eb..0347366a9 100644 --- a/vayesta/rpa/rirpa/RIURPA.py +++ b/vayesta/rpa/rirpa/RIURPA.py @@ -104,8 +104,9 @@ def get_apb_eri_ri(self): lovb = lovb.reshape((lovb.shape[0], -1)) if lova_neg is not None: if lovb_neg is None: - raise RuntimeError("Encountered negative cderi contribution in only one spin channel." - "Isn't this impossible?") + raise RuntimeError( + "Encountered negative cderi contribution in only one spin channel." "Isn't this impossible?" + ) lova_neg = lova_neg.reshape((lova_neg.shape[0], -1)) lovb_neg = lovb_neg.reshape((lovb_neg.shape[0], -1)) @@ -122,15 +123,11 @@ def get_ab_xc_ri(self): # Have low-rank representation for interactions over and above coulomb interaction. # Note that this is usually asymmetric, as correction is non-PSD. ri_a_aa = [ - einsum( - "npq,pi,qa->nia", x, self.mo_coeff_occ[0], self.mo_coeff_vir[0] - ).reshape((-1, self.ov[0])) + einsum("npq,pi,qa->nia", x, self.mo_coeff_occ[0], self.mo_coeff_vir[0]).reshape((-1, self.ov[0])) for x in self.rixc[0] ] ri_a_bb = [ - einsum( - "npq,pi,qa->nia", x, self.mo_coeff_occ[1], self.mo_coeff_vir[1] - ).reshape((-1, self.ov[1])) + einsum("npq,pi,qa->nia", x, self.mo_coeff_occ[1], self.mo_coeff_vir[1]).reshape((-1, self.ov[1])) for x in self.rixc[1] ] diff --git a/vayesta/rpa/rirpa/RIdRRPA.py b/vayesta/rpa/rirpa/RIdRRPA.py index 0aff47c7d..2478e777f 100644 --- a/vayesta/rpa/rirpa/RIdRRPA.py +++ b/vayesta/rpa/rirpa/RIdRRPA.py @@ -9,8 +9,7 @@ class ssRIdRRPA(ssRIRRPA): - """Class for computing direct RPA correlated quantites with a restricted reference state. - """ + """Class for computing direct RPA correlated quantites with a restricted reference state.""" @with_doc(ssRIRRPA.kernel_moms) def kernel_moms(self, max_moment, target_rot=None, return_spatial=False, **kwargs): @@ -18,9 +17,7 @@ def kernel_moms(self, max_moment, target_rot=None, return_spatial=False, **kwarg self.log.debug("Running specialised dRPA RHF code.") if target_rot is None: - self.log.warning( - "Warning; generating full moment rather than local component. Will scale as O(N^5)." - ) + self.log.warning("Warning; generating full moment rather than local component. Will scale as O(N^5).") if return_spatial: target_rot = np.eye(self.ov) else: @@ -30,9 +27,7 @@ def kernel_moms(self, max_moment, target_rot=None, return_spatial=False, **kwarg ri_mp, ri_apb, ri_amb = ri_decomps # First need to calculate zeroth moment. This only generates the spin-independent contribution in a single # spin channel; the spin-dependent contribution is just the identity rotated to our target rotation. - mom0, err0 = self._kernel_mom0( - target_rot, ri_decomps=ri_decomps, return_spatial=return_spatial, **kwargs - ) + mom0, err0 = self._kernel_mom0(target_rot, ri_decomps=ri_decomps, return_spatial=return_spatial, **kwargs) moments = np.zeros((max_moment + 1,) + mom0.shape, dtype=mom0.dtype) @@ -53,15 +48,16 @@ def gen_new_moment(prev_mom): moments[1] = target_rot * eps[None] else: + def gen_new_moment(prev_mom): - prev_aa, prev_bb = prev_mom[:, :self.ov], prev_mom[:, self.ov:] + prev_aa, prev_bb = prev_mom[:, : self.ov], prev_mom[:, self.ov :] spat_vv = dot(prev_aa + prev_bb, ri_mp[1].T, ri_mp[0]) new_aa = spat_vv + prev_aa * (eps**2)[None] new_bb = spat_vv + prev_bb * (eps**2)[None] return np.concatenate((new_aa, new_bb), axis=1) if target_rot.shape[1] == self.ov: - moments[1, :, :self.ov] = target_rot * eps[None] + moments[1, :, : self.ov] = target_rot * eps[None] else: moments[1] = target_rot * self.D[None] @@ -74,9 +70,7 @@ def gen_new_moment(prev_mom): "RIRPA Higher Moments wall time: %s", time_string(timer() - t_start_higher), ) - self.log.info( - "Overall RIRPA Moments wall time: %s", time_string(timer() - t_start) - ) + self.log.info("Overall RIRPA Moments wall time: %s", time_string(timer() - t_start)) return moments, err0 @with_doc(ssRIRRPA._kernel_mom0) @@ -92,7 +86,7 @@ def _kernel_mom0( ri_decomps=None, return_niworker=False, analytic_lower_bound=False, - return_spatial=False + return_spatial=False, ): t_start = timer() if analytic_lower_bound or adaptive_quad or integral_deduct != "HO": @@ -149,19 +143,11 @@ def _kernel_mom0( # Factor of two from sum over spin channels. mom0_spinindependent -= dot( - dot( - dot( - target_rot * self.eps[None] + 2 * integral, (ri_apb[1] * epsinv[None]).T - ), - u - ), - ri_apb[0] * epsinv[None] + dot(dot(target_rot * self.eps[None] + 2 * integral, (ri_apb[1] * epsinv[None]).T), u), + ri_apb[0] * epsinv[None], ) - - self.log.info( - "RIRPA Zeroth Moment wall time: %s", time_string(timer() - t_start) - ) + self.log.info("RIRPA Zeroth Moment wall time: %s", time_string(timer() - t_start)) if trrot is not None: target_rot = dot(trrot, target_rot) @@ -174,15 +160,19 @@ def _kernel_mom0( n = target_rot.shape[0] if stack_spin: # Half of target rot are actually other spin. - n = n// 2 + n = n // 2 mom0 = np.zeros((n, self.ov_tot)) if stack_spin: - mom0[:, :self.ov] = target_rot[:n] + mom0_spinindependent[:n] + mom0_spinindependent[n:] # Has aa and ab interactions. - mom0[:, self.ov:] = target_rot[n:] + mom0_spinindependent[n:] + mom0_spinindependent[:n] # Has bb and ba interactions. + mom0[:, : self.ov] = ( + target_rot[:n] + mom0_spinindependent[:n] + mom0_spinindependent[n:] + ) # Has aa and ab interactions. + mom0[:, self.ov :] = ( + target_rot[n:] + mom0_spinindependent[n:] + mom0_spinindependent[:n] + ) # Has bb and ba interactions. else: - mom0[:, :self.ov] = target_rot + mom0_spinindependent - mom0[:, self.ov:] = mom0_spinindependent + mom0[:, : self.ov] = target_rot + mom0_spinindependent + mom0[:, self.ov :] = mom0_spinindependent return mom0, (None, None) @@ -198,7 +188,6 @@ def kernel_trMPrt(self, npoints=48, ainit=10): return integral[0] + offset, err def kernel_energy(self, npoints=48, ainit=10, correction="linear"): - t_start = timer() e1, err = self.kernel_trMPrt(npoints, ainit) e2 = 0.0 @@ -209,7 +198,7 @@ def kernel_energy(self, npoints=48, ainit=10, correction="linear"): # Note that eri contribution to A and B is equal, so can get trace over one by dividing by two e3 = 2 * (sum(self.eps) + np.tensordot(cderi, cderi, ((0, 1), (0, 1)))) if cderi_neg is not None: - e3 -= 2 * np.tensordot(cderi_neg, cderi_neg, ((0, 1), (0, 1))) + e3 -= 2 * np.tensordot(cderi_neg, cderi_neg, ((0, 1), (0, 1))) err /= 2 self.e_corr_ss = 0.5 * (e1 + e2 - e3) self.log.info( @@ -227,8 +216,7 @@ def get_compressed_MP(self, alpha=1.0): if lov_neg is not None: lov_neg = lov_neg.reshape(lov_neg.shape[0], self.ov) - ri_apb = [np.concatenate([lov, lov_neg], axis=0), - np.concatenate([lov, -lov_neg], axis=0)] + ri_apb = [np.concatenate([lov, lov_neg], axis=0), np.concatenate([lov, -lov_neg], axis=0)] if self.compress > 3: ri_apb = self.compress_low_rank(*ri_apb, name="A+B") @@ -244,14 +232,12 @@ def get_compressed_MP(self, alpha=1.0): def check_target_rot(self, target_rot): stack_spins = False if target_rot is None: - self.log.warning( - "Warning; generating full moment rather than local component. Will scale as O(N^5)." - ) + self.log.warning("Warning; generating full moment rather than local component. Will scale as O(N^5).") target_rot = np.eye(self.ov) elif target_rot.shape[1] == self.ov_tot: # Provided rotation is in spinorbital space. We want to convert to spatial, but record that we've done this # so we can convert back later. - target_rot = np.concatenate([target_rot[:, :self.ov], target_rot[:, self.ov:]], axis=0) + target_rot = np.concatenate([target_rot[:, : self.ov], target_rot[:, self.ov :]], axis=0) stack_spins = True return target_rot, stack_spins diff --git a/vayesta/rpa/rirpa/energy_NI.py b/vayesta/rpa/rirpa/energy_NI.py index bc9740743..059b94fdc 100644 --- a/vayesta/rpa/rirpa/energy_NI.py +++ b/vayesta/rpa/rirpa/energy_NI.py @@ -3,10 +3,7 @@ import numpy as np from vayesta.core.util import dot, einsum -from vayesta.rpa.rirpa.NI_eval import ( - NumericalIntegratorClenCurInfinite, - NIException -) +from vayesta.rpa.rirpa.NI_eval import NumericalIntegratorClenCurInfinite, NIException from vayesta.rpa.rirpa.momzero_NI import diag_sqrt_contrib, diag_sqrt_grad, diag_sqrt_deriv2 @@ -49,9 +46,7 @@ def diagmat1(self): @diagmat1.setter def diagmat1(self, val): if val is not None and any(val < 0.0): - raise NIException( - "Error in numerical integration; diagonal approximation is non-PSD" - ) + raise NIException("Error in numerical integration; diagonal approximation is non-PSD") self._diagmat1 = val @property @@ -61,9 +56,7 @@ def diagmat2(self): @diagmat2.setter def diagmat2(self, val): if val is not None and any(val < 0.0): - raise NIException( - "Error in numerical integration; diagonal approximation is non-PSD" - ) + raise NIException("Error in numerical integration; diagonal approximation is non-PSD") self._diagmat2 = val def eval_diag_contrib(self, freq): @@ -113,6 +106,7 @@ def eval_contrib(self, freq): class NITrRootMP_dRHF(NITrRootMP): """All provided quantities are now in spatial orbitals. This actually only requires an additional factor in the get_Q method.""" + def get_Q(self, freq): # Have equal contributions from both spin channels. return 2 * super().get_Q(freq) diff --git a/vayesta/rpa/rirpa/momzero_NI.py b/vayesta/rpa/rirpa/momzero_NI.py index 28bb00ca6..0861ad108 100644 --- a/vayesta/rpa/rirpa/momzero_NI.py +++ b/vayesta/rpa/rirpa/momzero_NI.py @@ -45,9 +45,7 @@ def diagmat1(self): @diagmat1.setter def diagmat1(self, val): if val is not None and any(val < 0.0): - raise NIException( - "Error in numerical integration; diagonal approximation is non-PSD" - ) + raise NIException("Error in numerical integration; diagonal approximation is non-PSD") self._diagmat1 = val @property @@ -57,9 +55,7 @@ def diagmat2(self): @diagmat2.setter def diagmat2(self, val): if val is not None and any(val < 0.0): - raise NIException( - "Error in numerical integration; diagonal approximation is non-PSD" - ) + raise NIException("Error in numerical integration; diagonal approximation is non-PSD") self._diagmat2 = val @@ -121,9 +117,7 @@ def eval_contrib(self, freq): rrot = F lrot = einsum("lq,q->lq", self.target_rot, F) val_aux = np.linalg.inv(np.eye(self.n_aux) + Q) - res = dot( - dot(dot(lrot, self.S_L.T), val_aux), einsum("np,p->np", self.S_R, rrot) - ) + res = dot(dot(dot(lrot, self.S_L.T), val_aux), einsum("np,p->np", self.S_R, rrot)) res = (freq**2) * res / np.pi return res @@ -168,9 +162,7 @@ def eval_contrib(self, freq): rrot = F lrot = einsum("lq,q->lq", self.target_rot, F) val_aux = np.linalg.inv(np.eye(self.n_aux) + Q) - np.eye(self.n_aux) - res = dot( - dot(dot(lrot, self.S_L.T), val_aux), self.S_R * rrot[None] - ) + res = dot(dot(dot(lrot, self.S_L.T), val_aux), self.S_R * rrot[None]) res = (freq**2) * res / np.pi return res @@ -215,9 +207,7 @@ def eval_diag_exact(self): return 0.5 * np.multiply(self.D ** (-1), self.diagRI) -class MomzeroOffsetCalcGaussLag( - BaseMomzeroOffset, NumericalIntegratorGaussianSemiInfinite -): +class MomzeroOffsetCalcGaussLag(BaseMomzeroOffset, NumericalIntegratorGaussianSemiInfinite): pass @@ -242,9 +232,11 @@ def diag_sqrt_deriv2(D, freq): # Subclass for performing calculations with RHF quantities. + class MomzeroDeductHigherOrder_dRHF(MomzeroDeductHigherOrder): """All provided quantities are now in spatial orbitals. This actually only requires an additional factor in the get_Q method.""" + def get_Q(self, freq): # Have equal contributions from both spin channels. return 2 * super().get_Q(freq) diff --git a/vayesta/rpa/rpa.py b/vayesta/rpa/rpa.py index 34a8388fe..0cc43fe3c 100644 --- a/vayesta/rpa/rpa.py +++ b/vayesta/rpa/rpa.py @@ -63,9 +63,7 @@ def solve_RPA_problem(ApB, AmB): assert all(e > 1e-12) ecorr_contrib = 0.5 * (sum(freqs) - 0.5 * (ApB.trace() + AmB.trace())) XpY = np.einsum("n,pn->pn", freqs ** (-0.5), np.dot(AmB_rt, c)) - XmY = np.einsum( - "n,pn->pn", freqs ** (0.5), np.dot(np.linalg.inv(AmB_rt), c) - ) + XmY = np.einsum("n,pn->pn", freqs ** (0.5), np.dot(np.linalg.inv(AmB_rt), c)) return ( freqs, ecorr_contrib, @@ -74,12 +72,8 @@ def solve_RPA_problem(ApB, AmB): ) t0 = timer() - self.freqs_ss, self.e_corr_ss, self.XpY_ss, self.XmY_ss = solve_RPA_problem( - ApB_ss, AmB_ss - ) - self.freqs_sf, self.e_corr_sf, self.XpY_sf, self.XmY_sf = solve_RPA_problem( - ApB_sf, AmB_sf - ) + self.freqs_ss, self.e_corr_ss, self.XpY_ss, self.XmY_ss = solve_RPA_problem(ApB_ss, AmB_ss) + self.freqs_sf, self.e_corr_sf, self.XpY_sf, self.XmY_sf = solve_RPA_problem(ApB_sf, AmB_sf) self.log.timing("Time to solve RPA problems: %s", time_string(timer() - t0)) if xc_kernel == "rpax": @@ -141,9 +135,7 @@ def get_interaction_kernel(self, xc_kernel="rpax", tda=False): self.log.info("RPA using coulomb interaction kernel.") eris = self.ao2mo() - v = eris[: self.nocc, self.nocc :, : self.nocc, self.nocc :].reshape( - (self.ov, self.ov) - ) + v = eris[: self.nocc, self.nocc :, : self.nocc, self.nocc :].reshape((self.ov, self.ov)) # Only nonzero contribution is between same-spin excitations due to coulomb interaction. kernel = ( (2 * v, 2 * v), @@ -156,9 +148,9 @@ def get_interaction_kernel(self, xc_kernel="rpax", tda=False): self.log.info("RPA using coulomb-exchange interaction kernel.") eris = self.ao2mo() v = eris[: self.nocc, self.nocc :, : self.nocc, self.nocc :] - ka = np.einsum( - "ijab->iajb", eris[: self.nocc, : self.nocc, self.nocc :, self.nocc :] - ).reshape((self.ov, self.ov)) + ka = np.einsum("ijab->iajb", eris[: self.nocc, : self.nocc, self.nocc :, self.nocc :]).reshape( + (self.ov, self.ov) + ) kb = np.einsum("ibja->iajb", v).reshape((self.ov, self.ov)) v = v.reshape((self.ov, self.ov)) kernel = ( diff --git a/vayesta/rpa/ssrpa.py b/vayesta/rpa/ssrpa.py index 0dd042d00..8494f3054 100644 --- a/vayesta/rpa/ssrpa.py +++ b/vayesta/rpa/ssrpa.py @@ -116,14 +116,10 @@ def kernel(self, xc_kernel=None, alpha=1.0): def calc_energy_correction(self, xc_kernel, version=3): if xc_kernel is None: - raise ValueError( - "Without an xc-kernel the plasmon formula energy is exact." - ) + raise ValueError("Without an xc-kernel the plasmon formula energy is exact.") t0 = timer() M, AmB, ApB, eps, fullv = self._gen_arrays(xc_kernel) - ApB_xc, AmB_xc = self.get_xc_contribs( - xc_kernel, self.mo_coeff_occ, self.mo_coeff_vir - ) + ApB_xc, AmB_xc = self.get_xc_contribs(xc_kernel, self.mo_coeff_occ, self.mo_coeff_vir) A_xc = (ApB_xc + AmB_xc) / 2 B_xc = (ApB_xc - AmB_xc) / 2 full_mom0 = self.gen_moms(0, xc_kernel)[0] @@ -142,31 +138,22 @@ def run_ac_inter(func, deg=5): return sum([w * func(p) for w, p in zip(weights, points)]) if version == 0: - e_plasmon = ( - 0.5 * (np.dot(full_mom0, ApB) - (0.5 * (ApB + AmB) - A_xc)).trace() - ) + e_plasmon = 0.5 * (np.dot(full_mom0, ApB) - (0.5 * (ApB + AmB) - A_xc)).trace() # Full integration of the adiabatic connection. def get_val_alpha(alpha): eta0 = get_eta_alpha(alpha) - return ( - einsum("pq,qp", A_xc + B_xc, eta0) - + einsum("pq,qp", A_xc - B_xc, np.linalg.inv(eta0)) - ) / 4 + return (einsum("pq,qp", A_xc + B_xc, eta0) + einsum("pq,qp", A_xc - B_xc, np.linalg.inv(eta0))) / 4 e = e_plasmon - run_ac_inter(get_val_alpha) e = (e, get_val_alpha) elif version == 1: - e_plasmon = ( - 0.5 * (np.dot(full_mom0, ApB) - (0.5 * (ApB + AmB) - A_xc)).trace() - ) + e_plasmon = 0.5 * (np.dot(full_mom0, ApB) - (0.5 * (ApB + AmB) - A_xc)).trace() # Integration of adiabatic connection, but with approximation of the inverse eta0` def get_val_alpha(alpha): eta0 = get_eta_alpha(alpha) - return ( - A_xc.trace() + einsum("pq,qp", B_xc, eta0 - np.eye(self.ov)) - ) / 2 + return (A_xc.trace() + einsum("pq,qp", B_xc, eta0 - np.eye(self.ov))) / 2 e = e_plasmon - run_ac_inter(get_val_alpha) e = (e, get_val_alpha) @@ -175,18 +162,11 @@ def get_val_alpha(alpha): e = 0.5 * (np.dot(full_mom0, ApB) - (0.5 * (ApB + AmB) - A_xc)).trace() e -= ( einsum("pq,qp->", A_xc + B_xc, full_mom0 + np.eye(self.ov)) - + einsum( - "pq,qp->", A_xc - B_xc, np.linalg.inv(full_mom0) + np.eye(self.ov) - ) + + einsum("pq,qp->", A_xc - B_xc, np.linalg.inv(full_mom0) + np.eye(self.ov)) ) / 8 elif version == 3: # Linear approximation in AC and approx of inverse. - e = ( - 0.5 - * ( - np.dot(full_mom0, ApB - B_xc / 2) - (0.5 * (ApB + AmB) - B_xc / 2) - ).trace() - ) + e = 0.5 * (np.dot(full_mom0, ApB - B_xc / 2) - (0.5 * (ApB + AmB) - B_xc / 2)).trace() elif version == 4: def get_val_alpha(alpha): @@ -224,7 +204,6 @@ def _gen_eps(self): epsa = epsb = eps return epsa, epsb - def _gen_arrays(self, xc_kernel=None, alpha=1.0): t0 = timer() @@ -245,9 +224,7 @@ def _gen_arrays(self, xc_kernel=None, alpha=1.0): M = np.einsum("p,pq,q->pq", AmB ** (0.5), ApB, AmB ** (0.5)) else: # Grab A and B contributions for XC kernel. - ApB_xc, AmB_xc = self.get_xc_contribs( - xc_kernel, self.mo_coeff_occ, self.mo_coeff_vir, alpha - ) + ApB_xc, AmB_xc = self.get_xc_contribs(xc_kernel, self.mo_coeff_occ, self.mo_coeff_vir, alpha) ApB = ApB + ApB_xc AmB = np.diag(AmB) + AmB_xc del ApB_xc, AmB_xc @@ -260,13 +237,11 @@ def _gen_arrays(self, xc_kernel=None, alpha=1.0): def get_k(self): eris = self.ao2mo() # Get coulomb interaction in occupied-virtual space. - v = eris[: self.nocc, self.nocc :, : self.nocc, self.nocc :].reshape( - (self.ova, self.ova) - ) + v = eris[: self.nocc, self.nocc :, : self.nocc, self.nocc :].reshape((self.ova, self.ova)) fullv = np.zeros((self.ov, self.ov)) - fullv[: self.ova, : self.ova] = fullv[self.ova :, self.ova :] = fullv[ - : self.ova, self.ova : - ] = fullv[self.ova :, : self.ova] = v + fullv[: self.ova, : self.ova] = fullv[self.ova :, self.ova :] = fullv[: self.ova, self.ova :] = fullv[ + self.ova :, : self.ova + ] = v return fullv def get_xc_contribs(self, xc_kernel, c_o, c_v, alpha=1.0): @@ -282,43 +257,43 @@ def get_xc_contribs(self, xc_kernel, c_o, c_v, alpha=1.0): ApB = np.zeros((self.ov, self.ov)) AmB = np.zeros_like(ApB) - V_A_aa = einsum( - "pqrs,pi,qa,rj,sb->iajb", xc_kernel[0], c_o_a, c_v_a, c_o_a, c_v_a - ).reshape((self.ova, self.ova)) + V_A_aa = einsum("pqrs,pi,qa,rj,sb->iajb", xc_kernel[0], c_o_a, c_v_a, c_o_a, c_v_a).reshape( + (self.ova, self.ova) + ) ApB[: self.ova, : self.ova] += V_A_aa AmB[: self.ova, : self.ova] += V_A_aa del V_A_aa - V_B_aa = einsum( - "pqsr,pi,qa,rj,sb->iajb", xc_kernel[0], c_o_a, c_v_a, c_o_a, c_v_a - ).reshape((self.ova, self.ova)) + V_B_aa = einsum("pqsr,pi,qa,rj,sb->iajb", xc_kernel[0], c_o_a, c_v_a, c_o_a, c_v_a).reshape( + (self.ova, self.ova) + ) ApB[: self.ova, : self.ova] += V_B_aa AmB[: self.ova, : self.ova] -= V_B_aa del V_B_aa - V_A_ab = einsum( - "pqrs,pi,qa,rj,sb->iajb", xc_kernel[1], c_o_a, c_v_a, c_o_b, c_v_b - ).reshape((self.ova, self.ovb)) + V_A_ab = einsum("pqrs,pi,qa,rj,sb->iajb", xc_kernel[1], c_o_a, c_v_a, c_o_b, c_v_b).reshape( + (self.ova, self.ovb) + ) ApB[: self.ova, self.ova :] += V_A_ab ApB[self.ova :, : self.ova] += V_A_ab.T AmB[: self.ova, self.ova :] += V_A_ab AmB[self.ova :, : self.ova] += V_A_ab.T del V_A_ab - V_B_ab = einsum( - "pqsr,pi,qa,rj,sb->iajb", xc_kernel[1], c_o_a, c_v_a, c_o_b, c_v_b - ).reshape((self.ova, self.ovb)) + V_B_ab = einsum("pqsr,pi,qa,rj,sb->iajb", xc_kernel[1], c_o_a, c_v_a, c_o_b, c_v_b).reshape( + (self.ova, self.ovb) + ) ApB[: self.ova, self.ova :] += V_B_ab ApB[self.ova :, : self.ova] += V_B_ab.T AmB[: self.ova, self.ova :] -= V_B_ab AmB[self.ova :, : self.ova] -= V_B_ab.T del V_B_ab - V_A_bb = einsum( - "pqrs,pi,qa,rj,sb->iajb", xc_kernel[2], c_o_b, c_v_b, c_o_b, c_v_b - ).reshape((self.ovb, self.ovb)) + V_A_bb = einsum("pqrs,pi,qa,rj,sb->iajb", xc_kernel[2], c_o_b, c_v_b, c_o_b, c_v_b).reshape( + (self.ovb, self.ovb) + ) ApB[self.ova :, self.ova :] += V_A_bb AmB[self.ova :, self.ova :] += V_A_bb del V_A_bb - V_B_bb = einsum( - "pqsr,pi,qa,rj,sb->iajb", xc_kernel[2], c_o_b, c_v_b, c_o_b, c_v_b - ).reshape((self.ovb, self.ovb)) + V_B_bb = einsum("pqsr,pi,qa,rj,sb->iajb", xc_kernel[2], c_o_b, c_v_b, c_o_b, c_v_b).reshape( + (self.ovb, self.ovb) + ) ApB[self.ova :, self.ova :] += V_B_bb AmB[self.ova :, self.ova :] -= V_B_bb del V_B_bb @@ -340,12 +315,12 @@ def gen_moms(self, max_mom, xc_kernel=None): if self.ov_rot is not None: nova = self.ov_rot[0].shape[0] nov = nova + self.ov_rot[1].shape[0] - res = np.zeros((max_mom+1, nov, nov)) + res = np.zeros((max_mom + 1, nov, nov)) for x in range(max_mom + 1): # Have different spin components in general; these are alpha-alpha, alpha-beta and beta-beta. - res[x, :nova, :nova] = np.einsum("pn,n,qn->pq", self.XpY_ss[0], self.freqs_ss ** x, self.XpY_ss[0]) - res[x, nova:, nova:] = np.einsum("pn,n,qn->pq", self.XpY_ss[1], self.freqs_ss ** x, self.XpY_ss[1]) - res[x, :nova, nova:] = np.einsum("pn,n,qn->pq", self.XpY_ss[0], self.freqs_ss ** x, self.XpY_ss[1]) + res[x, :nova, :nova] = np.einsum("pn,n,qn->pq", self.XpY_ss[0], self.freqs_ss**x, self.XpY_ss[0]) + res[x, nova:, nova:] = np.einsum("pn,n,qn->pq", self.XpY_ss[1], self.freqs_ss**x, self.XpY_ss[1]) + res[x, :nova, nova:] = np.einsum("pn,n,qn->pq", self.XpY_ss[0], self.freqs_ss**x, self.XpY_ss[1]) res[x, nova:, :nova] = res[x][:nova, nova:].T return res diff --git a/vayesta/rpa/ssurpa.py b/vayesta/rpa/ssurpa.py index 48a353e20..9d5823b34 100644 --- a/vayesta/rpa/ssurpa.py +++ b/vayesta/rpa/ssurpa.py @@ -91,9 +91,7 @@ def _gen_arrays(self, xc_kernel=None, alpha=1.0): M = np.einsum("p,pq,q->pq", AmB ** (0.5), ApB, AmB ** (0.5)) else: # Grab A and B contributions for XC kernel. - ApB_xc, AmB_xc = self.get_xc_contribs( - xc_kernel, self.mo_coeff_occ, self.mo_coeff_vir, alpha - ) + ApB_xc, AmB_xc = self.get_xc_contribs(xc_kernel, self.mo_coeff_occ, self.mo_coeff_vir, alpha) ApB = ApB + ApB_xc AmB = np.diag(AmB) + AmB_xc del ApB_xc, AmB_xc @@ -127,8 +125,9 @@ def ao2mo(self, mo_coeff=None): if isinstance(self.mf._eri, tuple): eris_aa = pyscf.ao2mo.kernel(self.mf._eri[0], mo_coeff[0], compact=False) eris_bb = pyscf.ao2mo.kernel(self.mf._eri[2], mo_coeff[1], compact=False) - eris_ab = pyscf.ao2mo.kernel(self.mf._eri[1], (mo_coeff[0], mo_coeff[0], mo_coeff[1], mo_coeff[1]), - compact=False) + eris_ab = pyscf.ao2mo.kernel( + self.mf._eri[1], (mo_coeff[0], mo_coeff[0], mo_coeff[1], mo_coeff[1]), compact=False + ) else: # Call three-times to spin-restricted embedding self.log.debugv("Making (aa|aa) ERIs...") diff --git a/vayesta/solver/__init__.py b/vayesta/solver/__init__.py index d13d5941d..cfd7e0ad4 100644 --- a/vayesta/solver/__init__.py +++ b/vayesta/solver/__init__.py @@ -16,8 +16,9 @@ else: _has_ebcc = True + def get_solver_class(ham, solver): - assert (is_ham(ham)) + assert is_ham(ham) uhf = is_uhf_ham(ham) eb = is_eb_ham(ham) return _get_solver_class(uhf, eb, solver, ham.log) @@ -89,6 +90,7 @@ def _get_solver_class_internal(is_uhf, is_eb, solver, log): if solver == "CCSD" and is_eb: log.warning("CCSD solver requested for coupled electron-boson system; defaulting to CCSD-SD-1-1.") solver = "CCSD-SD-1-1" + # This is just a wrapper to allow us to use the solver option as the ansatz kwarg in this case. def get_right_CC(*args, **kwargs): setansatz = kwargs.get("ansatz", None) @@ -96,7 +98,8 @@ def get_right_CC(*args, **kwargs): if setansatz != solver: raise ValueError( "Desired CC ansatz specified differently in solver and solver_options.ansatz." - "Please use only specify via one approach, or ensure they agree.") + "Please use only specify via one approach, or ensure they agree." + ) kwargs["ansatz"] = solver return solverclass(*args, **kwargs) diff --git a/vayesta/solver/_uccsd_eris.py b/vayesta/solver/_uccsd_eris.py index 1c0fc42e2..33d0d9bf7 100644 --- a/vayesta/solver/_uccsd_eris.py +++ b/vayesta/solver/_uccsd_eris.py @@ -17,9 +17,9 @@ # Qiming Sun <osirpt.sun@gmail.com> # -''' +""" UCCSD with spatial integrals -''' +""" import numpy as np from pyscf import lib, ao2mo @@ -31,11 +31,11 @@ def uao2mo(self, mo_coeff=None): nao = self.mo_coeff[0].shape[0] nmo_pair = nmoa * (nmoa + 1) // 2 nao_pair = nao * (nao + 1) // 2 - mem_incore = (max(nao_pair ** 2, nmoa ** 4) + nmo_pair ** 2) * 8 / 1e6 + mem_incore = (max(nao_pair**2, nmoa**4) + nmo_pair**2) * 8 / 1e6 mem_now = lib.current_memory()[0] - if (self._scf._eri is not None): - assert (np.ndim(self._scf._eri[0]) == 4) - if (mem_incore + mem_now < self.max_memory or self.incore_complete): + if self._scf._eri is not None: + assert np.ndim(self._scf._eri[0]) == 4 + if mem_incore + mem_now < self.max_memory or self.incore_complete: ao2mofn = ( lambda mo_coeff: ao2mo.restore(1, ao2mo.full(self._scf._eri[0], mo_coeff), mo_coeff.shape[1]), lambda mo_coeff: ao2mo.general(self._scf._eri[1], mo_coeff), @@ -46,8 +46,9 @@ def uao2mo(self, mo_coeff=None): else: raise RuntimeError("Dense Cluster ERIs predicted to exceed available memory.") else: - raise NotImplementedError("Current modifications to only support spin-dependent eris in UCCSD without " - "density fitting.") + raise NotImplementedError( + "Current modifications to only support spin-dependent eris in UCCSD without " "density fitting." + ) def _make_eris_incore(mycc, mo_coeff=None, ao2mofn=None): @@ -124,7 +125,7 @@ def _make_eris_incore(mycc, mo_coeff=None, ao2mofn=None): ovVV = eris.ovVV.reshape(nocca * nvira, nvirb, nvirb) eris.ovVV = lib.pack_tril(ovVV).reshape(nocca, nvira, nvirb * (nvirb + 1) // 2) - vvVV = eris.vvVV.reshape(nvira ** 2, nvirb ** 2) + vvVV = eris.vvVV.reshape(nvira**2, nvirb**2) idxa = np.tril_indices(nvira) idxb = np.tril_indices(nvirb) eris.vvVV = lib.take_2d(vvVV, idxa[0] * nvira + idxa[1], idxb[0] * nvirb + idxb[1]) diff --git a/vayesta/solver/ccsd.py b/vayesta/solver/ccsd.py index 3555c8284..65119e0f9 100644 --- a/vayesta/solver/ccsd.py +++ b/vayesta/solver/ccsd.py @@ -17,18 +17,16 @@ class Options(ClusterSolver.Options): max_cycle: int = 100 # Max number of iterations conv_tol: float = None # Convergence energy tolerance conv_tol_normt: float = None # Convergence amplitude tolerance - diis_space: int = None # DIIS space size + diis_space: int = None # DIIS space size diis_start_cycle: int = None # The step to start DIIS, default is 0 iterative_damping: float = None # self-consistent damping parameter level_shift: float = None # Level shift for virtual orbitals to stabilize ccsd iterations - init_guess: str = 'MP2' # ['MP2', 'CISD'] + init_guess: str = "MP2" # ['MP2', 'CISD'] solve_lambda: bool = True # If false, use Lambda=T approximation - n_moments: tuple = None + n_moments: tuple = None # Self-consistent mode sc_mode: int = None - - def kernel(self, t1=None, t2=None, l1=None, l2=None, coupled_fragments=None, t_diagnostic=True): mf_clus, frozen = self.hamil.to_pyscf_mf(allow_dummy_orbs=True, allow_df=True) solver_cls = self.get_solver_class(mf_clus) @@ -77,12 +75,12 @@ def kernel(self, t1=None, t2=None, l1=None, l2=None, coupled_fragments=None, t_d nmom = self.opts.n_moments if nmom is not None: try: - from dyson.expressions import CCSD + from dyson.expressions import CCSD except ImportError: self.log.error("Dyson not found - required for moment calculations") self.log.info("Skipping in-cluster moment calculations") return - self.log.info("Calculating in-cluster CCSD moments %s"%str(nmom)) + self.log.info("Calculating in-cluster CCSD moments %s" % str(nmom)) # expr = CCSD["1h"](mf_clus, t1=mycc.t1, t2=mycc.t2, l1=l1, l2=l2) # vecs_bra = expr.build_gf_vectors(nmom[0], left=True) # amps_bra = [expr.eom.vector_to_amplitudes(amps[n,p], ccm.nmo, ccm.nocc) for p in range(ccm.nmo) for n in range(nmom)] @@ -108,10 +106,10 @@ def get_solver_class(self, mf): return pyscf.cc.ccsd.RCCSD def generate_init_guess(self, eris=None): - if self.opts.init_guess in ('default', 'MP2'): + if self.opts.init_guess in ("default", "MP2"): # CCSD will build MP2 amplitudes return None, None - if self.opts.init_guess == 'CISD': + if self.opts.init_guess == "CISD": cisd = CISD_Solver(self.hamil) cisd.kernel() wf = cisd.wf.as_ccsd() @@ -151,10 +149,10 @@ def _debug_exact_wf(self, wf): ro = dot(wf.mo.coeff_occ.T, ovlp, mo.coeff_occ) rv = dot(wf.mo.coeff_vir.T, ovlp, mo.coeff_vir) t1 = dot(ro.T, wf.t1, rv) - t2 = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', ro, ro, wf.t2, rv, rv) + t2 = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", ro, ro, wf.t2, rv, rv) if wf.l1 is not None: l1 = dot(ro.T, wf.l1, rv) - l2 = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', ro, ro, wf.l2, rv, rv) + l2 = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", ro, ro, wf.l2, rv, rv) else: l1 = l2 = None self.wf = CCSD_WaveFunction(mo, t1, t2, l1=l1, l2=l2) @@ -192,17 +190,17 @@ def _debug_exact_wf(self, wf): rvb = dot(wf.mo.coeff_vir[1].T, ovlp, mo.coeff_vir[1]) t1a = dot(roa.T, wf.t1a, rva) t1b = dot(rob.T, wf.t1b, rvb) - t2aa = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', roa, roa, wf.t2aa, rva, rva) - t2ab = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', roa, rob, wf.t2ab, rva, rvb) - t2bb = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', rob, rob, wf.t2bb, rvb, rvb) + t2aa = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", roa, roa, wf.t2aa, rva, rva) + t2ab = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", roa, rob, wf.t2ab, rva, rvb) + t2bb = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", rob, rob, wf.t2bb, rvb, rvb) t1 = (t1a, t1b) t2 = (t2aa, t2ab, t2bb) if wf.l1 is not None: l1a = dot(roa.T, wf.l1a, rva) l1b = dot(rob.T, wf.l1b, rvb) - l2aa = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', roa, roa, wf.l2aa, rva, rva) - l2ab = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', roa, rob, wf.l2ab, rva, rvb) - l2bb = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', rob, rob, wf.l2bb, rvb, rvb) + l2aa = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", roa, roa, wf.l2aa, rva, rva) + l2ab = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", roa, rob, wf.l2ab, rva, rvb) + l2bb = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", rob, rob, wf.l2bb, rvb, rvb) l1 = (l1a, l1b) l2 = (l2aa, l2ab, l2bb) else: diff --git a/vayesta/solver/ccsdtq.py b/vayesta/solver/ccsdtq.py index 63aa336d2..7156bfdee 100644 --- a/vayesta/solver/ccsdtq.py +++ b/vayesta/solver/ccsdtq.py @@ -9,11 +9,11 @@ def t2_residual_rhf_t3v(solver, fragment, t3, v): dt2 = np.zeros((nocc, nocc, nvir, nvir)) # First term: 1/2 P_ab [t_ijmaef v_efbm] - dt2 += einsum('bemf, jimeaf -> ijab', gvvov - gvvov.transpose(0, 3, 2, 1), t3) / 2 - dt2 += einsum('bemf, ijmaef -> ijab', gvvov, t3) + dt2 += einsum("bemf, jimeaf -> ijab", gvvov - gvvov.transpose(0, 3, 2, 1), t3) / 2 + dt2 += einsum("bemf, ijmaef -> ijab", gvvov, t3) # Second term: -1/2 P_ij [t_imnabe v_jemn] - dt2 -= einsum('mjne, minbae -> ijab', gooov - govoo.transpose(0, 3, 2, 1), t3) / 2 - dt2 -= einsum('mjne, imnabe -> ijab', gooov, t3) + dt2 -= einsum("mjne, minbae -> ijab", gooov - govoo.transpose(0, 3, 2, 1), t3) / 2 + dt2 -= einsum("mjne, imnabe -> ijab", gooov, t3) # Permutation dt2 += dt2.transpose(1, 0, 3, 2) @@ -36,51 +36,51 @@ def t_residual_rhf(solver, fragment, t1, t2, t3, t4, f, v, include_t3v=False): # --- T1 update # --- T3 * V - dt1 -= einsum('ijab, jiupab -> up', spinned_antiphys_g, t3) + dt1 -= einsum("ijab, jiupab -> up", spinned_antiphys_g, t3) # --- T2 update # --- T3 * F if np.allclose(fov, np.zeros_like(fov)): solver.log.info("fov block zero: No T3 * f contribution.") # (Fa) (Taba) contraction - dt2 += einsum('me, ijmabe -> ijab', fov, t3) + dt2 += einsum("me, ijmabe -> ijab", fov, t3) # (Fb) (Tabb) contraction - dt2 += einsum('me, jimbae -> ijab', fov, t3) + dt2 += einsum("me, jimbae -> ijab", fov, t3) solver.log.info("(T3 * F) -> T2 update norm from fragment {}: {}".format(fragment.id, np.linalg.norm(dt2))) # --- T4 * V # (Vaa) (Tabaa) contraction - t4v = einsum('mnef, ijmnabef -> ijab', antiphys_g, t4_abaa) / 4 + t4v = einsum("mnef, ijmnabef -> ijab", antiphys_g, t4_abaa) / 4 t4v += t4v.transpose(1, 0, 3, 2) # (Vab) (Tabab) contraction - t4v += einsum('menf, ijmnabef -> ijab', govov, t4_abab) + t4v += einsum("menf, ijmnabef -> ijab", govov, t4_abab) dt2 += t4v # --- (T1 T3) * V # Note: Approximate T1 by the CCSDTQ T1 amplitudes of this fragment. # TODO: Relax this approximation via the callback? t1t3v = np.zeros_like(dt2) - X_ = einsum('mnef, me -> nf', spinned_antiphys_g, t1) - t1t3v += einsum('nf, nijfab -> ijab', X_, t3) + X_ = einsum("mnef, me -> nf", spinned_antiphys_g, t1) + t1t3v += einsum("nf, nijfab -> ijab", X_, t3) - X_ = einsum('mnef, njiebf -> ijmb', antiphys_g, t3) / 2 - X_ += einsum('menf, jinfeb -> ijmb', govov, t3) - t1t3v += einsum('ijmb, ma -> ijab', X_, t1) + X_ = einsum("mnef, njiebf -> ijmb", antiphys_g, t3) / 2 + X_ += einsum("menf, jinfeb -> ijmb", govov, t3) + t1t3v += einsum("ijmb, ma -> ijab", X_, t1) - X_ = einsum('mnef, mjnfba -> ejab', antiphys_g, t3) / 2 - X_ += einsum('menf, nmjbaf -> ejab', govov, t3) - t1t3v += einsum('ejab, ie -> ijab', X_, t1) + X_ = einsum("mnef, mjnfba -> ejab", antiphys_g, t3) / 2 + X_ += einsum("menf, nmjbaf -> ejab", govov, t3) + t1t3v += einsum("ejab, ie -> ijab", X_, t1) # apply permutation t1t3v += t1t3v.transpose(1, 0, 3, 2) dt2 += t1t3v solver.log.info("T1 norm in ext corr from fragment {}: {}".format(fragment.id, np.linalg.norm(t1))) - # --- T3 * V + # --- T3 * V if include_t3v: # Option to leave out this term, and instead perform T3 * V with the # integrals in the parent cluster later. # This will give a different result since the V operators - # will span a different space. Instead, here we just contract T3 with integrals + # will span a different space. Instead, here we just contract T3 with integrals # in cluster y (FCI), rather than cluster x (CCSD) dt2 += t2_residual_rhf_t3v(solver, fragment, t3, v) diff --git a/vayesta/solver/coupling.py b/vayesta/solver/coupling.py index 1d1be2890..b93ba97b5 100644 --- a/vayesta/solver/coupling.py +++ b/vayesta/solver/coupling.py @@ -22,10 +22,10 @@ def transform_amplitude(t, u_occ, u_vir, u_occ2=None, u_vir2=None, inverse=False ndim = t[0].ndim + 1 # Restricted T1: if ndim == 2: - return einsum('ia,ix,ay->xy', t, u_occ, u_vir) + return einsum("ia,ix,ay->xy", t, u_occ, u_vir) # Restricted T2: if ndim == 4: - return einsum('ijab,ix,jy,az,bw->xyzw', t, u_occ, u_occ2, u_vir, u_vir2) + return einsum("ijab,ix,jy,az,bw->xyzw", t, u_occ, u_occ2, u_vir, u_vir2) # Unrestricted T1: if ndim == 3: ta = transform_amplitude(t[0], u_occ[0], u_vir[0]) @@ -65,10 +65,10 @@ def project_t2_rspin(t2, proj, projectors): if projectors == 0: return t2 if projectors == 1: - t2 = einsum('xi,i...->x...', proj, t2) + t2 = einsum("xi,i...->x...", proj, t2) return (t2 + t2.transpose(1, 0, 3, 2)) / 2 if projectors == 2: - return einsum('xi,yj,ij...->xy...', proj, proj, t2) + return einsum("xi,yj,ij...->xy...", proj, proj, t2) raise ValueError @@ -79,10 +79,9 @@ def project_t2_uspin(t2, proj, projectors): t2bb = project_t2_rspin(t2[2], proj[1], projectors=projectors) if projectors == 1: # Average between projecting alpha and beta: - t2ab = (einsum('xi,ij...->xj...', proj[0], t2[1]) - + einsum('xj,ij...->ix...', proj[1], t2[1])) / 2 + t2ab = (einsum("xi,ij...->xj...", proj[0], t2[1]) + einsum("xj,ij...->ix...", proj[1], t2[1])) / 2 elif projectors == 2: - t2ab = einsum('xi,yj,ij...->xy...', proj[0], proj[1], t2[1]) + t2ab = einsum("xi,yj,ij...->xy...", proj[0], proj[1], t2[1]) else: raise ValueError # assert np.allclose(t2ab, -t2ab.transpose(0,1,3,2)) @@ -102,42 +101,42 @@ def couple_ccsd_iterations(solver, fragments): # F(x): Fragment orbitals of fragment x ovlp = solver.base.get_ovlp() - c_occ_x = np.asarray(solver.cluster.c_active_occ, order='C') - c_vir_x = np.asarray(solver.cluster.c_active_vir, order='C') + c_occ_x = np.asarray(solver.cluster.c_active_occ, order="C") + c_vir_x = np.asarray(solver.cluster.c_active_vir, order="C") p_occ = {} r_occ = {} r_vir = {} - rma = RMA_Dict.from_dict(mpi, { - (solver.fragment.id, 'c_active_occ'): c_occ_x, - (solver.fragment.id, 'c_active_vir'): c_vir_x}) + rma = RMA_Dict.from_dict( + mpi, {(solver.fragment.id, "c_active_occ"): c_occ_x, (solver.fragment.id, "c_active_vir"): c_vir_x} + ) for y in fragments: fy = y.c_proj - c_occ_y = rma[(y.id, 'c_active_occ')] - c_vir_y = rma[(y.id, 'c_active_vir')] - p_occ[y.id] = einsum('ai,ab,by,cy,cd,dj->ij', c_occ_x, ovlp, fy, fy, ovlp, c_occ_y) - r_occ[y.id] = einsum('ai,ab,bj->ij', c_occ_x, ovlp, c_occ_y) - r_vir[y.id] = einsum('ai,ab,bj->ij', c_vir_x, ovlp, c_vir_y) + c_occ_y = rma[(y.id, "c_active_occ")] + c_vir_y = rma[(y.id, "c_active_vir")] + p_occ[y.id] = einsum("ai,ab,by,cy,cd,dj->ij", c_occ_x, ovlp, fy, fy, ovlp, c_occ_y) + r_occ[y.id] = einsum("ai,ab,bj->ij", c_occ_x, ovlp, c_occ_y) + r_vir[y.id] = einsum("ai,ab,bj->ij", c_vir_x, ovlp, c_vir_y) rma.clear() def tailorfunc(kwargs): - cc = kwargs['mycc'] - t1, t2 = kwargs['t1new'], kwargs['t2new'] + cc = kwargs["mycc"] + t1, t2 = kwargs["t1new"], kwargs["t2new"] cc.force_iter = True cc.force_exit = bool(mpi.world.allreduce(int(cc.conv_flag), op=mpi.MPI.PROD)) conv = mpi.world.gather(int(cc.conv_flag), root=0) - rma = RMA_Dict.from_dict(mpi, {(mpi.rank, 't1'): t1, (mpi.rank, 't2'): t2}) + rma = RMA_Dict.from_dict(mpi, {(mpi.rank, "t1"): t1, (mpi.rank, "t2"): t2}) t1_out = np.zeros_like(t1) t2_out = np.zeros_like(t2) for y in fragments: - t1y, t2y = rma[(y.id, 't1')], rma[(y.id, 't2')] + t1y, t2y = rma[(y.id, "t1")], rma[(y.id, "t2")] po = p_occ[y.id] ro = r_occ[y.id] rv = r_vir[y.id] # print(solver.fragment.id, y.id, py.shape, t1_out.shape, t1y.shape) - t1_out += einsum('Ii,ia,Aa->IA', po, t1y, rv) - t2_out += einsum('Ii,Jj,ijab,Aa,Bb->IJAB', po, ro, t2y, rv, rv) + t1_out += einsum("Ii,ia,Aa->IA", po, t1y, rv) + t2_out += einsum("Ii,Jj,ijab,Aa,Bb->IJAB", po, ro, t2y, rv, rv) solver.log.info("Tailoring: |dT1|= %.3e |dT2|= %.3e", np.linalg.norm(t1_out - t1), np.linalg.norm(t2_out - t2)) rma.clear() t1[:] = t1_out @@ -188,7 +187,7 @@ def tailor_with_fragments(solver, fragments, project=False, tailor_t1=True, tail def tailor_func(kwargs): """Add external correction to T1 and T2 amplitudes.""" - t1, t2 = kwargs['t1new'], kwargs['t2new'] + t1, t2 = kwargs["t1new"], kwargs["t2new"] # Collect all changes to the amplitudes in dt1 and dt2: if tailor_t1: dt1 = spinalg.zeros_like(t1) @@ -197,17 +196,18 @@ def tailor_func(kwargs): # Loop over all *other* fragments/cluster X for y, fy in enumerate(fragments): - assert (fy is not fragment) + assert fy is not fragment # Rotation & projections from cluster X active space to current fragment active space rxy_occ = spinalg.dot(cxs_occ, fy.cluster.c_active_occ) rxy_vir = spinalg.dot(cxs_vir, fy.cluster.c_active_vir) # Skip fragment if there is no overlap - if spinsym == 'restricted': + if spinsym == "restricted": maxovlp = min(abs(rxy_occ).max(), abs(rxy_vir).max()) - elif spinsym == 'unrestricted': - maxovlp = min(max(abs(rxy_occ[0]).max(), abs(rxy_occ[1]).max()), - max(abs(rxy_vir[0]).max(), abs(rxy_vir[1]).max())) + elif spinsym == "unrestricted": + maxovlp = min( + max(abs(rxy_occ[0]).max(), abs(rxy_occ[1]).max()), max(abs(rxy_vir[0]).max(), abs(rxy_vir[1]).max()) + ) if maxovlp < ovlp_tol: solver.log.debug("Skipping tailoring fragment %s due to small overlap= %.1e", fy, maxovlp) continue @@ -224,7 +224,7 @@ def tailor_func(kwargs): # Project first one/two occupied index/indices onto fragment(y) space: if project: - proj = fy.get_overlap('frag|cluster-occ') + proj = fy.get_overlap("frag|cluster-occ") proj = spinalg.dot(spinalg.T(proj), proj) if tailor_t1: dt1y = spinalg.dot(proj, dt1y) @@ -237,20 +237,26 @@ def tailor_func(kwargs): if tailor_t2: dt2 = spinalg.add(dt2, transform_amplitude(dt2y, rxy_occ, rxy_vir, inverse=True)) - solver.log.debug("Tailoring with fragment %3d (%s): S(occ)= %.3e S(vir)= %.3e dT1= %.3e dT2= %.3e", - fy.id, fy.solver, nxy_occ[y], nxy_vir[y], *get_amplitude_norm(dt1y, dt2y)) + solver.log.debug( + "Tailoring with fragment %3d (%s): S(occ)= %.3e S(vir)= %.3e dT1= %.3e dT2= %.3e", + fy.id, + fy.solver, + nxy_occ[y], + nxy_vir[y], + *get_amplitude_norm(dt1y, dt2y), + ) # Add correction: if tailor_t1: - if spinsym == 'restricted': + if spinsym == "restricted": t1[:] += dt1 - elif spinsym == 'unrestricted': + elif spinsym == "unrestricted": t1[0][:] += dt1[0] t1[1][:] += dt1[1] if tailor_t2: - if spinsym == 'restricted': + if spinsym == "restricted": t2[:] += dt2 - elif spinsym == 'unrestricted': + elif spinsym == "unrestricted": t2[0][:] += dt2[0] t2[1][:] += dt2[1] t2[2][:] += dt2[2] @@ -264,9 +270,9 @@ def _integrals_for_extcorr(fragment, fock): emb = fragment.base eris = fragment.hamil.get_eris_screened() - if emb.spinsym == 'restricted': - occ = np.s_[:cluster.nocc_active] - vir = np.s_[cluster.nocc_active:] + if emb.spinsym == "restricted": + occ = np.s_[: cluster.nocc_active] + vir = np.s_[cluster.nocc_active :] govov = eris[occ, vir, occ, vir] # chemical notation gvvov = eris[vir, vir, occ, vir] gooov = eris[occ, occ, occ, vir] @@ -274,11 +280,11 @@ def _integrals_for_extcorr(fragment, fock): fov = dot(cluster.c_active_occ.T, fock, cluster.c_active_vir) return fov, (govov, gvvov, gooov, govoo) - elif emb.spinsym == 'unrestricted': - oa = np.s_[:cluster.nocc_active[0]] - ob = np.s_[:cluster.nocc_active[1]] - va = np.s_[cluster.nocc_active[0]:] - vb = np.s_[cluster.nocc_active[1]:] + elif emb.spinsym == "unrestricted": + oa = np.s_[: cluster.nocc_active[0]] + ob = np.s_[: cluster.nocc_active[1]] + va = np.s_[cluster.nocc_active[0] :] + vb = np.s_[cluster.nocc_active[1] :] fova = dot(cluster.c_active_occ[0].T, fock[0], cluster.c_active_vir[0]) fovb = dot(cluster.c_active_occ[1].T, fock[1], cluster.c_active_vir[1]) fov = (fova, fovb) @@ -312,14 +318,14 @@ def _get_delta_t_for_extcorr(fragment, fock, solver, include_t3v=True): Used for logging options include_t3v : bool If include_t3v, then these terms are included. If not, they are left out - (to be contracted later with cluster y integrals). + (to be contracted later with cluster y integrals). Returns ------- dt1, dt2 : numpy.ndarray - T1 and T2 expressions. + T1 and T2 expressions. - NOTE: These expressions still need to be contracted with energy denominators + NOTE: These expressions still need to be contracted with energy denominators for full amplitude updates. """ @@ -329,7 +335,7 @@ def _get_delta_t_for_extcorr(fragment, fock, solver, include_t3v=True): # J. Chem. Theory Comput. 2021, 17, 182−190 # also with reference to git@github.com:gustavojra/Methods.git - if fragment.base.spinsym == 'restricted': + if fragment.base.spinsym == "restricted": # Get ERIs and Fock matrix for the given fragment f, v = _integrals_for_extcorr(fragment, fock) t1, t2, t3 = wf.t1, wf.t2, wf.t3 @@ -337,7 +343,7 @@ def _get_delta_t_for_extcorr(fragment, fock, solver, include_t3v=True): dt1, dt2 = ccsdtq.t_residual_rhf(solver, fragment, wf.t1, wf.t2, wf.t3, wf.t4, f, v, include_t3v=include_t3v) - elif fragment.base.spinsym == 'unrestricted': + elif fragment.base.spinsym == "unrestricted": # Get ERIs and Fock matrix for the given fragment f, v = _integrals_for_extcorr(fragment, fock) @@ -377,36 +383,35 @@ def _get_delta_t2_from_t3v(govvv_x, gvvov_x, gooov_x, govoo_x, frag_child, rxy_o wf = frag_child.results.wf.as_ccsdtq() t3 = wf.t3 - if frag_child.base.spinsym == 'restricted': - + if frag_child.base.spinsym == "restricted": # First term, using (vv|ov): 1/2 P_ab [t_ijmaef v_efbm] # Three-quarter transform of passed-in integrals from parent (x) to child (y) basis # Keep first index of vvov integrals in x basis. Transform rest to y basis. - gvvov_ = einsum('abic,bB,iI,cC -> aBIC', gvvov_x, rxy_vir, rxy_occ, rxy_vir) + gvvov_ = einsum("abic,bB,iI,cC -> aBIC", gvvov_x, rxy_vir, rxy_occ, rxy_vir) # Contract with T3 amplitudes in the y basis - t3v_ = 0.5 * einsum('bemf, jimeaf -> ijab', gvvov_ - gvvov_.transpose(0, 3, 2, 1), t3) - t3v_ += einsum('bemf, ijmaef -> ijab', gvvov_, t3) + t3v_ = 0.5 * einsum("bemf, jimeaf -> ijab", gvvov_ - gvvov_.transpose(0, 3, 2, 1), t3) + t3v_ += einsum("bemf, ijmaef -> ijab", gvvov_, t3) # Final is in a mixed basis form, with last index in t3v here in the x basis # Rotate remaining indices into x basis: another three-quarter transform - t3v_x = einsum('IJAb,iI,jJ,aA -> ijab', t3v_, rxy_occ, rxy_occ, rxy_vir) + t3v_x = einsum("IJAb,iI,jJ,aA -> ijab", t3v_, rxy_occ, rxy_occ, rxy_vir) # Second term: -1/2 P_ij [t_imnabe v_jemn] # ooov three-quarter transform, to IjKA (capital is y (child) basis) - gooov_ = einsum('ijka,iI,kK,aA -> IjKA', gooov_x, rxy_occ, rxy_occ, rxy_vir) + gooov_ = einsum("ijka,iI,kK,aA -> IjKA", gooov_x, rxy_occ, rxy_occ, rxy_vir) # ovoo three-quarter transform, to IAJk (capital is y (child) basis) - govoo_ = einsum('iajk,iI,aA,jJ -> IAJk', govoo_x, rxy_occ, rxy_vir, rxy_occ) + govoo_ = einsum("iajk,iI,aA,jJ -> IAJk", govoo_x, rxy_occ, rxy_vir, rxy_occ) # Second index of t3v_ in the parent (x) basis - t3v_ = -0.5 * einsum('mjne, minbae -> ijab', gooov_ - govoo_.transpose(0, 3, 2, 1), t3) - t3v_ -= einsum('mjne, imnabe -> ijab', gooov_, t3) + t3v_ = -0.5 * einsum("mjne, minbae -> ijab", gooov_ - govoo_.transpose(0, 3, 2, 1), t3) + t3v_ -= einsum("mjne, imnabe -> ijab", gooov_, t3) # Rotate remaining indices into x basis: another three-quarter transform - t3v_x += einsum('IjAB,iI,aA,bB -> ijab', t3v_, rxy_occ, rxy_vir, rxy_vir) + t3v_x += einsum("IjAB,iI,aA,bB -> ijab", t3v_, rxy_occ, rxy_vir, rxy_vir) # Include permutation dt2 = t3v_x + t3v_x.transpose(1, 0, 3, 2) - elif frag_child.base.spinsym == 'unrestricted': + elif frag_child.base.spinsym == "unrestricted": gooov_aaaa, gooov_aabb, gooov_bbbb = gooov_x govoo_aaaa, govoo_aabb, govoo_bbbb = govoo_x govvv_aaaa, govvv_aabb, govvv_bbbb = govvv_x @@ -414,21 +419,21 @@ def _get_delta_t2_from_t3v(govvv_x, gvvov_x, gooov_x, govoo_x, frag_child, rxy_o t3_aaa, t3_aba, t3_bab, t3_bbb = t3 - govvv_aaaa_ = einsum('iabc,iI,aA,cC -> IAbC', govvv_aaaa, rxy_occ[0], rxy_vir[0], rxy_vir[0]) - govvv_aabb_ = einsum('iabc,iI,aA,cC -> IAbC', govvv_aabb, rxy_occ[0], rxy_vir[0], rxy_vir[1]) - govvv_bbbb_ = einsum('iabc,iI,aA,cC -> IAbC', govvv_bbbb, rxy_occ[1], rxy_vir[1], rxy_vir[1]) + govvv_aaaa_ = einsum("iabc,iI,aA,cC -> IAbC", govvv_aaaa, rxy_occ[0], rxy_vir[0], rxy_vir[0]) + govvv_aabb_ = einsum("iabc,iI,aA,cC -> IAbC", govvv_aabb, rxy_occ[0], rxy_vir[0], rxy_vir[1]) + govvv_bbbb_ = einsum("iabc,iI,aA,cC -> IAbC", govvv_bbbb, rxy_occ[1], rxy_vir[1], rxy_vir[1]) - gvvov_aaaa_ = einsum('abic,bB,iI,cC -> aBIC', gvvov_aaaa, rxy_vir[0], rxy_occ[0], rxy_vir[0]) - gvvov_aabb_ = einsum('abic,bB,iI,cC -> aBIC', gvvov_aabb, rxy_vir[0], rxy_occ[1], rxy_vir[1]) - gvvov_bbbb_ = einsum('abic,bB,iI,cC -> aBIC', gvvov_bbbb, rxy_vir[1], rxy_occ[1], rxy_vir[1]) + gvvov_aaaa_ = einsum("abic,bB,iI,cC -> aBIC", gvvov_aaaa, rxy_vir[0], rxy_occ[0], rxy_vir[0]) + gvvov_aabb_ = einsum("abic,bB,iI,cC -> aBIC", gvvov_aabb, rxy_vir[0], rxy_occ[1], rxy_vir[1]) + gvvov_bbbb_ = einsum("abic,bB,iI,cC -> aBIC", gvvov_bbbb, rxy_vir[1], rxy_occ[1], rxy_vir[1]) - gooov_aaaa_ = einsum('ijka,jJ,kK,aA -> iJKA', gooov_aaaa, rxy_occ[0], rxy_occ[0], rxy_vir[0]) - gooov_aabb_ = einsum('ijka,jJ,kK,aA -> iJKA', gooov_aabb, rxy_occ[0], rxy_occ[1], rxy_vir[1]) - gooov_bbbb_ = einsum('ijka,jJ,kK,aA -> iJKA', gooov_bbbb, rxy_occ[1], rxy_occ[1], rxy_vir[1]) + gooov_aaaa_ = einsum("ijka,jJ,kK,aA -> iJKA", gooov_aaaa, rxy_occ[0], rxy_occ[0], rxy_vir[0]) + gooov_aabb_ = einsum("ijka,jJ,kK,aA -> iJKA", gooov_aabb, rxy_occ[0], rxy_occ[1], rxy_vir[1]) + gooov_bbbb_ = einsum("ijka,jJ,kK,aA -> iJKA", gooov_bbbb, rxy_occ[1], rxy_occ[1], rxy_vir[1]) - govoo_aaaa_ = einsum('iajk,iI,aA,kK -> IAjK', govoo_aaaa, rxy_occ[0], rxy_vir[0], rxy_occ[0]) - govoo_aabb_ = einsum('iajk,iI,aA,kK -> IAjK', govoo_aabb, rxy_occ[0], rxy_vir[0], rxy_occ[1]) - govoo_bbbb_ = einsum('iajk,iI,aA,kK -> IAjK', govoo_bbbb, rxy_occ[1], rxy_vir[1], rxy_occ[1]) + govoo_aaaa_ = einsum("iajk,iI,aA,kK -> IAjK", govoo_aaaa, rxy_occ[0], rxy_vir[0], rxy_occ[0]) + govoo_aabb_ = einsum("iajk,iI,aA,kK -> IAjK", govoo_aabb, rxy_occ[0], rxy_vir[0], rxy_occ[1]) + govoo_bbbb_ = einsum("iajk,iI,aA,kK -> IAjK", govoo_bbbb, rxy_occ[1], rxy_vir[1], rxy_occ[1]) x0 = einsum("Jlkc,iklacb->iJab", gooov_aabb_, t3_aba) x1 = einsum("Jlkc,iklabc->iJab", gooov_aaaa_, t3_aaa) * -1.0 @@ -545,10 +550,10 @@ def externally_correct(solver, external_corrections, hamil=None): # eris=None): cxs_occ = spinalg.dot(spinalg.T(cx_occ), ovlp) cxs_vir = spinalg.dot(spinalg.T(cx_vir), ovlp) if hamil is None: - # Note that if no MO energies are passed in, we construct them from the + # Note that if no MO energies are passed in, we construct them from the # get_fock function without with_exxdiv=False. For PBC CCSD, this may be different # behaviour. - mo_energy = einsum('ai,ab,bi->i', cluster.c_active, emb.get_fock(), cluster.c_active) + mo_energy = einsum("ai,ab,bi->i", cluster.c_active, emb.get_fock(), cluster.c_active) else: mo_energy = hamil.get_clus_mf_info(with_exxdiv=False)[2] # Do we want this True or False? @@ -563,7 +568,7 @@ def externally_correct(solver, external_corrections, hamil=None): # eris=None): # CCSD uses exxdiv-uncorrected Fock matrix for residuals fock = emb.get_fock(with_exxdiv=False) - if any([corr[3] and corr[1] == 'external' for corr in external_corrections]): + if any([corr[3] and corr[1] == "external" for corr in external_corrections]): # At least one fragment is externally corrected, *and* contracted with # integrals in the parent (i.e. CCSD) cluster. We can take the integrals from eris # if passed in. Otherwise, form the required integrals @@ -573,7 +578,7 @@ def externally_correct(solver, external_corrections, hamil=None): # eris=None): else: eri_generator = hamil.get_eris_bare - if emb.spinsym == 'restricted': + if emb.spinsym == "restricted": govvv_x = None gvvov_x = eri_generator("vvov") gooov_x = eri_generator("ooov") @@ -585,28 +590,28 @@ def externally_correct(solver, external_corrections, hamil=None): # eris=None): govoo_x = (eri_generator("ovoo"), eri_generator("ovOO"), eri_generator("OVOO")) # delta-T1 and delta-T2 amplitudes, to be added to the CCSD amplitudes - if emb.spinsym == 'restricted': + if emb.spinsym == "restricted": dt1 = np.zeros((nocc, nvir)) dt2 = np.zeros((nocc, nocc, nvir, nvir)) - elif emb.spinsym == 'unrestricted': - dt1 = (np.zeros((nocc[0], nvir[0])), - np.zeros((nocc[1], nvir[1]))) - dt2 = (np.zeros((nocc[0], nocc[0], nvir[0], nvir[0])), - np.zeros((nocc[0], nocc[1], nvir[0], nvir[1])), - np.zeros((nocc[1], nocc[1], nvir[1], nvir[1]))) + elif emb.spinsym == "unrestricted": + dt1 = (np.zeros((nocc[0], nvir[0])), np.zeros((nocc[1], nvir[1]))) + dt2 = ( + np.zeros((nocc[0], nocc[0], nvir[0], nvir[0])), + np.zeros((nocc[0], nocc[1], nvir[0], nvir[1])), + np.zeros((nocc[1], nocc[1], nvir[1], nvir[1])), + ) frag_dir = {f.id: f for f in emb.fragments} for y, corrtype, projectors, low_level_coul in external_corrections: - fy = frag_dir[y] # Get fragment y object from its index - assert (y != fx.id) + assert y != fx.id - if corrtype == 'external': + if corrtype == "external": if low_level_coul: dt1y, dt2y = _get_delta_t_for_extcorr(fy, fock, solver, include_t3v=False) else: dt1y, dt2y = _get_delta_t_for_extcorr(fy, fock, solver, include_t3v=True) - elif corrtype == 'delta-tailor': + elif corrtype == "delta-tailor": dt1y, dt2y = _get_delta_t_for_delta_tailor(fy) else: raise ValueError @@ -615,7 +620,7 @@ def externally_correct(solver, external_corrections, hamil=None): # eris=None): if projectors: # projectors is an integer giving the number of projectors onto # occupied fragment - proj = fy.get_overlap('frag|cluster-occ') + proj = fy.get_overlap("frag|cluster-occ") proj = spinalg.dot(spinalg.T(proj), proj) dt1y = spinalg.dot(proj, dt1y) dt2y = project_t2(dt2y, proj, projectors=projectors) @@ -628,54 +633,62 @@ def externally_correct(solver, external_corrections, hamil=None): # eris=None): dt1 = spinalg.add(dt1, dt1y) dt2 = spinalg.add(dt2, dt2y) - if low_level_coul and corrtype == 'external': + if low_level_coul and corrtype == "external": # Include the t3v term, contracting with the integrals from the x cluster # These have already been fragment projected, and rotated into the x cluster # in this function. - dt2y_t3v = _get_delta_t2_from_t3v(govvv_x, gvvov_x, gooov_x, govoo_x, fy, - rxy_occ, rxy_vir, cxs_occ, projectors) + dt2y_t3v = _get_delta_t2_from_t3v( + govvv_x, gvvov_x, gooov_x, govoo_x, fy, rxy_occ, rxy_vir, cxs_occ, projectors + ) dt2 = spinalg.add(dt2, dt2y_t3v) - solver.log.info("External correction residuals from fragment %3d (%s via %s): dT1= %.3e dT2= %.3e", - fy.id, fy.solver, corrtype, *get_amplitude_norm(dt1y, dt2y)) + solver.log.info( + "External correction residuals from fragment %3d (%s via %s): dT1= %.3e dT2= %.3e", + fy.id, + fy.solver, + corrtype, + *get_amplitude_norm(dt1y, dt2y), + ) - if emb.spinsym == 'restricted': - - if corrtype == 'external': + if emb.spinsym == "restricted": + if corrtype == "external": # Contract with fragment x (CCSD) energy denominators # Note that this will not work correctly if a level shift used eia = mo_energy[:nocc, None] - mo_energy[None, nocc:] - eijab = pyscf.lib.direct_sum('ia,jb->ijab', eia, eia) + eijab = pyscf.lib.direct_sum("ia,jb->ijab", eia, eia) dt1 /= eia dt2 /= eijab - solver.log.info("Total external correction amplitudes from all fragments: dT1= %.3e dT2= %.3e", - *get_amplitude_norm(dt1, dt2)) + solver.log.info( + "Total external correction amplitudes from all fragments: dT1= %.3e dT2= %.3e", + *get_amplitude_norm(dt1, dt2), + ) def callback(kwargs): """Add external correction to T1 and T2 amplitudes.""" - t1, t2 = kwargs['t1new'], kwargs['t2new'] + t1, t2 = kwargs["t1new"], kwargs["t2new"] t1[:] += dt1 t2[:] += dt2 - elif emb.spinsym == 'unrestricted': - + elif emb.spinsym == "unrestricted": if corrtype == "external": - eia_a = mo_energy[0][:nocc[0], None] - mo_energy[0][None, nocc[0]:] - eia_b = mo_energy[1][:nocc[1], None] - mo_energy[1][None, nocc[1]:] - eijab_aa = pyscf.lib.direct_sum('ia,jb->ijab', eia_a, eia_a) - eijab_ab = pyscf.lib.direct_sum('ia,jb->ijab', eia_a, eia_b) - eijab_bb = pyscf.lib.direct_sum('ia,jb->ijab', eia_b, eia_b) + eia_a = mo_energy[0][: nocc[0], None] - mo_energy[0][None, nocc[0] :] + eia_b = mo_energy[1][: nocc[1], None] - mo_energy[1][None, nocc[1] :] + eijab_aa = pyscf.lib.direct_sum("ia,jb->ijab", eia_a, eia_a) + eijab_ab = pyscf.lib.direct_sum("ia,jb->ijab", eia_a, eia_b) + eijab_bb = pyscf.lib.direct_sum("ia,jb->ijab", eia_b, eia_b) dt1 = (dt1[0] / eia_a, dt1[1] / eia_b) dt2 = (dt2[0] / eijab_aa, dt2[1] / eijab_ab, dt2[2] / eijab_bb) - solver.log.info("Total external correction amplitudes from all fragments: dT1= %.3e dT2= %.3e", \ - *get_amplitude_norm(dt1, dt2)) + solver.log.info( + "Total external correction amplitudes from all fragments: dT1= %.3e dT2= %.3e", + *get_amplitude_norm(dt1, dt2), + ) def callback(kwargs): """Add external correction to T1 and T2 amplitudes.""" - t1, t2 = kwargs['t1new'], kwargs['t2new'] + t1, t2 = kwargs["t1new"], kwargs["t2new"] t1[0][:] += dt1[0] t1[1][:] += dt1[1] diff --git a/vayesta/solver/dump.py b/vayesta/solver/dump.py index 41d1238d1..97424ee19 100644 --- a/vayesta/solver/dump.py +++ b/vayesta/solver/dump.py @@ -16,54 +16,54 @@ def kernel(self, *args, **kwargs): cluster = self.hamil.cluster if isinstance(self.opts.dumpfile, str): - h5file = h5py.File(self.opts.dumpfile, 'a') + h5file = h5py.File(self.opts.dumpfile, "a") else: h5file = self.opts.dumpfile with h5file as f: - grp = h5file.create_group('fragment_%d' % fragment.id) + grp = h5file.create_group("fragment_%d" % fragment.id) # Attributes - grp.attrs['id'] = fragment.id - grp.attrs['name'] = fragment.name - grp.attrs['norb'] = cluster.norb_active - grp.attrs['nocc'] = cluster.nocc_active - grp.attrs['nvir'] = cluster.nvir_active + grp.attrs["id"] = fragment.id + grp.attrs["name"] = fragment.name + grp.attrs["norb"] = cluster.norb_active + grp.attrs["nocc"] = cluster.nocc_active + grp.attrs["nvir"] = cluster.nvir_active c_dmet_cluster_occ = fragment._dmet_bath.c_cluster_occ c_dmet_cluster_vir = fragment._dmet_bath.c_cluster_vir c_dmet_cluster = spinalg.hstack_matrices(c_dmet_cluster_occ, c_dmet_cluster_vir) heff, eris = self.hamil.get_integrals(with_vext=True) if c_dmet_cluster[0].ndim == 1: - grp.attrs['norb_dmet_cluster'] = c_dmet_cluster.shape[-1] - grp.attrs['nocc_dmet_cluster'] = c_dmet_cluster_occ.shape[-1] - grp.attrs['nvir_dmet_cluster'] = c_dmet_cluster_vir.shape[-1] + grp.attrs["norb_dmet_cluster"] = c_dmet_cluster.shape[-1] + grp.attrs["nocc_dmet_cluster"] = c_dmet_cluster_occ.shape[-1] + grp.attrs["nvir_dmet_cluster"] = c_dmet_cluster_vir.shape[-1] # Orbital coefficients - grp.create_dataset('c_frag', data=fragment.c_frag) - grp.create_dataset('c_dmet_cluster', data=c_dmet_cluster) - grp.create_dataset('c_cluster', data=cluster.c_active) + grp.create_dataset("c_frag", data=fragment.c_frag) + grp.create_dataset("c_dmet_cluster", data=c_dmet_cluster) + grp.create_dataset("c_cluster", data=cluster.c_active) # Integrals - grp.create_dataset('heff', data=heff) - grp.create_dataset('fock', data=self.hamil.get_fock()) - grp.create_dataset('eris', data=eris) + grp.create_dataset("heff", data=heff) + grp.create_dataset("fock", data=self.hamil.get_fock()) + grp.create_dataset("eris", data=eris) elif c_dmet_cluster[0].ndim == 2: - grp.attrs['norb_dmet_cluster'] = [c_dmet_cluster[s].shape[-1] for s in range(2)] - grp.attrs['nocc_dmet_cluster'] = [c_dmet_cluster_occ[s].shape[-1] for s in range(2)] - grp.attrs['nvir_dmet_cluster'] = [c_dmet_cluster_vir[s].shape[-1] for s in range(2)] + grp.attrs["norb_dmet_cluster"] = [c_dmet_cluster[s].shape[-1] for s in range(2)] + grp.attrs["nocc_dmet_cluster"] = [c_dmet_cluster_occ[s].shape[-1] for s in range(2)] + grp.attrs["nvir_dmet_cluster"] = [c_dmet_cluster_vir[s].shape[-1] for s in range(2)] # Orbital coefficients - grp.create_dataset('c_frag_a', data=fragment.c_frag[0]) - grp.create_dataset('c_frag_b', data=fragment.c_frag[1]) - grp.create_dataset('c_dmet_cluster_a', data=c_dmet_cluster[0]) - grp.create_dataset('c_dmet_cluster_b', data=c_dmet_cluster[1]) - grp.create_dataset('c_cluster_a', data=cluster.c_active[0]) - grp.create_dataset('c_cluster_b', data=cluster.c_active[1]) + grp.create_dataset("c_frag_a", data=fragment.c_frag[0]) + grp.create_dataset("c_frag_b", data=fragment.c_frag[1]) + grp.create_dataset("c_dmet_cluster_a", data=c_dmet_cluster[0]) + grp.create_dataset("c_dmet_cluster_b", data=c_dmet_cluster[1]) + grp.create_dataset("c_cluster_a", data=cluster.c_active[0]) + grp.create_dataset("c_cluster_b", data=cluster.c_active[1]) # Integrals heffa, heffb = heff - grp.create_dataset('heff_a', data=heffa) - grp.create_dataset('heff_b', data=heffb) + grp.create_dataset("heff_a", data=heffa) + grp.create_dataset("heff_b", data=heffb) focka, fockb = self.hamil.get_fock() - grp.create_dataset('fock_a', data=focka) - grp.create_dataset('fock_b', data=fockb) + grp.create_dataset("fock_a", data=focka) + grp.create_dataset("fock_b", data=fockb) erisaa, erisab, erisbb = eris - grp.create_dataset('eris_aa', data=erisaa) - grp.create_dataset('eris_ab', data=erisab) - grp.create_dataset('eris_bb', data=erisbb) + grp.create_dataset("eris_aa", data=erisaa) + grp.create_dataset("eris_ab", data=erisab) + grp.create_dataset("eris_bb", data=erisbb) else: raise NotImplementedError diff --git a/vayesta/solver/eb_fci/ebfci.py b/vayesta/solver/eb_fci/ebfci.py index 37a1bb5ac..55b87288e 100644 --- a/vayesta/solver/eb_fci/ebfci.py +++ b/vayesta/solver/eb_fci/ebfci.py @@ -14,6 +14,7 @@ class REBFCI: -FCI RDMs. -FCI response specification. """ + solver = ebfci_slow @dataclasses.dataclass @@ -45,9 +46,18 @@ def kernel(self, eris=None): h1e, eris = self.get_hamil(eris) couplings = self.hamil.couplings bos_freqs = self.hamil.bos_freqs - self.e_fci, self.civec = self.solver.kernel(h1e, eris, couplings, np.diag(bos_freqs), self.norb, self.nelec, - self.nbos, max_occ=self.opts.max_boson_occ, tol=self.opts.conv_tol, - max_cycle=self.opts.max_cycle) + self.e_fci, self.civec = self.solver.kernel( + h1e, + eris, + couplings, + np.diag(bos_freqs), + self.norb, + self.nelec, + self.nbos, + max_occ=self.opts.max_boson_occ, + tol=self.opts.conv_tol, + max_cycle=self.opts.max_cycle, + ) return self.e_fci, self.civec def get_hamil(self, eris=None): @@ -78,9 +88,20 @@ def make_dd_moms(self, max_mom, dm1=None, coeffs=None, civec=None, eris=None): dm1 = self.make_rdm1() self.dd_moms = self.solver.calc_dd_resp_mom( - civec, self.e_fci, max_mom, self.norb, self.nelec, self.nbos, h1e, eris, - np.diag(self.freqs), self.couplings, self.opts.max_boson_occ, dm1, - coeffs=coeffs) + civec, + self.e_fci, + max_mom, + self.norb, + self.nelec, + self.nbos, + h1e, + eris, + np.diag(self.freqs), + self.couplings, + self.opts.max_boson_occ, + dm1, + coeffs=coeffs, + ) return self.dd_moms diff --git a/vayesta/solver/eb_fci/ebfci_slow.py b/vayesta/solver/eb_fci/ebfci_slow.py index 16d97470d..33362f6cd 100644 --- a/vayesta/solver/eb_fci/ebfci_slow.py +++ b/vayesta/solver/eb_fci/ebfci_slow.py @@ -12,15 +12,13 @@ from pyscf.fci.direct_spin1 import _unpack_nelec -def contract_all(h1e, g2e, hep, hpp, ci0, norbs, nelec, nbosons, max_occ, - ecore=0.0, adj_zero_pho=False): +def contract_all(h1e, g2e, hep, hpp, ci0, norbs, nelec, nbosons, max_occ, ecore=0.0, adj_zero_pho=False): # ci1 = contract_1e(h1e, ci0, norbs, nelec, nbosons, max_occ) contrib1 = contract_2e(g2e, ci0, norbs, nelec, nbosons, max_occ) - incbosons = (nbosons > 0 and max_occ > 0) + incbosons = nbosons > 0 and max_occ > 0 if incbosons: - contrib2 = contract_ep(hep, ci0, norbs, nelec, nbosons, max_occ, - adj_zero_pho=adj_zero_pho) + contrib2 = contract_ep(hep, ci0, norbs, nelec, nbosons, max_occ, adj_zero_pho=adj_zero_pho) contrib3 = contract_pp(hpp, ci0, norbs, nelec, nbosons, max_occ) cishape = make_shape(norbs, nelec, nbosons, max_occ) @@ -38,8 +36,7 @@ def contract_all(h1e, g2e, hep, hpp, ci0, norbs, nelec, nbosons, max_occ, def make_shape(norbs, nelec, nbosons, max_occ): - """Construct the shape of a single FCI vector in the coupled electron-boson space. - """ + """Construct the shape of a single FCI vector in the coupled electron-boson space.""" neleca, nelecb = _unpack_nelec(nelec) na = cistring.num_strings(norbs, neleca) nb = cistring.num_strings(norbs, nelecb) @@ -49,9 +46,11 @@ def make_shape(norbs, nelec, nbosons, max_occ): # Contract 1-electron integrals with fcivec. def contract_1e(h1e, fcivec, norb, nelec, nbosons, max_occ): - raise NotImplementedError("1 electron contraction is currently" - "bugged for coupled electron-boson systems." - "This should instead be folded into a two-body operator.") + raise NotImplementedError( + "1 electron contraction is currently" + "bugged for coupled electron-boson systems." + "This should instead be folded into a two-body operator." + ) if isinstance(nelec, (int, numpy.integer)): nelecb = nelec // 2 neleca = nelec - nelecb @@ -181,9 +180,9 @@ def contract_ep(heb, fcivec, norb, nelec, nbosons, max_occ, adj_zero_pho=False): # Contract phonon-phonon portion of the Hamiltonian. + def contract_pp(hpp, fcivec, norb, nelec, nbosons, max_occ): - """Arbitrary phonon-phonon coupling. - """ + """Arbitrary phonon-phonon coupling.""" cishape = make_shape(norb, nelec, nbosons, max_occ) ci0 = fcivec.reshape(cishape) fcinew = numpy.zeros_like(ci0) @@ -279,15 +278,20 @@ def make_hdiag(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ): cishape = make_shape(norb, nelec, nbosons, max_occ) hdiag = numpy.zeros(cishape) g2e = ao2mo.restore(1, g2e, norb) - diagj = numpy.einsum('iijj->ij', g2e) - diagk = numpy.einsum('ijji->ij', g2e) + diagj = numpy.einsum("iijj->ij", g2e) + diagk = numpy.einsum("ijji->ij", g2e) for ia, aocc in enumerate(occslista): for ib, bocc in enumerate(occslistb): e1 = h1e[aocc, aocc].sum() + h1e[bocc, bocc].sum() - e2 = diagj[aocc][:, aocc].sum() + diagj[aocc][:, bocc].sum() \ - + diagj[bocc][:, aocc].sum() + diagj[bocc][:, bocc].sum() \ - - diagk[aocc][:, aocc].sum() - diagk[bocc][:, bocc].sum() - hdiag[ia, ib] += e1 + e2 * .5 + e2 = ( + diagj[aocc][:, aocc].sum() + + diagj[aocc][:, bocc].sum() + + diagj[bocc][:, aocc].sum() + + diagj[bocc][:, bocc].sum() + - diagk[aocc][:, aocc].sum() + - diagk[bocc][:, bocc].sum() + ) + hdiag[ia, ib] += e1 + e2 * 0.5 # No electron-phonon part? @@ -302,11 +306,24 @@ def make_hdiag(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ): return hdiag.ravel() -def kernel(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ, - tol=1e-12, max_cycle=100, verbose=0, ecore=0, - returnhop=False, adj_zero_pho=False, - **kwargs): - h2e = fci_slow.absorb_h1e(h1e, g2e, norb, nelec, .5) +def kernel( + h1e, + g2e, + hep, + hpp, + norb, + nelec, + nbosons, + max_occ, + tol=1e-12, + max_cycle=100, + verbose=0, + ecore=0, + returnhop=False, + adj_zero_pho=False, + **kwargs, +): + h2e = fci_slow.absorb_h1e(h1e, g2e, norb, nelec, 0.5) cishape = make_shape(norb, nelec, nbosons, max_occ) ci0 = numpy.zeros(cishape) @@ -315,8 +332,7 @@ def kernel(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ, ci0.__setitem__((0, 0) + (0,) * nbosons, 1) def hop(c): - hc = contract_all(h1e, h2e, hep, hpp, c, norb, - nelec, nbosons, max_occ, adj_zero_pho=adj_zero_pho) + hc = contract_all(h1e, h2e, hep, hpp, c, norb, nelec, nbosons, max_occ, adj_zero_pho=adj_zero_pho) return hc.reshape(-1) hdiag = make_hdiag(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ) @@ -324,21 +340,47 @@ def hop(c): if returnhop: return hop, ci0, hdiag - e, c = lib.davidson(hop, ci0.reshape(-1), precond, - tol=tol, max_cycle=max_cycle, verbose=verbose, - **kwargs) + e, c = lib.davidson(hop, ci0.reshape(-1), precond, tol=tol, max_cycle=max_cycle, verbose=verbose, **kwargs) return e + ecore, c.reshape(cishape) -def kernel_multiroot(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ, - tol=1e-12, max_cycle=100, verbose=0, ecore=0, nroots=2, - returnhop=False, adj_zero_pho=False, - **kwargs): +def kernel_multiroot( + h1e, + g2e, + hep, + hpp, + norb, + nelec, + nbosons, + max_occ, + tol=1e-12, + max_cycle=100, + verbose=0, + ecore=0, + nroots=2, + returnhop=False, + adj_zero_pho=False, + **kwargs, +): if nroots == 1: - return kernel(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ, - tol, max_cycle, verbose, ecore, returnhop, adj_zero_pho, - **kwargs) - h2e = fci_slow.absorb_h1e(h1e, g2e, norb, nelec, .5) + return kernel( + h1e, + g2e, + hep, + hpp, + norb, + nelec, + nbosons, + max_occ, + tol, + max_cycle, + verbose, + ecore, + returnhop, + adj_zero_pho, + **kwargs, + ) + h2e = fci_slow.absorb_h1e(h1e, g2e, norb, nelec, 0.5) cishape = make_shape(norb, nelec, nbosons, max_occ) ci0 = numpy.zeros(cishape) @@ -348,8 +390,7 @@ def kernel_multiroot(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ, ci0[:, 0] += numpy.random.random(ci0[:, 0].shape) * 1e-6 def hop(c): - hc = contract_all(h1e, h2e, hep, hpp, c, norb, - nelec, nbosons, max_occ, adj_zero_pho=adj_zero_pho) + hc = contract_all(h1e, h2e, hep, hpp, c, norb, nelec, nbosons, max_occ, adj_zero_pho=adj_zero_pho) return hc.reshape(-1) hdiag = make_hdiag(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ) @@ -357,15 +398,23 @@ def hop(c): if returnhop: return hop, ci0, hdiag - es, cs = lib.davidson(hop, ci0.reshape(-1), precond, - tol=tol, max_cycle=max_cycle, verbose=verbose, nroots=nroots, max_space=20, - **kwargs) + es, cs = lib.davidson( + hop, + ci0.reshape(-1), + precond, + tol=tol, + max_cycle=max_cycle, + verbose=verbose, + nroots=nroots, + max_space=20, + **kwargs, + ) return es, cs # dm_pq = <|p^+ q|> def make_rdm1(fcivec, norb, nelec): - '''1-electron density matrix dm_pq = <|p^+ q|>''' + """1-electron density matrix dm_pq = <|p^+ q|>""" neleca, nelecb = _unpack_nelec(nelec) link_indexa = cistring.gen_linkstr_index(range(norb), neleca) link_indexb = cistring.gen_linkstr_index(range(norb), nelecb) @@ -381,15 +430,15 @@ def make_rdm1(fcivec, norb, nelec): ci0 = fcivec.reshape(na, nb, -1) for str0, tab in enumerate(link_indexb): for a, i, str1, sign in tab: - rdm1[a, i] += sign * numpy.einsum('ax,ax->', ci0[:, str1], ci0[:, str0]) + rdm1[a, i] += sign * numpy.einsum("ax,ax->", ci0[:, str1], ci0[:, str0]) return rdm1 def make_rdm12(fcivec, norb, nelec): - '''1-electron and 2-electron density matrices + """1-electron and 2-electron density matrices dm_qp = <|q^+ p|> dm_{pqrs} = <|p^+ r^+ s q|> - ''' + """ neleca, nelecb = _unpack_nelec(nelec) link_indexa = cistring.gen_linkstr_index(range(norb), neleca) link_indexb = cistring.gen_linkstr_index(range(norb), nelecb) @@ -409,20 +458,20 @@ def make_rdm12(fcivec, norb, nelec): for a, i, str1, sign in tab: t1[i, a, k] += sign * ci0[str0, str1] - rdm1 += numpy.einsum('mp,ijmp->ij', ci0[str0], t1) + rdm1 += numpy.einsum("mp,ijmp->ij", ci0[str0], t1) # i^+ j|0> => <0|j^+ i, so swap i and j #:rdm2 += numpy.einsum('ijmp,klmp->jikl', t1, t1) - tmp = lib.dot(t1.reshape(norb ** 2, -1), t1.reshape(norb ** 2, -1).T) + tmp = lib.dot(t1.reshape(norb**2, -1), t1.reshape(norb**2, -1).T) rdm2 += tmp.reshape((norb,) * 4).transpose(1, 0, 2, 3) rdm1, rdm2 = rdm.reorder_rdm(rdm1, rdm2, True) return rdm1, rdm2 def make_rdm12s(fcivec, norb, nelec): - '''1-electron and 2-electron spin-resolved density matrices + """1-electron and 2-electron spin-resolved density matrices dm_qp = <|q^+ p|> dm_{pqrs} = <|p^+ r^+ s q|> - ''' + """ neleca, nelecb = _unpack_nelec(nelec) link_indexa = cistring.gen_linkstr_index(range(norb), neleca) link_indexb = cistring.gen_linkstr_index(range(norb), nelecb) @@ -454,7 +503,7 @@ def make_rdm12s(fcivec, norb, nelec): # i^+ j|0> => <0|j^+ i, so swap i and j #:rdm2 += numpy.einsum('ijmp,klmp->jikl', t1, t1) # Calc <0|i^+ a b^+ j |0> - tmp = lib.dot(t1.reshape(nspinorb ** 2, -1), t1.reshape(nspinorb ** 2, -1).T) + tmp = lib.dot(t1.reshape(nspinorb**2, -1), t1.reshape(nspinorb**2, -1).T) rdm2 += tmp.reshape((nspinorb,) * 4).transpose(1, 0, 2, 3) # Need to fill in components where have two single excitations which are # spin disallowed, but in combination excitations conserve spin. @@ -466,10 +515,12 @@ def make_rdm12s(fcivec, norb, nelec): # - rdm2[1::2,1::2,::2,::2].transpose([2,1,0,3]) # + numpy.tensordot(rdm1[::2,::2], numpy.identity(norb), 0).transpose([0,2,3,1]) # ) - rdm2[::2, 1::2, 1::2, ::2] = -numpy.einsum("pqrs->rqps", rdm2[1::2, 1::2, ::2, ::2]) + \ - numpy.einsum("pq,rs->prsq", rdm1[::2, ::2], numpy.identity(norb)) - rdm2[1::2, ::2, ::2, 1::2] = -numpy.einsum("pqrs->rqps", rdm2[::2, ::2, 1::2, 1::2]) + \ - numpy.einsum("pq,rs->prsq", rdm1[1::2, 1::2], numpy.identity(norb)) + rdm2[::2, 1::2, 1::2, ::2] = -numpy.einsum("pqrs->rqps", rdm2[1::2, 1::2, ::2, ::2]) + numpy.einsum( + "pq,rs->prsq", rdm1[::2, ::2], numpy.identity(norb) + ) + rdm2[1::2, ::2, ::2, 1::2] = -numpy.einsum("pqrs->rqps", rdm2[::2, ::2, 1::2, 1::2]) + numpy.einsum( + "pq,rs->prsq", rdm1[1::2, 1::2], numpy.identity(norb) + ) save = rdm2.copy() # This should hopefully work with spinorbitals. rdm1, rdm2 = rdm.reorder_rdm(rdm1, rdm2, True) @@ -519,14 +570,14 @@ def make_eb_rdm(fcivec, norb, nelec, nbosons, max_occ): norm_slice = (slice(None, None, None),) * 2 + slices_for(ibos, nbosons, iocc) tempa[ex_slice] += t1a[norm_slice] * bos_cre[iocc] tempb[ex_slice] += t1b[norm_slice] * bos_cre[iocc] - rdm_fba = numpy.dot(tempa.reshape((norb ** 2 * nbosons, -1)), ci0.reshape(-1)).reshape((norb, norb, nbosons)) - rdm_fbb = numpy.dot(tempb.reshape((norb ** 2 * nbosons, -1)), ci0.reshape(-1)).reshape((norb, norb, nbosons)) + rdm_fba = numpy.dot(tempa.reshape((norb**2 * nbosons, -1)), ci0.reshape(-1)).reshape((norb, norb, nbosons)) + rdm_fbb = numpy.dot(tempb.reshape((norb**2 * nbosons, -1)), ci0.reshape(-1)).reshape((norb, norb, nbosons)) return rdm_fba, rdm_fbb -def calc_dd_resp_mom(ci0, e0, max_mom, norb, nel, nbos, h1e, eri, hbb, heb, max_boson_occ, rdm1, - trace=False, - coeffs=None, **kwargs): +def calc_dd_resp_mom( + ci0, e0, max_mom, norb, nel, nbos, h1e, eri, hbb, heb, max_boson_occ, rdm1, trace=False, coeffs=None, **kwargs +): """ Calculate up to the mth moment of the dd response, dealing with all spin components separately. To replace preceding function. @@ -582,20 +633,20 @@ def calc_dd_resp_mom(ci0, e0, max_mom, norb, nel, nbos, h1e, eri, hbb, heb, max_ bintermeds[iinter + 1] = numpy.zeros_like(t1b) for i in range(na): if trace: - aintermeds[iinter + 1][i] = hop(aintermeds[iinter][i]).reshape(-1) - \ - e0 * aintermeds[iinter][i] + aintermeds[iinter + 1][i] = hop(aintermeds[iinter][i]).reshape(-1) - e0 * aintermeds[iinter][i] else: for a in range(na): - aintermeds[iinter + 1][a, i] = hop(aintermeds[iinter][a, i]).reshape(-1) - \ - e0 * aintermeds[iinter][a, i] + aintermeds[iinter + 1][a, i] = ( + hop(aintermeds[iinter][a, i]).reshape(-1) - e0 * aintermeds[iinter][a, i] + ) for i in range(nb): if trace: - bintermeds[iinter + 1][i] = hop(bintermeds[iinter][i]).reshape(-1) - \ - e0 * bintermeds[iinter][i] + bintermeds[iinter + 1][i] = hop(bintermeds[iinter][i]).reshape(-1) - e0 * bintermeds[iinter][i] else: for a in range(nb): - bintermeds[iinter + 1][a, i] = hop(bintermeds[iinter][a, i]).reshape(-1) - \ - e0 * bintermeds[iinter][a, i] + bintermeds[iinter + 1][a, i] = ( + hop(bintermeds[iinter][a, i]).reshape(-1) - e0 * bintermeds[iinter][a, i] + ) # Need to adjust zeroth moment to remove ground state contributions; in all higher moments this is achieved by # deducting the reference energy. @@ -614,13 +665,13 @@ def calc_dd_resp_mom(ci0, e0, max_mom, norb, nel, nbos, h1e, eri, hbb, heb, max_ moments[imom] = ( numpy.dot(aintermed_l, aintermed_r.T), numpy.dot(aintermed_l, bintermed_r.T), - numpy.dot(bintermed_l, bintermed_r.T) + numpy.dot(bintermed_l, bintermed_r.T), ) else: moments[imom] = ( numpy.tensordot(aintermed_l, aintermed_r, (2, 2)), numpy.tensordot(aintermed_l, bintermed_r, (2, 2)), - numpy.tensordot(bintermed_l, bintermed_r, (2, 2)) + numpy.tensordot(bintermed_l, bintermed_r, (2, 2)), ) # Need to add additional adjustment for zeroth moment, as there is a nonzero ground state # contribution in this case (the current value is in fact the double occupancy <0|n_{pq} n_{sr}|0>). @@ -646,36 +697,30 @@ def calc_dd_resp_mom(ci0, e0, max_mom, norb, nel, nbos, h1e, eri, hbb, heb, max_ def run(nelec, h1e, eri, hpp, hep, max_occ, returnhop=False, nroots=1, **kwargs): - """run a calculation using a pyscf mf object. - """ + """run a calculation using a pyscf mf object.""" norb = h1e.shape[1] nbosons = hpp.shape[0] if returnhop: - hop0 = kernel(h1e, eri, hep, hpp, norb, nelec, nbosons, max_occ, - returnhop=True, **kwargs) + hop0 = kernel(h1e, eri, hep, hpp, norb, nelec, nbosons, max_occ, returnhop=True, **kwargs) # hop1 = fci_slow.kernel(h1e, eri, norb, nelec)#, returnhop=True) return hop0 # , hop1 if nroots > 1: - es, cs = kernel_multiroot(h1e, eri, hep, hpp, norb, nelec, nbosons, max_occ, nroots=nroots, - **kwargs) + es, cs = kernel_multiroot(h1e, eri, hep, hpp, norb, nelec, nbosons, max_occ, nroots=nroots, **kwargs) return es, cs else: - res0 = kernel(h1e, eri, hep, hpp, norb, nelec, nbosons, max_occ, - **kwargs) + res0 = kernel(h1e, eri, hep, hpp, norb, nelec, nbosons, max_occ, **kwargs) return res0 def run_hub_test(returnhop=False, **kwargs): - return run_ep_hubbard(t=1.0, u=1.5, g=0.5, pp=0.1, nsite=2, nelec=2, nphonon=3, - returnhop=returnhop) + return run_ep_hubbard(t=1.0, u=1.5, g=0.5, pp=0.1, nsite=2, nelec=2, nphonon=3, returnhop=returnhop) def run_ep_hubbard(t, u, g, pp, nsite, nelec, nphonon, returnhop=False, **kwargs): - """Run a calculation using a hubbard model coupled to some phonon modes. - """ + """Run a calculation using a hubbard model coupled to some phonon modes.""" idx = numpy.arange(nsite - 1) # 1 electron interactions. h1e = numpy.zeros((nsite, nsite)) @@ -692,6 +737,5 @@ def run_ep_hubbard(t, u, g, pp, nsite, nelec, nphonon, returnhop=False, **kwargs # Only have onsite coupling; so only couple . for i in range(nsite): hep[i, i, i] = g - res0 = kernel(h1e, eri, hep, hpp, nsite, nelec, nsite, nphonon, - adj_zero_pho=False, returnhop=returnhop, **kwargs) + res0 = kernel(h1e, eri, hep, hpp, nsite, nelec, nsite, nphonon, adj_zero_pho=False, returnhop=returnhop, **kwargs) return res0 diff --git a/vayesta/solver/eb_fci/uebfci_slow.py b/vayesta/solver/eb_fci/uebfci_slow.py index d515efde1..05936d565 100644 --- a/vayesta/solver/eb_fci/uebfci_slow.py +++ b/vayesta/solver/eb_fci/uebfci_slow.py @@ -12,15 +12,13 @@ from pyscf.fci.direct_spin1 import _unpack_nelec -def contract_all(h1e, g2e, hep, hpp, ci0, norbs, nelec, nbosons, max_occ, - ecore=0.0, adj_zero_pho=False): +def contract_all(h1e, g2e, hep, hpp, ci0, norbs, nelec, nbosons, max_occ, ecore=0.0, adj_zero_pho=False): # ci1 = contract_1e(h1e, ci0, norbs, nelec, nbosons, max_occ) contrib1 = contract_2e(g2e, ci0, norbs, nelec, nbosons, max_occ) - incbosons = (nbosons > 0 and max_occ > 0) + incbosons = nbosons > 0 and max_occ > 0 if incbosons: - contrib2 = contract_ep(hep, ci0, norbs, nelec, nbosons, max_occ, - adj_zero_pho=adj_zero_pho) + contrib2 = contract_ep(hep, ci0, norbs, nelec, nbosons, max_occ, adj_zero_pho=adj_zero_pho) contrib3 = contract_pp(hpp, ci0, norbs, nelec, nbosons, max_occ) return contrib1 + contrib2 + contrib3 @@ -29,8 +27,7 @@ def contract_all(h1e, g2e, hep, hpp, ci0, norbs, nelec, nbosons, max_occ, def make_shape(norbs, nelec, nbosons, max_occ): - """Construct the shape of a single FCI vector in the coupled electron-boson space. - """ + """Construct the shape of a single FCI vector in the coupled electron-boson space.""" neleca, nelecb = _unpack_nelec(nelec) na = cistring.num_strings(norbs, neleca) nb = cistring.num_strings(norbs, nelecb) @@ -40,9 +37,11 @@ def make_shape(norbs, nelec, nbosons, max_occ): # Contract 1-electron integrals with fcivec. def contract_1e(h1e, fcivec, norb, nelec, nbosons, max_occ): - raise NotImplementedError("1 electron contraction is currently" - "bugged for coupled electron-boson systems." - "This should instead be folded into a two-body operator.") + raise NotImplementedError( + "1 electron contraction is currently" + "bugged for coupled electron-boson systems." + "This should instead be folded into a two-body operator." + ) if isinstance(nelec, (int, numpy.integer)): nelecb = nelec // 2 neleca = nelec - nelecb @@ -88,8 +87,9 @@ def contract_2e(eri, fcivec, norb, nelec, nbosons, max_occ): # t1 = lib.einsum('bjai,aiAB...->bjAB...', eri.reshape([norb]*4), t1) t1a_ = numpy.tensordot(eri[0].reshape([norb] * 4), t1a, 2) + numpy.tensordot(eri[1].reshape([norb] * 4), t1b, 2) - t1b_ = numpy.tensordot(eri[2].reshape([norb] * 4), t1b, 2) + numpy.tensordot(eri[1].reshape([norb] * 4), t1a, - [[0, 1], [0, 1]]) + t1b_ = numpy.tensordot(eri[2].reshape([norb] * 4), t1b, 2) + numpy.tensordot( + eri[1].reshape([norb] * 4), t1a, [[0, 1], [0, 1]] + ) t1a, t1b = t1a_, t1b_ fcinew = numpy.zeros_like(ci0) @@ -177,9 +177,9 @@ def contract_ep(heb, fcivec, norb, nelec, nbosons, max_occ, adj_zero_pho=False): # Contract phonon-phonon portion of the Hamiltonian. + def contract_pp(hpp, fcivec, norb, nelec, nbosons, max_occ): - """Arbitrary phonon-phonon coupling. - """ + """Arbitrary phonon-phonon coupling.""" cishape = make_shape(norb, nelec, nbosons, max_occ) ci0 = fcivec.reshape(cishape) fcinew = numpy.zeros_like(ci0) @@ -279,20 +279,25 @@ def make_hdiag(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ): g2e_ab = ao2mo.restore(1, g2e[1], norb) g2e_bb = ao2mo.restore(1, g2e[2], norb) - diagj_aa = numpy.einsum('iijj->ij', g2e_aa) - diagj_ab = numpy.einsum('iijj->ij', g2e_ab) - diagj_bb = numpy.einsum('iijj->ij', g2e_bb) + diagj_aa = numpy.einsum("iijj->ij", g2e_aa) + diagj_ab = numpy.einsum("iijj->ij", g2e_ab) + diagj_bb = numpy.einsum("iijj->ij", g2e_bb) - diagk_aa = numpy.einsum('ijji->ij', g2e_aa) - diagk_bb = numpy.einsum('ijji->ij', g2e_bb) + diagk_aa = numpy.einsum("ijji->ij", g2e_aa) + diagk_bb = numpy.einsum("ijji->ij", g2e_bb) for ia, aocc in enumerate(occslista): for ib, bocc in enumerate(occslistb): e1 = h1e[0][aocc, aocc].sum() + h1e[1][bocc, bocc].sum() - e2 = diagj_aa[aocc][:, aocc].sum() + diagj_ab[aocc][:, bocc].sum() \ - + diagj_ab.T[bocc][:, aocc].sum() + diagj_bb[bocc][:, bocc].sum() \ - - diagk_aa[aocc][:, aocc].sum() - diagk_bb[bocc][:, bocc].sum() - hdiag[ia, ib] += e1 + e2 * .5 + e2 = ( + diagj_aa[aocc][:, aocc].sum() + + diagj_ab[aocc][:, bocc].sum() + + diagj_ab.T[bocc][:, aocc].sum() + + diagj_bb[bocc][:, bocc].sum() + - diagk_aa[aocc][:, aocc].sum() + - diagk_bb[bocc][:, bocc].sum() + ) + hdiag[ia, ib] += e1 + e2 * 0.5 # No electron-phonon part? @@ -307,11 +312,24 @@ def make_hdiag(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ): return hdiag.ravel() -def kernel(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ, - tol=1e-12, max_cycle=100, verbose=0, ecore=0, - returnhop=False, adj_zero_pho=False, - **kwargs): - h2e = tuple([ao2mo.restore(1, x, norb) for x in direct_uhf.absorb_h1e(h1e, g2e, norb, nelec, .5)]) +def kernel( + h1e, + g2e, + hep, + hpp, + norb, + nelec, + nbosons, + max_occ, + tol=1e-12, + max_cycle=100, + verbose=0, + ecore=0, + returnhop=False, + adj_zero_pho=False, + **kwargs, +): + h2e = tuple([ao2mo.restore(1, x, norb) for x in direct_uhf.absorb_h1e(h1e, g2e, norb, nelec, 0.5)]) cishape = make_shape(norb, nelec, nbosons, max_occ) ci0 = numpy.zeros(cishape) # Add noise for initial guess, remove it if problematic @@ -319,8 +337,7 @@ def kernel(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ, ci0.__setitem__((0, 0) + (0,) * nbosons, 1) def hop(c): - hc = contract_all(h1e, h2e, hep, hpp, c, norb, - nelec, nbosons, max_occ, adj_zero_pho=adj_zero_pho) + hc = contract_all(h1e, h2e, hep, hpp, c, norb, nelec, nbosons, max_occ, adj_zero_pho=adj_zero_pho) return hc.reshape(-1) hdiag = make_hdiag(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ) @@ -328,21 +345,47 @@ def hop(c): if returnhop: return hop, ci0, hdiag - e, c = lib.davidson(hop, ci0.reshape(-1), precond, - tol=tol, max_cycle=max_cycle, verbose=verbose, - **kwargs) + e, c = lib.davidson(hop, ci0.reshape(-1), precond, tol=tol, max_cycle=max_cycle, verbose=verbose, **kwargs) return e + ecore, c.reshape(cishape) -def kernel_multiroot(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ, - tol=1e-12, max_cycle=100, verbose=0, ecore=0, nroots=2, - returnhop=False, adj_zero_pho=False, - **kwargs): +def kernel_multiroot( + h1e, + g2e, + hep, + hpp, + norb, + nelec, + nbosons, + max_occ, + tol=1e-12, + max_cycle=100, + verbose=0, + ecore=0, + nroots=2, + returnhop=False, + adj_zero_pho=False, + **kwargs, +): if nroots == 1: - return kernel(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ, - tol, max_cycle, verbose, ecore, returnhop, adj_zero_pho, - **kwargs) - h2e = direct_uhf.absorb_h1e(h1e, g2e, norb, nelec, .5) + return kernel( + h1e, + g2e, + hep, + hpp, + norb, + nelec, + nbosons, + max_occ, + tol, + max_cycle, + verbose, + ecore, + returnhop, + adj_zero_pho, + **kwargs, + ) + h2e = direct_uhf.absorb_h1e(h1e, g2e, norb, nelec, 0.5) cishape = make_shape(norb, nelec, nbosons, max_occ) ci0 = numpy.zeros(cishape) @@ -352,8 +395,7 @@ def kernel_multiroot(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ, ci0[:, 0] += numpy.random.random(ci0[:, 0].shape) * 1e-6 def hop(c): - hc = contract_all(h1e, h2e, hep, hpp, c, norb, - nelec, nbosons, max_occ, adj_zero_pho=adj_zero_pho) + hc = contract_all(h1e, h2e, hep, hpp, c, norb, nelec, nbosons, max_occ, adj_zero_pho=adj_zero_pho) return hc.reshape(-1) hdiag = make_hdiag(h1e, g2e, hep, hpp, norb, nelec, nbosons, max_occ) @@ -361,15 +403,23 @@ def hop(c): if returnhop: return hop, ci0, hdiag - es, cs = lib.davidson(hop, ci0.reshape(-1), precond, - tol=tol, max_cycle=max_cycle, verbose=verbose, nroots=nroots, max_space=20, - **kwargs) + es, cs = lib.davidson( + hop, + ci0.reshape(-1), + precond, + tol=tol, + max_cycle=max_cycle, + verbose=verbose, + nroots=nroots, + max_space=20, + **kwargs, + ) return es, cs # dm_pq = <|p^+ q|> def make_rdm1(fcivec, norb, nelec): - '''1-electron density matrix dm_pq = <|p^+ q|>''' + """1-electron density matrix dm_pq = <|p^+ q|>""" neleca, nelecb = _unpack_nelec(nelec) link_indexa = cistring.gen_linkstr_index(range(norb), neleca) link_indexb = cistring.gen_linkstr_index(range(norb), nelecb) @@ -386,15 +436,15 @@ def make_rdm1(fcivec, norb, nelec): ci0 = fcivec.reshape(na, nb, -1) for str0, tab in enumerate(link_indexb): for a, i, str1, sign in tab: - rdm1b[a, i] += sign * numpy.einsum('ax,ax->', ci0[:, str1], ci0[:, str0]) + rdm1b[a, i] += sign * numpy.einsum("ax,ax->", ci0[:, str1], ci0[:, str0]) return rdm1a, rdm1b def make_rdm12(fcivec, norb, nelec): - '''1-electron and 2-electron density matrices + """1-electron and 2-electron density matrices dm_qp = <|q^+ p|> dm_{pqrs} = <|p^+ r^+ s q|> - ''' + """ neleca, nelecb = _unpack_nelec(nelec) link_indexa = cistring.gen_linkstr_index(range(norb), neleca) link_indexb = cistring.gen_linkstr_index(range(norb), nelecb) @@ -414,20 +464,20 @@ def make_rdm12(fcivec, norb, nelec): for a, i, str1, sign in tab: t1[i, a, k] += sign * ci0[str0, str1] - rdm1 += numpy.einsum('mp,ijmp->ij', ci0[str0], t1) + rdm1 += numpy.einsum("mp,ijmp->ij", ci0[str0], t1) # i^+ j|0> => <0|j^+ i, so swap i and j #:rdm2 += numpy.einsum('ijmp,klmp->jikl', t1, t1) - tmp = lib.dot(t1.reshape(norb ** 2, -1), t1.reshape(norb ** 2, -1).T) + tmp = lib.dot(t1.reshape(norb**2, -1), t1.reshape(norb**2, -1).T) rdm2 += tmp.reshape((norb,) * 4).transpose(1, 0, 2, 3) rdm1, rdm2 = rdm.reorder_rdm(rdm1, rdm2, True) return rdm1, rdm2 def make_rdm12s(fcivec, norb, nelec, reorder=True): - '''1-electron and 2-electron spin-resolved density matrices + """1-electron and 2-electron spin-resolved density matrices dm_qp = <|q^+ p|> dm_{pqrs} = <|p^+ r^+ s q|> - ''' + """ neleca, nelecb = _unpack_nelec(nelec) link_indexa = cistring.gen_linkstr_index(range(norb), neleca) link_indexb = cistring.gen_linkstr_index(range(norb), nelecb) @@ -464,8 +514,8 @@ def make_rdm12s(fcivec, norb, nelec, reorder=True): # i^+ j|0> => <0|j^+ i, so swap i and j #:rdm2 += numpy.einsum('ijmp,klmp->jikl', t1, t1) # Calc <0|i^+ a b^+ j |0> - t1a = t1a.reshape((norb ** 2, -1)) - t1b = t1b.reshape((norb ** 2, -1)) + t1a = t1a.reshape((norb**2, -1)) + t1b = t1b.reshape((norb**2, -1)) dm2aa += lib.dot(t1a, t1a.T).reshape((norb,) * 4).transpose(1, 0, 2, 3) dm2ab += lib.dot(t1a, t1b.T).reshape((norb,) * 4).transpose(1, 0, 2, 3) @@ -521,14 +571,14 @@ def make_eb_rdm(fcivec, norb, nelec, nbosons, max_occ): norm_slice = (slice(None, None, None),) * 2 + slices_for(ibos, nbosons, iocc) tempa[ex_slice] += t1a[norm_slice] * bos_cre[iocc] tempb[ex_slice] += t1b[norm_slice] * bos_cre[iocc] - rdm_fba = numpy.dot(tempa.reshape((norb ** 2 * nbosons, -1)), ci0.reshape(-1)).reshape((norb, norb, nbosons)) - rdm_fbb = numpy.dot(tempb.reshape((norb ** 2 * nbosons, -1)), ci0.reshape(-1)).reshape((norb, norb, nbosons)) + rdm_fba = numpy.dot(tempa.reshape((norb**2 * nbosons, -1)), ci0.reshape(-1)).reshape((norb, norb, nbosons)) + rdm_fbb = numpy.dot(tempb.reshape((norb**2 * nbosons, -1)), ci0.reshape(-1)).reshape((norb, norb, nbosons)) return rdm_fba, rdm_fbb -def calc_dd_resp_mom(ci0, e0, max_mom, norb, nel, nbos, h1e, eri, hbb, heb, max_boson_occ, rdm1, - trace=False, - coeffs=None, **kwargs): +def calc_dd_resp_mom( + ci0, e0, max_mom, norb, nel, nbos, h1e, eri, hbb, heb, max_boson_occ, rdm1, trace=False, coeffs=None, **kwargs +): """ Calculate up to the mth moment of the dd response, dealing with all spin components separately. To replace preceding function. @@ -583,20 +633,20 @@ def calc_dd_resp_mom(ci0, e0, max_mom, norb, nel, nbos, h1e, eri, hbb, heb, max_ bintermeds[iinter + 1] = numpy.zeros_like(t1b) for i in range(na): if trace: - aintermeds[iinter + 1][i] = hop(aintermeds[iinter][i]).reshape(-1) - \ - e0 * aintermeds[iinter][i] + aintermeds[iinter + 1][i] = hop(aintermeds[iinter][i]).reshape(-1) - e0 * aintermeds[iinter][i] else: for a in range(na): - aintermeds[iinter + 1][a, i] = hop(aintermeds[iinter][a, i]).reshape(-1) - \ - e0 * aintermeds[iinter][a, i] + aintermeds[iinter + 1][a, i] = ( + hop(aintermeds[iinter][a, i]).reshape(-1) - e0 * aintermeds[iinter][a, i] + ) for i in range(nb): if trace: - bintermeds[iinter + 1][i] = hop(bintermeds[iinter][i]).reshape(-1) - \ - e0 * bintermeds[iinter][i] + bintermeds[iinter + 1][i] = hop(bintermeds[iinter][i]).reshape(-1) - e0 * bintermeds[iinter][i] else: for a in range(nb): - bintermeds[iinter + 1][a, i] = hop(bintermeds[iinter][a, i]).reshape(-1) - \ - e0 * bintermeds[iinter][a, i] + bintermeds[iinter + 1][a, i] = ( + hop(bintermeds[iinter][a, i]).reshape(-1) - e0 * bintermeds[iinter][a, i] + ) # Need to adjust zeroth moment to remove ground state contributions; in all higher moments this is achieved by # deducting the reference energy. @@ -615,13 +665,13 @@ def calc_dd_resp_mom(ci0, e0, max_mom, norb, nel, nbos, h1e, eri, hbb, heb, max_ moments[imom] = ( numpy.dot(aintermed_l, aintermed_r.T), numpy.dot(aintermed_l, bintermed_r.T), - numpy.dot(bintermed_l, bintermed_r.T) + numpy.dot(bintermed_l, bintermed_r.T), ) else: moments[imom] = ( numpy.tensordot(aintermed_l, aintermed_r, (2, 2)), numpy.tensordot(aintermed_l, bintermed_r, (2, 2)), - numpy.tensordot(bintermed_l, bintermed_r, (2, 2)) + numpy.tensordot(bintermed_l, bintermed_r, (2, 2)), ) # Need to add additional adjustment for zeroth moment, as there is a nonzero ground state # contribution in this case (the current value is in fact the double occupancy <0|n_{pq} n_{sr}|0>). @@ -647,36 +697,30 @@ def calc_dd_resp_mom(ci0, e0, max_mom, norb, nel, nbos, h1e, eri, hbb, heb, max_ def run(nelec, h1e, eri, hpp, hep, max_occ, returnhop=False, nroots=1, **kwargs): - """run a calculation using a pyscf mf object. - """ + """run a calculation using a pyscf mf object.""" norb = h1e.shape[1] nbosons = hpp.shape[0] if returnhop: - hop0 = kernel(h1e, eri, hep, hpp, norb, nelec, nbosons, max_occ, - returnhop=True, **kwargs) + hop0 = kernel(h1e, eri, hep, hpp, norb, nelec, nbosons, max_occ, returnhop=True, **kwargs) # hop1 = fci_slow.kernel(h1e, eri, norb, nelec)#, returnhop=True) return hop0 # , hop1 if nroots > 1: - es, cs = kernel_multiroot(h1e, eri, hep, hpp, norb, nelec, nbosons, max_occ, nroots=nroots, - **kwargs) + es, cs = kernel_multiroot(h1e, eri, hep, hpp, norb, nelec, nbosons, max_occ, nroots=nroots, **kwargs) return es, cs else: - res0 = kernel(h1e, eri, hep, hpp, norb, nelec, nbosons, max_occ, - **kwargs) + res0 = kernel(h1e, eri, hep, hpp, norb, nelec, nbosons, max_occ, **kwargs) return res0 def run_hub_test(returnhop=False, **kwargs): - return run_ep_hubbard(t=1.0, u=1.5, g=0.5, pp=0.1, nsite=2, nelec=2, nphonon=3, - returnhop=returnhop) + return run_ep_hubbard(t=1.0, u=1.5, g=0.5, pp=0.1, nsite=2, nelec=2, nphonon=3, returnhop=returnhop) def run_ep_hubbard(t, u, g, pp, nsite, nelec, nphonon, returnhop=False, **kwargs): - """Run a calculation using a hubbard model coupled to some phonon modes. - """ + """Run a calculation using a hubbard model coupled to some phonon modes.""" idx = numpy.arange(nsite - 1) # 1 electron interactions. h1e = numpy.zeros((nsite, nsite)) @@ -693,6 +737,5 @@ def run_ep_hubbard(t, u, g, pp, nsite, nelec, nphonon, returnhop=False, **kwargs # Only have onsite coupling; so only couple . for i in range(nsite): hep[i, i, i] = g - res0 = kernel(h1e, eri, hep, hpp, nsite, nelec, nsite, nphonon, - adj_zero_pho=False, returnhop=returnhop, **kwargs) + res0 = kernel(h1e, eri, hep, hpp, nsite, nelec, nsite, nphonon, adj_zero_pho=False, returnhop=returnhop, **kwargs) return res0 diff --git a/vayesta/solver/ebcc.py b/vayesta/solver/ebcc.py index da1a17469..beea3c6c4 100644 --- a/vayesta/solver/ebcc.py +++ b/vayesta/solver/ebcc.py @@ -17,7 +17,7 @@ class Options(ClusterSolver.Options): max_cycle: int = 200 # Max number of iterations conv_tol: float = None # Convergence energy tolerance conv_tol_normt: float = None # Convergence amplitude tolerance - store_as_ccsd: bool = False # Store results as CCSD_WaveFunction + store_as_ccsd: bool = False # Store results as CCSD_WaveFunction c_cas_occ: np.array = None # Hacky place to put active space orbitals. c_cas_vir: np.array = None @@ -27,7 +27,9 @@ def kernel(self): mf_clus.mo_coeff, space = self.get_space(self.hamil.cluster.c_active, mf_clus.mo_occ, frozen=frozen) - mycc = ebcc.EBCC(mf_clus, log=self.log, ansatz=self.opts.ansatz, space=space, shift=False, **self.get_nonnull_solver_opts()) + mycc = ebcc.EBCC( + mf_clus, log=self.log, ansatz=self.opts.ansatz, space=space, shift=False, **self.get_nonnull_solver_opts() + ) mycc.kernel() self.converged = mycc.converged if self.opts.solve_lambda: @@ -40,15 +42,17 @@ def get_space(self, mo_coeff, mo_occ, frozen=None): s = self.hamil.orig_mf.get_ovlp() c_active_occ = self.opts.c_cas_occ c_active_vir = self.opts.c_cas_vir - if (c_active_occ is not None and c_active_vir is not None): + if c_active_occ is not None and c_active_vir is not None: c_active_occ = dot(mo_coeff.T, s, c_active_occ) c_active_vir = dot(mo_coeff.T, s, c_active_vir) - elif (c_active_occ is not None or c_active_vir is not None): + elif c_active_occ is not None or c_active_vir is not None: raise ValueError( - "Both or neither of the occupied and virtual components of an active space must be specified.") + "Both or neither of the occupied and virtual components of an active space must be specified." + ) mo_coeff = dot(mo_coeff.T, s, mo_coeff) - return gen_space(mo_coeff[:, mo_occ > 0], mo_coeff[:, mo_occ == 0], c_active_occ, c_active_vir, - frozen_orbs=frozen) + return gen_space( + mo_coeff[:, mo_occ > 0], mo_coeff[:, mo_occ == 0], c_active_occ, c_active_vir, frozen_orbs=frozen + ) def get_nonnull_solver_opts(self): def add_nonull_opt(d, key, newkey): @@ -75,17 +79,17 @@ def construct_wavefunction(self, mycc, mo, mbos=None): self.wf.rotate(t=mycc.mo_coeff.T, inplace=True) def _debug_exact_wf(self, wf): - assert (self.is_fCCSD) + assert self.is_fCCSD mo = self.hamil.mo # Project onto cluster: ovlp = self.hamil._fragment.base.get_ovlp() ro = dot(wf.mo.coeff_occ.T, ovlp, mo.coeff_occ) rv = dot(wf.mo.coeff_vir.T, ovlp, mo.coeff_vir) t1 = dot(ro.T, wf.t1, rv) - t2 = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', ro, ro, wf.t2, rv, rv) + t2 = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", ro, ro, wf.t2, rv, rv) if wf.l1 is not None: l1 = dot(ro.T, wf.l1, rv) - l2 = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', ro, ro, wf.l2, rv, rv) + l2 = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", ro, ro, wf.l2, rv, rv) else: l1 = l2 = None self.wf = CCSD_WaveFunction(mo, t1, t2, l1=l1, l2=l2) @@ -108,17 +112,18 @@ class Options(REBCC_Solver.Options, UClusterSolver.Options): c_cas_vir: np.array = (None, None) def get_space(self, mo_coeff, mo_occ, frozen=None): - s = self.hamil.orig_mf.get_ovlp() + def _get_space(c, occ, co_cas, cv_cas, fr): # Express active orbitals in terms of cluster orbitals. - if (co_cas is not None and cv_cas is not None): + if co_cas is not None and cv_cas is not None: co_cas = dot(c.T, s, co_cas) cv_cas = dot(c.T, s, cv_cas) - elif (co_cas is not None or cv_cas is not None): + elif co_cas is not None or cv_cas is not None: raise ValueError("Both or neither of the occupied and virtual components of an active space.") c = dot(c.T, s, c) return gen_space(c[:, occ > 0], c[:, occ == 0], co_cas, cv_cas, frozen_orbs=fr) + if frozen is None: frozen = [None, None] ca, spacea = _get_space(mo_coeff[0], mo_occ[0], self.opts.c_cas_occ[0], self.opts.c_cas_vir[0], frozen[0]) @@ -143,23 +148,20 @@ def to_spin_tuple2(x): return antisymmetrize_t2(x.aaaa), x.abab, antisymmetrize_t2(x.bbbb) try: - self.wf = CCSD_WaveFunction(mo, - to_spin_tuple1(mycc.t1), - to_spin_tuple2(mycc.t2), - l1=tuple([x.T for x in to_spin_tuple1(mycc.l1)]), - l2=tuple([x.transpose(2, 3, 0, 1) for x in to_spin_tuple2(mycc.l2)]), - ) + self.wf = CCSD_WaveFunction( + mo, + to_spin_tuple1(mycc.t1), + to_spin_tuple2(mycc.t2), + l1=tuple([x.T for x in to_spin_tuple1(mycc.l1)]), + l2=tuple([x.transpose(2, 3, 0, 1) for x in to_spin_tuple2(mycc.l2)]), + ) except TypeError: - self.wf = CCSD_WaveFunction(mo, - to_spin_tuple1(mycc.t1), - to_spin_tuple2(mycc.t2) - ) + self.wf = CCSD_WaveFunction(mo, to_spin_tuple1(mycc.t1), to_spin_tuple2(mycc.t2)) else: self.wf = EBCC_WaveFunction(mo, mycc.ansatz, mycc.amplitudes, mycc.lambdas, mbos=mbos) self.wf.rotate(t=[x.T for x in mycc.mo_coeff], inplace=True) - def _debug_exact_wf(self, wf): mo = self.hamil.mo # Project onto cluster: @@ -170,17 +172,17 @@ def _debug_exact_wf(self, wf): rvb = dot(wf.mo.coeff_vir[1].T, ovlp, mo.coeff_vir[1]) t1a = dot(roa.T, wf.t1a, rva) t1b = dot(rob.T, wf.t1b, rvb) - t2aa = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', roa, roa, wf.t2aa, rva, rva) - t2ab = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', roa, rob, wf.t2ab, rva, rvb) - t2bb = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', rob, rob, wf.t2bb, rvb, rvb) + t2aa = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", roa, roa, wf.t2aa, rva, rva) + t2ab = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", roa, rob, wf.t2ab, rva, rvb) + t2bb = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", rob, rob, wf.t2bb, rvb, rvb) t1 = (t1a, t1b) t2 = (t2aa, t2ab, t2bb) if wf.l1 is not None: l1a = dot(roa.T, wf.l1a, rva) l1b = dot(rob.T, wf.l1b, rvb) - l2aa = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', roa, roa, wf.l2aa, rva, rva) - l2ab = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', roa, rob, wf.l2ab, rva, rvb) - l2bb = einsum('Ii,Jj,IJAB,Aa,Bb->ijab', rob, rob, wf.l2bb, rvb, rvb) + l2aa = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", roa, roa, wf.l2aa, rva, rva) + l2ab = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", roa, rob, wf.l2ab, rva, rvb) + l2bb = einsum("Ii,Jj,IJAB,Aa,Bb->ijab", rob, rob, wf.l2bb, rvb, rvb) l1 = (l1a, l1b) l2 = (l2aa, l2ab, l2bb) else: @@ -210,9 +212,9 @@ def get_couplings(self): return self.hamil.couplings.transpose(0, 2, 1) def construct_wavefunction(self, mycc, mo, mbos=None): - - self.wf = EBCC_WaveFunction(mo, mycc.ansatz, mycc.amplitudes, mycc.lambdas, mbos=mbos, - xi=self.hamil.polaritonic_shift) + self.wf = EBCC_WaveFunction( + mo, mycc.ansatz, mycc.amplitudes, mycc.lambdas, mbos=mbos, xi=self.hamil.polaritonic_shift + ) self.wf.rotate(t=mycc.mo_coeff.T, inplace=True) @@ -226,9 +228,9 @@ def get_couplings(self): return tuple([x.transpose(0, 2, 1) for x in self.hamil.couplings]) def construct_wavefunction(self, mycc, mo, mbos=None): - - self.wf = EBCC_WaveFunction(mo, mycc.ansatz, mycc.amplitudes, mycc.lambdas, mbos=mbos, - xi=self.hamil.polaritonic_shift) + self.wf = EBCC_WaveFunction( + mo, mycc.ansatz, mycc.amplitudes, mycc.lambdas, mbos=mbos, xi=self.hamil.polaritonic_shift + ) self.wf.rotate(t=[x.T for x in mycc.mo_coeff], inplace=True) @@ -278,12 +280,12 @@ def gen_actspace(c_full, c_act, c_frozen, tol=1e-8): c_rest = c[:, e > tol] c = np.hstack([c_frozen, c_rest, c_act]) # Check that we have the right number of orbitals. - assert(c.shape[1] == c_full.shape[1]) + assert c.shape[1] == c_full.shape[1] # Generate information. frozen_orbs = np.zeros(c.shape[1], dtype=bool) active = np.zeros(c.shape[1], dtype=bool) - frozen_orbs[:c_frozen.shape[1]] = True - active[-c_act.shape[1]:] = True + frozen_orbs[: c_frozen.shape[1]] = True + active[-c_act.shape[1] :] = True return c, frozen_orbs, active if have_actspace: diff --git a/vayesta/solver/ebfci.py b/vayesta/solver/ebfci.py index 39edf8b59..e681ad1fb 100644 --- a/vayesta/solver/ebfci.py +++ b/vayesta/solver/ebfci.py @@ -20,19 +20,23 @@ def get_solver(self, *args, **kwargs): return REBFCI(*args, **kwargs) def kernel(self): - solver = self.get_solver(self.hamil, self.hamil.bos_freqs, self.hamil.couplings, - max_boson_occ=self.opts.max_boson_occ, conv_tol=self.opts.conv_tol or 1e-12) + solver = self.get_solver( + self.hamil, + self.hamil.bos_freqs, + self.hamil.couplings, + max_boson_occ=self.opts.max_boson_occ, + conv_tol=self.opts.conv_tol or 1e-12, + ) e_fci, civec = solver.kernel() self.wf = WaveFunction(self.hamil.mo) self.wf.make_rdm1 = lambda *args, **kwargs: solver.make_rdm1(*args, **kwargs) self.wf.make_rdm2 = lambda *args, **kwargs: solver.make_rdm2(*args, **kwargs) - self.wf.make_rdm_eb = lambda *args, **kwargs: np.array(solver.make_rdm_eb(*args, **kwargs)) + \ - np.array( - self.hamil.get_eb_dm_polaritonic_shift(self.wf.make_rdm1())) + self.wf.make_rdm_eb = lambda *args, **kwargs: np.array(solver.make_rdm_eb(*args, **kwargs)) + np.array( + self.hamil.get_eb_dm_polaritonic_shift(self.wf.make_rdm1()) + ) self.wf.make_dd_moms = lambda max_mom, *args, **kwargs: solver.make_dd_moms(max_mom, *args, **kwargs) class EB_UEBFCI_Solver(UClusterSolver, EB_EBFCI_Solver): - def get_solver(self, *args, **kwargs): return UEBFCI(*args, **kwargs) diff --git a/vayesta/solver/ext_ccsd.py b/vayesta/solver/ext_ccsd.py index f7868eb5b..b214d49c2 100644 --- a/vayesta/solver/ext_ccsd.py +++ b/vayesta/solver/ext_ccsd.py @@ -15,8 +15,8 @@ class Options(RCCSD_Solver.Options): def get_callback(self): # Tailoring of T1 and T2 - tailors = [ec for ec in self.opts.external_corrections if (ec[1] == 'tailor')] - externals = [ec for ec in self.opts.external_corrections if (ec[1] in ('external', 'delta-tailor'))] + tailors = [ec for ec in self.opts.external_corrections if (ec[1] == "tailor")] + externals = [ec for ec in self.opts.external_corrections if (ec[1] in ("external", "delta-tailor"))] if tailors and externals: raise NotImplementedError if tailors: diff --git a/vayesta/solver/fci.py b/vayesta/solver/fci.py index cd2308b69..7c1549cce 100644 --- a/vayesta/solver/fci.py +++ b/vayesta/solver/fci.py @@ -22,9 +22,10 @@ class Options(ClusterSolver.Options): fix_spin: float = 0.0 # If set to a number, the given S^2 value will be enforced fix_spin_penalty: float = 1.0 # Penalty for fixing spin davidson_only: bool = True - init_guess: str = 'default' + init_guess: str = "default" init_guess_noise: float = 1e-5 - n_moments: tuple = None + n_moments: tuple = None + cisd_solver = RCISD_Solver def __init__(self, *args, **kwargs): @@ -35,8 +36,8 @@ def __init__(self, *args, **kwargs): solver = solver_cls(self.hamil.orig_mf.mol) self.log.debugv("type(solver)= %r", type(solver)) # Set options - if self.opts.init_guess == 'default': - self.opts.init_guess = 'CISD' + if self.opts.init_guess == "default": + self.opts.init_guess = "CISD" if self.opts.threads is not None: solver.threads = self.opts.threads if self.opts.conv_tol is not None: @@ -82,12 +83,12 @@ def kernel(self, ci=None): nmom = self.opts.n_moments if nmom is not None: try: - from dyson.expressions import FCI + from dyson.expressions import FCI except ImportError: self.log.error("Dyson not found - required for moment calculations") self.log.info("Skipping in-cluster moment calculations") return - self.log.info("Calculating in-cluster FCI moments %s"%str(nmom)) + self.log.info("Calculating in-cluster FCI moments %s" % str(nmom)) mf_clus, frozen = self.hamil.to_pyscf_mf(allow_dummy_orbs=True, allow_df=True) expr = FCI["1h"](mf_clus, c_ci=self.civec) self.hole_moments = expr.build_gf_moments(nmom[0]) @@ -106,7 +107,6 @@ def get_solver_class(self): return pyscf.fci.direct_uhf.FCISolver def kernel(self, ci=None): - na, nb = self.hamil.ncas if na == nb: super().kernel(ci) @@ -115,7 +115,8 @@ def kernel(self, ci=None): if ci is None and self.opts.init_guess == "CISD": self.log.warning( "CISD initial guess not implemented for UHF FCI solver with different numbers of alpha and beta orbitals." - "Using meanfield guess.") + "Using meanfield guess." + ) # Get dummy meanfield object, with padding, to represent the cluster. mf, orbs_to_freeze = self.hamil.to_pyscf_mf(allow_dummy_orbs=True, allow_df=False) # Get padded integrals from this. diff --git a/vayesta/solver/hamiltonian.py b/vayesta/solver/hamiltonian.py index 4b66e32bd..347af6bed 100644 --- a/vayesta/solver/hamiltonian.py +++ b/vayesta/solver/hamiltonian.py @@ -71,12 +71,12 @@ def __init__(self, fragment, mf, log=None, cluster=None, **kwargs): # Do we want to populate all parameters at initialisation, so fragment isn't actually saved here? self._fragment = fragment self._cluster = cluster - self.log = (log or fragment.log) + self.log = log or fragment.log # --- Options: self.opts = self.Options() self.opts.update(**kwargs) self.log.info("Parameters of %s:" % self.__class__.__name__) - self.log.info(break_into_lines(str(self.opts), newline='\n ')) + self.log.info(break_into_lines(str(self.opts), newline="\n ")) self.v_ext = None self._seris = None self._eris = None @@ -138,12 +138,13 @@ def get_fock(self, with_vext=True, use_seris=None, with_exxdiv=False): if self._seris is not None and use_seris: # Generates the fock matrix if screened ERIs are used in place of bare eris. - occ = np.s_[:self.cluster.nocc_active] + occ = np.s_[: self.cluster.nocc_active] eris = self.get_eris_bare() - v_act_bare = 2 * einsum('iipq->pq', eris[occ, occ]) - einsum('iqpi->pq', eris[occ, :, :, occ]) - v_act_seris = 2 * einsum('iipq->pq', self._seris[occ, occ]) - \ - einsum('iqpi->pq', self._seris[occ, :, :, occ]) + v_act_bare = 2 * einsum("iipq->pq", eris[occ, occ]) - einsum("iqpi->pq", eris[occ, :, :, occ]) + v_act_seris = 2 * einsum("iipq->pq", self._seris[occ, occ]) - einsum( + "iqpi->pq", self._seris[occ, :, :, occ] + ) fock += v_act_seris - v_act_bare @@ -154,8 +155,8 @@ def get_heff(self, eris=None, fock=None, with_vext=True, with_exxdiv=False): eris = self.get_eris_screened() if fock is None: fock = self.get_fock(with_vext=False, with_exxdiv=with_exxdiv) - occ = np.s_[:self.cluster.nocc_active] - v_act = 2 * einsum('iipq->pq', eris[occ, occ]) - einsum('iqpi->pq', eris[occ, :, :, occ]) + occ = np.s_[: self.cluster.nocc_active] + v_act = 2 * einsum("iipq->pq", eris[occ, occ]) - einsum("iqpi->pq", eris[occ, :, :, occ]) h_eff = fock - v_act if with_vext and self.v_ext is not None: h_eff += self.v_ext @@ -205,7 +206,6 @@ def _get_cderi(self, coeff, *args, **kwargs): return self._fragment.base.get_cderi(coeff) def get_cderi_bare(self, only_ov=False, compress=False, svd_threshold=1e-12): - if only_ov: # We only need the (L|ov) block for MP2: mo_coeff = (self.cluster.c_active_occ, self.cluster.c_active_vir) @@ -329,9 +329,14 @@ def pad(a, diag_val): # -using bare ERIs in cluster. # -ERIs are PSD. # -our mean-field has DF. - use_df = allow_df and np.ndim(clusmf.mo_coeff[1]) == 1 and self.opts.screening is None and \ - not (self._fragment.base.pbc_dimension in (1, 2)) and hasattr(self.orig_mf, 'with_df') \ - and self.orig_mf.with_df is not None + use_df = ( + allow_df + and np.ndim(clusmf.mo_coeff[1]) == 1 + and self.opts.screening is None + and not (self._fragment.base.pbc_dimension in (1, 2)) + and hasattr(self.orig_mf, "with_df") + and self.orig_mf.with_df is not None + ) clusmol.incore_anyway = not use_df if use_df: @@ -353,13 +358,14 @@ def pad(a, diag_val): heff = pad_to_match(self.get_heff(with_vext=True), dummy_energy) - clusmf.get_hcore = lambda *args, **kwargs: heff if overwrite_fock: clusmf.get_fock = lambda *args, **kwargs: pad_to_match( - self.get_fock(with_vext=True, use_seris=not force_bare_eris), dummy_energy) - clusmf.get_veff = lambda *args, **kwargs: np.array(clusmf.get_fock(*args, **kwargs)) - \ - np.array(clusmf.get_hcore()) + self.get_fock(with_vext=True, use_seris=not force_bare_eris), dummy_energy + ) + clusmf.get_veff = lambda *args, **kwargs: np.array(clusmf.get_fock(*args, **kwargs)) - np.array( + clusmf.get_hcore() + ) return clusmf, orbs_to_freeze @@ -370,7 +376,7 @@ def get_clus_mf_info(self, ao_basis=False, with_vext=True, with_exxdiv=False): nao = self.ncas mo_energy = np.diag(self.get_fock(with_vext=with_vext, with_exxdiv=with_exxdiv)) mo_occ = np.zeros_like(mo_energy) - mo_occ[:self.nelec[0]] = 2.0 + mo_occ[: self.nelec[0]] = 2.0 # Determine whether we want our cluster orbitals expressed in the basis of active orbitals, or in the AO basis. if ao_basis: mo_coeff = self.cluster.c_active @@ -383,7 +389,6 @@ def get_clus_mf_info(self, ao_basis=False, with_vext=True, with_exxdiv=False): # Functionality for use with screened interactions and external corrections. def calc_loc_erpa(self, m0, amb, only_cumulant=False): - no, nv = self.cluster.nocc_active, self.cluster.nvir_active nov = no * nv # Bare coulomb interaction in cluster ov-ov space. @@ -405,15 +410,17 @@ def pr(m): return m.reshape((no * nv, no * nv)) erpa = 0.5 * einsum("pq,qp->", pr(m0_aa + m0_ab + m0_ab.T + m0_bb - 2 * np.eye(nov)), v) - self.log.info("Computed fragment RPA cumulant energy contribution for cluster %s as %s", - self._fragment.id, - energy_string(erpa)) + self.log.info( + "Computed fragment RPA cumulant energy contribution for cluster %s as %s", + self._fragment.id, + energy_string(erpa), + ) return erpa else: d_aa, d_ab, d_bb = gen_spin_components(amb) # This should be zero. - assert (abs(d_ab).max() < 1e-10) + assert abs(d_ab).max() < 1e-10 def compute_e_rrpa(proj): def pr(m): @@ -424,8 +431,11 @@ def pr(m): erpa = 0.5 * (einsum("pq,qp->", pr(m0_aa), d_aa) + einsum("pq,qp->", pr(m0_bb), d_bb)) erpa += einsum("pq,qp->", pr(m0_aa + m0_ab + m0_ab.T + m0_bb), v) erpa -= 0.5 * (pr(d_aa + v + d_bb + v).trace()) - self.log.info("Computed fragment RPA energy contribution for cluster %s as %s", self._fragment.id, - energy_string(erpa)) + self.log.info( + "Computed fragment RPA energy contribution for cluster %s as %s", + self._fragment.id, + energy_string(erpa), + ) return erpa compute_e_rrpa(np.eye(no)) @@ -445,8 +455,6 @@ def add_screening(self, seris_intermed=None): self._seris = self._add_screening(seris_intermed, spin_integrate=True) def _add_screening(self, seris_intermed=None, spin_integrate=True): - - def spin_integrate_and_report(m, warn_threshold=1e-6): spat = (m[0] + m[1] + m[2] + m[1].transpose((2, 3, 0, 1))) / 4.0 @@ -459,7 +467,7 @@ def spin_integrate_and_report(m, warn_threshold=1e-6): if self.opts.screening is None: raise ValueError("Attempted to add screening to fragment with no screening protocol specified.") if self.opts.screening == "mrpa": - assert (seris_intermed is not None) + assert seris_intermed is not None # Use bare coulomb interaction from hamiltonian. bare_eris = self.get_eris_bare() seris = screening_moment.get_screened_eris_full(bare_eris, seris_intermed[0], log=self.log) @@ -467,10 +475,13 @@ def spin_integrate_and_report(m, warn_threshold=1e-6): seris = spin_integrate_and_report(seris) elif self.opts.screening[:4] == "crpa": bare_eris = self.get_eris_bare() - delta, crpa = screening_crpa.get_frag_deltaW(self.orig_mf, self._fragment, - pcoupling=("pcoupled" in self.opts.screening), - only_ov_screened=("ov" in self.opts.screening), - log=self.log) + delta, crpa = screening_crpa.get_frag_deltaW( + self.orig_mf, + self._fragment, + pcoupling=("pcoupled" in self.opts.screening), + only_ov_screened=("ov" in self.opts.screening), + log=self.log, + ) if "store" in self.opts.screening: self.log.warning("Storing cRPA object in Hamiltonian- O(N^4) memory cost!") self.crpa = crpa @@ -496,7 +507,11 @@ def report_screening(screened, bare, spins): wstring = "W(%2s|%2s)" % (2 * spins[0], 2 * spins[1]) self.log.info( "Maximally screened element of %s: V= %.3e -> W= %.3e (delta= %.3e)", - wstring, bare[maxidx], screened[maxidx], screened[maxidx] - bare[maxidx]) + wstring, + bare[maxidx], + screened[maxidx], + screened[maxidx] - bare[maxidx], + ) # self.log.info( # " Corresponding norms%s: ||V||= %.3e, ||W||= %.3e, ||delta||= %.3e", # " " * len(wstring), np.linalg.norm(bare), np.linalg.norm(screened), @@ -525,8 +540,10 @@ def get_sym_breaking(norm_aa, norm_ab, norm_bb): def assert_equal_spin_channels(self, message=""): na, nb = self.ncas if na != nb: - raise NotImplementedError("Active spaces with different number of alpha and beta orbitals are not yet " - "supported with this configuration. %s" % message) + raise NotImplementedError( + "Active spaces with different number of alpha and beta orbitals are not yet " + "supported with this configuration. %s" % message + ) def with_new_cluster(self, cluster): return self.__class__(self._fragment, self.orig_mf, self.log, cluster, **self.opts.asdict()) @@ -547,27 +564,27 @@ class UClusterHamiltonian(RClusterHamiltonian): def _scf_class(self): class UHF_spindep(pyscf.scf.uhf.UHF): def energy_elec(mf, dm=None, h1e=None, vhf=None): - '''Electronic energy of Unrestricted Hartree-Fock + """Electronic energy of Unrestricted Hartree-Fock Note this function has side effects which cause mf.scf_summary updated. Returns: Hartree-Fock electronic energy and the 2-electron part contribution - ''' - if dm is None: dm = mf.make_rdm1() + """ + if dm is None: + dm = mf.make_rdm1() if h1e is None: h1e = mf.get_hcore() if isinstance(dm, np.ndarray) and dm.ndim == 2: - dm = np.array((dm * .5, dm * .5)) + dm = np.array((dm * 0.5, dm * 0.5)) if vhf is None: vhf = mf.get_veff(mf.mol, dm) - e1 = np.einsum('ij,ji->', h1e[0], dm[0]) - e1 += np.einsum('ij,ji->', h1e[1], dm[1]) - e_coul = (np.einsum('ij,ji->', vhf[0], dm[0]) + - np.einsum('ij,ji->', vhf[1], dm[1])) * .5 + e1 = np.einsum("ij,ji->", h1e[0], dm[0]) + e1 += np.einsum("ij,ji->", h1e[1], dm[1]) + e_coul = (np.einsum("ij,ji->", vhf[0], dm[0]) + np.einsum("ij,ji->", vhf[1], dm[1])) * 0.5 e_elec = (e1 + e_coul).real - mf.scf_summary['e1'] = e1.real - mf.scf_summary['e2'] = e_coul.real + mf.scf_summary["e1"] = e1.real + mf.scf_summary["e2"] = e_coul.real # logger.debug(mf, 'E1 = %s Ecoul = %s', e1, e_coul.real) return e_elec, e_coul @@ -594,25 +611,35 @@ def get_fock(self, with_vext=True, use_seris=True, with_exxdiv=False): fa, fb = self._fragment.base.get_fock(with_exxdiv=with_exxdiv) fock = (dot(ca.T, fa, ca), dot(cb.T, fb, cb)) if with_vext and self.v_ext is not None: - fock = ((fock[0] + self.v_ext[0]), - (fock[1] + self.v_ext[1])) + fock = ((fock[0] + self.v_ext[0]), (fock[1] + self.v_ext[1])) if self._seris is not None and use_seris: # Generates the fock matrix if screened ERIs are used in place of bare eris. noa, nob = self.cluster.nocc_active oa = np.s_[:noa] ob = np.s_[:nob] saa, sab, sbb = self._seris - dfa = (einsum('iipq->pq', saa[oa, oa]) + einsum('pqii->pq', sab[:, :, ob, ob]) # Coulomb - - einsum('ipqi->pq', saa[oa, :, :, oa])) # Exchange - dfb = (einsum('iipq->pq', sbb[ob, ob]) + einsum('iipq->pq', sab[oa, oa]) # Coulomb - - einsum('ipqi->pq', sbb[ob, :, :, ob])) # Exchange + dfa = ( + einsum("iipq->pq", saa[oa, oa]) + + einsum("pqii->pq", sab[:, :, ob, ob]) # Coulomb + - einsum("ipqi->pq", saa[oa, :, :, oa]) + ) # Exchange + dfb = ( + einsum("iipq->pq", sbb[ob, ob]) + + einsum("iipq->pq", sab[oa, oa]) # Coulomb + - einsum("ipqi->pq", sbb[ob, :, :, ob]) + ) # Exchange gaa, gab, gbb = self.get_eris_bare() - dfa -= (einsum('iipq->pq', gaa[oa, oa]) + einsum('pqii->pq', gab[:, :, ob, ob]) # Coulomb - - einsum('ipqi->pq', gaa[oa, :, :, oa])) # Exchange - dfb -= (einsum('iipq->pq', gbb[ob, ob]) + einsum('iipq->pq', gab[oa, oa]) # Coulomb - - einsum('ipqi->pq', gbb[ob, :, :, ob])) # Exchange - fock = ((fock[0] + dfa), - (fock[1] + dfb)) + dfa -= ( + einsum("iipq->pq", gaa[oa, oa]) + + einsum("pqii->pq", gab[:, :, ob, ob]) # Coulomb + - einsum("ipqi->pq", gaa[oa, :, :, oa]) + ) # Exchange + dfb -= ( + einsum("iipq->pq", gbb[ob, ob]) + + einsum("iipq->pq", gab[oa, oa]) # Coulomb + - einsum("ipqi->pq", gbb[ob, :, :, ob]) + ) # Exchange + fock = ((fock[0] + dfa), (fock[1] + dfb)) return fock @@ -622,17 +649,22 @@ def get_heff(self, eris=None, fock=None, with_vext=True, with_exxdiv=False): if fock is None: fock = self.get_fock(with_vext=False, use_seris=False, with_exxdiv=with_exxdiv) - oa = np.s_[:self.cluster.nocc_active[0]] - ob = np.s_[:self.cluster.nocc_active[1]] + oa = np.s_[: self.cluster.nocc_active[0]] + ob = np.s_[: self.cluster.nocc_active[1]] gaa, gab, gbb = eris - va = (einsum('iipq->pq', gaa[oa, oa]) + einsum('pqii->pq', gab[:, :, ob, ob]) # Coulomb - - einsum('ipqi->pq', gaa[oa, :, :, oa])) # Exchange - vb = (einsum('iipq->pq', gbb[ob, ob]) + einsum('iipq->pq', gab[oa, oa]) # Coulomb - - einsum('ipqi->pq', gbb[ob, :, :, ob])) # Exchange + va = ( + einsum("iipq->pq", gaa[oa, oa]) + + einsum("pqii->pq", gab[:, :, ob, ob]) # Coulomb + - einsum("ipqi->pq", gaa[oa, :, :, oa]) + ) # Exchange + vb = ( + einsum("iipq->pq", gbb[ob, ob]) + + einsum("iipq->pq", gab[oa, oa]) # Coulomb + - einsum("ipqi->pq", gbb[ob, :, :, ob]) + ) # Exchange h_eff = (fock[0] - va, fock[1] - vb) if with_vext and self.v_ext is not None: - h_eff = ((h_eff[0] + self.v_ext[0]), - (h_eff[1] + self.v_ext[1])) + h_eff = ((h_eff[0] + self.v_ext[0]), (h_eff[1] + self.v_ext[1])) return h_eff def get_eris_bare(self, block=None): @@ -647,8 +679,12 @@ def get_eris_bare(self, block=None): return self._eris else: if self._eris is None: - coeffs = [self.cluster.c_active_occ[int(i.upper() == i)] if i.lower() == "o" else - self.cluster.c_active_vir[int(i.upper() == i)] for i in block] + coeffs = [ + self.cluster.c_active_occ[int(i.upper() == i)] + if i.lower() == "o" + else self.cluster.c_active_vir[int(i.upper() == i)] + for i in block + ] return self._fragment.base.get_eris_array(coeffs) else: return self._get_eris_block(self._eris, block) @@ -659,10 +695,12 @@ def _get_eris_block(self, eris, block): flip = sum(sp) == 2 and sp[0] == 1 if flip: # Store ab not ba contribution. block = block[::-1] - d = {"o": slice(self.cluster.nocc_active[0]), "O": slice(self.cluster.nocc_active[1]), - "v": slice(self.cluster.nocc_active[0], self.cluster.norb_active[0]), - "V": slice(self.cluster.nocc_active[1], self.cluster.norb_active[1]) - } + d = { + "o": slice(self.cluster.nocc_active[0]), + "O": slice(self.cluster.nocc_active[1]), + "v": slice(self.cluster.nocc_active[0], self.cluster.norb_active[0]), + "V": slice(self.cluster.nocc_active[1], self.cluster.norb_active[1]), + } sl = tuple([d[i] for i in block]) res = eris[sum(sp) // 2][sl] if flip: @@ -670,7 +708,6 @@ def _get_eris_block(self, eris, block): return res def get_cderi_bare(self, only_ov=False, compress=False, svd_threshold=1e-12): - if only_ov: # We only need the (L|ov) and (L|OV) blocks: c_aa = [self.cluster.c_active_occ[0], self.cluster.c_active_vir[0]] @@ -706,8 +743,9 @@ def compress_cderi(cd): def to_pyscf_mf(self, allow_dummy_orbs=True, force_bare_eris=False, overwrite_fock=True, allow_df=False): # Need to overwrite fock integrals to avoid errors. - return super().to_pyscf_mf(allow_dummy_orbs=allow_dummy_orbs, force_bare_eris=force_bare_eris, - overwrite_fock=True, allow_df=allow_df) + return super().to_pyscf_mf( + allow_dummy_orbs=allow_dummy_orbs, force_bare_eris=force_bare_eris, overwrite_fock=True, allow_df=allow_df + ) def get_clus_mf_info(self, ao_basis=False, with_vext=True, with_exxdiv=False): if ao_basis: @@ -717,8 +755,8 @@ def get_clus_mf_info(self, ao_basis=False, with_vext=True, with_exxdiv=False): fock = self.get_fock(with_vext=with_vext, with_exxdiv=with_exxdiv) mo_energy = (np.diag(fock[0]), np.diag(fock[1])) mo_occ = [np.zeros_like(x) for x in mo_energy] - mo_occ[0][:self.nelec[0]] = 1.0 - mo_occ[1][:self.nelec[1]] = 1.0 + mo_occ[0][: self.nelec[0]] = 1.0 + mo_occ[1][: self.nelec[1]] = 1.0 if mo_occ[0].shape == mo_occ[1].shape: mo_occ = np.array(mo_occ) # Determine whether we want our cluster orbitals expressed in the basis of active orbitals, or in the AO basis. @@ -818,32 +856,41 @@ def set_polaritonic_shift(self, freqs, couplings): noa = nob = no else: noa, nob = no - self._polaritonic_shift = np.multiply(freqs ** (-1), einsum("npp->n", couplings[0][:, :noa, :noa]) + - einsum("npp->n", couplings[1][:, :nob, :nob])) - self.log.info("Applying Polaritonic shift gives energy change of %e", - -sum(np.multiply(self._polaritonic_shift ** 2, freqs))) + self._polaritonic_shift = np.multiply( + freqs ** (-1), einsum("npp->n", couplings[0][:, :noa, :noa]) + einsum("npp->n", couplings[1][:, :nob, :nob]) + ) + self.log.info( + "Applying Polaritonic shift gives energy change of %e", + -sum(np.multiply(self._polaritonic_shift**2, freqs)), + ) def get_heff(self, eris=None, fock=None, with_vext=True): heff = super().get_heff(eris, fock, with_vext) if self.opts.polaritonic_shift: fock_shift = self.get_polaritonic_fock_shift(self.unshifted_couplings) if not np.allclose(fock_shift[0], fock_shift[1]): - self.log.critical("Polaritonic shift breaks cluster spin symmetry; please either use an unrestricted" - "formalism or bosons without polaritonic shift.") + self.log.critical( + "Polaritonic shift breaks cluster spin symmetry; please either use an unrestricted" + "formalism or bosons without polaritonic shift." + ) heff = heff + fock_shift[0] return heff def get_polaritonic_fock_shift(self, couplings): - return tuple([- einsum("npq,n->pq", x + x.transpose(0, 2, 1), self.polaritonic_shift) for x in couplings]) + return tuple([-einsum("npq,n->pq", x + x.transpose(0, 2, 1), self.polaritonic_shift) for x in couplings]) def get_polaritonic_shifted_couplings(self): temp = np.multiply(self.polaritonic_shift, self.bos_freqs) / (2 * self.cluster.nocc_active) couplings = tuple([x - einsum("pq,n->npq", np.eye(x.shape[1]), temp) for x in self.unshifted_couplings]) if not np.allclose(couplings[0], couplings[1]): - self.log.critical("Polaritonic shifted bosonic fermion-boson couplings break cluster spin symmetry; please" - " use an unrestricted formalism.") - raise RuntimeError("Polaritonic shifted bosonic fermion-boson couplings break cluster spin symmetry; please" - " use an unrestricted formalism.") + self.log.critical( + "Polaritonic shifted bosonic fermion-boson couplings break cluster spin symmetry; please" + " use an unrestricted formalism." + ) + raise RuntimeError( + "Polaritonic shifted bosonic fermion-boson couplings break cluster spin symmetry; please" + " use an unrestricted formalism." + ) return couplings[0] def get_eb_dm_polaritonic_shift(self, dm1): diff --git a/vayesta/solver/mp2.py b/vayesta/solver/mp2.py index 9d51bd8b5..3d86cef00 100644 --- a/vayesta/solver/mp2.py +++ b/vayesta/solver/mp2.py @@ -13,7 +13,6 @@ class Options(ClusterSolver.Options): compress_cderi: bool = False def kernel(self, *args, **kwargs): - nao, mo_coeff, mo_energy, mo_occ, ovlp = self.hamil.get_clus_mf_info(ao_basis=False, with_exxdiv=True) eris = cderi = cderi_neg = None @@ -44,30 +43,30 @@ def make_t2(self, mo_energy, eris=None, cderi=None, cderi_neg=None, blksize=None # (ov|ov) if eris is not None: self.log.debugv("Making T2 amplitudes from ERIs") - assert (eris.ndim == 4) + assert eris.ndim == 4 nocc, nvir = eris.shape[:2] # (L|ov) elif cderi is not None: self.log.debugv("Making T2 amplitudes from CD-ERIs") - assert (cderi.ndim == 3) - assert (cderi_neg is None or cderi_neg.ndim == 3) + assert cderi.ndim == 3 + assert cderi_neg is None or cderi_neg.ndim == 3 nocc, nvir = cderi.shape[1:] else: raise ValueError() t2 = np.empty((nocc, nocc, nvir, nvir)) - eia = (mo_energy[:nocc, None] - mo_energy[None, nocc:]) + eia = mo_energy[:nocc, None] - mo_energy[None, nocc:] if blksize is None: blksize = int(1e9 / max(nocc * nvir * nvir * 8, 1)) for blk in brange(0, nocc, blksize): if eris is not None: gijab = eris[blk].transpose(0, 2, 1, 3) else: - gijab = einsum('Lia,Ljb->ijab', cderi[:, blk], cderi) + gijab = einsum("Lia,Ljb->ijab", cderi[:, blk], cderi) if cderi_neg is not None: - gijab -= einsum('Lia,Ljb->ijab', cderi_neg[:, blk], cderi_neg) - eijab = (eia[blk][:, None, :, None] + eia[None, :, None, :]) - t2[blk] = (gijab / eijab) + gijab -= einsum("Lia,Ljb->ijab", cderi_neg[:, blk], cderi_neg) + eijab = eia[blk][:, None, :, None] + eia[None, :, None, :] + t2[blk] = gijab / eijab return t2 def _debug_exact_wf(self, wf): @@ -81,7 +80,6 @@ def _debug_random_wf(self): class UMP2_Solver(UClusterSolver, RMP2_Solver): - def get_ov_eris(self, eris, nocc): na, nb = nocc gaa, gab, gbb = eris @@ -93,17 +91,17 @@ def make_t2(self, mo_energy, eris=None, cderi=None, cderi_neg=None, blksize=None if eris is not None: self.log.debugv("Making T2 amplitudes from ERIs") assert len(eris) == 3 - assert (eris[0].ndim == 4) - assert (eris[1].ndim == 4) - assert (eris[2].ndim == 4) + assert eris[0].ndim == 4 + assert eris[1].ndim == 4 + assert eris[2].ndim == 4 nocca, nvira = eris[0].shape[:2] noccb, nvirb = eris[2].shape[:2] # (L|ov) elif cderi is not None: self.log.debugv("Making T2 amplitudes from CD-ERIs") assert len(cderi) == 2 - assert (cderi[0].ndim == 3) - assert (cderi[1].ndim == 3) + assert cderi[0].ndim == 3 + assert cderi[1].ndim == 3 nocca, nvira = cderi[0].shape[1:] noccb, nvirb = cderi[1].shape[1:] else: @@ -112,8 +110,8 @@ def make_t2(self, mo_energy, eris=None, cderi=None, cderi_neg=None, blksize=None t2aa = np.empty((nocca, nocca, nvira, nvira)) t2ab = np.empty((nocca, noccb, nvira, nvirb)) t2bb = np.empty((noccb, noccb, nvirb, nvirb)) - eia_a = (mo_energy[0][:nocca, None] - mo_energy[0][None, nocca:]) - eia_b = (mo_energy[1][:noccb, None] - mo_energy[1][None, noccb:]) + eia_a = mo_energy[0][:nocca, None] - mo_energy[0][None, nocca:] + eia_b = mo_energy[1][:noccb, None] - mo_energy[1][None, noccb:] # Alpha-alpha and Alpha-beta: if blksize is None: @@ -125,21 +123,21 @@ def make_t2(self, mo_energy, eris=None, cderi=None, cderi_neg=None, blksize=None if eris is not None: gijab = eris[0][blk].transpose(0, 2, 1, 3) else: - gijab = einsum('Lia,Ljb->ijab', cderi[0][:, blk], cderi[0]) + gijab = einsum("Lia,Ljb->ijab", cderi[0][:, blk], cderi[0]) if cderi_neg[0] is not None: - gijab -= einsum('Lia,Ljb->ijab', cderi_neg[0][:, blk], cderi_neg[0]) - eijab = (eia_a[blk][:, None, :, None] + eia_a[None, :, None, :]) - t2aa[blk] = (gijab / eijab) + gijab -= einsum("Lia,Ljb->ijab", cderi_neg[0][:, blk], cderi_neg[0]) + eijab = eia_a[blk][:, None, :, None] + eia_a[None, :, None, :] + t2aa[blk] = gijab / eijab t2aa[blk] -= t2aa[blk].transpose(0, 1, 3, 2) # Alpha-beta if eris is not None: gijab = eris[1][blk].transpose(0, 2, 1, 3) else: - gijab = einsum('Lia,Ljb->ijab', cderi[0][:, blk], cderi[1]) + gijab = einsum("Lia,Ljb->ijab", cderi[0][:, blk], cderi[1]) if cderi_neg[0] is not None: - gijab -= einsum('Lia,Ljb->ijab', cderi_neg[0][:, blk], cderi_neg[1]) - eijab = (eia_a[blk][:, None, :, None] + eia_b[None, :, None, :]) - t2ab[blk] = (gijab / eijab) + gijab -= einsum("Lia,Ljb->ijab", cderi_neg[0][:, blk], cderi_neg[1]) + eijab = eia_a[blk][:, None, :, None] + eia_b[None, :, None, :] + t2ab[blk] = gijab / eijab # Beta-beta: if blksize is None: blksize_b = int(workmem / max(noccb * nvirb * nvirb * 8, 1)) @@ -149,11 +147,11 @@ def make_t2(self, mo_energy, eris=None, cderi=None, cderi_neg=None, blksize=None if eris is not None: gijab = eris[2][blk].transpose(0, 2, 1, 3) else: - gijab = einsum('Lia,Ljb->ijab', cderi[1][:, blk], cderi[1]) + gijab = einsum("Lia,Ljb->ijab", cderi[1][:, blk], cderi[1]) if cderi_neg[0] is not None: - gijab -= einsum('Lia,Ljb->ijab', cderi_neg[1][:, blk], cderi_neg[1]) - eijab = (eia_b[blk][:, None, :, None] + eia_b[None, :, None, :]) - t2bb[blk] = (gijab / eijab) + gijab -= einsum("Lia,Ljb->ijab", cderi_neg[1][:, blk], cderi_neg[1]) + eijab = eia_b[blk][:, None, :, None] + eia_b[None, :, None, :] + t2bb[blk] = gijab / eijab t2bb[blk] -= t2bb[blk].transpose(0, 1, 3, 2) return (t2aa, t2ab, t2bb) diff --git a/vayesta/solver/solver.py b/vayesta/solver/solver.py index 09c4b732b..f8934871b 100644 --- a/vayesta/solver/solver.py +++ b/vayesta/solver/solver.py @@ -20,12 +20,12 @@ def __init__(self, hamil, log=None, **kwargs): --------- """ self.hamil = hamil - self.log = (log or hamil.log) + self.log = log or hamil.log # --- Options: self.opts = self.Options() self.opts.update(**kwargs) self.log.info("Parameters of %s:" % self.__class__.__name__) - self.log.info(break_into_lines(str(self.opts), newline='\n ')) + self.log.info(break_into_lines(str(self.opts), newline="\n ")) # --- Results self.converged = False @@ -80,6 +80,7 @@ def optimize_cpt(self, nelectron, c_frag, cpt_guess=0, atol=1e-6, rtol=1e-6, cpt class CptFound(RuntimeError): """Raise when electron error is below tolerance.""" + pass def kernel(self, *args, **kwargs): @@ -102,8 +103,8 @@ def electron_err(cpt): replace = {} if cpt: - v_ext_0 = (self.v_ext if self.v_ext is not None else 0) - replace['v_ext'] = self.calc_v_ext(v_ext_0, cpt) + v_ext_0 = self.v_ext if self.v_ext is not None else 0 + replace["v_ext"] = self.calc_v_ext(v_ext_0, cpt) # self.reset() self.converged = False with replace_attr(self.hamil, **replace): @@ -112,14 +113,16 @@ def electron_err(cpt): raise ConvergenceError() dm1 = self.wf.make_rdm1() if np.ndim(p_frag[0]) == 1: - ne_frag = einsum('xi,ij,xj->', p_frag, dm1, p_frag) + ne_frag = einsum("xi,ij,xj->", p_frag, dm1, p_frag) else: - ne_frag = (einsum('xi,ij,xj->', p_frag[0], dm1[0], p_frag[0]) - + einsum('xi,ij,xj->', p_frag[1], dm1[1], p_frag[1])) + ne_frag = einsum("xi,ij,xj->", p_frag[0], dm1[0], p_frag[0]) + einsum( + "xi,ij,xj->", p_frag[1], dm1[1], p_frag[1] + ) - err = (ne_frag - nelectron) - self.log.debug("Fragment chemical potential= %+12.8f Ha: electrons= %.8f error= %+.3e", cpt, ne_frag, - err) + err = ne_frag - nelectron + self.log.debug( + "Fragment chemical potential= %+12.8f Ha: electrons= %.8f error= %+.3e", cpt, ne_frag, err + ) iterations += 1 if abs(err) < (atol + rtol * nelectron): cpt_opt = cpt @@ -135,7 +138,11 @@ def electron_err(cpt): except CptFound: self.log.debug( "Chemical potential= %.6f leads to electron error= %.3e within tolerance (atol= %.1e, rtol= %.1e)", - cpt_guess, err, atol, rtol) + cpt_guess, + err, + atol, + rtol, + ) return result # Not enough electrons in fragment space -> raise fragment chemical potential: @@ -152,18 +159,21 @@ def electron_err(cpt): for ntry in range(5): try: - cpt, res = scipy.optimize.brentq(electron_err, a=bounds[0], b=bounds[1], xtol=1e-12, - full_output=True) + cpt, res = scipy.optimize.brentq( + electron_err, a=bounds[0], b=bounds[1], xtol=1e-12, full_output=True + ) if res.converged: raise RuntimeError( - "Chemical potential converged to %+16.8f, but electron error is still %.3e" % (cpt, err)) + "Chemical potential converged to %+16.8f, but electron error is still %.3e" % (cpt, err) + ) except CptFound: break # Could not find chemical potential in bracket: except ValueError: bounds *= 2 - self.log.warning("Interval for chemical potential search too small. New search interval: [%f %f]", - *bounds) + self.log.warning( + "Interval for chemical potential search too small. New search interval: [%f %f]", *bounds + ) continue # Could not convergence in bracket: except ConvergenceError: @@ -172,7 +182,7 @@ def electron_err(cpt): continue raise RuntimeError("Invalid state: electron error= %.3e" % err) else: - errmsg = ("Could not find chemical potential within interval [%f %f]!" % (bounds[0], bounds[1])) + errmsg = "Could not find chemical potential within interval [%f %f]!" % (bounds[0], bounds[1]) self.log.critical(errmsg) raise RuntimeError(errmsg) @@ -190,7 +200,6 @@ def get_init_guess(self): class UClusterSolver(ClusterSolver): - def calc_v_ext(self, v_ext_0, cpt): pfrag = self.hamil.target_space_projector() # Surely None would be a better default? diff --git a/vayesta/solver/tccsd.py b/vayesta/solver/tccsd.py index 2c0b82b9e..8694e2d8a 100644 --- a/vayesta/solver/tccsd.py +++ b/vayesta/solver/tccsd.py @@ -41,17 +41,17 @@ def get_callback(self): del fci, tham def tailor_func(kwargs): - cc = kwargs['mycc'] - t1, t2 = kwargs['t1new'], kwargs['t2new'] + cc = kwargs["mycc"] + t1, t2 = kwargs["t1new"], kwargs["t2new"] # Rotate & project CC amplitudes to CAS - t1_cc = einsum('IA,Ii,Aa->ia', t1, ro, rv) - t2_cc = einsum('IJAB,Ii,Jj,Aa,Bb->ijab', t2, ro, ro, rv, rv) + t1_cc = einsum("IA,Ii,Aa->ia", t1, ro, rv) + t2_cc = einsum("IJAB,Ii,Jj,Aa,Bb->ijab", t2, ro, ro, rv, rv) # Take difference wrt to FCI - dt1 = (wf.t1 - t1_cc) - dt2 = (wf.t2 - t2_cc) + dt1 = wf.t1 - t1_cc + dt2 = wf.t2 - t2_cc # Rotate back to CC space - dt1 = einsum('ia,Ii,Aa->IA', dt1, ro, rv) - dt2 = einsum('ijab,Ii,Jj,Aa,Bb->IJAB', dt2, ro, ro, rv, rv) + dt1 = einsum("ia,Ii,Aa->IA", dt1, ro, rv) + dt2 = einsum("ijab,Ii,Jj,Aa,Bb->IJAB", dt2, ro, ro, rv, rv) # Add correction t1 += dt1 t2 += dt2 diff --git a/vayesta/tests/common.py b/vayesta/tests/common.py index d3d47b7a9..126751be5 100644 --- a/vayesta/tests/common.py +++ b/vayesta/tests/common.py @@ -15,7 +15,6 @@ def __exit__(self, *args): class TestCase(unittest.TestCase): - allclose_atol = 1e-8 allclose_rtol = 1e-7 @@ -32,19 +31,18 @@ def assertAllclose(self, actual, desired, rtol=allclose_atol, atol=allclose_rtol # Add higher precision output: message = e.args[0] args = e.args[1:] - message += '\nHigh precision:\n x: %r\n y: %r' % (actual, desired) + message += "\nHigh precision:\n x: %r\n y: %r" % (actual, desired) e.args = (message, *args) raise -if __name__ == '__main__': - - a = np.random.rand(3,4) - b = np.random.rand(4,5) +if __name__ == "__main__": + a = np.random.rand(3, 4) + b = np.random.rand(4, 5) test = TestCase() test.assertAllclose(a, a) test.assertAllclose((a, b), (a, b)) - test.assertAllclose([[a,a], b], [[a,a], b]) + test.assertAllclose([[a, a], b], [[a, a], b]) test.assertAllclose(a, b) diff --git a/vayesta/tests/core/ao2mo/test_helper.py b/vayesta/tests/core/ao2mo/test_helper.py index fc578c243..8707107c6 100644 --- a/vayesta/tests/core/ao2mo/test_helper.py +++ b/vayesta/tests/core/ao2mo/test_helper.py @@ -9,7 +9,6 @@ class Test_RHF(TestCase): - system = testsystems.water_631g @classmethod @@ -18,7 +17,7 @@ def setUpClass(cls): cls.cc = cls.system.rccsd() cls.eris = cls.cc.ao2mo() nmo = cls.mf.mo_coeff.shape[-1] - cls.g_ref = pyscf.ao2mo.kernel(cls.mf.mol, cls.mf.mo_coeff, compact=False).reshape(4*[nmo]) + cls.g_ref = pyscf.ao2mo.kernel(cls.mf.mol, cls.mf.mo_coeff, compact=False).reshape(4 * [nmo]) @classmethod def tearDownClass(cls): @@ -33,13 +32,12 @@ def test_get_full_array_ccsd(self): def test_dm2_eris_ccsd(self): dm2 = self.cc.make_rdm2() - e_ref = np.einsum('ijkl,ijkl', dm2, self.g_ref) + e_ref = np.einsum("ijkl,ijkl", dm2, self.g_ref) e_test = helper.contract_dm2_eris_rhf(dm2, self.eris) self.assertAllclose(e_ref, e_test) class Test_UHF(TestCase): - system = testsystems.water_cation_631g @classmethod @@ -49,18 +47,20 @@ def setUpClass(cls): cls.eris = cls.cc.ao2mo() g_ref_aa = pyscf.ao2mo.kernel(cls.mf.mol, cls.mf.mo_coeff[0], compact=False) g_ref_bb = pyscf.ao2mo.kernel(cls.mf.mol, cls.mf.mo_coeff[1], compact=False) - g_ref_ab = pyscf.ao2mo.kernel(cls.mf.mol, 2*[cls.mf.mo_coeff[0]] + 2*[cls.mf.mo_coeff[1]], compact=False) + g_ref_ab = pyscf.ao2mo.kernel(cls.mf.mol, 2 * [cls.mf.mo_coeff[0]] + 2 * [cls.mf.mo_coeff[1]], compact=False) nmoa = cls.mf.mo_coeff[0].shape[-1] nmob = cls.mf.mo_coeff[1].shape[-1] - cls.g_ref_aa = g_ref_aa.reshape(4*[nmoa]) - cls.g_ref_ab = g_ref_ab.reshape(2*[nmoa] + 2*[nmob]) - cls.g_ref_bb = g_ref_bb.reshape(4*[nmob]) + cls.g_ref_aa = g_ref_aa.reshape(4 * [nmoa]) + cls.g_ref_ab = g_ref_ab.reshape(2 * [nmoa] + 2 * [nmob]) + cls.g_ref_bb = g_ref_bb.reshape(4 * [nmob]) - #cls.dm2 = cls.cc.make_rdm2() + # cls.dm2 = cls.cc.make_rdm2() cls.dm2 = uccsd_rdm.make_rdm2(cls.cc, cls.cc.t1, cls.cc.t2, cls.cc.l1, cls.cc.l2, with_dm1=False) - cls.e_ref = (np.einsum('ijkl,ijkl', cls.dm2[0], cls.g_ref_aa) - + np.einsum('ijkl,ijkl', cls.dm2[1], cls.g_ref_ab)*2 - + np.einsum('ijkl,ijkl', cls.dm2[2], cls.g_ref_bb)) + cls.e_ref = ( + np.einsum("ijkl,ijkl", cls.dm2[0], cls.g_ref_aa) + + np.einsum("ijkl,ijkl", cls.dm2[1], cls.g_ref_ab) * 2 + + np.einsum("ijkl,ijkl", cls.dm2[2], cls.g_ref_bb) + ) @classmethod def tearDownClass(cls): @@ -90,6 +90,6 @@ def test_dm2intermeds_eris_ccsd(self): self.assertAllclose(e_test, self.e_ref) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/core/ao2mo/test_kao2gmo.py b/vayesta/tests/core/ao2mo/test_kao2gmo.py index 50718bfb7..8766c678e 100644 --- a/vayesta/tests/core/ao2mo/test_kao2gmo.py +++ b/vayesta/tests/core/ao2mo/test_kao2gmo.py @@ -16,7 +16,6 @@ @pytest.mark.slow class KAO2GMO_Tests(TestCase): - def test_kao2gmo_cderi_3d(self): cell = testsystems.he_k321.mol kmf = testsystems.he_k321.rhf() @@ -26,22 +25,26 @@ def test_kao2gmo_cderi_3d(self): nmo1, nmo2 = 1, 2 np.random.seed(1491) - mo1 = np.random.rand(len(kpts)*cell.nao, nmo1) - mo2 = np.random.rand(len(kpts)*cell.nao, nmo2) + mo1 = np.random.rand(len(kpts) * cell.nao, nmo1) + mo2 = np.random.rand(len(kpts) * cell.nao, nmo2) scell = pyscf.pbc.tools.super_cell(cell, kmesh) gdf_sc = pyscf.pbc.df.GDF(scell) - gdf_sc.auxbasis = 'def2-svp-ri' + gdf_sc.auxbasis = "def2-svp-ri" gdf_sc.build() eri_expected = gdf_sc.ao2mo((mo1, mo2, mo1, mo2)).reshape(nmo1, nmo2, nmo1, nmo2) - options = {'driver' : ['c', 'python'], 'make_real' : [True, False], 'blksize' : [None, 10], - 'tril_kij' : [True, False]} + options = { + "driver": ["c", "python"], + "make_real": [True, False], + "blksize": [None, 10], + "tril_kij": [True, False], + } options_comb = [dict(zip(options, val)) for val in itertools.product(*options.values())] for opts in options_comb: cderi, cderi_neg = kao2gmo_cderi(gdf, (mo1, mo2), **opts) - eri = np.einsum('Lij,Lkl->ijkl', cderi.conj(), cderi) + eri = np.einsum("Lij,Lkl->ijkl", cderi.conj(), cderi) self.assertIsNone(cderi_neg) self.assertIsNone(np.testing.assert_almost_equal(eri, eri_expected)) @@ -54,23 +57,28 @@ def test_kao2gmo_cderi_2d(self): nmo1, nmo2 = 1, 2 np.random.seed(15918) - mo1 = np.random.rand(len(kpts)*cell.nao, nmo1) - mo2 = np.random.rand(len(kpts)*cell.nao, nmo2) + mo1 = np.random.rand(len(kpts) * cell.nao, nmo1) + mo2 = np.random.rand(len(kpts) * cell.nao, nmo2) scell = pyscf.pbc.tools.super_cell(cell, kmesh) gdf_sc = pyscf.pbc.df.GDF(scell) - gdf_sc.auxbasis = 'def2-svp-ri' + gdf_sc.auxbasis = "def2-svp-ri" gdf_sc.build() eri_expected = gdf_sc.ao2mo((mo1, mo2, mo1, mo2)).reshape(nmo1, nmo2, nmo1, nmo2) - options = {'driver' : ['c', 'python'], 'make_real' : [True, False], 'blksize' : [None, 10], - 'tril_kij' : [True, False]} + options = { + "driver": ["c", "python"], + "make_real": [True, False], + "blksize": [None, 10], + "tril_kij": [True, False], + } options_comb = [dict(zip(options, val)) for val in itertools.product(*options.values())] for opts in options_comb: cderi, cderi_neg = kao2gmo_cderi(gdf, (mo1, mo2), **opts) - eri = (np.einsum('Lij,Lkl->ijkl', cderi.conj(), cderi) - - np.einsum('Lij,Lkl->ijkl', cderi_neg.conj(), cderi_neg)) + eri = np.einsum("Lij,Lkl->ijkl", cderi.conj(), cderi) - np.einsum( + "Lij,Lkl->ijkl", cderi_neg.conj(), cderi_neg + ) self.assertIsNone(np.testing.assert_almost_equal(eri, eri_expected)) def test_kao2gmo_cderi_1d(self): @@ -82,26 +90,31 @@ def test_kao2gmo_cderi_1d(self): nmo1, nmo2 = 1, 2 np.random.seed(10518) - mo1 = np.random.rand(len(kpts)*cell.nao, nmo1) - mo2 = np.random.rand(len(kpts)*cell.nao, nmo2) + mo1 = np.random.rand(len(kpts) * cell.nao, nmo1) + mo2 = np.random.rand(len(kpts) * cell.nao, nmo2) scell = pyscf.pbc.tools.super_cell(cell, kmesh) gdf_sc = pyscf.pbc.df.GDF(scell) - gdf_sc.auxbasis = 'def2-svp-ri' + gdf_sc.auxbasis = "def2-svp-ri" gdf_sc.build() eri_expected = gdf_sc.ao2mo((mo1, mo2, mo1, mo2)).reshape(nmo1, nmo2, nmo1, nmo2) - options = {'driver' : ['c', 'python'], 'make_real' : [True, False], 'blksize' : [None, 10], - 'tril_kij' : [True, False]} + options = { + "driver": ["c", "python"], + "make_real": [True, False], + "blksize": [None, 10], + "tril_kij": [True, False], + } options_comb = [dict(zip(options, val)) for val in itertools.product(*options.values())] for opts in options_comb: cderi, cderi_neg = kao2gmo_cderi(gdf, (mo1, mo2), **opts) - eri = (np.einsum('Lij,Lkl->ijkl', cderi.conj(), cderi) - - np.einsum('Lij,Lkl->ijkl', cderi_neg.conj(), cderi_neg)) + eri = np.einsum("Lij,Lkl->ijkl", cderi.conj(), cderi) - np.einsum( + "Lij,Lkl->ijkl", cderi_neg.conj(), cderi_neg + ) self.assertIsNone(np.testing.assert_almost_equal(eri, eri_expected)) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/core/ao2mo/test_postscf_ao2mo.py b/vayesta/tests/core/ao2mo/test_postscf_ao2mo.py index b9d47a049..71bb70e70 100644 --- a/vayesta/tests/core/ao2mo/test_postscf_ao2mo.py +++ b/vayesta/tests/core/ao2mo/test_postscf_ao2mo.py @@ -13,7 +13,6 @@ @pytest.mark.slow class PostSCF_KAO2GMO_Tests(TestCase): - def test_rhf(self): cell = testsystems.he_k321.mol khf = testsystems.he_k321.rhf() @@ -29,16 +28,15 @@ def test_rhf(self): mo2 = np.random.rand(nao, nao) nactive_occ = 2 nactive_vir = 3 - active = list(range(nocc-nactive_occ, nocc+nactive_vir)) - frozen = list(range(0, nocc-nactive_occ)) + list(range(nocc+nactive_vir, nao)) + active = list(range(nocc - nactive_occ, nocc + nactive_vir)) + frozen = list(range(0, nocc - nactive_occ)) + list(range(nocc + nactive_vir, nao)) fock = shf.get_fock() - mo_energy = np.einsum('ai,ab,bi->i', shf.mo_coeff[:,active], fock, shf.mo_coeff[:,active]) - mo_energy2 = np.einsum('ai,ab,bi->i', mo2[:,active], fock, mo2[:,active]) + mo_energy = np.einsum("ai,ab,bi->i", shf.mo_coeff[:, active], fock, shf.mo_coeff[:, active]) + mo_energy2 = np.einsum("ai,ab,bi->i", mo2[:, active], fock, mo2[:, active]) e_hf = shf.e_tot for postscfcls in [pyscf.cc.ccsd.CCSD, pyscf.cc.rccsd.RCCSD, pyscf.cc.dfccsd.RCCSD]: - postscf = postscfcls(shf, frozen=frozen) eris_expected_1 = postscf.ao2mo() eris_expected_2 = postscf.ao2mo(mo_coeff=mo2) @@ -47,15 +45,27 @@ def test_rhf(self): eris_2 = postscf_kao2gmo(postscf, gdf, fock=fock, mo_energy=mo_energy2, e_hf=e_hf, mo_coeff=mo2) for eris, eris_expected in [(eris_1, eris_expected_1), (eris_2, eris_expected_2)]: - for attr in ['oooo', 'ovoo', 'oovv', 'ovvo', 'ovov', 'ovvv', 'vvvv', 'vvL', 'fock', 'mo_energy', 'e_hf']: + for attr in [ + "oooo", + "ovoo", + "oovv", + "ovvo", + "ovov", + "ovvv", + "vvvv", + "vvL", + "fock", + "mo_energy", + "e_hf", + ]: expected = getattr(eris_expected, attr, None) if expected is None: continue val = getattr(eris, attr) - if attr == 'vvL': - expected = np.einsum('iQ,jQ->ij', expected, expected) - val = np.einsum('iQ,jQ->ij', val, val) + if attr == "vvL": + expected = np.einsum("iQ,jQ->ij", expected, expected) + val = np.einsum("iQ,jQ->ij", val, val) self.assertEqual(val.shape, expected.shape) self.assertIsNone(np.testing.assert_almost_equal(val, expected)) @@ -75,18 +85,17 @@ def test_rhf_2d(self): mo2 = np.random.rand(nao, nao) nactive_occ = 2 nactive_vir = 3 - active = list(range(nocc-nactive_occ, nocc+nactive_vir)) - frozen = list(range(0, nocc-nactive_occ)) + list(range(nocc+nactive_vir, nao)) + active = list(range(nocc - nactive_occ, nocc + nactive_vir)) + frozen = list(range(0, nocc - nactive_occ)) + list(range(nocc + nactive_vir, nao)) fock = shf.get_fock() - mo_energy = np.einsum('ai,ab,bi->i', shf.mo_coeff[:,active], fock, shf.mo_coeff[:,active]) - mo_energy2 = np.einsum('ai,ab,bi->i', mo2[:,active], fock, mo2[:,active]) + mo_energy = np.einsum("ai,ab,bi->i", shf.mo_coeff[:, active], fock, shf.mo_coeff[:, active]) + mo_energy2 = np.einsum("ai,ab,bi->i", mo2[:, active], fock, mo2[:, active]) e_hf = shf.e_tot - eriblocks = ['oooo', 'ovoo', 'oovv', 'ovvo', 'ovov', 'ovvv', 'vvvv', 'vvL'] + eriblocks = ["oooo", "ovoo", "oovv", "ovvo", "ovov", "ovvv", "vvvv", "vvL"] for postscfcls in [pyscf.cc.ccsd.CCSD, pyscf.cc.rccsd.RCCSD]: - postscf = postscfcls(shf, frozen=frozen) eris_expected_1 = postscf.ao2mo() eris_expected_2 = postscf.ao2mo(mo_coeff=mo2) @@ -95,15 +104,15 @@ def test_rhf_2d(self): eris_2 = postscf_kao2gmo(postscf, gdf, fock=fock, mo_energy=mo_energy2, e_hf=e_hf, mo_coeff=mo2) for eris, eris_expected in [(eris_1, eris_expected_1), (eris_2, eris_expected_2)]: - for attr in (eriblocks + ['fock', 'mo_energy', 'e_hf']): + for attr in eriblocks + ["fock", "mo_energy", "e_hf"]: expected = getattr(eris_expected, attr, None) if expected is None: continue val = getattr(eris, attr) - if attr == 'vvL': - expected = np.einsum('iQ,jQ->ij', expected, expected) - val = np.einsum('iQ,jQ->ij', val, val) + if attr == "vvL": + expected = np.einsum("iQ,jQ->ij", expected, expected) + val = np.einsum("iQ,jQ->ij", val, val) self.assertEqual(val.shape, expected.shape) self.assertIsNone(np.testing.assert_almost_equal(val, expected)) @@ -125,26 +134,25 @@ def test_uhf(self): mo2 = (mo2a, mo2b) nactive_occ = 2 nactive_vir = 3 - active = list(range(nocc-nactive_occ, nocc+nactive_vir)) - frozen = list(range(0, nocc-nactive_occ)) + list(range(nocc+nactive_vir, nao)) + active = list(range(nocc - nactive_occ, nocc + nactive_vir)) + frozen = list(range(0, nocc - nactive_occ)) + list(range(nocc + nactive_vir, nao)) fock = shf.get_fock() - mo_energy_a = np.einsum('ai,ab,bi->i', shf.mo_coeff[0][:,active], fock[0], shf.mo_coeff[0][:,active]) - mo_energy_b = np.einsum('ai,ab,bi->i', shf.mo_coeff[1][:,active], fock[1], shf.mo_coeff[1][:,active]) + mo_energy_a = np.einsum("ai,ab,bi->i", shf.mo_coeff[0][:, active], fock[0], shf.mo_coeff[0][:, active]) + mo_energy_b = np.einsum("ai,ab,bi->i", shf.mo_coeff[1][:, active], fock[1], shf.mo_coeff[1][:, active]) mo_energy = (mo_energy_a, mo_energy_b) - mo_energy2_a = np.einsum('ai,ab,bi->i', mo2[0][:,active], fock[0], mo2[0][:,active]) - mo_energy2_b = np.einsum('ai,ab,bi->i', mo2[1][:,active], fock[1], mo2[1][:,active]) + mo_energy2_a = np.einsum("ai,ab,bi->i", mo2[0][:, active], fock[0], mo2[0][:, active]) + mo_energy2_b = np.einsum("ai,ab,bi->i", mo2[1][:, active], fock[1], mo2[1][:, active]) mo_energy2 = (mo_energy2_a, mo_energy2_b) e_hf = shf.e_tot - eriblocks_aa = ['oooo', 'ovoo', 'oovv', 'ovvo', 'ovov', 'ovvv', 'vvvv'] - eriblocks_ab = ['%s%s' % (x[:2], x[2:].upper()) for x in eriblocks_aa] + eriblocks_aa = ["oooo", "ovoo", "oovv", "ovvo", "ovov", "ovvv", "vvvv"] + eriblocks_ab = ["%s%s" % (x[:2], x[2:].upper()) for x in eriblocks_aa] eriblocks_bb = [x.upper() for x in eriblocks_aa] - eriblocks_ba = ['OVoo', 'OOvv', 'OVvo', 'OVvv'] + eriblocks_ba = ["OVoo", "OOvv", "OVvo", "OVvv"] eriblocks = eriblocks_aa + eriblocks_bb + eriblocks_ab + eriblocks_ba for postscfcls in [pyscf.cc.uccsd.UCCSD]: - postscf = postscfcls(shf, frozen=frozen) eris_expected_1 = postscf.ao2mo() eris_expected_2 = postscf.ao2mo(mo_coeff=mo2) @@ -153,7 +161,7 @@ def test_uhf(self): eris_2 = postscf_kao2gmo_uhf(postscf, gdf, fock=fock, mo_energy=mo_energy2, e_hf=e_hf, mo_coeff=mo2) for eris, eris_expected in [(eris_1, eris_expected_1), (eris_2, eris_expected_2)]: - for attr in (eriblocks + ['fock', 'mo_energy', 'e_hf']): + for attr in eriblocks + ["fock", "mo_energy", "e_hf"]: expected = getattr(eris_expected, attr, None) if expected is None: continue @@ -161,6 +169,6 @@ def test_uhf(self): self.assertAllclose(val, expected) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/core/ao2mo/test_postscf_kao2gmo.py b/vayesta/tests/core/ao2mo/test_postscf_kao2gmo.py index d07e5745e..9c46b769e 100644 --- a/vayesta/tests/core/ao2mo/test_postscf_kao2gmo.py +++ b/vayesta/tests/core/ao2mo/test_postscf_kao2gmo.py @@ -9,7 +9,6 @@ @pytest.mark.slow class Test_CCSD(TestCase): - @classmethod def setUpClass(cls): cls.mf = testsystems.h2_sto3g_k311.rhf() @@ -38,12 +37,12 @@ def test_postscf_kao2gmo(self): self.assertAllclose(eris.fock, eris_ref.fock) self.assertAllclose(eris.mo_energy, eris_ref.mo_energy) # PySCF version < v2.1: - if hasattr(eris, 'e_hf'): + if hasattr(eris, "e_hf"): self.assertAllclose(eris.e_hf, eris_ref.e_hf) + @pytest.mark.slow class Test_UCCSD(Test_CCSD): - @classmethod def setUpClass(cls): cls.mf = testsystems.h3_sto3g_k311.uhf() @@ -64,37 +63,37 @@ def test_postscf_kao2gmo(self): self.assertAllclose(eris.oovv.flatten(), eris_ref.oovv.flatten()) self.assertAllclose(eris.ovov.flatten(), eris_ref.ovov.flatten()) # TODO: Currently (ov|vv) and (vv|vv) integrals are not tested, as they need to be unpacked - #self.assertAllclose(eris.ovvv.flatten(), eris_ref.ovvv.flatten()) - #self.assertAllclose(eris.vvvv.flatten(), eris_ref.vvvv.flatten()) + # self.assertAllclose(eris.ovvv.flatten(), eris_ref.ovvv.flatten()) + # self.assertAllclose(eris.vvvv.flatten(), eris_ref.vvvv.flatten()) # Alpha-Beta self.assertAllclose(eris.ooOO.flatten(), eris_ref.ooOO.flatten()) self.assertAllclose(eris.ovOO.flatten(), eris_ref.ovOO.flatten()) self.assertAllclose(eris.ovVO.flatten(), eris_ref.ovVO.flatten()) self.assertAllclose(eris.ooVV.flatten(), eris_ref.ooVV.flatten()) self.assertAllclose(eris.ovOV.flatten(), eris_ref.ovOV.flatten()) - #self.assertAllclose(eris.ovVV.flatten(), eris_ref.ovVV.flatten()) - #self.assertAllclose(eris.vvVV.flatten(), eris_ref.vvVV.flatten()) + # self.assertAllclose(eris.ovVV.flatten(), eris_ref.ovVV.flatten()) + # self.assertAllclose(eris.vvVV.flatten(), eris_ref.vvVV.flatten()) # Beta-Alpha self.assertAllclose(eris.OVoo.flatten(), eris_ref.OVoo.flatten()) self.assertAllclose(eris.OVvo.flatten(), eris_ref.OVvo.flatten()) self.assertAllclose(eris.OOvv.flatten(), eris_ref.OOvv.flatten()) - #self.assertAllclose(eris.OVvv.flatten(), eris_ref.OVvv.flatten()) + # self.assertAllclose(eris.OVvv.flatten(), eris_ref.OVvv.flatten()) # Beta-Beta self.assertAllclose(eris.OOOO.flatten(), eris_ref.OOOO.flatten()) self.assertAllclose(eris.OVOO.flatten(), eris_ref.OVOO.flatten()) self.assertAllclose(eris.OVVO.flatten(), eris_ref.OVVO.flatten()) self.assertAllclose(eris.OOVV.flatten(), eris_ref.OOVV.flatten()) self.assertAllclose(eris.OVOV.flatten(), eris_ref.OVOV.flatten()) - #self.assertAllclose(eris.OVVV.flatten(), eris_ref.OVVV.flatten()) - #self.assertAllclose(eris.VVVV.flatten(), eris_ref.VVVV.flatten()) + # self.assertAllclose(eris.OVVV.flatten(), eris_ref.OVVV.flatten()) + # self.assertAllclose(eris.VVVV.flatten(), eris_ref.VVVV.flatten()) # Other self.assertAllclose(eris.fock, eris_ref.fock) self.assertAllclose(eris.mo_energy, eris_ref.mo_energy) # PySCF version < v2.1: - if hasattr(eris, 'e_hf'): + if hasattr(eris, "e_hf"): self.assertAllclose(eris.e_hf, eris_ref.e_hf) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/core/ao2mo/test_pyscf_eris.py b/vayesta/tests/core/ao2mo/test_pyscf_eris.py index 6e156e432..11a040e48 100644 --- a/vayesta/tests/core/ao2mo/test_pyscf_eris.py +++ b/vayesta/tests/core/ao2mo/test_pyscf_eris.py @@ -9,7 +9,6 @@ class TestRSpin(TestCase): - system = testsystems.water_631g @classmethod @@ -18,7 +17,7 @@ def setUpClass(cls): nmo = cls.mf.mo_coeff.shape[-1] cls.nocc = np.count_nonzero(cls.mf.mo_occ > 0) cls.fock = np.diag(cls.mf.mo_energy) - cls.eris = pyscf.ao2mo.kernel(cls.mf.mol, cls.mf.mo_coeff, compact=False).reshape(4*[nmo]) + cls.eris = pyscf.ao2mo.kernel(cls.mf.mol, cls.mf.mo_coeff, compact=False).reshape(4 * [nmo]) @classmethod def tearDownClass(cls): @@ -32,19 +31,20 @@ def test_make_ccsd_eris(self): class TestUSpin(TestRSpin): - system = testsystems.water_cation_631g @classmethod def setUpClass(cls): cls.mf = cls.system.uhf() - cls.nocc = (np.count_nonzero(cls.mf.mo_occ[0]>0), np.count_nonzero(cls.mf.mo_occ[1]>0)) + cls.nocc = (np.count_nonzero(cls.mf.mo_occ[0] > 0), np.count_nonzero(cls.mf.mo_occ[1] > 0)) cls.fock = (np.diag(cls.mf.mo_energy[0]), np.diag(cls.mf.mo_energy[1])) nmoa = cls.mf.mo_coeff[0].shape[-1] nmob = cls.mf.mo_coeff[1].shape[-1] - eris_aa = pyscf.ao2mo.kernel(cls.mf.mol, cls.mf.mo_coeff[0], compact=False).reshape(4*[nmoa]) - eris_bb = pyscf.ao2mo.kernel(cls.mf.mol, cls.mf.mo_coeff[1], compact=False).reshape(4*[nmob]) - eris_ab = pyscf.ao2mo.kernel(cls.mf.mol, 2*[cls.mf.mo_coeff[0]] + 2*[cls.mf.mo_coeff[1]], compact=False).reshape(2*[nmoa]+2*[nmob]) + eris_aa = pyscf.ao2mo.kernel(cls.mf.mol, cls.mf.mo_coeff[0], compact=False).reshape(4 * [nmoa]) + eris_bb = pyscf.ao2mo.kernel(cls.mf.mol, cls.mf.mo_coeff[1], compact=False).reshape(4 * [nmob]) + eris_ab = pyscf.ao2mo.kernel( + cls.mf.mol, 2 * [cls.mf.mo_coeff[0]] + 2 * [cls.mf.mo_coeff[1]], compact=False + ).reshape(2 * [nmoa] + 2 * [nmob]) cls.eris = (eris_aa, eris_ab, eris_bb) def test_make_ccsd_eris(self): @@ -54,6 +54,6 @@ def test_make_ccsd_eris(self): self.assertAllclose(eris, self.eris, atol=1e-13, rtol=0) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/core/bath/test_bath.py b/vayesta/tests/core/bath/test_bath.py index 1577d572b..bb5b2cdfd 100644 --- a/vayesta/tests/core/bath/test_bath.py +++ b/vayesta/tests/core/bath/test_bath.py @@ -11,7 +11,6 @@ class EwDMET_Bath_Test(TestCase): - def test_ewdmet_bath(self): return True mf = testsystems.ethanol_ccpvdz.rhf() @@ -27,11 +26,10 @@ def test_ewdmet_bath(self): moments = np.arange(12) c_frag = frag.c_frag csc = np.linalg.multi_dot((c_frag.T, ovlp, mf.mo_coeff)) - mom_full = np.einsum('xi,ik,yi->kxy', csc, np.power.outer(mf.mo_energy, moments), csc) + mom_full = np.einsum("xi,ik,yi->kxy", csc, np.power.outer(mf.mo_energy, moments), csc) # Test bath orbitals up to kmax = 4 for kmax in range(0, 4): - ewdmet_bath = EwDMET_Bath(frag, dmet_bath, max_order=kmax) ewdmet_bath.kernel() @@ -44,34 +42,34 @@ def test_ewdmet_bath(self): c_cluster_mo = np.dot(c_cluster, r) csc = np.linalg.multi_dot((c_frag.T, ovlp, c_cluster_mo)) - mom_cluster = np.einsum('xi,ik,yi->kxy', csc, np.power.outer(e, moments), csc) + mom_cluster = np.einsum("xi,ik,yi->kxy", csc, np.power.outer(e, moments), csc) # Check "2n + 1" - for order in range(2*kmax + 2): + for order in range(2 * kmax + 2): print("Testing EwDMET bath: kmax= %d moment= %d" % (kmax, order)) self.assertIsNone(np.testing.assert_allclose(mom_cluster[order], mom_full[order], atol=1e-7, rtol=1e-7)) -class MP2_BNO_Test(TestCase): +class MP2_BNO_Test(TestCase): def test_bno_Bath(self): rhf = testsystems.ethanol_ccpvdz.rhf() remb = Embedding(rhf) with remb.iao_fragmentation() as f: - rfrag = f.add_atomic_fragment('O') + rfrag = f.add_atomic_fragment("O") rdmet_bath = DMET_Bath(rfrag) rdmet_bath.kernel() - rbno_bath_occ = MP2_Bath(rfrag, rdmet_bath, occtype='occupied') - rbno_bath_vir = MP2_Bath(rfrag, rdmet_bath, occtype='virtual') + rbno_bath_occ = MP2_Bath(rfrag, rdmet_bath, occtype="occupied") + rbno_bath_vir = MP2_Bath(rfrag, rdmet_bath, occtype="virtual") uhf = testsystems.ethanol_ccpvdz.uhf() uemb = UEmbedding(uhf) with uemb.iao_fragmentation() as f: - ufrag = f.add_atomic_fragment('O') + ufrag = f.add_atomic_fragment("O") udmet_bath = DMET_Bath(ufrag) udmet_bath.kernel() - ubno_bath_occ = MP2_Bath(ufrag, udmet_bath, occtype='occupied') - ubno_bath_vir = MP2_Bath(ufrag, udmet_bath, occtype='virtual') + ubno_bath_occ = MP2_Bath(ufrag, udmet_bath, occtype="occupied") + ubno_bath_vir = MP2_Bath(ufrag, udmet_bath, occtype="virtual") # Check maximum, minimum, and mean occupations n_occ_max = 0.005243099445814127 @@ -108,25 +106,24 @@ def test_bno_Bath(self): self.assertAllclose(rbno_bath_vir.occup, ubno_bath_vir.occup[1]) def test_project_dmet(self): - rhf = testsystems.ethanol_ccpvdz.rhf() uhf = testsystems.ethanol_ccpvdz.uhf() remb = Embedding(rhf) with remb.iao_fragmentation() as f: - rfrag = f.add_atomic_fragment('O') + rfrag = f.add_atomic_fragment("O") rdmet_bath = DMET_Bath(rfrag) rdmet_bath.kernel() - rbno_bath_occ = MP2_Bath(rfrag, rdmet_bath, project_dmet='linear', occtype='occupied') - rbno_bath_vir = MP2_Bath(rfrag, rdmet_bath, project_dmet='linear', occtype='virtual') + rbno_bath_occ = MP2_Bath(rfrag, rdmet_bath, project_dmet="linear", occtype="occupied") + rbno_bath_vir = MP2_Bath(rfrag, rdmet_bath, project_dmet="linear", occtype="virtual") uemb = UEmbedding(uhf) with uemb.iao_fragmentation() as f: - ufrag = f.add_atomic_fragment('O') + ufrag = f.add_atomic_fragment("O") udmet_bath = DMET_Bath(ufrag) udmet_bath.kernel() - ubno_bath_occ = MP2_Bath(ufrag, udmet_bath, project_dmet='linear', occtype='occupied') - ubno_bath_vir = MP2_Bath(ufrag, udmet_bath, project_dmet='linear', occtype='virtual') + ubno_bath_occ = MP2_Bath(ufrag, udmet_bath, project_dmet="linear", occtype="occupied") + ubno_bath_vir = MP2_Bath(ufrag, udmet_bath, project_dmet="linear", occtype="virtual") # Check maximum, minimum, and mean occupations n_occ_mean = 7.377564076299791e-05 @@ -145,18 +142,18 @@ def test_project_dmet(self): self.assertAllclose(rbno_bath_vir.occup, ubno_bath_vir.occup[0]) self.assertAllclose(rbno_bath_vir.occup, ubno_bath_vir.occup[1]) -class RPA_Test(TestCase): +class RPA_Test(TestCase): def test_bno_Bath(self): rhf = testsystems.ethanol_631g_df.rhf() remb = Embedding(rhf) with remb.iao_fragmentation() as f: - rfrag = f.add_atomic_fragment('O') + rfrag = f.add_atomic_fragment("O") rdmet_bath = DMET_Bath(rfrag) rdmet_bath.kernel() - rbno_bath_occ = RPA_Bath(rfrag, rdmet_bath, occtype='occupied') - rbno_bath_vir = RPA_Bath(rfrag, rdmet_bath, occtype='virtual') + rbno_bath_occ = RPA_Bath(rfrag, rdmet_bath, occtype="occupied") + rbno_bath_vir = RPA_Bath(rfrag, rdmet_bath, occtype="virtual") # Check maximum, minimum, and mean occupations n_occ_max = 0.008502908095347851 @@ -175,6 +172,6 @@ def test_bno_Bath(self): self.assertAlmostEqual(np.mean(rbno_bath_vir.occup), n_vir_mean) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/core/bath/test_bosonic_bath.py b/vayesta/tests/core/bath/test_bosonic_bath.py index e5506293b..72e3f0c06 100644 --- a/vayesta/tests/core/bath/test_bosonic_bath.py +++ b/vayesta/tests/core/bath/test_bosonic_bath.py @@ -6,7 +6,6 @@ class QBA_RPA_Bath_Test(TestCase): - @classmethod def setUpClass(cls): try: @@ -16,9 +15,14 @@ def setUpClass(cls): def _get_occupation(self, target_orbs, local_projection): rhf = testsystems.ethanol_631g_df.rhf() - emb = vayesta.ewf.EWF(rhf, bosonic_bath_options=dict(bathtype='rpa', target_orbitals=target_orbs, - local_projection=local_projection, threshold=1e-1), - bath_options=dict(bathtype="mp2", threshold=1e-4), solver="CCSD-S-1-1") + emb = vayesta.ewf.EWF( + rhf, + bosonic_bath_options=dict( + bathtype="rpa", target_orbitals=target_orbs, local_projection=local_projection, threshold=1e-1 + ), + bath_options=dict(bathtype="mp2", threshold=1e-4), + solver="CCSD-S-1-1", + ) with emb.iao_fragmentation() as f: f.add_atomic_fragment([0]) emb.kernel() @@ -28,7 +32,7 @@ def _get_occupation(self, target_orbs, local_projection): def test_target_full_project_fragment(self): occ = self._get_occupation("full", "fragment") values = np.array([np.amin(occ), np.amax(occ), np.mean(occ)]) - known_values = np.array([-1.23436133e-19, 3.65521662e-03, 5.97968938e-05]) + known_values = np.array([-1.23436133e-19, 3.65521662e-03, 5.97968938e-05]) self.assertAllclose(values, known_values) def test_target_dmet_project_fragment(self): diff --git a/vayesta/tests/core/qemb/test_fragment.py b/vayesta/tests/core/qemb/test_fragment.py index fda17d9db..c20beeb6a 100644 --- a/vayesta/tests/core/qemb/test_fragment.py +++ b/vayesta/tests/core/qemb/test_fragment.py @@ -7,7 +7,7 @@ from vayesta.tests.common import temporary_seed, TestCase from vayesta.tests import testsystems -#TODO readd some tests for ghost atoms +# TODO readd some tests for ghost atoms class MolFragmentTests(TestCase): @@ -23,23 +23,21 @@ def tearDownClass(cls): del cls.mf def trace(self, c): - return np.einsum('xi,xi->', c, c.conj()) + return np.einsum("xi,xi->", c, c.conj()) def test_properties(self): - """Test methods registered with @property decorator. - """ + """Test methods registered with @property decorator.""" qemb = self.Embedding(self.mf) with qemb.iao_fragmentation() as f: frag = f.add_atomic_fragment(0) - self.assertIs(frag.mol, self.mf.mol) - self.assertIs(frag.mf, frag.base.mf) - self.assertIs(frag.n_frag, frag.c_frag.shape[-1]) + self.assertIs(frag.mol, self.mf.mol) + self.assertIs(frag.mf, frag.base.mf) + self.assertIs(frag.n_frag, frag.c_frag.shape[-1]) def test_iao_atoms(self): - """Test IAO atomic fragmentation. - """ + """Test IAO atomic fragmentation.""" qemb = self.Embedding(self.mf) with qemb.iao_fragmentation() as f: @@ -48,69 +46,63 @@ def test_iao_atoms(self): e_elec = sum([f.get_fragment_mf_energy() for f in frags]) self.assertAlmostEqual(e_elec + self.mf.energy_nuc(), self.mf.e_tot, self.PLACES) self.assertAlmostEqual(frags[0].get_fragment_mf_energy(), -81.55618063907355, self.PLACES) - self.assertAlmostEqual(frags[1].get_fragment_mf_energy(), -1.83005213220285, self.PLACES) - self.assertAlmostEqual(frags[2].get_fragment_mf_energy(), -1.83005213220285, self.PLACES) + self.assertAlmostEqual(frags[1].get_fragment_mf_energy(), -1.83005213220285, self.PLACES) + self.assertAlmostEqual(frags[2].get_fragment_mf_energy(), -1.83005213220285, self.PLACES) def test_iao_aos(self): - """Test IAO orbital fragmentation. - """ + """Test IAO orbital fragmentation.""" qemb = self.Embedding(self.mf) with qemb.iao_fragmentation() as f: - frag = f.add_orbital_fragment([0, 1]) self.assertAlmostEqual(frag.get_fragment_mf_energy(), -61.40491054071134, self.PLACES) - frag = f.add_atomic_fragment([0], orbital_filter=['1s', '2s']) + frag = f.add_atomic_fragment([0], orbital_filter=["1s", "2s"]) self.assertAlmostEqual(frag.get_fragment_mf_energy(), -61.40491054071134, self.PLACES) def test_iao_minao(self): - """Test IAO fragmentation with a custom `minao` keyword. - """ + """Test IAO fragmentation with a custom `minao` keyword.""" qemb = self.Embedding(self.mf) - with qemb.iao_fragmentation(minao='sto3g') as f: + with qemb.iao_fragmentation(minao="sto3g") as f: frag = f.add_atomic_fragment(0) self.assertAlmostEqual(frag.get_fragment_mf_energy(), -80.8244967591169, self.PLACES) def test_sao_atoms(self): - """Test SAO atomic fragmentation. - """ + """Test SAO atomic fragmentation.""" qemb = self.Embedding(self.mf) with qemb.sao_fragmentation() as f: - frags = [f.add_atomic_fragment(['O'])] + frags = [f.add_atomic_fragment(["O"])] frags += [f.add_atomic_fragment(i) for i in [1, 2]] self.assertAlmostEqual(frags[0].get_fragment_mf_energy(), -78.51384197417300, self.PLACES) - self.assertAlmostEqual(frags[1].get_fragment_mf_energy(), -3.35122146047156, self.PLACES) - self.assertAlmostEqual(frags[2].get_fragment_mf_energy(), -3.35122146047156, self.PLACES) + self.assertAlmostEqual(frags[1].get_fragment_mf_energy(), -3.35122146047156, self.PLACES) + self.assertAlmostEqual(frags[2].get_fragment_mf_energy(), -3.35122146047156, self.PLACES) e_mf = sum([f.get_fragment_mf_energy() for f in frags]) - self.assertAlmostEqual(e_mf, (self.mf.e_tot-self.mf.energy_nuc()), self.PLACES) + self.assertAlmostEqual(e_mf, (self.mf.e_tot - self.mf.energy_nuc()), self.PLACES) def test_sao_aos(self): - """Test SAO orbital fragmentation. - """ + """Test SAO orbital fragmentation.""" qemb = self.Embedding(self.mf) with qemb.sao_fragmentation() as f: frag = f.add_orbital_fragment([0, 1]) self.assertAlmostEqual(frag.get_fragment_mf_energy(), -56.62602303262458, self.PLACES) frag = f.add_orbital_fragment(0) self.assertAlmostEqual(frag.get_fragment_mf_energy(), -50.92864009746159, self.PLACES) - frag = f.add_orbital_fragment('1s') + frag = f.add_orbital_fragment("1s") self.assertAlmostEqual(frag.get_fragment_mf_energy(), -54.56972351221932, self.PLACES) def test_iaopao_atoms(self): - """Test IAO+PAO atomic fragmentation. - """ + """Test IAO+PAO atomic fragmentation.""" qemb = self.Embedding(self.mf) with qemb.iaopao_fragmentation() as f: frags = f.add_all_atomic_fragments() e_mf = sum([f.get_fragment_mf_energy() for f in frags]) - self.assertAlmostEqual(e_mf, (self.mf.e_tot-self.mf.energy_nuc()), self.PLACES) + self.assertAlmostEqual(e_mf, (self.mf.e_tot - self.mf.energy_nuc()), self.PLACES) - #def test_project_ref_orbitals(self): + # def test_project_ref_orbitals(self): # """Test the project_ref_orbitals function. # """ @@ -130,36 +122,35 @@ def test_iaopao_atoms(self): # self.assertAlmostEqual(np.sum(v*v)**0.5, 235.00085653529, self.PLACES) def test_dmet_bath(self): - """Test the DMET bath. - """ + """Test the DMET bath.""" qemb = self.Embedding(self.mf) with qemb.iao_fragmentation() as f: frags = f.add_all_atomic_fragments() - bath = DMET_Bath(frags[0], frags[0].opts.bath_options['dmet_threshold']) + bath = DMET_Bath(frags[0], frags[0].opts.bath_options["dmet_threshold"]) bath.kernel() - self.assertAlmostEqual(self.trace(bath.c_dmet), 2.53936714739812, self.PLACES) - self.assertAlmostEqual(self.trace(bath.c_env_occ), 0.00000000000000, self.PLACES) + self.assertAlmostEqual(self.trace(bath.c_dmet), 2.53936714739812, self.PLACES) + self.assertAlmostEqual(self.trace(bath.c_env_occ), 0.00000000000000, self.PLACES) self.assertAlmostEqual(self.trace(bath.c_env_vir), 73.98633044345692, self.PLACES) - bath = DMET_Bath(frags[1], frags[1].opts.bath_options['dmet_threshold']) + bath = DMET_Bath(frags[1], frags[1].opts.bath_options["dmet_threshold"]) bath.kernel() - self.assertAlmostEqual(self.trace(bath.c_dmet), 0.92508900878229, self.PLACES) - self.assertAlmostEqual(self.trace(bath.c_env_occ), 2.7927781539529537, self.PLACES) + self.assertAlmostEqual(self.trace(bath.c_dmet), 0.92508900878229, self.PLACES) + self.assertAlmostEqual(self.trace(bath.c_env_occ), 2.7927781539529537, self.PLACES) self.assertAlmostEqual(self.trace(bath.c_env_vir), 75.71109293760985, self.PLACES) - bath = DMET_Bath(frags[2], frags[2].opts.bath_options['dmet_threshold']) + bath = DMET_Bath(frags[2], frags[2].opts.bath_options["dmet_threshold"]) bath.kernel() - self.assertAlmostEqual(self.trace(bath.c_dmet), 0.92508900878229, self.PLACES) - self.assertAlmostEqual(self.trace(bath.c_env_occ), 2.79277815395295, self.PLACES) + self.assertAlmostEqual(self.trace(bath.c_dmet), 0.92508900878229, self.PLACES) + self.assertAlmostEqual(self.trace(bath.c_env_occ), 2.79277815395295, self.PLACES) self.assertAlmostEqual(self.trace(bath.c_env_vir), 75.71109293760989, self.PLACES) frag = frags[2] - self.assertIs(bath.mf, frag.mf) - self.assertIs(bath.mol, frag.mol) - self.assertIs(bath.log, frag.log) - self.assertIs(bath.base, frag.base) + self.assertIs(bath.mf, frag.mf) + self.assertIs(bath.mol, frag.mol) + self.assertIs(bath.log, frag.log) + self.assertIs(bath.base, frag.base) self.assertIs(bath.c_frag, frag.c_frag) self.assertIs(bath.get_environment()[0], bath.c_env_occ) self.assertIs(bath.get_environment()[1], bath.c_env_vir) @@ -167,8 +158,7 @@ def test_dmet_bath(self): frag.reset() def test_mp2_bno_bath(self): - """Test the MP2 BNO bath. - """ + """Test the MP2 BNO bath.""" qemb = self.Embedding(self.mf) with qemb.iao_fragmentation() as f: @@ -184,45 +174,45 @@ def test_mp2_bno_bath(self): dmet_bath = DMET_Bath(frags[0]) dmet_bath.kernel() - bath_occ = MP2_Bath(frags[0], dmet_bath, occtype='occupied') - bath_vir = MP2_Bath(frags[0], dmet_bath, occtype='virtual') + bath_occ = MP2_Bath(frags[0], dmet_bath, occtype="occupied") + bath_vir = MP2_Bath(frags[0], dmet_bath, occtype="virtual") dm1o = bath_occ.make_delta_dm1(t2a, t2b) dm1v = bath_vir.make_delta_dm1(t2a, t2b) - self.assertAlmostEqual(lib.fp(dm1o), 366.180724570873/2, self.PLACES) - self.assertAlmostEqual(lib.fp(dm1v), -1.956209725591/2, self.PLACES) - - #bath = MP2_BNO_Bath(frags[0], dmet_bath, local_dm='semi', canonicalize=False) - #dm1o = bath.make_delta_dm1('occ', t2a, t2b) - #dm1v = bath.make_delta_dm1('vir', t2a, t2b) - #self.assertIs(bath.get_mp2_class(), pyscf.mp.MP2) - #self.assertAlmostEqual(lib.fp(dm1o), -11.0426240019808/2, self.PLACES) - #self.assertAlmostEqual(lib.fp(dm1v), 11.2234922296148/2, self.PLACES) - - #bath = MP2_BNO_Bath(frags[0], dmet_bath, local_dm=True, canonicalize=False) - #dm1o = bath.make_delta_dm1('occ', t2a, t2b) - #dm1v = bath.make_delta_dm1('vir', t2a, t2b) - #self.assertIs(bath.get_mp2_class(), pyscf.mp.MP2) - #self.assertAlmostEqual(lib.fp(dm1o), 382.7366604206307/2, self.PLACES) - #self.assertAlmostEqual(lib.fp(dm1v), 16.0277297008466/2, self.PLACES) - - #FIXME: bugs #6 and #7 - then add to CellFragmentTests - #NOTE these values were for an old system - - #tr = lambda c: np.einsum('xi,xi->', c, c.conj()) - - #bath = MP2_BNO_Bath(frags[1]) - #bath.kernel() - #c_bno_occ, n_bno_occ = bath.make_bno_coeff('occ') - #c_bno_vir, n_bno_vir = bath.make_bno_coeff('vir') - #self.assertAlmostEqual(tr(c_bno_occ), 0.0, self.PLACES) - #self.assertAlmostequal(lib.fp(n_bno_occ), 0.0, self.PLACES) - - #bath = MP2_BNO_Bath(frags[1], canonicalize=(False, False)) - #bath.kernel() - #mp2 = pyscf.mp.mp2.MP2(self.mf) - #eris = mp2.ao2mo() - #c_bno_occ, n_bno_occ = bath.make_bno_coeff('occ', eris=eris) - #c_bno_vir, n_bno_vir = bath.make_bno_coeff('vir', eris=eris) + self.assertAlmostEqual(lib.fp(dm1o), 366.180724570873 / 2, self.PLACES) + self.assertAlmostEqual(lib.fp(dm1v), -1.956209725591 / 2, self.PLACES) + + # bath = MP2_BNO_Bath(frags[0], dmet_bath, local_dm='semi', canonicalize=False) + # dm1o = bath.make_delta_dm1('occ', t2a, t2b) + # dm1v = bath.make_delta_dm1('vir', t2a, t2b) + # self.assertIs(bath.get_mp2_class(), pyscf.mp.MP2) + # self.assertAlmostEqual(lib.fp(dm1o), -11.0426240019808/2, self.PLACES) + # self.assertAlmostEqual(lib.fp(dm1v), 11.2234922296148/2, self.PLACES) + + # bath = MP2_BNO_Bath(frags[0], dmet_bath, local_dm=True, canonicalize=False) + # dm1o = bath.make_delta_dm1('occ', t2a, t2b) + # dm1v = bath.make_delta_dm1('vir', t2a, t2b) + # self.assertIs(bath.get_mp2_class(), pyscf.mp.MP2) + # self.assertAlmostEqual(lib.fp(dm1o), 382.7366604206307/2, self.PLACES) + # self.assertAlmostEqual(lib.fp(dm1v), 16.0277297008466/2, self.PLACES) + + # FIXME: bugs #6 and #7 - then add to CellFragmentTests + # NOTE these values were for an old system + + # tr = lambda c: np.einsum('xi,xi->', c, c.conj()) + + # bath = MP2_BNO_Bath(frags[1]) + # bath.kernel() + # c_bno_occ, n_bno_occ = bath.make_bno_coeff('occ') + # c_bno_vir, n_bno_vir = bath.make_bno_coeff('vir') + # self.assertAlmostEqual(tr(c_bno_occ), 0.0, self.PLACES) + # self.assertAlmostequal(lib.fp(n_bno_occ), 0.0, self.PLACES) + + # bath = MP2_BNO_Bath(frags[1], canonicalize=(False, False)) + # bath.kernel() + # mp2 = pyscf.mp.mp2.MP2(self.mf) + # eris = mp2.ao2mo() + # c_bno_occ, n_bno_occ = bath.make_bno_coeff('occ', eris=eris) + # c_bno_vir, n_bno_vir = bath.make_bno_coeff('vir', eris=eris) class UMolFragmentTests(MolFragmentTests): @@ -235,10 +225,10 @@ def setUpClass(cls): def trace(self, c): ca, cb = c - return (np.einsum('xi,xi->', ca, ca.conj()) + np.einsum('xi,xi->', cb, cb.conj())) / 2 + return (np.einsum("xi,xi->", ca, ca.conj()) + np.einsum("xi,xi->", cb, cb.conj())) / 2 def test_properties(self): - #TODO + # TODO pass test_project_amplitude_to_fragment = None @@ -258,11 +248,10 @@ def tearDownClass(cls): del cls.mf def trace(self, c): - return np.einsum('xi,xi->', c, c.conj()) + return np.einsum("xi,xi->", c, c.conj()) def test_iao_atoms(self): - """Test IAO atomic fragmentation. - """ + """Test IAO atomic fragmentation.""" qemb = Embedding(self.mf) with qemb.iao_fragmentation() as f: @@ -272,8 +261,7 @@ def test_iao_atoms(self): self.assertAlmostEqual(frag.get_fragment_mf_energy().real, -4.261995344528813, self.PLACES) def test_iao_aos(self): - """Test IAO orbital fragmentation. - """ + """Test IAO orbital fragmentation.""" qemb = Embedding(self.mf) with qemb.iao_fragmentation() as f: @@ -284,14 +272,13 @@ def test_iao_aos(self): def test_sao_atoms(self): qemb = Embedding(self.mf) with qemb.sao_fragmentation() as f: - frags = [f.add_atomic_fragment([i*2, i*2+1]) for i in range(len(qemb.kpts))] + frags = [f.add_atomic_fragment([i * 2, i * 2 + 1]) for i in range(len(qemb.kpts))] for frag in frags: self.assertAlmostEqual(frag.get_fragment_mf_energy().real, -4.261995344528689, self.PLACES) def test_sao_aos(self): - """Test SAO orbital fragmentation. - """ + """Test SAO orbital fragmentation.""" qemb = Embedding(self.mf) with qemb.sao_fragmentation() as f: @@ -300,37 +287,36 @@ def test_sao_aos(self): self.assertAlmostEqual(frag.get_fragment_mf_energy().real, -4.261995344528689, self.PLACES) def test_dmet_bath(self): - """Test the DMET bath. - """ + """Test the DMET bath.""" qemb = Embedding(self.mf) with qemb.sao_fragmentation() as f: frag = f.add_atomic_fragment([0]) frags = [frag] + frag.add_tsymmetric_fragments([2, 2, 2]) - bath = DMET_Bath(frags[1], frags[1].opts.bath_options['dmet_threshold']) + bath = DMET_Bath(frags[1], frags[1].opts.bath_options["dmet_threshold"]) c_bath, n_bath, c_occenv, c_virenv = bath.make_dmet_bath(frags[0].c_env) - self.assertAlmostEqual(self.trace(c_bath), 3.34569601263718, self.PLACES) - self.assertAlmostEqual(self.trace(c_occenv), 8.60026059294578, self.PLACES) + self.assertAlmostEqual(self.trace(c_bath), 3.34569601263718, self.PLACES) + self.assertAlmostEqual(self.trace(c_occenv), 8.60026059294578, self.PLACES) self.assertAlmostEqual(self.trace(c_virenv), 38.23956564189844, self.PLACES) - #bath = DMET_Bath(frags[0], frags[0].opts.dmet_threshold) - #c_bath, c_occenv, c_virenv = bath.make_dmet_bath(frags[0].c_env, nbath=c_bath.shape[-1]) #FIXME bug #5 (these values are old) - #self.assertAlmostEqual(self.trace(c_bath), 3.30703579288843, self.PLACES) - #self.assertAlmostEqual(self.trace(c_occenv), 8.60108764820888, self.PLACES) - #self.assertAlmostEqual(self.trace(c_virenv), self.PLACES3.27964350816293, self.PLACES) + # bath = DMET_Bath(frags[0], frags[0].opts.dmet_threshold) + # c_bath, c_occenv, c_virenv = bath.make_dmet_bath(frags[0].c_env, nbath=c_bath.shape[-1]) #FIXME bug #5 (these values are old) + # self.assertAlmostEqual(self.trace(c_bath), 3.30703579288843, self.PLACES) + # self.assertAlmostEqual(self.trace(c_occenv), 8.60108764820888, self.PLACES) + # self.assertAlmostEqual(self.trace(c_virenv), self.PLACES3.27964350816293, self.PLACES) qemb = Embedding(self.mf) with qemb.sao_fragmentation() as f: - frags = [f.add_atomic_fragment([i*2, i*2+1]) for i in range(len(qemb.kpts))] + frags = [f.add_atomic_fragment([i * 2, i * 2 + 1]) for i in range(len(qemb.kpts))] bath = DMET_Bath(frags[0], 1e-5) c_bath, n_bath, c_occenv, c_virenv = bath.make_dmet_bath(frags[0].c_env, verbose=False) - self.assertAlmostEqual(self.trace(c_bath), 0.00000000000000, self.PLACES) - self.assertAlmostEqual(self.trace(c_occenv), 8.60025893931299, self.PLACES) + self.assertAlmostEqual(self.trace(c_bath), 0.00000000000000, self.PLACES) + self.assertAlmostEqual(self.trace(c_occenv), 8.60025893931299, self.PLACES) self.assertAlmostEqual(self.trace(c_virenv), 38.23956182500297, self.PLACES) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/core/qemb/test_integrals.py b/vayesta/tests/core/qemb/test_integrals.py index b3952589c..426330bb4 100644 --- a/vayesta/tests/core/qemb/test_integrals.py +++ b/vayesta/tests/core/qemb/test_integrals.py @@ -13,7 +13,6 @@ @pytest.mark.slow class Integral_Test(TestCase): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_631g.rhf() @@ -33,10 +32,10 @@ def get_embedding(cls): def get_eris_ref(self, mf, mo_coeff): if isinstance(mo_coeff, np.ndarray) and mo_coeff.ndim == 2: - shape = 4*[mo_coeff.shape[-1]] + shape = 4 * [mo_coeff.shape[-1]] else: shape = [mo.shape[-1] for mo in mo_coeff] - if hasattr(mf, 'with_df'): + if hasattr(mf, "with_df"): eris_ref = mf.with_df.ao2mo(mo_coeff, compact=False).reshape(shape) else: eris_ref = pyscf.ao2mo.kernel(mf.mol, mo_coeff, compact=False).reshape(shape) @@ -55,12 +54,12 @@ def test_eris_1coeff(self): self.assertAllclose(eris, eris_ref) # Density-fitting - if not hasattr(mf, 'with_df'): + if not hasattr(mf, "with_df"): return cderi, cderi_neg = emb.get_cderi(mo_coeff) - eris = np.einsum('Lij,Lkl->ijkl', cderi, cderi) + eris = np.einsum("Lij,Lkl->ijkl", cderi, cderi) if cderi_neg is not None: - eris -= np.einsum('Lij,Lkl->ijkl', cderi_neg, cderi_neg) + eris -= np.einsum("Lij,Lkl->ijkl", cderi_neg, cderi_neg) self.assertAllclose(eris, eris_ref) def test_eris_4coeff(self): @@ -68,7 +67,7 @@ def test_eris_4coeff(self): emb = self.get_embedding() mf = emb.mf nao = mf.mol.nao - nmos = [2,3,1,4] + nmos = [2, 3, 1, 4] np.random.seed(0) mo_coeffs = [np.random.rand(nao, nmos[i]) for i in range(4)] @@ -77,73 +76,74 @@ def test_eris_4coeff(self): self.assertAllclose(eris, eris_ref) # Density-fitting - if not hasattr(mf, 'with_df'): + if not hasattr(mf, "with_df"): return cderi1, cderi_neg1 = emb.get_cderi(mo_coeffs[:2]) cderi2, cderi_neg2 = emb.get_cderi(mo_coeffs[2:]) - eris = np.einsum('Lij,Lkl->ijkl', cderi1, cderi2) + eris = np.einsum("Lij,Lkl->ijkl", cderi1, cderi2) if cderi_neg1 is not None: - eris -= np.einsum('Lij,Lkl->ijkl', cderi_neg1, cderi_neg2) + eris -= np.einsum("Lij,Lkl->ijkl", cderi_neg1, cderi_neg2) self.assertAllclose(eris, eris_ref) @pytest.mark.slow class Integral_DF_Test(Integral_Test): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_631g_df.rhf() + @pytest.mark.slow class Integral_UHF_Test(Integral_Test): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_cation_631g.uhf() + @pytest.mark.slow class Integral_DF_UHF_Test(Integral_Test): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_cation_631g_df.uhf() + # PBC + @pytest.mark.slow class Integral_PBC_Test(Integral_Test): - @classmethod def setUpClass(cls): cls.mf = testsystems.h2_sto3g_s311.rhf() + @pytest.mark.slow class Integral_PBC_UHF_Test(Integral_Test): - @classmethod def setUpClass(cls): cls.mf = testsystems.h3_sto3g_s311.uhf() + # PBC 2D + @pytest.mark.slow class Integral_PBC_2D_Test(Integral_Test): - @classmethod def setUpClass(cls): cls.mf = testsystems.h2_sto3g_s31.rhf() + @pytest.mark.slow class Integral_PBC_2D_UHF_Test(Integral_Test): - @classmethod def setUpClass(cls): cls.mf = testsystems.h3_sto3g_s31.uhf() # TODO: Figure out why this fails: -#from vayesta.tests.cache import moles, cells -#class IntegralTest(unittest.TestCase): +# from vayesta.tests.cache import moles, cells +# class IntegralTest(unittest.TestCase): # # def test_eris_solid_2d(self): # @@ -190,6 +190,6 @@ def setUpClass(cls): # self.assertIsNone(np.testing.assert_allclose(eris, eris_expected)) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/core/qemb/test_rdm.py b/vayesta/tests/core/qemb/test_rdm.py index 8a79c8b1f..732fdd151 100644 --- a/vayesta/tests/core/qemb/test_rdm.py +++ b/vayesta/tests/core/qemb/test_rdm.py @@ -13,7 +13,6 @@ class Test_Restricted(TestCase): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_631g.rhf() @@ -22,14 +21,14 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): del cls.mf, cls.cc - if hasattr(cls, 'kmf'): + if hasattr(cls, "kmf"): del cls.kmf cls.emb.cache_clear() @classmethod @cache def emb(cls, bno_threshold): - mf = getattr(cls, 'kmf', cls.mf) + mf = getattr(cls, "kmf", cls.mf) solver_opts = dict(conv_tol=1e-10, conv_tol_normt=1e-8) emb = vayesta.ewf.EWF(mf, solver_options=solver_opts, bath_options=dict(threshold=bno_threshold)) with emb.sao_fragmentation() as f: @@ -39,7 +38,7 @@ def emb(cls, bno_threshold): @property def nkpts(self): - if hasattr(self, 'kmf'): + if hasattr(self, "kmf"): return len(self.kmf.kpts) return 1 @@ -66,36 +65,36 @@ def _energy_from_dms(self, dm1, dm2): e_nuc = self.mf.energy_nuc() h1e = self.mf.get_hcore() eri = pyscf.ao2mo.restore(1, self.mf._eri, h1e.shape[-1]) - if getattr(self.mf, 'exxdiv', None) is not None: + if getattr(self.mf, "exxdiv", None) is not None: madelung = pyscf.pbc.tools.madelung(self.mf.mol, self.mf.kpt) - e_exxdiv = -madelung * self.mf.mol.nelectron/2 #/ len(self.mf.kpts) + e_exxdiv = -madelung * self.mf.mol.nelectron / 2 # / len(self.mf.kpts) else: e_exxdiv = 0 - return (e_nuc + e_exxdiv + np.sum(h1e*dm1) + np.sum(dm2*eri)/2) / self.nkpts + return (e_nuc + e_exxdiv + np.sum(h1e * dm1) + np.sum(dm2 * eri) / 2) / self.nkpts def test_dmet_energy_part_2dm_full_bath(self): """Literature DMET energy.""" emb = self.emb(-1) # DMET energy: e_dmet = emb.get_dmet_energy(part_cumulant=False) - self.assertAllclose(e_dmet, self.cc.e_tot/self.nkpts) + self.assertAllclose(e_dmet, self.cc.e_tot / self.nkpts) # DMET energy from DMs: dm1 = emb.make_rdm1_demo(ao_basis=True) dm2 = emb.make_rdm2_demo(ao_basis=True, part_cumulant=False) e_dmet = self._energy_from_dms(dm1, dm2) - self.assertAllclose(e_dmet, self.cc.e_tot/self.nkpts) + self.assertAllclose(e_dmet, self.cc.e_tot / self.nkpts) def test_dmet_energy_part_cumulant_full_bath(self): """Improved DMET energy.""" emb = self.emb(-1) # DMET energy: e_dmet = emb.get_dmet_energy(part_cumulant=True) - self.assertAllclose(e_dmet, self.cc.e_tot/self.nkpts) + self.assertAllclose(e_dmet, self.cc.e_tot / self.nkpts) # DMET energy from DMs: dm1 = emb.make_rdm1_demo(ao_basis=True) dm2 = emb.make_rdm2_demo(ao_basis=True, part_cumulant=True) e_dmet = self._energy_from_dms(dm1, dm2) - self.assertAllclose(e_dmet, self.cc.e_tot/self.nkpts) + self.assertAllclose(e_dmet, self.cc.e_tot / self.nkpts) e_ref_dmet_part_2dm = -76.16067576457968 @@ -127,7 +126,6 @@ def test_dmet_energy_part_cumulant_dmet_bath(self): class Test_Unrestricted(Test_Restricted): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_cation_631g.uhf() @@ -135,7 +133,7 @@ def setUpClass(cls): def _energy_from_dms(self, dm1, dm2): dm1 = dm1[0] + dm1[1] - dm2 = dm2[0] + 2*dm2[1] + dm2[2] + dm2 = dm2[0] + 2 * dm2[1] + dm2[2] return super()._energy_from_dms(dm1, dm2) e_ref_dmet_part_2dm = -75.6952601321852 @@ -143,7 +141,6 @@ def _energy_from_dms(self, dm1, dm2): class Test_PBC_Restricted(Test_Restricted): - @classmethod def setUpClass(cls): cls.mf = testsystems.h2_sto3g_s311.rhf() @@ -153,8 +150,8 @@ def setUpClass(cls): e_ref_dmet_part_2dm = -3.848969147919312 / 3 e_ref_dmet_part_cumulant = -3.8505073633380364 / 3 -class Test_PBC_Unrestricted(Test_Unrestricted): +class Test_PBC_Unrestricted(Test_Unrestricted): @classmethod def setUpClass(cls): cls.mf = testsystems.h3_sto3g_s311.uhf() @@ -165,6 +162,6 @@ def setUpClass(cls): e_ref_dmet_part_cumulant = -1.7461038863675442 -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/core/qemb/test_symmetry.py b/vayesta/tests/core/qemb/test_symmetry.py index 127534588..cfcd6731c 100644 --- a/vayesta/tests/core/qemb/test_symmetry.py +++ b/vayesta/tests/core/qemb/test_symmetry.py @@ -9,15 +9,14 @@ CENTER = np.asarray((0.1, 0.2, -0.3)) co2_geom = [ - ('C', CENTER), - ('O1', CENTER + np.asarray([0, 0, -1.163])), - ('O2', CENTER + np.asarray([0, 0, +1.163])), - ] -co2 = TestMolecule(atom=co2_geom, basis='6-31G', incore_anyway=True) + ("C", CENTER), + ("O1", CENTER + np.asarray([0, 0, -1.163])), + ("O2", CENTER + np.asarray([0, 0, +1.163])), +] +co2 = TestMolecule(atom=co2_geom, basis="6-31G", incore_anyway=True) class TestCO2(TestCase): - system = co2 @classmethod @@ -38,15 +37,15 @@ def emb(cls, bno_threshold, symmetry=None, **kwargs): with emb.fragmentation() as f: if symmetry is None: f.add_all_atomic_fragments() - elif symmetry == 'inversion': + elif symmetry == "inversion": f.add_atomic_fragment(0) with f.inversion_symmetry(center=CENTER): f.add_atomic_fragment(1) - elif symmetry == 'reflection': + elif symmetry == "reflection": f.add_atomic_fragment(0) with f.mirror_symmetry(center=CENTER, **kwargs): f.add_atomic_fragment(1) - elif symmetry == 'rotation': + elif symmetry == "rotation": f.add_atomic_fragment(0) with f.rotational_symmetry(center=CENTER, order=2, **kwargs): f.add_atomic_fragment(1) @@ -55,14 +54,14 @@ def emb(cls, bno_threshold, symmetry=None, **kwargs): def test_inversion(self): emb = self.emb(np.inf) - emb_sym = self.emb(np.inf, symmetry='inversion') + emb_sym = self.emb(np.inf, symmetry="inversion") dm1 = emb.make_rdm1() dm1_sym = emb_sym.make_rdm1() self.assertAllclose(dm1_sym, dm1) def test_reflection(self): emb = self.emb(np.inf) - emb_sym = self.emb(np.inf, symmetry='reflection', axis='z') + emb_sym = self.emb(np.inf, symmetry="reflection", axis="z") dm1 = emb.make_rdm1() dm1_sym = emb_sym.make_rdm1() self.assertAllclose(dm1_sym, dm1) @@ -71,12 +70,12 @@ def test_rotation(self): emb = self.emb(np.inf) dm1 = emb.make_rdm1() # Test different rotation axes - for ax in ('x', 'y', (0.6, 1.3, 0)): - emb_sym = self.emb(np.inf, symmetry='rotation', axis=ax) + for ax in ("x", "y", (0.6, 1.3, 0)): + emb_sym = self.emb(np.inf, symmetry="rotation", axis=ax) dm1_sym = emb_sym.make_rdm1() self.assertAllclose(dm1_sym, dm1) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/core/scmf/test_scmf.py b/vayesta/tests/core/scmf/test_scmf.py index e349113b3..36ea1ea7c 100644 --- a/vayesta/tests/core/scmf/test_scmf.py +++ b/vayesta/tests/core/scmf/test_scmf.py @@ -8,6 +8,7 @@ class SCMF_Test(TestCase): solver = "CCSD" + @classmethod def setUpClass(cls): cls.mf = testsystems.h2_dz.rhf() @@ -19,12 +20,14 @@ def tearDownClass(cls): @classmethod @cache def emb(cls, scmf=None): - emb = ewf.EWF(cls.mf, solver=cls.solver, solver_options=dict(solve_lambda=True), bath_options=dict(bathtype='dmet')) + emb = ewf.EWF( + cls.mf, solver=cls.solver, solver_options=dict(solve_lambda=True), bath_options=dict(bathtype="dmet") + ) with emb.sao_fragmentation() as f: f.add_all_atomic_fragments() - if scmf == 'pdmet': + if scmf == "pdmet": emb.pdmet_scmf() - elif scmf == 'brueckner': + elif scmf == "brueckner": emb.brueckner_scmf() emb.kernel() return emb @@ -32,7 +35,7 @@ def emb(cls, scmf=None): def test_pdmet(self): """Test p-DMET.""" emb0 = self.emb() - emb = self.emb('pdmet') + emb = self.emb("pdmet") self.assertAllclose(emb.with_scmf.e_tot_oneshot, emb0.e_tot) self.assertAllclose(emb.with_scmf.e_tot_oneshot, -1.1419060823155505) self.assertTrue(emb.with_scmf.converged) @@ -41,7 +44,7 @@ def test_pdmet(self): def test_brueckner(self): """Test Brueckner DMET.""" emb0 = self.emb() - emb = self.emb('brueckner') + emb = self.emb("brueckner") self.assertAllclose(emb.with_scmf.e_tot_oneshot, emb0.e_tot) self.assertAllclose(emb.with_scmf.e_tot_oneshot, -1.1419060823155505) self.assertTrue(emb.with_scmf.converged) @@ -49,18 +52,18 @@ def test_brueckner(self): class SCMF_UHF_Test(SCMF_Test): - @classmethod def setUpClass(cls): cls.mf = testsystems.h2_dz.rhf().to_uhf() + class SCMF_TCCSD_Test(SCMF_Test): solver = "TCCSD" def test_pdmet(self): """Test p-DMET.""" emb0 = self.emb() - emb = self.emb('pdmet') + emb = self.emb("pdmet") self.assertAllclose(emb.with_scmf.e_tot_oneshot, emb0.e_tot) self.assertAllclose(emb.with_scmf.e_tot_oneshot, -1.1419060814972688) self.assertTrue(emb.with_scmf.converged) @@ -69,13 +72,13 @@ def test_pdmet(self): def test_brueckner(self): """Test Brueckner DMET.""" emb0 = self.emb() - emb = self.emb('brueckner') + emb = self.emb("brueckner") self.assertAllclose(emb.with_scmf.e_tot_oneshot, emb0.e_tot) self.assertAllclose(emb.with_scmf.e_tot_oneshot, -1.1419060814979487) self.assertTrue(emb.with_scmf.converged) self.assertAllclose(emb.with_scmf.e_tot, -1.1348718457034288) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/core/test_foldscf.py b/vayesta/tests/core/test_foldscf.py index cec9738d2..a042e05d2 100644 --- a/vayesta/tests/core/test_foldscf.py +++ b/vayesta/tests/core/test_foldscf.py @@ -8,7 +8,6 @@ @pytest.mark.slow class FoldSCF_RHF_Tests(TestCase): - @classmethod def setUpClass(cls): cls.kmf = testsystems.he2_631g_k222.rhf() @@ -47,7 +46,6 @@ def test_fock(self): @pytest.mark.slow class FoldSCF_UHF_Tests(FoldSCF_RHF_Tests): - @classmethod def setUpClass(cls): cls.kmf = testsystems.he2_631g_k222.uhf() @@ -59,6 +57,6 @@ def setUpClass(cls): cls.smf.kernel() -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/core/test_linalg.py b/vayesta/tests/core/test_linalg.py index 501f16ddf..168376d12 100644 --- a/vayesta/tests/core/test_linalg.py +++ b/vayesta/tests/core/test_linalg.py @@ -9,8 +9,7 @@ @pytest.mark.fast class LinalgTests(TestCase): def test_recursive_block_svd(self): - """Test the recursive_block_svd function. - """ + """Test the recursive_block_svd function.""" n = 100 np.random.seed(1) @@ -18,8 +17,8 @@ def test_recursive_block_svd(self): dm += dm.T fock = np.random.random((n, n)) - 0.5 fock += fock.T - c_frag = np.random.random((n, n//2)) - c_env = np.random.random((n, n-n//2)) + c_frag = np.random.random((n, n // 2)) + c_env = np.random.random((n, n - n // 2)) dmocc1 = np.linalg.multi_dot((c_frag.T, fock, c_env)) u, s, vh = np.linalg.svd(dmocc1) @@ -37,9 +36,9 @@ def test_recursive_block_svd(self): e_svd = np.linalg.eigh(np.dot(mo_svd, mo_svd.T))[0] e_svd2 = np.linalg.eigh(np.dot(mo_svd2, mo_svd2.T))[0] - self.assertAlmostEqual(np.max(np.abs(e_svd-e_svd2)), 0.0, 10) + self.assertAlmostEqual(np.max(np.abs(e_svd - e_svd2)), 0.0, 10) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/core/test_util.py b/vayesta/tests/core/test_util.py index 40b7dd42b..fa6ec3fc4 100644 --- a/vayesta/tests/core/test_util.py +++ b/vayesta/tests/core/test_util.py @@ -6,133 +6,133 @@ from vayesta.tests.common import TestCase + @pytest.mark.fast class TestEinsum(TestCase): - allclose_atol = 1e-12 allclose_rtol = 1e-10 def test_mmm(self): - a = np.random.rand(3,4) - b = np.random.rand(4,5) - c = np.random.rand(5,6) + a = np.random.rand(3, 4) + b = np.random.rand(4, 5) + c = np.random.rand(5, 6) ops = (a, b, c) - expected = np.einsum('ab,bc,cd->ad', *ops) + expected = np.einsum("ab,bc,cd->ad", *ops) - res = einsum('ab,bc,cd->ad', *ops) + res = einsum("ab,bc,cd->ad", *ops) self.assertAllclose(res, expected) - res = einsum('ab^,b^c,cd2->ad2', *ops) + res = einsum("ab^,b^c,cd2->ad2", *ops) self.assertAllclose(res, expected) - res = einsum('ab,bc,cd', *ops) + res = einsum("ab,bc,cd", *ops) self.assertAllclose(res, expected) - res = einsum('(ab,bc,cd->ad)', *ops) + res = einsum("(ab,bc,cd->ad)", *ops) self.assertAllclose(res, expected) - res = einsum('(ab,bc),cd->ad', *ops) + res = einsum("(ab,bc),cd->ad", *ops) self.assertAllclose(res, expected) - res = einsum('(ab# ,b#c),cd->ad', *ops) + res = einsum("(ab# ,b#c),cd->ad", *ops) self.assertAllclose(res, expected) - res = einsum('(ab,bc),cd', *ops) + res = einsum("(ab,bc),cd", *ops) self.assertAllclose(res, expected) - res = einsum('[(ab,bc),cd]', *ops) + res = einsum("[(ab,bc),cd]", *ops) self.assertAllclose(res, expected) - res = einsum('(ab,bc->ac),cd', *ops) + res = einsum("(ab,bc->ac),cd", *ops) self.assertAllclose(res, expected) - res = einsum('(ab,bc->ac),cd->ad', *ops) + res = einsum("(ab,bc->ac),cd->ad", *ops) self.assertAllclose(res, expected) - res = einsum('ab,(bc,cd)->ad', *ops) + res = einsum("ab,(bc,cd)->ad", *ops) self.assertAllclose(res, expected) - res = einsum('ab,(bc,cd->bd)->ad', *ops) + res = einsum("ab,(bc,cd->bd)->ad", *ops) self.assertAllclose(res, expected) - res = einsum('ab,(bc,cd)->ad', *ops) + res = einsum("ab,(bc,cd)->ad", *ops) self.assertAllclose(res, expected) def test_mmmm(self): - a = np.random.rand(3,4) - b = np.random.rand(4,5) - c = np.random.rand(5,6) - d = np.random.rand(6,7) + a = np.random.rand(3, 4) + b = np.random.rand(4, 5) + c = np.random.rand(5, 6) + d = np.random.rand(6, 7) ops = (a, b, c, d) - expected = np.einsum('ab,bc,cd,de->ae', *ops) + expected = np.einsum("ab,bc,cd,de->ae", *ops) - res = einsum('ab,bc,cd,de->ae', *ops) + res = einsum("ab,bc,cd,de->ae", *ops) self.assertAllclose(res, expected) - res = einsum('ab,bc,cd,de', *ops) + res = einsum("ab,bc,cd,de", *ops) self.assertAllclose(res, expected) - res = einsum('(ab,bc,cd,de->ae)', *ops) + res = einsum("(ab,bc,cd,de->ae)", *ops) self.assertAllclose(res, expected) - res = einsum('(ab,bc),(cd,de)->ae', *ops) + res = einsum("(ab,bc),(cd,de)->ae", *ops) self.assertAllclose(res, expected) - res = einsum('[ab,bc],{cd,de}->ae', *ops) + res = einsum("[ab,bc],{cd,de}->ae", *ops) self.assertAllclose(res, expected) - res = einsum('(ab,bc->ac),(cd,de)->ae', *ops) + res = einsum("(ab,bc->ac),(cd,de)->ae", *ops) self.assertAllclose(res, expected) - res = einsum('(ab,bc->ac),(cd,de)', *ops) + res = einsum("(ab,bc->ac),(cd,de)", *ops) self.assertAllclose(res, expected) - res = einsum('(...b,bc->...c),(cd,de->ce)->...e', *ops) + res = einsum("(...b,bc->...c),(cd,de->ce)->...e", *ops) self.assertAllclose(res, expected) - res = einsum('(ab,bc->ac),(c...,...e->ce)->ae', *ops) + res = einsum("(ab,bc->ac),(c...,...e->ce)->ae", *ops) self.assertAllclose(res, expected) - res = einsum('[(ab,bc),cd],de->ae', *ops) + res = einsum("[(ab,bc),cd],de->ae", *ops) self.assertAllclose(res, expected) def test_mtm(self): - a = np.random.rand(3,4) - b = np.random.rand(4,5,6) - c = np.random.rand(6,7) + a = np.random.rand(3, 4) + b = np.random.rand(4, 5, 6) + c = np.random.rand(6, 7) ops = (a, b, c) - expected = np.einsum('ab,bcd,de->ace', *ops) + expected = np.einsum("ab,bcd,de->ace", *ops) - res = einsum('ab,bcd,de->ace', *ops) + res = einsum("ab,bcd,de->ace", *ops) self.assertAllclose(res, expected) - res = einsum('(ab,bcd),de->ace', *ops) + res = einsum("(ab,bcd),de->ace", *ops) self.assertAllclose(res, expected) - res = einsum('ab,(bcd,de)->ace', *ops) + res = einsum("ab,(bcd,de)->ace", *ops) self.assertAllclose(res, expected) def test_mtm_2(self): - a = np.random.rand(3,4) - b = np.random.rand(4,5,6) - c = np.random.rand(6,7) + a = np.random.rand(3, 4) + b = np.random.rand(4, 5, 6) + c = np.random.rand(6, 7) ops = (a, b, c) - expected = np.einsum('ab,bcd,de->e', *ops) + expected = np.einsum("ab,bcd,de->e", *ops) - res = einsum('ab,bcd,de->e', *ops) + res = einsum("ab,bcd,de->e", *ops) self.assertAllclose(res, expected) - res = einsum('(ab,bcd),de->e', *ops) + res = einsum("(ab,bcd),de->e", *ops) self.assertAllclose(res, expected) - res = einsum('ab,(bcd,de)->e', *ops) + res = einsum("ab,(bcd,de)->e", *ops) self.assertAllclose(res, expected) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/core/wf/test_ebcc_wf.py b/vayesta/tests/core/wf/test_ebcc_wf.py index bdbc963e4..c54dcef1e 100644 --- a/vayesta/tests/core/wf/test_ebcc_wf.py +++ b/vayesta/tests/core/wf/test_ebcc_wf.py @@ -16,12 +16,16 @@ def setUpClass(cls): pytest.skip("Requires ebcc") def _test(self, system, mf, ansatz): - assert(mf in ["rhf", "uhf"]) + assert mf in ["rhf", "uhf"] # Test a complete bath calculation with given ansatz reproduces full calculation. mymf = getattr(getattr(testsystems, system), mf)() - emborig = vayesta.ewf.EWF(mymf, solver=f'EB{ansatz}', bath_options=dict(bathtype='dmet'), - solver_options=dict(solve_lambda=True, store_as_ccsd=True)) + emborig = vayesta.ewf.EWF( + mymf, + solver=f"EB{ansatz}", + bath_options=dict(bathtype="dmet"), + solver_options=dict(solve_lambda=True, store_as_ccsd=True), + ) with emborig.iao_fragmentation() as f: f.add_atomic_fragment([0]) emborig.kernel() @@ -30,7 +34,7 @@ def _test(self, system, mf, ansatz): wf1 = f._results.wf - f.opts.solver_options['store_as_ccsd'] = False + f.opts.solver_options["store_as_ccsd"] = False f._results = None f._hamil = None f.kernel() @@ -56,23 +60,23 @@ def _test(self, system, mf, ansatz): @pytest.mark.fast def test_rccsd_h2(self): - return self._test('h2_ccpvdz', 'rhf', 'CCSD') + return self._test("h2_ccpvdz", "rhf", "CCSD") @pytest.mark.fast def test_rccsd_water_sto3g(self): - return self._test('water_sto3g', 'rhf', 'CCSD') + return self._test("water_sto3g", "rhf", "CCSD") @pytest.mark.fast def test_uccsd_water_sto3g(self): - return self._test('water_sto3g', 'uhf', 'CCSD') + return self._test("water_sto3g", "uhf", "CCSD") def test_uccsd_water_cation_sto3g(self): - return self._test('water_cation_sto3g', 'uhf', 'CCSD') + return self._test("water_cation_sto3g", "uhf", "CCSD") @pytest.mark.fast def test_rccsdt_water_sto3g(self): - return self._test('water_sto3g', 'rhf', 'CCSDT') + return self._test("water_sto3g", "rhf", "CCSDT") @pytest.mark.slow def test_uccsdt_water_cation_sto3g(self): - return self._test('water_cation_sto3g', 'uhf', 'CCSDT') + return self._test("water_cation_sto3g", "uhf", "CCSDT") diff --git a/vayesta/tests/core/wf/test_wf.py b/vayesta/tests/core/wf/test_wf.py index 513fcaf52..92bfaf605 100644 --- a/vayesta/tests/core/wf/test_wf.py +++ b/vayesta/tests/core/wf/test_wf.py @@ -8,8 +8,6 @@ from vayesta.tests import testsystems - - class Test_UFCI_wf_w_dummy(TestCase): @classmethod def setUpClass(cls): @@ -28,42 +26,52 @@ def test_ufci_w_dummy_atomic_fragmentation(self): emb = vayesta.ewf.EWF(self.mf) with emb.iao_fragmentation() as f: - fci_frags = f.add_all_atomic_fragments(solver='FCI', - bath_options=dict(bathtype='full'), store_wf_type='CCSDTQ', auxiliary=True) - ccsd_frag = f.add_full_system(solver='CCSD', bath_options=dict(bathtype='full')) - ccsd_frag.add_external_corrections(fci_frags, correction_type='external', projectors=1, low_level_coul=True) + fci_frags = f.add_all_atomic_fragments( + solver="FCI", bath_options=dict(bathtype="full"), store_wf_type="CCSDTQ", auxiliary=True + ) + ccsd_frag = f.add_full_system(solver="CCSD", bath_options=dict(bathtype="full")) + ccsd_frag.add_external_corrections(fci_frags, correction_type="external", projectors=1, low_level_coul=True) emb.kernel() self.assertAlmostEqual(emb.e_tot, self.get_ufci_ref()) def test_ufci_w_dummy_full_system_fragment(self): emb = vayesta.ewf.EWF(self.mf) - fci_frags=[] + fci_frags = [] with emb.iao_fragmentation() as f: - fci_frags.append(f.add_full_system(solver='FCI', - bath_options=dict(bathtype='full'), store_wf_type='CCSDTQ', auxiliary=True)) - ccsd_frag = f.add_full_system(solver='CCSD', bath_options=dict(bathtype='full')) - ccsd_frag.add_external_corrections(fci_frags, correction_type='external', projectors=1, low_level_coul=True) + fci_frags.append( + f.add_full_system( + solver="FCI", bath_options=dict(bathtype="full"), store_wf_type="CCSDTQ", auxiliary=True + ) + ) + ccsd_frag = f.add_full_system(solver="CCSD", bath_options=dict(bathtype="full")) + ccsd_frag.add_external_corrections(fci_frags, correction_type="external", projectors=1, low_level_coul=True) emb.kernel() self.assertAlmostEqual(emb.e_tot, self.get_ufci_ref()) def test_ufci_w_dummy_regression_full_system_fragment(self): emb = vayesta.ewf.EWF(self.mf) - fci_frags=[] + fci_frags = [] with emb.iao_fragmentation() as f: - fci_frags.append(f.add_full_system(solver='FCI', - bath_options=dict(bathtype='dmet'), store_wf_type='CCSDTQ', auxiliary=True, - solver_options={"init_guess":"mf"})) - ccsd_frag = f.add_full_system(solver='CCSD', bath_options=dict(bathtype='full')) - ccsd_frag.add_external_corrections(fci_frags, correction_type='external', projectors=1, low_level_coul=True) + fci_frags.append( + f.add_full_system( + solver="FCI", + bath_options=dict(bathtype="dmet"), + store_wf_type="CCSDTQ", + auxiliary=True, + solver_options={"init_guess": "mf"}, + ) + ) + ccsd_frag = f.add_full_system(solver="CCSD", bath_options=dict(bathtype="full")) + ccsd_frag.add_external_corrections(fci_frags, correction_type="external", projectors=1, low_level_coul=True) emb.kernel() self.assertAlmostEqual(emb.e_tot, -10.290621999634174) -class Test_DM(TestCase): - solver = 'CCSD' +class Test_DM(TestCase): + solver = "CCSD" solver_opts = dict(conv_tol=1e-10, conv_tol_normt=1e-8) @classmethod @@ -79,8 +87,12 @@ def tearDownClass(cls): @classmethod @cache def frag(cls): - emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(bathtype='full'), solver=cls.solver, - solver_options=dict(**cls.solver_opts, solve_lambda=True)) + emb = vayesta.ewf.EWF( + cls.mf, + bath_options=dict(bathtype="full"), + solver=cls.solver, + solver_options=dict(**cls.solver_opts, solve_lambda=True), + ) with emb.iao_fragmentation() as f: frag = f.add_atomic_fragment(list(range(emb.mol.natm))) emb.kernel() @@ -97,22 +109,22 @@ def test_dm1(self): dm1_ref = self.get_dm1_ref(ao_repr=True) dm1 = frag.results.wf.make_rdm1(ao_basis=True) self.assertAllclose(dm1, dm1_ref) - #dm1_ref = self.get_dm1_ref(ao_repr=True, with_mf=False) - #dm1 = frag.results.wf.make_rdm1(ao_basis=True, with_mf=False) - #self.assertAllclose(dm1, dm1_ref) + # dm1_ref = self.get_dm1_ref(ao_repr=True, with_mf=False) + # dm1 = frag.results.wf.make_rdm1(ao_basis=True, with_mf=False) + # self.assertAllclose(dm1, dm1_ref) def test_dm2(self): frag = self.frag() dm2_ref = self.get_dm2_ref(ao_repr=True) dm2 = frag.results.wf.make_rdm2(ao_basis=True) self.assertAllclose(dm2, dm2_ref) - #dm2_ref = self.get_dm2_ref(ao_repr=True, with_dm1=False) - #dm2 = frag.results.wf.make_rdm2(ao_basis=True, with_dm1=False) - #self.assertAllclose(dm2, dm2_ref) + # dm2_ref = self.get_dm2_ref(ao_repr=True, with_dm1=False) + # dm2 = frag.results.wf.make_rdm2(ao_basis=True, with_dm1=False) + # self.assertAllclose(dm2, dm2_ref) -class Test_DM_MP2(Test_DM): - solver = 'MP2' +class Test_DM_MP2(Test_DM): + solver = "MP2" solver_opts = {} @classmethod @@ -120,19 +132,19 @@ def setUpClass(cls): cls.mf = testsystems.water_631g.rhf() cls.cc = testsystems.water_631g.rmp2() - #def test_dm2(self): + # def test_dm2(self): # pass -class Test_DM_UHF(Test_DM): +class Test_DM_UHF(Test_DM): @classmethod def setUpClass(cls): cls.mf = testsystems.water_cation_631g.uhf() cls.cc = testsystems.water_cation_631g.uccsd() -class Test_DM_FCI(Test_DM): - solver = 'FCI' +class Test_DM_FCI(Test_DM): + solver = "FCI" solver_opts = dict(conv_tol=1e-14) @classmethod @@ -151,36 +163,40 @@ def get_dm2_ref(self, ao_repr=False): args = (self.cc.ci, self.mf.mol.nao, self.mf.mol.nelectron) dm2_ref = self.cc.make_rdm12(*args)[1] if ao_repr: - dm2_ref = einsum('ijkl,ai,bj,ck,dl->abcd', dm2_ref, *(4*[self.mf.mo_coeff])) + dm2_ref = einsum("ijkl,ai,bj,ck,dl->abcd", dm2_ref, *(4 * [self.mf.mo_coeff])) return dm2_ref -class Test_DM_FCI_UHF(Test_DM_FCI): +class Test_DM_FCI_UHF(Test_DM_FCI): @classmethod def setUpClass(cls): cls.mf = testsystems.water_cation_sto3g.uhf() cls.cc = testsystems.water_cation_sto3g.ufci() def get_dm1_ref(self, ao_repr=False): - nelec = (np.count_nonzero(self.mf.mo_occ[0]>0), np.count_nonzero(self.mf.mo_occ[1]>0)) + nelec = (np.count_nonzero(self.mf.mo_occ[0] > 0), np.count_nonzero(self.mf.mo_occ[1] > 0)) args = (self.cc.ci, self.mf.mol.nao, nelec) dm1_ref = self.cc.make_rdm1s(*args) if ao_repr: - dm1_ref = (np.linalg.multi_dot((self.mf.mo_coeff[0], dm1_ref[0], self.mf.mo_coeff[0].T)), - np.linalg.multi_dot((self.mf.mo_coeff[1], dm1_ref[1], self.mf.mo_coeff[1].T))) + dm1_ref = ( + np.linalg.multi_dot((self.mf.mo_coeff[0], dm1_ref[0], self.mf.mo_coeff[0].T)), + np.linalg.multi_dot((self.mf.mo_coeff[1], dm1_ref[1], self.mf.mo_coeff[1].T)), + ) return dm1_ref def get_dm2_ref(self, ao_repr=False): - nelec = (np.count_nonzero(self.mf.mo_occ[0]>0), np.count_nonzero(self.mf.mo_occ[1]>0)) + nelec = (np.count_nonzero(self.mf.mo_occ[0] > 0), np.count_nonzero(self.mf.mo_occ[1] > 0)) args = (self.cc.ci, self.mf.mol.nao, nelec) dm2_ref = self.cc.make_rdm12s(*args)[1] if ao_repr: - dm2_ref = (einsum('ijkl,ai,bj,ck,dl->abcd', dm2_ref[0], *(4*[self.mf.mo_coeff[0]])), - einsum('ijkl,ai,bj,ck,dl->abcd', dm2_ref[1], *(2*[self.mf.mo_coeff[0]] + 2*[self.mf.mo_coeff[1]])), - einsum('ijkl,ai,bj,ck,dl->abcd', dm2_ref[2], *(4*[self.mf.mo_coeff[1]]))) + dm2_ref = ( + einsum("ijkl,ai,bj,ck,dl->abcd", dm2_ref[0], *(4 * [self.mf.mo_coeff[0]])), + einsum("ijkl,ai,bj,ck,dl->abcd", dm2_ref[1], *(2 * [self.mf.mo_coeff[0]] + 2 * [self.mf.mo_coeff[1]])), + einsum("ijkl,ai,bj,ck,dl->abcd", dm2_ref[2], *(4 * [self.mf.mo_coeff[1]])), + ) return dm2_ref -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/dmet/test_hubbard.py b/vayesta/tests/dmet/test_hubbard.py index 3d2eaa2e8..225583781 100644 --- a/vayesta/tests/dmet/test_hubbard.py +++ b/vayesta/tests/dmet/test_hubbard.py @@ -18,92 +18,104 @@ def setUpClass(cls): pytest.skip("Requires cvxpy") def _test_converged(self, emb, known_values=None): - """Test that the DMET has converged. - """ + """Test that the DMET has converged.""" self.assertTrue(emb.converged) def _test_energy(self, emb, known_values): - """Test that the energy matches a known value. - """ - self.assertAlmostEqual(emb.e_tot, known_values['e_tot'], self.PLACES_ENERGY) + """Test that the energy matches a known value.""" + self.assertAlmostEqual(emb.e_tot, known_values["e_tot"], self.PLACES_ENERGY) def test_6_u0_1imp(self): - """Tests for N=6 U=0 Hubbard model with single site impurities. - """ - emb = dmet.DMET(testsystems.hubb_6_u0.rhf(), solver='FCI', charge_consistent=False, - conv_tol=self.CONV_TOL, maxiter=50) + """Tests for N=6 U=0 Hubbard model with single site impurities.""" + emb = dmet.DMET( + testsystems.hubb_6_u0.rhf(), solver="FCI", charge_consistent=False, conv_tol=self.CONV_TOL, maxiter=50 + ) with emb.site_fragmentation() as f: frag = f.add_atomic_fragment(0) frag.add_tsymmetric_fragments(tvecs=[6, 1, 1]) emb.kernel() - known_values = {'e_tot': -8.0} + known_values = {"e_tot": -8.0} self._test_converged(emb) self._test_energy(emb, known_values) def test_10_u2_2imp(self): - """Tests for N=10 U=2 Hubbard model with double site impurities. - """ - emb = dmet.DMET(testsystems.hubb_10_u2.rhf(), solver='FCI', charge_consistent=False, - conv_tol=self.CONV_TOL, maxiter=50) + """Tests for N=10 U=2 Hubbard model with double site impurities.""" + emb = dmet.DMET( + testsystems.hubb_10_u2.rhf(), solver="FCI", charge_consistent=False, conv_tol=self.CONV_TOL, maxiter=50 + ) with emb.site_fragmentation() as f: frag = f.add_atomic_fragment([0, 1]) frag.add_tsymmetric_fragments(tvecs=[5, 1, 1]) emb.kernel() - known_values = {'e_tot': -8.741824246073978} + known_values = {"e_tot": -8.741824246073978} self._test_converged(emb) self._test_energy(emb, known_values) def test_6x6_u0_1x1imp(self): - """Tests for 6x6 U=0 Hubbard model with single site impurities. - """ - emb = dmet.DMET(testsystems.hubb_6x6_u0_1x1imp.rhf(), solver='FCI', charge_consistent=False, - conv_tol=self.CONV_TOL, maxiter=50) + """Tests for 6x6 U=0 Hubbard model with single site impurities.""" + emb = dmet.DMET( + testsystems.hubb_6x6_u0_1x1imp.rhf(), + solver="FCI", + charge_consistent=False, + conv_tol=self.CONV_TOL, + maxiter=50, + ) with emb.site_fragmentation() as f: frag = f.add_atomic_fragment([0]) frag.add_tsymmetric_fragments(tvecs=[6, 6, 1]) emb.kernel() - known_values = {'e_tot': -56.0} + known_values = {"e_tot": -56.0} self._test_converged(emb) self._test_energy(emb, known_values) def test_6x6_u6_1x1imp(self): - """Tests for 6x6 U=6 Hubbard model with single site impurities. - """ - emb = dmet.DMET(testsystems.hubb_6x6_u6_1x1imp.rhf(), solver='FCI', charge_consistent=False, - conv_tol=self.CONV_TOL, maxiter=50, solver_options={"conv_tol": 1e-12}) + """Tests for 6x6 U=6 Hubbard model with single site impurities.""" + emb = dmet.DMET( + testsystems.hubb_6x6_u6_1x1imp.rhf(), + solver="FCI", + charge_consistent=False, + conv_tol=self.CONV_TOL, + maxiter=50, + solver_options={"conv_tol": 1e-12}, + ) with emb.site_fragmentation() as f: frag = f.add_atomic_fragment([0]) frag.add_tsymmetric_fragments(tvecs=[6, 6, 1]) emb.kernel() - known_values = {'e_tot': -41.040841420346695 } + known_values = {"e_tot": -41.040841420346695} self._test_converged(emb) self._test_energy(emb, known_values) def test_8x8_u2_2x2imp(self): - """Tests for 8x8 U=2 Hubbard model with 2x2 impurities. - """ - emb = dmet.DMET(testsystems.hubb_8x8_u2_2x2imp.rhf(), solver='FCI', charge_consistent=False, - conv_tol=self.CONV_TOL, maxiter=100, solver_options={"conv_tol": 1e-12}) + """Tests for 8x8 U=2 Hubbard model with 2x2 impurities.""" + emb = dmet.DMET( + testsystems.hubb_8x8_u2_2x2imp.rhf(), + solver="FCI", + charge_consistent=False, + conv_tol=self.CONV_TOL, + maxiter=100, + solver_options={"conv_tol": 1e-12}, + ) with emb.site_fragmentation() as f: frag = f.add_atomic_fragment([0, 1, 2, 3]) frag.add_tsymmetric_fragments(tvecs=[4, 4, 1]) emb.kernel() - known_values = {'e_tot': -85.02643076273672} + known_values = {"e_tot": -85.02643076273672} self._test_converged(emb) self._test_energy(emb, known_values) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/dmet/test_molecule.py b/vayesta/tests/dmet/test_molecule.py index 39db42bac..2d7f3335a 100644 --- a/vayesta/tests/dmet/test_molecule.py +++ b/vayesta/tests/dmet/test_molecule.py @@ -8,6 +8,7 @@ FCI_SOLVER_OPTS = dict(davidson_only=False, init_guess="mf") + class MoleculeTest(TestCase): PLACES_ENERGY = 7 CONV_TOL = 1e-9 @@ -25,95 +26,119 @@ def tearDownClass(cls): del cls.mf def _test_converged(self, emb, known_values=None): - """Test that the DMET has converged. - """ + """Test that the DMET has converged.""" self.assertTrue(emb.converged) def _test_energy(self, emb, known_values): - """Test that the energy matches a known value. - """ - self.assertAlmostEqual(emb.e_tot, known_values['e_tot'], self.PLACES_ENERGY) + """Test that the energy matches a known value.""" + self.assertAlmostEqual(emb.e_tot, known_values["e_tot"], self.PLACES_ENERGY) def test_cc(self): - """Test H6 STO-6G with FCI solver, IAO fragmentation and charge consistency. - """ - emb = dmet.DMET(testsystems.h6_sto6g.rhf(), solver='FCI', charge_consistent=True, - bath_options=dict(bathtype='dmet'), conv_tol=self.CONV_TOL, solver_options=FCI_SOLVER_OPTS) + """Test H6 STO-6G with FCI solver, IAO fragmentation and charge consistency.""" + emb = dmet.DMET( + testsystems.h6_sto6g.rhf(), + solver="FCI", + charge_consistent=True, + bath_options=dict(bathtype="dmet"), + conv_tol=self.CONV_TOL, + solver_options=FCI_SOLVER_OPTS, + ) with emb.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) f.add_atomic_fragment([2, 3]) f.add_atomic_fragment([4, 5]) emb.kernel() - known_values = {'e_tot': -3.259355757394294} + known_values = {"e_tot": -3.259355757394294} self._test_converged(emb) self._test_energy(emb, known_values) def test_nocc(self): - """Test H6 STO-6G with FCI solver, IAO fragmentation and no charge consistency. - """ - emb = dmet.DMET(testsystems.h6_sto6g.rhf(), solver='FCI', charge_consistent=False, - bath_options=dict(bathtype='dmet'), conv_tol=self.CONV_TOL, solver_options=FCI_SOLVER_OPTS) + """Test H6 STO-6G with FCI solver, IAO fragmentation and no charge consistency.""" + emb = dmet.DMET( + testsystems.h6_sto6g.rhf(), + solver="FCI", + charge_consistent=False, + bath_options=dict(bathtype="dmet"), + conv_tol=self.CONV_TOL, + solver_options=FCI_SOLVER_OPTS, + ) with emb.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) f.add_atomic_fragment([2, 3]) f.add_atomic_fragment([4, 5]) emb.kernel() - known_values = {'e_tot': -3.2593557575050305} + known_values = {"e_tot": -3.2593557575050305} self._test_converged(emb) self._test_energy(emb, known_values) def test_nocc_ccsd(self): - """Test H6 STO-6G with FCI solver, IAO fragmentation and no charge consistency. - """ - emb = dmet.DMET(testsystems.h6_sto6g.rhf(), solver='CCSD', charge_consistent=False, - bath_options=dict(bathtype='dmet'), conv_tol=self.CONV_TOL) + """Test H6 STO-6G with FCI solver, IAO fragmentation and no charge consistency.""" + emb = dmet.DMET( + testsystems.h6_sto6g.rhf(), + solver="CCSD", + charge_consistent=False, + bath_options=dict(bathtype="dmet"), + conv_tol=self.CONV_TOL, + ) with emb.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) f.add_atomic_fragment([2, 3]) f.add_atomic_fragment([4, 5]) emb.kernel() - known_values = {'e_tot': -3.2593387676678667} + known_values = {"e_tot": -3.2593387676678667} self._test_converged(emb) self._test_energy(emb, known_values) def test_renorm_interaction(self): - emb = dmet.DMET(testsystems.h6_sto6g_df.uhf(), solver='FCI', charge_consistent=False, - bath_options=dict(bathtype='dmet'), conv_tol=self.CONV_TOL, oneshot=True, - screening='mrpa', solver_options=FCI_SOLVER_OPTS) + emb = dmet.DMET( + testsystems.h6_sto6g_df.uhf(), + solver="FCI", + charge_consistent=False, + bath_options=dict(bathtype="dmet"), + conv_tol=self.CONV_TOL, + oneshot=True, + screening="mrpa", + solver_options=FCI_SOLVER_OPTS, + ) with emb.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) f.add_atomic_fragment([2, 3]) f.add_atomic_fragment([4, 5]) emb.kernel() - known_values = {'e_tot': -3.2536357694705185} + known_values = {"e_tot": -3.2536357694705185} self._test_energy(emb, known_values) def test_full_bath(self): - """Test H6 STO-6G with FCI solver, IAO fragmentation and complete bath. - """ - emb = dmet.DMET(testsystems.h6_sto6g.rhf(), solver='FCI', charge_consistent=False, - bath_options=dict(bathtype='full'), conv_tol=self.CONV_TOL, solver_options=FCI_SOLVER_OPTS) + """Test H6 STO-6G with FCI solver, IAO fragmentation and complete bath.""" + emb = dmet.DMET( + testsystems.h6_sto6g.rhf(), + solver="FCI", + charge_consistent=False, + bath_options=dict(bathtype="full"), + conv_tol=self.CONV_TOL, + solver_options=FCI_SOLVER_OPTS, + ) with emb.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) f.add_atomic_fragment([2, 3]) f.add_atomic_fragment([4, 5]) emb.kernel() - known_values = {'e_tot': -3.2587710893946102} + known_values = {"e_tot": -3.2587710893946102} self._test_converged(emb) self._test_energy(emb, known_values) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/edmet/test_dfedmet_hubbard.py b/vayesta/tests/edmet/test_dfedmet_hubbard.py index b0ff9fb94..c8065f4d5 100644 --- a/vayesta/tests/edmet/test_dfedmet_hubbard.py +++ b/vayesta/tests/edmet/test_dfedmet_hubbard.py @@ -18,10 +18,9 @@ def setUpClass(cls): pytest.skip("Requires cvxpy") def _test_energy(self, emb, dfedmet, known_values): - """Test that the energy contributions match a known value and the non-density fitted value. - """ - self.assertAlmostEqual(dfedmet.e_tot - dfedmet.e_nonlocal, known_values['e_clus'], self.PLACES_ENERGY) - self.assertAlmostEqual(dfedmet.e_nonlocal, known_values['e_nl'], self.PLACES_ENERGY) + """Test that the energy contributions match a known value and the non-density fitted value.""" + self.assertAlmostEqual(dfedmet.e_tot - dfedmet.e_nonlocal, known_values["e_clus"], self.PLACES_ENERGY) + self.assertAlmostEqual(dfedmet.e_nonlocal, known_values["e_nl"], self.PLACES_ENERGY) self.assertAlmostEqual(emb.e_tot, dfedmet.e_tot, self.PLACES_ENERGY) def test_14_upoint4_2imp_4occ(self): @@ -31,10 +30,10 @@ def test_14_upoint4_2imp_4occ(self): """ emb = edmet.EDMET( testsystems.hubb_14_u4.rhf(), - solver='FCI', + solver="FCI", solver_options={"max_boson_occ": 2}, maxiter=1, - max_elec_err=1e-6 + max_elec_err=1e-6, ) emb.symmetry.set_translations([7, 1, 1]) with emb.site_fragmentation() as f: @@ -43,21 +42,21 @@ def test_14_upoint4_2imp_4occ(self): dfedmet = edmet.EDMET( testsystems.hubb_14_u4_df.rhf(), - solver='FCI', - solver_options={"max_boson_occ": 2}, - maxiter=1, - max_elec_err=1e-6 + solver="FCI", + solver_options={"max_boson_occ": 2}, + maxiter=1, + max_elec_err=1e-6, ) dfedmet.symmetry.set_translations([7, 1, 1]) with dfedmet.site_fragmentation() as f: f.add_atomic_fragment([0, 1]) dfedmet.kernel() # This is the energy without the nonlocal correlation energy RPA correction. - known_values = {'e_clus': -8.01015928145061, 'e_nl': -0.5338487284590787} + known_values = {"e_clus": -8.01015928145061, "e_nl": -0.5338487284590787} self._test_energy(emb, dfedmet, known_values) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/edmet/test_dfedmet_molecule.py b/vayesta/tests/edmet/test_dfedmet_molecule.py index 87b7472e1..b7346ec67 100644 --- a/vayesta/tests/edmet/test_dfedmet_molecule.py +++ b/vayesta/tests/edmet/test_dfedmet_molecule.py @@ -7,6 +7,7 @@ from vayesta.tests.common import TestCase from vayesta.tests import testsystems + class MolecularDFEDMETTest(TestCase): ENERGY_PLACES = 8 CONV_TOL = 1e-9 @@ -19,19 +20,18 @@ def setUpClass(cls): pytest.skip("Requires cvxpy") def _test_energy(self, emb, known_values): - """Tests that the energy matfhes a known values. - """ - self.assertAlmostEqual(emb.e_tot, known_values['e_tot'], self.ENERGY_PLACES) + """Tests that the energy matfhes a known values.""" + self.assertAlmostEqual(emb.e_tot, known_values["e_tot"], self.ENERGY_PLACES) def test_h6_sto6g_FCI_IAO_1occ(self): emb = edmet.EDMET( - testsystems.h6_sto6g_df.rhf(), - solver='FCI', - solver_options={"max_boson_occ":1, "conv_tol":1e-12}, - conv_tol=self.CONV_TOL, - bosonic_interaction="direct", - oneshot=True, - make_dd_moments=False, + testsystems.h6_sto6g_df.rhf(), + solver="FCI", + solver_options={"max_boson_occ": 1, "conv_tol": 1e-12}, + conv_tol=self.CONV_TOL, + bosonic_interaction="direct", + oneshot=True, + make_dd_moments=False, ) with emb.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) @@ -39,19 +39,19 @@ def test_h6_sto6g_FCI_IAO_1occ(self): f.add_atomic_fragment([4, 5]) emb.kernel() - known_values = {'e_tot': -3.268863371644988} + known_values = {"e_tot": -3.268863371644988} self._test_energy(emb, known_values) def test_h6_sto6g_FCI_IAO_2occ(self): emb = edmet.EDMET( - testsystems.h6_sto6g_df.rhf(), - solver='FCI', - solver_options={"max_boson_occ":2, "conv_tol":1e-12}, - conv_tol=self.CONV_TOL, - bosonic_interaction="direct", - oneshot=True, - make_dd_moments=False, + testsystems.h6_sto6g_df.rhf(), + solver="FCI", + solver_options={"max_boson_occ": 2, "conv_tol": 1e-12}, + conv_tol=self.CONV_TOL, + bosonic_interaction="direct", + oneshot=True, + make_dd_moments=False, ) with emb.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) @@ -60,13 +60,13 @@ def test_h6_sto6g_FCI_IAO_2occ(self): emb.kernel() uemb = edmet.EDMET( - testsystems.h6_sto6g_df.uhf(), - solver='FCI', - solver_options={"max_boson_occ":2, "conv_tol":1e-12}, - conv_tol=self.CONV_TOL, - bosonic_interaction="direct", - oneshot=True, - make_dd_moments=False, + testsystems.h6_sto6g_df.uhf(), + solver="FCI", + solver_options={"max_boson_occ": 2, "conv_tol": 1e-12}, + conv_tol=self.CONV_TOL, + bosonic_interaction="direct", + oneshot=True, + make_dd_moments=False, ) with uemb.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) @@ -74,7 +74,7 @@ def test_h6_sto6g_FCI_IAO_2occ(self): f.add_atomic_fragment([4, 5]) uemb.kernel() - known_values = {'e_tot': -3.2689751906025126} + known_values = {"e_tot": -3.2689751906025126} self._test_energy(emb, known_values) self._test_energy(uemb, known_values) @@ -82,36 +82,36 @@ def test_h6_sto6g_FCI_IAO_2occ(self): @unittest.skipIf(vayesta.ebcc is None, "EBCC installation not found.") def test_h2o_ccpvdz_EBCCSD_IAO(self): emb = edmet.EDMET( - testsystems.water_ccpvdz_df.rhf(), - solver='CCSD-S-1-1', - conv_tol=self.CONV_TOL, - oneshot=True, - make_dd_moments=False, - bosonic_interaction="direct", + testsystems.water_ccpvdz_df.rhf(), + solver="CCSD-S-1-1", + conv_tol=self.CONV_TOL, + oneshot=True, + make_dd_moments=False, + bosonic_interaction="direct", ) - with emb.iao_fragmentation(minao='minao') as f: + with emb.iao_fragmentation(minao="minao") as f: f.add_all_atomic_fragments() emb.kernel() - known_values = {'e_tot': -76.26516984456478} + known_values = {"e_tot": -76.26516984456478} self._test_energy(emb, known_values) uemb = edmet.EDMET( - testsystems.water_ccpvdz_df.uhf(), - solver='CCSD-S-1-1', - conv_tol=self.CONV_TOL, - oneshot=True, - make_dd_moments=False, - bosonic_interaction="direct", + testsystems.water_ccpvdz_df.uhf(), + solver="CCSD-S-1-1", + conv_tol=self.CONV_TOL, + oneshot=True, + make_dd_moments=False, + bosonic_interaction="direct", ) - with uemb.iao_fragmentation(minao='minao') as f: + with uemb.iao_fragmentation(minao="minao") as f: f.add_all_atomic_fragments() uemb.kernel() self._test_energy(uemb, known_values) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/edmet/test_edmet_hubbard.py b/vayesta/tests/edmet/test_edmet_hubbard.py index eb9321d3a..127fd077b 100644 --- a/vayesta/tests/edmet/test_edmet_hubbard.py +++ b/vayesta/tests/edmet/test_edmet_hubbard.py @@ -18,18 +18,16 @@ def setUpClass(cls): pytest.skip("Requires cvxpy") def _test_energy(self, emb, known_values): - """Test that the energy matches a known value. - """ - self.assertAlmostEqual(emb.e_tot, known_values['e_tot'], self.PLACES_ENERGY) + """Test that the energy matches a known value.""" + self.assertAlmostEqual(emb.e_tot, known_values["e_tot"], self.PLACES_ENERGY) def test_6_u0_1imp_1occ(self): - """Tests for N=6 U=0 Hubbard model with single site impurities. - """ + """Tests for N=6 U=0 Hubbard model with single site impurities.""" emb = edmet.EDMET( - testsystems.hubb_6_u0.rhf(), - solver='FCI', - solver_options={"max_boson_occ": 1}, + testsystems.hubb_6_u0.rhf(), + solver="FCI", + solver_options={"max_boson_occ": 1}, ) emb.symmetry.set_translations([6, 1, 1]) @@ -37,57 +35,54 @@ def test_6_u0_1imp_1occ(self): f.add_atomic_fragment([0]) emb.kernel() - known_values = {'e_tot': -8.0} + known_values = {"e_tot": -8.0} self._test_energy(emb, known_values) def test_6_u0_2imp_6occ(self): - """Tests for N=6 U=0 Hubbard model with double site impurities. - """ + """Tests for N=6 U=0 Hubbard model with double site impurities.""" emb = edmet.EDMET( - testsystems.hubb_6_u0.rhf(), - solver='FCI', - solver_options={"max_boson_occ": 6}, + testsystems.hubb_6_u0.rhf(), + solver="FCI", + solver_options={"max_boson_occ": 6}, ) emb.symmetry.set_translations([3, 1, 1]) with emb.site_fragmentation() as f: f.add_atomic_fragment([0, 1]) emb.kernel() - known_values = {'e_tot': -8.0} + known_values = {"e_tot": -8.0} self._test_energy(emb, known_values) def test_10_u2_2imp_2occ(self): - """Tests for N=10 U=2 Hubbard model with double site impurities. - """ + """Tests for N=10 U=2 Hubbard model with double site impurities.""" emb = edmet.EDMET( - testsystems.hubb_10_u2.rhf(), - solver='FCI', - solver_options={"max_boson_occ": 2}, - bosonic_interaction="direct", - oneshot=True, - make_dd_moments=False, + testsystems.hubb_10_u2.rhf(), + solver="FCI", + solver_options={"max_boson_occ": 2}, + bosonic_interaction="direct", + oneshot=True, + make_dd_moments=False, ) emb.symmetry.set_translations([5, 1, 1]) with emb.site_fragmentation() as f: f.add_atomic_fragment([0, 1]) emb.kernel() - known_values = {'e_tot':-8.793485086132375} + known_values = {"e_tot": -8.793485086132375} self._test_energy(emb, known_values) def test_6x6_u0_1x1imp_2occ(self): - """Tests for 6x6 U=0 Hubbard model with single site impurities. - """ + """Tests for 6x6 U=0 Hubbard model with single site impurities.""" emb = edmet.EDMET( - testsystems.hubb_6x6_u0_1x1imp.rhf(), - solver='FCI', - solver_options={"max_boson_occ": 2}, + testsystems.hubb_6x6_u0_1x1imp.rhf(), + solver="FCI", + solver_options={"max_boson_occ": 2}, ) emb.symmetry.set_translations([6, 6, 1]) @@ -95,32 +90,31 @@ def test_6x6_u0_1x1imp_2occ(self): f.add_atomic_fragment([0]) emb.kernel() - known_values = {'e_tot': -56.0} + known_values = {"e_tot": -56.0} self._test_energy(emb, known_values) def test_6x6_u6_1x1imp_2occ(self): - """Tests for 6x6 U=6 Hubbard model with single site impurities. - """ + """Tests for 6x6 U=6 Hubbard model with single site impurities.""" emb = edmet.EDMET( - testsystems.hubb_6x6_u6_1x1imp.rhf(), - solver='FCI', - solver_options={"max_boson_occ": 2}, - bosonic_interaction="direct", - oneshot=True, - make_dd_moments=False, + testsystems.hubb_6x6_u6_1x1imp.rhf(), + solver="FCI", + solver_options={"max_boson_occ": 2}, + bosonic_interaction="direct", + oneshot=True, + make_dd_moments=False, ) emb.symmetry.set_translations([6, 6, 1]) with emb.site_fragmentation() as f: f.add_atomic_fragment([0]) emb.kernel() - known_values = {'e_tot':-49.255623407653644} + known_values = {"e_tot": -49.255623407653644} self._test_energy(emb, known_values) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/edmet/test_edmet_molecule.py b/vayesta/tests/edmet/test_edmet_molecule.py index 215c71ddf..57959a55d 100644 --- a/vayesta/tests/edmet/test_edmet_molecule.py +++ b/vayesta/tests/edmet/test_edmet_molecule.py @@ -20,19 +20,18 @@ def setUpClass(cls): pytest.skip("Requires cvxpy.") def _test_energy(self, emb, known_values): - """Tests that the energy matfhes a known values. - """ - self.assertAlmostEqual(emb.e_tot, known_values['e_tot'], self.ENERGY_PLACES) + """Tests that the energy matfhes a known values.""" + self.assertAlmostEqual(emb.e_tot, known_values["e_tot"], self.ENERGY_PLACES) def test_h6_sto6g_FCI_IAO_1occ(self): emb = edmet.EDMET( - testsystems.h6_sto6g.rhf(), - solver='FCI', - solver_options={"max_boson_occ":1}, - conv_tol=self.CONV_TOL, - bosonic_interaction="direct", - oneshot=True, - make_dd_moments=False, + testsystems.h6_sto6g.rhf(), + solver="FCI", + solver_options={"max_boson_occ": 1}, + conv_tol=self.CONV_TOL, + bosonic_interaction="direct", + oneshot=True, + make_dd_moments=False, ) with emb.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) @@ -41,13 +40,13 @@ def test_h6_sto6g_FCI_IAO_1occ(self): emb.kernel() uemb = edmet.EDMET( - testsystems.h6_sto6g.uhf(), - solver='FCI', - solver_options={"max_boson_occ":1}, - conv_tol=self.CONV_TOL, - bosonic_interaction="direct", - oneshot=True, - make_dd_moments=False, + testsystems.h6_sto6g.uhf(), + solver="FCI", + solver_options={"max_boson_occ": 1}, + conv_tol=self.CONV_TOL, + bosonic_interaction="direct", + oneshot=True, + make_dd_moments=False, ) with uemb.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) @@ -55,17 +54,17 @@ def test_h6_sto6g_FCI_IAO_1occ(self): f.add_atomic_fragment([4, 5]) uemb.kernel() - known_values = {'e_tot': -3.2687823852000726} + known_values = {"e_tot": -3.2687823852000726} self._test_energy(emb, known_values) self._test_energy(uemb, known_values) def test_h6_sto6g_FCI_IAO_2occ(self): emb = edmet.EDMET( - testsystems.h6_sto6g.rhf(), - solver='FCI', - solver_options={"max_boson_occ":2}, - conv_tol=self.CONV_TOL, + testsystems.h6_sto6g.rhf(), + solver="FCI", + solver_options={"max_boson_occ": 2}, + conv_tol=self.CONV_TOL, ) with emb.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) @@ -74,13 +73,13 @@ def test_h6_sto6g_FCI_IAO_2occ(self): emb.kernel() uemb = edmet.EDMET( - testsystems.h6_sto6g.uhf(), - solver='FCI', - solver_options={"max_boson_occ":2}, - conv_tol=self.CONV_TOL, - bosonic_interaction="direct", - oneshot=True, - make_dd_moments=False, + testsystems.h6_sto6g.uhf(), + solver="FCI", + solver_options={"max_boson_occ": 2}, + conv_tol=self.CONV_TOL, + bosonic_interaction="direct", + oneshot=True, + make_dd_moments=False, ) with uemb.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) @@ -88,21 +87,20 @@ def test_h6_sto6g_FCI_IAO_2occ(self): f.add_atomic_fragment([4, 5]) uemb.kernel() - - known_values = {'e_tot': -3.268894063433855} + known_values = {"e_tot": -3.268894063433855} self._test_energy(emb, known_values) self._test_energy(uemb, known_values) def test_h6_sto6g_FCI_IAO_2occ(self): emb = edmet.EDMET( - testsystems.h6_sto6g.rhf(), - solver='FCI', - solver_options={"max_boson_occ":2}, - conv_tol=self.CONV_TOL, - bosonic_interaction="direct", - oneshot=True, - make_dd_moments=False, + testsystems.h6_sto6g.rhf(), + solver="FCI", + solver_options={"max_boson_occ": 2}, + conv_tol=self.CONV_TOL, + bosonic_interaction="direct", + oneshot=True, + make_dd_moments=False, ) with emb.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) @@ -110,18 +108,18 @@ def test_h6_sto6g_FCI_IAO_2occ(self): f.add_atomic_fragment([4, 5]) emb.kernel() - known_values = {'e_tot': -3.268894063433855} + known_values = {"e_tot": -3.268894063433855} self._test_energy(emb, known_values) uemb = edmet.EDMET( - testsystems.h6_sto6g.uhf(), - solver='FCI', - solver_options={"max_boson_occ":2}, - conv_tol=self.CONV_TOL, - bosonic_interaction="direct", - oneshot=True, - make_dd_moments=False, + testsystems.h6_sto6g.uhf(), + solver="FCI", + solver_options={"max_boson_occ": 2}, + conv_tol=self.CONV_TOL, + bosonic_interaction="direct", + oneshot=True, + make_dd_moments=False, ) with uemb.iao_fragmentation() as f: f.add_atomic_fragment([0, 1]) @@ -134,36 +132,36 @@ def test_h6_sto6g_FCI_IAO_2occ(self): @unittest.skipIf(vayesta.ebcc is None, "EBCC installation not found.") def test_h2o_ccpvdz_EBCCSD_IAO_2occ(self): emb = edmet.EDMET( - testsystems.water_ccpvdz.rhf(), - solver='CCSD-S-1-1', - conv_tol=self.CONV_TOL, - bosonic_interaction="direct", - oneshot=True, - make_dd_moments=False, + testsystems.water_ccpvdz.rhf(), + solver="CCSD-S-1-1", + conv_tol=self.CONV_TOL, + bosonic_interaction="direct", + oneshot=True, + make_dd_moments=False, ) - with emb.iao_fragmentation(minao='minao') as f: + with emb.iao_fragmentation(minao="minao") as f: f.add_all_atomic_fragments() emb.kernel() - known_values = {'e_tot': -76.26535574691708} + known_values = {"e_tot": -76.26535574691708} self._test_energy(emb, known_values) uemb = edmet.EDMET( - testsystems.water_ccpvdz.uhf(), - solver='CCSD-S-1-1', - conv_tol=self.CONV_TOL, - bosonic_interaction="direct", - oneshot=True, - make_dd_moments=False, + testsystems.water_ccpvdz.uhf(), + solver="CCSD-S-1-1", + conv_tol=self.CONV_TOL, + bosonic_interaction="direct", + oneshot=True, + make_dd_moments=False, ) - with uemb.iao_fragmentation(minao='minao') as f: + with uemb.iao_fragmentation(minao="minao") as f: f.add_all_atomic_fragments() uemb.kernel() self._test_energy(uemb, known_values) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_bosonic_bath.py b/vayesta/tests/ewf/test_bosonic_bath.py index a599f4058..3ca7fa8b9 100644 --- a/vayesta/tests/ewf/test_bosonic_bath.py +++ b/vayesta/tests/ewf/test_bosonic_bath.py @@ -7,7 +7,6 @@ class BosonicBathTests(TestCase): - @classmethod def setUpClass(cls): try: @@ -16,10 +15,14 @@ def setUpClass(cls): pytest.skip("Requires ebcc") def _get_emb(self, mf, solver, target_orbs, local_projection, threshold): - emb = ewf.EWF(mf, solver=solver, bath_options=dict(bathtype="mp2", threshold=threshold, project_dmet_order=1, - project_dmet_mode='full'), - bosonic_bath_options=dict(bathtype="rpa", target_orbitals=target_orbs, - local_projection=local_projection, threshold=threshold)) + emb = ewf.EWF( + mf, + solver=solver, + bath_options=dict(bathtype="mp2", threshold=threshold, project_dmet_order=1, project_dmet_mode="full"), + bosonic_bath_options=dict( + bathtype="rpa", target_orbitals=target_orbs, local_projection=local_projection, threshold=threshold + ), + ) emb.kernel() return emb.e_tot diff --git a/vayesta/tests/ewf/test_corrfunc.py b/vayesta/tests/ewf/test_corrfunc.py index f72989a1d..061cbc1c4 100644 --- a/vayesta/tests/ewf/test_corrfunc.py +++ b/vayesta/tests/ewf/test_corrfunc.py @@ -8,9 +8,8 @@ class Test_RCCSD(TestCase): - system = testsystems.h2_dz - solver = 'CCSD' + solver = "CCSD" @classmethod def setUpClass(cls): @@ -29,9 +28,12 @@ def tearDownClass(cls): @classmethod @cache def remb(cls, bno_threshold, run_kernel=True): - remb = vayesta.ewf.EWF(cls.rhf, solver=cls.solver, - bath_options=dict(threshold=bno_threshold), - solver_options=dict(solve_lambda=True)) + remb = vayesta.ewf.EWF( + cls.rhf, + solver=cls.solver, + bath_options=dict(threshold=bno_threshold), + solver_options=dict(solve_lambda=True), + ) if run_kernel: remb.kernel() return remb @@ -39,9 +41,12 @@ def remb(cls, bno_threshold, run_kernel=True): @classmethod @cache def uemb(cls, bno_threshold, run_kernel=True): - uemb = vayesta.ewf.EWF(cls.uhf, solver=cls.solver, - bath_options=dict(threshold=bno_threshold), - solver_options=dict(solve_lambda=True)) + uemb = vayesta.ewf.EWF( + cls.uhf, + solver=cls.solver, + bath_options=dict(threshold=bno_threshold), + solver_options=dict(solve_lambda=True), + ) if run_kernel: uemb.kernel() return uemb @@ -49,11 +54,11 @@ def uemb(cls, bno_threshold, run_kernel=True): def test_mf(self): remb = self.remb(np.inf, run_kernel=False) uemb = self.uemb(np.inf, run_kernel=False) - ssz_r = remb.get_corrfunc_mf('Sz,Sz') - ssz_u = uemb.get_corrfunc_mf('Sz,Sz') + ssz_r = remb.get_corrfunc_mf("Sz,Sz") + ssz_u = uemb.get_corrfunc_mf("Sz,Sz") self.assertAllclose(ssz_r, ssz_u, atol=1e-6) # OLD - ssz_old_u = uemb.get_corrfunc_mf('Sz,Sz') + ssz_old_u = uemb.get_corrfunc_mf("Sz,Sz") self.assertAllclose(ssz_old_u, ssz_u, atol=1e-6) def test_full_bath(self): @@ -63,30 +68,30 @@ def test_full_bath(self): dm1 = self.rcc.make_rdm1() dm2 = self.rcc.make_rdm2() - ssz = remb.get_corrfunc('Sz,Sz', dm1=dm1, dm2=dm2) + ssz = remb.get_corrfunc("Sz,Sz", dm1=dm1, dm2=dm2) - rssz = remb.get_corrfunc('Sz,Sz') - ussz = uemb.get_corrfunc('Sz,Sz') + rssz = remb.get_corrfunc("Sz,Sz") + ussz = uemb.get_corrfunc("Sz,Sz") self.assertAllclose(rssz, ssz, atol=1e-6) self.assertAllclose(ussz, ssz, atol=1e-6) # NEW - corr_r = remb.get_corrfunc('Sz,Sz') - corr_u = uemb.get_corrfunc('Sz,Sz') + corr_r = remb.get_corrfunc("Sz,Sz") + corr_u = uemb.get_corrfunc("Sz,Sz") self.assertAllclose(corr_r, ssz, atol=1e-6) self.assertAllclose(corr_u, ssz, atol=1e-6) # Full 2DMs rdm2 = remb.make_rdm2() udm2 = uemb.make_rdm2() - rssz = remb.get_corrfunc('Sz,Sz', dm2=rdm2) - ussz = uemb.get_corrfunc('Sz,Sz', dm2=udm2) + rssz = remb.get_corrfunc("Sz,Sz", dm2=rdm2) + ussz = uemb.get_corrfunc("Sz,Sz", dm2=udm2) self.assertAllclose(rssz, ssz, atol=1e-6) self.assertAllclose(ussz, ssz, atol=1e-6) # NEW - corr_r = remb.get_corrfunc('Sz,Sz', dm2=rdm2) - corr_u = uemb.get_corrfunc('Sz,Sz', dm2=udm2) + corr_r = remb.get_corrfunc("Sz,Sz", dm2=rdm2) + corr_u = uemb.get_corrfunc("Sz,Sz", dm2=udm2) self.assertAllclose(corr_r, ssz, atol=1e-6) self.assertAllclose(corr_u, ssz, atol=1e-6) @@ -95,35 +100,35 @@ def test_finite_bath(self): remb = self.remb(eta) uemb = self.uemb(eta) - ssz = rssz = remb.get_corrfunc('Sz,Sz') - ussz = uemb.get_corrfunc('Sz,Sz') + ssz = rssz = remb.get_corrfunc("Sz,Sz") + ussz = uemb.get_corrfunc("Sz,Sz") self.assertAllclose(rssz, ssz, atol=1e-6) self.assertAllclose(ussz, ssz, atol=1e-6) # NEW - corr_r = remb.get_corrfunc('Sz,Sz') - corr_u = uemb.get_corrfunc('Sz,Sz') + corr_r = remb.get_corrfunc("Sz,Sz") + corr_u = uemb.get_corrfunc("Sz,Sz") self.assertAllclose(corr_r, ssz, atol=1e-6) self.assertAllclose(corr_u, ssz, atol=1e-6) # Full 2DMs rdm2 = remb.make_rdm2() udm2 = uemb.make_rdm2() - rssz = remb.get_corrfunc('Sz,Sz', dm2=rdm2) - ussz = uemb.get_corrfunc('Sz,Sz', dm2=udm2) + rssz = remb.get_corrfunc("Sz,Sz", dm2=rdm2) + ussz = uemb.get_corrfunc("Sz,Sz", dm2=udm2) self.assertAllclose(rssz, ssz, atol=1e-6) self.assertAllclose(ussz, ssz, atol=1e-6) # NEW - corr_r = remb.get_corrfunc('Sz,Sz', dm2=rdm2) - corr_u = uemb.get_corrfunc('Sz,Sz', dm2=udm2) + corr_r = remb.get_corrfunc("Sz,Sz", dm2=rdm2) + corr_u = uemb.get_corrfunc("Sz,Sz", dm2=udm2) self.assertAllclose(corr_r, ssz, atol=1e-6) self.assertAllclose(corr_u, ssz, atol=1e-6) -class Test_RMP2(Test_RCCSD): +class Test_RMP2(Test_RCCSD): system = testsystems.h2_dz - solver = 'MP2' + solver = "MP2" @classmethod def setUpClass(cls): @@ -134,9 +139,8 @@ def setUpClass(cls): class Test_UCCSD(TestCase): - system = testsystems.water_cation_631g - solver = 'CCSD' + solver = "CCSD" @classmethod def setUpClass(cls): @@ -151,9 +155,12 @@ def tearDownClass(cls): @classmethod @cache def uemb(cls, bno_threshold): - uemb = vayesta.ewf.EWF(cls.uhf, solver=cls.solver, - bath_options=dict(threshold=bno_threshold), - solver_options=dict(solve_lambda=True)) + uemb = vayesta.ewf.EWF( + cls.uhf, + solver=cls.solver, + bath_options=dict(threshold=bno_threshold), + solver_options=dict(solve_lambda=True), + ) uemb.kernel() return uemb @@ -164,27 +171,26 @@ def _test_full_bath(self, projection): dm1 = self.ucc.make_rdm1() dm2 = self.ucc.make_rdm2() # Deprecated: get_corrfunc: - ssz = uemb.get_corrfunc('Sz,Sz', dm1=dm1, dm2=dm2, projection=projection) + ssz = uemb.get_corrfunc("Sz,Sz", dm1=dm1, dm2=dm2, projection=projection) udm2 = uemb.make_rdm2() - ssz_2 = uemb.get_corrfunc('Sz,Sz', dm2=udm2, dm2_with_dm1=True, projection=projection) + ssz_2 = uemb.get_corrfunc("Sz,Sz", dm2=udm2, dm2_with_dm1=True, projection=projection) self.assertAllclose(ssz_2, ssz, atol=1e-6) # Replacement: get_corrfunc('Sz,Sz', ...) - ssz_3 = uemb.get_corrfunc('Sz,Sz', dm2=udm2, dm2_with_dm1=True, projection=projection) + ssz_3 = uemb.get_corrfunc("Sz,Sz", dm2=udm2, dm2_with_dm1=True, projection=projection) self.assertAllclose(ssz_3, ssz, atol=1e-6) - ssz_4 = uemb.get_corrfunc('Sz,Sz', projection=projection) + ssz_4 = uemb.get_corrfunc("Sz,Sz", projection=projection) self.assertAllclose(ssz_4, ssz, atol=1e-6) def test_full_bath_sao(self): - self._test_full_bath('sao') + self._test_full_bath("sao") def test_full_bath_iaopao(self): - self._test_full_bath('iaopao') + self._test_full_bath("iaopao") class Test_UMP2(Test_UCCSD): - - solver = 'MP2' + solver = "MP2" @classmethod def setUpClass(cls): @@ -192,6 +198,6 @@ def setUpClass(cls): cls.ucc = cls.system.ump2() -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_diamond.py b/vayesta/tests/ewf/test_diamond.py index 7ff697458..e2918af3c 100644 --- a/vayesta/tests/ewf/test_diamond.py +++ b/vayesta/tests/ewf/test_diamond.py @@ -9,7 +9,6 @@ @pytest.mark.veryslow class DiamondTest(TestCase): - @classmethod def setUpClass(cls): cls.mf = testsystems.diamond_sto3g_k333.rhf() @@ -22,28 +21,28 @@ def tearDownClass(cls): @classmethod @cache def emb(cls, bno_threshold, symmetry=None): - emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold), - solver_options=dict(solve_lambda=True)) - center = (1/8, 1/8, 1/8) # point between C-atoms + emb = vayesta.ewf.EWF( + cls.mf, bath_options=dict(threshold=bno_threshold), solver_options=dict(solve_lambda=True) + ) + center = (1 / 8, 1 / 8, 1 / 8) # point between C-atoms with emb.fragmentation() as frag: if symmetry is None: frag.add_atomic_fragment(0) frag.add_atomic_fragment(1) - elif symmetry == 'rotation': - with frag.rotational_symmetry(2, axis=(1, 0, -1), center=center, unit='latvec'): + elif symmetry == "rotation": + with frag.rotational_symmetry(2, axis=(1, 0, -1), center=center, unit="latvec"): frag.add_atomic_fragment(0) - elif symmetry == 'inversion': - with frag.inversion_symmetry(center=center, unit='latvec'): + elif symmetry == "inversion": + with frag.inversion_symmetry(center=center, unit="latvec"): frag.add_atomic_fragment(0) - elif symmetry == 'mirror': - with frag.mirror_symmetry(center=center, axis=(1, 1, 1), unit='latvec'): + elif symmetry == "mirror": + with frag.mirror_symmetry(center=center, axis=(1, 1, 1), unit="latvec"): frag.add_atomic_fragment(0) else: raise ValueError emb.kernel() return emb - # --- Tests for T-symmetry def test_dm1_tsymmetry(self): @@ -54,14 +53,14 @@ def test_dm1_tsymmetry(self): def test_corrfunc_dndn_tsymmetry(self): emb = self.emb(1e-4) - corr_nosym = emb.get_corrfunc('dN,dN', use_symmetry=False) - corr_sym = emb.get_corrfunc('dN,dN', use_symmetry=True) + corr_nosym = emb.get_corrfunc("dN,dN", use_symmetry=False) + corr_sym = emb.get_corrfunc("dN,dN", use_symmetry=True) self.assertAllclose(corr_sym, corr_nosym) def test_corrfunc_szsz_tsymmetry(self): emb = self.emb(1e-4) - corr_nosym = emb.get_corrfunc('Sz,Sz', use_symmetry=False) - corr_sym = emb.get_corrfunc('Sz,Sz', use_symmetry=True) + corr_nosym = emb.get_corrfunc("Sz,Sz", use_symmetry=False) + corr_sym = emb.get_corrfunc("Sz,Sz", use_symmetry=True) self.assertAllclose(corr_sym, corr_nosym) # --- Tests for combined symmetries @@ -76,16 +75,16 @@ def _test_dm1_symmetry(self, symmetry): self.assertAllclose(dm1_tsym, dm1_nosym) def test_dm1_rotation_symmetry(self): - return self._test_dm1_symmetry('rotation') + return self._test_dm1_symmetry("rotation") def test_dm1_inversion_symmetry(self): - return self._test_dm1_symmetry('inversion') + return self._test_dm1_symmetry("inversion") # TODO: Fix failing - #def test_dm1_mirror_symmetry(self): + # def test_dm1_mirror_symmetry(self): # return self._test_dm1_symmetry('mirror') -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_extcorr.py b/vayesta/tests/ewf/test_extcorr.py index 710dc9bd6..119c5217b 100644 --- a/vayesta/tests/ewf/test_extcorr.py +++ b/vayesta/tests/ewf/test_extcorr.py @@ -10,26 +10,26 @@ from vayesta.tests import testsystems -#@pytest.mark.fast +# @pytest.mark.fast class TestFullEC_tailor(TestCase): @classmethod def setUpClass(cls): cls.fci = {} cls.mf = {} - cls.mf['lih_631g'] = testsystems.lih_631g.rhf() - cls.fci['lih_631g'] = pyscf.fci.FCI(cls.mf['lih_631g']) - cls.fci['lih_631g'].threads = 1 - cls.fci['lih_631g'].conv_tol = 1.0e-12 - cls.fci['lih_631g'].davidson_only = True - cls.fci['lih_631g'].kernel() + cls.mf["lih_631g"] = testsystems.lih_631g.rhf() + cls.fci["lih_631g"] = pyscf.fci.FCI(cls.mf["lih_631g"]) + cls.fci["lih_631g"].threads = 1 + cls.fci["lih_631g"].conv_tol = 1.0e-12 + cls.fci["lih_631g"].davidson_only = True + cls.fci["lih_631g"].kernel() - cls.mf['lih_ccpvdz'] = testsystems.lih_ccpvdz.rhf() - cls.fci['lih_ccpvdz'] = pyscf.fci.FCI(cls.mf['lih_ccpvdz']) - cls.fci['lih_ccpvdz'].threads = 1 - cls.fci['lih_ccpvdz'].conv_tol = 1.0e-12 - cls.fci['lih_ccpvdz'].davidson_only = True - cls.fci['lih_ccpvdz'].kernel() + cls.mf["lih_ccpvdz"] = testsystems.lih_ccpvdz.rhf() + cls.fci["lih_ccpvdz"] = pyscf.fci.FCI(cls.mf["lih_ccpvdz"]) + cls.fci["lih_ccpvdz"].threads = 1 + cls.fci["lih_ccpvdz"].conv_tol = 1.0e-12 + cls.fci["lih_ccpvdz"].davidson_only = True + cls.fci["lih_ccpvdz"].kernel() @classmethod def tearDownClass(cls): @@ -38,173 +38,326 @@ def tearDownClass(cls): del cls.fci del cls.mf - - def _test(cls, key, proj=0, bathtype='full', fcifragtype = 'atomic', mode='external', low_level_coul=False, store_wf_ccsdtq=True): + def _test( + cls, + key, + proj=0, + bathtype="full", + fcifragtype="atomic", + mode="external", + low_level_coul=False, + store_wf_ccsdtq=True, + ): mf = getattr(getattr(testsystems, key[0]), key[1])() - if fcifragtype == 'fullsystem': - emb = vayesta.ewf.EWF(mf, solver_options={'conv_tol':1.0e-12} ) + if fcifragtype == "fullsystem": + emb = vayesta.ewf.EWF(mf, solver_options={"conv_tol": 1.0e-12}) fci_frags = [] with emb.iao_fragmentation() as f: if store_wf_ccsdtq: - fci_frags.append(f.add_full_system(solver='FCI', bath_options=dict(bathtype=bathtype), - store_wf_type='CCSDTQ', auxiliary=True)) + fci_frags.append( + f.add_full_system( + solver="FCI", bath_options=dict(bathtype=bathtype), store_wf_type="CCSDTQ", auxiliary=True + ) + ) else: - fci_frags.append(f.add_full_system(solver='FCI', bath_options=dict(bathtype=bathtype), - auxiliary=True)) - ccsd_frag = f.add_full_system(solver='extCCSD', bath_options=dict(bathtype='full')) - ccsd_frag.add_external_corrections(fci_frags, correction_type=mode, projectors=proj, low_level_coul=low_level_coul, test_extcorr=True) + fci_frags.append( + f.add_full_system(solver="FCI", bath_options=dict(bathtype=bathtype), auxiliary=True) + ) + ccsd_frag = f.add_full_system(solver="extCCSD", bath_options=dict(bathtype="full")) + ccsd_frag.add_external_corrections( + fci_frags, correction_type=mode, projectors=proj, low_level_coul=low_level_coul, test_extcorr=True + ) emb.kernel() - - if fcifragtype == 'atomic': - emb = vayesta.ewf.EWF(mf, solver_options={'conv_tol':1.0e-12}) + if fcifragtype == "atomic": + emb = vayesta.ewf.EWF(mf, solver_options={"conv_tol": 1.0e-12}) with emb.iao_fragmentation() as f: if store_wf_ccsdtq: - fci_frag = f.add_all_atomic_fragments(solver='FCI', bath_options=dict(bathtype=bathtype), - store_wf_type='CCSDTQ', auxiliary=True) + fci_frag = f.add_all_atomic_fragments( + solver="FCI", bath_options=dict(bathtype=bathtype), store_wf_type="CCSDTQ", auxiliary=True + ) else: - fci_frag = f.add_all_atomic_fragments(solver='FCI', bath_options=dict(bathtype=bathtype), - auxiliary=True) - ccsd_frag = f.add_full_system(solver='extCCSD', bath_options=dict(bathtype='full')) - ccsd_frag.add_external_corrections(fci_frag, correction_type=mode, projectors=proj, low_level_coul=low_level_coul, test_extcorr=True) - + fci_frag = f.add_all_atomic_fragments( + solver="FCI", bath_options=dict(bathtype=bathtype), auxiliary=True + ) + ccsd_frag = f.add_full_system(solver="extCCSD", bath_options=dict(bathtype="full")) + ccsd_frag.add_external_corrections( + fci_frag, correction_type=mode, projectors=proj, low_level_coul=low_level_coul, test_extcorr=True + ) + emb.kernel() return emb.e_tot, cls.fci[key[0]].e_tot - -# FCI fragment bath type=full - + # FCI fragment bath type=full @pytest.mark.fast def test_r_exact_ec_lih_631g_atomicfrags_proj1_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'rhf'), proj=1, bathtype='full', fcifragtype = 'atomic', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "rhf"), + proj=1, + bathtype="full", + fcifragtype="atomic", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, fci_e_tot) @pytest.mark.fast def test_r_regression_ec_lih_631g_atomicfrags_proj2_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'rhf'), proj=2, bathtype='full', fcifragtype = 'atomic', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) - self.assertAlmostEqual(e_tot, -7.98893732925952) + e_tot, fci_e_tot = self._test( + ("lih_631g", "rhf"), + proj=2, + bathtype="full", + fcifragtype="atomic", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) + self.assertAlmostEqual(e_tot, -7.98893732925952) @pytest.mark.slow def test_r_exact_ec_lih_631g_fullsystem_proj0_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'rhf'), proj=0, bathtype='full', fcifragtype = 'fullsystem', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "rhf"), + proj=0, + bathtype="full", + fcifragtype="fullsystem", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, fci_e_tot) @pytest.mark.slow def test_r_exact_ec_lih_631g_fullsystem_proj1_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'rhf'), proj=1, bathtype='full', fcifragtype = 'fullsystem', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "rhf"), + proj=1, + bathtype="full", + fcifragtype="fullsystem", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, fci_e_tot) @pytest.mark.slow def test_r_exact_ec_lih_631g_fullsystem_proj2_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'rhf'), proj=2, bathtype='full', fcifragtype = 'fullsystem', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "rhf"), + proj=2, + bathtype="full", + fcifragtype="fullsystem", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, fci_e_tot) - # uhf tests @pytest.mark.slow def test_u_exact_ec_lih_631g_atomicfrags_proj1_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'uhf'), proj=1, bathtype='full', fcifragtype = 'atomic', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "uhf"), + proj=1, + bathtype="full", + fcifragtype="atomic", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, fci_e_tot) @pytest.mark.slow def test_u_regression_ec_lih_631g_atomicfrags_proj2_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'uhf'), proj=2, bathtype='full', fcifragtype = 'atomic', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "uhf"), + proj=2, + bathtype="full", + fcifragtype="atomic", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, -7.988937329208046) @pytest.mark.fast def test_u_exact_ec_lih_631g_fullsystem_proj0_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'uhf'), proj=0, bathtype='full', fcifragtype = 'fullsystem', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "uhf"), + proj=0, + bathtype="full", + fcifragtype="fullsystem", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, fci_e_tot) @pytest.mark.slow def test_u_exact_ec_lih_631g_fullsystem_proj1_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'uhf'), proj=1, bathtype='full', fcifragtype = 'fullsystem', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "uhf"), + proj=1, + bathtype="full", + fcifragtype="fullsystem", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, fci_e_tot) @pytest.mark.fast def test_u_exact_ec_lih_631g_fullsystem_proj2_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'uhf'), proj=2, bathtype='full', fcifragtype = 'fullsystem', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "uhf"), + proj=2, + bathtype="full", + fcifragtype="fullsystem", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, fci_e_tot) # FCI fragment bathtype = dmet - @pytest.mark.fast def test_r_regression_ec_lih_631g_atomicfrags_bathype_dmet_proj1_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'rhf'), proj=1, bathtype='dmet', fcifragtype = 'atomic', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "rhf"), + proj=1, + bathtype="dmet", + fcifragtype="atomic", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, -7.988931393747468) @pytest.mark.fast def test_r_regression_ec_lih_631g_atomicfrags_bathype_dmet_proj2_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'rhf'), proj=2, bathtype='dmet', fcifragtype = 'atomic', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) - self.assertAlmostEqual(e_tot, -7.988931393747468) + e_tot, fci_e_tot = self._test( + ("lih_631g", "rhf"), + proj=2, + bathtype="dmet", + fcifragtype="atomic", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) + self.assertAlmostEqual(e_tot, -7.988931393747468) @pytest.mark.slow def test_r_regression_ec_lih_631g_fullsystem_bathype_dmet_proj0_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'rhf'), proj=0, bathtype='dmet', fcifragtype = 'fullsystem', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "rhf"), + proj=0, + bathtype="dmet", + fcifragtype="fullsystem", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, -7.988931393745471) @pytest.mark.slow def test_r_regression_ec_lih_631g_fullsystem_bathype_dmet_proj1_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'rhf'), proj=1, bathtype='dmet', fcifragtype = 'fullsystem', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "rhf"), + proj=1, + bathtype="dmet", + fcifragtype="fullsystem", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, -7.988931393747714) @pytest.mark.slow def test_r_regression_ec_lih_631g_fullsystem_bathype_dmet_proj2_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'rhf'), proj=2, bathtype='dmet', fcifragtype = 'fullsystem', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "rhf"), + proj=2, + bathtype="dmet", + fcifragtype="fullsystem", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, -7.98893139374664) - # uhf tests + # uhf tests @pytest.mark.slow def test_u_regression_ec_lih_631g_atomicfrags_bathype_dmet_proj1_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'uhf'), proj=1, bathtype='dmet', fcifragtype = 'atomic', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "uhf"), + proj=1, + bathtype="dmet", + fcifragtype="atomic", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, -7.988931393739508) @pytest.mark.slow def test_u_regression_ec_lih_631g_atomicfrags_bathype_dmet_proj2_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'uhf'), proj=2, bathtype='dmet', fcifragtype = 'atomic', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "uhf"), + proj=2, + bathtype="dmet", + fcifragtype="atomic", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, -7.988931393739506) - @pytest.mark.fast def test_u_regression_ec_lih_631g_fullsystem_bathype_dmet_proj0_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'uhf'), proj=0, bathtype='dmet', fcifragtype = 'fullsystem', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "uhf"), + proj=0, + bathtype="dmet", + fcifragtype="fullsystem", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, -7.988931393739505) @pytest.mark.slow def test_u_regression_ec_lih_631g_fullsystem_bathype_dmet_proj1_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'uhf'), proj=1, bathtype='dmet', fcifragtype = 'fullsystem', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "uhf"), + proj=1, + bathtype="dmet", + fcifragtype="fullsystem", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, -7.9889313937395094) @pytest.mark.fast def test_u_regression_ec_lih_631g_fullsystem_bathype_dmet_proj2_ccsdv_store(self): - e_tot, fci_e_tot = self._test(('lih_631g', 'uhf'), proj=2, bathtype='dmet', fcifragtype = 'fullsystem', - mode='external', low_level_coul=True, store_wf_ccsdtq=True) + e_tot, fci_e_tot = self._test( + ("lih_631g", "uhf"), + proj=2, + bathtype="dmet", + fcifragtype="fullsystem", + mode="external", + low_level_coul=True, + store_wf_ccsdtq=True, + ) self.assertAlmostEqual(e_tot, -7.988931393739503) + @pytest.mark.fast class TestHubCompleteEC(TestCase): def _test_10_u4_2imp(self, mode, proj, low_level_coul=True): @@ -215,11 +368,13 @@ def _test_10_u4_2imp(self, mode, proj, low_level_coul=True): mf = testsystems.hubb_10_u4.rhf() emb = vayesta.ewf.EWF(mf) - emb.opts.solver_options['conv_tol'] = 1.0e-12 + emb.opts.solver_options["conv_tol"] = 1.0e-12 with emb.site_fragmentation() as f: - fci_frag = f.add_atomic_fragment(list(range(10)), solver='FCI', store_wf_type='CCSDTQ') - ccsd_frag = f.add_atomic_fragment([0, 1], solver='extCCSD', bath_options=dict(bathtype='full'), active=False) + fci_frag = f.add_atomic_fragment(list(range(10)), solver="FCI", store_wf_type="CCSDTQ") + ccsd_frag = f.add_atomic_fragment( + [0, 1], solver="extCCSD", bath_options=dict(bathtype="full"), active=False + ) ccsd_frag.add_tsymmetric_fragments(tvecs=[5, 1, 1]) emb.kernel() fci_frag_ecorr = emb.e_corr @@ -227,7 +382,9 @@ def _test_10_u4_2imp(self, mode, proj, low_level_coul=True): fci_frag.active = False ccsd_frag.active = True - ccsd_frag.add_external_corrections([fci_frag], correction_type=mode, projectors=proj, low_level_coul=low_level_coul, test_extcorr=True) + ccsd_frag.add_external_corrections( + [fci_frag], correction_type=mode, projectors=proj, low_level_coul=low_level_coul, test_extcorr=True + ) emb.kernel() fci = pyscf.fci.FCI(mf) @@ -240,19 +397,25 @@ def _test_10_u4_2imp(self, mode, proj, low_level_coul=True): self.assertAlmostEqual(fci_frag_etot, fci.e_tot) self.assertAlmostEqual(emb.e_corr, fci_frag_ecorr) self.assertAlmostEqual(emb.e_tot, fci_frag_etot) - + def test_hub_ec_2imp_proj0_ccsdv(self): - return self._test_10_u4_2imp(mode='external', proj=0, low_level_coul=True) - def test_hub_ec_2imp_proj1_ccsdv(self): - return self._test_10_u4_2imp(mode='external', proj=1, low_level_coul=True) - def test_hub_ec_2imp_proj2_ccsdv(self): - return self._test_10_u4_2imp(mode='external', proj=2, low_level_coul=True) - def test_hub_ec_2imp_proj0_fciv(self): - return self._test_10_u4_2imp(mode='external', proj=0, low_level_coul=False) - def test_hub_ec_2imp_proj1_fciv(self): - return self._test_10_u4_2imp(mode='external', proj=1, low_level_coul=False) - def test_hub_ec_2imp_proj2_fciv(self): - return self._test_10_u4_2imp(mode='external', proj=2, low_level_coul=False) + return self._test_10_u4_2imp(mode="external", proj=0, low_level_coul=True) + + def test_hub_ec_2imp_proj1_ccsdv(self): + return self._test_10_u4_2imp(mode="external", proj=1, low_level_coul=True) + + def test_hub_ec_2imp_proj2_ccsdv(self): + return self._test_10_u4_2imp(mode="external", proj=2, low_level_coul=True) + + def test_hub_ec_2imp_proj0_fciv(self): + return self._test_10_u4_2imp(mode="external", proj=0, low_level_coul=False) + + def test_hub_ec_2imp_proj1_fciv(self): + return self._test_10_u4_2imp(mode="external", proj=1, low_level_coul=False) + + def test_hub_ec_2imp_proj2_fciv(self): + return self._test_10_u4_2imp(mode="external", proj=2, low_level_coul=False) + @pytest.mark.fast class TestHubBathEC(TestCase): @@ -265,19 +428,24 @@ def _test_10_u2_2impfci(self, mode, low_level_coul=True): mf = testsystems.hubb_10_u2.rhf() emb = vayesta.ewf.EWF(mf) - emb.opts.solver_options['conv_tol'] = 1.0e-12 + emb.opts.solver_options["conv_tol"] = 1.0e-12 with emb.site_fragmentation() as f: - fci_frag = f.add_atomic_fragment([0, 1], solver='FCI', bath_options=dict(bathtype='full'), - store_wf_type='CCSDTQ') - ccsd_frag = f.add_atomic_fragment([0, 1], solver='extCCSD', bath_options=dict(bathtype='full'), active=False) + fci_frag = f.add_atomic_fragment( + [0, 1], solver="FCI", bath_options=dict(bathtype="full"), store_wf_type="CCSDTQ" + ) + ccsd_frag = f.add_atomic_fragment( + [0, 1], solver="extCCSD", bath_options=dict(bathtype="full"), active=False + ) ccsd_frag.add_tsymmetric_fragments(tvecs=[5, 1, 1]) emb.kernel() fci_frag.active = False ccsd_frag.active = True # Given the complete bath of the FCI fragment, should still be exact - ccsd_frag.add_external_corrections([fci_frag], correction_type=mode, projectors=0, low_level_coul=low_level_coul, test_extcorr=True) + ccsd_frag.add_external_corrections( + [fci_frag], correction_type=mode, projectors=0, low_level_coul=low_level_coul, test_extcorr=True + ) emb.kernel() fci = pyscf.fci.FCI(mf) @@ -290,35 +458,41 @@ def _test_10_u2_2impfci(self, mode, low_level_coul=True): self.assertAlmostEqual(emb.e_tot, fci.e_tot) def test_hub_ec_2impfci_proj0_fciv(self): - return self._test_10_u2_2impfci(mode='external', low_level_coul=False) + return self._test_10_u2_2impfci(mode="external", low_level_coul=False) + def test_hub_ec_2impfci_proj0_ccsdv(self): - return self._test_10_u2_2impfci(mode='external', low_level_coul=True) + return self._test_10_u2_2impfci(mode="external", low_level_coul=True) + @pytest.mark.fast class TestECSym(TestCase): def _test_10_u2_eccc_sym(self, mode, low_level_coul=True, proj=0): - """Test symmetry in external correction via comparison to system without use of symmetry - """ + """Test symmetry in external correction via comparison to system without use of symmetry""" mf = testsystems.hubb_10_u2.rhf() emb = vayesta.ewf.EWF(mf) - emb.opts.solver_options['conv_tol'] = 1.0e-12 + emb.opts.solver_options["conv_tol"] = 1.0e-12 fci_frags = [] with emb.site_fragmentation() as f: # Set up a two-site FCI fragment on all symmetrically equivalent fragments - fci_frags.append(f.add_atomic_fragment([0, 1], solver='FCI', bath_options=dict(bathtype='dmet'), - store_wf_type='CCSDTQ')) - fci_frags.append(f.add_atomic_fragment([2, 3], solver='FCI', bath_options=dict(bathtype='dmet'), - store_wf_type='CCSDTQ')) - fci_frags.append(f.add_atomic_fragment([4, 5], solver='FCI', bath_options=dict(bathtype='dmet'), - store_wf_type='CCSDTQ')) - fci_frags.append(f.add_atomic_fragment([6, 7], solver='FCI', bath_options=dict(bathtype='dmet'), - store_wf_type='CCSDTQ')) - fci_frags.append(f.add_atomic_fragment([8, 9], solver='FCI', bath_options=dict(bathtype='dmet'), - store_wf_type='CCSDTQ')) - ccsd_frag = f.add_full_system(solver='extCCSD', bath_options=dict(bathtype='full'), active=False) + fci_frags.append( + f.add_atomic_fragment([0, 1], solver="FCI", bath_options=dict(bathtype="dmet"), store_wf_type="CCSDTQ") + ) + fci_frags.append( + f.add_atomic_fragment([2, 3], solver="FCI", bath_options=dict(bathtype="dmet"), store_wf_type="CCSDTQ") + ) + fci_frags.append( + f.add_atomic_fragment([4, 5], solver="FCI", bath_options=dict(bathtype="dmet"), store_wf_type="CCSDTQ") + ) + fci_frags.append( + f.add_atomic_fragment([6, 7], solver="FCI", bath_options=dict(bathtype="dmet"), store_wf_type="CCSDTQ") + ) + fci_frags.append( + f.add_atomic_fragment([8, 9], solver="FCI", bath_options=dict(bathtype="dmet"), store_wf_type="CCSDTQ") + ) + ccsd_frag = f.add_full_system(solver="extCCSD", bath_options=dict(bathtype="full"), active=False) emb.kernel() for fci_frag in fci_frags: @@ -326,19 +500,22 @@ def _test_10_u2_eccc_sym(self, mode, low_level_coul=True, proj=0): ccsd_frag.active = True # Note that proj=0 will 'over correct' here and double-count bath-bath tailoring contributions. # However, it should still be equivalent to the non symmetry version - ccsd_frag.add_external_corrections(fci_frags, correction_type=mode, projectors=proj, low_level_coul=low_level_coul, test_extcorr=True) + ccsd_frag.add_external_corrections( + fci_frags, correction_type=mode, projectors=proj, low_level_coul=low_level_coul, test_extcorr=True + ) emb.kernel() e_tot_nosym = emb.e_tot # use symmetry of FCI clusters. emb = vayesta.ewf.EWF(mf) - emb.opts.solver_options['conv_tol'] = 1.0e-12 + emb.opts.solver_options["conv_tol"] = 1.0e-12 fci_frags = [] with emb.site_fragmentation() as f: - fci_frags.append(f.add_atomic_fragment([0, 1], solver='FCI', bath_options=dict(bathtype='dmet'), - store_wf_type='CCSDTQ')) - ccsd_frag = f.add_full_system(solver='extCCSD', bath_options=dict(bathtype='full'), active=False) + fci_frags.append( + f.add_atomic_fragment([0, 1], solver="FCI", bath_options=dict(bathtype="dmet"), store_wf_type="CCSDTQ") + ) + ccsd_frag = f.add_full_system(solver="extCCSD", bath_options=dict(bathtype="full"), active=False) # Add the symmetry-derived FCI fragments for fci_frag_sym in fci_frags[0].add_tsymmetric_fragments(tvecs=[5, 1, 1]): @@ -348,42 +525,52 @@ def _test_10_u2_eccc_sym(self, mode, low_level_coul=True, proj=0): for fci_frag in fci_frags: fci_frag.active = False ccsd_frag.active = True - ccsd_frag.add_external_corrections(fci_frags, correction_type=mode, projectors=proj, low_level_coul=low_level_coul, test_extcorr=True) + ccsd_frag.add_external_corrections( + fci_frags, correction_type=mode, projectors=proj, low_level_coul=low_level_coul, test_extcorr=True + ) emb.kernel() self.assertAlmostEqual(emb.e_tot, e_tot_nosym) def test_hub_ec_sym_proj0_fciv(self): - return self._test_10_u2_eccc_sym(mode='external', proj=0, low_level_coul=False) + return self._test_10_u2_eccc_sym(mode="external", proj=0, low_level_coul=False) + def test_hub_ec_sym_proj1_fciv(self): - return self._test_10_u2_eccc_sym(mode='external', proj=1, low_level_coul=False) + return self._test_10_u2_eccc_sym(mode="external", proj=1, low_level_coul=False) + def test_hub_ec_sym_proj2_fciv(self): - return self._test_10_u2_eccc_sym(mode='external', proj=2, low_level_coul=False) + return self._test_10_u2_eccc_sym(mode="external", proj=2, low_level_coul=False) + def test_hub_ec_sym_proj0_ccsdv(self): - return self._test_10_u2_eccc_sym(mode='external', proj=0, low_level_coul=True) + return self._test_10_u2_eccc_sym(mode="external", proj=0, low_level_coul=True) + def test_hub_ec_sym_proj1_ccsdv(self): - return self._test_10_u2_eccc_sym(mode='external', proj=1, low_level_coul=True) + return self._test_10_u2_eccc_sym(mode="external", proj=1, low_level_coul=True) + def test_hub_ec_sym_proj2_ccsdv(self): - return self._test_10_u2_eccc_sym(mode='external', proj=2, low_level_coul=True) + return self._test_10_u2_eccc_sym(mode="external", proj=2, low_level_coul=True) + def test_hub_ec_sym_proj1_dtailor(self): - return self._test_10_u2_eccc_sym(mode='delta-tailor', proj=1) + return self._test_10_u2_eccc_sym(mode="delta-tailor", proj=1) + def test_hub_ec_sym_proj1_tailor(self): - return self._test_10_u2_eccc_sym(mode='tailor', proj=1) + return self._test_10_u2_eccc_sym(mode="tailor", proj=1) + @pytest.mark.fast class TestRegEC_CCSD(TestCase): def _test_water_ec_regression(self, mode=None, projectors=None, low_level_coul=True): - mf = testsystems.water_ccpvdz_df.rhf() - + emb = vayesta.ewf.EWF(mf) - emb.opts.solver_options['conv_tol'] = 1.0e-12 + emb.opts.solver_options["conv_tol"] = 1.0e-12 fci_frags = [] with emb.iao_fragmentation() as f: - fci_frags = f.add_all_atomic_fragments(solver='FCI', bath_options=dict(bathtype='dmet'), - store_wf_type='CCSDTQ') - ccsd_frags = f.add_all_atomic_fragments(solver='extCCSD', bath_options=dict(bathtype='full'), active=False) + fci_frags = f.add_all_atomic_fragments( + solver="FCI", bath_options=dict(bathtype="dmet"), store_wf_type="CCSDTQ" + ) + ccsd_frags = f.add_all_atomic_fragments(solver="extCCSD", bath_options=dict(bathtype="full"), active=False) emb.kernel() for fci_frag in fci_frags: @@ -391,32 +578,38 @@ def _test_water_ec_regression(self, mode=None, projectors=None, low_level_coul=T for ccsd_frag in ccsd_frags: ccsd_frag.active = True - ccsd_frag.add_external_corrections(fci_frags, correction_type=mode, projectors=projectors, low_level_coul=low_level_coul, - test_extcorr=True) + ccsd_frag.add_external_corrections( + fci_frags, correction_type=mode, projectors=projectors, low_level_coul=low_level_coul, test_extcorr=True + ) emb.kernel() return emb.e_tot def test_water_ec_regression_proj0_fciv(self): - e_tot = self._test_water_ec_regression(mode='external', projectors=0, low_level_coul=False) + e_tot = self._test_water_ec_regression(mode="external", projectors=0, low_level_coul=False) self.assertAlmostEqual(e_tot, -76.2402530047042) + def test_water_ec_regression_proj0_ccsdv(self): - e_tot = self._test_water_ec_regression(mode='external', projectors=0, low_level_coul=True) + e_tot = self._test_water_ec_regression(mode="external", projectors=0, low_level_coul=True) self.assertAlmostEqual(e_tot, -76.24018922136538) + def test_water_ec_regression_proj1_fciv(self): - e_tot = self._test_water_ec_regression(mode='external', projectors=1, low_level_coul=False) + e_tot = self._test_water_ec_regression(mode="external", projectors=1, low_level_coul=False) self.assertAlmostEqual(e_tot, -76.24022612405739) + def test_water_ec_regression_proj1_ccsdv(self): - e_tot = self._test_water_ec_regression(mode='external', projectors=1, low_level_coul=True) + e_tot = self._test_water_ec_regression(mode="external", projectors=1, low_level_coul=True) self.assertAlmostEqual(e_tot, -76.24017967220551) + def test_water_ec_regression_proj2_fciv(self): - e_tot = self._test_water_ec_regression(mode='external', projectors=2, low_level_coul=False) + e_tot = self._test_water_ec_regression(mode="external", projectors=2, low_level_coul=False) self.assertAlmostEqual(e_tot, -76.24020498388937) + def test_water_ec_regression_proj2_ccsdv(self): - e_tot = self._test_water_ec_regression(mode='external', projectors=2, low_level_coul=True) + e_tot = self._test_water_ec_regression(mode="external", projectors=2, low_level_coul=True) self.assertAlmostEqual(e_tot, -76.24017093290053) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_fbc.py b/vayesta/tests/ewf/test_fbc.py index 96d7f648b..0c1dda5ff 100644 --- a/vayesta/tests/ewf/test_fbc.py +++ b/vayesta/tests/ewf/test_fbc.py @@ -7,7 +7,6 @@ class Test_Restricted(TestCase): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_631g.rhf() @@ -42,7 +41,6 @@ def test_finite_bath_vir(self): class Test_Unrestricted(Test_Restricted): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_cation_631g.uhf() @@ -60,6 +58,6 @@ def test_finite_bath_vir(self): self.assertAllclose(emb.get_fbc_energy(occupied=False), -0.008843579257449625) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_h2.py b/vayesta/tests/ewf/test_h2.py index d13cd09f7..7a615b9f5 100644 --- a/vayesta/tests/ewf/test_h2.py +++ b/vayesta/tests/ewf/test_h2.py @@ -6,8 +6,8 @@ from vayesta.tests import testsystems from vayesta.tests.common import TestCase -class Test_MP2(TestCase): +class Test_MP2(TestCase): @classmethod def setUpClass(cls): cls.mf = testsystems.h2_dz.rhf() @@ -22,7 +22,7 @@ def tearDownClass(cls): @classmethod @cache def emb(cls, bno_threshold): - emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold), solver='MP2') + emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold), solver="MP2") emb.kernel() return emb @@ -53,8 +53,8 @@ def test_global_dm1(self): dm1 = emb.make_rdm1(late_t2_sym=False) self.assertAllclose(dm1, dm1_exact) -class Test_CCSD(Test_MP2): +class Test_CCSD(Test_MP2): @classmethod def setUpClass(cls): cls.mf = testsystems.h2_dz.rhf() @@ -63,9 +63,8 @@ def setUpClass(cls): @classmethod @cache def emb(cls, bno_threshold): - solver_opts = dict(conv_tol= 1e-10, conv_tol_normt=1e-8, solve_lambda=True) - emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold), - solver_options=solver_opts) + solver_opts = dict(conv_tol=1e-10, conv_tol_normt=1e-8, solve_lambda=True) + emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold), solver_options=solver_opts) emb.kernel() return emb @@ -107,7 +106,6 @@ def test_global_dm2(self): class Test_UMP2(Test_MP2): - @classmethod def setUpClass(cls): cls.mf = testsystems.h2anion_dz.uhf() @@ -121,7 +119,6 @@ def test_global_dm1(self): class Test_UCCSD(Test_CCSD): - @classmethod def setUpClass(cls): cls.mf = testsystems.h2anion_dz.uhf() @@ -130,10 +127,11 @@ def setUpClass(cls): def test_global_dm2(self): pass + # --- FCI -class Test_FCI(TestCase): +class Test_FCI(TestCase): @classmethod def setUpClass(cls): cls.mf = testsystems.h2_dz.rhf() @@ -148,7 +146,7 @@ def tearDownClass(cls): @classmethod @cache def emb(cls, bno_threshold): - emb = vayesta.ewf.EWF(cls.mf, solver='FCI', bath_options=dict(threshold=bno_threshold)) + emb = vayesta.ewf.EWF(cls.mf, solver="FCI", bath_options=dict(threshold=bno_threshold)) emb.kernel() return emb @@ -156,15 +154,15 @@ def test_energy(self): emb = self.emb(-1) self.assertAllclose(emb.e_tot, self.fci.e_tot, rtol=0) -class Test_UFCI(TestCase): +class Test_UFCI(TestCase): @classmethod def setUpClass(cls): cls.mf = testsystems.h2anion_dz.uhf() cls.fci = testsystems.h2anion_dz.ufci() -class Test_UFCI_dissoc(TestCase): +class Test_UFCI_dissoc(TestCase): @classmethod def setUpClass(cls): # TODO ensure this tests works if density fitting is used. @@ -180,7 +178,7 @@ def tearDownClass(cls): @classmethod @cache def emb(cls, bno_threshold): - emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(bathtype="dmet"), solver='FCI') + emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(bathtype="dmet"), solver="FCI") emb.kernel() return emb @@ -189,6 +187,6 @@ def test_energy(self): self.assertAllclose(emb.e_tot, self.fci.e_tot, rtol=0) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_h2_pbc.py b/vayesta/tests/ewf/test_h2_pbc.py index 3df2e8605..221c2947c 100644 --- a/vayesta/tests/ewf/test_h2_pbc.py +++ b/vayesta/tests/ewf/test_h2_pbc.py @@ -11,25 +11,24 @@ from vayesta.tests.common import TestCase -pyscf_version = [int(x) for x in pyscf.__version__.split('.')] +pyscf_version = [int(x) for x in pyscf.__version__.split(".")] pyscf_version_atleast_2_1 = np.all(np.asarray(pyscf_version) >= (2, 1, 0)) TIGHT_SOLVER = dict(conv_tol=1e-10, conv_tol_normt=1e-8) @pytest.mark.slow class Test_MP2(TestCase): - @classmethod def setUpClass(cls): cls.mf = testsystems.h2_sto3g_k311.rhf() cls.cc = testsystems.h2_sto3g_s311.rmp2() nk = len(cls.mf.kpts) cls.ref_values = { - ('e_corr', -1) : cls.cc.e_corr/nk, - ('e_tot', -1) : cls.cc.e_tot/nk, - ('e_corr', 1e-3) : -0.009599078822158, - ('e_tot', 1e-3) : -1.277732258158756, - } + ("e_corr", -1): cls.cc.e_corr / nk, + ("e_tot", -1): cls.cc.e_tot / nk, + ("e_corr", 1e-3): -0.009599078822158, + ("e_tot", 1e-3): -1.277732258158756, + } @classmethod def tearDownClass(cls): @@ -40,9 +39,9 @@ def tearDownClass(cls): @classmethod def get_e_exxdiv(cls): - if cls.mf.exxdiv == 'ewald': + if cls.mf.exxdiv == "ewald": madelung = pyscf.pbc.tools.madelung(cls.mf.mol, cls.mf.kpts) - exxdiv = -madelung * cls.mf.mol.nelectron/2 + exxdiv = -madelung * cls.mf.mol.nelectron / 2 return exxdiv elif cls.mf.exxdiv is None: return 0 @@ -51,8 +50,8 @@ def get_e_exxdiv(cls): @classmethod @cache def emb(cls, bno_threshold): - emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold), solver='MP2') - with emb.iao_fragmentation(minao='minao') as f: + emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold), solver="MP2") + with emb.iao_fragmentation(minao="minao") as f: f.add_all_atomic_fragments() emb.kernel() return emb @@ -60,29 +59,29 @@ def emb(cls, bno_threshold): def test_energy_full_bath(self): eta = -1 emb = self.emb(eta) - self.assertAllclose(emb.e_corr, self.ref_values[('e_corr', eta)], rtol=0) - self.assertAllclose(emb.e_tot, self.ref_values[('e_tot', eta)], rtol=0) + self.assertAllclose(emb.e_corr, self.ref_values[("e_corr", eta)], rtol=0) + self.assertAllclose(emb.e_tot, self.ref_values[("e_tot", eta)], rtol=0) def test_energy_finite_bath(self): eta = 1e-3 emb = self.emb(eta) - self.assertAllclose(emb.e_corr, self.ref_values[('e_corr', eta)], rtol=0) - self.assertAllclose(emb.e_tot, self.ref_values[('e_tot', eta)], rtol=0) + self.assertAllclose(emb.e_corr, self.ref_values[("e_corr", eta)], rtol=0) + self.assertAllclose(emb.e_tot, self.ref_values[("e_tot", eta)], rtol=0) def _get_ref_t1_ao(self, t1): occ = self.cc._scf.mo_occ > 0 vir = self.cc._scf.mo_occ == 0 - mo_occ = self.cc.mo_coeff[:,occ] - mo_vir = self.cc.mo_coeff[:,vir] - t1_ref = np.einsum('Ii,ia,Aa->IA', mo_occ, t1, mo_vir, optimize=True) + mo_occ = self.cc.mo_coeff[:, occ] + mo_vir = self.cc.mo_coeff[:, vir] + t1_ref = np.einsum("Ii,ia,Aa->IA", mo_occ, t1, mo_vir, optimize=True) return t1_ref def _get_ref_t2_ao(self, t2): occ = self.cc._scf.mo_occ > 0 vir = self.cc._scf.mo_occ == 0 - mo_occ = self.cc.mo_coeff[:,occ] - mo_vir = self.cc.mo_coeff[:,vir] - t2_ref = np.einsum('Ii,Jj,ijab,Aa,Bb->IJAB', mo_occ, mo_occ, t2, mo_vir, mo_vir, optimize=True) + mo_occ = self.cc.mo_coeff[:, occ] + mo_vir = self.cc.mo_coeff[:, vir] + t2_ref = np.einsum("Ii,Jj,ijab,Aa,Bb->IJAB", mo_occ, mo_occ, t2, mo_vir, mo_vir, optimize=True) return t2_ref def test_t_amplitudes(self): @@ -104,7 +103,7 @@ def test_dm1(self): self.assertAllclose(dm1, dm1_exact) # Broken for non-zero exxdiv correction: - #def test_dmet_energy(self): + # def test_dmet_energy(self): # emb = self.emb(-1) # e_ref = self.ref_values[('e_tot', -1)] # etot_dmet = emb.get_dmet_energy() @@ -117,41 +116,41 @@ def test_dm1(self): @pytest.mark.slow class Test_CCSD(Test_MP2): - @classmethod def setUpClass(cls): cls.mf = testsystems.h2_sto3g_k311.rhf() cls.cc = testsystems.h2_sto3g_s311.rccsd() nk = len(cls.mf.kpts) cls.ref_values = { - ('e_corr', -1) : cls.cc.e_corr/nk, - ('e_tot', -1) : cls.cc.e_tot/nk, - ('e_corr', 1e-3) : -0.0153692736073979, - ('e_tot', 1e-3) : -1.2835024529439953, - } + ("e_corr", -1): cls.cc.e_corr / nk, + ("e_tot", -1): cls.cc.e_tot / nk, + ("e_corr", 1e-3): -0.0153692736073979, + ("e_tot", 1e-3): -1.2835024529439953, + } if not pyscf_version_atleast_2_1: - cls.ref_values[('e_tot', -1)] += cls.get_e_exxdiv() + cls.ref_values[("e_tot", -1)] += cls.get_e_exxdiv() @classmethod @cache def emb(cls, bno_threshold): - emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold), - solver_options={**TIGHT_SOLVER, 'solve_lambda': True}) - with emb.iao_fragmentation(minao='minao') as f: + emb = vayesta.ewf.EWF( + cls.mf, bath_options=dict(threshold=bno_threshold), solver_options={**TIGHT_SOLVER, "solve_lambda": True} + ) + with emb.iao_fragmentation(minao="minao") as f: f.add_all_atomic_fragments() emb.kernel() return emb def test_dmet_energy(self): emb = self.emb(-1) - e_ref = self.ref_values[('e_tot', -1)] + e_ref = self.ref_values[("e_tot", -1)] etot_dmet = emb.get_dmet_energy() self.assertAllclose(etot_dmet, e_ref, rtol=0) etot_dmet = emb.get_dmet_energy(part_cumulant=False) self.assertAllclose(etot_dmet, e_ref, rtol=0) # Broken for non-zero exxdiv correction: - #etot_dmet = emb.get_dmet_energy(approx_cumulant=False) - #self.assertAllclose(etot_dmet, e_ref, rtol=0) + # etot_dmet = emb.get_dmet_energy(approx_cumulant=False) + # self.assertAllclose(etot_dmet, e_ref, rtol=0) def test_t_amplitudes(self): emb = self.emb(-1) @@ -199,22 +198,23 @@ def test_dm2_demo(self): dm2 = emb.make_rdm2_demo(ao_basis=True) self.assertAllclose(dm2, dm2_exact) + # --- Unrestricted + @pytest.mark.slow class Test_UMP2(Test_MP2): - @classmethod def setUpClass(cls): cls.mf = testsystems.h3_sto3g_k311.uhf() cls.cc = testsystems.h3_sto3g_s311.ump2() nk = len(cls.mf.kpts) cls.ref_values = { - ('e_corr', -1) : cls.cc.e_corr/nk, - ('e_tot', -1) : cls.cc.e_tot/nk, - ('e_corr', 1e-3) : -0.00820754179397088, - ('e_tot', 1e-3) : -1.716742435416252 - } + ("e_corr", -1): cls.cc.e_corr / nk, + ("e_tot", -1): cls.cc.e_tot / nk, + ("e_corr", 1e-3): -0.00820754179397088, + ("e_tot", 1e-3): -1.716742435416252, + } def _get_ref_t1_ao(self, t1): t1a, t1b = t1 @@ -222,12 +222,12 @@ def _get_ref_t1_ao(self, t1): occb = self.cc._scf.mo_occ[1] > 0 vira = self.cc._scf.mo_occ[0] == 0 virb = self.cc._scf.mo_occ[1] == 0 - mo_occ_a = self.cc.mo_coeff[0][:,occa] - mo_occ_b = self.cc.mo_coeff[1][:,occb] - mo_vir_a = self.cc.mo_coeff[0][:,vira] - mo_vir_b = self.cc.mo_coeff[1][:,virb] - t1a_ref = np.einsum('Ii,ia,Aa->IA', mo_occ_a, t1a, mo_vir_a, optimize=True) - t1b_ref = np.einsum('Ii,ia,Aa->IA', mo_occ_b, t1b, mo_vir_b, optimize=True) + mo_occ_a = self.cc.mo_coeff[0][:, occa] + mo_occ_b = self.cc.mo_coeff[1][:, occb] + mo_vir_a = self.cc.mo_coeff[0][:, vira] + mo_vir_b = self.cc.mo_coeff[1][:, virb] + t1a_ref = np.einsum("Ii,ia,Aa->IA", mo_occ_a, t1a, mo_vir_a, optimize=True) + t1b_ref = np.einsum("Ii,ia,Aa->IA", mo_occ_b, t1b, mo_vir_b, optimize=True) return (t1a_ref, t1b_ref) def _get_ref_t2_ao(self, t2): @@ -236,13 +236,13 @@ def _get_ref_t2_ao(self, t2): occb = self.cc._scf.mo_occ[1] > 0 vira = self.cc._scf.mo_occ[0] == 0 virb = self.cc._scf.mo_occ[1] == 0 - mo_occ_a = self.cc.mo_coeff[0][:,occa] - mo_occ_b = self.cc.mo_coeff[1][:,occb] - mo_vir_a = self.cc.mo_coeff[0][:,vira] - mo_vir_b = self.cc.mo_coeff[1][:,virb] - t2aa_ref = np.einsum('Ii,Jj,ijab,Aa,Bb->IJAB', mo_occ_a, mo_occ_a, t2aa, mo_vir_a, mo_vir_a, optimize=True) - t2ab_ref = np.einsum('Ii,Jj,ijab,Aa,Bb->IJAB', mo_occ_a, mo_occ_b, t2ab, mo_vir_a, mo_vir_b, optimize=True) - t2bb_ref = np.einsum('Ii,Jj,ijab,Aa,Bb->IJAB', mo_occ_b, mo_occ_b, t2bb, mo_vir_b, mo_vir_b, optimize=True) + mo_occ_a = self.cc.mo_coeff[0][:, occa] + mo_occ_b = self.cc.mo_coeff[1][:, occb] + mo_vir_a = self.cc.mo_coeff[0][:, vira] + mo_vir_b = self.cc.mo_coeff[1][:, virb] + t2aa_ref = np.einsum("Ii,Jj,ijab,Aa,Bb->IJAB", mo_occ_a, mo_occ_a, t2aa, mo_vir_a, mo_vir_a, optimize=True) + t2ab_ref = np.einsum("Ii,Jj,ijab,Aa,Bb->IJAB", mo_occ_a, mo_occ_b, t2ab, mo_vir_a, mo_vir_b, optimize=True) + t2bb_ref = np.einsum("Ii,Jj,ijab,Aa,Bb->IJAB", mo_occ_b, mo_occ_b, t2bb, mo_vir_b, mo_vir_b, optimize=True) return (t2aa_ref, t2ab_ref, t2bb_ref) # Not implemented: @@ -253,22 +253,22 @@ def test_dm1_demo(self): def test_dm2_demo(self): pass + @pytest.mark.slow class Test_UCCSD(Test_CCSD): - @classmethod def setUpClass(cls): cls.mf = testsystems.h3_sto3g_k311.uhf() cls.cc = testsystems.h3_sto3g_s311.uccsd() nk = len(cls.mf.kpts) cls.ref_values = { - ('e_corr', -1) : cls.cc.e_corr/nk, - ('e_tot', -1) : cls.cc.e_tot/nk, - ('e_corr', 1e-3) : -0.01654717440912164, - ('e_tot', 1e-3) : -1.7250820680314027, - } + ("e_corr", -1): cls.cc.e_corr / nk, + ("e_tot", -1): cls.cc.e_tot / nk, + ("e_corr", 1e-3): -0.01654717440912164, + ("e_tot", 1e-3): -1.7250820680314027, + } if not pyscf_version_atleast_2_1: - cls.ref_values[('e_tot', -1)] += cls.get_e_exxdiv() + cls.ref_values[("e_tot", -1)] += cls.get_e_exxdiv() def _get_ref_t1_ao(self, t1): t1a, t1b = t1 @@ -276,12 +276,12 @@ def _get_ref_t1_ao(self, t1): occb = self.cc._scf.mo_occ[1] > 0 vira = self.cc._scf.mo_occ[0] == 0 virb = self.cc._scf.mo_occ[1] == 0 - mo_occ_a = self.cc.mo_coeff[0][:,occa] - mo_occ_b = self.cc.mo_coeff[1][:,occb] - mo_vir_a = self.cc.mo_coeff[0][:,vira] - mo_vir_b = self.cc.mo_coeff[1][:,virb] - t1a_ref = np.einsum('Ii,ia,Aa->IA', mo_occ_a, t1a, mo_vir_a, optimize=True) - t1b_ref = np.einsum('Ii,ia,Aa->IA', mo_occ_b, t1b, mo_vir_b, optimize=True) + mo_occ_a = self.cc.mo_coeff[0][:, occa] + mo_occ_b = self.cc.mo_coeff[1][:, occb] + mo_vir_a = self.cc.mo_coeff[0][:, vira] + mo_vir_b = self.cc.mo_coeff[1][:, virb] + t1a_ref = np.einsum("Ii,ia,Aa->IA", mo_occ_a, t1a, mo_vir_a, optimize=True) + t1b_ref = np.einsum("Ii,ia,Aa->IA", mo_occ_b, t1b, mo_vir_b, optimize=True) return (t1a_ref, t1b_ref) def _get_ref_t2_ao(self, t2): @@ -290,13 +290,13 @@ def _get_ref_t2_ao(self, t2): occb = self.cc._scf.mo_occ[1] > 0 vira = self.cc._scf.mo_occ[0] == 0 virb = self.cc._scf.mo_occ[1] == 0 - mo_occ_a = self.cc.mo_coeff[0][:,occa] - mo_occ_b = self.cc.mo_coeff[1][:,occb] - mo_vir_a = self.cc.mo_coeff[0][:,vira] - mo_vir_b = self.cc.mo_coeff[1][:,virb] - t2aa_ref = np.einsum('Ii,Jj,ijab,Aa,Bb->IJAB', mo_occ_a, mo_occ_a, t2aa, mo_vir_a, mo_vir_a, optimize=True) - t2ab_ref = np.einsum('Ii,Jj,ijab,Aa,Bb->IJAB', mo_occ_a, mo_occ_b, t2ab, mo_vir_a, mo_vir_b, optimize=True) - t2bb_ref = np.einsum('Ii,Jj,ijab,Aa,Bb->IJAB', mo_occ_b, mo_occ_b, t2bb, mo_vir_b, mo_vir_b, optimize=True) + mo_occ_a = self.cc.mo_coeff[0][:, occa] + mo_occ_b = self.cc.mo_coeff[1][:, occb] + mo_vir_a = self.cc.mo_coeff[0][:, vira] + mo_vir_b = self.cc.mo_coeff[1][:, virb] + t2aa_ref = np.einsum("Ii,Jj,ijab,Aa,Bb->IJAB", mo_occ_a, mo_occ_a, t2aa, mo_vir_a, mo_vir_a, optimize=True) + t2ab_ref = np.einsum("Ii,Jj,ijab,Aa,Bb->IJAB", mo_occ_a, mo_occ_b, t2ab, mo_vir_a, mo_vir_b, optimize=True) + t2bb_ref = np.einsum("Ii,Jj,ijab,Aa,Bb->IJAB", mo_occ_b, mo_occ_b, t2bb, mo_vir_b, mo_vir_b, optimize=True) return (t2aa_ref, t2ab_ref, t2bb_ref) # Not implemented: @@ -304,38 +304,40 @@ def _get_ref_t2_ao(self, t2): def test_dm2_demo(self): pass + # --- 2D + @pytest.mark.slow class Test_MP2_2D(Test_MP2): - @classmethod def setUpClass(cls): cls.mf = testsystems.h2_sto3g_k31.rhf() cls.cc = testsystems.h2_sto3g_s31.rmp2() nk = len(cls.mf.kpts) cls.ref_values = { - ('e_corr', -1) : cls.cc.e_corr/nk, - ('e_tot', -1) : cls.cc.e_tot/nk, - ('e_corr', 1e-3) : -0.013767086213673153, - } - cls.ref_values[('e_tot', 1e-3)] = cls.mf.e_tot + cls.ref_values[('e_corr', 1e-3)] + ("e_corr", -1): cls.cc.e_corr / nk, + ("e_tot", -1): cls.cc.e_tot / nk, + ("e_corr", 1e-3): -0.013767086213673153, + } + cls.ref_values[("e_tot", 1e-3)] = cls.mf.e_tot + cls.ref_values[("e_corr", 1e-3)] + @pytest.mark.slow class Test_CCSD_2D(Test_CCSD): - @classmethod def setUpClass(cls): cls.mf = testsystems.h2_sto3g_k31.rhf() cls.cc = testsystems.h2_sto3g_s31.rccsd() nk = len(cls.mf.kpts) cls.ref_values = { - ('e_corr', -1) : cls.cc.e_corr/nk, - ('e_tot', -1) : cls.cc.e_tot/nk, - ('e_corr', 1e-3) : -0.019820060226576966, - } - cls.ref_values[('e_tot', 1e-3)] = cls.mf.e_tot + cls.ref_values[('e_corr', 1e-3)] - -if __name__ == '__main__': - print('Running %s' % __file__) + ("e_corr", -1): cls.cc.e_corr / nk, + ("e_tot", -1): cls.cc.e_tot / nk, + ("e_corr", 1e-3): -0.019820060226576966, + } + cls.ref_values[("e_tot", 1e-3)] = cls.mf.e_tot + cls.ref_values[("e_corr", 1e-3)] + + +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_hubbard.py b/vayesta/tests/ewf/test_hubbard.py index 4a61004b5..8c586d0bb 100644 --- a/vayesta/tests/ewf/test_hubbard.py +++ b/vayesta/tests/ewf/test_hubbard.py @@ -7,151 +7,142 @@ class HubbardEWFTests(TestCase): PLACES_ENERGY = 6 - CONV_TOL = None #FIXME + CONV_TOL = None # FIXME def _test_energy(self, emb, known_values): - """Tests the EWF energy. - """ + """Tests the EWF energy.""" - self.assertAlmostEqual(emb.e_tot, known_values['e_tot'], self.PLACES_ENERGY) + self.assertAlmostEqual(emb.e_tot, known_values["e_tot"], self.PLACES_ENERGY) def test_6_u0_1imp(self): - """Tests for N=6 U=0 Hubbard model with single site impurities. - """ + """Tests for N=6 U=0 Hubbard model with single site impurities.""" emb = ewf.EWF( - testsystems.hubb_6_u0.rhf(), - bath_options=dict(threshold=1e-8), - solver_options={ - 'conv_tol': self.CONV_TOL, - }, + testsystems.hubb_6_u0.rhf(), + bath_options=dict(threshold=1e-8), + solver_options={ + "conv_tol": self.CONV_TOL, + }, ) with emb.site_fragmentation() as f: frag = f.add_atomic_fragment(0) frag.add_tsymmetric_fragments(tvecs=[6, 1, 1]) emb.kernel() - known_values = {'e_tot': -8.0} + known_values = {"e_tot": -8.0} self._test_energy(emb, known_values) def test_10_u2_2imp(self): - """Tests for N=10 U=2 Hubbard model with double site impurities. - """ + """Tests for N=10 U=2 Hubbard model with double site impurities.""" emb = ewf.EWF( - testsystems.hubb_10_u2.rhf(), - bath_options=dict(threshold=1e-8), - solver_options={ - 'conv_tol': self.CONV_TOL, - }, + testsystems.hubb_10_u2.rhf(), + bath_options=dict(threshold=1e-8), + solver_options={ + "conv_tol": self.CONV_TOL, + }, ) with emb.site_fragmentation() as f: frag = f.add_atomic_fragment([0, 1]) frag.add_tsymmetric_fragments(tvecs=[5, 1, 1]) emb.kernel() - known_values = {'e_tot': -8.633958869633286} + known_values = {"e_tot": -8.633958869633286} self._test_energy(emb, known_values) def test_10_u2_2imp_uhf(self): - """Tests for N=10 U=2 Hubbard model with double site impurities, with a uhf reference. - """ + """Tests for N=10 U=2 Hubbard model with double site impurities, with a uhf reference.""" emb = ewf.EWF( - testsystems.hubb_10_u2.rhf(), - bath_options=dict(threshold=1e-2), - solver_options={ - 'conv_tol': self.CONV_TOL, - }, - solver="FCI" + testsystems.hubb_10_u2.rhf(), + bath_options=dict(threshold=1e-2), + solver_options={ + "conv_tol": self.CONV_TOL, + }, + solver="FCI", ) with emb.site_fragmentation() as f: frag = f.add_atomic_fragment([0, 1]) frag.add_tsymmetric_fragments(tvecs=[5, 1, 1]) emb.kernel() - uemb = ewf.EWF( - testsystems.hubb_10_u2.uhf(), - bath_options=dict(threshold=1e-2), - solver_options={ - 'conv_tol': self.CONV_TOL, - }, - solver="FCI" + testsystems.hubb_10_u2.uhf(), + bath_options=dict(threshold=1e-2), + solver_options={ + "conv_tol": self.CONV_TOL, + }, + solver="FCI", ) with uemb.site_fragmentation() as f: frag = f.add_atomic_fragment([0, 1]) frag.add_tsymmetric_fragments(tvecs=[5, 1, 1]) uemb.kernel() - known_values = {'e_tot': emb.e_tot} + known_values = {"e_tot": emb.e_tot} self._test_energy(uemb, known_values) def test_6x6_u0_1x1imp(self): - """Tests for 6x6 U=0 Hubbard model with single site impurities. - """ + """Tests for 6x6 U=0 Hubbard model with single site impurities.""" emb = ewf.EWF( - testsystems.hubb_6x6_u0_1x1imp.rhf(), - bath_options=dict(threshold=1e-8), - solver_options={ - 'conv_tol': self.CONV_TOL, - }, + testsystems.hubb_6x6_u0_1x1imp.rhf(), + bath_options=dict(threshold=1e-8), + solver_options={ + "conv_tol": self.CONV_TOL, + }, ) with emb.site_fragmentation() as f: frag = f.add_atomic_fragment([0]) frag.add_tsymmetric_fragments(tvecs=[6, 6, 1]) emb.kernel() - known_values = {'e_tot': -56.0} + known_values = {"e_tot": -56.0} self._test_energy(emb, known_values) def test_6x6_u6_1x1imp(self): - """Tests for 6x6 U=6 Hubbard model with single site impurities. - """ + """Tests for 6x6 U=6 Hubbard model with single site impurities.""" emb = ewf.EWF( - testsystems.hubb_6x6_u6_1x1imp.rhf(), - bath_options=dict(threshold=1e-8), - solver_options={ - 'conv_tol': self.CONV_TOL, - }, + testsystems.hubb_6x6_u6_1x1imp.rhf(), + bath_options=dict(threshold=1e-8), + solver_options={ + "conv_tol": self.CONV_TOL, + }, ) with emb.site_fragmentation() as f: frag = f.add_atomic_fragment([0]) frag.add_tsymmetric_fragments(tvecs=[6, 6, 1]) emb.kernel() - known_values = {'e_tot': -37.71020224582783} + known_values = {"e_tot": -37.71020224582783} self._test_energy(emb, known_values) def test_8x8_u2_2x2imp(self): - """Tests for 8x8 U=2 Hubbard model with 2x2 impurities. - """ + """Tests for 8x8 U=2 Hubbard model with 2x2 impurities.""" emb = ewf.EWF( - testsystems.hubb_8x8_u2_2x2imp.rhf(), - bath_options=dict(threshold=1e-8), - solver_options={ - 'conv_tol': self.CONV_TOL, - }, + testsystems.hubb_8x8_u2_2x2imp.rhf(), + bath_options=dict(threshold=1e-8), + solver_options={ + "conv_tol": self.CONV_TOL, + }, ) with emb.site_fragmentation() as f: frag = f.add_atomic_fragment([0, 1, 2, 3]) frag.add_tsymmetric_fragments(tvecs=[4, 4, 1]) emb.kernel() - known_values = {'e_tot': -84.3268698533661} + known_values = {"e_tot": -84.3268698533661} self._test_energy(emb, known_values) - -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_icmp2.py b/vayesta/tests/ewf/test_icmp2.py index 5229dee88..240f7dc44 100644 --- a/vayesta/tests/ewf/test_icmp2.py +++ b/vayesta/tests/ewf/test_icmp2.py @@ -9,7 +9,6 @@ class ICMP2_Test(TestCase): - @classmethod def tearDownClass(cls): del cls.mf @@ -18,8 +17,8 @@ def tearDownClass(cls): @classmethod @cache def emb(cls, bno_threshold): - emb = vayesta.ewf.EWF(cls.mf, solver='MP2', bath_options=dict(threshold=bno_threshold, project_dmet_order=0)) - with emb.iao_fragmentation(minao='minao') as f: + emb = vayesta.ewf.EWF(cls.mf, solver="MP2", bath_options=dict(threshold=bno_threshold, project_dmet_order=0)) + with emb.iao_fragmentation(minao="minao") as f: f.add_all_atomic_fragments() emb.kernel() return emb @@ -27,8 +26,8 @@ def emb(cls, bno_threshold): @classmethod @cache def emb_nosym(cls, bno_threshold): - emb = vayesta.ewf.EWF(cls.mf, solver='MP2', bath_options=dict(threshold=bno_threshold, project_dmet_order=0)) - with emb.iao_fragmentation(minao='minao', add_symmetric=False) as f: + emb = vayesta.ewf.EWF(cls.mf, solver="MP2", bath_options=dict(threshold=bno_threshold, project_dmet_order=0)) + with emb.iao_fragmentation(minao="minao", add_symmetric=False) as f: for natom in range(emb.mol.natm): f.add_atomic_fragment(natom) emb.kernel() @@ -36,16 +35,15 @@ def emb_nosym(cls, bno_threshold): class ICMP2_RHF_Test(ICMP2_Test): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_631g_df.rhf() def test_dmet_bath(self): emb = self.emb(np.inf) - e_ic = emb.get_intercluster_mp2_energy(bno_threshold_vir=1e-3, project_dc='vir') + e_ic = emb.get_intercluster_mp2_energy(bno_threshold_vir=1e-3, project_dc="vir") self.assertAllclose(e_ic, -0.05927010355296033) - e_ic = emb.get_intercluster_mp2_energy(bno_threshold_vir=1e-5, project_dc='vir') + e_ic = emb.get_intercluster_mp2_energy(bno_threshold_vir=1e-5, project_dc="vir") self.assertAllclose(e_ic, -0.0721498800072952) def test_full_bath(self): @@ -55,7 +53,6 @@ def test_full_bath(self): class ICMP2_UHF_Test(ICMP2_Test): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_cation_631g_df.uhf() @@ -74,28 +71,26 @@ def test_full_bath(self): class ICMP2_RHF_PBC_Test(ICMP2_Test): - @classmethod def setUpClass(cls): cls.mf = testsystems.h2_sto3g_k311.rhf() def test_dmet_bath(self): emb = self.emb(np.inf) - e_ic = emb.get_intercluster_mp2_energy(1e-1, project_dc='vir') + e_ic = emb.get_intercluster_mp2_energy(1e-1, project_dc="vir") self.assertAllclose(e_ic, -0.0002008447130808661) - e_ic = emb.get_intercluster_mp2_energy(1e-8, project_dc='vir') + e_ic = emb.get_intercluster_mp2_energy(1e-8, project_dc="vir") self.assertAllclose(e_ic, -0.00020293482504415167) def test_dmet_bath_nosym(self): emb = self.emb_nosym(np.inf) - e_ic = emb.get_intercluster_mp2_energy(1e-1, project_dc='vir') + e_ic = emb.get_intercluster_mp2_energy(1e-1, project_dc="vir") self.assertAllclose(e_ic, -0.0002008447130808661) - e_ic = emb.get_intercluster_mp2_energy(1e-8, project_dc='vir') + e_ic = emb.get_intercluster_mp2_energy(1e-8, project_dc="vir") self.assertAllclose(e_ic, -0.00020293482504415167) class ICMP2_UHF_PBC_Test(ICMP2_Test): - @classmethod def setUpClass(cls): cls.mf = testsystems.h3_sto3g_k311.uhf() @@ -115,6 +110,6 @@ def test_dmet_bath_nosym(self): self.assertAllclose(e_ic, -0.0010737526376497356) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_molecules.py b/vayesta/tests/ewf/test_molecules.py index e7ea57cd7..eba73bad4 100644 --- a/vayesta/tests/ewf/test_molecules.py +++ b/vayesta/tests/ewf/test_molecules.py @@ -6,7 +6,7 @@ from vayesta.tests import testsystems -BATH_OPTS = dict(project_dmet_order = 0) +BATH_OPTS = dict(project_dmet_order=0) class MoleculeTests(TestCase): @@ -17,99 +17,92 @@ class MoleculeTests(TestCase): CONV_TOL_NORMT = 1e-8 def _test_energy(self, emb, known_values): - """Tests the EWF energy. - """ + """Tests the EWF energy.""" - self.assertAlmostEqual(emb.e_tot, known_values['e_tot'], self.PLACES_ENERGY) + self.assertAlmostEqual(emb.e_tot, known_values["e_tot"], self.PLACES_ENERGY) def _test_dmet_energy(self, emb, known_values): - """Tests the DMET energy. - """ + """Tests the DMET energy.""" - self.assertAlmostEqual(emb.get_dmet_energy(part_cumulant=False), known_values['e_dmet'], self.PLACES_ENERGY) + self.assertAlmostEqual(emb.get_dmet_energy(part_cumulant=False), known_values["e_dmet"], self.PLACES_ENERGY) def _test_t1(self, emb, known_values): - """Tests the T1 and L1 amplitudes. - """ + """Tests the T1 and L1 amplitudes.""" t1 = emb.get_global_t1() l1 = emb.get_global_l1() - self.assertAlmostEqual(np.linalg.norm(t1), known_values['t1'], self.PLACES_T) - self.assertAlmostEqual(np.linalg.norm(l1), known_values['l1'], self.PLACES_T) + self.assertAlmostEqual(np.linalg.norm(t1), known_values["t1"], self.PLACES_T) + self.assertAlmostEqual(np.linalg.norm(l1), known_values["l1"], self.PLACES_T) def _test_t2(self, emb, known_values): - """Tests the T2 and L2 amplitudes. - """ + """Tests the T2 and L2 amplitudes.""" t2 = emb.get_global_t2() l2 = emb.get_global_l2() - self.assertAlmostEqual(np.linalg.norm(t2), known_values['t2'], self.PLACES_T) - self.assertAlmostEqual(np.linalg.norm(l2), known_values['l2'], self.PLACES_T) + self.assertAlmostEqual(np.linalg.norm(t2), known_values["t2"], self.PLACES_T) + self.assertAlmostEqual(np.linalg.norm(l2), known_values["l2"], self.PLACES_T) def _test_rdm1(self, emb, known_values): - """Tests the traces of the first-order density matrices. - """ + """Tests the traces of the first-order density matrices.""" dm = emb.make_rdm1_demo() - self.assertAlmostEqual(np.trace(dm), known_values['rdm1_demo'], self.PLACES_DM) + self.assertAlmostEqual(np.trace(dm), known_values["rdm1_demo"], self.PLACES_DM) dm = emb.make_rdm1_demo(ao_basis=True) - self.assertAlmostEqual(np.trace(dm), known_values['rdm1_demo_ao'], self.PLACES_DM) + self.assertAlmostEqual(np.trace(dm), known_values["rdm1_demo_ao"], self.PLACES_DM) dm = emb._make_rdm1_ccsd() - self.assertAlmostEqual(np.trace(dm), known_values['rdm1_ccsd'], self.PLACES_DM) + self.assertAlmostEqual(np.trace(dm), known_values["rdm1_ccsd"], self.PLACES_DM) dm = emb._make_rdm1_ccsd(ao_basis=True) - self.assertAlmostEqual(np.trace(dm), known_values['rdm1_ccsd_ao'], self.PLACES_DM) + self.assertAlmostEqual(np.trace(dm), known_values["rdm1_ccsd_ao"], self.PLACES_DM) def _test_rdm2(self, emb, known_values): - """Tests the traces of the second-order density matrices. - """ + """Tests the traces of the second-order density matrices.""" - trace = lambda m: np.einsum('iiii->', m) + trace = lambda m: np.einsum("iiii->", m) dm = emb.make_rdm2_demo() - self.assertAlmostEqual(trace(dm), known_values['rdm2_demo'], self.PLACES_DM) + self.assertAlmostEqual(trace(dm), known_values["rdm2_demo"], self.PLACES_DM) dm = emb.make_rdm2_demo(ao_basis=True, part_cumulant=False) - self.assertAlmostEqual(trace(dm), known_values['rdm2_demo_ao'], self.PLACES_DM) + self.assertAlmostEqual(trace(dm), known_values["rdm2_demo_ao"], self.PLACES_DM) dm = emb._make_rdm2_ccsd_global_wf() - self.assertAlmostEqual(trace(dm), known_values['rdm2_ccsd'], self.PLACES_DM) + self.assertAlmostEqual(trace(dm), known_values["rdm2_ccsd"], self.PLACES_DM) dm = emb._make_rdm2_ccsd_global_wf(ao_basis=True) - self.assertAlmostEqual(trace(dm), known_values['rdm2_ccsd_ao'], self.PLACES_DM) + self.assertAlmostEqual(trace(dm), known_values["rdm2_ccsd_ao"], self.PLACES_DM) def test_lih_iao_atoms(self): - """Tests EWF for LiH cc-pvdz with IAO atomic fragmentation. - """ + """Tests EWF for LiH cc-pvdz with IAO atomic fragmentation.""" emb = ewf.EWF( - testsystems.lih_ccpvdz.rhf(), - bath_options={'bathtype': 'full', **BATH_OPTS}, - solver_options={ - 'solve_lambda': True, - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.lih_ccpvdz.rhf(), + bath_options={"bathtype": "full", **BATH_OPTS}, + solver_options={ + "solve_lambda": True, + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) emb.kernel() known_values = { - 'e_tot': -8.008269603007381, - 'e_dmet': -7.976643405222605, - 't1': 0.02004058442926091, - 'l1': 0.01909464775558478, - 't2': 0.15951192869005756, - 'l2': 0.15539564391258082, - 'rdm1_demo': 3.9673905666426640, - 'rdm1_demo_ao': 2.8599690310182355, - 'rdm1_ccsd': 4.0, - 'rdm1_ccsd_ao': 2.9402121097868132, - 'rdm2_demo': 3.9534283584199380, - 'rdm2_demo_ao': 1.9147846236993586, - 'rdm2_ccsd': 3.9689488519403370, - 'rdm2_ccsd_ao': 2.0677172173309413, + "e_tot": -8.008269603007381, + "e_dmet": -7.976643405222605, + "t1": 0.02004058442926091, + "l1": 0.01909464775558478, + "t2": 0.15951192869005756, + "l2": 0.15539564391258082, + "rdm1_demo": 3.9673905666426640, + "rdm1_demo_ao": 2.8599690310182355, + "rdm1_ccsd": 4.0, + "rdm1_ccsd_ao": 2.9402121097868132, + "rdm2_demo": 3.9534283584199380, + "rdm2_demo_ao": 1.9147846236993586, + "rdm2_ccsd": 3.9689488519403370, + "rdm2_ccsd_ao": 2.0677172173309413, } self._test_energy(emb, known_values) @@ -120,123 +113,118 @@ def test_lih_iao_atoms(self): self._test_rdm2(emb, known_values) def test_lih_sao_orbitals(self): - """Tests EWF for LiH cc-pvdz with SAO orbital fragmentation. - """ + """Tests EWF for LiH cc-pvdz with SAO orbital fragmentation.""" emb = ewf.EWF( - testsystems.lih_ccpvdz.rhf(), - bath_options={'threshold': 1e-5/2, **BATH_OPTS}, - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.lih_ccpvdz.rhf(), + bath_options={"threshold": 1e-5 / 2, **BATH_OPTS}, + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) with emb.sao_fragmentation() as f: f.add_orbital_fragment([0, 1]) f.add_orbital_fragment([2, 3, 4]) emb.kernel() - known_values = {'e_tot': -7.98424889149862} + known_values = {"e_tot": -7.98424889149862} self._test_energy(emb, known_values) def test_lih_sao_atoms(self): - """Tests EWF for LiH cc-pvdz with SAO atomic fragmentation. - """ + """Tests EWF for LiH cc-pvdz with SAO atomic fragmentation.""" emb = ewf.EWF( - testsystems.lih_ccpvdz.rhf(), - bath_options={'bathtype': 'dmet'}, - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.lih_ccpvdz.rhf(), + bath_options={"bathtype": "dmet"}, + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) with emb.sao_fragmentation() as f: f.add_all_atomic_fragments() emb.kernel() - known_values = {'e_tot': -7.99502192669842} + known_values = {"e_tot": -7.99502192669842} self._test_energy(emb, known_values) def test_h2o_FCI(self): - """Tests EWF for H2O cc-pvdz with FCI solver. - """ + """Tests EWF for H2O cc-pvdz with FCI solver.""" emb = ewf.EWF( - testsystems.water_ccpvdz.rhf(), - bath_options={'bathtype': 'dmet'}, - solver='FCI', - solver_options={ - 'conv_tol': 1e-12, - 'fix_spin': 0.0, - } + testsystems.water_ccpvdz.rhf(), + bath_options={"bathtype": "dmet"}, + solver="FCI", + solver_options={ + "conv_tol": 1e-12, + "fix_spin": 0.0, + }, ) with emb.iao_fragmentation() as f: f.add_atomic_fragment(0) f.add_atomic_fragment(1, sym_factor=2) emb.kernel() - known_values = {'e_tot': -76.06365118513072} + known_values = {"e_tot": -76.06365118513072} self._test_energy(emb, known_values) def test_h2o_FCI_noguess(self): - """Tests EWF for H2O cc-pvdz with FCI solver. - """ + """Tests EWF for H2O cc-pvdz with FCI solver.""" emb = ewf.EWF( - testsystems.water_ccpvdz.rhf(), - bath_options={'bathtype': 'dmet'}, - solver='FCI', - solver_options={ - 'init_guess': 'meanfield', - 'conv_tol': 100, - } + testsystems.water_ccpvdz.rhf(), + bath_options={"bathtype": "dmet"}, + solver="FCI", + solver_options={ + "init_guess": "meanfield", + "conv_tol": 100, + }, ) emb.kernel() - known_values = {'e_tot': -76.02677312899381} + known_values = {"e_tot": -76.02677312899381} self._test_energy(emb, known_values) def test_h2o_TCCSD(self): - """Tests EWF for H2O cc-pvdz with FCI solver. - """ + """Tests EWF for H2O cc-pvdz with FCI solver.""" emb = ewf.EWF( - testsystems.water_ccpvdz.rhf(), - solver='TCCSD', - bath_options={'threshold': 1e-4/2, **BATH_OPTS}, - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.water_ccpvdz.rhf(), + solver="TCCSD", + bath_options={"threshold": 1e-4 / 2, **BATH_OPTS}, + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) with emb.iao_fragmentation() as f: f.add_atomic_fragment(0) f.add_atomic_fragment(1, sym_factor=2) emb.kernel() - known_values = {'e_tot': -76.23613576956096} - self.assertAlmostEqual(emb.e_tot, known_values['e_tot'], 6) + known_values = {"e_tot": -76.23613576956096} + self.assertAlmostEqual(emb.e_tot, known_values["e_tot"], 6) def test_tccsd_solver(self): """Tests EWF for H2O cc-pvdz with TCCSD solver and CAS picker.""" emb = ewf.EWF( - testsystems.water_ccpvdz.rhf(), - solver='TCCSD', - bath_options={'threshold': 1e-4/2, **BATH_OPTS}, - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.water_ccpvdz.rhf(), + solver="TCCSD", + bath_options={"threshold": 1e-4 / 2, **BATH_OPTS}, + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) with emb.iao_fragmentation() as f: frag1 = f.add_atomic_fragment(0) f.add_atomic_fragment(1, sym_factor=2) - frag1.set_cas(['0 O 2p']) + frag1.set_cas(["0 O 2p"]) emb.kernel() self.assertAllclose(emb.e_tot, -76.23559827815198, rtol=0) @@ -244,18 +232,18 @@ def test_tccsd_via_fragment(self): """Tests EWF for H2O cc-pvdz with tailoring FCI fragment. (Should reproduce result of test_tccsd_solver)""" emb = ewf.EWF( - testsystems.water_ccpvdz.rhf(), - bath_options={'threshold': 1e-4/2, **BATH_OPTS}, - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.water_ccpvdz.rhf(), + bath_options={"threshold": 1e-4 / 2, **BATH_OPTS}, + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) with emb.iao_fragmentation() as f: - cas_o = f.add_atomic_fragment(0, orbital_filter='2p', solver='FCI', bath_options=dict(bathtype='dmet')) - cas_h = f.add_atomic_fragment(1, solver='FCI', bath_options=dict(bathtype='dmet')) - frag_o = f.add_atomic_fragment(0, solver='extCCSD', active=False) - frag_h = f.add_atomic_fragment(1, solver='extCCSD', sym_factor=2, active=False) + cas_o = f.add_atomic_fragment(0, orbital_filter="2p", solver="FCI", bath_options=dict(bathtype="dmet")) + cas_h = f.add_atomic_fragment(1, solver="FCI", bath_options=dict(bathtype="dmet")) + frag_o = f.add_atomic_fragment(0, solver="extCCSD", active=False) + frag_h = f.add_atomic_fragment(1, solver="extCCSD", sym_factor=2, active=False) emb.kernel() frag_o.add_external_corrections([cas_o], projectors=0) frag_h.add_external_corrections([cas_h], projectors=0) @@ -265,50 +253,48 @@ def test_tccsd_via_fragment(self): self.assertAllclose(emb.e_tot, -76.23559827815198, atol=1e-6, rtol=0) def test_h2o_sc(self): - """Tests EWF for H2O cc-pvdz with self-consistency. - """ + """Tests EWF for H2O cc-pvdz with self-consistency.""" emb = ewf.EWF( - testsystems.water_ccpvdz.rhf(), - bath_options={'threshold': 1e-4/2, **BATH_OPTS}, - sc_mode=1, - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.water_ccpvdz.rhf(), + bath_options={"threshold": 1e-4 / 2, **BATH_OPTS}, + sc_mode=1, + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) with emb.iao_fragmentation() as f: f.add_atomic_fragment(0) f.add_atomic_fragment(1, sym_factor=2) emb.kernel() - known_values = {'e_tot': -76.23147227604929} + known_values = {"e_tot": -76.23147227604929} self._test_energy(emb, known_values) def test_h2_cisd(self): - """Compares CCSD and CISD solvers for a two-electron system. - """ + """Compares CCSD and CISD solvers for a two-electron system.""" ecisd = ewf.EWF( - testsystems.h2_ccpvdz.rhf(), - bath_options=dict(bathtype='dmet'), - solver='CISD', - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.h2_ccpvdz.rhf(), + bath_options=dict(bathtype="dmet"), + solver="CISD", + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) ecisd.kernel() eccsd = ewf.EWF( - testsystems.h2_ccpvdz.rhf(), - bath_options=dict(bathtype='dmet'), - solver='CCSD', - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.h2_ccpvdz.rhf(), + bath_options=dict(bathtype="dmet"), + solver="CCSD", + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) eccsd.kernel() @@ -316,25 +302,18 @@ def test_h2_cisd(self): self.assertAlmostEqual(ecisd.e_tot, eccsd.e_tot) def test_h2o_mp2_compression(self): - """Compares MP2 solver with ERIs, uncompressed CDERIs, and compressed CDERIs. - """ - rewf = ewf.EWF( - testsystems.water_ccpvdz.rhf(), - bath_options=dict(bathtype='dmet'), - solver="MP2" - ) + """Compares MP2 solver with ERIs, uncompressed CDERIs, and compressed CDERIs.""" + rewf = ewf.EWF(testsystems.water_ccpvdz.rhf(), bath_options=dict(bathtype="dmet"), solver="MP2") with rewf.iao_fragmentation() as f: f.add_all_atomic_fragments() rewf.kernel() df_rewf = ewf.EWF( - testsystems.water_ccpvdz_df.rhf(), - bath_options=dict(bathtype='dmet'), - solver="MP2", - solver_options={ - "compress_cderi":False - } + testsystems.water_ccpvdz_df.rhf(), + bath_options=dict(bathtype="dmet"), + solver="MP2", + solver_options={"compress_cderi": False}, ) with df_rewf.iao_fragmentation() as f: f.add_all_atomic_fragments() @@ -343,12 +322,10 @@ def test_h2o_mp2_compression(self): self.assertAlmostEqual(rewf.e_corr, df_rewf.e_corr, int(np.log10(abs(rewf.e_mf - df_rewf.e_mf)))) cdf_rewf = ewf.EWF( - testsystems.water_ccpvdz_df.rhf(), - bath_options=dict(bathtype='dmet'), - solver="MP2", - solver_options={ - "compress_cderi":True - } + testsystems.water_ccpvdz_df.rhf(), + bath_options=dict(bathtype="dmet"), + solver="MP2", + solver_options={"compress_cderi": True}, ) with cdf_rewf.iao_fragmentation() as f: f.add_all_atomic_fragments() @@ -356,34 +333,34 @@ def test_h2o_mp2_compression(self): # Compression shouldn't change the answer. self.assertAlmostEqual(df_rewf.e_tot, cdf_rewf.e_tot, self.PLACES_ENERGY) + class MoleculeTestsUnrestricted(unittest.TestCase): PLACES_ENERGY = 7 CONV_TOL = 1e-9 CONV_TOL_NORMT = 1e-7 def test_lih_rhf_vs_uhf(self): - """Compares RHF to UHF LiH cc-pvdz. - """ + """Compares RHF to UHF LiH cc-pvdz.""" rewf = ewf.EWF( - testsystems.lih_ccpvdz.rhf(), - bath_options=dict(bathtype='dmet'), - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.lih_ccpvdz.rhf(), + bath_options=dict(bathtype="dmet"), + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) with rewf.sao_fragmentation() as f: f.add_atomic_fragment([0, 1]) rewf.kernel() uewf = ewf.UEWF( - testsystems.lih_ccpvdz.uhf(), - bath_options=dict(bathtype='dmet'), - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.lih_ccpvdz.uhf(), + bath_options=dict(bathtype="dmet"), + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) with uewf.sao_fragmentation() as f: f.add_atomic_fragment([0, 1]) @@ -393,30 +370,29 @@ def test_lih_rhf_vs_uhf(self): self.assertAlmostEqual(rewf.e_tot, uewf.e_tot, self.PLACES_ENERGY) def test_lih_rhf_vs_uhf_cisd(self): - """Compares RHF to UHF LiH cc-pvdz. - """ + """Compares RHF to UHF LiH cc-pvdz.""" rewf = ewf.EWF( - testsystems.lih_ccpvdz.rhf(), - solver='CISD', - bath_options=dict(bathtype='dmet'), - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.lih_ccpvdz.rhf(), + solver="CISD", + bath_options=dict(bathtype="dmet"), + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) with rewf.sao_fragmentation() as f: f.add_atomic_fragment([0, 1]) rewf.kernel() uewf = ewf.UEWF( - testsystems.lih_ccpvdz.uhf(), - solver='CISD', - bath_options=dict(bathtype='dmet'), - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.lih_ccpvdz.uhf(), + solver="CISD", + bath_options=dict(bathtype="dmet"), + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) with uewf.sao_fragmentation() as f: f.add_atomic_fragment([0, 1]) @@ -426,28 +402,27 @@ def test_lih_rhf_vs_uhf_cisd(self): self.assertAlmostEqual(rewf.e_tot, uewf.e_tot, self.PLACES_ENERGY) def test_h2_rhf_vs_uhf_fci(self): - """Compares RHF to UHF with an FCI solver for H2 cc-pvdz. - """ + """Compares RHF to UHF with an FCI solver for H2 cc-pvdz.""" rewf = ewf.EWF( - testsystems.h2_ccpvdz.rhf(), - solver='FCI', - bath_options=dict(bathtype='dmet'), - solver_options={ - 'conv_tol': 1e-12, - }, + testsystems.h2_ccpvdz.rhf(), + solver="FCI", + bath_options=dict(bathtype="dmet"), + solver_options={ + "conv_tol": 1e-12, + }, ) with rewf.sao_fragmentation() as f: f.add_atomic_fragment([0, 1]) rewf.kernel() uewf = ewf.UEWF( - testsystems.h2_ccpvdz.uhf(), - solver='FCI', - bath_options=dict(bathtype='dmet'), - solver_options={ - 'conv_tol': 1e-12, - }, + testsystems.h2_ccpvdz.uhf(), + solver="FCI", + bath_options=dict(bathtype="dmet"), + solver_options={ + "conv_tol": 1e-12, + }, ) with uewf.sao_fragmentation() as f: f.add_atomic_fragment([0, 1]) @@ -457,28 +432,27 @@ def test_h2_rhf_vs_uhf_fci(self): self.assertAlmostEqual(rewf.e_tot, uewf.e_tot, self.PLACES_ENERGY) def test_lih_rhf_vs_uhf_CAS(self): - """Compares RHF to UHF LiH cc-pvdz using a 2,4 CAS as a fragment. - """ + """Compares RHF to UHF LiH cc-pvdz using a 2,4 CAS as a fragment.""" rewf = ewf.EWF( - testsystems.lih_ccpvdz.rhf(), - bath_options=dict(bathtype='dmet'), - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.lih_ccpvdz.rhf(), + bath_options=dict(bathtype="dmet"), + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) with rewf.cas_fragmentation() as f: f.add_cas_fragment(4, 2) rewf.kernel() uewf = ewf.UEWF( - testsystems.lih_ccpvdz.uhf(), - bath_options=dict(bathtype='dmet'), - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.lih_ccpvdz.uhf(), + bath_options=dict(bathtype="dmet"), + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) with uewf.cas_fragmentation() as f: f.add_cas_fragment(4, 2) @@ -492,14 +466,12 @@ def test_h2o_nelectron_target(self): Also compares to ensure UHF obtains the same result.""" remb = ewf.EWF( - testsystems.water_ccpvdz.rhf(), - bath_options={ - "bathtype":"dmet" - }, - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.water_ccpvdz.rhf(), + bath_options={"bathtype": "dmet"}, + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) with remb.iao_fragmentation() as f: f.add_atomic_fragment([0], nelectron_target=8.0) @@ -512,14 +484,12 @@ def test_h2o_nelectron_target(self): self.assertAlmostEqual(nel, 8.0, places=4) uemb = ewf.EWF( - testsystems.water_ccpvdz.uhf(), - bath_options={ - "bathtype":"dmet" - }, - solver_options={ - 'conv_tol': self.CONV_TOL, - 'conv_tol_normt': self.CONV_TOL_NORMT, - }, + testsystems.water_ccpvdz.uhf(), + bath_options={"bathtype": "dmet"}, + solver_options={ + "conv_tol": self.CONV_TOL, + "conv_tol_normt": self.CONV_TOL_NORMT, + }, ) with uemb.iao_fragmentation() as f: f.add_atomic_fragment([0], nelectron_target=8.0) @@ -530,6 +500,6 @@ def test_h2o_nelectron_target(self): self.assertAlmostEqual(remb.e_corr, -0.018721331700187655, self.PLACES_ENERGY) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_moments.py b/vayesta/tests/ewf/test_moments.py index 68003d934..60a839075 100644 --- a/vayesta/tests/ewf/test_moments.py +++ b/vayesta/tests/ewf/test_moments.py @@ -10,11 +10,10 @@ from vayesta.tests.common import TestCase from vayesta.tests import testsystems -class Test_RFCI(TestCase): +class Test_RFCI(TestCase): def test(self): - - #RHF + # RHF mf = testsystems.h6_sto6g.rhf() try: @@ -29,16 +28,18 @@ def test(self): fci = FCI["1p"](mf) fci_ea = fci.build_gf_moments(4) - #Full bath EWF - ewf = vayesta.ewf.EWF(mf, bath_options=dict(bathtype='full'), solver_options=dict(n_moments=(4,4)), solver='FCI') + # Full bath EWF + ewf = vayesta.ewf.EWF( + mf, bath_options=dict(bathtype="full"), solver_options=dict(n_moments=(4, 4)), solver="FCI" + ) ewf.kernel() for f in ewf.fragments: - ip, ea = f.results.moms + ip, ea = f.results.moms - cx = f.get_overlap('mo|cluster') - ip = np.einsum('pP,qQ,nPQ->npq', cx, cx, ip) - ea = np.einsum('pP,qQ,nPQ->npq', cx, cx, ea) + cx = f.get_overlap("mo|cluster") + ip = np.einsum("pP,qQ,nPQ->npq", cx, cx, ip) + ea = np.einsum("pP,qQ,nPQ->npq", cx, cx, ea) self.assertTrue(np.allclose(ip, fci_ip, atol=1e-14)) self.assertTrue(np.allclose(ea, fci_ea, atol=1e-14)) @@ -68,7 +69,7 @@ def test(self): # ewf.kernel() # for f in ewf.fragments: -# ip, ea = f.results.moms +# ip, ea = f.results.moms # cx = f.get_overlap('mo|cluster') # ip = np.einsum('pP,qQ,nPQ->npq', cx, cx, ip) @@ -84,7 +85,6 @@ def test(self): # # self.assertTrue(np.allclose(ip, cc_ip)) # # self.assertTrue(np.allclose(ea, cc_ea)) -if __name__ == '__main__': +if __name__ == "__main__": print("Running %s" % __file__) unittest.main() - diff --git a/vayesta/tests/ewf/test_nelectron_target.py b/vayesta/tests/ewf/test_nelectron_target.py index 027b0c256..a01d1d5a6 100644 --- a/vayesta/tests/ewf/test_nelectron_target.py +++ b/vayesta/tests/ewf/test_nelectron_target.py @@ -8,8 +8,7 @@ class TestCCSD(TestCase): - - solver = 'CCSD' + solver = "CCSD" targets = [9.0, 0.7, 0.3] @classmethod @@ -21,9 +20,15 @@ def setUpClass(cls): def emb(cls, bno_threshold): emb = vayesta.ewf.EWF(cls.mf, solver=cls.solver, bath_options=dict(threshold=bno_threshold)) with emb.sao_fragmentation() as f: - f.add_atomic_fragment(0, nelectron_target=cls.targets[0], nelectron_target_atol=1e-7, nelectron_target_rtol=0) - f.add_atomic_fragment(1, nelectron_target=cls.targets[1], nelectron_target_atol=1e-7, nelectron_target_rtol=0) - f.add_atomic_fragment(2, nelectron_target=cls.targets[2], nelectron_target_atol=1e-7, nelectron_target_rtol=0) + f.add_atomic_fragment( + 0, nelectron_target=cls.targets[0], nelectron_target_atol=1e-7, nelectron_target_rtol=0 + ) + f.add_atomic_fragment( + 1, nelectron_target=cls.targets[1], nelectron_target_atol=1e-7, nelectron_target_rtol=0 + ) + f.add_atomic_fragment( + 2, nelectron_target=cls.targets[2], nelectron_target_atol=1e-7, nelectron_target_rtol=0 + ) emb.kernel() return emb @@ -34,25 +39,24 @@ def tearDownClass(cls): def _test_nelectron_target(self, bno_threshold): emb = self.emb(bno_threshold) - for idx, label in enumerate(['O', 'H', 'H']): + for idx, label in enumerate(["O", "H", "H"]): dm1 = emb.fragments[idx].results.wf.make_rdm1(ao_basis=True) pop = emb.pop_analysis(dm1) - aos = [(('%d %s' % (idx, label)) in ao) for ao in emb.mol.ao_labels()] - if emb.spinsym == 'restricted': + aos = [(("%d %s" % (idx, label)) in ao) for ao in emb.mol.ao_labels()] + if emb.spinsym == "restricted": ne = sum(pop[aos]) - elif emb.spinsym == 'unrestricted': + elif emb.spinsym == "unrestricted": ne = sum(pop[0][aos]) + sum(pop[1][aos]) self.assertAllclose(ne, self.targets[idx]) def test_nelectron_target_dmet_bath(self): return self._test_nelectron_target(np.inf) - #def test_nelectron_target_full_bath(self): + # def test_nelectron_target_full_bath(self): # return self._test_nelectron_target(-np.inf) class TestUCCSD(TestCCSD): - targets = [8.0, 0.7, 0.3] @classmethod @@ -61,21 +65,21 @@ def setUpClass(cls): class TestFCI(TestCCSD): - - solver = 'FCI' + solver = "FCI" @classmethod def setUpClass(cls): cls.mf = testsystems.water_sto3g.rhf() -class TestUFCI(TestUCCSD): - solver = 'FCI' +class TestUFCI(TestUCCSD): + solver = "FCI" @classmethod def setUpClass(cls): cls.mf = testsystems.water_cation_sto3g.rhf() -if __name__ == '__main__': - print('Running %s' % __file__) + +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_rdm_energy.py b/vayesta/tests/ewf/test_rdm_energy.py index d42bb5ace..7870c980c 100644 --- a/vayesta/tests/ewf/test_rdm_energy.py +++ b/vayesta/tests/ewf/test_rdm_energy.py @@ -7,19 +7,18 @@ from vayesta.tests.common import TestCase from vayesta.tests import testsystems -class Test_RHF(TestCase): +class Test_RHF(TestCase): def test(self): - - #RHF + # RHF mf = testsystems.water_631g.rhf() - #CCSD + # CCSD cc = pyscf.cc.CCSD(mf) cc.kernel() - #Full bath EWF - ewf = vayesta.ewf.EWF(mf, bath_options=dict(bathtype='full'), solver_options=dict(solve_lambda=True)) + # Full bath EWF + ewf = vayesta.ewf.EWF(mf, bath_options=dict(bathtype="full"), solver_options=dict(solve_lambda=True)) ewf.kernel() ll = ewf._get_dm_corr_energy_old(global_dm1=False, global_dm2=False) @@ -33,19 +32,18 @@ def test(self): self.assertAlmostEqual(gg, cc.e_corr) self.assertAlmostEqual(ewf.get_dm_energy(), cc.e_tot) -class Test_UHF(TestCase): +class Test_UHF(TestCase): def test(self): - - #RHF + # RHF mf = testsystems.water_cation_631g.uhf() - #CCSD + # CCSD cc = pyscf.cc.UCCSD(mf) cc.kernel() - #Full bath EWF - ewf = vayesta.ewf.EWF(mf, bath_options=dict(bathtype='full'), solver_options=dict(solve_lambda=True)) + # Full bath EWF + ewf = vayesta.ewf.EWF(mf, bath_options=dict(bathtype="full"), solver_options=dict(solve_lambda=True)) ewf.kernel() ll = ewf._get_dm_corr_energy_old(global_dm1=False, global_dm2=False) @@ -59,6 +57,7 @@ def test(self): self.assertAlmostEqual(gg, cc.e_corr) self.assertAlmostEqual(ewf.get_dm_energy(), cc.e_tot) + # def test_h2_solid(self): # # #RHF @@ -83,6 +82,6 @@ def test(self): # self.assertAlmostEqual(gg, cc.e_corr) -if __name__ == '__main__': +if __name__ == "__main__": print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_rpa_bath.py b/vayesta/tests/ewf/test_rpa_bath.py index 64a709712..51aada4d8 100644 --- a/vayesta/tests/ewf/test_rpa_bath.py +++ b/vayesta/tests/ewf/test_rpa_bath.py @@ -7,6 +7,7 @@ from vayesta.tests.common import TestCase import pytest + class TestWaterRHF(TestCase): system = testsystems.water_631g_df @@ -22,20 +23,24 @@ def tearDownClass(cls): @classmethod @cache def emb(cls, bno_threshold, solver): - emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(bathtype="rpa", threshold=bno_threshold), solver=solver, - solver_options=dict(conv_tol=1e-12)) + emb = vayesta.ewf.EWF( + cls.mf, + bath_options=dict(bathtype="rpa", threshold=bno_threshold), + solver=solver, + solver_options=dict(conv_tol=1e-12), + ) emb.kernel() return emb def test_ccsd_rpa_1(self): - eta=10**-(1.5) - emb = self.emb(eta, 'CCSD') + eta = 10 ** -(1.5) + emb = self.emb(eta, "CCSD") emb.kernel() self.assertAllclose(emb.e_tot, -76.10582744548097) @pytest.mark.slow def test_ccsd_rpa_2(self): - eta=1e-2 - emb = self.emb(eta, 'CCSD') + eta = 1e-2 + emb = self.emb(eta, "CCSD") emb.kernel() self.assertAllclose(emb.e_tot, -76.12444492796294) diff --git a/vayesta/tests/ewf/test_rpa_correction.py b/vayesta/tests/ewf/test_rpa_correction.py index 63e9265db..91a0d0e02 100644 --- a/vayesta/tests/ewf/test_rpa_correction.py +++ b/vayesta/tests/ewf/test_rpa_correction.py @@ -8,7 +8,6 @@ class Test_RPA_Corrections_Ethanol_RHF(TestCase): - system = testsystems.ethanol_631g_df @property @@ -16,9 +15,9 @@ def mf(self): return self.system.rhf() def get_nl_energies(self, correction, bathtype="dmet"): - - emb = vayesta.ewf.EWF(self.mf, bath_options={"bathtype":bathtype}, solver="CCSD", - screening="mrpa", ext_rpa_correction=correction) + emb = vayesta.ewf.EWF( + self.mf, bath_options={"bathtype": bathtype}, solver="CCSD", screening="mrpa", ext_rpa_correction=correction + ) with emb.iao_fragmentation() as f: f.add_all_atomic_fragments() emb.kernel() @@ -34,8 +33,10 @@ def test_cumulant_correction(self): self.assertAlmostEqual(erpa, -0.5145262339186916) self.assertAlmostEqual(enl, -0.31553721476368396) + class Test_RPA_Corrections_complete(Test_RPA_Corrections_Ethanol_RHF): """Tests with a complete bath in all clusters. This should give no nonlocal correction in any case.""" + system = testsystems.water_631g_df def test_rpa_correction(self): diff --git a/vayesta/tests/ewf/test_screening.py b/vayesta/tests/ewf/test_screening.py index 0eacdf1e4..25c7ea663 100644 --- a/vayesta/tests/ewf/test_screening.py +++ b/vayesta/tests/ewf/test_screening.py @@ -8,9 +8,8 @@ class TestTwoElectron(TestCase): - system = testsystems.h2_ccpvdz_df - e_ref = {"mrpa":-1.123779303361342, "crpa":-1.1237769151822752} + e_ref = {"mrpa": -1.123779303361342, "crpa": -1.1237769151822752} @classmethod def setUpClass(cls): @@ -24,45 +23,48 @@ def tearDownClass(cls): @classmethod @cache def emb(cls, bno_threshold, solver, screening): - emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold), solver=solver, - screening=screening, solver_options=dict(conv_tol=1e-12)) + emb = vayesta.ewf.EWF( + cls.mf, + bath_options=dict(threshold=bno_threshold), + solver=solver, + screening=screening, + solver_options=dict(conv_tol=1e-12), + ) emb.kernel() return emb def test_ccsd_mrpa(self): - emb = self.emb(np.inf, 'CCSD', 'mrpa') + emb = self.emb(np.inf, "CCSD", "mrpa") emb.kernel() - self.assertAllclose(emb.e_tot, self.e_ref['mrpa']) + self.assertAllclose(emb.e_tot, self.e_ref["mrpa"]) def test_fci_mrpa(self): - emb = self.emb(np.inf, 'FCI', 'mrpa') + emb = self.emb(np.inf, "FCI", "mrpa") emb.kernel() - self.assertAllclose(emb.e_tot, self.e_ref['mrpa']) + self.assertAllclose(emb.e_tot, self.e_ref["mrpa"]) def test_ccsd_crpa(self): - emb = self.emb(np.inf, 'CCSD', 'crpa') + emb = self.emb(np.inf, "CCSD", "crpa") emb.kernel() - self.assertAllclose(emb.e_tot, self.e_ref['crpa']) + self.assertAllclose(emb.e_tot, self.e_ref["crpa"]) def test_fci_crpa(self): - emb = self.emb(np.inf, 'FCI', 'crpa') + emb = self.emb(np.inf, "FCI", "crpa") emb.kernel() - self.assertAllclose(emb.e_tot, self.e_ref['crpa']) + self.assertAllclose(emb.e_tot, self.e_ref["crpa"]) class TestTwoHole(TestTwoElectron): - system = testsystems.f2_sto6g_df - e_ref = {"mrpa":-197.84155758368854, "crpa":-197.83928243962046} + e_ref = {"mrpa": -197.84155758368854, "crpa": -197.83928243962046} class TestTwoHoleRHF(TestTwoElectron): - @classmethod def setUpClass(cls): cls.mf = cls.system.rhf() -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_secfrag.py b/vayesta/tests/ewf/test_secfrag.py index 5d83c5d5d..f5e441a76 100644 --- a/vayesta/tests/ewf/test_secfrag.py +++ b/vayesta/tests/ewf/test_secfrag.py @@ -7,7 +7,6 @@ class Test_Restricted(TestCase): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_631g.rhf() @@ -22,7 +21,7 @@ def tearDownClass(cls): def emb(cls, bno_threshold): emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold, project_dmet_order=0)) with emb.fragmentation() as f: - with f.secondary_fragments(solver='MP2', bno_threshold_factor=0.1): + with f.secondary_fragments(solver="MP2", bno_threshold_factor=0.1): f.add_all_atomic_fragments() emb.kernel() return emb @@ -52,7 +51,7 @@ def setUpClass(cls): def emb_nosym(cls, bno_threshold): emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold, project_dmet_order=0)) with emb.sao_fragmentation() as f: - with f.secondary_fragments(solver='MP2', bno_threshold_factor=0.1): + with f.secondary_fragments(solver="MP2", bno_threshold_factor=0.1): f.add_all_atomic_fragments() emb.kernel() return emb @@ -62,8 +61,8 @@ def emb_nosym(cls, bno_threshold): def emb_rotsym(cls, bno_threshold): emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold, project_dmet_order=0)) with emb.sao_fragmentation() as f: - with f.rotational_symmetry(order=6, axis='z'): - with f.secondary_fragments(solver='MP2', bno_threshold_factor=0.1): + with f.rotational_symmetry(order=6, axis="z"): + with f.secondary_fragments(solver="MP2", bno_threshold_factor=0.1): f.add_atomic_fragment(0) emb.kernel() return emb @@ -74,8 +73,8 @@ def emb_invsym(cls, bno_threshold): emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold, project_dmet_order=0)) with emb.sao_fragmentation() as f: with f.inversion_symmetry(): - with f.rotational_symmetry(order=3, axis='z'): - with f.secondary_fragments(solver='MP2', bno_threshold_factor=0.1): + with f.rotational_symmetry(order=3, axis="z"): + with f.secondary_fragments(solver="MP2", bno_threshold_factor=0.1): f.add_atomic_fragment(0) emb.kernel() return emb @@ -85,8 +84,8 @@ def emb_invsym(cls, bno_threshold): def emb_mirrorsym(cls, bno_threshold): emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold, project_dmet_order=0)) with emb.sao_fragmentation() as f: - with f.mirror_symmetry(axis='x'): - with f.secondary_fragments(solver='MP2', bno_threshold_factor=0.1): + with f.mirror_symmetry(axis="x"): + with f.secondary_fragments(solver="MP2", bno_threshold_factor=0.1): f.add_atomic_fragment(0) f.add_atomic_fragment(1) f.add_atomic_fragment(5) @@ -116,7 +115,6 @@ def test_full_bath(self): class Test_Unrestricted(Test_Restricted): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_cation_631g.uhf() @@ -134,6 +132,6 @@ def test_full_bath(self): self.assertAllclose(emb.e_tot, -75.6830484961464) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_tailoring.py b/vayesta/tests/ewf/test_tailoring.py index 50d68c16d..74df40266 100644 --- a/vayesta/tests/ewf/test_tailoring.py +++ b/vayesta/tests/ewf/test_tailoring.py @@ -7,8 +7,7 @@ class TestRestricted(TestCase): - - correction_type = 'tailor' + correction_type = "tailor" @classmethod def setUpClass(cls): @@ -23,20 +22,20 @@ def tearDownClass(cls): @cache def emb(cls, bno_threshold, tailoring, correction_type=None): correction_type = correction_type or cls.correction_type - solver_opts = dict(conv_tol= 1e-12, conv_tol_normt=1e-8, solve_lambda=False) + solver_opts = dict(conv_tol=1e-12, conv_tol_normt=1e-8, solve_lambda=False) emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold), solver_options=solver_opts) with emb.iao_fragmentation() as f: - fci1 = f.add_atomic_fragment(0, solver='FCI', bath_options=dict(bathtype='dmet'), auxiliary=True) - fci2 = f.add_atomic_fragment(1, solver='FCI', bath_options=dict(bathtype='dmet'), auxiliary=True) - ccsd1 = f.add_atomic_fragment(0, solver='extCCSD') - ccsd2 = f.add_atomic_fragment(1, solver='extCCSD') - if tailoring == 'onsite': + fci1 = f.add_atomic_fragment(0, solver="FCI", bath_options=dict(bathtype="dmet"), auxiliary=True) + fci2 = f.add_atomic_fragment(1, solver="FCI", bath_options=dict(bathtype="dmet"), auxiliary=True) + ccsd1 = f.add_atomic_fragment(0, solver="extCCSD") + ccsd2 = f.add_atomic_fragment(1, solver="extCCSD") + if tailoring == "onsite": ccsd1.add_external_corrections([fci1], correction_type, projectors=0) ccsd2.add_external_corrections([fci2], correction_type, projectors=0) - elif tailoring == 'all-1p': + elif tailoring == "all-1p": ccsd1.add_external_corrections([fci1, fci2], correction_type, projectors=1) ccsd2.add_external_corrections([fci1, fci2], correction_type, projectors=1) - elif tailoring == 'all-2p': + elif tailoring == "all-2p": ccsd1.add_external_corrections([fci1, fci2], correction_type, projectors=2) ccsd2.add_external_corrections([fci1, fci2], correction_type, projectors=2) else: @@ -45,65 +44,63 @@ def emb(cls, bno_threshold, tailoring, correction_type=None): return emb def test_wf_energy_onsite(self): - emb = self.emb(1e-4, 'onsite') + emb = self.emb(1e-4, "onsite") self.assertAllclose(emb.e_corr, -0.43332813177760987, atol=1e-6, rtol=0) def test_dm_energy_onsite(self): - emb = self.emb(1e-4, 'onsite') + emb = self.emb(1e-4, "onsite") self.assertAllclose(emb.get_dm_corr_energy(), -0.4073623273252255, atol=1e-6, rtol=0) def test_wf_energy_all(self): - emb = self.emb(1e-4, 'all-1p') + emb = self.emb(1e-4, "all-1p") self.assertAllclose(emb.e_corr, -0.43328577969028964, atol=1e-6, rtol=0) def test_dm_energy_all(self): - emb = self.emb(1e-4, 'all-1p') + emb = self.emb(1e-4, "all-1p") self.assertAllclose(emb.get_dm_corr_energy(), -0.4067136384165535, atol=1e-6, rtol=0) def test_wf_energy_all_2p(self): - emb = self.emb(1e-4, 'all-2p') + emb = self.emb(1e-4, "all-2p") self.assertAllclose(emb.e_corr, -0.4216611412243197, atol=1e-6, rtol=0) def test_dm_energy_all_2p(self): - emb = self.emb(1e-4, 'all-2p') + emb = self.emb(1e-4, "all-2p") self.assertAllclose(emb.get_dm_corr_energy(), -0.4109557775015299, atol=1e-6, rtol=0) class TestUnrestrictedS0(TestRestricted): - @classmethod def setUpClass(cls): cls.mf = testsystems.n2_sto_150pm.rhf().to_uhf() class TestUnrestrictedS4(TestRestricted): - @classmethod def setUpClass(cls): cls.mf = testsystems.n2_sto_s4_150pm.uhf() def test_wf_energy_onsite(self): - emb = self.emb(1e-4, 'onsite') + emb = self.emb(1e-4, "onsite") self.assertAllclose(emb.e_corr, -0.20294262284592862, atol=1e-6, rtol=0) def test_dm_energy_onsite(self): - emb = self.emb(1e-4, 'onsite') + emb = self.emb(1e-4, "onsite") self.assertAllclose(emb.get_dm_corr_energy(), -0.2187131950991378, atol=1e-6, rtol=0) def test_wf_energy_all(self): - emb = self.emb(1e-4, 'all-1p') + emb = self.emb(1e-4, "all-1p") self.assertAllclose(emb.e_corr, -0.20279235812388702, atol=1e-6, rtol=0) def test_dm_energy_all(self): - emb = self.emb(1e-4, 'all-1p') + emb = self.emb(1e-4, "all-1p") self.assertAllclose(emb.get_dm_corr_energy(), -0.21845089572304, atol=1e-6, rtol=0) def test_wf_energy_all_2p(self): - emb = self.emb(1e-4, 'all-2p') + emb = self.emb(1e-4, "all-2p") self.assertAllclose(emb.e_corr, -0.2025278277300585, atol=1e-6, rtol=0) def test_dm_energy_all_2p(self): - emb = self.emb(1e-4, 'all-2p') + emb = self.emb(1e-4, "all-2p") self.assertAllclose(emb.get_dm_corr_energy(), -0.21849821198673974, atol=1e-6, rtol=0) @@ -111,33 +108,33 @@ def test_dm_energy_all_2p(self): class TestExternalCorrection(TestRestricted): """TODO: replace test values, once implemented.""" - correction_type = 'external' + correction_type = "external" def test_wf_energy_onsite(self): - emb = self.emb(1e-4, 'onsite') + emb = self.emb(1e-4, "onsite") self.assertAllclose(emb.e_corr, -0.43332813177760987, atol=1e-6, rtol=0) def test_dm_energy_onsite(self): - emb = self.emb(1e-4, 'onsite') + emb = self.emb(1e-4, "onsite") self.assertAllclose(emb.get_dm_corr_energy(), -0.4073623273252255, atol=1e-6, rtol=0) def test_wf_energy_all(self): - emb = self.emb(1e-4, 'all-1p') + emb = self.emb(1e-4, "all-1p") self.assertAllclose(emb.e_corr, -0.43328577969028964, atol=1e-6, rtol=0) def test_dm_energy_all(self): - emb = self.emb(1e-4, 'all-1p') + emb = self.emb(1e-4, "all-1p") self.assertAllclose(emb.get_dm_corr_energy(), -0.4067136384165535, atol=1e-6, rtol=0) def test_wf_energy_all_2p(self): - emb = self.emb(1e-4, 'all-2p') + emb = self.emb(1e-4, "all-2p") self.assertAllclose(emb.e_corr, -0.4216611412243197, atol=1e-6, rtol=0) def test_dm_energy_all_2p(self): - emb = self.emb(1e-4, 'all-2p') + emb = self.emb(1e-4, "all-2p") self.assertAllclose(emb.get_dm_corr_energy(), -0.4109557775015299, atol=1e-6, rtol=0) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_water.py b/vayesta/tests/ewf/test_water.py index cb32a2807..6ba16bb08 100644 --- a/vayesta/tests/ewf/test_water.py +++ b/vayesta/tests/ewf/test_water.py @@ -3,9 +3,9 @@ from vayesta.tests.ewf import test_h2 from vayesta.tests import testsystems + @pytest.mark.slow class Test_MP2(test_h2.Test_MP2): - system = testsystems.water_631g @classmethod @@ -13,9 +13,9 @@ def setUpClass(cls): cls.mf = cls.system.rhf() cls.cc = cls.system.rmp2() + @pytest.mark.slow class Test_CCSD(test_h2.Test_CCSD): - system = testsystems.water_631g @classmethod @@ -23,9 +23,9 @@ def setUpClass(cls): cls.mf = cls.system.rhf() cls.cc = cls.system.rccsd() + @pytest.mark.slow class Test_UMP2(test_h2.Test_UMP2): - system = testsystems.water_cation_631g @classmethod @@ -33,9 +33,9 @@ def setUpClass(cls): cls.mf = cls.system.uhf() cls.cc = cls.system.ump2() + @pytest.mark.slow class Test_UCCSD(test_h2.Test_UCCSD): - system = testsystems.water_cation_631g @classmethod @@ -44,6 +44,6 @@ def setUpClass(cls): cls.cc = cls.system.uccsd() -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/ewf/test_water_sao.py b/vayesta/tests/ewf/test_water_sao.py index 49a3437d8..764310cd8 100644 --- a/vayesta/tests/ewf/test_water_sao.py +++ b/vayesta/tests/ewf/test_water_sao.py @@ -7,8 +7,8 @@ from vayesta.tests import testsystems from vayesta.tests.common import TestCase -class Test_MP2(test_h2.Test_MP2): +class Test_MP2(test_h2.Test_MP2): @classmethod def setUpClass(cls): cls.mf = testsystems.water_631g.rhf() @@ -17,7 +17,7 @@ def setUpClass(cls): @classmethod @cache def emb(cls, bno_threshold): - emb = vayesta.ewf.EWF(cls.mf, solver='MP2', bath_options=dict(threshold=bno_threshold)) + emb = vayesta.ewf.EWF(cls.mf, solver="MP2", bath_options=dict(threshold=bno_threshold)) with emb.sao_fragmentation() as f: f.add_all_atomic_fragments() emb.kernel() @@ -43,8 +43,8 @@ def test_dmet_energy_full_bath(self): etot_dmet = emb.get_dmet_energy(part_cumulant=False) self.assertAllclose(etot_dmet, self.cc.e_tot, rtol=0) # Not implemented: - #etot_dmet = emb.get_dmet_energy(approx_cumulant=False) - #self.assertAllclose(etot_dmet, self.cc.e_tot, rtol=0) + # etot_dmet = emb.get_dmet_energy(approx_cumulant=False) + # self.assertAllclose(etot_dmet, self.cc.e_tot, rtol=0) def test_dmet_energy_dmet_bath(self): emb = self.emb(np.inf) @@ -54,7 +54,6 @@ def test_dmet_energy_dmet_bath(self): @pytest.mark.slow class Test_CCSD(test_h2.Test_CCSD): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_631g.rhf() @@ -63,9 +62,8 @@ def setUpClass(cls): @classmethod @cache def emb(cls, bno_threshold): - solver_opts = dict(conv_tol= 1e-10, conv_tol_normt=1e-8, solve_lambda=True) - emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold), - solver_options=solver_opts) + solver_opts = dict(conv_tol=1e-10, conv_tol_normt=1e-8, solve_lambda=True) + emb = vayesta.ewf.EWF(cls.mf, bath_options=dict(threshold=bno_threshold), solver_options=solver_opts) with emb.sao_fragmentation() as f: f.add_all_atomic_fragments() emb.kernel() @@ -100,7 +98,6 @@ def test_dm2_demo(self): class Test_UMP2(test_h2.Test_UMP2): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_cation_631g.uhf() @@ -109,7 +106,7 @@ def setUpClass(cls): @classmethod @cache def emb(cls, bno_threshold): - emb = vayesta.ewf.EWF(cls.mf, solver='MP2', bath_options=dict(threshold=bno_threshold)) + emb = vayesta.ewf.EWF(cls.mf, solver="MP2", bath_options=dict(threshold=bno_threshold)) with emb.sao_fragmentation() as f: f.add_all_atomic_fragments() emb.kernel() @@ -118,7 +115,6 @@ def emb(cls, bno_threshold): @pytest.mark.slow class Test_UCCSD(Test_CCSD, test_h2.Test_UCCSD): - @classmethod def setUpClass(cls): cls.mf = testsystems.water_cation_631g.uhf() @@ -132,7 +128,6 @@ def test_dmet_energy_dmet_bath(self): @pytest.mark.slow class Test_RCCSD_vs_UCCSD(TestCase): - @classmethod def setUpClass(cls): cls.rhf = testsystems.water_631g.rhf() @@ -152,9 +147,8 @@ def tearDownClass(cls): @classmethod @cache def remb(cls, bno_threshold): - solver_opts = dict(conv_tol= 1e-10, conv_tol_normt=1e-8, solve_lambda=True) - emb = vayesta.ewf.EWF(cls.rhf, bath_options=dict(threshold=bno_threshold), - solver_options=solver_opts) + solver_opts = dict(conv_tol=1e-10, conv_tol_normt=1e-8, solve_lambda=True) + emb = vayesta.ewf.EWF(cls.rhf, bath_options=dict(threshold=bno_threshold), solver_options=solver_opts) with emb.sao_fragmentation() as f: f.add_all_atomic_fragments() emb.kernel() @@ -163,9 +157,8 @@ def remb(cls, bno_threshold): @classmethod @cache def uemb(cls, bno_threshold): - solver_opts = dict(conv_tol= 1e-10, conv_tol_normt=1e-8, solve_lambda=True) - emb = vayesta.ewf.EWF(cls.uhf, bath_options=dict(threshold=bno_threshold), - solver_options=solver_opts) + solver_opts = dict(conv_tol=1e-10, conv_tol_normt=1e-8, solve_lambda=True) + emb = vayesta.ewf.EWF(cls.uhf, bath_options=dict(threshold=bno_threshold), solver_options=solver_opts) with emb.sao_fragmentation() as f: f.add_all_atomic_fragments() emb.kernel() @@ -193,7 +186,7 @@ def test_dm1_demo(self): def test_dm2_demo(self): rdm2 = self.remb(-1).make_rdm2(ao_basis=True) udm2aa, udm2ab, udm2bb = self.uemb(-1).make_rdm2(ao_basis=True) - udm2 = (udm2aa + udm2ab + udm2ab.transpose(2,3,0,1) + udm2bb) + udm2 = udm2aa + udm2ab + udm2ab.transpose(2, 3, 0, 1) + udm2bb self.assertAllclose(rdm2, udm2) def test_dmet_energy_full_bath(self): @@ -228,6 +221,6 @@ def test_dmet_energy_dmet_bath(self): self.assertAllclose(e_rdmet, e_udmet) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/latt/test_bethe.py b/vayesta/tests/latt/test_bethe.py index 21104e89e..7b271b3cb 100644 --- a/vayesta/tests/latt/test_bethe.py +++ b/vayesta/tests/latt/test_bethe.py @@ -11,48 +11,45 @@ class BetheTests(TestCase): PLACES_DOCC = 8 def _test_bethe(self, t, u, known_values): - """Test the Bethe lattice for a given T, U. - """ + """Test the Bethe lattice for a given T, U.""" e = bethe.hubbard1d_bethe_energy(t, u) - self.assertAlmostEqual(e, known_values['energy'], self.PLACES_ENERGY) + self.assertAlmostEqual(e, known_values["energy"], self.PLACES_ENERGY) d = bethe.hubbard1d_bethe_docc(t, u) - self.assertAlmostEqual(d, known_values['docc'], self.PLACES_DOCC) + self.assertAlmostEqual(d, known_values["docc"], self.PLACES_DOCC) d1 = bethe.hubbard1d_bethe_docc_numdiff(t, u, du=1e-12, order=1) - self.assertAlmostEqual(d1, known_values['docc-numdiff1'], self.PLACES_DOCC) + self.assertAlmostEqual(d1, known_values["docc-numdiff1"], self.PLACES_DOCC) d2 = bethe.hubbard1d_bethe_docc_numdiff(t, u, du=1e-12, order=2) - self.assertAlmostEqual(d2, known_values['docc-numdiff2'], self.PLACES_DOCC) + self.assertAlmostEqual(d2, known_values["docc-numdiff2"], self.PLACES_DOCC) def test_bethe_T1_U0(self): - """Test the T=1, U=0 Bethe lattice. - """ + """Test the T=1, U=0 Bethe lattice.""" known_values = { - 'energy': -1.2732565954632262, - 'docc': 0.2499999972419595, - 'docc-numdiff1': 0.25002222514558525, - 'docc-numdiff2': 0.2500916140846243, + "energy": -1.2732565954632262, + "docc": 0.2499999972419595, + "docc-numdiff1": 0.25002222514558525, + "docc-numdiff2": 0.2500916140846243, } self._test_bethe(1.0, 0.0, known_values) def test_bethe_T2_U4(self): - """Test the T=2, U=4 Bethe lattice. - """ + """Test the T=2, U=4 Bethe lattice.""" known_values = { - 'energy': -1.688748682251278, - 'docc': 0.17545254464835117, - 'docc-numdiff1': 0.17530421558831222, - 'docc-numdiff2': 0.17510992655900282, + "energy": -1.688748682251278, + "docc": 0.17545254464835117, + "docc-numdiff1": 0.17530421558831222, + "docc-numdiff2": 0.17510992655900282, } self._test_bethe(2.0, 4.0, known_values) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/latt/test_hubbard1d.py b/vayesta/tests/latt/test_hubbard1d.py index 45534f768..aa0e7fa21 100644 --- a/vayesta/tests/latt/test_hubbard1d.py +++ b/vayesta/tests/latt/test_hubbard1d.py @@ -26,7 +26,7 @@ def test_fock(self): f0 = self.mf.get_fock() f0 = np.einsum("pq,pi,qj->ij", f0, self.mf.mo_coeff.conj(), self.mf.mo_coeff) f1 = np.diag(self.mf.mo_energy) - self.assertAlmostEqual(np.max(np.abs(f0-f1)), 0.0, 8) + self.assertAlmostEqual(np.max(np.abs(f0 - f1)), 0.0, 8) @pytest.mark.fast @@ -59,6 +59,6 @@ def setUpClass(cls): cls.known_values = {"e_tot": 7.0557280900008275} -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/latt/test_hubbard2d.py b/vayesta/tests/latt/test_hubbard2d.py index 8bbfe8911..a782a5351 100644 --- a/vayesta/tests/latt/test_hubbard2d.py +++ b/vayesta/tests/latt/test_hubbard2d.py @@ -10,7 +10,7 @@ class Hubbard2DTests_6_0(TestCase): @classmethod def setUpClass(cls): - cls.mol = lattmod.Hubbard2D((6, 6), hubbard_u=0.0, nelectron=6*6) + cls.mol = lattmod.Hubbard2D((6, 6), hubbard_u=0.0, nelectron=6 * 6) cls.mf = lattmod.LatticeMF(cls.mol) cls.mf.kernel() cls.known_values = {"e_tot": -59.7128129211020} @@ -26,14 +26,14 @@ def test_fock(self): f0 = self.mf.get_fock() f0 = np.einsum("pq,pi,qj->ij", f0, self.mf.mo_coeff.conj(), self.mf.mo_coeff) f1 = np.diag(self.mf.mo_energy) - self.assertAlmostEqual(np.max(np.abs(f0-f1)), 0.0, 8) + self.assertAlmostEqual(np.max(np.abs(f0 - f1)), 0.0, 8) @pytest.mark.fast class Hubbard2DTests_10_0(Hubbard2DTests_6_0): @classmethod def setUpClass(cls): - cls.mol = lattmod.Hubbard2D((10, 10), hubbard_u=0.0, nelectron=10*10) + cls.mol = lattmod.Hubbard2D((10, 10), hubbard_u=0.0, nelectron=10 * 10) cls.mf = lattmod.LatticeMF(cls.mol) cls.mf.kernel() cls.known_values = {"e_tot": -163.45383275624567} @@ -43,7 +43,7 @@ def setUpClass(cls): class Hubbard2DTests_6_4(Hubbard2DTests_6_0): @classmethod def setUpClass(cls): - cls.mol = lattmod.Hubbard2D((6, 6), hubbard_u=4.0, nelectron=6*6) + cls.mol = lattmod.Hubbard2D((6, 6), hubbard_u=4.0, nelectron=6 * 6) cls.mf = lattmod.LatticeMF(cls.mol) cls.mf.kernel() cls.known_values = {"e_tot": -23.712812921102035} @@ -53,12 +53,12 @@ def setUpClass(cls): class Hubbard2DTests_6_8(Hubbard2DTests_6_0): @classmethod def setUpClass(cls): - cls.mol = lattmod.Hubbard2D((6, 6), hubbard_u=8.0, nelectron=6*6) + cls.mol = lattmod.Hubbard2D((6, 6), hubbard_u=8.0, nelectron=6 * 6) cls.mf = lattmod.LatticeMF(cls.mol) cls.mf.kernel() cls.known_values = {"e_tot": 12.28718707889798} -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/misc/test_molstructs.py b/vayesta/tests/misc/test_molstructs.py index 8ed6350b5..61637e8f3 100644 --- a/vayesta/tests/misc/test_molstructs.py +++ b/vayesta/tests/misc/test_molstructs.py @@ -14,179 +14,166 @@ class MolstructsTests(TestCase): PLACES_ENERGY = 7 def _test_nuclear_energy(self, mol, known_values): - """Test the nuclear energy. - """ + """Test the nuclear energy.""" - self.assertAlmostEqual(mol.energy_nuc(), known_values['e_nuc'], self.PLACES_ENERGY) + self.assertAlmostEqual(mol.energy_nuc(), known_values["e_nuc"], self.PLACES_ENERGY) def test_water(self): - """Tests for water molecule. - """ + """Tests for water molecule.""" mol = gto.M(atom=molecules.water(), verbose=0) known_values = { - 'e_nuc': 9.189533762934902, - 'e_tot': -74.96302313846098, + "e_nuc": 9.189533762934902, + "e_tot": -74.96302313846098, } self._test_nuclear_energy(mol, known_values) def test_alkane(self): - """Tests for alkane molecules. - """ + """Tests for alkane molecules.""" mols = [ - gto.M(atom=molecules.alkane(3), verbose=0), - gto.M(atom=molecules.alkane(3, numbering='atom'), verbose=0), - gto.M(atom=molecules.alkane(3, numbering='unit'), verbose=0), + gto.M(atom=molecules.alkane(3), verbose=0), + gto.M(atom=molecules.alkane(3, numbering="atom"), verbose=0), + gto.M(atom=molecules.alkane(3, numbering="unit"), verbose=0), ] known_values = { - 'e_nuc': 82.6933759181699, - 'e_tot': -116.88512481584637, + "e_nuc": 82.6933759181699, + "e_tot": -116.88512481584637, } for mol in mols: self._test_nuclear_energy(mol, known_values) def test_arene(self): - """Tests for arene molecules. - """ + """Tests for arene molecules.""" mol = gto.M(atom=molecules.arene(4), verbose=0) known_values = { - 'e_nuc': 102.06071721540009, - 'e_tot': -151.66864765196442, + "e_nuc": 102.06071721540009, + "e_tot": -151.66864765196442, } self._test_nuclear_energy(mol, known_values) def test_neopentane(self): - """Tests for neopentane molecule. - """ + """Tests for neopentane molecule.""" mol = gto.M(atom=molecules.neopentane(), verbose=0) known_values = { - 'e_nuc': 198.347989543373, - 'e_tot': -194.04575839870574, + "e_nuc": 198.347989543373, + "e_tot": -194.04575839870574, } self._test_nuclear_energy(mol, known_values) def test_boronene(self): - """Tests for boronene molecule. - """ + """Tests for boronene molecule.""" mol = gto.M(atom=molecules.boronene(), verbose=0) known_values = { - 'e_nuc': 1788.3645271898426, + "e_nuc": 1788.3645271898426, } self._test_nuclear_energy(mol, known_values) def test_coronene(self): - """Tests for coronene molecule. - """ + """Tests for coronene molecule.""" mol = gto.M(atom=molecules.coronene(), verbose=0) known_values = { - 'e_nuc': 1837.229262707072, + "e_nuc": 1837.229262707072, } self._test_nuclear_energy(mol, known_values) def test_no2(self): - """Tests for NO2 molecule. - """ + """Tests for NO2 molecule.""" mol = gto.M(atom=molecules.no2(), spin=1, verbose=0) known_values = { - 'e_nuc': 65.07473745355408, - #'e_tot': -201.27201791520167, # UHF not very stable + "e_nuc": 65.07473745355408, + #'e_tot': -201.27201791520167, # UHF not very stable } self._test_nuclear_energy(mol, known_values) def test_diamond(self): - """Tests for diamond cell. - """ + """Tests for diamond cell.""" a, atom = solids.diamond() - mol = pbc_gto.Cell(atom=atom, a=a, verbose=0, basis='gth-szv-molopt-sr', pseudo='gth-pade') + mol = pbc_gto.Cell(atom=atom, a=a, verbose=0, basis="gth-szv-molopt-sr", pseudo="gth-pade") mol.exp_to_discard = 0.1 mol.build() known_values = { - 'e_nuc': -12.775667300117394, + "e_nuc": -12.775667300117394, } self._test_nuclear_energy(mol, known_values) def test_graphene(self): - """Tests for graphene cell. - """ + """Tests for graphene cell.""" a, atom = solids.graphene() - mol = pbc_gto.Cell(atom=atom, a=a, verbose=0, basis='gth-szv-molopt-sr', pseudo='gth-pade') + mol = pbc_gto.Cell(atom=atom, a=a, verbose=0, basis="gth-szv-molopt-sr", pseudo="gth-pade") mol.exp_to_discard = 0.1 mol.build() known_values = { - 'e_nuc': 47.86219216629114, + "e_nuc": 47.86219216629114, } self._test_nuclear_energy(mol, known_values) def test_graphite(self): - """Tests for graphite cell. - """ + """Tests for graphite cell.""" a, atom = solids.graphite() - mol = pbc_gto.Cell(atom=atom, a=a, verbose=0, basis='gth-szv-molopt-sr', pseudo='gth-pade') + mol = pbc_gto.Cell(atom=atom, a=a, verbose=0, basis="gth-szv-molopt-sr", pseudo="gth-pade") mol.exp_to_discard = 0.1 mol.build() known_values = { - 'e_nuc': -16.925780021798708, + "e_nuc": -16.925780021798708, } self._test_nuclear_energy(mol, known_values) def test_rocksalt(self): - """Tests for rocksalt cell. - """ + """Tests for rocksalt cell.""" - a, atom = solids.rocksalt(unitcell='cubic') - mol = pbc_gto.Cell(atom=atom, a=a, verbose=0, basis='gth-szv-molopt-sr', pseudo='gth-pade') + a, atom = solids.rocksalt(unitcell="cubic") + mol = pbc_gto.Cell(atom=atom, a=a, verbose=0, basis="gth-szv-molopt-sr", pseudo="gth-pade") mol.exp_to_discard = 0.1 mol.build() known_values = { - 'e_nuc': -137.60716858207616, + "e_nuc": -137.60716858207616, } self._test_nuclear_energy(mol, known_values) - a, atom = solids.rocksalt(unitcell='primitive') - mol = pbc_gto.Cell(atom=atom, a=a, verbose=0, basis='gth-szv-molopt-sr', pseudo='gth-pade') + a, atom = solids.rocksalt(unitcell="primitive") + mol = pbc_gto.Cell(atom=atom, a=a, verbose=0, basis="gth-szv-molopt-sr", pseudo="gth-pade") mol.exp_to_discard = 0.1 mol.build() known_values = { - 'e_nuc': -34.4017921624733, + "e_nuc": -34.4017921624733, } self._test_nuclear_energy(mol, known_values) def test_perovskite(self): - """Tests for perovskite cell. - """ + """Tests for perovskite cell.""" a, atom = solids.perovskite() - mol = pbc_gto.Cell(atom=atom, a=a, verbose=0, basis='gth-szv-molopt-sr', pseudo='gth-pade') + mol = pbc_gto.Cell(atom=atom, a=a, verbose=0, basis="gth-szv-molopt-sr", pseudo="gth-pade") mol.exp_to_discard = 0.1 mol.build() known_values = { - 'e_nuc': -106.25339755801713, + "e_nuc": -106.25339755801713, } self._test_nuclear_energy(mol, known_values) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/misc/test_varembedding.py b/vayesta/tests/misc/test_varembedding.py index 38fca10b4..47f85ebd0 100644 --- a/vayesta/tests/misc/test_varembedding.py +++ b/vayesta/tests/misc/test_varembedding.py @@ -8,6 +8,7 @@ from vayesta.tests.common import TestCase from vayesta.tests import testsystems + class Test_UHF_var_emb(TestCase): @classmethod def setUpClass(cls): @@ -15,9 +16,8 @@ def setUpClass(cls): import pygnme except ImportError: pytest.skip("Variational Embedding requires pygnme") - + cls.mf = testsystems.heli_631g.uhf() - @classmethod def tearDownClass(cls): @@ -25,11 +25,11 @@ def tearDownClass(cls): def test_unrestr_regression_var_emb(self): emb = vayesta.ewf.EWF(self.mf) - with emb.iao_fragmentation(minao='sto-6g') as f: - fci_frags = f.add_all_atomic_fragments(solver='FCI', - bath_options=dict(bathtype='dmet'), auxiliary=True) + with emb.iao_fragmentation(minao="sto-6g") as f: + fci_frags = f.add_all_atomic_fragments(solver="FCI", bath_options=dict(bathtype="dmet"), auxiliary=True) emb.kernel() from vayesta.misc.variational_embedding import variational_params + h, s, dm = variational_params.get_wf_couplings(emb, inc_mf=False) w_bare, _, _ = lib.linalg_helper.safe_eigh(h, s, lindep=1e-12) # Return lowest eigenvalue. @@ -39,6 +39,7 @@ def test_unrestr_regression_var_emb(self): self.assertAlmostEqual(e_opt, -10.275860643502385) self.assertAlmostEqual(emb.e_tot, -10.274941481117565) + class Test_RHF_var_emb(TestCase): @classmethod def setUpClass(cls): @@ -55,13 +56,15 @@ def tearDownClass(cls): def test_restr_regression_var_emb(self): emb = vayesta.ewf.EWF(self.mf) - with emb.iao_fragmentation(minao='sto-6g') as f: - with f.rotational_symmetry(3, axis='z'): - fci_frags = f.add_atomic_fragment([0,1], solver='FCI', - bath_options=dict(bathtype='dmet'), auxiliary=True) + with emb.iao_fragmentation(minao="sto-6g") as f: + with f.rotational_symmetry(3, axis="z"): + fci_frags = f.add_atomic_fragment( + [0, 1], solver="FCI", bath_options=dict(bathtype="dmet"), auxiliary=True + ) emb.kernel() from vayesta.misc.variational_embedding import variational_params + h, s, dm = variational_params.get_wf_couplings(emb, inc_mf=False) w_bare, _, _ = lib.linalg_helper.safe_eigh(h, s, lindep=1e-12) # Return lowest eigenvalue. diff --git a/vayesta/tests/rpa/test_rirpa_hubbard.py b/vayesta/tests/rpa/test_rirpa_hubbard.py index bb46d7cd4..83c344bc9 100644 --- a/vayesta/tests/rpa/test_rirpa_hubbard.py +++ b/vayesta/tests/rpa/test_rirpa_hubbard.py @@ -9,25 +9,22 @@ class MoleculeRPATest(TestCase): PLACES = 8 def _test_energy(self, emb, known_values): - """Test the RPA energy. - """ + """Test the RPA energy.""" - self.assertAlmostEqual(emb.e_tot, known_values['e_tot'], self.PLACES) + self.assertAlmostEqual(emb.e_tot, known_values["e_tot"], self.PLACES) def _test_mom0(self, rpa_orig, rirpa): - """Test that the RPA and RIRPA zeroth moments agree. - """ + """Test that the RPA and RIRPA zeroth moments agree.""" rim0, error_est = rirpa.kernel_moms(3) self.assertAlmostEqual(abs(rim0 - rpa_orig.gen_moms(3)).max(), 0.0, self.PLACES) def test_14_u4(self): - """Tests for N=14 U=4 Hubbard model. - """ + """Tests for N=14 U=4 Hubbard model.""" - key = 'hubb_14_u4_df' - known_values_drpa = {'e_tot': -7.776536889696544} + key = "hubb_14_u4_df" + known_values_drpa = {"e_tot": -7.776536889696544} emb = rpa.ssRPA(getattr(testsystems, key).rhf()) emb.kernel() @@ -38,6 +35,6 @@ def test_14_u4(self): self._test_mom0(emb, rirpa) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/rpa/test_rirpa_molecule.py b/vayesta/tests/rpa/test_rirpa_molecule.py index 8c7dbcf6f..e3de24a88 100644 --- a/vayesta/tests/rpa/test_rirpa_molecule.py +++ b/vayesta/tests/rpa/test_rirpa_molecule.py @@ -9,19 +9,16 @@ class MoleculeRPATest(TestCase): PLACES = 8 def _test_energy(self, emb, known_values): - """Test the RPA energy. - """ + """Test the RPA energy.""" - self.assertAlmostEqual(emb.e_tot, known_values['e_tot'], self.PLACES) + self.assertAlmostEqual(emb.e_tot, known_values["e_tot"], self.PLACES) def _test_mom0(self, rpa_orig, rirpa_moms): - """Test that the RPA and RIRPA zeroth moments agree. - """ + """Test that the RPA and RIRPA zeroth moments agree.""" self.assertAlmostEqual(abs(rirpa_moms - rpa_orig.gen_moms(3)).max(), 0.0, self.PLACES) def test_n2_ccpvdz_dRIRPA(self): - """Tests for LiH cc-pvdz with dRIRPA. - """ + """Tests for LiH cc-pvdz with dRIRPA.""" orig_rpa = rpa.ssRPA(testsystems.n2_ccpvdz_df.rhf()) orig_rpa.kernel() @@ -45,11 +42,13 @@ def test_n2_ccpvdz_dRIRPA_error_estimates(self): # Use number of points where errors will be meaningful. # Note that with this few points the fact that the quadrature optimisation is not invariant to orbital rotations # can cause issues, so we can just use a specific grid spacing. - rirpa_moms, error_est = rirpa.kernel_moms(0, analytic_lower_bound=True, npoints=4, ainit=1.5214230673470202, - opt_quad=False) + rirpa_moms, error_est = rirpa.kernel_moms( + 0, analytic_lower_bound=True, npoints=4, ainit=1.5214230673470202, opt_quad=False + ) self.assertAlmostEqual(error_est[0], 0.05074756294730469) self.assertAlmostEqual(error_est[1], 0.00024838720802440015) -if __name__ == '__main__': - print('Running %s' % __file__) + +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/rpa/test_rirpa_solids.py b/vayesta/tests/rpa/test_rirpa_solids.py index d82672a88..be2e326fa 100644 --- a/vayesta/tests/rpa/test_rirpa_solids.py +++ b/vayesta/tests/rpa/test_rirpa_solids.py @@ -14,15 +14,13 @@ def setUpClass(cls): cls.known_results = dict(e_tot=-149.51936410641733, e_corr=-0.19193623440986585) def _test_energy(self, myrpa): - """Test the RPA energy. - """ + """Test the RPA energy.""" self.assertAlmostEqual(myrpa.e_corr, self.known_results["e_corr"], self.PLACES) self.assertAlmostEqual(myrpa.e_tot, self.known_results["e_tot"], self.PLACES) @pytest.mark.slow def test_energy_rhf_opt(self): - """Tests for diamond with optimised RHF dRPA code. - """ + """Tests for diamond with optimised RHF dRPA code.""" rirpa = rpa.rirpa.ssRIdRRPA(self.sys.rhf()) rirpa.kernel_energy() @@ -30,8 +28,7 @@ def test_energy_rhf_opt(self): @pytest.mark.fast def test_energy_rhf_generic(self): - """Tests for diamond with generic RHF RIRPA code. - """ + """Tests for diamond with generic RHF RIRPA code.""" rirpa = rpa.rirpa.ssRIRRPA(self.sys.rhf()) rirpa.kernel_energy() @@ -39,8 +36,7 @@ def test_energy_rhf_generic(self): @pytest.mark.slow def test_energy_uhf(self): - """Tests for diamond with generic UHF RIRPA code. - """ + """Tests for diamond with generic UHF RIRPA code.""" rirpa = rpa.rirpa.ssRIURPA(self.sys.uhf()) rirpa.kernel_energy() @@ -54,9 +50,10 @@ def test_rhf_moments(self): mom0_opt = opt_rirpa.kernel_moms(0)[0] self.assertAllclose(mom0_gen, mom0_opt, self.PLACES) + @pytest.mark.slow class GrapheneRIRPATest(DiamondRIRPATest): @classmethod def setUpClass(cls): cls.sys = testsystems.graphene_sto3g_s211 - cls.known_results = dict(e_tot=-150.15057360171875, e_corr=-0.17724246753903117) \ No newline at end of file + cls.known_results = dict(e_tot=-150.15057360171875, e_corr=-0.17724246753903117) diff --git a/vayesta/tests/rpa/test_rpa_hubbard.py b/vayesta/tests/rpa/test_rpa_hubbard.py index cf1ed21b0..ce3d5230f 100644 --- a/vayesta/tests/rpa/test_rpa_hubbard.py +++ b/vayesta/tests/rpa/test_rpa_hubbard.py @@ -9,25 +9,23 @@ class MoleculeRPATest(TestCase): PLACES = 8 def _test_energy(self, emb, known_values): - """Test the RPA energy. - """ + """Test the RPA energy.""" - self.assertAlmostEqual(emb.e_tot, known_values['e_tot'], self.PLACES) + self.assertAlmostEqual(emb.e_tot, known_values["e_tot"], self.PLACES) def test_6_u0(self): - """Tests for N=6 U=0 Hubbard model. - """ + """Tests for N=6 U=0 Hubbard model.""" - key = 'hubb_6_u0' - known_values_rpax = {'e_tot': -8.0} - known_values_drpa = {'e_tot': -8.0} + key = "hubb_6_u0" + known_values_rpax = {"e_tot": -8.0} + known_values_drpa = {"e_tot": -8.0} emb = rpa.RPA(getattr(testsystems, key).rhf()) - emb.kernel('rpax') + emb.kernel("rpax") self._test_energy(emb, known_values_rpax) emb = rpa.RPA(getattr(testsystems, key).rhf()) - emb.kernel('drpa') + emb.kernel("drpa") self._test_energy(emb, known_values_drpa) emb = rpa.ssRPA(getattr(testsystems, key).rhf()) @@ -35,19 +33,18 @@ def test_6_u0(self): self._test_energy(emb, known_values_drpa) def test_10_u2(self): - """Tests for N=10 U=2 Hubbard model. - """ + """Tests for N=10 U=2 Hubbard model.""" - key = 'hubb_10_u2' - known_values_rpax = {'e_tot': -9.064312326273644} - known_values_drpa = {'e_tot': -8.824440982421532} + key = "hubb_10_u2" + known_values_rpax = {"e_tot": -9.064312326273644} + known_values_drpa = {"e_tot": -8.824440982421532} emb = rpa.RPA(getattr(testsystems, key).rhf()) - emb.kernel('rpax') + emb.kernel("rpax") self._test_energy(emb, known_values_rpax) emb = rpa.RPA(getattr(testsystems, key).rhf()) - emb.kernel('drpa') + emb.kernel("drpa") self._test_energy(emb, known_values_drpa) emb = rpa.ssRPA(getattr(testsystems, key).rhf()) @@ -55,19 +52,18 @@ def test_10_u2(self): self._test_energy(emb, known_values_drpa) def test_6x6_u0(self): - """Tests for 6x6 U=0 Hubbard model. - """ + """Tests for 6x6 U=0 Hubbard model.""" - key = 'hubb_6x6_u0_1x1imp' - known_values_rpax = {'e_tot': -56.0} - known_values_drpa = {'e_tot': -56.0} + key = "hubb_6x6_u0_1x1imp" + known_values_rpax = {"e_tot": -56.0} + known_values_drpa = {"e_tot": -56.0} emb = rpa.RPA(getattr(testsystems, key).rhf()) - emb.kernel('rpax') + emb.kernel("rpax") self._test_energy(emb, known_values_rpax) emb = rpa.RPA(getattr(testsystems, key).rhf()) - emb.kernel('drpa') + emb.kernel("drpa") self._test_energy(emb, known_values_drpa) emb = rpa.ssRPA(getattr(testsystems, key).rhf()) @@ -75,19 +71,18 @@ def test_6x6_u0(self): self._test_energy(emb, known_values_drpa) def test_6x6_u2(self): - """Tests for 6x6 U=2 Hubbard model. - """ + """Tests for 6x6 U=2 Hubbard model.""" - key = 'hubb_6x6_u2_1x1imp' - known_values_rpax = {'e_tot': -48.314526436495500} - known_values_drpa = {'e_tot': -48.740268837302494} + key = "hubb_6x6_u2_1x1imp" + known_values_rpax = {"e_tot": -48.314526436495500} + known_values_drpa = {"e_tot": -48.740268837302494} emb = rpa.RPA(getattr(testsystems, key).rhf()) - emb.kernel('rpax') + emb.kernel("rpax") self._test_energy(emb, known_values_rpax) emb = rpa.RPA(getattr(testsystems, key).rhf()) - emb.kernel('drpa') + emb.kernel("drpa") self._test_energy(emb, known_values_drpa) emb = rpa.ssRPA(getattr(testsystems, key).rhf()) @@ -95,7 +90,6 @@ def test_6x6_u2(self): self._test_energy(emb, known_values_drpa) - -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/rpa/test_rpa_molecule.py b/vayesta/tests/rpa/test_rpa_molecule.py index 54c6e23ae..f5ef213d6 100644 --- a/vayesta/tests/rpa/test_rpa_molecule.py +++ b/vayesta/tests/rpa/test_rpa_molecule.py @@ -9,28 +9,25 @@ class MoleculeRPATest(TestCase): PLACES = 8 def _test_energy(self, emb, known_values): - """Test the RPA energy. - """ + """Test the RPA energy.""" - self.assertAlmostEqual(emb.e_tot, known_values['e_tot'], self.PLACES) + self.assertAlmostEqual(emb.e_tot, known_values["e_tot"], self.PLACES) def test_lih_ccpvdz_RPAX(self): - """Tests for LiH cc-pvdz with RPAX. - """ + """Tests for LiH cc-pvdz with RPAX.""" emb = rpa.RPA(testsystems.lih_ccpvdz.rhf()) - emb.kernel('rpax') + emb.kernel("rpax") known_values = {"e_tot": -8.021765296851472} self._test_energy(emb, known_values) def test_lih_ccpvdz_dRPA(self): - """Tests for LiH cc-pvdz with dPRA. - """ + """Tests for LiH cc-pvdz with dPRA.""" emb = rpa.RPA(testsystems.lih_ccpvdz.rhf()) - emb.kernel('drpa') + emb.kernel("drpa") known_values = {"e_tot": -8.015594007709575} @@ -42,8 +39,6 @@ def test_lih_ccpvdz_dRPA(self): self._test_energy(emb, known_values) - - -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/solver/test_ccsd.py b/vayesta/tests/solver/test_ccsd.py index ee606832a..c8122f3ee 100644 --- a/vayesta/tests/solver/test_ccsd.py +++ b/vayesta/tests/solver/test_ccsd.py @@ -13,11 +13,10 @@ @pytest.mark.fast class TestSolvers(TestCase): - def _test(self, key): mf = getattr(getattr(testsystems, key[0]), key[1])() - emb = vayesta.ewf.EWF(mf, solver='CCSD', bath_options=dict(bathtype='full')) + emb = vayesta.ewf.EWF(mf, solver="CCSD", bath_options=dict(bathtype="full")) emb.kernel() cc = pyscf.cc.CCSD(mf) @@ -27,18 +26,18 @@ def _test(self, key): self.assertAlmostEqual(emb.e_tot, cc.e_tot) def test_rccsd_h2(self): - return self._test(('h2_ccpvdz', 'rhf')) + return self._test(("h2_ccpvdz", "rhf")) def test_rccsd_h2_df(self): - return self._test(('h2_ccpvdz_df', 'rhf')) + return self._test(("h2_ccpvdz_df", "rhf")) def test_uccsd_h3(self): - return self._test(('h3_ccpvdz', 'uhf')) + return self._test(("h3_ccpvdz", "uhf")) def test_uccsd_h3_df(self): - return self._test(('h3_ccpvdz_df', 'uhf')) + return self._test(("h3_ccpvdz_df", "uhf")) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/solver/test_cisd.py b/vayesta/tests/solver/test_cisd.py index fc4c6f95e..af311173c 100644 --- a/vayesta/tests/solver/test_cisd.py +++ b/vayesta/tests/solver/test_cisd.py @@ -13,12 +13,11 @@ @pytest.mark.fast class TestSolvers(TestCase): - def _test(self, key): mf = getattr(getattr(testsystems, key[0]), key[1])() solver_opts = dict(conv_tol=1e-10) - emb = vayesta.ewf.EWF(mf, solver='CISD', bath_options=dict(bathtype='full'), solver_options=solver_opts) + emb = vayesta.ewf.EWF(mf, solver="CISD", bath_options=dict(bathtype="full"), solver_options=solver_opts) emb.kernel() ci = pyscf.ci.CISD(mf) @@ -29,12 +28,12 @@ def _test(self, key): self.assertAlmostEqual(emb.e_tot, ci.e_tot) def test_rcisd_h2(self): - return self._test(('h2_ccpvdz', 'rhf')) + return self._test(("h2_ccpvdz", "rhf")) def test_ucisd_h3(self): - return self._test(('h3_ccpvdz', 'uhf')) + return self._test(("h3_ccpvdz", "uhf")) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/solver/test_ebcc.py b/vayesta/tests/solver/test_ebcc.py index 7147d56a5..8e2276ab2 100644 --- a/vayesta/tests/solver/test_ebcc.py +++ b/vayesta/tests/solver/test_ebcc.py @@ -21,8 +21,12 @@ def _test(self, system, mf, ansatz): # Test a complete bath calculation with given ansatz reproduces full calculation. mymf = getattr(getattr(testsystems, system), mf)() - emb = vayesta.ewf.EWF(mymf, solver=f'EB{ansatz}', bath_options=dict(bathtype='full'), - solver_options=dict(solve_lambda=False, store_as_ccsd=False)) + emb = vayesta.ewf.EWF( + mymf, + solver=f"EB{ansatz}", + bath_options=dict(bathtype="full"), + solver_options=dict(solve_lambda=False, store_as_ccsd=False), + ) emb.kernel() import ebcc @@ -34,26 +38,26 @@ def _test(self, system, mf, ansatz): @pytest.mark.fast def test_rccsd_h2(self): - return self._test('h2_ccpvdz', 'rhf', 'CCSD') + return self._test("h2_ccpvdz", "rhf", "CCSD") @pytest.mark.fast def test_rccsd_water_sto3g(self): - return self._test('water_sto3g', 'rhf', 'CCSD') + return self._test("water_sto3g", "rhf", "CCSD") @pytest.mark.fast def test_uccsd_water_sto3g(self): - return self._test('water_sto3g', 'uhf', 'CCSD') + return self._test("water_sto3g", "uhf", "CCSD") def test_uccsd_water_cation_sto3g(self): - return self._test('water_cation_sto3g', 'uhf', 'CCSD') + return self._test("water_cation_sto3g", "uhf", "CCSD") @pytest.mark.fast def test_rccsdt_water_sto3g(self): - return self._test('water_sto3g', 'rhf', 'CCSDT') + return self._test("water_sto3g", "rhf", "CCSDT") @pytest.mark.slow def test_uccsdt_water_cation_sto3g(self): - return self._test('water_cation_sto3g', 'uhf', 'CCSDT') + return self._test("water_cation_sto3g", "uhf", "CCSDT") class TestEBCCActSpace(TestCase): @@ -64,17 +68,22 @@ def setUpClass(cls): except ImportError: pytest.skip("Requires ebcc") - def _test(self, system, mf, actansatz, fullansatz, bathtype='dmet', setcas=False): + def _test(self, system, mf, actansatz, fullansatz, bathtype="dmet", setcas=False): # Test that active space calculation with complete active space reproduces equivalent calculation using higher- # level approach of active space. This defaults to a DMET bath space. mymf = getattr(getattr(testsystems, system), mf)() - embfull = vayesta.ewf.EWF(mymf, solver=f'EB{fullansatz}', bath_options=dict(bathtype=bathtype), - solver_options=dict(solve_lambda=False)) + embfull = vayesta.ewf.EWF( + mymf, + solver=f"EB{fullansatz}", + bath_options=dict(bathtype=bathtype), + solver_options=dict(solve_lambda=False), + ) embfull.kernel() - embact = vayesta.ewf.EWF(mymf, solver=f'EB{actansatz}', bath_options=dict(bathtype=bathtype), - solver_options=dict(solve_lambda=False)) + embact = vayesta.ewf.EWF( + mymf, solver=f"EB{actansatz}", bath_options=dict(bathtype=bathtype), solver_options=dict(solve_lambda=False) + ) if setcas: # Set up fragmentation, then set CAS to complete cluster space in previous calculation. with embact.iao_fragmentation() as f: @@ -89,14 +98,14 @@ def _test(self, system, mf, actansatz, fullansatz, bathtype='dmet', setcas=False @pytest.mark.fast def test_rccsdtprime_water_sto3g_dmet(self): - return self._test('water_sto3g', 'rhf', "CCSDt'", 'CCSDT', bathtype='dmet', setcas=False) + return self._test("water_sto3g", "rhf", "CCSDt'", "CCSDT", bathtype="dmet", setcas=False) def test_uccsdtprime_water_sto3g_dmet(self): - return self._test('water_sto3g', 'uhf', "CCSDt'", 'CCSDT', bathtype='dmet', setcas=False) + return self._test("water_sto3g", "uhf", "CCSDt'", "CCSDT", bathtype="dmet", setcas=False) @pytest.mark.slow def test_rccsdtprime_h4_sto3g_setcas_full(self): - return self._test('h4_sto3g', 'rhf', "CCSDt'", 'CCSDT', bathtype='full', setcas=True) + return self._test("h4_sto3g", "rhf", "CCSDt'", "CCSDT", bathtype="full", setcas=True) def test_uccsdtprime_h3_sto3g_setcas_full(self): - return self._test('h3_sto3g', 'uhf', "CCSDt'", 'CCSDT', bathtype='full', setcas=True) + return self._test("h3_sto3g", "uhf", "CCSDt'", "CCSDT", bathtype="full", setcas=True) diff --git a/vayesta/tests/solver/test_fci.py b/vayesta/tests/solver/test_fci.py index 341253719..2b88c675b 100644 --- a/vayesta/tests/solver/test_fci.py +++ b/vayesta/tests/solver/test_fci.py @@ -11,11 +11,10 @@ @pytest.mark.fast class TestSolvers(unittest.TestCase): - def _test(self, key, ss=None, places=8): mf = getattr(getattr(testsystems, key[0]), key[1])() - emb = vayesta.ewf.EWF(mf, solver='FCI', bath_options=dict(bathtype='full'), solver_options={'conv_tol' : 1e-12}) + emb = vayesta.ewf.EWF(mf, solver="FCI", bath_options=dict(bathtype="full"), solver_options={"conv_tol": 1e-12}) emb.kernel() fci = pyscf.fci.FCI(mf) @@ -24,7 +23,7 @@ def _test(self, key, ss=None, places=8): if ss is not None: fci = pyscf.fci.addons.fix_spin_(fci, ss=ss) e_fci, ci = fci.kernel() - fci.e_corr = (fci.e_tot - mf.e_tot) + fci.e_corr = fci.e_tot - mf.e_tot self.assertAlmostEqual(emb.e_corr, fci.e_corr, places=places) self.assertAlmostEqual(emb.e_tot, fci.e_tot, places=places) @@ -32,18 +31,18 @@ def _test(self, key, ss=None, places=8): # TODO: Why inaccurate? def test_rfci_h2(self): - return self._test(('h2_ccpvdz', 'rhf'), ss=0, places=4) + return self._test(("h2_ccpvdz", "rhf"), ss=0, places=4) def test_rfci_h2_df(self): - return self._test(('h2_ccpvdz_df', 'rhf'), ss=0, places=4) + return self._test(("h2_ccpvdz_df", "rhf"), ss=0, places=4) def test_ufci_h3(self): - return self._test(('h3_ccpvdz', 'uhf')) + return self._test(("h3_ccpvdz", "uhf")) def test_ufci_h3_df(self): - return self._test(('h3_ccpvdz_df', 'uhf'), places=4) + return self._test(("h3_ccpvdz_df", "uhf"), places=4) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/solver/test_mp2.py b/vayesta/tests/solver/test_mp2.py index 4566994ef..fe37a5822 100644 --- a/vayesta/tests/solver/test_mp2.py +++ b/vayesta/tests/solver/test_mp2.py @@ -11,11 +11,10 @@ @pytest.mark.fast class TestSolvers(unittest.TestCase): - def _test(self, key): mf = getattr(getattr(testsystems, key[0]), key[1])() - emb = vayesta.ewf.EWF(mf, solver='MP2', bath_options=dict(bathtype='full')) + emb = vayesta.ewf.EWF(mf, solver="MP2", bath_options=dict(bathtype="full")) emb.kernel() mp2 = pyscf.mp.MP2(mf) @@ -25,18 +24,18 @@ def _test(self, key): self.assertAlmostEqual(emb.e_tot, mp2.e_tot) def test_rmp2_h2o(self): - return self._test(('water_ccpvdz', 'rhf')) + return self._test(("water_ccpvdz", "rhf")) def test_rmp2_h2o_df(self): - return self._test(('water_ccpvdz_df', 'rhf')) + return self._test(("water_ccpvdz_df", "rhf")) def test_ump2_h2o(self): - return self._test(('water_cation_631g', 'uhf')) + return self._test(("water_cation_631g", "uhf")) def test_ump2_h2o_df(self): - return self._test(('water_cation_631g_df', 'uhf')) + return self._test(("water_cation_631g_df", "uhf")) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) unittest.main() diff --git a/vayesta/tests/testsets/test_testset.py b/vayesta/tests/testsets/test_testset.py index 80ea428fd..2f3789680 100644 --- a/vayesta/tests/testsets/test_testset.py +++ b/vayesta/tests/testsets/test_testset.py @@ -12,7 +12,7 @@ from vayesta.tests.common import TestCase -Result = namedtuple('Result', ['ncluster_mean', 'ncluster_max', 'energy_dm_error', 'energy_wf_error', 'time']) +Result = namedtuple("Result", ["ncluster_mean", "ncluster_max", "energy_dm_error", "energy_wf_error", "time"]) def _run_hf(mol): @@ -23,6 +23,7 @@ def _run_hf(mol): hf.kernel() return hf + def _run_mp2(hf): if hf.mol.spin > 0: mp2 = pyscf.mp.UMP2(hf) @@ -31,6 +32,7 @@ def _run_mp2(hf): mp2.kernel() return mp2 + def _run_ccsd(hf): if hf.mol.spin > 0: cc = pyscf.cc.UCCSD(hf) @@ -39,15 +41,15 @@ def _run_ccsd(hf): cc.kernel() return cc -class Test_TestSet(TestCase): +class Test_TestSet(TestCase): @classmethod def setUpClass(cls): cls.results = defaultdict(dict) @classmethod def tearDownClass(cls): - cls.analyze('%s.out' % cls.__name__) + cls.analyze("%s.out" % cls.__name__) @classmethod def analyze(self, filename): @@ -74,7 +76,7 @@ def analyze(self, filename): e_wf_rmse[idx] = np.sqrt(e_wf_rmse[idx] / len(samples)) t_mean[idx] = t_mean[idx] / len(samples) data = np.vstack((n_mean, n_max, e_wf_mae, e_dm_mae, e_wf_rmse, e_dm_rmse, t_mean)).T - np.savetxt(filename, data, fmt='%.8e') + np.savetxt(filename, data, fmt="%.8e") @classmethod def get_embedding(cls, hf, *args, **kwargs): @@ -82,16 +84,15 @@ def get_embedding(cls, hf, *args, **kwargs): @classmethod def add_tests(cls, testset, solver, basis, petas, **kwargs): - kwargs['min_atoms'] = kwargs.get('min_atoms', 2) + kwargs["min_atoms"] = kwargs.get("min_atoms", 2) for key, mol in testset.loop(basis=basis, **kwargs): for peta in petas: cls.add_test(mol, key, solver, peta) @classmethod def add_test(cls, mol, key, solver, peta): - - name = 'test_%s_%d' % (key, 10*peta) - eta = 10.0**(-peta) + name = "test_%s_%d" % (key, 10 * peta) + eta = 10.0 ** (-peta) def test(self): print("Testing system %s" % key) @@ -99,9 +100,9 @@ def test(self): hf = _run_hf(mol) self.assertTrue(hf.converged) # --- Benchmark - if solver == 'MP2': + if solver == "MP2": cc = _run_mp2(hf) - elif solver == 'CCSD': + elif solver == "CCSD": cc = _run_ccsd(hf) self.assertTrue(cc.converged) else: @@ -120,22 +121,23 @@ def test(self): print("Adding test %s" % name) setattr(cls, name, test) + class Test_W411_DZ(Test_TestSet): pass -class Test_W411_DZ_project_dmet(Test_TestSet): +class Test_W411_DZ_project_dmet(Test_TestSet): @classmethod def get_embedding(cls, hf, *args, **kwargs): - kwargs['bath_options']['project_dmet'] = 'full' + kwargs["bath_options"]["project_dmet"] = "full" return vayesta.ewf.EWF(hf, *args, **kwargs) -if __name__ == '__main__': - print('Running %s' % __file__) +if __name__ == "__main__": + print("Running %s" % __file__) petas = np.arange(3.0, 9.1, 0.5) - Test_W411_DZ.add_tests(gmtkn55.W411, 'MP2', 'cc-pvdz', petas) - Test_W411_DZ_project_dmet.add_tests(gmtkn55.W411, 'MP2', 'cc-pvdz', petas) + Test_W411_DZ.add_tests(gmtkn55.W411, "MP2", "cc-pvdz", petas) + Test_W411_DZ_project_dmet.add_tests(gmtkn55.W411, "MP2", "cc-pvdz", petas) unittest.main() diff --git a/vayesta/tests/testsystems.py b/vayesta/tests/testsystems.py index d2a2f79ee..d7910ea58 100644 --- a/vayesta/tests/testsystems.py +++ b/vayesta/tests/testsystems.py @@ -2,11 +2,13 @@ from functools import cache except ImportError: from functools import lru_cache + cache = lru_cache(maxsize=None) import numpy as np import copy import pyscf + # Open boundary import pyscf.gto import pyscf.scf @@ -14,6 +16,7 @@ import pyscf.cc import pyscf.fci import pyscf.tools.ring + # Periodic boundary import pyscf.pbc import pyscf.pbc.gto @@ -28,9 +31,9 @@ PYSCF_VERBOSITY = 0 + class TestMolecule: - """Molecular test system. - """ + """Molecular test system.""" def __init__(self, atom, basis, auxbasis=None, verbose=PYSCF_VERBOSITY, **kwargs): super().__init__() @@ -157,8 +160,20 @@ def ufci(self): class TestSolid: """Solid test system.""" - def __init__(self, a, atom, basis, kmesh=None, auxbasis=None, supercell=None, exxdiv='ewald', df='gdf', - precision=1e-10, verbose=PYSCF_VERBOSITY, **kwargs): + def __init__( + self, + a, + atom, + basis, + kmesh=None, + auxbasis=None, + supercell=None, + exxdiv="ewald", + df="gdf", + precision=1e-10, + verbose=PYSCF_VERBOSITY, + **kwargs, + ): super().__init__() mol = pyscf.pbc.gto.Cell() mol.a = a @@ -187,9 +202,9 @@ def rhf(self): rhf = pyscf.pbc.scf.RHF(self.mol) else: rhf = pyscf.pbc.scf.KRHF(self.mol, self.kpts) - if self.df == 'gdf': + if self.df == "gdf": rhf = rhf.density_fit(auxbasis=self.auxbasis) - elif self.df == 'rsgdf': + elif self.df == "rsgdf": rhf = rhf.rs_density_fit(auxbasis=self.auxbasis) rhf.conv_tol = 1e-10 rhf.conv_tol_grad = 1e-8 @@ -204,9 +219,9 @@ def uhf(self): uhf = pyscf.pbc.scf.UHF(self.mol) else: uhf = pyscf.pbc.scf.KUHF(self.mol, self.kpts) - if self.df == 'gdf': + if self.df == "gdf": uhf = uhf.density_fit(auxbasis=self.auxbasis) - elif self.df == 'rsgdf': + elif self.df == "rsgdf": uhf = uhf.rs_density_fit(auxbasis=self.auxbasis) uhf.conv_tol = 1e-10 uhf.conv_tol_grad = 1e-8 @@ -273,38 +288,37 @@ def uccsd(self): class TestLattice: - """Lattice test system. - """ + """Lattice test system.""" def __init__( - self, - nsite, - nelectron=None, - spin=0, - order=None, - boundary="pbc", - tiles=(1, 1), - verbose=0, - with_df=False, - **kwargs, + self, + nsite, + nelectron=None, + spin=0, + order=None, + boundary="pbc", + tiles=(1, 1), + verbose=0, + with_df=False, + **kwargs, ): super().__init__() if isinstance(nsite, int): mol = latt.Hubbard1D( - nsite, - nelectron=nelectron, - spin=spin, - order=order, - boundary=boundary, + nsite, + nelectron=nelectron, + spin=spin, + order=order, + boundary=boundary, ) else: mol = latt.Hubbard2D( - nsite, - nelectron=nelectron, - spin=spin, - order=order, - tiles=tiles, - boundary=boundary, + nsite, + nelectron=nelectron, + spin=spin, + order=order, + tiles=tiles, + boundary=boundary, ) mol.verbose = verbose for key, val in kwargs.items(): @@ -342,22 +356,22 @@ def uhf(self): h2_sto3g_dissoc = TestMolecule("H 0 0 0; H 0 0 10.0", basis="sto3g") h6_sto6g = TestMolecule( - atom=["H %f %f %f" % xyz for xyz in pyscf.tools.ring.make(6, 1.0)], - basis="sto6g", + atom=["H %f %f %f" % xyz for xyz in pyscf.tools.ring.make(6, 1.0)], + basis="sto6g", ) h6_sto6g_df = TestMolecule( - atom=["H %f %f %f" % xyz for xyz in pyscf.tools.ring.make(6, 1.0)], - basis="sto6g", - auxbasis="weigend", + atom=["H %f %f %f" % xyz for xyz in pyscf.tools.ring.make(6, 1.0)], + basis="sto6g", + auxbasis="weigend", ) -water_sto3g = TestMolecule(atom=molecules.water(), basis='sto3g') -water_cation_sto3g = TestMolecule(atom=molecules.water(), basis='sto3g', charge=1, spin=1) +water_sto3g = TestMolecule(atom=molecules.water(), basis="sto3g") +water_cation_sto3g = TestMolecule(atom=molecules.water(), basis="sto3g", charge=1, spin=1) -water_631g = TestMolecule(atom=molecules.water(), basis='6-31G', incore_anyway=True) -water_cation_631g = TestMolecule(atom=molecules.water(), basis='6-31G', charge=1, spin=1, incore_anyway=True) -water_631g_df = TestMolecule(atom=molecules.water(), basis='6-31G', auxbasis='6-31G') -water_cation_631g_df = TestMolecule(atom=molecules.water(), basis='6-31G', auxbasis='6-31G', charge=1, spin=1) +water_631g = TestMolecule(atom=molecules.water(), basis="6-31G", incore_anyway=True) +water_cation_631g = TestMolecule(atom=molecules.water(), basis="6-31G", charge=1, spin=1, incore_anyway=True) +water_631g_df = TestMolecule(atom=molecules.water(), basis="6-31G", auxbasis="6-31G") +water_cation_631g_df = TestMolecule(atom=molecules.water(), basis="6-31G", auxbasis="6-31G", charge=1, spin=1) water_ccpvdz = TestMolecule(atom=molecules.water(), basis="cc-pvdz") water_ccpvdz_df = TestMolecule(atom=molecules.water(), basis="cc-pvdz", auxbasis="cc-pvdz-jkfit") @@ -377,50 +391,50 @@ def uhf(self): h4_sto3g = TestMolecule(atom="H1 0 0 0; H2 0 0 1.0; H3 0 1.0 0; H4 0 1.0 1.0", basis="sto3g", spin=0) -heli_631g = TestMolecule(atom="He 0 0 0; Li 0 0 2.0", basis='6-31G', spin=1) -h6_dz = TestMolecule(atom=molecules.ring('H', 6, 1.0), basis='cc-pVDZ') +heli_631g = TestMolecule(atom="He 0 0 0; Li 0 0 2.0", basis="6-31G", spin=1) +h6_dz = TestMolecule(atom=molecules.ring("H", 6, 1.0), basis="cc-pVDZ") -atoms = 'N 0 0 -0.75; N 0 0 0.75' -n2_sto_150pm = TestMolecule(atoms, basis='cc-pvdz') -n2_sto_s4_150pm = TestMolecule(atoms, basis='cc-pvdz', spin=4) +atoms = "N 0 0 -0.75; N 0 0 0.75" +n2_sto_150pm = TestMolecule(atoms, basis="cc-pvdz") +n2_sto_s4_150pm = TestMolecule(atoms, basis="cc-pvdz", spin=4) n2_ccpvdz_df = TestMolecule("N1 0 0 0; N2 0 0 1.1", basis="cc-pvdz", auxbasis="cc-pvdz-jkfit") -f2_sto6g = TestMolecule(atom="F 0 0 0; F 0 0 1.2", basis='sto-6g') -f2_sto6g_df = TestMolecule(atom="F 0 0 0; F 0 0 1.2", basis='sto-6g', auxbasis=True) +f2_sto6g = TestMolecule(atom="F 0 0 0; F 0 0 1.2", basis="sto-6g") +f2_sto6g_df = TestMolecule(atom="F 0 0 0; F 0 0 1.2", basis="sto-6g", auxbasis=True) # Solids -a = 2*np.eye(3) -a[2,2] = 4 +a = 2 * np.eye(3) +a[2, 2] = 4 nk = 3 -opts = dict(basis='sto-3g', auxbasis='sto-3g', exp_to_discard=0.1) +opts = dict(basis="sto-3g", auxbasis="sto-3g", exp_to_discard=0.1) -h2_sto3g_k311 = TestSolid(a=a, atom='H 0 0 0 ; H 0 0 0.74', kmesh=(nk,1,1), **opts) -h2_sto3g_s311 = TestSolid(a=a, atom='H 0 0 0 ; H 0 0 0.74', supercell=(nk,1,1), **opts) +h2_sto3g_k311 = TestSolid(a=a, atom="H 0 0 0 ; H 0 0 0.74", kmesh=(nk, 1, 1), **opts) +h2_sto3g_s311 = TestSolid(a=a, atom="H 0 0 0 ; H 0 0 0.74", supercell=(nk, 1, 1), **opts) -h3_sto3g_k311 = TestSolid(a=a, atom='H 0 0 0 ; H 0 0 1 ; H 0 0 2', kmesh=(nk,1,1), spin=3, **opts) -h3_sto3g_s311 = TestSolid(a=a, atom='H 0 0 0 ; H 0 0 1 ; H 0 0 2', supercell=(nk,1,1), spin=3, **opts) +h3_sto3g_k311 = TestSolid(a=a, atom="H 0 0 0 ; H 0 0 1 ; H 0 0 2", kmesh=(nk, 1, 1), spin=3, **opts) +h3_sto3g_s311 = TestSolid(a=a, atom="H 0 0 0 ; H 0 0 1 ; H 0 0 2", supercell=(nk, 1, 1), spin=3, **opts) a = a.copy() -a[2,2] = 15.0 -opts['dimension'] = 2 -h2_sto3g_k31 = TestSolid(a=a, atom='H 0 0 0 ; H 0 0 0.74', kmesh=(nk,1,1), **opts) -h2_sto3g_s31 = TestSolid(a=a, atom='H 0 0 0 ; H 0 0 0.74', supercell=(nk,1,1), **opts) +a[2, 2] = 15.0 +opts["dimension"] = 2 +h2_sto3g_k31 = TestSolid(a=a, atom="H 0 0 0 ; H 0 0 0.74", kmesh=(nk, 1, 1), **opts) +h2_sto3g_s31 = TestSolid(a=a, atom="H 0 0 0 ; H 0 0 0.74", supercell=(nk, 1, 1), **opts) -h3_sto3g_k31 = TestSolid(a=a, atom='H 0 0 0 ; H 0 0 1 ; H 0 0 2', kmesh=(nk,1,1), spin=3, **opts) -h3_sto3g_s31 = TestSolid(a=a, atom='H 0 0 0 ; H 0 0 1 ; H 0 0 2', supercell=(nk,1,1), spin=3, **opts) +h3_sto3g_k31 = TestSolid(a=a, atom="H 0 0 0 ; H 0 0 1 ; H 0 0 2", kmesh=(nk, 1, 1), spin=3, **opts) +h3_sto3g_s31 = TestSolid(a=a, atom="H 0 0 0 ; H 0 0 1 ; H 0 0 2", supercell=(nk, 1, 1), spin=3, **opts) a, atom = solids.diamond() -opts = dict(basis='sto-3g', auxbasis='sto-3g', exp_to_discard=0.1) -mesh = (2,1,1) +opts = dict(basis="sto-3g", auxbasis="sto-3g", exp_to_discard=0.1) +mesh = (2, 1, 1) diamond_sto3g_k211 = TestSolid(a=a, atom=atom, kmesh=mesh, **opts) diamond_sto3g_s211 = TestSolid(a=a, atom=atom, supercell=mesh, **opts) a, atom = solids.diamond() -opts = dict(basis='sto3g', auxbasis='sto3g', exp_to_discard=0.1) -mesh = (3,3,3) +opts = dict(basis="sto3g", auxbasis="sto3g", exp_to_discard=0.1) +mesh = (3, 3, 3) diamond_sto3g_k333 = TestSolid(a=a, atom=atom, kmesh=mesh, **opts) diamond_sto3g_s333 = TestSolid(a=a, atom=atom, supercell=mesh, **opts) @@ -428,8 +442,12 @@ def uhf(self): a[2, 2] = 20.0 # TODO: precision lowered to 1e-15 due to bug in PySCF v2.1 # Use precision 1e-8 again once this is fixed -he_k32 = TestSolid(a, atom="He 0 0 0", dimension=2, basis="def2-svp", auxbasis="def2-svp-ri", precision=1e-15, kmesh=(3, 2, 1)) -he_s32 = TestSolid(a, atom="He 0 0 0", dimension=2, basis="def2-svp", auxbasis="def2-svp-ri", precision=1e-15, supercell=(3, 2, 1)) +he_k32 = TestSolid( + a, atom="He 0 0 0", dimension=2, basis="def2-svp", auxbasis="def2-svp-ri", precision=1e-15, kmesh=(3, 2, 1) +) +he_s32 = TestSolid( + a, atom="He 0 0 0", dimension=2, basis="def2-svp", auxbasis="def2-svp-ri", precision=1e-15, supercell=(3, 2, 1) +) a = np.eye(3) * 3.0 a[1, 1] = a[2, 2] = 30.0 @@ -450,8 +468,8 @@ def uhf(self): he_s321 = TestSolid(a, atom="He 0 0 0", basis="def2-svp", auxbasis="def2-svp-ri", supercell=(3, 2, 1)) a, atom = solids.graphene() -opts = dict(basis='sto3g', auxbasis='sto3g', exp_to_discard=0.1, dimension=2) -mesh = (2,1,1) +opts = dict(basis="sto3g", auxbasis="sto3g", exp_to_discard=0.1, dimension=2) +mesh = (2, 1, 1) graphene_sto3g_k211 = TestSolid(a=a, atom=atom, kmesh=mesh, **opts) graphene_sto3g_s211 = TestSolid(a=a, atom=atom, supercell=mesh, **opts) @@ -460,12 +478,12 @@ def uhf(self): hubb_6_u0 = TestLattice(6, hubbard_u=0.0, nelectron=6) hubb_10_u2 = TestLattice(10, hubbard_u=2.0, nelectron=10) -hubb_10_u4 = TestLattice(10, hubbard_u=4.0, nelectron=16, boundary='apbc') -hubb_14_u4 = TestLattice(14, hubbard_u=4.0, nelectron=14, boundary='pbc') -hubb_14_u4_df = TestLattice(14, hubbard_u=4.0, nelectron=14, boundary='pbc', with_df=True) - -hubb_6x6_u0_1x1imp = TestLattice((6, 6), hubbard_u=0.0, nelectron=26, tiles=(1, 1), boundary='pbc') -hubb_6x6_u2_1x1imp = TestLattice((6, 6), hubbard_u=2.0, nelectron=26, tiles=(1, 1), boundary='pbc') -hubb_6x6_u6_1x1imp = TestLattice((6, 6), hubbard_u=6.0, nelectron=26, tiles=(1, 1), boundary='pbc') -hubb_8x8_u2_2x2imp = TestLattice((8, 8), hubbard_u=2.0, nelectron=50, tiles=(2, 2), boundary='pbc') -hubb_8x8_u2_2x1imp = TestLattice((8, 8), hubbard_u=2.0, nelectron=50, tiles=(2, 1), boundary='pbc') +hubb_10_u4 = TestLattice(10, hubbard_u=4.0, nelectron=16, boundary="apbc") +hubb_14_u4 = TestLattice(14, hubbard_u=4.0, nelectron=14, boundary="pbc") +hubb_14_u4_df = TestLattice(14, hubbard_u=4.0, nelectron=14, boundary="pbc", with_df=True) + +hubb_6x6_u0_1x1imp = TestLattice((6, 6), hubbard_u=0.0, nelectron=26, tiles=(1, 1), boundary="pbc") +hubb_6x6_u2_1x1imp = TestLattice((6, 6), hubbard_u=2.0, nelectron=26, tiles=(1, 1), boundary="pbc") +hubb_6x6_u6_1x1imp = TestLattice((6, 6), hubbard_u=6.0, nelectron=26, tiles=(1, 1), boundary="pbc") +hubb_8x8_u2_2x2imp = TestLattice((8, 8), hubbard_u=2.0, nelectron=50, tiles=(2, 2), boundary="pbc") +hubb_8x8_u2_2x1imp = TestLattice((8, 8), hubbard_u=2.0, nelectron=50, tiles=(2, 1), boundary="pbc") diff --git a/vayesta/tools/eos.py b/vayesta/tools/eos.py index 61a2d2eb3..533f03b68 100755 --- a/vayesta/tools/eos.py +++ b/vayesta/tools/eos.py @@ -9,27 +9,28 @@ HARTREE2JOULE = 4.359744e-18 ANGSTROM2METER = 1e-10 # Hartree per cubic Angstrom to Giga-Pascal -HPCA2GPA = (HARTREE2JOULE/ANGSTROM2METER**3) / 1e9 +HPCA2GPA = (HARTREE2JOULE / ANGSTROM2METER**3) / 1e9 def parabola(v, e0, v0, b0): """Quadratic equation of state.""" - ev = 0.5*b0/v0*(v-v0)**2 + e0 + ev = 0.5 * b0 / v0 * (v - v0) ** 2 + e0 return ev def birch_murnaghan(v, e0, v0, b0, bp): """Third-order Birch-Murnaghan equation of states.""" - vr = (v0/v)**(2.0/3) - t1 = (vr-1)**3 - t2 = (vr-1)**2 * (6-4*vr) - ev = e0 + 9/16*v0*b0 * (t1*bp + t2) + vr = (v0 / v) ** (2.0 / 3) + t1 = (vr - 1) ** 3 + t2 = (vr - 1) ** 2 * (6 - 4 * vr) + ev = e0 + 9 / 16 * v0 * b0 * (t1 * bp + t2) return ev @dataclasses.dataclass class FitResult: """DOC""" + e0: float = None b0: float = None x0: float = None @@ -39,13 +40,13 @@ class FitResult: def __repr__(self): txt = "Fit results: E0= %.4f Ha" % self.e0 if self.x0 is not None: - txt += (" x0= %.4f A" % self.x0) + txt += " x0= %.4f A" % self.x0 if self.v0 is not None: - txt += (" V0= %.4f A^3" % self.v0) + txt += " V0= %.4f A^3" % self.v0 if self.b0 is not None: - txt += (" B0= %.4f GPa" % self.b0) + txt += " B0= %.4f GPa" % self.b0 if self.bp is not None: - txt += (" B'= %.4f" % self.bp) + txt += " B'= %.4f" % self.bp return txt @@ -71,20 +72,25 @@ def fit_eos(volumes, energies, fitfunc=birch_murnaghan, plot=True, value_at=None if plot: try: from matplotlib import pyplot as plt + grid = np.linspace(volumes[0], volumes[-1], 100) y = fitfunc(grid, *popt) ax = plt.subplot(111) plt.subplots_adjust(left=0.16, bottom=0.12, right=0.98, top=0.98) ax.plot(grid, y, label="Fit") - ax.plot(volumes, energies, label="Data points", marker=".", ls="", markersize=10, markeredgecolor='black') - ax.plot([v0], [e0], label="Minimum", marker='p', color='C1', markersize=12, markeredgecolor='black', ls='') - ax.set_xlabel('Unit cell volume ($\mathrm{\AA}^3$)') - ax.set_ylabel('Unit cell energy ($E_\mathrm{H}$)') + ax.plot(volumes, energies, label="Data points", marker=".", ls="", markersize=10, markeredgecolor="black") + ax.plot([v0], [e0], label="Minimum", marker="p", color="C1", markersize=12, markeredgecolor="black", ls="") + ax.set_xlabel("Unit cell volume ($\mathrm{\AA}^3$)") + ax.set_ylabel("Unit cell energy ($E_\mathrm{H}$)") text = """ $E_0 = % .6f\,\mathrm{Ha}$ $V_0 = % .6f\,\mathrm{\AA}^3$ $B_0 = % .6f\,\mathrm{GPa}$ - """ % (e0, v0, b0) + """ % ( + e0, + v0, + b0, + ) if other: bp = other[0] text += "$B'= % .6f$\n" % bp @@ -92,9 +98,18 @@ def fit_eos(volumes, energies, fitfunc=birch_murnaghan, plot=True, value_at=None xv = value_at yv = fitfunc(xv, *popt) text += "$E(%g\,\mathrm{\AA}^3)= %.6f\,\mathrm{Ha}$" % (xv, yv) - ax.plot([xv], [yv], label="Value at $V= %g\,\mathrm{\AA}^3$" % xv, marker='d', color='C3', markersize=12, markeredgecolor='black', ls='') - - ax.text(0.3, 0.7, text, transform=ax.transAxes, ha='left', va='top') + ax.plot( + [xv], + [yv], + label="Value at $V= %g\,\mathrm{\AA}^3$" % xv, + marker="d", + color="C3", + markersize=12, + markeredgecolor="black", + ls="", + ) + + ax.text(0.3, 0.7, text, transform=ax.transAxes, ha="left", va="top") ax.legend() if plot is True: plt.show() @@ -113,8 +128,8 @@ def fit_eos(volumes, energies, fitfunc=birch_murnaghan, plot=True, value_at=None def fit_from_file(filename, xcol=0, ycol=1, volume_func=None): """Load and fit EOS to textfile.""" data = np.loadtxt(filename) - x = data[:,xcol] - y = data[:,ycol] + x = data[:, xcol] + y = data[:, ycol] if volume_func is not None: x = volume_func(x) result = fit_eos(x, y) @@ -123,32 +138,33 @@ def fit_from_file(filename, xcol=0, ycol=1, volume_func=None): def cmdline_tool(): import argparse + parser = argparse.ArgumentParser(allow_abbrev=False) - parser.add_argument('file') - parser.add_argument('--lattice') - parser.add_argument('--bsse') - parser.add_argument('--xcol', type=int, default=0) - parser.add_argument('--ycol', type=int, default=1) - parser.add_argument('--no-plot', dest='plot', action='store_false', default=True) - parser.add_argument('--value-at', type=float) + parser.add_argument("file") + parser.add_argument("--lattice") + parser.add_argument("--bsse") + parser.add_argument("--xcol", type=int, default=0) + parser.add_argument("--ycol", type=int, default=1) + parser.add_argument("--no-plot", dest="plot", action="store_false", default=True) + parser.add_argument("--value-at", type=float) args = parser.parse_args() - volume_func = lambda x : x - if args.lattice == 'diamond': - volume_func = lambda x : (x**3) / 4 - inv_volume_func = lambda v : np.power(4*v, 1.0/3) + volume_func = lambda x: x + if args.lattice == "diamond": + volume_func = lambda x: (x**3) / 4 + inv_volume_func = lambda v: np.power(4 * v, 1.0 / 3) elif args.lattice is not None: raise ValueError() data = np.loadtxt(args.file) print("Loading file %s using columns x= %d y= %d" % (args.file, args.xcol, args.ycol)) - x = data[:,args.xcol] - y = data[:,args.ycol] + x = data[:, args.xcol] + y = data[:, args.ycol] if args.bsse: print("Loading BSSE from file %s" % args.bsse) bsse_data = np.loadtxt(args.bsse) - assert np.allclose(bsse_data[:,args.xcol], x) - y = y - bsse_data[:,args.ycol] + assert np.allclose(bsse_data[:, args.xcol], x) + y = y - bsse_data[:, args.ycol] x = volume_func(x) result = fit_eos(x, y, plot=args.plot, value_at=args.value_at) @@ -156,5 +172,5 @@ def cmdline_tool(): print(result) -if __name__ == '__main__': +if __name__ == "__main__": cmdline_tool() diff --git a/vayesta/tools/plotting/colors.py b/vayesta/tools/plotting/colors.py index cd20a15be..c7393ed7b 100644 --- a/vayesta/tools/plotting/colors.py +++ b/vayesta/tools/plotting/colors.py @@ -1,119 +1,120 @@ colors_jmol = [ - (1 , 'H', [255,255,255]), - (2 , 'He', [217,255,255]), - (3 , 'Li', [204,128,255]), - (4 , 'Be', [194,255,0] ), - (5 , 'B', [255,181,181]), - (6 , 'C', [144,144,144]), - (7 , 'N', [48,80,248] ), - (8 , 'O', [255,13,13] ), - (9 , 'F', [144,224,80] ), - (10 , 'Ne', [179,227,245]), - (11 , 'Na', [171,92,242] ), - (12 , 'Mg', [138,255,0] ), - (13 , 'Al', [191,166,166]), - (14 , 'Si', [240,200,160]), - (15 , 'P', [255,128,0] ), - (16 , 'S', [255,255,48] ), - (17 , 'Cl', [31,240,31] ), - (18 , 'Ar', [128,209,227]), - (19 , 'K', [143,64,212] ), - (20 , 'Ca', [61,255,0] ), - (21 , 'Sc', [230,230,230]), - (22 , 'Ti', [191,194,199]), - (23 , 'V', [166,166,171]), - (24 , 'Cr', [138,153,199]), - (25 , 'Mn', [156,122,199]), - (26 , 'Fe', [224,102,51] ), - (27 , 'Co', [240,144,160]), - (28 , 'Ni', [80,208,80] ), - (29 , 'Cu', [200,128,51] ), - (30 , 'Zn', [125,128,176]), - (31 , 'Ga', [194,143,143]), - (32 , 'Ge', [102,143,143]), - (33 , 'As', [189,128,227]), - (34 , 'Se', [255,161,0] ), - (35 , 'Br', [166,41,41] ), - (36 , 'Kr', [92,184,209] ), - (37 , 'Rb', [112,46,176] ), - (38 , 'Sr', [0,255,0] ), - (39 , 'Y', [148,255,255]), - (40 , 'Zr', [148,224,224]), - (41 , 'Nb', [115,194,201]), - (42 , 'Mo', [84,181,181] ), - (43 , 'Tc', [59,158,158] ), - (44 , 'Ru', [36,143,143] ), - (45 , 'Rh', [10,125,140] ), - (46 , 'Pd', [0,105,133] ), - (47 , 'Ag', [192,192,192]), - (48 , 'Cd', [255,217,143]), - (49 , 'In', [166,117,115]), - (50 , 'Sn', [102,128,128]), - (51 , 'Sb', [158,99,181] ), - (52 , 'Te', [212,122,0] ), - (53 , 'I', [148,0,148] ), - (54 , 'Xe', [66,158,176] ), - (55 , 'Cs', [87,23,143] ), - (56 , 'Ba', [0,201,0] ), - (57 , 'La', [112,212,255]), - (58 , 'Ce', [255,255,199]), - (59 , 'Pr', [217,255,199]), - (60 , 'Nd', [199,255,199]), - (61 , 'Pm', [163,255,199]), - (62 , 'Sm', [143,255,199]), - (63 , 'Eu', [97,255,199] ), - (64 , 'Gd', [69,255,199] ), - (65 , 'Tb', [48,255,199] ), - (66 , 'Dy', [31,255,199] ), - (67 , 'Ho', [0,255,156] ), - (68 , 'Er', [0,230,117] ), - (69 , 'Tm', [0,212,82] ), - (70 , 'Yb', [0,191,56] ), - (71 , 'Lu', [0,171,36] ), - (72 , 'Hf', [77,194,255] ), - (73 , 'Ta', [77,166,255] ), - (74 , 'W', [33,148,214] ), - (75 , 'Re', [38,125,171] ), - (76 , 'Os', [38,102,150] ), - (77 , 'Ir', [23,84,135] ), - (78 , 'Pt', [208,208,224]), - (79 , 'Au', [255,209,35] ), - (80 , 'Hg', [184,184,208]), - (81 , 'Tl', [166,84,77] ), - (82 , 'Pb', [87,89,97] ), - (83 , 'Bi', [158,79,181] ), - (84 , 'Po', [171,92,0] ), - (85 , 'At', [117,79,69] ), - (86 , 'Rn', [66,130,150] ), - (87 , 'Fr', [66,0,102] ), - (88 , 'Ra', [0,125,0] ), - (89 , 'Ac', [112,171,250]), - (90 , 'Th', [0,186,255] ), - (91 , 'Pa', [0,161,255] ), - (92 , 'U', [0,143,255] ), - (93 , 'Np', [0,128,255] ), - (94 , 'Pu', [0,107,255] ), - (95 , 'Am', [84,92,242] ), - (96 , 'Cm', [120,92,227] ), - (97 , 'Bk', [138,79,227] ), - (98 , 'Cf', [161,54,212] ), - (99 , 'Es', [179,31,212] ), - (100, 'Fm', [179,31,186] ), - (101, 'Md', [179,13,166] ), - (102, 'No', [189,13,135] ), - (103, 'Lr', [199,0,102] ), - (104, 'Rf', [204,0,89] ), - (105, 'Db', [209,0,79] ), - (106, 'Sg', [217,0,69] ), - (107, 'Bh', [224,0,56] ), - (108, 'Hs', [230,0,46] ), - (109, 'Mt', [235,0,38] ), + (1, "H", [255, 255, 255]), + (2, "He", [217, 255, 255]), + (3, "Li", [204, 128, 255]), + (4, "Be", [194, 255, 0]), + (5, "B", [255, 181, 181]), + (6, "C", [144, 144, 144]), + (7, "N", [48, 80, 248]), + (8, "O", [255, 13, 13]), + (9, "F", [144, 224, 80]), + (10, "Ne", [179, 227, 245]), + (11, "Na", [171, 92, 242]), + (12, "Mg", [138, 255, 0]), + (13, "Al", [191, 166, 166]), + (14, "Si", [240, 200, 160]), + (15, "P", [255, 128, 0]), + (16, "S", [255, 255, 48]), + (17, "Cl", [31, 240, 31]), + (18, "Ar", [128, 209, 227]), + (19, "K", [143, 64, 212]), + (20, "Ca", [61, 255, 0]), + (21, "Sc", [230, 230, 230]), + (22, "Ti", [191, 194, 199]), + (23, "V", [166, 166, 171]), + (24, "Cr", [138, 153, 199]), + (25, "Mn", [156, 122, 199]), + (26, "Fe", [224, 102, 51]), + (27, "Co", [240, 144, 160]), + (28, "Ni", [80, 208, 80]), + (29, "Cu", [200, 128, 51]), + (30, "Zn", [125, 128, 176]), + (31, "Ga", [194, 143, 143]), + (32, "Ge", [102, 143, 143]), + (33, "As", [189, 128, 227]), + (34, "Se", [255, 161, 0]), + (35, "Br", [166, 41, 41]), + (36, "Kr", [92, 184, 209]), + (37, "Rb", [112, 46, 176]), + (38, "Sr", [0, 255, 0]), + (39, "Y", [148, 255, 255]), + (40, "Zr", [148, 224, 224]), + (41, "Nb", [115, 194, 201]), + (42, "Mo", [84, 181, 181]), + (43, "Tc", [59, 158, 158]), + (44, "Ru", [36, 143, 143]), + (45, "Rh", [10, 125, 140]), + (46, "Pd", [0, 105, 133]), + (47, "Ag", [192, 192, 192]), + (48, "Cd", [255, 217, 143]), + (49, "In", [166, 117, 115]), + (50, "Sn", [102, 128, 128]), + (51, "Sb", [158, 99, 181]), + (52, "Te", [212, 122, 0]), + (53, "I", [148, 0, 148]), + (54, "Xe", [66, 158, 176]), + (55, "Cs", [87, 23, 143]), + (56, "Ba", [0, 201, 0]), + (57, "La", [112, 212, 255]), + (58, "Ce", [255, 255, 199]), + (59, "Pr", [217, 255, 199]), + (60, "Nd", [199, 255, 199]), + (61, "Pm", [163, 255, 199]), + (62, "Sm", [143, 255, 199]), + (63, "Eu", [97, 255, 199]), + (64, "Gd", [69, 255, 199]), + (65, "Tb", [48, 255, 199]), + (66, "Dy", [31, 255, 199]), + (67, "Ho", [0, 255, 156]), + (68, "Er", [0, 230, 117]), + (69, "Tm", [0, 212, 82]), + (70, "Yb", [0, 191, 56]), + (71, "Lu", [0, 171, 36]), + (72, "Hf", [77, 194, 255]), + (73, "Ta", [77, 166, 255]), + (74, "W", [33, 148, 214]), + (75, "Re", [38, 125, 171]), + (76, "Os", [38, 102, 150]), + (77, "Ir", [23, 84, 135]), + (78, "Pt", [208, 208, 224]), + (79, "Au", [255, 209, 35]), + (80, "Hg", [184, 184, 208]), + (81, "Tl", [166, 84, 77]), + (82, "Pb", [87, 89, 97]), + (83, "Bi", [158, 79, 181]), + (84, "Po", [171, 92, 0]), + (85, "At", [117, 79, 69]), + (86, "Rn", [66, 130, 150]), + (87, "Fr", [66, 0, 102]), + (88, "Ra", [0, 125, 0]), + (89, "Ac", [112, 171, 250]), + (90, "Th", [0, 186, 255]), + (91, "Pa", [0, 161, 255]), + (92, "U", [0, 143, 255]), + (93, "Np", [0, 128, 255]), + (94, "Pu", [0, 107, 255]), + (95, "Am", [84, 92, 242]), + (96, "Cm", [120, 92, 227]), + (97, "Bk", [138, 79, 227]), + (98, "Cf", [161, 54, 212]), + (99, "Es", [179, 31, 212]), + (100, "Fm", [179, 31, 186]), + (101, "Md", [179, 13, 166]), + (102, "No", [189, 13, 135]), + (103, "Lr", [199, 0, 102]), + (104, "Rf", [204, 0, 89]), + (105, "Db", [209, 0, 79]), + (106, "Sg", [217, 0, 69]), + (107, "Bh", [224, 0, 56]), + (108, "Hs", [230, 0, 46]), + (109, "Mt", [235, 0, 38]), ] -atom_colors = {x[1] : x[2] for x in colors_jmol} -unknown = '#FF1493' +atom_colors = {x[1]: x[2] for x in colors_jmol} +unknown = "#FF1493" + def get_atom_color(symbol, default=unknown): - sym = ''.join([l for l in symbol if l.isalpha()]) + sym = "".join([l for l in symbol if l.isalpha()]) return atom_colors.get(sym, default) diff --git a/vayesta/tools/plotting/mpl_mol.py b/vayesta/tools/plotting/mpl_mol.py index 16cb27d5a..743d57059 100644 --- a/vayesta/tools/plotting/mpl_mol.py +++ b/vayesta/tools/plotting/mpl_mol.py @@ -9,9 +9,8 @@ from vayesta.tools.plotting.colors import atom_colors -def plot_mol(mol, size=30, indices=False, colors=None, colormap='bwr', add_images=False, **kwargs): - - if add_images and hasattr(mol, 'lattice_vectors'): +def plot_mol(mol, size=30, indices=False, colors=None, colormap="bwr", add_images=False, **kwargs): + if add_images and hasattr(mol, "lattice_vectors"): if mol.dimension == 1: images = [3, 1, 1] elif mol.dimension == 2: @@ -20,14 +19,14 @@ def plot_mol(mol, size=30, indices=False, colors=None, colormap='bwr', add_image images = [3, 3, 3] mol = pyscf.pbc.tools.super_cell(mol, images) if colors is not None: - nimages = images[0]*images[1]*images[2] - colors = nimages*list(colors) + nimages = images[0] * images[1] * images[2] + colors = nimages * list(colors) atoms = mol._atom - a_matrix = mol.lattice_vectors() if hasattr(mol, 'lattice_vectors') else None + a_matrix = mol.lattice_vectors() if hasattr(mol, "lattice_vectors") else None fig = plt.figure() - ax = fig.add_subplot(111, projection='3d') + ax = fig.add_subplot(111, projection="3d") if a_matrix is not None: x = np.zeros((1,)) @@ -35,14 +34,14 @@ def plot_mol(mol, size=30, indices=False, colors=None, colormap='bwr', add_image z = np.zeros((1,)) axcolors = ["red", "blue", "green"] for d in range(3): - #dx=a_matrix[d,0] - #dy=a_matrix[d,1] - #dz=a_matrix[d,2] + # dx=a_matrix[d,0] + # dy=a_matrix[d,1] + # dz=a_matrix[d,2] dx, dy, dz = a_matrix[d] ax.quiver(x, y, z, dx, dy, dz, color=axcolors[d]) - ax.set_xlim(1.1*a_matrix[0,0]) - ax.set_ylim(1.1*a_matrix[1,1]) - ax.set_zlim(1.1*a_matrix[2,2]) + ax.set_xlim(1.1 * a_matrix[0, 0]) + ax.set_ylim(1.1 * a_matrix[1, 1]) + ax.set_zlim(1.1 * a_matrix[2, 2]) ax.set_xlabel("x") ax.set_ylabel("y") @@ -59,27 +58,25 @@ def plot_mol(mol, size=30, indices=False, colors=None, colormap='bwr', add_image if l in string.ascii_letters: s += l sym.append(s) - #sym = [re.sub("\d", "", a[0]) for a in atoms] + # sym = [re.sub("\d", "", a[0]) for a in atoms] if colors is None: colors = [atom_colors.get(s, "black") for s in sym] if isinstance(colors[0], numbers.Number): vmax = np.max(np.abs(colors)) - kwargs['vmax'] = kwargs.get('vmax', vmax) - kwargs['vmin'] = kwargs.get('vmin', -vmax) + kwargs["vmax"] = kwargs.get("vmax", vmax) + kwargs["vmin"] = kwargs.get("vmin", -vmax) points = ax.scatter(x, y, z, s=size, c=colors, depthshade=False, edgecolor="black", cmap=colormap, **kwargs) - #ax.set_box_aspect((1,1,1)) + # ax.set_box_aspect((1,1,1)) # NotImplemented... - #ax.set_aspect('equal') + # ax.set_aspect('equal') if isinstance(colors[0], numbers.Number): fig.colorbar(points) - - maxdist = np.amax((x, y, z)) for i, j, k in itertools.product([-1, 1], repeat=3): - ax.scatter(i*maxdist, j*maxdist, k*maxdist, color="w", alpha=0.0) + ax.scatter(i * maxdist, j * maxdist, k * maxdist, color="w", alpha=0.0) if indices: offset = [0, 0, 1] @@ -87,18 +84,18 @@ def plot_mol(mol, size=30, indices=False, colors=None, colormap='bwr', add_image idx = atom[0][-1] idx = idx if idx.isdigit() else None if idx is not None: - ax.text(x[i]+offset[0], y[i]+offset[1], z[i]+offset[2], idx) + ax.text(x[i] + offset[0], y[i] + offset[1], z[i] + offset[2], idx) return fig -if __name__ == '__main__': +if __name__ == "__main__": import pyscf.gto mol = pyscf.gto.Mole() - mol.atom = 'H 0 0 0 ; F 0 0 2' + mol.atom = "H 0 0 0 ; F 0 0 2" mol.build() - #fig = plot_mol(mol, colors=['red', 'green']) - fig = plot_mol(mol, colors=[0, 3], colormap='bwr') + # fig = plot_mol(mol, colors=['red', 'green']) + fig = plot_mol(mol, colors=[0, 3], colormap="bwr") plt.show() diff --git a/vayesta/tools/plotting/plotly_mol.py b/vayesta/tools/plotting/plotly_mol.py index 43d058787..b88930fac 100644 --- a/vayesta/tools/plotting/plotly_mol.py +++ b/vayesta/tools/plotting/plotly_mol.py @@ -1,4 +1,3 @@ - import numpy as np import plotly.graph_objects as go @@ -8,21 +7,21 @@ def mol_supercell(mol, charges, spins): - if not hasattr(mol, 'lattice_vectors'): + if not hasattr(mol, "lattice_vectors"): return mol if mol.dimension == 1: images = [3, 1, 1] - #images = [1, 0, 0] + # images = [1, 0, 0] elif mol.dimension == 2: images = [3, 3, 1] - #images = [1, 1, 0] + # images = [1, 1, 0] else: images = [3, 3, 3] - #images = [1, 1, 1] + # images = [1, 1, 1] mol = pyscf.pbc.tools.super_cell(mol, images) - #mol = pyscf.pbc.tools.cell_plus_imgs(mol, images) - #nimages = images[0]*images[1]*images[2] - #ncells = np.product(2*np.asarray(images)+1) + # mol = pyscf.pbc.tools.cell_plus_imgs(mol, images) + # nimages = images[0]*images[1]*images[2] + # ncells = np.product(2*np.asarray(images)+1) ncells = np.product(images) if charges is not None: charges = ncells * list(charges) @@ -30,6 +29,7 @@ def mol_supercell(mol, charges, spins): spins = ncells * list(spins) return mol, charges, spins + def get_bonds(mol, threshold=3.0): coords = mol.atom_coords() bonds_x = [] @@ -45,6 +45,7 @@ def get_bonds(mol, threshold=3.0): bonds_z += [coord[2], coord2[2], None] return bonds_x, bonds_y, bonds_z + def get_cell_bounds(mol): amat = mol.lattice_vectors() points_x = [] @@ -57,8 +58,16 @@ def add_line(point1, point2): points_y += [point1[1], point2[1], None] points_z += [point1[2], point2[2], None] - corner = [np.asarray((0, 0, 0)), amat[0], amat[1], amat[2], - amat[0]+amat[1], amat[0]+amat[2], amat[1]+amat[2], amat[0]+amat[1]+amat[2]] + corner = [ + np.asarray((0, 0, 0)), + amat[0], + amat[1], + amat[2], + amat[0] + amat[1], + amat[0] + amat[2], + amat[1] + amat[2], + amat[0] + amat[1] + amat[2], + ] add_line(corner[0], corner[1]) add_line(corner[0], corner[2]) @@ -78,92 +87,95 @@ def add_line(point1, point2): add_line(corner[6], corner[7]) return points_x, points_y, points_z -def get_ranges(mol, margin=1.0): - #xmin, xmax = coords[:,0].min(), coords[:,0].max() - #ymin, ymax = coords[:,1].min(), coords[:,1].max() - #zmin, zmax = coords[:,2].min(), coords[:,2].max() +def get_ranges(mol, margin=1.0): + # xmin, xmax = coords[:,0].min(), coords[:,0].max() + # ymin, ymax = coords[:,1].min(), coords[:,1].max() + # zmin, zmax = coords[:,2].min(), coords[:,2].max() amat = mol.lattice_vectors() - m = amat[0]+amat[1]+amat[2] + m = amat[0] + amat[1] + amat[2] - xmin, xmax = -margin, m[0]+margin - ymin, ymax = -margin, m[1]+margin - zmin, zmax = -margin, m[2]+margin + xmin, xmax = -margin, m[0] + margin + ymin, ymax = -margin, m[1] + margin + zmin, zmax = -margin, m[2] + margin return [xmin, xmax], [ymin, ymax], [zmin, zmax] def plot_mol(mol, charges=None, spins=None, add_images=False, **kwargs): - mol0 = mol if add_images: mol, charges, spins = mol_supercell(mol, charges, spins) atoms = mol._atom - #a_matrix = mol.lattice_vectors() if hasattr(mol, 'lattice_vectors') else None + # a_matrix = mol.lattice_vectors() if hasattr(mol, 'lattice_vectors') else None coords = mol.atom_coords() symbols = [mol.atom_symbol(a) for a in range(len(atoms))] - atom_colors = kwargs.get('atom_colors', get_atom_color) + atom_colors = kwargs.get("atom_colors", get_atom_color) atom_colors = [atom_colors(s) for s in symbols] # Bonds bonds_x, bonds_y, bonds_z = get_bonds(mol) - bonds = go.Scatter3d(x=bonds_x, y=bonds_y, z=bonds_z, mode='lines', line=dict(width=5, color='grey')) + bonds = go.Scatter3d(x=bonds_x, y=bonds_y, z=bonds_z, mode="lines", line=dict(width=5, color="grey")) data = [bonds] # Bounds bounds_x, bounds_y, bounds_z = get_cell_bounds(mol0) - bounds = go.Scatter3d(x=bounds_x, y=bounds_y, z=bounds_z, mode='lines', line=dict(width=5, color='rgb(0, 200, 0)')) + bounds = go.Scatter3d(x=bounds_x, y=bounds_y, z=bounds_z, mode="lines", line=dict(width=5, color="rgb(0, 200, 0)")) data.append(bounds) - #data.append(bonds) + # data.append(bonds) def make_scatter(colors, colorscale, **kwargs): colorbar = dict(thickness=20) if colorscale else None - marker = go.scatter3d.Marker(size=8, color=colors, colorscale=colorscale, cmid=0.0, colorbar=colorbar, line=dict(width=4, color='black')) - hovertext = ['Atom %d: %s' % (i, s) for (i, s) in enumerate(symbols)] - scatter = go.Scatter3d(x=coords[:,0], y=coords[:,1], z=coords[:,2], marker=marker, mode='markers', text=hovertext, **kwargs) + marker = go.scatter3d.Marker( + size=8, color=colors, colorscale=colorscale, cmid=0.0, colorbar=colorbar, line=dict(width=4, color="black") + ) + hovertext = ["Atom %d: %s" % (i, s) for (i, s) in enumerate(symbols)] + scatter = go.Scatter3d( + x=coords[:, 0], y=coords[:, 1], z=coords[:, 2], marker=marker, mode="markers", text=hovertext, **kwargs + ) return scatter scatter = make_scatter(atom_colors, None) data.append(scatter) if charges is not None: - scatter = make_scatter(charges, ['Blue', 'White', 'Red'], visible=False) + scatter = make_scatter(charges, ["Blue", "White", "Red"], visible=False) data.append(scatter) if spins is not None: - scatter = make_scatter(spins, ['Blue', 'White', 'Red'], visible=False) + scatter = make_scatter(spins, ["Blue", "White", "Red"], visible=False) data.append(scatter) range_x, range_y, range_z = get_ranges(mol) - scene = dict(aspectmode='data', - xaxis = dict(backgroundcolor="rgb(220, 220, 220)", gridcolor='black', zerolinecolor='black', range=range_x), - yaxis = dict(backgroundcolor="rgb(200, 200, 200)", gridcolor='black', zerolinecolor='black', range=range_y), - zaxis = dict(backgroundcolor="rgb(180, 180, 180)", gridcolor='black', zerolinecolor='black', range=range_z), - ) - + scene = dict( + aspectmode="data", + xaxis=dict(backgroundcolor="rgb(220, 220, 220)", gridcolor="black", zerolinecolor="black", range=range_x), + yaxis=dict(backgroundcolor="rgb(200, 200, 200)", gridcolor="black", zerolinecolor="black", range=range_y), + zaxis=dict(backgroundcolor="rgb(180, 180, 180)", gridcolor="black", zerolinecolor="black", range=range_z), + ) bonds_bounds = [True, True] buttons = [ - dict(label='Atoms', method='update', args=[{'visible': bonds_bounds+[True, False, False]}]), - dict(label='Charges', method='update', args=[{'visible': bonds_bounds+[False, True, False]}]), - dict(label='Spins', method='update', args=[{'visible': bonds_bounds+[False, False, True]}]) - ] + dict(label="Atoms", method="update", args=[{"visible": bonds_bounds + [True, False, False]}]), + dict(label="Charges", method="update", args=[{"visible": bonds_bounds + [False, True, False]}]), + dict(label="Spins", method="update", args=[{"visible": bonds_bounds + [False, False, True]}]), + ] updatemenus = list([dict(active=0, buttons=buttons)]) layout = go.Layout(scene=scene, showlegend=False, updatemenus=updatemenus) - fig=go.Figure(data=data, layout=layout) + fig = go.Figure(data=data, layout=layout) return fig -if __name__ == '__main__': +if __name__ == "__main__": import pyscf.gto mol = pyscf.gto.Mole() - mol.atom = 'H 0 0 0 ; F 0 0 2' + mol.atom = "H 0 0 0 ; F 0 0 2" mol.build() charges = [-1.0, 0.5] spins = [-1, 1] - #colors = None + # colors = None fig = plot_mol(mol, charges=charges, spins=spins) - fig.write_html('test.html') + fig.write_html("test.html") From 2151c5da738e022d5cb2ef5e8a64fa5b64297432 Mon Sep 17 00:00:00 2001 From: Charles Scott <cjcargillscott@gmail.com> Date: Fri, 25 Aug 2023 11:14:15 +0100 Subject: [PATCH 4/5] Added .git-blame-ignore-revs file --- .git-blame-ignore-revs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000..ff08f4a7c --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,6 @@ +# Run this command to always ignore formatting commits in `git blame` +# git config blame.ignoreRevsFile .git-blame-ignore-revs + +# Formatted entire existing codebase with black +3f80f568262b20fb82fbaa3d780d8a0d5cfebc3f + From 2bfab760f8a1a086387f284e1d12485530d411c9 Mon Sep 17 00:00:00 2001 From: Charles Scott <cjcargillscott@gmail.com> Date: Fri, 25 Aug 2023 14:14:37 +0100 Subject: [PATCH 5/5] Added pre-commit functionality using black. --- .pre-commit-config.yaml | 14 ++++++++++++++ pyproject.toml | 1 + 2 files changed, 15 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..a481ea648 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,14 @@ + # Install via pre-commit by running + # `pre-commit install` + # See https://pre-commit.com for more information + # See https://pre-commit.com/hooks.html for more hooks + # We'll need to manually update the version of black used in our pre-commits via + # `pre-commit autoupdate`. + # This is a conscious decision on the part of the pre-commit developers; for more information + # see https://pre-commit.com/#using-the-latest-version-for-a-repository + repos: + - repo: https://github.com/ambv/black + rev: 23.7.0 + hooks: + - id: black + language_version: python3.10 diff --git a/pyproject.toml b/pyproject.toml index 811ee52db..3d5e00b8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,6 +81,7 @@ dev = [ "black>=22.6.0", "pytest", "pytest-cov", + "pre-commit", ] [tool.coverage.run]