From bdfd2f502291feb0a5a761b2f63d62cc8e3caefa Mon Sep 17 00:00:00 2001 From: fedy9 Date: Thu, 5 Mar 2026 19:59:00 +0100 Subject: [PATCH 01/26] full IP/EA ADC(0-3) implementation including tests without any properties --- adcc/AdcMatrix.py | 63 +- adcc/AdcMethod.py | 33 +- adcc/AmplitudeVector.py | 4 +- adcc/__init__.py | 63 +- adcc/adc_ea/matrix.py | 212 ++++ adcc/adc_ea/util.py | 64 ++ adcc/adc_ip/matrix.py | 222 +++++ adcc/adc_ip/util.py | 64 ++ adcc/adc_pp/matrix.py | 16 +- adcc/block.py | 22 + adcc/guess/__init__.py | 115 ++- adcc/guess/guess_zero.py | 200 ++-- adcc/guess/guesses_from_diagonal.py | 110 ++- adcc/guess/util.py | 84 ++ adcc/projection.py | 16 +- adcc/solver/explicit_symmetrisation.py | 9 +- adcc/tests/AdcMatrix_test.py | 233 ++++- adcc/tests/guess_test.py | 930 ++++++++++++++++-- adcc/tests/testcases.py | 40 +- adcc/tests/testdata_cache.py | 129 ++- adcc/tests/workflow_test.py | 482 ++++++++- adcc/workflow.py | 423 ++++---- .../amplitude_vector_enforce_spin_kind.cc | 129 ++- .../amplitude_vector_enforce_spin_kind.hh | 6 +- libadcc_src/fill_ea_doubles_guesses.cc | 78 ++ libadcc_src/fill_ea_doubles_guesses.hh | 53 + libadcc_src/fill_ip_doubles_guesses.cc | 78 ++ libadcc_src/fill_ip_doubles_guesses.hh | 53 + libadcc_src/guess/ea_adc_guess_d.cc | 484 +++++++++ libadcc_src/guess/ea_adc_guess_d.hh | 31 + libadcc_src/guess/index_group_h2p.cc | 84 ++ libadcc_src/guess/index_group_h2p.hh | 164 +++ libadcc_src/guess/index_group_p2h.cc | 84 ++ libadcc_src/guess/index_group_p2h.hh | 164 +++ libadcc_src/guess/ip_adc_guess_d.cc | 485 +++++++++ libadcc_src/guess/ip_adc_guess_d.hh | 31 + libadcc_src/pyiface/ExportAdcc.cc | 4 + libadcc_src/pyiface/export_adc_ea.cc | 56 ++ libadcc_src/pyiface/export_adc_ip.cc | 56 ++ 39 files changed, 5001 insertions(+), 573 deletions(-) create mode 100644 adcc/adc_ea/matrix.py create mode 100644 adcc/adc_ea/util.py create mode 100644 adcc/adc_ip/matrix.py create mode 100644 adcc/adc_ip/util.py create mode 100644 adcc/guess/util.py create mode 100644 libadcc_src/fill_ea_doubles_guesses.cc create mode 100644 libadcc_src/fill_ea_doubles_guesses.hh create mode 100644 libadcc_src/fill_ip_doubles_guesses.cc create mode 100644 libadcc_src/fill_ip_doubles_guesses.hh create mode 100644 libadcc_src/guess/ea_adc_guess_d.cc create mode 100644 libadcc_src/guess/ea_adc_guess_d.hh create mode 100644 libadcc_src/guess/index_group_h2p.cc create mode 100644 libadcc_src/guess/index_group_h2p.hh create mode 100644 libadcc_src/guess/index_group_p2h.cc create mode 100644 libadcc_src/guess/index_group_p2h.hh create mode 100644 libadcc_src/guess/ip_adc_guess_d.cc create mode 100644 libadcc_src/guess/ip_adc_guess_d.hh create mode 100644 libadcc_src/pyiface/export_adc_ea.cc create mode 100644 libadcc_src/pyiface/export_adc_ip.cc diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 54bec0698..1e9fe7c64 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -27,6 +27,8 @@ from .LazyMp import LazyMp from .adc_pp import matrix as ppmatrix +from .adc_ip import matrix as ipmatrix +from .adc_ea import matrix as eamatrix from .timings import Timer, timed_member_call from .AdcMethod import AdcMethod from .functions import ones_like @@ -73,6 +75,8 @@ class AdcMatrixlike: _special_block_orders = { "adc2x": {"ph_ph": 2, "ph_pphh": 1, "pphh_ph": 1, "pphh_pphh": 1}, + "ip-adc2x": {"h_h": 2, "h_phh": 1, "phh_h": 1, "phh_phh": 1}, + "ea-adc2x": {"p_p": 2, "p_pph": 1, "pph_p": 1, "pph_pph": 1}, } @classmethod @@ -90,7 +94,9 @@ def _default_block_orders(cls, method: AdcMethod) -> dict[str, int]: # - determine which spaces are available in the ADC(n) matrix # starting from the given minimal space min_space = { - "pp": "ph" + "pp": "ph", + "ip": "h", + "ea": "p" }.get(method.adc_type, None) if min_space is None: raise ValueError(f"Unknown adc type {method.adc_type} for method " @@ -178,6 +184,10 @@ def _is_valid_space(cls, space: str, method: AdcMethod) -> bool: # be equal or differ e.g. by +-1 (IP/EA) if method.adc_type == "pp": return n_particle == n_hole + elif method.adc_type == "ip": + return n_particle == n_hole - 1 + elif method.adc_type == "ea": + return n_particle == n_hole + 1 raise ValueError(f"Unknown adc type {method.adc_type} for method " f"{method.name}. Can not validate space.") @@ -252,11 +262,19 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, variant = None if self.is_core_valence_separated: variant = "cvs" + # Directly import block dispatch functions? + BLOCK_DISPATCH = { + "pp": ppmatrix.block, + "ip": ipmatrix.block, + "ea": eamatrix.block} + block_dispatch_fun = BLOCK_DISPATCH[self.method.adc_type] blocks = { - block: ppmatrix.block(self.ground_state, block.split("_"), - order=order, intermediates=self.intermediates, + block: block_dispatch_fun(self.ground_state, block.split("_"), + order=order, + intermediates=self.intermediates, variant=variant) - for block, order in self.block_orders.items() if order is not None + for block, order in self.block_orders.items() + if order is not None } self.blocks = {bl: blocks[bl].apply for bl in blocks} if diagonal_precomputed: @@ -362,7 +380,8 @@ def block_apply(self, block, tensor): with another AmplitudeVector or Tensor. Non-matching blocks in the AmplitudeVector will be ignored. """ - if not isinstance(tensor, libadcc.Tensor): + # TODO: Allow AmplitudeVector? + if not isinstance(tensor, (libadcc.Tensor, AmplitudeVector)): raise TypeError("tensor should be an adcc.Tensor") with self.timer.record(f"apply/{block}"): @@ -426,16 +445,30 @@ def construct_symmetrisation_for_blocks(self): Returns a dictionary block identifier -> function """ ret = {} - if self.is_core_valence_separated: - # CVS doubles part is antisymmetric wrt. (i,K,a,b) <-> (i,K,b,a) - ret["pphh"] = lambda v: v.antisymmetrise([(2, 3)]) - else: - def symmetrise_generic_adc_doubles(invec): - # doubles part is antisymmetric wrt. (i,j,a,b) <-> (i,j,b,a) - scratch = invec.antisymmetrise([(2, 3)]) - # doubles part is symmetric wrt. (i,j,a,b) <-> (j,i,b,a) - return scratch.symmetrise([(0, 1), (2, 3)]) - ret["pphh"] = symmetrise_generic_adc_doubles + # TODO: IP/EA doubles + if self.method.adc_type == "pp": + if self.is_core_valence_separated: + # CVS doubles part is antisymmetric wrt. (i,K,a,b) <-> (i,K,b,a) + ret["pphh"] = lambda v: v.antisymmetrise([(2, 3)]) + else: + def symmetrise_generic_adc_doubles(invec): + # doubles part is antisymmetric wrt. (i,j,a,b) <-> (i,j,b,a) + scratch = invec.antisymmetrise([(2, 3)]) + # doubles part is symmetric wrt. (i,j,a,b) <-> (j,i,b,a) + return scratch.symmetrise([(0, 1), (2, 3)]) + ret["pphh"] = symmetrise_generic_adc_doubles + elif self.method.adc_type == "ip": + if not self.is_core_valence_separated: + def symmetrise_generic_adc_doubles(invec): + # doubles part is antisymmetric wrt. (i,j,a) <-> (j,i,a) + return invec.antisymmetrise([(0, 1)]) + ret["phh"] = symmetrise_generic_adc_doubles + elif self.method.adc_type == "ea": + if not self.is_core_valence_separated: + def symmetrise_generic_adc_doubles(invec): + # doubles part is antisymmetric wrt. (i,a,b) <-> (i,a,b) + return invec.antisymmetrise([(1, 2)]) + ret["pph"] = symmetrise_generic_adc_doubles return ret def dense_basis(self, axis_blocks=None, ordering="adcc"): diff --git a/adcc/AdcMethod.py b/adcc/AdcMethod.py index d1781e28e..347ed7c4e 100644 --- a/adcc/AdcMethod.py +++ b/adcc/AdcMethod.py @@ -24,10 +24,13 @@ def get_valid_methods(): valid_prefixes = ["cvs"] + valid_adc_types = ["ip", "ea"] valid_bases = ["adc0", "adc1", "adc2", "adc2x", "adc3"] - ret = valid_bases + [p + "-" + m for p in valid_prefixes - for m in valid_bases] + ret = (valid_bases + + [p + "-" + m for p in valid_prefixes for m in valid_bases] + + [t + "-" + m for t in valid_adc_types for m in valid_bases] + ) return ret @@ -43,8 +46,13 @@ def __init__(self, method): self.__base_method = split[-1] split = split[:-1] self.is_core_valence_separated = "cvs" in split - # NOTE: added this to make the testdata generation ready for IP/EA - self.adc_type = "pp" + + if "ip" in split: + self.adc_type = "ip" + elif "ea" in split: + self.adc_type = "ea" + else: + self.adc_type = "pp" try: if self.__base_method == "adc2x": @@ -59,17 +67,22 @@ def at_level(self, newlevel): Return an equivalent method, where only the level is changed (e.g. calling this on a CVS method returns a CVS method) """ + name_str = "adc" + if self.adc_type != "pp": + name_str = self.adc_type + "-" + "adc" if self.is_core_valence_separated: - return AdcMethod("cvs-adc" + str(newlevel)) + return AdcMethod("cvs-" + name_str + str(newlevel)) else: - return AdcMethod("adc" + str(newlevel)) + return AdcMethod(name_str + str(newlevel)) @property def name(self): + name = self.__base_method + if self.adc_type != "pp": + name = self.adc_type + "-" + name if self.is_core_valence_separated: - return "cvs-" + self.__base_method - else: - return self.__base_method + name = "cvs-" + name + return name @property def property_method(self): @@ -89,6 +102,8 @@ def base_method(self): The base (full) method, i.e. with all approximations such as CVS stripped off. """ + if self.adc_type != "pp": + return AdcMethod(self.adc_type + "-" + self.__base_method) return AdcMethod(self.__base_method) def __eq__(self, other): diff --git a/adcc/AmplitudeVector.py b/adcc/AmplitudeVector.py index aff573b60..b08adad29 100644 --- a/adcc/AmplitudeVector.py +++ b/adcc/AmplitudeVector.py @@ -26,7 +26,9 @@ class AmplitudeVector(dict): def __init__(self, **kwargs): """ Construct an AmplitudeVector. Typical use cases are - ``AmplitudeVector(ph=tensor_singles, pphh=tensor_doubles)``. + ``AmplitudeVector(ph=tensor_singles, pphh=tensor_doubles)``. For IP-ADC + ``AmplitudeVector(h=tensor_singles, phh=tensor_doubles)``, and for + EA-ADC ``AmplitudeVector(p=tensor_singles, pph=tensor_doubles)`` """ super().__init__(**kwargs) diff --git a/adcc/__init__.py b/adcc/__init__.py index 872963c00..d96501da2 100644 --- a/adcc/__init__.py +++ b/adcc/__init__.py @@ -36,6 +36,8 @@ from .memory_pool import memory_pool from .State2States import State2States from .ExcitedStates import ExcitedStates +from .ChargedExcitations import (ChargedExcitation, DetachedStates, + AttachedStates) from .Excitation import Excitation from .ElectronicTransition import ElectronicTransition from .DataHfProvider import DataHfProvider, DictHfProvider @@ -49,8 +51,9 @@ from .opt_einsum_integration import register_with_opt_einsum # This has to be the last set of import -from .guess import (guess_symmetries, guess_zero, guesses_any, guesses_singlet, - guesses_spin_flip, guesses_triplet) +from .guess import (guess_symmetries, guess_zero, guesses_any, + guesses_singlet, guesses_spin_flip, guesses_triplet, + guesses_doublet) from .workflow import run_adc from .exceptions import InputError @@ -61,13 +64,17 @@ "linear_combination", "zeros_like", "direct_sum", "memory_pool", "set_n_threads", "get_n_threads", "AmplitudeVector", "HartreeFockProvider", "ExcitedStates", "State2States", + "ChargedExcitation", "DetachedStates", "AttachedStates" "Excitation", "ElectronicTransition", "Tensor", "DictHfProvider", "DataHfProvider", "OneParticleOperator", "OneParticleDensity", "TwoParticleOperator", "TwoParticleDensity", "OperatorSymmetry", "guesses_singlet", "guesses_triplet", "guesses_any", - "guess_symmetries", "guesses_spin_flip", "guess_zero", "LazyMp", + "guesses_doublet", "guess_symmetries", "guesses_spin_flip", + "guess_zero", "LazyMp", "adc0", "cis", "adc1", "adc2", "adc2x", "adc3", "cvs_adc0", "cvs_adc1", "cvs_adc2", "cvs_adc2x", "cvs_adc3", + "ip_adc0", "ip_adc1", "ip_adc2", "ip_adc2x", "ip_adc3", + "ea_adc0", "ea_adc1", "ea_adc2", "ea_adc2x", "ea_adc3", "banner"] __version__ = "0.16.1" @@ -141,6 +148,56 @@ def cvs_adc3(*args, **kwargs): return run_adc(*args, **kwargs, method="cvs-adc3") +@with_runadc_doc +def ip_adc0(*args, **kwargs): + return run_adc(*args, **kwargs, method="ip-adc0") + + +@with_runadc_doc +def ip_adc1(*args, **kwargs): + return run_adc(*args, **kwargs, method="ip-adc1") + + +@with_runadc_doc +def ip_adc2(*args, **kwargs): + return run_adc(*args, **kwargs, method="ip-adc2") + + +@with_runadc_doc +def ip_adc2x(*args, **kwargs): + return run_adc(*args, **kwargs, method="ip-adc2x") + + +@with_runadc_doc +def ip_adc3(*args, **kwargs): + return run_adc(*args, **kwargs, method="ip-adc3") + + +@with_runadc_doc +def ea_adc0(*args, **kwargs): + return run_adc(*args, **kwargs, method="ea-adc0") + + +@with_runadc_doc +def ea_adc1(*args, **kwargs): + return run_adc(*args, **kwargs, method="ea-adc1") + + +@with_runadc_doc +def ea_adc2(*args, **kwargs): + return run_adc(*args, **kwargs, method="ea-adc2") + + +@with_runadc_doc +def ea_adc2x(*args, **kwargs): + return run_adc(*args, **kwargs, method="ea-adc2x") + + +@with_runadc_doc +def ea_adc3(*args, **kwargs): + return run_adc(*args, **kwargs, method="ea-adc3") + + def banner(colour=sys.stdout.isatty()): """Return a nice banner describing adcc and its components diff --git a/adcc/adc_ea/matrix.py b/adcc/adc_ea/matrix.py new file mode 100644 index 000000000..63deeb6c6 --- /dev/null +++ b/adcc/adc_ea/matrix.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab +## --------------------------------------------------------------------- +## +## Copyright (C) 2020 by the adcc authors +## +## This file is part of adcc. +## +## adcc is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## adcc is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with adcc. If not, see . +## +## --------------------------------------------------------------------- +from math import sqrt +from collections import namedtuple + +from adcc import block as b +from adcc.functions import direct_sum, einsum +from adcc.Intermediates import Intermediates, register_as_intermediate +from adcc.AmplitudeVector import AmplitudeVector + +# +# Dispatch routine lives in 'adc_pp/matrix.py' +# +__all__ = ["block"] + +AdcBlock = namedtuple("AdcBlock", ["apply", "diagonal"]) + + +def block(ground_state, spaces, order, variant=None, intermediates=None): + """ + Gets ground state, potentially intermediates, spaces (ph, pphh and so on) + and the perturbation theory order for the block, + variant is "cvs" or sth like that. + + It is assumed largely, that CVS is equivalent to mp.has_core_occupied_space, + while one would probably want in the long run that one can have an "o2" space, + but not do CVS. + """ + reference_state = ground_state.reference_state + if intermediates is None: + intermediates = Intermediates(ground_state) + + fn = b.get_block_name(spaces, order, variant, + ground_state.has_core_occupied_space) + + if fn not in globals(): + raise ValueError("Could not dispatch: " + f"spaces={spaces} order={order} variant={variant}. " + "Probably the secular matrix is not implemented for " + "the requested method.") + return globals()[fn](reference_state, ground_state, intermediates) + + +# +# 0th order main +# +def block_p_p_0(hf, mp, intermediates): + # M_{11} + def apply(ampl): + return AmplitudeVector(p=einsum("ab,b->a", hf.fvv, ampl.p)) + diagonal = AmplitudeVector(p=hf.fvv.diagonal()) + return AdcBlock(apply, diagonal) + + +def diagonal_pph_pph_0(hf): + res = direct_sum("-i+a+b->iab", + hf.foo.diagonal(), hf.fvv.diagonal(), hf.fvv.diagonal()) + return AmplitudeVector(pph=res.symmetrise(1, 2)) + + +def block_pph_pph_0(hf, mp, intermediates): + # M_{22} + def apply(ampl): + return AmplitudeVector(pph=( + - einsum("jab,ij->iab", ampl.pph, hf.foo) + + 2 * einsum("ac,icb->iab", hf.fvv, ampl.pph).antisymmetrise(1, 2) + )) + return AdcBlock(apply, diagonal_pph_pph_0(hf)) + + +# +# 1st order main +# +def block_p_p_1(hf, mp, intermediates): + # M_{11}, same as ADC(0) + return block_p_p_0(hf, mp, intermediates) + + +def diagonal_pph_pph_1(hf): + # TODO + pass + + +def block_pph_pph_1(hf, mp, intermediates): + # M_{22} + def apply(ampl): + return AmplitudeVector(pph=( + - einsum("jab,ij->iab", ampl.pph, hf.foo) + + 2 * einsum("ac,icb->iab", hf.fvv, ampl.pph).antisymmetrise(1, 2) + + 0.5 * einsum("abcd,icd->iab", hf.vvvv, ampl.pph) + - 2 * einsum("icka,kcb->iab", hf.ovov, ampl.pph + ).antisymmetrise(1, 2) + )) + return AdcBlock(apply, diagonal_pph_pph_0(hf)) + + +# +# 1st order coupling +# +def block_p_pph_1(hf, mp, intermediates): + # M_{12} + def apply(ampl): + return AmplitudeVector(p=( + - 1 / sqrt(2) * einsum("jabc,jbc->a", hf.ovvv, ampl.pph))) + return AdcBlock(apply, 0) + + +def block_pph_p_1(hf, mp, intermediates): + # M_{21} + def apply(ampl): + return AmplitudeVector(pph=( + - 1 / sqrt(2) * einsum("icab,c->iab", hf.ovvv, ampl.p))) + return AdcBlock(apply, 0) + + +# +# 2nd order main +# +def block_p_p_2(hf, mp, intermediates): + # M_{11} + # Intermediate can be found in 'adc_pp/matrix.py' + i1 = intermediates.adc2_i1 + diagonal = AmplitudeVector(p=i1.diagonal()) + + def apply(ampl): + return AmplitudeVector(p=einsum("ab,b->a", i1, ampl.p)) + return AdcBlock(apply, diagonal) + + +# +# 2nd order coupling +# +def block_p_pph_2(hf, mp, intermediates): + # M_{12} + # Intermediate can be found in 'adc_pp/matrix.py' + i2 = - intermediates.adc3_pib + + def apply(ampl): + return AmplitudeVector(p=( + + 1 / sqrt(2) * einsum("jabc,jbc->a", i2, ampl.pph))) + return AdcBlock(apply, 0) + + +def block_pph_p_2(hf, mp, intermediates): + # M_{21} + # Intermediate can be found in 'adc_pp/matrix.py' + i2 = - intermediates.adc3_pib + + def apply(ampl): + return AmplitudeVector(pph=( + + 1 / sqrt(2) * einsum("icab,c->iab", i2, ampl.p))) + return AdcBlock(apply, 0) + + +# +# 3rd order main +# +def block_p_p_3(hf, mp, intermediates): + # M_{11} + i1 = intermediates.adc3_ea_i1 + diagonal = AmplitudeVector(p=i1.diagonal()) + + def apply(ampl): + return AmplitudeVector(p=einsum("ab,b->a", i1, ampl.p)) + return AdcBlock(apply, diagonal) + + +# +# Intermediates +# + +@register_as_intermediate +def adc3_ea_i1(hf, mp, intermediates): + return ( + hf.fvv + ( + + 0.5 * einsum("ijac,ijbc->ab", mp.t2oo, hf.oovv) + - 0.25 * einsum("ijbc,ijac->ab", mp.t2oo, mp.t2eri(b.oovv, b.oo)) + + 0.5 * einsum("ijbc,ijca->ab", mp.t2oo, mp.t2eri(b.oovv, b.vv)) + + einsum("ijbc,jiac->ab", mp.t2oo, mp.t2eri(b.oovv, b.ov)) + - 2 * einsum("ijbc,jica->ab", mp.t2oo, mp.t2eri(b.oovv, b.ov)) + ).symmetrise() + + intermediates.sigma_vv + ) + + +@register_as_intermediate +def sigma_vv(hf, mp, intermediates): + # Static self-energy, oo part \Sigma_{ij}(\infty) + p0 = mp.mp2_diffdm + return (einsum("iajb,ij->ab", hf.ovov, p0.oo) + + 2 * einsum("iacb,ic->ab", hf.ovvv, p0.ov) + + einsum("acbd,cd->ab", hf.vvvv, p0.vv)).symmetrise() diff --git a/adcc/adc_ea/util.py b/adcc/adc_ea/util.py new file mode 100644 index 000000000..951a5212c --- /dev/null +++ b/adcc/adc_ea/util.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab +## --------------------------------------------------------------------- +## +## Copyright (C) 2020 by the adcc authors +## +## This file is part of adcc. +## +## adcc is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## adcc is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with adcc. If not, see . +## +## --------------------------------------------------------------------- + + +def check_singles_amplitudes(spaces, *amplitudes): + check_have_singles_block(*amplitudes) + check_singles_subspaces(spaces, *amplitudes) + + +def check_doubles_amplitudes(spaces, *amplitudes): + check_have_doubles_block(*amplitudes) + check_doubles_subspaces(spaces, *amplitudes) + + +def check_have_singles_block(*amplitudes): + if any("p" not in amplitude.keys() for amplitude in amplitudes): + raise ValueError("ADC(0) level and " + "beyond expects an excitation amplitude with a " + "singles part.") + + +def check_have_doubles_block(*amplitudes): + if any("pph" not in amplitude.keys() for amplitude in amplitudes): + raise ValueError("ADC(2) level and " + "beyond expects an excitation amplitude with a " + "singles and a doubles part.") + + +def check_singles_subspaces(spaces, *amplitudes): + for amplitude in amplitudes: + u1 = amplitude.p + if u1.subspaces != spaces: + raise ValueError("Mismatch in subspaces singles part " + f"(== {u1.subspaces}), where {spaces} " + "was expected.") + + +def check_doubles_subspaces(spaces, *amplitudes): + for amplitude in amplitudes: + u2 = amplitude.pph + if u2.subspaces != spaces: + raise ValueError("Mismatch in subspaces doubles part " + f"(== {u2.subspaces}), where " + f"{spaces} was expected.") diff --git a/adcc/adc_ip/matrix.py b/adcc/adc_ip/matrix.py new file mode 100644 index 000000000..9b2a56606 --- /dev/null +++ b/adcc/adc_ip/matrix.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 +## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab +## --------------------------------------------------------------------- +## +## Copyright (C) 2020 by the adcc authors +## +## This file is part of adcc. +## +## adcc is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## adcc is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with adcc. If not, see . +## +## --------------------------------------------------------------------- +from math import sqrt +from collections import namedtuple + +from adcc import block as b +from adcc.functions import direct_sum, einsum, zeros_like +from adcc.Intermediates import Intermediates, register_as_intermediate +from adcc.AmplitudeVector import AmplitudeVector + +# +# Dispatch routine lives in 'adc_pp/matrix.py' +# +__all__ = ["block"] + +AdcBlock = namedtuple("AdcBlock", ["apply", "diagonal"]) + + +def block(ground_state, spaces, order, variant=None, intermediates=None): + """ + Gets ground state, potentially intermediates, spaces (ph, pphh and so on) + and the perturbation theory order for the block, + variant is "cvs" or sth like that. + + It is assumed largely, that CVS is equivalent to mp.has_core_occupied_space, + while one would probably want in the long run that one can have an "o2" space, + but not do CVS. + """ + reference_state = ground_state.reference_state + if intermediates is None: + intermediates = Intermediates(ground_state) + + fn = b.get_block_name(spaces, order, variant, + ground_state.has_core_occupied_space) + + if fn not in globals(): + raise ValueError("Could not dispatch: " + f"spaces={spaces} order={order} variant={variant}. " + "Probably the secular matrix is not implemented for " + "the requested method.") + return globals()[fn](reference_state, ground_state, intermediates) + + +# +# 0th order main +# +def block_h_h_0(hf, mp, intermediates): + # M_{11} + def apply(ampl): + return AmplitudeVector(h=-einsum("ij,j->i", hf.foo, ampl.h)) + diagonal = AmplitudeVector(h=-hf.foo.diagonal()) + return AdcBlock(apply, diagonal) + + +def diagonal_phh_phh_0(hf): + fCC = hf.fcc if hf.has_core_occupied_space else hf.foo + res = direct_sum("-i-J+a->iJa", + hf.foo.diagonal(), fCC.diagonal(), hf.fvv.diagonal()) + return AmplitudeVector(phh=res) + + +def block_phh_phh_0(hf, mp, intermediates): + # M_{22} + def apply(ampl): + return AmplitudeVector(phh=( + + einsum("ab,ijb->ija", hf.fvv, ampl.phh) + - 2 * einsum("ik,kja->ija", hf.foo, ampl.phh).antisymmetrise(0, 1) + )) + return AdcBlock(apply, diagonal_phh_phh_0(hf)) + + +# +# 1st order main +# +def block_h_h_1(hf, mp, intermediates): + # M_{11}, same as ADC(0) + return block_h_h_0(hf, mp, intermediates) + + +def diagonal_phh_phh_1(hf): + fCC = hf.fcc if hf.has_core_occupied_space else hf.foo + i1 = direct_sum("-i-J+a->iJa", + hf.foo.diagonal(), fCC.diagonal(), hf.fvv.diagonal()) + + # Build Kronecker delta + d_vv = zeros_like(hf.fvv) + d_vv.set_mask("aa", 1.0) + + i2 = einsum("ijij,aa->ija", hf.oooo, d_vv) + res = i1 + i2 + return AmplitudeVector(phh=res.symmetrise(0, 1)) + + +def block_phh_phh_1(hf, mp, intermediates): + # M_{22} + def apply(ampl): + return AmplitudeVector(phh=( + + einsum("ac,ijc->ija", hf.fvv, ampl.phh) + - 2 * einsum("ik,kja->ija", hf.foo, ampl.phh).antisymmetrise(0, 1) + + 0.5 * einsum("ijkl,kla->ija", hf.oooo, ampl.phh) + - 2 * einsum("kaic,kjc->ija", hf.ovov, ampl.phh + ).antisymmetrise(0, 1) + )) + return AdcBlock(apply, diagonal_phh_phh_1(hf)) + + +# +# 1st order coupling +# +def block_h_phh_1(hf, mp, intermediates): + # M_{12} + def apply(ampl): + return AmplitudeVector(h=( + + 1 / sqrt(2) * einsum("jkib,jkb->i", hf.ooov, ampl.phh))) + return AdcBlock(apply, 0) + + +def block_phh_h_1(hf, mp, intermediates): + # M_{21} + def apply(ampl): + return AmplitudeVector(phh=( + + 1 / sqrt(2) * einsum("ijka,k->ija", hf.ooov, ampl.h))) + return AdcBlock(apply, 0) + + +# +# 2nd order main +# +def block_h_h_2(hf, mp, intermediates): + # M_{11} + # Intermediate can be found in 'adc_pp/matrix.py' + i1 = - intermediates.adc2_i2 + diagonal = AmplitudeVector(h=i1.diagonal()) + + def apply(ampl): + return AmplitudeVector(h=einsum("ij,j->i", i1, ampl.h)) + return AdcBlock(apply, diagonal) + + +# +# 2nd order coupling +# +def block_h_phh_2(hf, mp, intermediates): + # M_{12} + # Intermediate can be found in 'adc_pp/matrix.py' + i2 = intermediates.adc3_pia + + def apply(ampl): + return AmplitudeVector(h=( + + 1 / sqrt(2) * einsum("jkib,jkb->i", i2, ampl.phh))) + return AdcBlock(apply, 0) + + +def block_phh_h_2(hf, mp, intermediates): + # M_{21} + # Intermediate can be found in 'adc_pp/matrix.py' + i2 = intermediates.adc3_pia + + def apply(ampl): + return AmplitudeVector(phh=( + + 1 / sqrt(2) * einsum("ijka,k->ija", i2, ampl.h))) + return AdcBlock(apply, 0) + + +# +# 3rd order main +# +def block_h_h_3(hf, mp, intermediates): + # M_{11} + i1 = intermediates.adc3_ip_i1 + diagonal = AmplitudeVector(h=i1.diagonal()) + + def apply(ampl): + return AmplitudeVector(h=einsum("ij,j->i", i1, ampl.h)) + return AdcBlock(apply, diagonal) + + +# +# Intermediates +# + +@register_as_intermediate +def adc3_ip_i1(hf, mp, intermediates): + return ( + - hf.foo + ( + + 0.5 * einsum("ikab,jkab->ij", mp.t2oo, hf.oovv) + - 0.25 * einsum("jkab,ikab->ij", mp.t2oo, mp.t2eri(b.oovv, b.vv)) + + 0.5 * einsum("jkab,ikba->ij", mp.t2oo, mp.t2eri(b.oovv, b.oo)) + + einsum("jkab,kiab->ij", mp.t2oo, mp.t2eri(b.oovv, b.ov)) + - 2 * einsum("jkab,ikab->ij", mp.t2oo, mp.t2eri(b.oovv, b.ov)) + ).symmetrise() + - intermediates.sigma_oo + ) + + +@register_as_intermediate +def sigma_oo(hf, mp, intermediates): + # Static self-energy, oo part \Sigma_{ij}(\infty) + p0 = mp.mp2_diffdm + return (einsum("ikjl,kl->ij", hf.oooo, p0.oo) + + 2 * einsum("ikja,ka->ij", hf.ooov, p0.ov) + + einsum("iajb,ab->ij", hf.ovov, p0.vv)).symmetrise() diff --git a/adcc/adc_ip/util.py b/adcc/adc_ip/util.py new file mode 100644 index 000000000..8b13a9459 --- /dev/null +++ b/adcc/adc_ip/util.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab +## --------------------------------------------------------------------- +## +## Copyright (C) 2020 by the adcc authors +## +## This file is part of adcc. +## +## adcc is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## adcc is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with adcc. If not, see . +## +## --------------------------------------------------------------------- + + +def check_singles_amplitudes(spaces, *amplitudes): + check_have_singles_block(*amplitudes) + check_singles_subspaces(spaces, *amplitudes) + + +def check_doubles_amplitudes(spaces, *amplitudes): + check_have_doubles_block(*amplitudes) + check_doubles_subspaces(spaces, *amplitudes) + + +def check_have_singles_block(*amplitudes): + if any("h" not in amplitude.keys() for amplitude in amplitudes): + raise ValueError("ADC(0) level and " + "beyond expects an excitation amplitude with a " + "singles part.") + + +def check_have_doubles_block(*amplitudes): + if any("phh" not in amplitude.keys() for amplitude in amplitudes): + raise ValueError("ADC(2) level and " + "beyond expects an excitation amplitude with a " + "singles and a doubles part.") + + +def check_singles_subspaces(spaces, *amplitudes): + for amplitude in amplitudes: + u1 = amplitude.h + if u1.subspaces != spaces: + raise ValueError("Mismatch in subspaces singles part " + f"(== {u1.subspaces}), where {spaces} " + "was expected.") + + +def check_doubles_subspaces(spaces, *amplitudes): + for amplitude in amplitudes: + u2 = amplitude.phh + if u2.subspaces != spaces: + raise ValueError("Mismatch in subspaces doubles part " + f"(== {u2.subspaces}), where " + f"{spaces} was expected.") diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index de36b66a4..2a9befdb6 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -60,24 +60,12 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): while one would probably want in the long run that one can have an "o2" space, but not do CVS. """ - if isinstance(variant, str): - variant = [variant] - elif variant is None: - variant = [] reference_state = ground_state.reference_state if intermediates is None: intermediates = Intermediates(ground_state) - if ground_state.has_core_occupied_space and "cvs" not in variant: - raise ValueError("Cannot run a general (non-core-valence approximated) " - "ADC method on top of a ground state with a " - "core-valence separation.") - if not ground_state.has_core_occupied_space and "cvs" in variant: - raise ValueError("Cannot run a core-valence approximated ADC method on " - "top of a ground state without a " - "core-valence separation.") - - fn = "_".join(["block"] + variant + spaces + [str(order)]) + fn = b.get_block_name(spaces, order, variant, + ground_state.has_core_occupied_space) if fn not in globals(): raise ValueError("Could not dispatch: " diff --git a/adcc/block.py b/adcc/block.py index f5f0ae96a..5124aa85c 100644 --- a/adcc/block.py +++ b/adcc/block.py @@ -176,3 +176,25 @@ def invert_transpose_tuple(p: tuple[int, ...]) -> tuple[int, ...]: factor *= -1 transpose = invert_transpose_tuple(transpose) return (canonical_block, factor, transpose) + + +def get_block_name(spaces, order, variant, has_core_occupied_space): + """ + Assembles name of block function with variant, spaces, and order. + Does some sanity checks. + """ + if isinstance(variant, str): + variant = [variant] + elif variant is None: + variant = [] + + if has_core_occupied_space and "cvs" not in variant: + raise ValueError("Cannot run a general (non-core-valence approximated) " + "ADC method on top of a ground state with a " + "core-valence separation.") + if not has_core_occupied_space and "cvs" in variant: + raise ValueError("Cannot run a core-valence approximated ADC method on " + "top of a ground state without a " + "core-valence separation.") + + return "_".join(["block"] + variant + spaces + [str(order)]) diff --git a/adcc/guess/__init__.py b/adcc/guess/__init__.py index b2690876b..8fb0aaae4 100644 --- a/adcc/guess/__init__.py +++ b/adcc/guess/__init__.py @@ -22,25 +22,29 @@ ## --------------------------------------------------------------------- from .guess_zero import guess_symmetries, guess_zero from .guesses_from_diagonal import guesses_from_diagonal +from .util import estimate_n_guesses, determine_spin_change -__all__ = ["guess_zero", "guesses_from_diagonal", +__all__ = ["guess_zero", "guesses_from_diagonal", + "get_spin_block_symmetrisation", "guesses_doublet", "guesses_singlet", "guesses_triplet", "guesses_any", - "guesses_spin_flip", "guess_symmetries"] + "guesses_spin_flip", "guess_symmetries", + "estimate_n_guesses", "determine_spin_change"] -def guess_kwargs_kind(kind): +def get_spin_block_symmetrisation(kind: str) -> str: """ Return the kwargs required to be passed to `guesses_from_diagonal` to computed states of the passed excitation `kind`. """ - kwargsmap = dict( - singlet=dict(spin_block_symmetrisation="symmetric", spin_change=0), - triplet=dict(spin_block_symmetrisation="antisymmetric", spin_change=0), - spin_flip=dict(spin_block_symmetrisation="none", spin_change=-1), - any=dict(spin_block_symmetrisation="none", spin_change=0), - ) + symmetrisation = { + "singlet": "symmetric", + "doublet": "none", + "triplet": "antisymmetric", + "spin_flip":"none", + "any": "none" + } try: - return kwargsmap[kind] + return symmetrisation[kind] except KeyError: raise ValueError(f"Kind not known: {kind}") @@ -50,15 +54,46 @@ def guesses_singlet(matrix, n_guesses, block="ph", **kwargs): Obtain guesses for computing singlet states by inspecting the passed ADC matrix. - matrix The matrix for which guesses are to be constructed - n_guesses The number of guesses to be searched for. Less number of - vectors are returned if this many could not be found. - block Diagonal block to use for obtaining the guesses - (typically "ph" or "pphh"). - kwargs Any other argument understood by guesses_from_diagonal. + matrix The matrix for which guesses are to be constructed + n_guesses The number of guesses to be searched for. Less number of + vectors are returned if this many could not be found. + block Diagonal block to use for obtaining the guesses + (typically "ph" or "pphh"). + is_alpha Is the detached/attached electron alpha spin for the respective + IP-/EA-ADC calculation. + kwargs Any other argument understood by guesses_from_diagonal. + """ + return guesses_from_diagonal( + matrix, n_guesses, block=block, spin_change=0, + spin_block_symmetrisation=get_spin_block_symmetrisation("singlet"), + **kwargs + ) + + +def guesses_doublet(matrix, n_guesses, block="h", is_alpha=True, **kwargs): + """ + Obtain guesses for computing doublet states by inspecting the passed + ADC matrix. + + matrix The matrix for which guesses are to be constructed + n_guesses The number of guesses to be searched for. Less number of + vectors are returned if this many could not be found. + block Diagonal block to use for obtaining the guesses + (typically "ph" or "pphh"). + is_alpha Is the detached/attached electron alpha spin for the respective + IP-/EA-ADC calculation. + kwargs Any other argument understood by guesses_from_diagonal. """ - return guesses_from_diagonal(matrix, n_guesses, block=block, - **guess_kwargs_kind("singlet"), **kwargs) + if matrix.method.adc_type == "ip": + spin_change = -0.5 + elif matrix.method.adc_type == "ea": + spin_change = 0.5 + return guesses_from_diagonal( + matrix, n_guesses, block=block, + is_alpha=is_alpha, spin_change=spin_change, + spin_block_symmetrisation= get_spin_block_symmetrisation("doublet"), + **kwargs + ) def guesses_triplet(matrix, n_guesses, block="ph", **kwargs): @@ -66,18 +101,23 @@ def guesses_triplet(matrix, n_guesses, block="ph", **kwargs): Obtain guesses for computing triplet states by inspecting the passed ADC matrix. - matrix The matrix for which guesses are to be constructed - n_guesses The number of guesses to be searched for. Less number of - vectors are returned if this many could not be found. - block Diagonal block to use for obtaining the guesses - (typically "ph" or "pphh"). - kwargs Any other argument understood by guesses_from_diagonal. + matrix The matrix for which guesses are to be constructed + n_guesses The number of guesses to be searched for. Less number of + vectors are returned if this many could not be found. + block Diagonal block to use for obtaining the guesses + (typically "ph" or "pphh"). + is_alpha Is the detached/attached electron alpha spin for the respective + IP-/EA-ADC calculation. + kwargs Any other argument understood by guesses_from_diagonal. """ - return guesses_from_diagonal(matrix, n_guesses, block=block, - **guess_kwargs_kind("triplet"), **kwargs) + return guesses_from_diagonal( + matrix, n_guesses, block=block, spin_change=0, + spin_block_symmetrisation= get_spin_block_symmetrisation("triplet"), + **kwargs + ) -# guesses for computing any state (singlet or triplet) +# guesses for computing any state (excluding spin-flip states) guesses_any = guesses_from_diagonal @@ -86,12 +126,17 @@ def guesses_spin_flip(matrix, n_guesses, block="ph", **kwargs): Obtain guesses for computing spin-flip states by inspecting the passed ADC matrix. - matrix The matrix for which guesses are to be constructed - n_guesses The number of guesses to be searched for. Less number of - vectors are returned if this many could not be found. - block Diagonal block to use for obtaining the guesses - (typically "ph" or "pphh"). - kwargs Any other argument understood by guesses_from_diagonal. + matrix The matrix for which guesses are to be constructed + n_guesses The number of guesses to be searched for. Less number of + vectors are returned if this many could not be found. + block Diagonal block to use for obtaining the guesses + (typically "ph" or "pphh"). + is_alpha Is the detached/attached electron alpha spin for the respective + IP-/EA-ADC calculation. + kwargs Any other argument understood by guesses_from_diagonal. """ - return guesses_from_diagonal(matrix, n_guesses, block=block, - **guess_kwargs_kind("spin_flip"), **kwargs) + return guesses_from_diagonal( + matrix, n_guesses, block=block, spin_change=-1, + spin_block_symmetrisation= get_spin_block_symmetrisation("spin_flip"), + **kwargs + ) \ No newline at end of file diff --git a/adcc/guess/guess_zero.py b/adcc/guess/guess_zero.py index 67791b9be..4ed50dc21 100644 --- a/adcc/guess/guess_zero.py +++ b/adcc/guess/guess_zero.py @@ -34,14 +34,14 @@ def guess_zero(matrix, spin_change=0, spin_block_symmetrisation="none"): spin_change The spin change to enforce in an excitation. Typical values are 0 (singlet/triplet/any) and -1 (spin-flip). spin_block_symmetrisation - Symmetrisation to enforce between equivalent spin blocks, which - all yield the desired spin_change. E.g. if spin_change == 0, - then both the alpha->alpha and beta->beta blocks of the singles - part of the excitation vector achieve a spin change of 0. - The symmetry specified with this parameter will then be imposed - between the a-a and b-b blocks. Valid values are "none", - "symmetric" and "antisymmetric", where "none" enforces - no particular symmetry. + Symmetrisation to enforce between equivalent spin blocks, + which all yield the desired spin_change. E.g. if + spin_change == 0, then both the alpha->alpha and beta->beta + blocks of the singles part of the excitation vector achieve a + spin change of 0. The symmetry specified with this parameter + will then be imposed between the a-a and b-b blocks. Valid + values are "none", "symmetric" and "antisymmetric", where + "none" enforces no particular symmetry. """ return AmplitudeVector(**{ block: Tensor(sym) for block, sym in guess_symmetries( @@ -60,14 +60,14 @@ def guess_symmetries(matrix, spin_change=0, spin_block_symmetrisation="none"): spin_change The spin change to enforce in an excitation. Typical values are 0 (singlet/triplet/any) and -1 (spin-flip). spin_block_symmetrisation - Symmetrisation to enforce between equivalent spin blocks, which - all yield the desired spin_change. E.g. if spin_change == 0, - then both the alpha->alpha and beta->beta blocks of the singles - part of the excitation vector achieve a spin change of 0. - The symmetry specified with this parameter will then be imposed - between the a-a and b-b blocks. Valid values are "none", - "symmetric" and "antisymmetric", where "none" enforces - no particular symmetry. + Symmetrisation to enforce between equivalent spin blocks, + which all yield the desired spin_change. E.g. if + spin_change == 0, then both the alpha->alpha and beta->beta + blocks of the singles part of the excitation vector achieve a + spin change of 0. The symmetry specified with this parameter + will then be imposed between the a-a and b-b blocks. Valid + values are "none", "symmetric" and "antisymmetric", where + "none" enforces no particular symmetry. """ if not isinstance(matrix, AdcMatrixlike): raise TypeError("matrix needs to be of type AdcMatrixlike") @@ -80,52 +80,60 @@ def guess_symmetries(matrix, spin_change=0, spin_block_symmetrisation="none"): "ADC calculations on top of restricted reference " "states.") if int(spin_change * 2) / 2 != spin_change: - raise ValueError("Only integer or half-integer spin_change is allowed. " - "You passed {}".format(spin_change)) - - max_spin_change = 0 - if "ph" in matrix.axis_blocks: - max_spin_change = 1 - if "pphh" in matrix.axis_blocks: - max_spin_change = 2 - if spin_change > max_spin_change: - raise ValueError("spin_change for singles guesses may only be in the " - f"range [{-max_spin_change}, {max_spin_change}] and " + raise ValueError("Only integer or half-integer spin_change is allowed." + " You passed {}".format(spin_change)) + + max_spin_change = 0.5 * len(matrix.axis_blocks[-1]) + valid_spin_changes = [max_spin_change - i for i in range(int(2 * max_spin_change + 1))] + + if spin_change not in valid_spin_changes: + raise ValueError("spin_change for may only be one of " + f"{valid_spin_changes}, and " f"not {spin_change}.") symmetries = {} - if "ph" in matrix.axis_blocks: - symmetries["ph"] = guess_symmetry_singles( + block = matrix.axis_blocks[0] + symmetries[block] = guess_symmetry_singles( + matrix, spin_change=spin_change, + spin_block_symmetrisation=spin_block_symmetrisation, block=block + ) + if len(matrix.axis_blocks) >= 2: + block = matrix.axis_blocks[1] + symmetries[block] = guess_symmetry_doubles( matrix, spin_change=spin_change, - spin_block_symmetrisation=spin_block_symmetrisation - ) - if "pphh" in matrix.axis_blocks: - symmetries["pphh"] = guess_symmetry_doubles( - matrix, spin_change=spin_change, - spin_block_symmetrisation=spin_block_symmetrisation + spin_block_symmetrisation=spin_block_symmetrisation, block=block ) return symmetries def guess_symmetry_singles(matrix, spin_change=0, - spin_block_symmetrisation="none"): - symmetry = Symmetry(matrix.mospaces, "".join(matrix.axis_spaces["ph"])) + spin_block_symmetrisation="none", block="ph"): + symmetry = Symmetry(matrix.mospaces, "".join(matrix.axis_spaces[block])) symmetry.irreps_allowed = ["A"] + if spin_change != 0 and spin_block_symmetrisation != "none": raise NotImplementedError("spin_symmetrisation != 'none' only " "implemented for spin_change == 0") - elif spin_block_symmetrisation == "symmetric": - symmetry.spin_block_maps = [("aa", "bb", 1)] - symmetry.spin_blocks_forbidden = ["ab", "ba"] - elif spin_block_symmetrisation == "antisymmetric": - symmetry.spin_block_maps = [("aa", "bb", -1)] + + if spin_block_symmetrisation in ("symmetric", "antisymmetric"): + # PP-ADC + fac = 1 if spin_block_symmetrisation == "symmetric" else -1 + symmetry.spin_block_maps = [("aa", "bb", fac)] symmetry.spin_blocks_forbidden = ["ab", "ba"] + + elif matrix.method.adc_type != "pp" and matrix.reference_state.restricted: + # IP- and EA-ADC + # attach/detach alpha electron + # forbidden beta blocks (["a"]) not needed because for a + # restricted reference only alpha states will be computed + symmetry.spin_blocks_forbidden = ["b"] + return symmetry def guess_symmetry_doubles(matrix, spin_change=0, - spin_block_symmetrisation="none"): - spaces_d = matrix.axis_spaces["pphh"] + spin_block_symmetrisation="none", block="pphh"): + spaces_d = matrix.axis_spaces[block] symmetry = Symmetry(matrix.mospaces, "".join(spaces_d)) symmetry.irreps_allowed = ["A"] @@ -133,32 +141,80 @@ def guess_symmetry_doubles(matrix, spin_change=0, raise NotImplementedError("spin_symmetrisation != 'none' only " "implemented for spin_change == 0") - if spin_change == 0 \ - and spin_block_symmetrisation in ("symmetric", "antisymmetric"): - fac = 1 if spin_block_symmetrisation == "symmetric" else -1 - # Spin mapping between blocks where alpha and beta are just mirrored - symmetry.spin_block_maps = [("aaaa", "bbbb", fac), - ("abab", "baba", fac), - ("abba", "baab", fac)] - - # Mark blocks which change spin as forbidden - symmetry.spin_blocks_forbidden = ["aabb", # spin_change +2 - "bbaa", # spin_change -2 - "aaab", # spin_change +1 - "aaba", # spin_change +1 - "abaa", # spin_change -1 - "baaa", # spin_change -1 - "abbb", # spin_change +1 - "babb", # spin_change +1 - "bbab", # spin_change -1 - "bbba"] # spin_change -1 - - # Add index permutation symmetry: - permutations = ["ijab"] - if spaces_d[0] == spaces_d[1]: - permutations.append("-jiab") - if spaces_d[2] == spaces_d[3]: - permutations.append("-ijba") - if len(permutations) > 1: - symmetry.permutations = permutations + if matrix.method.adc_type == "pp": + # PP-ADC + if spin_block_symmetrisation in ("symmetric", "antisymmetric"): + fac = 1 if spin_block_symmetrisation == "symmetric" else -1 + # Spin mapping between blocks where alpha and beta are mirrored + symmetry.spin_block_maps = [("aaaa", "bbbb", fac), + ("abab", "baba", fac), + ("abba", "baab", fac)] + + # Mark blocks which change spin as forbidden + symmetry.spin_blocks_forbidden = ["aabb", # spin_change -2 + "bbaa", # spin_change +2 + "aaab", # spin_change -1 + "aaba", # spin_change -1 + "abaa", # spin_change +1 + "baaa", # spin_change +1 + "abbb", # spin_change -1 + "babb", # spin_change -1 + "bbab", # spin_change +1 + "bbba"] # spin_change +1 + + # Add index permutation symmetry: + permutations = ["ijab"] + if spaces_d[0] == spaces_d[1]: + permutations.append("-jiab") + if spaces_d[2] == spaces_d[3]: + permutations.append("-ijba") + if len(permutations) > 1: + symmetry.permutations = permutations + + elif matrix.method.adc_type == "ip": + # IP-ADC + # No spin mapping between blocks + symmetry.spin_block_maps = [] + + # Mark blocks which change spin incorrectly as forbidden + if matrix.reference_state.restricted: # kind=doublet + # detach alpha electron + # forbidden beta ionization blocks not needed because for a + # restricted reference only alpha states will be computed + symmetry.spin_blocks_forbidden = ["aab", # spin_change -3/2 + "bba", # spin_change +3/2 + "aba", # spin_change +1/2 + "baa", # spin_change +1/2 + "bbb"] # spin_change +1/2 + + # Add index permutation symmetry: + permutations = ["ija"] + if spaces_d[0] == spaces_d[1]: + permutations.append("-jia") + if len(permutations) > 1: + symmetry.permutations = permutations + + else: + # EA-ADC + # No spin mapping between blocks + symmetry.spin_block_maps = [] + + # Mark blocks which change spin incorrectly as forbidden + if matrix.reference_state.restricted: # kind=doublet + # attach alpha electron + # forbidden beta attachment blocks not needed because for a + # restricted reference only alpha states will be computed + symmetry.spin_blocks_forbidden = ["baa", # spin_change +3/2 + "abb", # spin_change -3/2 + "aab", # spin_change -1/2 + "aba", # spin_change -1/2 + "bbb"] # spin_change -1/2 + + # Add index permutation symmetry: + permutations = ["iab"] + if spaces_d[1] == spaces_d[2]: + permutations.append("-iba") + if len(permutations) > 1: + symmetry.permutations = permutations + return symmetry diff --git a/adcc/guess/guesses_from_diagonal.py b/adcc/guess/guesses_from_diagonal.py index 2d94f9720..a8224178f 100644 --- a/adcc/guess/guesses_from_diagonal.py +++ b/adcc/guess/guesses_from_diagonal.py @@ -31,7 +31,8 @@ from .guess_zero import guess_zero -def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, +def guesses_from_diagonal(matrix, n_guesses, block="ph", kind=None, + is_alpha=None, spin_change=0, spin_block_symmetrisation="none", degeneracy_tolerance=1e-14, max_diagonal_value=1000): """ @@ -45,23 +46,26 @@ def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, vectors are returned if this many could not be found. block Diagonal block to use for obtaining the guesses (typically "ph" or "pphh"). + is_alpha Is the detached/attached electron alpha spin for the + respective IP-/EA-ADC calculation. spin_change The spin change to enforce in an excitation. - Typical values are 0 (singlet/triplet/any) and -1 (spin-flip). + Typical values are 0 (singlet/triplet/any), +/- 0.5 (doublet) + and -1 (spin-flip). spin_block_symmetrisation - Symmetrisation to enforce between equivalent spin blocks, which - all yield the desired spin_change. E.g. if spin_change == 0, - then both the alpha->alpha and beta->beta blocks of the singles - part of the excitation vector achieve a spin change of 0. - The symmetry specified with this parameter will then be imposed - between the a-a and b-b blocks. Valid values are "none", - "symmetric" and "antisymmetric", where "none" enforces - no particular symmetry. + Symmetrisation to enforce between equivalent spin blocks, + which all yield the desired spin_change. E.g. if + spin_change == 0, then both the alpha->alpha and beta->beta + blocks of the singles part of the excitation vector achieve a + spin change of 0. The symmetry specified with this parameter + will then be imposed between the a-a and b-b blocks. Valid + values are "none", "symmetric" and "antisymmetric", where + "none" enforces no particular symmetry. degeneracy_tolerance Tolerance for two entries of the diagonal to be considered degenerate, i.e. identical. max_diagonal_value - Maximal diagonal value, which is considered as a valid candidate - to form a guess. + Maximal diagonal value, which is considered as a valid + candidate to form a guess. """ if not isinstance(matrix, AdcMatrixlike): raise TypeError("matrix needs to be of type AdcMatrixlike") @@ -74,8 +78,8 @@ def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, "ADC calculations on top of restricted reference " "states.") if int(spin_change * 2) / 2 != spin_change: - raise ValueError("Only integer or half-integer spin_change is allowed. " - "You passed {}".format(spin_change)) + raise ValueError("Only integer or half-integer spin_change is allowed." + " You passed {}".format(spin_change)) if block not in matrix.axis_blocks: raise ValueError("The passed ADC matrix does not have the block '{}.'" @@ -83,14 +87,15 @@ def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, if n_guesses == 0: return [] - if block == "ph": + if block in ["h", "p", "ph"]: guessfunction = guesses_from_diagonal_singles - elif block == "pphh": + elif block in ["phh", "pph", "pphh"]: guessfunction = guesses_from_diagonal_doubles else: - raise ValueError(f"Don't know how to generate guesses for block {block}") + raise ValueError("Don't know how to generate guesses for block " + f"{block}") - return guessfunction(matrix, n_guesses, spin_change, + return guessfunction(matrix, n_guesses, block, kind, is_alpha, spin_change, spin_block_symmetrisation, degeneracy_tolerance, max_diagonal_value) @@ -127,8 +132,8 @@ def spin_change(self): ("v", "a"): +0.5, # add alpha ("v", "b"): -0.5, # add beta } - return int(sum(mapping_spin_change[(space[0], spin)] - for space, spin in zip(self.subspaces, self.spin_block))) + return sum(mapping_spin_change[(space[0], spin)] + for space, spin in zip(self.subspaces, self.spin_block)) def __repr__(self): return f"({self.index} {self.value})" @@ -197,11 +202,12 @@ def telem_nospin(telem): return res -def guesses_from_diagonal_singles(matrix, n_guesses, spin_change=0, +def guesses_from_diagonal_singles(matrix, n_guesses, block="ph", kind=None, + is_alpha=None, spin_change=0, spin_block_symmetrisation="none", degeneracy_tolerance=1e-14, max_diagonal_value=1000): - motrans = MoIndexTranslation(matrix.mospaces, matrix.axis_spaces["ph"]) + motrans = MoIndexTranslation(matrix.mospaces, matrix.axis_spaces[block]) if n_guesses == 0: return [] @@ -216,12 +222,12 @@ def guesses_from_diagonal_singles(matrix, n_guesses, spin_change=0, # Also it filters out too large diagonal entries (which are essentially # hopeless to give useful excitations) def pred_singles(telem): - return (ret[0].ph.is_allowed(telem.index) + return (ret[0].get(block).is_allowed(telem.index) and telem.spin_change == spin_change and abs(telem.value) <= max_diagonal_value) elements = find_smallest_matching_elements( - pred_singles, matrix.diagonal().ph, motrans, n_guesses, + pred_singles, matrix.diagonal().get(block), motrans, n_guesses, degeneracy_tolerance=degeneracy_tolerance ) if len(elements) == 0: @@ -245,25 +251,25 @@ def telem_nospin(telem): group = list(group) if len(group) == 1: # Just add the single vector - ret[ivec].ph[group[0].index] = 1.0 + ret[ivec].get(block)[group[0].index] = 1.0 ivec += 1 elif len(group) == 2: # Since these two are grouped together, their # spatial parts must be identical. # Add the positive linear combination ... - ret[ivec].ph[group[0].index] = 1 - ret[ivec].ph[group[1].index] = 1 + ret[ivec].get(block)[group[0].index] = 1 + ret[ivec].get(block)[group[1].index] = 1 ivec += 1 # ... and the negative linear combination if ivec < n_guesses: - ret[ivec].ph[group[0].index] = +1 - ret[ivec].ph[group[1].index] = -1 + ret[ivec].get(block)[group[0].index] = +1 + ret[ivec].get(block)[group[1].index] = -1 ivec += 1 else: raise AssertionError("group size > 3 should not occur " - "when setting up single guesse.") + "when setting up single guesses.") assert ivec <= n_guesses # Resize in case less guesses found than requested @@ -271,7 +277,8 @@ def telem_nospin(telem): return [evaluate(v / np.sqrt(v @ v)) for v in ret[:ivec]] -def guesses_from_diagonal_doubles(matrix, n_guesses, spin_change=0, +def guesses_from_diagonal_doubles(matrix, n_guesses, block="pphh", kind=None, + is_alpha=None, spin_change=0, spin_block_symmetrisation="none", degeneracy_tolerance=1e-14, max_diagonal_value=1000): @@ -283,24 +290,41 @@ def guesses_from_diagonal_doubles(matrix, n_guesses, spin_change=0, spin_block_symmetrisation=spin_block_symmetrisation) for _ in range(n_guesses)] - # Build delta-Fock matrices - spaces_d = matrix.axis_spaces["pphh"] - df02 = matrix.ground_state.df(spaces_d[0] + spaces_d[2]) - df13 = matrix.ground_state.df(spaces_d[1] + spaces_d[3]) - - guesses_d = [gv.pphh for gv in ret] # Extract doubles parts spin_change_twice = int(spin_change * 2) assert spin_change_twice / 2 == spin_change - n_found = libadcc.fill_pp_doubles_guesses( - guesses_d, matrix.mospaces, df02, df13, - spin_change_twice, degeneracy_tolerance - ) + # Extract doubles parts + guesses_d = [gv.get(block) for gv in ret] + + if matrix.method.adc_type == "pp": + # PP-ADC + spaces_d = matrix.axis_spaces[block] + # Build delta-Fock matrices + df02 = matrix.ground_state.df(spaces_d[0] + spaces_d[2]) + df13 = matrix.ground_state.df(spaces_d[1] + spaces_d[3]) + n_found = libadcc.fill_pp_doubles_guesses( + guesses_d, matrix.mospaces, df02, df13, + spin_change_twice, degeneracy_tolerance + ) + else: + # IP- and EA-ADC + # Build Fock matrices and multiply occ. orbitals with -1 to invert the + # order for simplicity in the C++ code + d_o = matrix.reference_state.foo.diagonal() * (-1) + d_v = matrix.reference_state.fvv.diagonal() + doublet = (kind == "doublet") + is_restricted = matrix.reference_state.restricted + double_guess_func = {"ip": libadcc.fill_ip_doubles_guesses, + "ea": libadcc.fill_ea_doubles_guesses} + n_found = double_guess_func[matrix.method.adc_type]( + guesses_d, matrix.mospaces, d_o, d_v, is_alpha, is_restricted, + doublet, spin_change_twice, degeneracy_tolerance) + # Resize in case less guesses found than requested ret = ret[:n_found] # Filter out elements above the noted diagonal value - diagonal_elements = [ret_d.pphh.dot(matrix.diagonal().pphh * ret_d.pphh) - for ret_d in ret] + diagonal_elements = [ret_d.get(block).dot(matrix.diagonal().get(block) + * ret_d.get(block)) for ret_d in ret] return [ret[i] for (i, elem) in enumerate(diagonal_elements) if elem <= max_diagonal_value] diff --git a/adcc/guess/util.py b/adcc/guess/util.py new file mode 100644 index 000000000..aa0eb7ebc --- /dev/null +++ b/adcc/guess/util.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab +## --------------------------------------------------------------------- +## +## Copyright (C) 2018 by the adcc authors +## +## This file is part of adcc. +## +## adcc is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## adcc is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with adcc. If not, see . +## +## --------------------------------------------------------------------- + +from ..AdcMethod import AdcMethod +from typing import Optional + + +def determine_spin_change(method: AdcMethod, kind: str, + is_alpha: Optional[bool] = None) -> int | float: + if method.adc_type == "pp": + if kind == "spin_flip": + return -1 + else: + return 0 + elif method.adc_type == "ip": + if is_alpha is None: + raise TypeError("'is_alpha' has to be True|False for IP-ADC") + return +0.5 - int(is_alpha) + elif method.adc_type == "ea": + if is_alpha is None: + raise TypeError("'is_alpha' has to be True|False for EA-ADC") + return -0.5 + int(is_alpha) + else: + raise ValueError(f"Unknown ADC method: {method.name}") + + +def estimate_n_guesses(matrix, n_states, n_guesses_per_state=2, + singles_only=True) -> int: + """ + Implementation of a basic heuristic to find a good number of guess + vectors to be searched for using the find_guesses function. + Internal function called from run_adc. + + matrix ADC matrix + n_states Number of states to be computed + singles_only Try to stay withing the singles excitation space + with the number of guess vectors. + n_guesses_per_state Number of guesses to search for for each state + """ + # Try to use at least 4 or twice the number of states + # to be computed as guesses + n_guesses = n_guesses_per_state * max(2, n_states) + + if singles_only: + # Compute the maximal number of sensible singles block guesses. + # This is roughly the number of occupied alpha orbitals + # times the number of virtual alpha orbitals + # + # If the system is core valence separated, then only the + # core electrons count as "occupied". + mospaces = matrix.mospaces + sp_occ = "o2" if matrix.is_core_valence_separated else "o1" + n_virt_a = mospaces.n_orbs_alpha("v1") + n_occ_a = mospaces.n_orbs_alpha(sp_occ) + estimate = n_occ_a * n_virt_a + if matrix.method.level < 2 and matrix.method.adc_type != "pp": + # Adjustment for IP- and EA-ADC(0/1) calculations + estimate = (n_occ_a if matrix.method.adc_type == "ip" + else n_virt_a) + n_guesses = min(n_guesses, estimate) + + # Adjust if we overshoot the maximal number of sensible singles block + # guesses, but make sure we get at least n_states guesses + return max(n_states, n_guesses) \ No newline at end of file diff --git a/adcc/projection.py b/adcc/projection.py index d6eee2580..b3b7fa749 100644 --- a/adcc/projection.py +++ b/adcc/projection.py @@ -26,7 +26,8 @@ import numpy as np -from .guess import guess_kwargs_kind, guess_symmetries +from .guess import (get_spin_block_symmetrisation, guess_symmetries, + determine_spin_change) from .Tensor import Tensor from .MoSpaces import expand_spaceargs from .Symmetry import Symmetry @@ -332,8 +333,10 @@ def transfer_cvs_to_full(state_matrix_cvs, matrix_full, vector=None, kind=None, raise ValueError("kind needs to be given if first argument is not an " "ExcitedStates object and spin symmetry setup is not " "explicitly given.") - return transfer_cvs_to_full(state_matrix_cvs, matrix_full, vector, kind, - **guess_kwargs_kind(kind)) + return transfer_cvs_to_full( + state_matrix_cvs, matrix_full, vector, kind, + spin_change=determine_spin_change(matrix_full.method, kind), + spin_block_symmetrisation=get_spin_block_symmetrisation(kind)) if vector is None: if hasattr(state_matrix_cvs, "excitation_vector"): @@ -342,8 +345,11 @@ def transfer_cvs_to_full(state_matrix_cvs, matrix_full, vector=None, kind=None, raise ValueError("vector needs to be given if first argument is not an " "ExcitedStates object.") if isinstance(vector, list): - return [transfer_cvs_to_full(state_matrix_cvs, matrix_full, v, kind, - **guess_kwargs_kind(kind)) for v in vector] + return [transfer_cvs_to_full( + state_matrix_cvs, matrix_full, v, kind, + spin_change=determine_spin_change(matrix_full.method, kind), + spin_block_symmetrisation=get_spin_block_symmetrisation(kind) + ) for v in vector] if isinstance(state_matrix_cvs, AdcMatrixlike): mospaces_cvs = state_matrix_cvs.mospaces diff --git a/adcc/solver/explicit_symmetrisation.py b/adcc/solver/explicit_symmetrisation.py index d49e55bf0..01a16b031 100644 --- a/adcc/solver/explicit_symmetrisation.py +++ b/adcc/solver/explicit_symmetrisation.py @@ -76,6 +76,8 @@ class IndexSpinSymmetrisation(IndexSymmetrisation): def __init__(self, matrix, enforce_spin_kind="singlet"): super().__init__(matrix) self.enforce_spin_kind = enforce_spin_kind + # Bool to distinguish IP/EA if 'spin_kind=='doublet' + self.is_ip_adc = matrix.method.adc_type == "ip" def symmetrise(self, new_vectors): if isinstance(new_vectors, AmplitudeVector): @@ -87,12 +89,13 @@ def symmetrise(self, new_vectors): for vec in new_vectors: # Only work on the doubles part # the other blocks are not yet implemented - # or nothing needs to be done ("ph" block) - if "pphh" in vec.blocks: + # or nothing needs to be done ("ph"/"h"/"p" block) + if len(vec.blocks) > 1: # TODO: Note that the "d" is needed here because the C++ side # does not yet understand ph and pphh amplitude_vector_enforce_spin_kind( - vec.pphh, "d", self.enforce_spin_kind + vec.get(vec.blocks[1]), "d", self.enforce_spin_kind, + self.is_ip_adc ) return new_vectors diff --git a/adcc/tests/AdcMatrix_test.py b/adcc/tests/AdcMatrix_test.py index 325eb2f30..b23247fa1 100644 --- a/adcc/tests/AdcMatrix_test.py +++ b/adcc/tests/AdcMatrix_test.py @@ -68,7 +68,7 @@ def test_default_block_orders(self): assert block_orders == ref assert block_orders == cvs_block_orders - def test_validate_block_orders(self): + def test_validate_block_orders_pp(self): valid_block_orders = ( {"ph_ph": 0}, # adc0 {"ph_ph": 1, "ph_pphh": None, "ppphhh_ppphhh": None}, # adc1 @@ -90,7 +90,7 @@ def test_validate_block_orders(self): block_orders, AdcMethod("adc0") ) - def test_validate_space(self): + def test_validate_space_pp(self): # some valid PP-ADC methods assert AdcMatrixlike._is_valid_space( "ph", AdcMethod("adc0") @@ -112,15 +112,65 @@ def test_validate_space(self): "phh", AdcMethod("adc0") ) + def test_validate_block_orders_ip_ea(self): + # Valid IP block orders + valid_ip = ( + {"h_h": 0}, # ip-adc0 + {"h_h": 2, "h_phh": 1, "phh_h": 1, "phh_phh": 0}, # ip-adc2 + ) + for block_orders in valid_ip: + AdcMatrixlike._validate_block_orders( + block_orders, AdcMethod("ip-adc2") + ) + + # Valid EA block orders + valid_ea = ( + {"p_p": 0}, # ea-adc0 + {"p_p": 2, "p_pph": 1, "pph_p": 1, "pph_pph": 0}, # ea-adc2 + ) + for block_orders in valid_ea: + AdcMatrixlike._validate_block_orders( + block_orders, AdcMethod("ea-adc2") + ) + + # Invalid mixed blocks + with pytest.raises(ValueError): + AdcMatrixlike._validate_block_orders( + {"ph_ph": 0}, AdcMethod("ip-adc2") + ) + + def test_validate_space_ip(self): + ip = AdcMethod("ip-adc0") + assert AdcMatrixlike._is_valid_space("h", ip) + assert AdcMatrixlike._is_valid_space("phh", ip) + assert AdcMatrixlike._is_valid_space("ppphhhh", ip) + assert not AdcMatrixlike._is_valid_space("ph", ip) + assert not AdcMatrixlike._is_valid_space("p", ip) + + def test_validate_space_ea(self): + ea = AdcMethod("ea-adc0") + assert AdcMatrixlike._is_valid_space("p", ea) + assert AdcMatrixlike._is_valid_space("pph", ea) + assert AdcMatrixlike._is_valid_space("pppphhh", ea) + assert not AdcMatrixlike._is_valid_space("ph", ea) + assert not AdcMatrixlike._is_valid_space("h", ea) + h2o_sto3g = testcases.get_by_filename("h2o_sto3g").pop() -methods = ["adc0", "adc1", "adc2", "adc2x", "adc3"] +pp_methods = ["adc0", "adc1", "adc2", "adc2x", "adc3"] +ip_methods = ["ip-" + m for m in ["adc0", "adc2", "adc2x", "adc3"]] +ea_methods = ["ea-" + m for m in ["adc0", "adc2", "adc2x", "adc3"]] + +cases = [(m, c) for c in ["gen", "cvs"] for m in pp_methods] +# No CVS for IP-ADC (yet) +# Test only for is_alpha=True for simplicity +cases += [(m, c) for c in ["gen"] for m in ip_methods] +cases += [(m, c) for c in ["gen"] for m in ea_methods] # Distinct implementations of the matrix equations only exist for the cases # "gen" and "cvs". -@pytest.mark.parametrize("method", methods) -@pytest.mark.parametrize("case", ["gen", "cvs"]) +@pytest.mark.parametrize("method,case", cases) @pytest.mark.parametrize("system", ["h2o_sto3g", "cn_sto3g"]) class TestAdcMatrix: def load_matrix_data(self, system: str, case: str, method: str) -> dict: @@ -171,16 +221,18 @@ def test_matvec(self, system: str, case: str, method: str): if matrix.reference_state.restricted: if matrix.method.adc_type == "pp": kind = "singlet" + elif matrix.method.adc_type in ("ip", "ea"): + kind = "doublet" else: raise ValueError(f"Unknown adc type {matrix.method.adc_type}.") else: kind = "any" # we don't do the test for spin flip trial_vec = self.construct_trial_vec(system, case, method, kind) result = matrix @ trial_vec - assert_allclose(matdata["matvec_singles"], result.ph.to_ndarray(), + assert_allclose(matdata["matvec_singles"], result.get(matrix.axis_blocks[0]).to_ndarray(), rtol=1e-10, atol=1e-12) if "matvec_doubles" in matdata: - assert_allclose(matdata["matvec_doubles"], result.pphh.to_ndarray(), + assert_allclose(matdata["matvec_doubles"], result.get(matrix.axis_blocks[1]).to_ndarray(), rtol=1e-10, atol=1e-12) def test_compute_block(self, system: str, case: str, method: str): @@ -191,6 +243,8 @@ def test_compute_block(self, system: str, case: str, method: str): if matrix.reference_state.restricted: if matrix.method.adc_type == "pp": kind = "singlet" + elif matrix.method.adc_type in ("ip", "ea"): + kind = "doublet" else: raise ValueError(f"Unknwon adc type {matrix.method.adc_type}.") else: @@ -209,10 +263,89 @@ def test_compute_block(self, system: str, case: str, method: str): atol=1e-12 ) + def test_hermiticity(self, system, case, method): + matrix = self.construct_matrix(system, case, method) + + # Only test for Hermitian ADC variants + # (Projected matrix may not preserve symmetry fully) + spin_change = 0 + if matrix.method.adc_type == "ip": + spin_change = -0.5 + elif matrix.method.adc_type == "ea": + spin_change = 0.5 + + v = adcc.guess_zero(matrix, spin_change=spin_change) + w = adcc.guess_zero(matrix, spin_change=spin_change) + + v.set_random() + w.set_random() + + Av = matrix @ v + Aw = matrix @ w + + lhs = v.dot(Aw) + rhs = Av.dot(w) + + assert abs(lhs - rhs) < 1e-10 + class TestAdcMatrixInterface: - @pytest.mark.parametrize("method", methods) - @pytest.mark.parametrize("case", h2o_sto3g.cases) + @pytest.mark.parametrize("method", pp_methods + ip_methods + ea_methods) + @pytest.mark.parametrize("system", ["h2o_sto3g"]) + @pytest.mark.parametrize("case", ["gen"]) # no CVS for IP/EA + def test_axis_structure_all_types(self, system, case, method): + reference_state = testdata_cache.refstate(system=system, case=case) + ground_state = adcc.LazyMp(reference_state) + + matrix = adcc.AdcMatrix(method, ground_state) + + assert matrix.ndim == 2 + assert matrix.shape[0] == matrix.shape[1] + assert len(matrix) == matrix.shape[0] + + blocks = matrix.axis_blocks + assert isinstance(blocks, list) + assert len(blocks) >= 1 + + # Block ordering must follow excitation rank + lengths = [len(b) for b in blocks] + assert lengths == sorted(lengths) + + # Axis dictionaries must match blocks + assert sorted(matrix.axis_spaces.keys(), key=len) == blocks + assert sorted(matrix.axis_lengths.keys(), key=len) == blocks + + # Validate block labels by ADC type + adc_type = matrix.method.adc_type + + if adc_type == "pp": + assert all(set(b).issubset({"p", "h"}) for b in blocks) + assert blocks[0].count("p") == 1 + assert blocks[0].count("h") == 1 + + elif adc_type == "ip": + # First block must remove one electron + assert blocks[0].count("h") == 1 + assert blocks[0].count("p") == 0 + + elif adc_type == "ea": + # First block must add one electron + assert blocks[0].count("p") == 1 + assert blocks[0].count("h") == 0 + + else: + raise AssertionError(f"Unknown ADC type {adc_type}") + + # Validate axis lengths consistency + for block in blocks: + assert matrix.axis_lengths[block] > 0 + + # Reference consistency + assert matrix.reference_state == reference_state + assert matrix.mospaces == reference_state.mospaces + + @pytest.mark.parametrize("method", pp_methods + ip_methods + ea_methods) + @pytest.mark.parametrize("case", ["gen"]) # No CVS for IP/EA @pytest.mark.parametrize("system", ["h2o_sto3g"]) def test_properties(self, system: str, case: str, method: str): reference_state = testdata_cache.refstate(system=system, case=case) @@ -225,8 +358,14 @@ def test_properties(self, system: str, case: str, method: str): assert matrix.is_core_valence_separated == ("cvs" in case) # check that the blocks are correct blocks = matrix.axis_blocks + assert isinstance(blocks, list) + assert len(blocks) >= 1 if matrix.method.adc_type == "pp": assert blocks == ["ph", "pphh", "ppphhh"][:matrix.method.level // 2 + 1] + elif matrix.method.adc_type == "ip": + assert blocks == ["h", "phh", "pphhh"][:matrix.method.level // 2 + 1] + elif matrix.method.adc_type == "ea": + assert blocks == ["p", "pph", "ppphh"][:matrix.method.level // 2 + 1] else: raise NotImplementedError(f"Unknown adc type {matrix.method.adc_type}.") assert sorted(matrix.axis_spaces.keys(), key=len) == blocks @@ -268,19 +407,28 @@ def test_properties(self, system: str, case: str, method: str): assert matrix.mospaces == reference_state.mospaces assert isinstance(matrix.timer, adcc.timings.Timer) - def test_intermediates_adc2(self): + @pytest.mark.parametrize("method", ["adc2", "ip-adc2", "ea-adc2"]) + def test_intermediates_adc2(self, method: str): ground_state = adcc.LazyMp(testdata_cache.refstate("h2o_sto3g", case="gen")) - matrix = adcc.AdcMatrix("adc2", ground_state) + matrix = adcc.AdcMatrix(method, ground_state) assert isinstance(matrix.intermediates, Intermediates) intermediates = Intermediates(ground_state) matrix.intermediates = intermediates assert matrix.intermediates == intermediates - def test_matvec_adc2(self): + @pytest.mark.parametrize("method", ["adc2", "ip-adc2", "ea-adc2"]) + def test_matvec_adc2(self, method: str): ground_state = adcc.LazyMp(testdata_cache.refstate("h2o_sto3g", case="gen")) - matrix = adcc.AdcMatrix("adc2", ground_state) + matrix = adcc.AdcMatrix(method, ground_state) + blocks = matrix.axis_blocks - vectors = [adcc.guess_zero(matrix) for _ in range(3)] + spin_change = 0 + if matrix.method.adc_type == "ip": + spin_change = -0.5 + elif matrix.method.adc_type == "ea": + spin_change = 0.5 + + vectors = [adcc.guess_zero(matrix, spin_change=spin_change) for _ in range(3)] for vec in vectors: vec.set_random() v, w, x = vectors @@ -293,32 +441,32 @@ def test_matvec_adc2(self): # @ operator (1 vector) resv = matrix @ v diffv = refv - resv - assert diffv.ph.dot(diffv.ph) < 1e-12 - assert diffv.pphh.dot(diffv.pphh) < 1e-12 + assert diffv.get(blocks[0]).dot(diffv.get(blocks[0])) < 1e-12 + assert diffv.get(blocks[1]).dot(diffv.get(blocks[1])) < 1e-12 # @ operator (multiple vectors) resv, resw, resx = matrix @ [v, w, x] diffs = [refv - resv, refw - resw, refx - resx] for i in range(3): - assert diffs[i].ph.dot(diffs[i].ph) < 1e-12 - assert diffs[i].pphh.dot(diffs[i].pphh) < 1e-12 + assert diffs[i].get(blocks[0]).dot(diffs[i].get(blocks[0])) < 1e-12 + assert diffs[i].get(blocks[1]).dot(diffs[i].get(blocks[1])) < 1e-12 # compute matvec resv = matrix.matvec(v) diffv = refv - resv - assert diffv.ph.dot(diffv.ph) < 1e-12 - assert diffv.pphh.dot(diffv.pphh) < 1e-12 + assert diffv.get(blocks[0]).dot(diffv.get(blocks[0])) < 1e-12 + assert diffv.get(blocks[1]).dot(diffv.get(blocks[1])) < 1e-12 resv = matrix.rmatvec(v) diffv = refv - resv - assert diffv.ph.dot(diffv.ph) < 1e-12 - assert diffv.pphh.dot(diffv.pphh) < 1e-12 + assert diffv.get(blocks[0]).dot(diffv.get(blocks[0])) < 1e-12 + assert diffv.get(blocks[1]).dot(diffv.get(blocks[1])) < 1e-12 # Test apply - resv.ph = matrix.block_apply("ph_ph", v.ph) - resv.ph += matrix.block_apply("ph_pphh", v.pphh) + resv[blocks[0]] = matrix.block_apply(f"{blocks[0]}_{blocks[0]}", v.get(blocks[0])) + resv[blocks[0]] += matrix.block_apply(f"{blocks[0]}_{blocks[1]}", v.get(blocks[1])) refv = matrix.matvec(v) - diffv = resv.ph - refv.ph + diffv = resv.get(blocks[0]) - refv.get(blocks[0]) assert diffv.dot(diffv) < 1e-12 def test_extra_term(self): @@ -389,40 +537,47 @@ def apply(invec): @pytest.mark.parametrize("system", ["h2o_sto3g", "cn_sto3g"]) +@pytest.mark.parametrize("method", ["adc3", "ip-adc3", "ea-adc3"]) class TestAdcMatrixShifted: - def construct_matrices(self, system, shift): + def construct_matrices(self, system:str, method: str, shift: float): reference_state = testdata_cache.refstate(system, case="gen") ground_state = adcc.LazyMp(reference_state) - matrix = adcc.AdcMatrix("adc3", ground_state) + matrix = adcc.AdcMatrix(method, ground_state) shifted = AdcMatrixShifted(matrix, shift) return matrix, shifted - def test_diagonal(self, system: str): + def test_diagonal(self, system: str, method: str): shift = -0.3 - matrix, shifted = self.construct_matrices(system, shift) + matrix, shifted = self.construct_matrices(system, method, shift) - for block in ("ph", "pphh"): + for block in matrix.axis_blocks: odiag = matrix.diagonal()[block].to_ndarray() sdiag = shifted.diagonal()[block].to_ndarray() assert np.max(np.abs(sdiag - shift - odiag)) < 1e-12 - def test_matmul(self, system: str): + def test_matmul(self, system: str, method: str): shift = -0.3 - matrix, shifted = self.construct_matrices(system, shift) + matrix, shifted = self.construct_matrices(system, method, shift) + blocks = matrix.axis_blocks - vec = adcc.guess_zero(matrix) + spin_change = 0 + if matrix.method.adc_type == "ip": + spin_change = -0.5 + elif matrix.method.adc_type == "ea": + spin_change = 0.5 + + vec = adcc.guess_zero(matrix, spin_change=spin_change) vec.set_random() ores = matrix @ vec sres = shifted @ vec - assert ores.ph.describe_symmetry() == sres.ph.describe_symmetry() - assert ores.pphh.describe_symmetry() == sres.pphh.describe_symmetry() + for block in blocks: + assert ores.get(block).describe_symmetry() == sres.get( + block).describe_symmetry() - diff_s = sres.ph - ores.ph - shift * vec.ph - diff_d = sres.pphh - ores.pphh - shift * vec.pphh - assert np.max(np.abs(diff_s.to_ndarray())) < 1e-12 - assert np.max(np.abs(diff_d.to_ndarray())) < 1e-12 + diff = sres.get(block) - ores.get(block) - shift * vec.get(block) + assert np.max(np.abs(diff.to_ndarray())) < 1e-12 # TODO Test block_view, block_apply diff --git a/adcc/tests/guess_test.py b/adcc/tests/guess_test.py index 6463500c0..ea4f1b9e8 100644 --- a/adcc/tests/guess_test.py +++ b/adcc/tests/guess_test.py @@ -34,8 +34,12 @@ # The methods to test -singles_methods = ["adc0", "adc1", "adc2", "adc2x", "adc3"] -doubles_methods = ["adc2", "adc2x", "adc3"] +singles_methods_pp = ["adc0", "adc1", "adc2", "adc2x", "adc3"] +doubles_methods_pp = ["adc2", "adc2x", "adc3"] +singles_methods_ip = ["ip-adc0", "ip-adc1", "ip-adc2", "ip-adc2x", "ip-adc3"] +doubles_methods_ip = ["ip-adc2", "ip-adc2x", "ip-adc3"] +singles_methods_ea = ["ea-adc0", "ea-adc1", "ea-adc2", "ea-adc2x", "ea-adc3"] +doubles_methods_ea = ["ea-adc2", "ea-adc2x", "ea-adc3"] # the testcases h2o_sto3g = testcases.get_by_filename("h2o_sto3g").pop() cn_sto3g = testcases.get_by_filename("cn_sto3g").pop() @@ -43,6 +47,110 @@ class TestGuess: + def test_determine_spin_change_pp(self): + from adcc.guess import determine_spin_change + + method = adcc.AdcMethod("adc2") + + spin = determine_spin_change(method, kind="singlet") + assert spin == 0.0 + + spin = determine_spin_change(method, kind="spin_flip") + assert spin == -1.0 + + + def test_determine_spin_change_ip(self): + from adcc.guess import determine_spin_change + + method = adcc.AdcMethod("ip-adc2") + + spin_alpha = determine_spin_change(method, kind="any", is_alpha=True) + spin_beta = determine_spin_change(method, kind="any", is_alpha=False) + + assert spin_alpha == -0.5 + assert spin_beta == +0.5 + + with pytest.raises(TypeError): + determine_spin_change(method, kind="any", is_alpha=None) + + def test_determine_spin_change_ea(self): + from adcc.guess import determine_spin_change + + method = adcc.AdcMethod("ea-adc2") + + spin_alpha = determine_spin_change(method, kind="any", is_alpha=True) + spin_beta = determine_spin_change(method, kind="any", is_alpha=False) + + assert spin_alpha == +0.5 + assert spin_beta == -0.5 + + with pytest.raises(TypeError): + determine_spin_change(method, kind="any", is_alpha=None) + + def test_determine_spin_change_unknown_adc_type(self): + from adcc.guess import determine_spin_change + + method = adcc.AdcMethod("adc2") + method.adc_type = "bla" + + with pytest.raises(ValueError, match="Unknown ADC method"): + determine_spin_change(method, kind="any") + + def test_estimate_n_guesses_pp(self): + from adcc.guess import estimate_n_guesses + + refstate = testdata_cache.refstate("h2o_sto3g", case="gen") + ground_state = adcc.LazyMp(refstate) + matrix = adcc.AdcMatrix("adc2", ground_state) + + # Check minimal number of guesses is 4 and at some point + # we get more than four guesses + assert 4 == estimate_n_guesses(matrix, n_states=1, singles_only=True) + assert 4 == estimate_n_guesses(matrix, n_states=2, singles_only=True) + for i in range(3, 20): + assert i <= estimate_n_guesses(matrix, n_states=i, singles_only=True) + + def test_estimate_n_guesses_ip(self): + from adcc.guess import estimate_n_guesses + + refstate = testdata_cache.refstate("h2o_sto3g", case="gen") + ground_state = adcc.LazyMp(refstate) + matrix = adcc.AdcMatrix("ip-adc2", ground_state) + + # Check minimal number of guesses is 4 and at some point + # we get more than four guesses + assert 4 == estimate_n_guesses(matrix, n_states=1, singles_only=True) + assert 4 == estimate_n_guesses(matrix, n_states=2, singles_only=True) + for i in range(3, 20): + assert i <= estimate_n_guesses(matrix, n_states=i, singles_only=True) + + # Test different behaviour for IP-ADC(0/1) + matrix = adcc.AdcMatrix("ip-adc0", ground_state) + assert 4 == estimate_n_guesses(matrix, n_states=2, singles_only=True) + assert 5 == estimate_n_guesses(matrix, n_states=5, singles_only=True) + assert 10 == estimate_n_guesses(matrix, n_states=10, singles_only=True) + + def test_estimate_n_guesses_ea(self): + from adcc.guess import estimate_n_guesses + + refstate = testdata_cache.refstate("h2o_sto3g", case="gen") + ground_state = adcc.LazyMp(refstate) + matrix = adcc.AdcMatrix("ea-adc2", ground_state) + + # Check minimal number of guesses is 4 and at some point + # we get more than four guesses + assert 4 == estimate_n_guesses(matrix, n_states=1, singles_only=True) + assert 4 == estimate_n_guesses(matrix, n_states=2, singles_only=True) + for i in range(3, 20): + assert i <= estimate_n_guesses(matrix, n_states=i, + singles_only=True) + + # Test different behaviour for EA-ADC(0/1) + matrix = adcc.AdcMatrix("ea-adc0", ground_state) + assert 2 == estimate_n_guesses(matrix, n_states=1, singles_only=True) + assert 2 == estimate_n_guesses(matrix, n_states=2, singles_only=True) + assert 3 == estimate_n_guesses(matrix, n_states=3, singles_only=True) + def assert_symmetry_no_spin_change(self, matrix, guess, block, spin_block_symmetrisation): """ @@ -183,6 +291,152 @@ def assert_symmetry_spin_flip(self, matrix, guess, block): has_babb = np.max(np.abs(gtd[noa:, :nCa, nva:, nva:])) > 0 assert has_aaab or has_aaba or has_abbb or has_babb + def assert_symmetry_ip(self, matrix, guess, block, is_alpha): + """ + Assert a guess vector has the correct symmetry for alpha/beta detachment + """ + # Extract useful quantities + mospaces = matrix.mospaces + nCa = noa = mospaces.n_orbs_alpha("o1") + nCb = nob = mospaces.n_orbs_beta("o1") + nva = mospaces.n_orbs_alpha("v1") + nvb = mospaces.n_orbs_beta("v1") + if mospaces.has_core_occupied_space: + nCa = mospaces.n_orbs_alpha("o2") + nCb = mospaces.n_orbs_beta("o2") + + # Singles + gts = guess.h.to_ndarray() + assert gts.shape == (nCa + nCb,) + if is_alpha: + assert np.max(np.abs(gts[nCa:])) == 0 + else: + assert np.max(np.abs(gts[:nCa])) == 0 + + # Doubles + if "phh" not in matrix.axis_blocks: + return + + gtd = guess.phh.to_ndarray() + assert gtd.shape == (noa + nob, nCa + nCb, nva + nvb) + + if is_alpha: + assert np.max(np.abs(gtd[:noa, nCa:, :nva])) == 0 # ab->a + assert np.max(np.abs(gtd[noa:, :nCa, :nva])) == 0 # ba->a + assert np.max(np.abs(gtd[noa:, nCa:, nva:])) == 0 # bb->b + assert np.max(np.abs(gtd[noa:, nCa:, :nva])) == 0 # bb->a + else: + assert np.max(np.abs(gtd[:noa, nCa:, nva:])) == 0 # ab->b + assert np.max(np.abs(gtd[noa:, :nCa, nva:])) == 0 # ba->b + assert np.max(np.abs(gtd[:noa, :nCa, :nva])) == 0 # aa->a + assert np.max(np.abs(gtd[:noa, :nCa, nva:])) == 0 # aa->b + + if matrix.reference_state.restricted: + # Restricted automatically means alpha ionization + # Thus forbid spin-flip blocks with right spin + assert np.max(np.abs(gtd[:noa, :nCa, nva:])) == 0 # aa->b + + if not matrix.is_core_valence_separated: + assert_array_equal(gtd.transpose((1, 0, 2)), -gtd) + + if block == "h": + if is_alpha: + assert np.max(np.abs(gtd[:noa, nCa:, nva:])) == 0 # ab->b + assert np.max(np.abs(gtd[noa:, :nCa, nva:])) == 0 # ba->b + assert np.max(np.abs(gtd[:noa, :nCa, :nva])) == 0 # aa->a + + assert np.max(np.abs(gts[:nCa])) > 0 # has_alpha + else: + assert np.max(np.abs(gtd[:noa, nCa:, :nva])) == 0 # ab->a + assert np.max(np.abs(gtd[noa:, :nCa, :nva])) == 0 # ba->a + assert np.max(np.abs(gtd[noa:, nCa:, nva:])) == 0 # bb->b + + assert np.max(np.abs(gts[nCa:])) > 0 # has_beta + elif block == "phh": + if is_alpha: + assert np.max(np.abs(gts[:nCa])) == 0 + has_aaa = np.max(np.abs(gtd[:noa, :nCa, :nva])) > 0 + has_abb = np.max(np.abs(gtd[:noa, nCa:, nva:])) > 0 + has_bab = np.max(np.abs(gtd[noa:, :nCa, nva:])) > 0 + assert has_aaa or has_abb or has_bab + else: + assert np.max(np.abs(gts[nCa:])) == 0 + has_aba = np.max(np.abs(gtd[:noa, nCa:, :nva])) > 0 + has_baa = np.max(np.abs(gtd[noa:, :nCa, :nva])) > 0 + has_bbb = np.max(np.abs(gtd[noa:, nCa:, nva:])) > 0 + assert has_aba or has_baa or has_bbb + + def assert_symmetry_ea(self, matrix, guess, block, is_alpha): + """ + Assert a guess vector has the correct symmetry for alpha/beta attachment + """ + # Extract useful quantities + mospaces = matrix.mospaces + noa = mospaces.n_orbs_alpha("o1") + nob = mospaces.n_orbs_beta("o1") + nva = mospaces.n_orbs_alpha("v1") + nvb = mospaces.n_orbs_beta("v1") + + # Singles + gts = guess.p.to_ndarray() + assert gts.shape == (nva + nvb,) + if is_alpha: + assert np.max(np.abs(gts[nva:])) == 0 + else: + assert np.max(np.abs(gts[:nva])) == 0 + + # Doubles + if "pph" not in matrix.axis_blocks: + return + + gtd = guess.pph.to_ndarray() + assert gtd.shape == (noa + nob, nva + nvb, nva + nvb) + + if is_alpha: + assert np.max(np.abs(gtd[:noa, :nva, nva:])) == 0 # a->ab + assert np.max(np.abs(gtd[:noa, nva:, :nva])) == 0 # a->ba + assert np.max(np.abs(gtd[noa:, nva:, nva:])) == 0 # b->bb + assert np.max(np.abs(gtd[:noa, nva:, nva:])) == 0 # a->bb + else: + assert np.max(np.abs(gtd[noa:, :nva, nva:])) == 0 # b->ab + assert np.max(np.abs(gtd[noa:, nva:, :nva])) == 0 # b->ba + assert np.max(np.abs(gtd[:noa, :nva, :nva])) == 0 # a->aa + assert np.max(np.abs(gtd[noa:, :nva, :nva])) == 0 # b->aa + + if matrix.reference_state.restricted: + # Restricted automatically means alpha attachment + # Thus forbid spin-flip blocks with right spin + assert np.max(np.abs(gtd[:noa, nva:, nva:])) == 0 # a->bb + + assert_array_equal(gtd.transpose((0, 2, 1)), -gtd) + + if block == "p": + if is_alpha: + assert np.max(np.abs(gtd[noa:, :nva, nva:])) == 0 # b->ab + assert np.max(np.abs(gtd[noa:, nva:, :nva])) == 0 # b->ba + assert np.max(np.abs(gtd[:noa, :nva, :nva])) == 0 # a->aa + + assert np.max(np.abs(gts[:nva])) > 0 # has_alpha + else: + assert np.max(np.abs(gtd[:noa, :nva, nva:])) == 0 # a->ab + assert np.max(np.abs(gtd[:noa, nva:, :nva])) == 0 # a->ba + assert np.max(np.abs(gtd[noa:, nva:, nva:])) == 0 # b->bb + + assert np.max(np.abs(gts[nva:])) > 0 # has_beta + elif block == "pph": + if is_alpha: + assert np.max(np.abs(gts[:nva])) == 0 + has_aaa = np.max(np.abs(gtd[:noa, :nva, :nva])) > 0 + has_bab = np.max(np.abs(gtd[noa:, :nva, nva:])) > 0 + has_bba = np.max(np.abs(gtd[noa:, nva:, :nva])) > 0 + assert has_aaa or has_bab or has_bba + else: + assert np.max(np.abs(gts[nva:])) == 0 + has_aab = np.max(np.abs(gtd[:noa, :nva, nva:])) > 0 + has_aba = np.max(np.abs(gtd[:noa, nva:, :nva])) > 0 + has_bbb = np.max(np.abs(gtd[noa:, nva:, nva:])) > 0 + assert has_aab or has_aba or has_bbb + def assert_orthonormal(self, guesses): for (i, gi) in enumerate(guesses): for (j, gj) in enumerate(guesses): @@ -190,10 +444,10 @@ def assert_orthonormal(self, guesses): assert adcc.dot(gi, gj) == approx(ref) def assert_guess_values(self, matrix, block, guesses, spin_flip=False, - triplet=False): + triplet=False, is_alpha: bool = None): """ - Assert that the guesses correspond to the smallest - diagonal values. + Assert that the provided guesses correspond to the smallest + allowed diagonal elements for the requested block. """ # Extract useful quantities mospaces = matrix.mospaces @@ -204,40 +458,42 @@ def assert_guess_values(self, matrix, block, guesses, spin_flip=False, # Make a list of diagonal indices, ordered by the corresponding # diagonal values - sidcs = None - if block == "ph": - diagonal = matrix.diagonal().ph.to_ndarray() + diagonal = matrix.diagonal().get(block).to_ndarray() + + # Doubles guesses are constructed from the 0th order diagonal + if matrix.method.level > 1 and not matrix.method.name.endswith("adc2"): + if block == "pphh": + diagonal = adcc.adc_pp.matrix.diagonal_pphh_pphh_0( + matrix.reference_state + ).pphh.to_ndarray() + elif block == "phh": + diagonal = adcc.adc_ip.matrix.diagonal_phh_phh_0( + matrix.reference_state + ).phh.to_ndarray() + elif block == "pph": + diagonal = adcc.adc_ea.matrix.diagonal_pph_pph_0( + matrix.reference_state + ).pph.to_ndarray() + + # Build list of indices, which would sort the diagonal + order = np.argsort(diagonal.ravel()) + sidcs = list(zip(*np.unravel_index(order, diagonal.shape))) + assert sidcs - # Build list of indices, which would sort the diagonal - sidcs = np.dstack(np.unravel_index(np.argsort(diagonal.ravel()), - diagonal.shape)) - assert sidcs.shape[0] == 1 + if block == "ph": if spin_flip: - sidcs = [idx for idx in sidcs[0] - if idx[0] < nCa and idx[1] >= nva] + sidcs = [idx for idx in sidcs + if idx[0] < nCa and idx[1] >= nva] else: sidcs = [ - idx for idx in sidcs[0] + idx for idx in sidcs if any((idx[0] >= nCa and idx[1] >= nva, idx[0] < nCa and idx[1] < nva)) # noqa: E221 ] elif block == "pphh": - # the doubles guesses are constructed from the 0th order diagonal - if matrix.method.name.endswith("adc2"): - diagonal = matrix.diagonal().pphh.to_ndarray() - else: - diagonal = adcc.adc_pp.matrix.diagonal_pphh_pphh_0( - matrix.reference_state - ).pphh.to_ndarray() - - # Build list of indices, which would sort the diagonal - sidcs = np.dstack(np.unravel_index(np.argsort(diagonal.ravel()), - diagonal.shape)) - - assert sidcs.shape[0] == 1 if spin_flip: sidcs = [ - idx for idx in sidcs[0] + idx for idx in sidcs if any((idx[0] < noa and idx[1] < nCa and idx[2] < nva and idx[3] >= nva, # noqa: E221,E501 idx[0] < noa and idx[1] < nCa and idx[2] >= nva and idx[3] < nva, # noqa: E221,E501 idx[0] < noa and idx[1] >= nCa and idx[2] >= nva and idx[3] >= nva, # noqa: E221,E501 @@ -245,7 +501,7 @@ def assert_guess_values(self, matrix, block, guesses, spin_flip=False, ] else: sidcs = [ - idx for idx in sidcs[0] + idx for idx in sidcs # aaaa / bbbb / abab / baba / abba / baab if any((idx[0] < noa and idx[1] < nCa and idx[2] < nva and idx[3] < nva, # noqa: E221,E501 idx[0] >= noa and idx[1] >= nCa and idx[2] >= nva and idx[3] >= nva, # noqa: E221,E501 @@ -261,17 +517,69 @@ def assert_guess_values(self, matrix, block, guesses, spin_flip=False, # cover the ccvv block in CVS-ADC. if triplet and not mospaces.has_core_occupied_space: sidcs = [idx for idx in sidcs - if abs(idx[0] - idx[1]) != noa - or abs(idx[2] - idx[3]) != nva] + if abs(idx[0] - idx[1]) != noa + or abs(idx[2] - idx[3]) != nva] sidcs = [idx for idx in sidcs if idx[2] != idx[3]] if not matrix.is_core_valence_separated: sidcs = [idx for idx in sidcs if idx[0] != idx[1]] + elif block == "h": + # IP-ADC singles + if is_alpha: + sidcs = [idx for idx in sidcs if idx[0] < noa] + else: + sidcs = [idx for idx in sidcs if idx[0] >= noa] + elif block == "phh": + # IP-ADC doubles + if is_alpha: + sidcs = [ + idx for idx in sidcs + # aaa / abb / bab + if any(( + idx[0] < noa and idx[1] < nCa and idx[2] < nva, + idx[0] < noa and idx[1] >= nCa and idx[2] >= nva, + idx[0] >= noa and idx[1] < nCa and idx[2] >= nva)) + ] + else: + sidcs = [ + idx for idx in sidcs + # aba / baa / bbb + if any(( + idx[0] < noa and idx[1] >= nCa and idx[2] < nva, + idx[0] >= noa and idx[1] < nCa and idx[2] < nva, + idx[0] >= noa and idx[1] >= nCa and idx[2] >= nva)) + ] + sidcs = [idx for idx in sidcs if idx[0] != idx[1]] + elif block == "p": + # EA-ADC singles + if is_alpha: + sidcs = [idx for idx in sidcs if idx[0] < nva] + else: + sidcs = [idx for idx in sidcs if idx[0] >= nva] + elif block == "pph": + # EA-ADC doubles + if is_alpha: + sidcs = [ + idx for idx in sidcs + if any(( + idx[0] < noa and idx[1] < nva and idx[2] < nva, + idx[0] >= noa and idx[1] < nva and idx[2] >= nva, + idx[0] >= noa and idx[1] >= nva and idx[2] < nva)) + ] + else: + sidcs = [ + idx for idx in sidcs + if any(( + idx[0] < noa and idx[1] < nva and idx[2] >= nva, + idx[0] < noa and idx[1] >= nva and idx[2] < nva, + idx[0] >= noa and idx[1] >= nva and idx[2] >= nva)) + ] + sidcs = [idx for idx in sidcs if idx[1] != idx[2]] # Group the indices by corresponding diagonal value def grouping(x): return np.round(diagonal[tuple(x)], decimals=12) gidcs = [[tuple(gitem) for gitem in group] - for _, group in itertools.groupby(sidcs, grouping)] + for key, group in itertools.groupby(sidcs, grouping)] igroup = 0 # The current diagonal value group we are in for (i, guess) in enumerate(guesses): # Extract indices of non-zero elements @@ -328,9 +636,47 @@ def base_test_spin_flip(self, system: str, case: str, method: str, block: str, self.assert_orthonormal(guesses) self.assert_guess_values(matrix, block, guesses, spin_flip=True) - @pytest.mark.parametrize("method", singles_methods) + def base_test_ip(self, system: str, case: str, method: str, block: str, + is_alpha: bool, max_guesses: int = 10): + """ + Test IP-ADC guess construction for alpha/beta detachment + """ + hf = testdata_cache.refstate(system, case=case) + matrix = adcc.AdcMatrix(method, hf) + spin_change = -0.5 if is_alpha else +0.5 + for n_guesses in range(3, max_guesses + 1): + guesses = adcc.guess.guesses_from_diagonal( + matrix, n_guesses, block=block, spin_change=spin_change, + is_alpha=is_alpha + ) + assert len(guesses) == n_guesses + for gs in guesses: + self.assert_symmetry_ip(matrix, gs, block, is_alpha) + self.assert_orthonormal(guesses) + self.assert_guess_values(matrix, block, guesses, is_alpha=is_alpha) + + def base_test_ea(self, system: str, case: str, method: str, block: str, + is_alpha: bool, max_guesses: int = 10): + """ + Test EA-ADC guess construction for alpha/beta attachment + """ + hf = testdata_cache.refstate(system, case=case) + matrix = adcc.AdcMatrix(method, hf) + spin_change = +0.5 if is_alpha else -0.5 + for n_guesses in range(1, max_guesses + 1): + guesses = adcc.guess.guesses_from_diagonal( + matrix, n_guesses, block=block, spin_change=spin_change, + is_alpha=is_alpha + ) + assert len(guesses) == n_guesses + for gs in guesses: + self.assert_symmetry_ea(matrix, gs, block, is_alpha) + self.assert_orthonormal(guesses) + self.assert_guess_values(matrix, block, guesses, is_alpha=is_alpha) + + @pytest.mark.parametrize("method", singles_methods_pp) @pytest.mark.parametrize("case", h2o_sto3g.cases) - def test_singles_h2o(self, method: str, case: str): + def test_singles_h2o_pp(self, method: str, case: str): guesses = { # fewer guesses available "fv-cvs": 1, "cvs": 2, "fc": 8, "fv": 5, "fc-fv": 4, "fc-cvs": 2, "fc-fv-cvs": 1 @@ -340,9 +686,9 @@ def test_singles_h2o(self, method: str, case: str): max_guesses=guesses.get(case, 10) ) - @pytest.mark.parametrize("method", doubles_methods) + @pytest.mark.parametrize("method", doubles_methods_pp) @pytest.mark.parametrize("case", h2o_sto3g.cases) - def test_doubles_h2o(self, method: str, case: str): + def test_doubles_h2o_pp(self, method: str, case: str): guesses = { # fewer ocvv guesses available "fv-cvs": 4, "fc-fv-cvs": 3 } @@ -351,9 +697,41 @@ def test_doubles_h2o(self, method: str, case: str): max_guesses=guesses.get(case, 5) ) - @pytest.mark.parametrize("method", singles_methods) + @pytest.mark.parametrize("method", singles_methods_ip) + @pytest.mark.parametrize("case", h2o_sto3g.filter_cases("ip")) + def test_singles_h2o_ip(self, method: str, case: str): + self.base_test_ip( + "h2o_sto3g", case, method, block="h", is_alpha=True, + max_guesses=3 + ) + + @pytest.mark.parametrize("method", doubles_methods_ip) + @pytest.mark.parametrize("case", h2o_sto3g.filter_cases("ip")) + def test_doubles_h2o_ip(self, method: str, case: str): + self.base_test_ip( + "h2o_sto3g", case, method, block="phh", is_alpha=True, + max_guesses=5 + ) + + @pytest.mark.parametrize("method", singles_methods_ea) + @pytest.mark.parametrize("case", h2o_sto3g.filter_cases("ea")) + def test_singles_h2o_ea(self, method: str, case: str): + self.base_test_ea( + "h2o_sto3g", case, method, block="p", is_alpha=True, + max_guesses=1 + ) + + @pytest.mark.parametrize("method", doubles_methods_ea) + @pytest.mark.parametrize("case", h2o_sto3g.filter_cases("ea")) + def test_doubles_h2o_ea(self, method: str, case: str): + self.base_test_ea( + "h2o_sto3g", case, method, block="pph", is_alpha=True, + max_guesses=4 + ) + + @pytest.mark.parametrize("method", singles_methods_pp) @pytest.mark.parametrize("case", cn_sto3g.cases) - def test_singles_cn(self, method: str, case: str): + def test_singles_cn_pp(self, method: str, case: str): guesses = { # fewer guesses available "cvs": 7, "fc-cvs": 7, "fv-cvs": 5, "fc-fv-cvs": 5 } @@ -362,34 +740,108 @@ def test_singles_cn(self, method: str, case: str): max_guesses=guesses.get(case, 10) ) - @pytest.mark.parametrize("method", doubles_methods) + @pytest.mark.parametrize("method", doubles_methods_pp) @pytest.mark.parametrize("case", cn_sto3g.cases) - def test_doubles_cn(self, method: str, case: str): + def test_doubles_cn_pp(self, method: str, case: str): self.base_test_no_spin_change( system="cn_sto3g", case=case, method=method, block="pphh", max_guesses=5 ) - @pytest.mark.parametrize("method", singles_methods) + @pytest.mark.parametrize("method", singles_methods_ip) + @pytest.mark.parametrize("case", cn_sto3g.filter_cases("ip")) + @pytest.mark.parametrize("is_alpha", [True, False]) + def test_singles_cn_ip(self, method: str, case: str, is_alpha: bool): + self.base_test_ip( + "cn_sto3g", case, method, block="h", is_alpha=is_alpha, + max_guesses=3 + ) + + @pytest.mark.parametrize("method", doubles_methods_ip) + @pytest.mark.parametrize("case", cn_sto3g.filter_cases("ip")) + @pytest.mark.parametrize("is_alpha", [True, False]) + def test_doubles_cn_ip(self, method: str, case: str, is_alpha: bool): + case="gen" + self.base_test_ip( + "cn_sto3g", case, method, block="phh", is_alpha=is_alpha, + max_guesses=5 + ) + + @pytest.mark.parametrize("method", singles_methods_ea) + @pytest.mark.parametrize("case", cn_sto3g.filter_cases("ea")) + @pytest.mark.parametrize("is_alpha", [True, False]) + def test_singles_cn_ea(self, method: str, case: str, is_alpha: bool): + self.base_test_ea( + "cn_sto3g", case, method, block="p", is_alpha=is_alpha, + max_guesses=1 + ) + + @pytest.mark.parametrize("method", doubles_methods_ea) + @pytest.mark.parametrize("case", cn_sto3g.filter_cases("ea")) + @pytest.mark.parametrize("is_alpha", [True, False]) + def test_doubles_cn_ea(self, method: str, case: str, is_alpha: bool): + self.base_test_ea( + "cn_sto3g", case, method, block="pph", is_alpha=is_alpha, + max_guesses=5 + ) + + @pytest.mark.parametrize("method", singles_methods_pp) @pytest.mark.parametrize("case", hf_631g.cases) - def test_singles_hf(self, method: str, case: str): + def test_singles_hf_pp(self, method: str, case: str): self.base_test_spin_flip( system="hf_631g", case=case, method=method, block="ph", max_guesses=10 ) - @pytest.mark.parametrize("method", doubles_methods) + @pytest.mark.parametrize("method", doubles_methods_pp) @pytest.mark.parametrize("case", hf_631g.cases) - def test_doubles_hf(self, method: str, case: str): + def test_doubles_hf_pp(self, method: str, case: str): self.base_test_spin_flip( system="hf_631g", case=case, method=method, block="pphh", max_guesses=5 ) + @pytest.mark.parametrize("method", singles_methods_ip) + @pytest.mark.parametrize("case", hf_631g.filter_cases("ip")) + @pytest.mark.parametrize("is_alpha", [True, False]) + def test_singles_hf_ip(self, method: str, case: str, is_alpha: bool): + self.base_test_ip( + "hf_631g", case, method, block="h", is_alpha=is_alpha, + max_guesses=3 + ) + + @pytest.mark.parametrize("method", doubles_methods_ip) + @pytest.mark.parametrize("case", hf_631g.filter_cases("ip")) + @pytest.mark.parametrize("is_alpha", [True, False]) + def test_doubles_hf_ip(self, method: str, case: str, is_alpha: bool): + self.base_test_ip( + "hf_631g", case, method, block="phh", is_alpha=is_alpha, + max_guesses=5 + ) + + @pytest.mark.parametrize("method", singles_methods_ea) + @pytest.mark.parametrize("case", hf_631g.filter_cases("ea")) + @pytest.mark.parametrize("is_alpha", [True, False]) + def test_singles_hf_ea(self, method: str, case: str, is_alpha: bool): + self.base_test_ea( + "hf_631g", case, method, block="p", is_alpha=is_alpha, + max_guesses=1 + ) + + @pytest.mark.parametrize("method", doubles_methods_ea) + @pytest.mark.parametrize("case", hf_631g.filter_cases("ea")) + @pytest.mark.parametrize("is_alpha", [True, False]) + def test_doubles_hf_ea(self, method: str, case: str, is_alpha: bool): + self.base_test_ea( + "hf_631g", case, method, block="pph", is_alpha=is_alpha, + max_guesses=5 + ) + + # # Tests against reference values # - def base_reference(self, matrix, ref): + def base_reference_pp(self, matrix, ref): symmetrisations = ["none"] if matrix.reference_state.restricted: symmetrisations = ["symmetric", "antisymmetric"] @@ -411,15 +863,234 @@ def base_reference(self, matrix, ref): nonzeros = np.dstack(np.where(guess_b != 0)) assert nonzeros.shape[0] == 1 nonzeros = [tuple(nzitem) for nzitem in nonzeros[0]] - values = guess_b[guess_b != 0] - assert nonzeros == ref_sb[i][0] - assert_array_equal(values, np.array(ref_sb[i][1])) + indices_sorted = tuple(sorted(nonzeros)) + indices_ref_sorted = tuple(sorted(ref_sb[i][0])) + assert indices_sorted == indices_ref_sorted + + def base_reference_degenerate_pp(self, matrix, ref): + """ + Validate PP guesses in presence of orbital degeneracies. + + Ensures that: + - The number of generated guesses matches the reference manifold size. + - Each guess belongs to the correct diagonal energy group. + - Ordering within degenerate subspaces is not enforced. + """ + symmetrisations = ["none"] + if matrix.reference_state.restricted: + symmetrisations = ["symmetric", "antisymmetric"] + + for block in ["ph", "pphh"]: + for symm in symmetrisations: + ref_sb = ref[(block, symm)] + guesses = adcc.guess.guesses_from_diagonal( + matrix, len(ref_sb), block, spin_change=0, + spin_block_symmetrisation=symm + ) + assert len(guesses) == len(ref_sb) + for gs in guesses: + self.assert_symmetry_no_spin_change(matrix, gs, block, symm) + self.assert_orthonormal(guesses) + + # Collect diagonal energies of actual guesses + diag_block = matrix.diagonal()[block].to_ndarray() + actual_energies = [] + for guess in guesses: + arr = guess[block].to_ndarray() + nonzeros = np.dstack(np.where(arr != 0)) + assert nonzeros.shape[0] == 1 + idx = tuple(nonzeros[0][0]) + actual_energies.append(diag_block[idx]) + + # Collect diagonal energies of reference guesses + ref_energies = [] + for ref_entry in ref_sb: + ref_indices = ref_entry[0] + values = [diag_block[idx] for idx in ref_indices] + + # enforce internal degeneracy consistency + np.testing.assert_allclose( + values, [values[0]] * len(values), + rtol=1e-12, atol=1e-14 + ) + + ref_energies.append(values[0]) + + # Compare as multisets + np.testing.assert_allclose( + sorted(actual_energies), + sorted(ref_energies), + rtol=1e-12, + atol=1e-14 + ) + + def base_reference_ip(self, matrix, ref, is_alpha=True): + spin_change = -0.5 if is_alpha else +0.5 + for block in ["h", "phh"]: + ref_sb = ref[(block, is_alpha)] + guesses = adcc.guess.guesses_from_diagonal( + matrix, len(ref_sb), block=block, spin_change=spin_change + ) + assert len(guesses) == len(ref_sb) + + for gs in guesses: + self.assert_symmetry_ip(matrix, gs, block, is_alpha) + self.assert_orthonormal(guesses) + + for (i, guess) in enumerate(guesses): + guess_b = guess[block].to_ndarray() + nonzeros = np.dstack(np.where(guess_b != 0)) + assert nonzeros.shape[0] == 1 + nonzeros = [tuple(nzitem) for nzitem in nonzeros[0]] + values = guess_b[guess_b != 0] + assert nonzeros == ref_sb[i][0] + assert_array_equal(values, np.array(ref_sb[i][1])) + + def base_reference_degenerate_ip(self, matrix, ref, is_alpha=True): + """ + Validate IP guesses in presence of orbital degeneracies. + + Ensures that: + - The number of generated guesses matches the reference manifold size. + - Each guess belongs to the correct diagonal energy group. + - Ordering within degenerate subspaces is not enforced. + """ + spin_change = -0.5 if is_alpha else +0.5 + for block in ["h", "phh"]: + ref_sb = ref[(block, is_alpha)] + guesses = adcc.guess.guesses_from_diagonal( + matrix, len(ref_sb), block=block, spin_change=spin_change + ) + assert len(guesses) == len(ref_sb) + + for gs in guesses: + self.assert_symmetry_ip(matrix, gs, block, is_alpha) + self.assert_orthonormal(guesses) + + # Collect diagonal energies of actual guesses + diag_block = matrix.diagonal()[block].to_ndarray() + actual_energies = [] + for guess in guesses: + arr = guess[block].to_ndarray() + nonzeros = np.dstack(np.where(arr != 0)) + assert nonzeros.shape[0] == 1 + idx = tuple(nonzeros[0][0]) + actual_energies.append(diag_block[idx]) + + # Collect diagonal energies of reference guesses + ref_energies = [] + for ref_entry in ref_sb: + ref_indices = ref_entry[0] + values = [diag_block[idx] for idx in ref_indices] + + # enforce internal degeneracy consistency + np.testing.assert_allclose( + values, [values[0]] * len(values), + rtol=1e-12, atol=1e-14 + ) + + ref_energies.append(values[0]) - @pytest.mark.parametrize("method", doubles_methods) - def test_reference_h2o(self, method: str): + # Compare as multisets + np.testing.assert_allclose( + sorted(actual_energies), + sorted(ref_energies), + rtol=1e-12, + atol=1e-14 + ) + + def base_reference_ea(self, matrix, ref, is_alpha=True): + spin_change = +0.5 if is_alpha else -0.5 + for block in ["p", "pph"]: + ref_sb = ref[(block, is_alpha)] + guesses = adcc.guess.guesses_from_diagonal( + matrix, len(ref_sb), block=block, spin_change=spin_change + ) + assert len(guesses) == len(ref_sb) + + for gs in guesses: + self.assert_symmetry_ea(matrix, gs, block, is_alpha) + self.assert_orthonormal(guesses) + + for (i, guess) in enumerate(guesses): + guess_b = guess[block].to_ndarray() + nonzeros = np.dstack(np.where(guess_b != 0)) + assert nonzeros.shape[0] == 1 + nonzeros = [tuple(nzitem) for nzitem in nonzeros[0]] + values = guess_b[guess_b != 0] + assert nonzeros == ref_sb[i][0] + assert_array_equal(values, np.array(ref_sb[i][1])) + + def base_reference_degenerate_ea(self, matrix, ref, is_alpha=True): + """ + Validate EA guesses in presence of orbital degeneracies. + + Ensures that: + - The number of generated guesses matches the reference manifold size. + - Each guess belongs to the correct diagonal energy group. + - Ordering within degenerate subspaces is not enforced. + """ + spin_change = +0.5 if is_alpha else -0.5 + for block in ["p", "pph"]: + ref_sb = ref[(block, is_alpha)] + guesses = adcc.guess.guesses_from_diagonal( + matrix, len(ref_sb), block=block, spin_change=spin_change + ) + assert len(guesses) == len(ref_sb) + + for gs in guesses: + self.assert_symmetry_ea(matrix, gs, block, is_alpha) + self.assert_orthonormal(guesses) + + # Collect diagonal energies of actual guesses + diag_block = matrix.diagonal()[block].to_ndarray() + actual_energies = [] + for guess in guesses: + arr = guess[block].to_ndarray() + nonzeros = np.dstack(np.where(arr != 0)) + assert nonzeros.shape[0] == 1 + idx = tuple(nonzeros[0][0]) + actual_energies.append(diag_block[idx]) + + # Collect diagonal energies of reference guesses + ref_energies = [] + for ref_entry in ref_sb: + ref_indices = ref_entry[0] + values = [diag_block[idx] for idx in ref_indices] + + # enforce internal degeneracy consistency + np.testing.assert_allclose( + values, [values[0]] * len(values), + rtol=1e-12, atol=1e-14 + ) + + ref_energies.append(values[0]) + + # Compare as multisets + np.testing.assert_allclose( + sorted(actual_energies), + sorted(ref_energies), + rtol=1e-12, + atol=1e-14 + ) + + @pytest.mark.parametrize("method", doubles_methods_pp) + def test_reference_h2o_pp(self, method: str): hf = testdata_cache.refstate("h2o_sto3g", "gen") matrix = adcc.AdcMatrix(method=method, hf_or_mp=hf) - self.base_reference(matrix=matrix, ref=self.get_ref_h2o()) + self.base_reference_pp(matrix=matrix, ref=self.get_ref_h2o_pp()) + + @pytest.mark.parametrize("method", doubles_methods_ip) + def test_reference_h2o_ip(self, method: str): + hf = testdata_cache.refstate("h2o_sto3g", "gen") + matrix = adcc.AdcMatrix(method=method, hf_or_mp=hf) + self.base_reference_ip(matrix=matrix, ref=self.get_ref_h2o_ip()) + + @pytest.mark.parametrize("method", doubles_methods_ea) + def test_reference_h2o_ea(self, method: str): + hf = testdata_cache.refstate("h2o_sto3g", "gen") + matrix = adcc.AdcMatrix(method=method, hf_or_mp=hf) + self.base_reference_ea(matrix=matrix, ref=self.get_ref_h2o_ea()) # NOTE: This test is a bit weird: the order of the guesses is # ill defined, because some orbitals are degenerate for cn sto3g: @@ -431,13 +1102,44 @@ def test_reference_h2o(self, method: str): # against hard coded reference data. The test against numpy above should be # sufficient. - # @pytest.mark.parametrize("method", doubles_methods) - # def test_reference_cn(self, method: str): - # hf = testdata_cache.refstate("cn_sto3g", case="gen") - # matrix = adcc.AdcMatrix(method=method, hf_or_mp=hf) - # self.base_reference(matrix=matrix, ref=self.get_ref_cn()) + # Current workaround: Compare guess energies rather than exact ordering for + # these cases by calling 'base_reference_degenerate_{adc_type}()' + + @pytest.mark.parametrize("method", doubles_methods_pp) + def test_reference_cn_pp(self, method: str): + hf = testdata_cache.refstate("cn_sto3g", case="gen") + matrix = adcc.AdcMatrix(method=method, hf_or_mp=hf) + if not method.endswith("adc2"): + # NOTE: doubles guesses for higher ADC levels are constructed + # from the ADC(2) zeroth-order diagonal. + # We enforce this here explicitly to avoid method-dependent + # degeneracy reordering. + matrix._diagonal = adcc.AdcMatrix(method="adc2", hf_or_mp=hf).diagonal() + self.base_reference_degenerate_pp(matrix=matrix, ref=self.get_ref_cn_pp()) + + @pytest.mark.parametrize("method", doubles_methods_ip) + @pytest.mark.parametrize("is_alpha", [True, False]) + def test_reference_cn_ip(self, method: str, is_alpha: bool): + hf = testdata_cache.refstate("cn_sto3g", case="gen") + matrix = adcc.AdcMatrix(method=method, hf_or_mp=hf) + if not method.endswith("adc2"): + matrix._diagonal = adcc.AdcMatrix(method="ip-adc2", hf_or_mp=hf).diagonal() + self.base_reference_degenerate_ip( + matrix=matrix, ref=self.get_ref_cn_ip(), is_alpha=is_alpha + ) - def get_ref_h2o(self): + @pytest.mark.parametrize("method", doubles_methods_ea) + @pytest.mark.parametrize("is_alpha", [True, False]) + def test_reference_cn_ea(self, method: str, is_alpha: bool): + hf = testdata_cache.refstate("cn_sto3g", case="gen") + matrix = adcc.AdcMatrix(method=method, hf_or_mp=hf) + if not method.endswith("adc2"): + matrix._diagonal = adcc.AdcMatrix(method="ea-adc2", hf_or_mp=hf).diagonal() + self.base_reference_degenerate_ea( + matrix=matrix, ref=self.get_ref_cn_ea(), is_alpha=is_alpha + ) + + def get_ref_h2o_pp(self): sq8 = 1 / np.sqrt(8) sq12 = 1 / np.sqrt(12) sq48 = 1 / np.sqrt(48) @@ -523,7 +1225,7 @@ def get_ref_h2o(self): ], } - def get_ref_cn(self): + def get_ref_cn_pp(self): sq8 = 1 / np.sqrt(8) return { ("ph", "none"): [ @@ -553,3 +1255,113 @@ def get_ref_cn(self): [0.5, -0.5, -0.5, 0.5]), ], } + + def get_ref_h2o_ip(self): + sq6 = 1 / np.sqrt(6) + asymm = [1 / np.sqrt(2), -1 / np.sqrt(2)] + return { + ("h", True): [ + ([(4, )], [1]), + ([(3, )], [1]), + ([(2, )], [1]), + ([(1, )], [1]), + ([(0, )], [1]) + ], + ("phh", True): [ + ([(4, 9, 2), (9, 4, 2)], asymm), + ([(3, 4, 0), (3, 9, 2), (4, 3, 0), + (4, 8, 2), (8, 4, 2), (9, 3, 2)], + [-sq6, -sq6, sq6, -sq6, sq6, sq6]), + ([(3, 8, 2), (8, 3, 2)], asymm), + ([(4, 9, 3), (9, 4, 3)], asymm), + ([(3, 4, 1), (3, 9, 3), (4, 3, 1), + (4, 8, 3), (8, 4, 3), (9, 3, 3)], + [-sq6, -sq6, sq6, -sq6, sq6, sq6]) + ], + } + + def get_ref_h2o_ea(self): + sq6 = 1 / np.sqrt(6) + asymm = [1 / np.sqrt(2), -1 / np.sqrt(2)] + return { + ("p", True): [ + ([(0, )], [1]), + ([(1, )], [1]) + ], + ("pph", True): [ + ([(9, 0, 2), (9, 2, 0)], asymm), + ([(8, 0, 2), (8, 2, 0)], asymm), + ([(4, 0, 1), (4, 1, 0), (9, 0, 3), + (9, 1, 2), (9, 2, 1), (9, 3, 0)], + [-sq6, sq6, -sq6, -sq6, sq6, sq6]), + ([(3, 0, 1), (3, 1, 0), (8, 0, 3), + (8, 1, 2), (8, 2, 1), (8, 3, 0)], + [-sq6, sq6, -sq6, -sq6, sq6, sq6]), + ([(7, 0, 2), (7, 2, 0)], asymm) + ], + } + + def get_ref_cn_ip(self): + asymm = [1 / np.sqrt(2), -1 / np.sqrt(2)] + asymm1 = [-1 / np.sqrt(2), 1 / np.sqrt(2)] + return { + ("h", True): [ + ([(6, )], [1]), + ([(4, )], [1]), # occ. 4 and 5 are degenerate + ([(5, )], [1]), # occ. 4 and 5 are degenerate + ([(3, )], [1]), + ([(2, )], [1]) + ], + ("h", False): [ + ([(11, )], [1]), + ([(12, )], [1]), + ([(10, )], [1]), + ([(9, )], [1]), + ([(8, )], [1]) + ], + ("phh", True): [ + ([(6, 11, 3), (11, 6, 3)], asymm1), + ([(6, 12, 3), (12, 6, 3)], asymm1), + ([(4, 11, 3), (11, 4, 3)], asymm), + ([(4, 12, 3), (12, 4, 3)], asymm), + ([(5, 11, 3), (11, 5, 3)], asymm1) + ], + ("phh", False): [ + ([(11, 12, 3), (12, 11, 3)], asymm), + ([(10, 11, 3), (11, 10, 3)], asymm), + ([(10, 12, 3), (12, 10, 3)], asymm), + ([(6, 11, 0), (11, 6, 0)], asymm1), + ([(6, 12, 0), (12, 6, 0)], asymm1) + ], + } + + def get_ref_cn_ea(self): + asymm = [1 / np.sqrt(2), -1 / np.sqrt(2)] + asymm1 = [-1 / np.sqrt(2), 1 / np.sqrt(2)] + return { + ("p", True): [ + ([(0, )], [1]), + ([(1, )], [1]), + ([(2, )], [1]) + ], + ("p", False): [ + ([(3, )], [1]), + ([(4, )], [1]), + ([(5, )], [1]), + ([(6, )], [1]) + ], + ("pph", True): [ + ([(11, 0, 3), (11, 3, 0)], asymm), + ([(12, 0, 3), (12, 3, 0)], asymm), + ([(11, 1, 3), (11, 3, 1)], asymm1), + ([(12, 1, 3), (12, 3, 1)], asymm1), + ([(10, 0, 3), (10, 3, 0)], asymm) + ], + ("pph", False): [ + ([(6, 0, 3), (6, 3, 0)], asymm), + ([(6, 1, 3), (6, 3, 1)], asymm1), + ([(4, 0, 3), (4, 3, 0)], asymm), # occ. 4 and 5 are degenerate + ([(5, 0, 3), (5, 3, 0)], asymm), # occ. 4 and 5 are degenerate + ([(4, 1, 3), (4, 3, 1)], asymm1) + ], + } \ No newline at end of file diff --git a/adcc/tests/testcases.py b/adcc/tests/testcases.py index 4daeb8b1f..e834f2ad6 100644 --- a/adcc/tests/testcases.py +++ b/adcc/tests/testcases.py @@ -92,6 +92,8 @@ def filter_cases(self, adc_type: str) -> tuple[str, ...]: # since cvs is not (yet) implemented for IP. if adc_type == "pp": return self.cases + elif adc_type in ("ip", "ea"): + return tuple(case for case in self.cases if "cvs" not in case) raise NotImplementedError(f"Filtering for adc type {adc_type} not " "implemented.") @@ -110,14 +112,26 @@ def validate(self): continue assert component in requirements assert getattr(self, requirements[component], None) is not None - # validate the PP-ADC kinds + # validate the IP/EA/PP-ADC kinds assert len(fields(self.kinds)) == 3 - assert not self.kinds.ip - assert not self.kinds.ea - if self.restricted: - assert all(kind in ["singlet", "triplet"] for kind in self.kinds.pp) - else: - assert all(kind in ["any", "spin_flip"] for kind in self.kinds.pp) + + if self.kinds.pp: + if self.restricted: + assert all(kind in ["singlet", "triplet"] + for kind in self.kinds.pp) + else: + assert all(kind in ["any", "spin_flip"] + for kind in self.kinds.pp) + if self.kinds.ip: + if self.restricted: + assert all(kind in ["doublet"] for kind in self.kinds.ip) + else: + assert all(kind in ["any"] for kind in self.kinds.ip) + if self.kinds.ea: + if self.restricted: + assert all(kind in ["doublet"] for kind in self.kinds.ea) + else: + assert all(kind in ["any"] for kind in self.kinds.ea) def kinds_to_nstates(kinds: tuple[str, ...]) -> list[str]: @@ -125,9 +139,9 @@ def kinds_to_nstates(kinds: tuple[str, ...]) -> list[str]: Transforms the given kinds to a list of keywords to request states of the corresponding kind in an adc calculation. """ - # singlet, triplet -> n_singlets, n_triplets - # any -> n_states - # spin_flip -> n_spin_flip + # singlet, doublet, triplet -> n_singlets, n_triplets + # any -> n_states + # spin_flip -> n_spin_flip ret = [] for kind in kinds: if kind == "any": @@ -197,8 +211,10 @@ def kinds_to_nstates(kinds: tuple[str, ...]) -> list[str]: def _init_test_cases() -> tuple[TestCase, ...]: test_cases: list[TestCase] = [] # some shared data - restricted_kinds = Kinds(pp=("singlet", "triplet")) - unrestricted_kinds = Kinds(pp=("any",)) + restricted_kinds = Kinds(pp=("singlet", "triplet"), + ip=("doublet",), + ea=("doublet",)) + unrestricted_kinds = Kinds(pp=("any",), ip=("any",), ea=("any",)) spin_flip_kinds = Kinds(pp=("spin_flip",)) # CH2NH2 ref_cases = ("gen", "cvs") diff --git a/adcc/tests/testdata_cache.py b/adcc/tests/testdata_cache.py index a1effde56..a4679b2a4 100644 --- a/adcc/tests/testdata_cache.py +++ b/adcc/tests/testdata_cache.py @@ -1,12 +1,15 @@ from . import testcases from adcc.AdcMatrix import AdcMatrix +from adcc.AdcMethod import AdcMethod from adcc.ExcitedStates import ExcitedStates +from adcc.ChargedExcitations import AttachedStates, DetachedStates from adcc.LazyMp import LazyMp from adcc.misc import cached_member_function from adcc.ReferenceState import ReferenceState from adcc.solver import EigenSolverStateBase -from adcc import hdf5io, guess_zero +from adcc import hdf5io +from adcc.guess import guess_zero, determine_spin_change from pathlib import Path from typing import Optional, Union @@ -118,11 +121,12 @@ def hfimport(self, system: Union[str, testcases.TestCase], @cached_member_function() def _load_data(self, system: Union[str, testcases.TestCase], method: str, case: str, source: str, - gs_density_order: Optional[int] = None) -> dict: + gs_density_order: Optional[int] = None, + is_alpha: Optional[bool] = None) -> dict: """ Load the reference data for the given system, method (mpn / adcn), reference case (cvs, fc, fv-cvs, ...) and optionally gs_density_order - (2, 3, sigma4+, ...). + (2, 3, sigma4+, ...) and if it is an alpha process for IP/EA. Source defines the source which generated the reference data, i.e., either adcman or adcc. """ @@ -132,7 +136,7 @@ def _load_data(self, system: Union[str, testcases.TestCase], system = testcases.get_by_filename(system).pop() return self._load_data( system, method=method, case=case, source=source, - gs_density_order=gs_density_order + gs_density_order=gs_density_order, is_alpha=is_alpha ) assert isinstance(system, testcases.TestCase) assert case in system.cases @@ -146,54 +150,73 @@ def _load_data(self, system: Union[str, testcases.TestCase], else: # adc data is one level deeper than mpdata: gs_density_order datafile = datadir / system.adcdata_file_name(source, method) key = f"{case}/{gs_density_order}" + if AdcMethod(method).adc_type in ("ip", "ea"): + assert isinstance(is_alpha, bool) + spin = "alpha" if is_alpha else "beta" + key = f"{case}/{gs_density_order}/{spin}" if not datafile.exists(): raise FileNotFoundError(f"Missing reference data file {datafile}.") with h5py.File(datafile, "r") as hdf5_file: if key not in hdf5_file: - raise ValueError( - f"No data available for case {case} and gs_density_order " - f"{gs_density_order} in file {datafile}." - ) + if is_alpha is None: + raise ValueError( + f"No data available for case {case} and " + f"gs_density_order {gs_density_order} in file {datafile}." + ) + else: + raise ValueError( + f"No data available for case {case}, gs_density_order " + f"{gs_density_order} and spin {spin} in file {datafile}." + ) data = hdf5io.extract_group(hdf5_file[key]) return data def adcc_data(self, system: str, method: str, case: str, - gs_density_order: Optional[int] = None) -> dict: + gs_density_order: Optional[int] = None, + is_alpha: Optional[bool] = None) -> dict: """ Load the adcc reference data for the given system, method (mpn / adcn), reference case (cvs, fc, fv-cvs, ...) and optionally gs_density_order - (2, 3, sigma4+, ...). + (2, 3, sigma4+, ...) and optionally is_alpha for IP/EA data. """ + if ("ip" in method or "ea" in method) and is_alpha is None: + is_alpha = True return self._load_data( system=system, method=method, case=case, - gs_density_order=gs_density_order, source="adcc" + gs_density_order=gs_density_order, source="adcc", is_alpha=is_alpha ) def adcman_data(self, system: str, method: str, case: str, - gs_density_order: Optional[int] = None) -> dict: + gs_density_order: Optional[int] = None, + is_alpha: Optional[bool] = None) -> dict: """ Load the adcman reference data for the given system, method (mpn / adcn), reference case (cvs, fc, fv-cvs, ...) and optionally gs_density_order - (2, 3, sigma4+, ...). + (2, 3, sigma4+, ...) and optionally is_alpha for IP/EA data. """ + if ("ip" in method or "ea" in method) and is_alpha is None: + is_alpha = True return self._load_data( system=system, method=method, case=case, - gs_density_order=gs_density_order, source="adcman" + gs_density_order=gs_density_order, source="adcman", + is_alpha=is_alpha ) @cached_member_function() - def _make_mock_adc_state(self, system: Union[str, testcases.TestCase], - method: str, case: str, - kind: str, source: str, - gs_density_order: Optional[int] = None - ) -> ExcitedStates: + def _make_mock_adc_state( + self, system: Union[str, testcases.TestCase], + method: str, case: str, + kind: str, source: str, + gs_density_order: Optional[int] = None, + is_alpha: Optional[bool] = None + ) -> ExcitedStates | AttachedStates | DetachedStates: """ - Create an ExcitedStates instance for the given test case, method (adcn), - reference case (gen/cvs/fc/...), state kind (singlet/triplet/any/...) - and optionally gs_density_order (2/3/sigma4+). - Source refers to the source with which the data were generated - (adcman/adcc). - The excited states object is build on top of the loaded HF data and + Create an ExcitedStates/AttachedStates/DetachedStates instance for the + given test case, method (adcn), reference case (gen/cvs/fc/...), + state kind (singlet/triplet/any/...) and optionally gs_density_order + (2/3/sigma4+) and optionally is_alpha for IP/EA. Source refers to the + source with which the data were generated (adcman/adcc). + The states object is build on top of the loaded HF data and contains the eigenstates and eigenvalues of the loaded ADC data. """ if isinstance(system, str): @@ -203,7 +226,7 @@ def _make_mock_adc_state(self, system: Union[str, testcases.TestCase], system = testcases.get_by_filename(system).pop() return self._make_mock_adc_state( system, method=method, case=case, kind=kind, source=source, - gs_density_order=gs_density_order + gs_density_order=gs_density_order, is_alpha=is_alpha ) assert isinstance(system, testcases.TestCase) assert case in system.cases @@ -211,7 +234,7 @@ def _make_mock_adc_state(self, system: Union[str, testcases.TestCase], # load the adc data data = self._load_data( system, method=method, case=case, source=source, - gs_density_order=gs_density_order + gs_density_order=gs_density_order, is_alpha=is_alpha ) adc_data = data.get(kind, None) if adc_data is None: @@ -239,12 +262,15 @@ def _make_mock_adc_state(self, system: Union[str, testcases.TestCase], elif refstate.restricted and kind == "triplet": symm = "antisymmetric" spin_change = 0 + elif refstate.restricted and kind == "doublet": + symm = "none" elif kind in ["spin_flip", "any"]: symm = "none" - spin_change = 0 if kind == "any" else -1 else: raise ValueError(f"Unknown kind: {kind}") + spin_change = determine_spin_change(matrix.method, kind, is_alpha) + n_states = len(adc_data["eigenvalues"]) states.eigenvectors = [guess_zero(matrix, spin_change=spin_change, spin_block_symmetrisation=symm) @@ -257,34 +283,49 @@ def _make_mock_adc_state(self, system: Union[str, testcases.TestCase], evec[blocks[1]].set_from_ndarray( adc_data["eigenvectors_doubles"][i], 1e-14 ) - return ExcitedStates(states) + + if matrix.method.adc_type == "pp": + return ExcitedStates(states) + elif matrix.method.adc_type == "ip": + return DetachedStates(states, is_alpha) + elif matrix.method.adc_type == "ea": + return AttachedStates(states, is_alpha) + else: + raise ValueError(f"Unknown ADC method: {method.name}") def adcc_states(self, system: str, method: str, kind: str, - case: str, gs_density_order: Optional[int] = None - ) -> ExcitedStates: + case: str, gs_density_order: Optional[int] = None, + is_alpha: Optional[bool] = None + ) -> ExcitedStates | AttachedStates | DetachedStates: """ - Create an ExcitedStates instance for the given test case, method (adcn), - reference case (gen/cvs/fc/...), state kind (singlet/triplet/any/...) - and optionally gs_density_order (2/3/sigma4+) using the adcc eigenstates - and eigenvalues. + Create an ExcitedStates/AttachedStates/DetachedStates instance for the + given test case, method (adcn), reference case (gen/cvs/fc/...), + state kind (singlet/triplet/any/...) and optionally gs_density_order + (2/3/sigma4+) using the adcc eigenstates and eigenvalues. """ + if ("ip" in method or "ea" in method) and is_alpha is None: + is_alpha = True return self._make_mock_adc_state( system, method=method, case=case, kind=kind, - gs_density_order=gs_density_order, source="adcc" + gs_density_order=gs_density_order, source="adcc", is_alpha=is_alpha ) def adcman_states(self, system: str, method: str, kind: str, - case: str, gs_density_order: Optional[int] = None - ) -> ExcitedStates: + case: str, gs_density_order: Optional[int] = None, + is_alpha: Optional[bool] = None + ) -> ExcitedStates | AttachedStates | DetachedStates: """ - Create an ExcitedStates instance for the given test case, method (adcn), - reference case (gen/cvs/fc/...), state kind (singlet/triplet/any/...) - and optionally gs_density_order (2/3/sigma4+) using the adcman eigenstates - and eigenvalues. + Create an ExcitedStates/AttachedStates/DetachedStates instance for the + given test case, method (adcn), reference case (gen/cvs/fc/...), + state kind (singlet/triplet/any/...) and optionally gs_density_order + (2/3/sigma4+) using the adcman eigenstates and eigenvalues. """ + if ("ip" in method or "ea" in method) and is_alpha is None: + is_alpha = True return self._make_mock_adc_state( system, method=method, case=case, kind=kind, - gs_density_order=gs_density_order, source="adcman" + gs_density_order=gs_density_order, source="adcman", + is_alpha=is_alpha ) @@ -299,7 +340,7 @@ def read_json_data(name: str) -> dict: return json.load(open(jsonfile, "r"), object_hook=_import_hook) -def _import_hook(data: dict): +def _import_hook(data: dict) -> dict: return {key: np.array(val) if isinstance(val, list) else val for key, val in data.items()} diff --git a/adcc/tests/workflow_test.py b/adcc/tests/workflow_test.py index 6dbc8e6d2..169a5abef 100644 --- a/adcc/tests/workflow_test.py +++ b/adcc/tests/workflow_test.py @@ -29,18 +29,26 @@ class TestWorkflow: - def test_validate_state_parameters_rhf(self): + def test_validate_state_parameters_rhf_pp(self): from adcc.workflow import validate_state_parameters - - refstate = testdata_cache.refstate("h2o_sto3g", case="gen") - - assert 3, "any" == validate_state_parameters(refstate, n_states=3) - assert 4, "singlet" == validate_state_parameters(refstate, n_states=4, - kind="singlet") - assert 2, "triplet" == validate_state_parameters(refstate, n_states=2, - kind="triplet") - assert 2, "triplet" == validate_state_parameters(refstate, n_triplets=2) - assert 6, "singlet" == validate_state_parameters(refstate, n_singlets=6) + from adcc.AdcMatrix import AdcMatrixlike + from adcc.AdcMethod import AdcMethod + + # Build empty AdcMatrixlike object and assign ref_state and method + matrix = AdcMatrixlike() + matrix.reference_state = testdata_cache.refstate("h2o_sto3g", case="gen") + matrix.method = AdcMethod("adc2") + + assert (3, "any", None) == validate_state_parameters( + matrix, n_states=3) + assert (4, "singlet", None) == validate_state_parameters( + matrix, n_states=4, kind="singlet") + assert (2, "triplet", None) == validate_state_parameters( + matrix, n_states=2, kind="triplet") + assert (2, "triplet", None) == validate_state_parameters( + matrix, n_triplets=2) + assert (6, "singlet", None) == validate_state_parameters( + matrix, n_singlets=6) invalid_cases = [ dict(), # No states requested @@ -52,21 +60,30 @@ def test_validate_state_parameters_rhf(self): dict(n_states=2, n_spin_flip=2), # States of two sorts dict(n_triplets=2, kind="singlet"), # kind and n_ do not agree dict(n_states=2, kind="bla"), # Kind invaled + dict(n_states=2, kind="doublet"), # Kind invalid for PP-ADC + dict(n_states=2, is_alpha=True), # Parameter only for IP/EA-ADC + dict(n_states=2, is_alpha=False), # Parameter only for IP/EA-ADC ] for case in invalid_cases: with pytest.raises(InputError): - validate_state_parameters(refstate, **case) + validate_state_parameters(matrix, **case) - def test_validate_state_parameters_uhf(self): + def test_validate_state_parameters_uhf_pp(self): from adcc.workflow import validate_state_parameters + from adcc.AdcMatrix import AdcMatrixlike + from adcc.AdcMethod import AdcMethod - refstate = testdata_cache.refstate("cn_sto3g", case="gen") + # Build empty AdcMatrixlike object and assign ref_state and method + matrix = AdcMatrixlike() + matrix.reference_state = testdata_cache.refstate("cn_sto3g", case="gen") + matrix.method = AdcMethod("adc2") - assert 3, "any" == validate_state_parameters(refstate, n_states=3, - kind="any") - assert 3, "any" == validate_state_parameters(refstate, n_states=3) - assert 2, "spin_flip" == validate_state_parameters(refstate, - n_spin_flip=2) + assert (3, "any", None) == validate_state_parameters( + matrix, n_states=3, kind="any") + assert (3, "any", None) == validate_state_parameters( + matrix, n_states=3) + assert (2, "spin_flip", None) == validate_state_parameters( + matrix, n_spin_flip=2) invalid_cases = [ dict(), # No states requested @@ -77,15 +94,170 @@ def test_validate_state_parameters_uhf(self): dict(n_triplets=2, n_singlets=2), # States of two sorts dict(n_states=2, n_spin_flip=2), # States of two sorts dict(n_spin_flip=2, kind="singlet"), # kind and n_ do not agree - dict(n_states=2, kind="bla"), # Kind invaled + dict(n_states=2, kind="bla"), # Kind invalid dict(n_states=4, kind="singlet"), # UHF with singlets dict(n_states=2, kind="triplet"), # UHF with triplets dict(n_triplets=2), # UHF with triplets dict(n_singlets=6), # UHF with singlets + dict(n_doublets=3), # UHF with doublets (only restricted IP/EA) + dict(n_states=2, is_alpha=True), # Parameter only for IP/EA-ADC + dict(n_states=2, is_alpha=False), # Parameter only for IP/EA-ADC + ] + for case in invalid_cases: + with pytest.raises(InputError): + validate_state_parameters(matrix, **case) + + def test_validate_state_parameters_rhf_ip(self): + from adcc.workflow import validate_state_parameters + from adcc.AdcMatrix import AdcMatrixlike + from adcc.AdcMethod import AdcMethod + + # Build empty AdcMatrixlike object and assign ref_state and method + matrix = AdcMatrixlike() + matrix.reference_state = testdata_cache.refstate("h2o_sto3g", case="gen") + matrix.method = AdcMethod("ip-adc2") + + assert (3, "any", True) == (validate_state_parameters( + matrix, n_states=3)) + assert (3, "any", True) == (validate_state_parameters( + matrix, n_states=3, is_alpha=False)) + assert (3, "any", True) == (validate_state_parameters( + matrix, n_states=3, is_alpha=True)) # restricted always beta + assert (2, "doublet", True) == (validate_state_parameters( + matrix, n_states=2, kind="doublet")) + assert (2, "doublet", True) == (validate_state_parameters( + matrix, n_doublets=2)) + assert (6, "doublet", True) == (validate_state_parameters( + matrix, n_doublets=6, is_alpha=True)) + + invalid_cases = [ + dict(), # No states requested + dict(n_states=0), # No states requested + dict(n_doublets=-2), # Negative number of states requested + dict(n_states=2, kind="bla"), # Kind invalid + dict(n_singlets=2), # Kind invalid for IP/EA-ADC + dict(n_triplets=2), # Kind invalid for IP/EA-ADC + dict(n_spin_flip=2), # Kind invalid for IP/EA-ADC + dict(n_states=2, is_alpha="yes"), # is_alpha not boolean + dict(n_states=2, is_alpha=1), # is_alpha not boolean + dict(n_states=2, n_spin_flip=2), # States of two sorts + dict(n_doublets=2, kind="singlet"), # kind and n_ do not agree + ] + + for case in invalid_cases: + with pytest.raises(InputError): + validate_state_parameters(matrix, **case) + + def test_validate_state_parameters_uhf_ip(self): + from adcc.workflow import validate_state_parameters + from adcc.AdcMatrix import AdcMatrixlike + from adcc.AdcMethod import AdcMethod + + # Build empty AdcMatrixlike object and assign ref_state and method + matrix = AdcMatrixlike() + matrix.reference_state = testdata_cache.refstate("cn_sto3g", case="gen") + matrix.method = AdcMethod("ip-adc2") + + assert (3, "any", True) == validate_state_parameters( + matrix, n_states=3, kind="any") + assert (3, "any", False) == validate_state_parameters( + matrix, n_states=3, is_alpha=False) + assert (3, "any", True) == validate_state_parameters( + matrix, n_states=3, is_alpha=True) + + invalid_cases = [ + dict(), # No states requested + dict(n_states=0), # No states requested + dict(n_states=-2), # Negative number of states requested + dict(n_states=2, kind="bla"), # Kind invalid + dict(n_doublets=2), # UHF with doublets + dict(n_states=2, kind="doublet"), # UHF with doublets + dict(n_singlets=2), # Kind invalid for IP/EA-ADC and UHF + dict(n_triplets=2), # Kind invalid for IP/EA-ADC and UHF + dict(n_spin_flip=2), # Kind invalid for IP/EA-ADC and UHF + dict(n_states=2, is_alpha="yes"), # is_alpha not boolean + dict(n_states=2, is_alpha=1), # is_alpha not boolean + ] + for case in invalid_cases: + with pytest.raises(InputError): + validate_state_parameters(matrix, **case) + + def test_validate_state_parameters_rhf_ea(self): + from adcc.workflow import validate_state_parameters + from adcc.AdcMatrix import AdcMatrixlike + from adcc.AdcMethod import AdcMethod + + # Build empty AdcMatrixlike object and assign ref_state and method + matrix = AdcMatrixlike() + matrix.reference_state = testdata_cache.refstate("h2o_sto3g", case="gen") + + # IP + matrix.method = AdcMethod("ea-adc2") + + assert (3, "any", True) == (validate_state_parameters( + matrix, n_states=3)) + assert (3, "any", True) == (validate_state_parameters( + matrix, n_states=3, is_alpha=False)) + assert (3, "any", True) == (validate_state_parameters( + matrix, n_states=3, is_alpha=True)) # restricted always beta + assert (2, "doublet", True) == (validate_state_parameters( + matrix, n_states=2, kind="doublet")) + assert (2, "doublet", True) == (validate_state_parameters( + matrix, n_doublets=2)) + assert (6, "doublet", True) == (validate_state_parameters( + matrix, n_doublets=6, is_alpha=True)) + + invalid_cases = [ + dict(), # No states requested + dict(n_states=0), # No states requested + dict(n_doublets=-2), # Negative number of states requested + dict(n_states=2, kind="bla"), # Kind invalid + dict(n_singlets=2), # Kind invalid for IP/EA-ADC + dict(n_triplets=2), # Kind invalid for IP/EA-ADC + dict(n_spin_flip=2), # Kind invalid for IP/EA-ADC + dict(n_states=2, is_alpha="yes"), # is_alpha not boolean + dict(n_states=2, is_alpha=1), # is_alpha not boolean + dict(n_states=2, n_spin_flip=2), # States of two sorts + dict(n_doublets=2, kind="singlet"), # kind and n_ do not agree + ] + + for case in invalid_cases: + with pytest.raises(InputError): + validate_state_parameters(matrix, **case) + + def test_validate_state_parameters_uhf_ea(self): + from adcc.workflow import validate_state_parameters + from adcc.AdcMatrix import AdcMatrixlike + from adcc.AdcMethod import AdcMethod + + # Build empty AdcMatrixlike object and assign ref_state and method + matrix = AdcMatrixlike() + matrix.reference_state = testdata_cache.refstate("cn_sto3g", case="gen") + matrix.method = AdcMethod("ea-adc2") + + assert (3, "any", True) == validate_state_parameters( + matrix, n_states=3, kind="any") + assert (3, "any", False) == validate_state_parameters( + matrix, n_states=3, is_alpha=False) + assert (3, "any", True) == validate_state_parameters( + matrix, n_states=3, is_alpha=True) + + invalid_cases = [ + dict(), # No states requested + dict(n_states=0), # No states requested + dict(n_states=-2), # Negative number of states requested + dict(n_states=2, kind="bla"), # Kind invalid + dict(n_doublets=2), # UHF with doublets + dict(n_states=2, kind="doublet"), # UHF with doublets + dict(n_singlets=2), # Kind invalid for IP/EA-ADC and UHF + dict(n_triplets=2), # Kind invalid for IP/EA-ADC and UHF + dict(n_spin_flip=2), # Kind invalid for IP/EA-ADC and UHF + dict(n_states=2, is_alpha="yes"), # is_alpha not boolean + dict(n_states=2, is_alpha=1), # is_alpha not boolean ] for case in invalid_cases: with pytest.raises(InputError): - validate_state_parameters(refstate, **case) + validate_state_parameters(matrix, **case) def test_construct_adcmatrix(self): from adcc.workflow import construct_adcmatrix @@ -98,6 +270,8 @@ def test_construct_adcmatrix(self): res = construct_adcmatrix(hfdata, method="adc3") assert isinstance(res, adcc.AdcMatrix) assert res.method == adcc.AdcMethod("adc3") + assert res.method.adc_type == "pp" + assert res.axis_blocks == ["ph", "pphh"] assert res.mospaces.core_orbitals == [] assert res.mospaces.frozen_core == [] assert res.mospaces.frozen_virtual == [] @@ -105,26 +279,52 @@ def test_construct_adcmatrix(self): res = construct_adcmatrix(hfdata, method="cvs-adc3", core_orbitals=1) assert isinstance(res, adcc.AdcMatrix) assert res.method == adcc.AdcMethod("cvs-adc3") + assert res.method.adc_type == "pp" + assert res.axis_blocks == ["ph", "pphh"] assert res.mospaces.core_orbitals == [0, 7] assert res.mospaces.frozen_core == [] assert res.mospaces.frozen_virtual == [] res = construct_adcmatrix(hfdata, method="adc2", frozen_core=1) + assert res.method.adc_type == "pp" + assert res.axis_blocks == ["ph", "pphh"] assert res.mospaces.core_orbitals == [] assert res.mospaces.frozen_core == [0, 7] assert res.mospaces.frozen_virtual == [] res = construct_adcmatrix(hfdata, method="adc2", frozen_virtual=1) + assert res.method.adc_type == "pp" + assert res.axis_blocks == ["ph", "pphh"] assert res.mospaces.core_orbitals == [] assert res.mospaces.frozen_core == [] assert res.mospaces.frozen_virtual == [6, 13] res = construct_adcmatrix(hfdata, method="adc2", frozen_virtual=1, frozen_core=1) + assert res.method.adc_type == "pp" + assert res.axis_blocks == ["ph", "pphh"] assert res.mospaces.core_orbitals == [] assert res.mospaces.frozen_core == [0, 7] assert res.mospaces.frozen_virtual == [6, 13] + res = construct_adcmatrix(hfdata, method="ip-adc3") + assert isinstance(res, adcc.AdcMatrix) + assert res.method == adcc.AdcMethod("ip-adc3") + assert res.method.adc_type == "ip" + assert res.axis_blocks == ["h", "phh"] + assert res.mospaces.core_orbitals == [] + assert res.mospaces.frozen_core == [] + assert res.mospaces.frozen_virtual == [] + + res = construct_adcmatrix(hfdata, method="ea-adc2") + assert isinstance(res, adcc.AdcMatrix) + assert res.method == adcc.AdcMethod("ea-adc2") + assert res.method.adc_type == "ea" + assert res.axis_blocks == ["p", "pph"] + assert res.mospaces.core_orbitals == [] + assert res.mospaces.frozen_core == [] + assert res.mospaces.frozen_virtual == [] + invalid_cases = [ dict(), # Missing method dict(method="dadadad"), # Unknown method @@ -195,7 +395,7 @@ def test_construct_adcmatrix(self): match=r"^Ignored frozen_virtual parameter"): construct_adcmatrix(mtx_cvs, frozen_virtual=1) - def test_diagonalise_adcmatrix(self): + def test_diagonalise_adcmatrix_pp(self): from adcc.workflow import diagonalise_adcmatrix system = "h2o_sto3g" @@ -209,11 +409,6 @@ def test_diagonalise_adcmatrix(self): matrix = adcc.AdcMatrix(method, testdata_cache.refstate(system, case=case)) - res = diagonalise_adcmatrix(matrix, n_states=n_states, kind=kind, - eigensolver="davidson") - assert res.converged - assert res.eigenvalues[:n_states] == approx(ref_singlets[:n_states]) - guesses = adcc.guesses_singlet(matrix, n_guesses=6, block="ph") res = diagonalise_adcmatrix(matrix, n_states=n_states, kind=kind, guesses=guesses) @@ -222,34 +417,91 @@ def test_diagonalise_adcmatrix(self): with pytest.raises(InputError): # Too low tolerance # SCF tolerance = 1e-14 currently + res = diagonalise_adcmatrix(matrix, n_states=9, kind=kind, + guesses=guesses,eigensolver="davidson", + conv_tol=1e-15) + + with pytest.raises(InputError): # Wrong solver method + res = diagonalise_adcmatrix(matrix, n_states=9, kind=kind, + guesses=guesses, eigensolver="blubber") + + with pytest.raises(ValueError): # Too few guesses res = diagonalise_adcmatrix(matrix, n_states=9, kind=kind, eigensolver="davidson", + guesses=guesses) + + def test_diagonalise_adcmatrix_ip(self): + from adcc.workflow import diagonalise_adcmatrix + # pytest.skip("adcman referencedata not yet available") + system = "h2o_sto3g" + case = "gen" + method = "ip-adc2" + kind = "doublet" + + refdata = testdata_cache.adcman_data(system, method=method, case=case) + ref_doublets = refdata[kind]["eigenvalues"] + n_states = min(len(ref_doublets), 3) + + matrix = adcc.AdcMatrix(method, testdata_cache.refstate(system, case=case)) + + guesses = adcc.guesses_doublet(matrix, n_guesses=6, block="h", + is_alpha=True) + res = diagonalise_adcmatrix(matrix, n_states=n_states, kind=kind, + guesses=guesses, is_alpha=True) + assert res.converged + assert res.eigenvalues[:n_states] == approx(ref_doublets[:n_states]) + + with pytest.raises(InputError): # Too low tolerance + # SCF tolerance = 1e-14 currently + res = diagonalise_adcmatrix(matrix, n_states=9, kind=kind, + guesses=guesses,eigensolver="davidson", conv_tol=1e-15) with pytest.raises(InputError): # Wrong solver method res = diagonalise_adcmatrix(matrix, n_states=9, kind=kind, - eigensolver="blubber") + guesses=guesses, eigensolver="blubber") - with pytest.raises(InputError): # Too few guesses + with pytest.raises(ValueError): # Too few guesses res = diagonalise_adcmatrix(matrix, n_states=9, kind=kind, eigensolver="davidson", guesses=guesses) - def test_estimate_n_guesses(self): - from adcc.workflow import estimate_n_guesses + def test_diagonalise_adcmatrix_ea(self): + from adcc.workflow import diagonalise_adcmatrix + system = "h2o_sto3g" + case = "gen" + method = "ea-adc2" + kind = "doublet" - refstate = testdata_cache.refstate("h2o_sto3g", case="gen") - ground_state = adcc.LazyMp(refstate) - matrix = adcc.AdcMatrix("adc2", ground_state) + refdata = testdata_cache.adcman_data(system, method=method, case=case) + ref_doublets = refdata[kind]["eigenvalues"] + n_states = min(len(ref_doublets), 3) - # Check minimal number of guesses is 4 and at some point - # we get more than four guesses - assert 4 == estimate_n_guesses(matrix, n_states=1, singles_only=True) - assert 4 == estimate_n_guesses(matrix, n_states=2, singles_only=True) - for i in range(3, 20): - assert i <= estimate_n_guesses(matrix, n_states=i, singles_only=True) + matrix = adcc.AdcMatrix(method, testdata_cache.refstate(system, case=case)) - def test_obtain_guesses_by_inspection(self): + guesses = adcc.guesses_doublet(matrix, n_guesses=6, block="p", + is_alpha=True) + res = diagonalise_adcmatrix(matrix, n_states=n_states, kind=kind, + guesses=guesses, is_alpha=True) + assert res.converged + assert res.eigenvalues[:n_states] == approx(ref_doublets[:n_states]) + + with pytest.raises(InputError): # Too low tolerance + # SCF tolerance = 1e-14 currently + res = diagonalise_adcmatrix(matrix, n_states=9, kind=kind, + guesses=guesses,eigensolver="davidson", + conv_tol=1e-15) + + with pytest.raises(InputError): # Wrong solver method + res = diagonalise_adcmatrix(matrix, n_states=9, kind=kind, + guesses=guesses, eigensolver="blubber") + + with pytest.raises(ValueError): # Too few guesses + res = diagonalise_adcmatrix(matrix, n_states=9, kind=kind, + eigensolver="davidson", + guesses=guesses) + + def test_obtain_guesses_by_inspection_pp(self): from adcc.workflow import obtain_guesses_by_inspection refstate = testdata_cache.refstate("h2o_sto3g", case="gen") @@ -269,8 +521,156 @@ def test_obtain_guesses_by_inspection(self): matrix2, n_guesses=i, kind="triplet", n_guesses_doubles=2) assert len(res) == i + # Test right number of guesses if insufficient singles guesses + res = obtain_guesses_by_inspection(matrix2, n_guesses=20, kind="singlet") + assert len(res) == 20 + + # Only doubles guesses + res = obtain_guesses_by_inspection(matrix2, n_guesses=4, + kind="singlet", + n_guesses_doubles=4) + assert len(res) == 4 + with pytest.raises(InputError): obtain_guesses_by_inspection(matrix1, n_guesses=4, kind="any", n_guesses_doubles=2) with pytest.raises(InputError): obtain_guesses_by_inspection(matrix1, n_guesses=40, kind="any") + + def test_obtain_guesses_by_inspection_ip(self): + from adcc.workflow import obtain_guesses_by_inspection + + refstate = testdata_cache.refstate("h2o_sto3g", case="gen") + ground_state = adcc.LazyMp(refstate) + matrix2 = adcc.AdcMatrix("ip-adc2", ground_state) + matrix1 = adcc.AdcMatrix("ip-adc1", ground_state) + + # Test that the right number of guesses is returned + for i in range(4, 9): + res = obtain_guesses_by_inspection(matrix2, n_guesses=i, + kind="doublet", + spin_change=-0.5, is_alpha=True) + assert len(res) == i + + for i in range(2, 5): + res = obtain_guesses_by_inspection( + matrix1, n_guesses=i, kind="doublet", + spin_change=-0.5, is_alpha=True) + assert len(res) == i + + # Test right number of guesses if insufficient singles guesses + res = obtain_guesses_by_inspection(matrix2, n_guesses=20, + kind="doublet", + spin_change=-0.5, is_alpha=True) + assert len(res) == 20 + + # Only doubles guesses + res = obtain_guesses_by_inspection(matrix2, n_guesses=4, + kind="doublet", + spin_change=-0.5, is_alpha=True, + n_guesses_doubles=4) + assert len(res) == 4 + + with pytest.raises(InputError): + obtain_guesses_by_inspection(matrix1, n_guesses=6, kind="any", + spin_change=-0.5, is_alpha=True) + with pytest.raises(InputError): + obtain_guesses_by_inspection(matrix1, n_guesses=2, kind="any", + n_guesses_doubles=2, spin_change=-0.5, + is_alpha=True) + + def test_obtain_guesses_by_inspection_ea(self): + from adcc.workflow import obtain_guesses_by_inspection + + refstate = testdata_cache.refstate("h2o_sto3g", case="gen") + ground_state = adcc.LazyMp(refstate) + matrix2 = adcc.AdcMatrix("ea-adc2", ground_state) + matrix1 = adcc.AdcMatrix("ea-adc1", ground_state) + + # Test that the right number of guesses is returned + for i in range(4, 9): + res = obtain_guesses_by_inspection(matrix2, n_guesses=i, + kind="doublet", + spin_change=0.5, + is_alpha=True) + assert len(res) == i + + for i in range(1, 2): + res = obtain_guesses_by_inspection( + matrix1, n_guesses=i, kind="doublet", + spin_change=0.5, is_alpha=True) + assert len(res) == i + + # Test right number of guesses if insufficient singles guesses + res = obtain_guesses_by_inspection(matrix2, n_guesses=20, + kind="doublet", + spin_change=0.5, is_alpha=True) + assert len(res) == 20 + + # Only doubles guesses + res = obtain_guesses_by_inspection(matrix2, n_guesses=4, + kind="doublet", + spin_change=0.5, is_alpha=True, + n_guesses_doubles=4) + assert len(res) == 4 + + with pytest.raises(InputError): + obtain_guesses_by_inspection(matrix1, n_guesses=6, kind="any", + spin_change=0.5, is_alpha=True) + with pytest.raises(InputError): + obtain_guesses_by_inspection(matrix1, n_guesses=2, kind="any", + n_guesses_doubles=2, spin_change=0.5, + is_alpha=True) + + def test_construct_guesses_explicit(self): + from adcc.workflow import construct_guesses + + refstate = testdata_cache.refstate("h2o_sto3g", case="gen") + ground_state = adcc.LazyMp(refstate) + matrix = adcc.AdcMatrix("adc2", ground_state) + + res = construct_guesses( + matrix=matrix, + n_states=3, + kind="singlet", + spin_change=0, + n_guesses=5 + ) + + assert len(res) == 5 + + def test_construct_guesses_davidson(self): + from adcc.workflow import construct_guesses + + refstate = testdata_cache.refstate("h2o_sto3g", case="gen") + ground_state = adcc.LazyMp(refstate) + matrix = adcc.AdcMatrix("adc2", ground_state) + + res = construct_guesses( + matrix=matrix, + n_states=2, + kind="singlet", + spin_change=0, + n_guesses=None, + eigensolver="davidson" + ) + + assert len(res) == 4 + + def test_construct_guesses_lanczos(self): + from adcc.workflow import construct_guesses + + refstate = testdata_cache.refstate("h2o_sto3g", case="gen") + ground_state = adcc.LazyMp(refstate) + matrix = adcc.AdcMatrix("adc2", ground_state) + + res = construct_guesses( + matrix=matrix, + n_states=2, + kind="singlet", + spin_change=0, + n_guesses=None, + eigensolver="lanczos" + ) + + assert len(res) == 2 \ No newline at end of file diff --git a/adcc/workflow.py b/adcc/workflow.py index a9d27bf63..694f70bc6 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -25,14 +25,18 @@ from libadcc import ReferenceState +from typing import Optional + from . import solver -from .guess import (guesses_any, guesses_singlet, guesses_spin_flip, - guesses_triplet) +from .guess import (determine_spin_change, estimate_n_guesses, + guesses_from_diagonal, get_spin_block_symmetrisation) from .LazyMp import LazyMp from .AdcMatrix import AdcMatrix, AdcMatrixlike, AdcExtraTerm from .AdcMethod import AdcMethod +from .AmplitudeVector import AmplitudeVector from .exceptions import InputError from .ExcitedStates import ExcitedStates +# from .ChargedExcitations import DetachedStates, AttachedStates from .ReferenceState import ReferenceState as adcc_ReferenceState from .solver.lanczos import lanczos from .solver.davidson import jacobi_davidson @@ -43,11 +47,12 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, - eigensolver=None, guesses=None, n_guesses=None, + eigensolver="davidson", guesses=None, n_guesses=None, n_guesses_doubles=None, output=sys.stdout, core_orbitals=None, frozen_core=None, frozen_virtual=None, method=None, - n_singlets=None, n_triplets=None, n_spin_flip=None, - environment=None, **solverargs): + n_singlets=None, n_doublets=None, n_triplets=None, + n_spin_flip=None, is_alpha=None, environment=None, + **solverargs): """Run an ADC calculation. Main entry point to run an ADC calculation. The reference to build the ADC @@ -70,17 +75,24 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, n_states : int, optional kind : str, optional n_singlets : int, optional + n_doublets : int, optional n_triplets : int, optional n_spin_flip : int, optional Specify the number and kind of states to be computed. Possible values - for kind are "singlet", "triplet", "spin_flip" and "any", which is - the default. For unrestricted references clamping spin-pure + for kind are "singlet", "doublet", "triplet", "spin_flip" and "any", + which is the default. For unrestricted references clamping spin-pure singlets/triplets is currently not possible and kind has to remain as - "any". For restricted references `kind="singlets"` or `kind="triplets"` - may be employed to enforce a particular excited states manifold. + "any". For restricted references `kind="singlets"`, `kind="doublets"` + or `kind="triplets"` may be employed to enforce a particular excited + states manifold. Specifying `n_singlets` is equivalent to setting `kind="singlet"` and - `n_states=5`. Similarly for `n_triplets` and `n_spin_flip`. - `n_spin_flip` is only valid for unrestricted references. + `n_states=5`. Similarly for `n_doublets`, `n_triplets` and + `n_spin_flip`. `n_spin_flip` is only valid for unrestricted references. + + is_alpha : bool, optional + Is the detached/attached electron alpha spin for the respective + IP-/EA-ADC calculation. Per default it will be set to `True` for + IP- and EA-ADC calculations. conv_tol : float, optional Convergence tolerance to employ in the iterative solver for obtaining @@ -91,8 +103,8 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, The eigensolver algorithm to use. n_guesses : int, optional - Total number of guesses to compute. By default only guesses derived from - the singles block of the ADC matrix are employed. See + Total number of guesses to compute. By default only guesses derived + from the singles block of the ADC matrix are employed. See `n_guesses_doubles` for alternatives. If no number is given here `n_guesses = min(4, 2 * number of excited states to compute)` or a smaller number if the number of excitation is estimated to be less @@ -178,38 +190,81 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, ... mf.kernel() ... ... state = adcc.cvs_adc3(mf, core_orbitals=1, n_singlets=3) - """ + + Run an IP-ADC(2) calculation of water with a detached alpha + electron + + >>> import psi4 + ... import adcc + ... # Run SCF in Psi4 + ... mol = psi4.geometry(''' + ... 0 1 + ... O 0.0000000000 0.0000000000 0.0000000000 + ... H 0.0000000000 0.0000000000 0.9570000000 + ... H 0.9270000000 0.0000000000 -0.2400000000 + ... symmetry c1 + ... units Angstrom + ... ''') + ... psi4.core.be_quiet() + ... psi4.set_options({'basis': "6-31++G(d)", 'e_convergence': 1e-13, + ... 'd_convergence': 1e-7, 'reference': "uhf", + ... 'scf_type': "direct"}) + ... scf_e, wfn = psi4.energy('SCF', return_wfn=True) + ... + ... state = adcc.ip_adc2(wfn, n_doublets=3, is_alpha=True) +""" matrix = construct_adcmatrix( data_or_matrix, core_orbitals=core_orbitals, frozen_core=frozen_core, frozen_virtual=frozen_virtual, method=method) - n_states, kind = validate_state_parameters( - matrix.reference_state, n_states=n_states, n_singlets=n_singlets, - n_triplets=n_triplets, n_spin_flip=n_spin_flip, kind=kind) - - # Determine spin change during excitation. If guesses is not None, - # i.e. user-provided, we cannot guarantee for obtaining a particular - # spin_change in case of a spin_flip calculation. - spin_change = None - if kind == "spin_flip" and guesses is None: - spin_change = -1 - - # Select solver to run - if eigensolver is None: - eigensolver = "davidson" - + n_states, kind, is_alpha = validate_state_parameters( + matrix, n_states=n_states, n_singlets=n_singlets, + n_doublets=n_doublets, n_triplets=n_triplets, n_spin_flip=n_spin_flip, + kind=kind, is_alpha=is_alpha) + # Setup environment coupling terms and energy corrections - ret = setup_environment(matrix, environment) - env_matrix_term, env_energy_corrections = ret + env_matrix_term, env_energy_corrections = setup_environment(matrix, + environment) # add terms to matrix if env_matrix_term: matrix += env_matrix_term + # Construct guesses and determine spin_change + if guesses is None: + spin_change = determine_spin_change(matrix.method, kind, is_alpha) + guesses = construct_guesses( + matrix, n_states, kind, spin_change, n_guesses, n_guesses_doubles, + is_alpha, eigensolver + ) + else: + if len(guesses) < n_states: + raise InputError("Less guesses provided via guesses (== {}) " + "than states to be computed (== {})" + "".format(len(guesses), n_states)) + if n_guesses is not None: + warnings.warn("Ignoring n_guesses parameter, since guesses are " + "explicitly provided.") + if n_guesses_doubles is not None: + warnings.warn("Ignoring n_guesses_doubles parameter, since guesses" + " are explicitly provided.") + # Set spin_change to None since we don't know if guesses are provided + spin_change = None + + diagres = diagonalise_adcmatrix( - matrix, n_states, kind, guesses=guesses, n_guesses=n_guesses, - n_guesses_doubles=n_guesses_doubles, conv_tol=conv_tol, output=output, - eigensolver=eigensolver, **solverargs) - exstates = ExcitedStates(diagres) + matrix, n_states, guesses, kind=kind, conv_tol=conv_tol, + output=output, eigensolver=eigensolver, is_alpha=is_alpha, + **solverargs) + + if matrix.method.adc_type == "pp": + exstates = ExcitedStates(diagres) + # elif matrix.method.adc_type == "ip": + # exstates = DetachedStates(diagres, is_alpha) + # elif matrix.method.adc_type == "ea": + # exstates = AttachedStates(diagres, is_alpha) + else: + raise ValueError(f"Unknown ADC method: {matrix.method.name}") + exstates.kind = kind exstates.spin_change = spin_change @@ -255,20 +310,20 @@ def construct_adcmatrix(data_or_matrix, core_orbitals=None, frozen_core=None, elif core_orbitals is not None: mospaces = data_or_matrix.mospaces warnings.warn("Ignored core_orbitals parameter because data_or_matrix" - " is a ReferenceState, a LazyMp or an AdcMatrixlike object " - " (which has a value of core_orbitals={})." + " is a ReferenceState, a LazyMp or an AdcMatrixlike " + "object (which has a value of core_orbitals={})." "".format(mospaces.n_orbs_alpha("o2"))) elif frozen_core is not None: mospaces = data_or_matrix.mospaces warnings.warn("Ignored frozen_core parameter because data_or_matrix" - " is a ReferenceState, a LazyMp or an AdcMatrixlike object " - " (which has a value of frozen_core={})." + " is a ReferenceState, a LazyMp or an AdcMatrixlike " + "object (which has a value of frozen_core={})." "".format(mospaces.n_orbs_alpha("o3"))) elif frozen_virtual is not None: mospaces = data_or_matrix.mospaces warnings.warn("Ignored frozen_virtual parameter because data_or_matrix" - " is a ReferenceState, a LazyMp or an AdcMatrixlike object " - " (which has a value of frozen_virtual={})." + " is a ReferenceState, a LazyMp or an AdcMatrixlike " + "object (which has a value of frozen_virtual={})." "".format(mospaces.n_orbs_alpha("v2"))) # Make AdcMatrix (if not done) @@ -285,18 +340,23 @@ def construct_adcmatrix(data_or_matrix, core_orbitals=None, frozen_core=None, return data_or_matrix -def validate_state_parameters(reference_state, n_states=None, n_singlets=None, - n_triplets=None, n_spin_flip=None, kind="any"): +def validate_state_parameters(matrix, n_states=None, n_singlets=None, + n_doublets=None, n_triplets=None, + n_spin_flip=None, kind="any", is_alpha=None + ) -> tuple[int, str, Optional[bool]]: """ Check the passed state parameters for consistency with itself and with the passed reference and normalise them. In the end return the number of - states and the corresponding kind parameter selected. + states, the corresponding kind parameter selected and is_alpha which will + only be set to a Boolean for IP- and EA-ADC calculations. Internal function called from run_adc. """ - if sum(nst is not None for nst in [n_states, n_singlets, + reference_state = matrix.reference_state + adc_type = matrix.method.adc_type + if sum(nst is not None for nst in [n_states, n_singlets, n_doublets, n_triplets, n_spin_flip]) > 1: raise InputError("One may only specify one out of n_states, " - "n_singlets, n_triplets and n_spin_flip") + "n_singlets, n_doublets, n_triplets and n_spin_flip") if n_singlets is not None: if not reference_state.restricted: @@ -307,6 +367,16 @@ def validate_state_parameters(reference_state, n_states=None, n_singlets=None, "with n_singlets > 0") kind = "singlet" n_states = n_singlets + if n_doublets is not None: + if not reference_state.restricted: + raise InputError("The n_doublets parameter may only be employed " + "for restricted references") + if kind not in ["doublet", "any"]: + raise InputError(f"Kind parameter {kind} not compatible " + "with n_doublets > 0") + kind = "doublet" + n_states = n_doublets + is_alpha = True if n_triplets is not None: if not reference_state.restricted: raise InputError("The n_triplets parameter may only be employed " @@ -326,35 +396,139 @@ def validate_state_parameters(reference_state, n_states=None, n_singlets=None, kind = "spin_flip" n_states = n_spin_flip + # Check for IP- and EA-ADC parameter is_alpha + if adc_type == "pp": + if is_alpha is not None: + raise InputError("is_alpha may only be set for IP- and EA-ADC " + "calculations") + else: + if not isinstance(is_alpha, bool) and is_alpha is not None: + raise InputError("is_alpha has to be a Boolean or None.") + if is_alpha is None or reference_state.restricted: + # Per default set to True and for restricted references, only alpha + # states will be computed (beta states are identical) + is_alpha = True + # Check if there are states to be computed if n_states is None or n_states == 0: raise InputError("No excited states to be computed. Specify at least " - "one of n_states, n_singlets, n_triplets, " - "or n_spin_flip") + "one of n_states, n_singlets, n_doublets, " + "n_triplets, or n_spin_flip.") if n_states < 0: raise InputError("n_states needs to be positive") - if kind not in ["any", "spin_flip", "singlet", "triplet"]: + if kind not in ["any", "spin_flip", "singlet", "doublet", "triplet"]: raise InputError("The kind parameter may only take the values 'any', " - "'singlet', 'triplet' or 'spin_flip'") - if kind in ["singlet", "triplet"] and not reference_state.restricted: - raise InputError("kind==singlet and kind==triplet are only valid for " - "ADC calculations in combination with a restricted " - "ground state.") + "'singlet', 'doublet', 'triplet' or 'spin_flip'") + if (kind in ["singlet", "doublet", "triplet"] + and not reference_state.restricted): + raise InputError("kind==singlet, kind==doublet and kind==triplet are " + "only valid for ADC calculations in combination with " + "a restricted ground state.") if kind in ["spin_flip"] and reference_state.restricted: raise InputError("kind==spin_flip is only valid for " - "ADC calculations in combination with an unrestricted " + "ADC calculations in combination with an unrestricted" + " ground state.") + if kind in ["spin_flip", "singlet", "triplet"] and adc_type != "pp": + raise InputError("kind==singlet, kind==triplet, and kind==spin_flip " + "are only valid for PP-ADC calculations.") + if kind == "doublet" and adc_type == "pp": + raise InputError("kind==doublet is only valid for IP/EA-ADC " + "calculations in combination with a restricted " "ground state.") - return n_states, kind + return n_states, kind, is_alpha + + +def obtain_guesses_by_inspection(matrix, n_guesses, kind, + n_guesses_doubles=None, is_alpha=None, + spin_change=0): + """ + Obtain guesses by inspecting the diagonal matrix elements. + If n_guesses_doubles is not None, this number is always adhered to. + Otherwise the number of doubles guesses is adjusted to fill up whatever + the singles guesses cannot provide to reach n_guesses. + + matrix The matrix for which guesses are to be constructed + is_alpha Is the detached/attached electron alpha spin for the + respective IP-/EA-ADC calculation. + kwargs Any other argument understood by guesses_from_diagonal. + """ + spin_block_symmetrisation = get_spin_block_symmetrisation(kind) + + # Determine number of singles guesses to request + if n_guesses_doubles is None: + n_guesses_doubles = 0 + + n_guesses_singles = n_guesses - n_guesses_doubles + + guesses = guesses_from_diagonal( + matrix, n_guesses_singles, block=matrix.axis_blocks[0], kind=kind, + is_alpha=is_alpha, spin_change=spin_change, + spin_block_symmetrisation=spin_block_symmetrisation) + + # Determine number of doubles guesses to request if not + # explicitly specified + n_guesses_doubles = n_guesses - len(guesses) + + if n_guesses_doubles > 0: + if matrix.method.level < 2: + raise InputError("n_guesses_doubles > 0 is only sensible if the " + "ADC method has a doubles block (i.e. it is *not*" + " ADC(0), ADC(1) or a variant thereof.") + guesses += guesses_from_diagonal( + matrix, n_guesses_doubles, matrix.axis_blocks[1], kind, + is_alpha, spin_change, spin_block_symmetrisation) -def diagonalise_adcmatrix(matrix, n_states, kind, eigensolver="davidson", - guesses=None, n_guesses=None, n_guesses_doubles=None, - conv_tol=None, output=sys.stdout, **solverargs): + if len(guesses) < n_guesses: + raise InputError("Less guesses found than requested: {} found, " + "{} requested".format( + len(guesses), n_guesses)) + return guesses + + +def construct_guesses( + matrix: AdcMatrix, + n_states: int, + kind: str, + spin_change: float, + n_guesses: int, + n_guesses_doubles: Optional[int] = None, + is_alpha: Optional[bool] = None, + eigensolver: Optional[str] = "davidson" + ) -> list[AmplitudeVector]: + """ + This function constructs appropriate guesses if not given. + Returns a :class:`Guesses` object containing all crucial guess information. + Internal function called from run_adc. + """ + if n_guesses is None: + # Set solver-specific parameters + if eigensolver == "davidson": + n_guesses_per_state = 2 + else: + n_guesses_per_state = 1 + n_guesses = estimate_n_guesses(matrix, n_states, + n_guesses_per_state) + + return obtain_guesses_by_inspection( + matrix, n_guesses, kind, n_guesses_doubles, is_alpha, spin_change + ) + + +def diagonalise_adcmatrix(matrix, n_states, guesses, kind="any", conv_tol=None, + eigensolver="davidson", output=sys.stdout, + is_alpha=None, **solverargs): """ This function seeks appropriate guesses and afterwards proceeds to diagonalise the ADC matrix using the specified eigensolver. Internal function called from run_adc. + + matrix : AdcMatrix + n_states : int + guesses : list[AmplitudeVector] + A list of guess vectors + kind : str """ reference_state = matrix.reference_state @@ -370,149 +544,50 @@ def diagonalise_adcmatrix(matrix, n_states, kind, eigensolver="davidson", # Determine explicit_symmetrisation explicit_symmetrisation = IndexSymmetrisation - if kind in ["singlet", "triplet"]: + if kind in ["singlet", "doublet", "triplet"]: explicit_symmetrisation = IndexSpinSymmetrisation( matrix, enforce_spin_kind=kind ) # Set some solver-specific parameters if eigensolver == "davidson": - n_guesses_per_state = 2 callback = setup_solver_printing( - "Jacobi-Davidson", matrix, kind, solver.davidson.default_print, + "Jacobi-Davidson", matrix, kind, + solver.davidson.default_print, is_alpha=is_alpha, output=output) run_eigensolver = jacobi_davidson elif eigensolver == "lanczos": - n_guesses_per_state = 1 callback = setup_solver_printing( "Lanczos", matrix, kind, solver.lanczos.default_print, - output=output) + is_alpha=is_alpha, output=output) run_eigensolver = lanczos else: raise InputError(f"Solver {eigensolver} unknown, try 'davidson'.") - # Obtain or check guesses - if guesses is None: - if n_guesses is None: - # restrict to the number of available singles guesses if no doubles - # are available - n_guesses = estimate_n_guesses( - matrix=matrix, n_states=n_states, - singles_only=("pphh" not in matrix.axis_blocks), - n_guesses_per_state=n_guesses_per_state - ) - guesses = obtain_guesses_by_inspection(matrix, n_guesses, kind, - n_guesses_doubles) - else: - if len(guesses) < n_states: - raise InputError("Less guesses provided via guesses (== {}) " - "than states to be computed (== {})" - "".format(len(guesses), n_states)) - if n_guesses is not None: - warnings.warn("Ignoring n_guesses parameter, since guesses are " - "explicitly provided.") - if n_guesses_doubles is not None: - warnings.warn("Ignoring n_guesses_doubles parameter, since guesses " - "are explicitly provided.") - solverargs.setdefault("which", "SA") - return run_eigensolver(matrix, guesses, n_ep=n_states, conv_tol=conv_tol, - callback=callback, + return run_eigensolver(matrix, guesses, n_ep=n_states, + conv_tol=conv_tol, callback=callback, explicit_symmetrisation=explicit_symmetrisation, **solverargs) -def estimate_n_guesses(matrix, n_states, singles_only=True, - n_guesses_per_state=2): - """ - Implementation of a basic heuristic to find a good number of guess - vectors to be searched for using the find_guesses function. - Internal function called from run_adc. - - matrix ADC matrix - n_states Number of states to be computed - singles_only Try to stay withing the singles excitation space - with the number of guess vectors. - n_guesses_per_state Number of guesses to search for for each state - """ - # Try to use at least 4 or twice the number of states - # to be computed as guesses - n_guesses = n_guesses_per_state * max(2, n_states) - - if singles_only: - # Compute the maximal number of sensible singles block guesses. - # This is roughly the number of occupied alpha orbitals - # times the number of virtual alpha orbitals - # - # If the system is core valence separated, then only the - # core electrons count as "occupied". - mospaces = matrix.mospaces - sp_occ = "o2" if matrix.is_core_valence_separated else "o1" - n_virt_a = mospaces.n_orbs_alpha("v1") - n_occ_a = mospaces.n_orbs_alpha(sp_occ) - n_guesses = min(n_guesses, n_occ_a * n_virt_a) - - # Adjust if we overshoot the maximal number of sensible singles block - # guesses, but make sure we get at least n_states guesses - return max(n_states, n_guesses) - - -def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None): - """ - Obtain guesses by inspecting the diagonal matrix elements. - If n_guesses_doubles is not None, this is number is always adhered to. - Otherwise the number of doubles guesses is adjusted to fill up whatever - the singles guesses cannot provide to reach n_guesses. - Internal function called from run_adc. - """ - if n_guesses_doubles is not None and n_guesses_doubles > 0 \ - and "pphh" not in matrix.axis_blocks: - raise InputError("n_guesses_doubles > 0 is only sensible if the ADC " - "method has a doubles block (i.e. it is *not* ADC(0), " - "ADC(1) or a variant thereof.") - - # Determine guess function - guess_function = {"any": guesses_any, "singlet": guesses_singlet, - "triplet": guesses_triplet, - "spin_flip": guesses_spin_flip}[kind] - - # Determine number of singles guesses to request - n_guess_singles = n_guesses - if n_guesses_doubles is not None: - n_guess_singles = n_guesses - n_guesses_doubles - singles_guesses = guess_function(matrix, n_guess_singles, block="ph") - - doubles_guesses = [] - if "pphh" in matrix.axis_blocks: - # Determine number of doubles guesses to request if not - # explicitly specified - if n_guesses_doubles is None: - n_guesses_doubles = n_guesses - len(singles_guesses) - if n_guesses_doubles > 0: - doubles_guesses = guess_function(matrix, n_guesses_doubles, - block="pphh") - - total_guesses = singles_guesses + doubles_guesses - if len(total_guesses) < n_guesses: - raise InputError("Less guesses found than requested: {} found, " - "{} requested".format(len(total_guesses), n_guesses)) - return total_guesses - - def setup_solver_printing(solmethod_name, matrix, kind, default_print, - output=None): + is_alpha=None, output=None): """ Setup default printing for solvers. Internal function called from run_adc. """ - kstr = " " + kstr = "" if kind != "any": kstr = " " + kind method_name = f"{matrix}" if hasattr(matrix, "method"): method_name = matrix.method.name + spin_type = "" + if is_alpha is not None: + spin_type = "alpha " if is_alpha else "beta " if output is not None: - print(f"Starting {method_name}{kstr} {solmethod_name} ...", + print(f"Starting {spin_type}{method_name}{kstr} {solmethod_name} ...", file=output) def inner_callback(state, identifier): @@ -525,6 +600,10 @@ def setup_environment(matrix, environment): Setup environment matrix terms and/or energy corrections. Internal function called from run_adc. """ + if environment and matrix.method.adc_type != "pp": + raise NotImplementedError("Environment for IP- and EA-ADC calculations" + " not implemented.") + valid_envs = ["ptss", "ptlr", "linear_response"] hf = matrix.reference_state if hf.environment and environment is None: @@ -583,8 +662,8 @@ def setup_environment(matrix, environment): from adcc.adc_pp import environment as adcpp_env block_key = f"block_ph_ph_0_{hf.environment}" if not hasattr(adcpp_env, block_key): - raise NotImplementedError("Matrix term for linear response coupling" - f" with solvent {hf.environment}" + raise NotImplementedError("Matrix term for linear response " + f"coupling with solvent {hf.environment}" " not implemented.") block_fun = getattr(adcpp_env, block_key) env_matrix_term = AdcExtraTerm(matrix, {'ph_ph': block_fun}) diff --git a/libadcc_src/amplitude_vector_enforce_spin_kind.cc b/libadcc_src/amplitude_vector_enforce_spin_kind.cc index e5727efc4..fb4d6f04a 100644 --- a/libadcc_src/amplitude_vector_enforce_spin_kind.cc +++ b/libadcc_src/amplitude_vector_enforce_spin_kind.cc @@ -31,7 +31,8 @@ namespace libadcc { namespace lt = libtensor; void amplitude_vector_enforce_spin_kind(std::shared_ptr doubles_tensor, - std::string block, std::string spin_kind) { + std::string block, + std::string spin_kind, bool is_ip) { // Nothing to do for singles block if (block == "s") return; @@ -64,10 +65,130 @@ void amplitude_vector_enforce_spin_kind(std::shared_ptr doubles_tensor, return; } + if (spin_kind == "doublet") { + auto& u2 = asbt3(doubles_tensor); + lt::block_tensor_ctrl<3, scalar_type> ctrl(u2); + const lt::symmetry<3, scalar_type>& sym = ctrl.req_const_symmetry(); + + // Extract the number of blocks per dimension + const lt::block_index_space<3>& bis = sym.get_bis(); + lt::dimensions<3> bidims(bis.get_block_index_dims()); + + // Setup i1 to point to 0,0,0 and i2 to the half of the + // full number of blocks, i.e. to the alpha blocks in each + // dimension only. + lt::index<3> i1, i2; + for (size_t i = 0; i < 3; i++) i2[i] = bidims[i] / 2 - 1; + + // Index range over all alpha-alpha-alpha blocks + // in all point group symmetries + const lt::index_range<3> index_range_alpha(i1, i2); + + // This dimensions object contains the number of alpha blocks per dimension + lt::dimensions<3> bidims_alpha(index_range_alpha); + + // Iterate over all alpha-alpha-alpha blocks + lt::abs_index<3> ai(bidims_alpha); + do { + // This gives the block index tuple + const lt::index<3>& ii = ai.get_index(); + + // Construct the orbit corresponding to this index (i.e. the iterator + // running over all elements equivalent by symmetry + // Ignore spin-forbidden, i.e. zero orbits + lt::short_orbit<3, scalar_type> orbi(sym, ii, /* compute_if_allowed_orbit = */ true); + if (!orbi.is_allowed()) continue; + + // get_acindex -> get absolute canonical index + // Continue if our current index is larger than the canonical index + if (orbi.get_acindex() < ai.get_abs_index()) continue; + + // TODO This might be wrong ... think about it and talk to Adrian + // the point is that orbi might have other strides than bidims_alpha + // Continue if the canonical index is already past the + // alpha-alpha-alpha block + if (orbi.get_acindex() > bidims_alpha.get_size()) continue; + + // Get the index tuple of the canonical block of (alpha, alpha, alpha) + const lt::index<3>& ci = orbi.get_cindex(); + + lt::index<3> i1(ci); + lt::index<3> i2(ci); + + if (is_ip) { // IP-ADC calculation + // set i1 to (alpha, beta, beta) equivalent of the canonical index + // pinned by ai and orbi + i1[1] += bidims_alpha[1]; + i1[2] += bidims_alpha[2]; + + // set i2 to (beta, alpha, beta) + i2[0] += bidims_alpha[0]; + i2[2] += bidims_alpha[2]; + + } else { // EA-ADC calculation + // set i1 to (beta, alpha, beta) equivalent of the canonical index + // pinned by ai and orbi + i1[0] += bidims_alpha[0]; + i1[2] += bidims_alpha[2]; + + // set i2 to (beta, beta, alpha) + i2[0] += bidims_alpha[0]; + i2[1] += bidims_alpha[1]; + } + + // + // What the following code does is that it keeps the spin projection (S^2) + // properly, provided that the symmetry setup is done as in + // contrib/adc_pp/adc_guess_d.C. It assumes the coefficients as setup in + // contrib/adc_pp/adc_guess_d.C adc_guess_d::build_guesses in order to preserve + // S^2 value setup in the guess. + // + + lt::orbit<3, scalar_type> orb1(sym, i1, false), orb2(sym, i2, false); + // Canonical block of (a, b, b) for IP-ADC or (b, a, b) for EA-ADC + const lt::index<3>& ci1 = orb1.get_cindex(); + // Canonical block of (b, a, b) for IP-ADC or (b, b, a) for EA-ADC + const lt::index<3>& ci2 = orb2.get_cindex(); + bool zero1 = ctrl.req_is_zero_block(ci1); + bool zero2 = ctrl.req_is_zero_block(ci2); + if (zero1 && zero2) { + // Set (alpha, alpha, alpha) to zero + // This effectively filters out the quartet components with zero blocks + // in (alpha, beta, beta) and (beta, alpha, beta) (IP-ADC) erroneously + // introduced due to numerical errors. (beta, alpha, beta) and + // (beta, beta, alpha) blocks for EA-ADC. + ctrl.req_zero_block(ci); + continue; + } + + // Get block corresponding to canonical index of (alpha, alpha, alpha) + lt::dense_tensor_wr_i<3, scalar_type>& blk = ctrl.req_block(ci); + + if (!zero1) { + // IP: (alpha, beta, beta) / EA: (beta, alpha, beta) is not zero + lt::dense_tensor_rd_i<3, scalar_type>& blk1 = ctrl.req_const_block(ci1); + lt::tod_copy<3>(blk1, orb1.get_transf(i1)).perform(/* assign= */ true, blk); + ctrl.ret_const_block(ci1); + } + if (!zero2) { + // IP: (beta, alpha, beta) / EA: (beta, beta, alpha) is not zero + lt::dense_tensor_rd_i<3, scalar_type>& blk2 = ctrl.req_const_block(ci2); + + // Assign if (alpha, beta, beta) for IP- and (beta, alpha, beta) for + // EA-ADC is zero, else += + lt::tod_copy<3>(blk2, orb2.get_transf(i2)).perform(zero1, blk); + ctrl.ret_const_block(ci2); + } + ctrl.ret_block(ci); + + } while (ai.inc()); + return; + } + if (spin_kind != "singlet") { throw not_implemented_error( - "Only implemented for spin_kind == 'singlet' and spin_kind == " - "'triplet'."); + "Only implemented for spin_kind == 'singlet', spin_kind == " + "'triplet' or spin_kind == 'doublet'."); } auto& u2 = asbt4(doubles_tensor); @@ -169,4 +290,4 @@ void amplitude_vector_enforce_spin_kind(std::shared_ptr doubles_tensor, } while (ai.inc()); } -} // namespace libadcc +} // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/amplitude_vector_enforce_spin_kind.hh b/libadcc_src/amplitude_vector_enforce_spin_kind.hh index 6a7a4dbd8..7e6eaddfb 100644 --- a/libadcc_src/amplitude_vector_enforce_spin_kind.hh +++ b/libadcc_src/amplitude_vector_enforce_spin_kind.hh @@ -36,7 +36,9 @@ namespace libadcc { * @param block The block of an amplitude this tensor represents * @param spin_kind The kind of spin to enforce */ -void amplitude_vector_enforce_spin_kind(std::shared_ptr tensor, std::string block, - std::string spin_kind); +void amplitude_vector_enforce_spin_kind(std::shared_ptr tensor, + std::string block, + std::string spin_kind, + bool is_ip); ///@} } // namespace libadcc diff --git a/libadcc_src/fill_ea_doubles_guesses.cc b/libadcc_src/fill_ea_doubles_guesses.cc new file mode 100644 index 000000000..b3d5c2f32 --- /dev/null +++ b/libadcc_src/fill_ea_doubles_guesses.cc @@ -0,0 +1,78 @@ +// +// Copyright (C) 2020 by the adcc authors +// +// This file is part of adcc. +// +// adcc is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// adcc is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with adcc. If not, see . +// + +#include "fill_ea_doubles_guesses.hh" +#include "TensorImpl.hh" +#include "guess/ea_adc_guess_d.hh" + +namespace libadcc { + +size_t fill_ea_doubles_guesses(std::vector> guesses_d, + std::shared_ptr mospaces, + std::shared_ptr d_o, + std::shared_ptr d_v, + bool a_spin, bool restricted, bool doublet, + int spin_change_twice, + scalar_type degeneracy_tolerance) { + + size_t n_guesses = guesses_d.size(); + if (n_guesses == 0) return 0; + + // Make a copy of the doubles symmetry + libtensor::block_tensor_ctrl<3, scalar_type> ctrl(asbt3(guesses_d[0])); + libtensor::symmetry<3, scalar_type> sym_s(ctrl.req_const_symmetry().get_bis()); + libtensor::so_copy<3, scalar_type>(ctrl.req_const_symmetry()).perform(sym_s); + + // Make ab pointers object + auto make_ab = [](const MoSpaces& mo, const std::string& space) { + const std::vector& block_spin = mo.map_block_spin.at(space); + std::vector ab; + for (size_t i = 0; i < block_spin.size(); ++i) { + ab.push_back(block_spin[i] == 'b'); + } + return ab; + }; + + const std::vector spaces_d = guesses_d[0]->subspaces(); + std::vector> abvectors; + for (size_t i = 0; i < 3; ++i) { + abvectors.push_back(make_ab(*mospaces, spaces_d[i])); + } + libtensor::sequence<3, std::vector*> ab_d; + for (size_t i = 0; i < 3; ++i) { + ab_d[i] = &abvectors[i]; + } + + // Make singles list data structure + std::list*, double>> guesspairs; + for (size_t i = 0; i < n_guesses; i++) { + guesspairs.emplace_back(&(asbt3(guesses_d[i])), 0.0); + } + + if (abs(spin_change_twice) != 1){ + throw not_implemented_error("spin_change ==" + + std::to_string(spin_change_twice) + " has not been tested."); + } + + return ea_adc_guess_d(guesspairs, asbt1(d_o), asbt1(d_v), sym_s, a_spin, + restricted, doublet, ab_d, spin_change_twice, + degeneracy_tolerance); +} + +} // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/fill_ea_doubles_guesses.hh b/libadcc_src/fill_ea_doubles_guesses.hh new file mode 100644 index 000000000..4d6c3c072 --- /dev/null +++ b/libadcc_src/fill_ea_doubles_guesses.hh @@ -0,0 +1,53 @@ +// +// Copyright (C) 2020 by the adcc authors +// +// This file is part of adcc. +// +// adcc is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// adcc is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with adcc. If not, see . +// + +#pragma once +#include "Tensor.hh" + +namespace libadcc { + + +/** Fill the passed vector of doubles blocks with doubles guesses using + * the O and V matrices. + * + * + * guesses_d Vectors of guesses, all elements are assumed to be initialised to zero + * and the symmetry is assumed to be properly set up. + * mospaces Mospaces object + * d_o Fock matrix to construct guesses from (occ.) + * d_v Fock matrix to construct guesses from (virt.) + * a_spin If alpha ionization (false: beta) + * restricted Is this a restricted calculation + * doublet Doublet or quartet states (only in case of restricted calculation) + * spin_change_twice Twice the value of the spin change to enforce in an excitation. + * degeneracy_tolerance Tolerance for two entries of the diagonal to be considered + * degenerate, i.e. identical. + * + * \returns The number of guess vectors which have been properly initialised + * (the others are invalid and should be discarded). + */ +size_t fill_ea_doubles_guesses(std::vector> guesses_d, + std::shared_ptr mospaces, + std::shared_ptr d_o, + std::shared_ptr d_v, + bool a_spin, bool restricted, bool doublet, + int spin_change_twice, + scalar_type degeneracy_tolerance); + +} // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/fill_ip_doubles_guesses.cc b/libadcc_src/fill_ip_doubles_guesses.cc new file mode 100644 index 000000000..aa8885d02 --- /dev/null +++ b/libadcc_src/fill_ip_doubles_guesses.cc @@ -0,0 +1,78 @@ +// +// Copyright (C) 2020 by the adcc authors +// +// This file is part of adcc. +// +// adcc is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// adcc is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with adcc. If not, see . +// + +#include "fill_ip_doubles_guesses.hh" +#include "TensorImpl.hh" +#include "guess/ip_adc_guess_d.hh" + +namespace libadcc { + +size_t fill_ip_doubles_guesses(std::vector> guesses_d, + std::shared_ptr mospaces, + std::shared_ptr d_o, + std::shared_ptr d_v, + bool a_spin, bool restricted, bool doublet, + int spin_change_twice, + scalar_type degeneracy_tolerance) { + + size_t n_guesses = guesses_d.size(); + if (n_guesses == 0) return 0; + + // Make a copy of the doubles symmetry + libtensor::block_tensor_ctrl<3, scalar_type> ctrl(asbt3(guesses_d[0])); + libtensor::symmetry<3, scalar_type> sym_s(ctrl.req_const_symmetry().get_bis()); + libtensor::so_copy<3, scalar_type>(ctrl.req_const_symmetry()).perform(sym_s); + + // Make ab pointers object + auto make_ab = [](const MoSpaces& mo, const std::string& space) { + const std::vector& block_spin = mo.map_block_spin.at(space); + std::vector ab; + for (size_t i = 0; i < block_spin.size(); ++i) { + ab.push_back(block_spin[i] == 'b'); + } + return ab; + }; + + const std::vector spaces_d = guesses_d[0]->subspaces(); + std::vector> abvectors; + for (size_t i = 0; i < 3; ++i) { + abvectors.push_back(make_ab(*mospaces, spaces_d[i])); + } + libtensor::sequence<3, std::vector*> ab_d; + for (size_t i = 0; i < 3; ++i) { + ab_d[i] = &abvectors[i]; + } + + // Make singles list data structure + std::list*, double>> guesspairs; + for (size_t i = 0; i < n_guesses; i++) { + guesspairs.emplace_back(&(asbt3(guesses_d[i])), 0.0); + } + + if (abs(spin_change_twice) != 1){ + throw not_implemented_error("spin_change ==" + + std::to_string(spin_change_twice) + " has not been tested."); + } + + return ip_adc_guess_d(guesspairs, asbt1(d_o), asbt1(d_v), sym_s, a_spin, + restricted, doublet, ab_d, spin_change_twice, + degeneracy_tolerance); +} + +} // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/fill_ip_doubles_guesses.hh b/libadcc_src/fill_ip_doubles_guesses.hh new file mode 100644 index 000000000..10bbf1d2d --- /dev/null +++ b/libadcc_src/fill_ip_doubles_guesses.hh @@ -0,0 +1,53 @@ +// +// Copyright (C) 2020 by the adcc authors +// +// This file is part of adcc. +// +// adcc is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// adcc is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with adcc. If not, see . +// + +#pragma once +#include "Tensor.hh" + +namespace libadcc { + + +/** Fill the passed vector of doubles blocks with doubles guesses using + * the O and V matrices. + * + * + * guesses_d Vectors of guesses, all elements are assumed to be initialised to zero + * and the symmetry is assumed to be properly set up. + * mospaces Mospaces object + * d_o Fock matrix to construct guesses from (occ.) + * d_v Fock matrix to construct guesses from (virt.) + * a_spin If alpha ionization (false: beta) + * restricted Is this a restricted calculation + * doublet Doublet or quartet states (only in case of restricted calculation) + * spin_change_twice Twice the value of the spin change to enforce in an excitation. + * degeneracy_tolerance Tolerance for two entries of the diagonal to be considered + * degenerate, i.e. identical. + * + * \returns The number of guess vectors which have been properly initialised + * (the others are invalid and should be discarded). + */ +size_t fill_ip_doubles_guesses(std::vector> guesses_d, + std::shared_ptr mospaces, + std::shared_ptr d_o, + std::shared_ptr d_v, + bool a_spin, bool restricted, bool doublet, + int spin_change_twice, + scalar_type degeneracy_tolerance); + +} // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/guess/ea_adc_guess_d.cc b/libadcc_src/guess/ea_adc_guess_d.cc new file mode 100644 index 000000000..9f432d69c --- /dev/null +++ b/libadcc_src/guess/ea_adc_guess_d.cc @@ -0,0 +1,484 @@ +#include "ea_adc_guess_d.hh" +#include "../exceptions.hh" + +// Change visibility of libtensor singletons to public +#pragma GCC visibility push(default) +#include +#include +#include +#include +#include +#include +#pragma GCC visibility pop + +namespace libadcc { + +// TODO This file definitely needs a cleanup. + +using namespace libtensor; +using libtensor::index; + +/** \brief Element type for guess vectors + **/ +template +struct guess_element { + libtensor::index bidx; //!< Block index + libtensor::index idx; //!< In block index + double coeff; //!< Coefficient; + + guess_element(const libtensor::index& bidx_, const libtensor::index& idx_, + const double& coeff_) + : bidx(bidx_), idx(idx_), coeff(coeff_) {} +}; + +/** \brief Base class for guess formation **/ +template +class index_handler { + public: + protected: + libtensor::sequence*> m_ab; //!< Alpha-beta block markers + + private: + libtensor::sequence m_na; //!< Number of alpha spin blocks + + public: + /** \brief Constructor + \param ab Alpha-beta block markers (for N orbital spaces) + \param sym Symmetry of guess vectors + \param ms Spin multiplicity + **/ + index_handler(const libtensor::sequence*>& ab) + : m_ab(ab), m_na(0) { + for (size_t i = 0; i < N; i++) { + for (size_t j = 0; j < m_ab[i]->size(); j++) { + if (!m_ab[i]->at(j)) m_na[i]++; + } + } + } + + /** \brief Calculates the spin projection \f$ m_s \f$ of the block. + \param bidx Block index + \param orb_type Orbital type per dim (true = occupied) + \return -1 or +1 for ionization of an alpha or beta electron + */ + int get_spin_proj(const libtensor::mask& orb_type, + const libtensor::index& bidx) const { + for (size_t i = 0; i < N; i++) { + if (bidx[i] > m_ab[i]->size()) { + throw runtime_error("Block index exceeds dim"); + } + } + + int ms = 0; + for (size_t i = 0; i < N; i++) { + // Left side is true if orbital is occ. + // Right side is true if orbital has beta spin + // Hence it is true for occ. beta orbitals and virt. alpha orbitals + if (orb_type[i] == m_ab[i]->at(bidx[i])) + ms += 1; + else + ms -= 1; + } + return ms; + } + + /** \brief Split block index into spatial part and spin part + \param bidx Input block index + \param sp Spin index (alpha = false, beta = true) + \param sbidx Spatial block index + **/ + void split_block_index(const libtensor::index& bidx, libtensor::mask& sp, + libtensor::index& sbidx) const { + for (size_t i = 0; i < N; i++) { + sp[i] = m_ab[i]->at(bidx[i]); + sbidx[i] = (sp[i] ? bidx[i] - m_na[i] : bidx[i]); + } + } + + /** \brief Merge spatial part and spin part of block index + \param sp Spin index (alpha = false, beta = true) + \param sbidx Spatial block index + \param bidx Input block index + **/ + void merge_block_index(const libtensor::mask& sp, const libtensor::index& sbidx, + libtensor::index& bidx) const { + for (size_t i = 0; i < N; i++) { + bidx[i] = (sp[i] ? sbidx[i] + m_na[i] : sbidx[i]); + } + } +}; + +namespace { +typedef libtensor::compare4min compare_t; +typedef libtensor::btod_select<1, compare_t>::list_type list1d_t; +typedef libtensor::btod_select<3, compare_t>::list_type list3d_t; +typedef std::list*, double>> list_t; + +/** Determine if occupied indices should be symmetrized */ +void determine_sym(const symmetry<3, double>& sym, bool& sym_v) { + + sym_v = false; + for (symmetry<3, double>::iterator it1 = sym.begin(); it1 != sym.end(); it1++) { + + const symmetry_element_set<3, double>& set = sym.get_subset(it1); + const std::string& id = set.get_id(); + + if (id.compare(se_perm<3, double>::k_sym_type) != 0) continue; + if (set.is_empty()) return; + + typedef symmetry_element_set_adapter<3, double, se_perm<3, double>> adapter_t; + + adapter_t ad(set); + for (adapter_t::iterator it2 = ad.begin(); it2 != ad.end(); it2++) { + + const se_perm<3, double>& el = ad.get_elem(it2); + + const permutation<3>& p = el.get_perm(); + sym_v |= (p[1] == 2 && p[2] == 1); + } + } +} + +/** Determine the spin of the guess vectors */ +unsigned determine_spin(bool restricted, bool doublet) { + + if (restricted) { + if (doublet) return 2; + else return 4; + } else { + return 0; + } +} + +/** Transfers the elements of a 1D list to a 3D list */ +void transfer_elements(const list1d_t& o, const list1d_t& v, + index_group_map_h2p& to, const libtensor::symmetry<3, + double>& sym, const index_handler<3>& base, int dm_s) { + + // Determine symmetry + bool sym_v; // Are the two virtual indices identical + determine_sym(sym, sym_v); + + to.clear(); + + dimensions<3> bidims = sym.get_bis().get_block_index_dims(); + + for (list1d_t::const_iterator ita = o.begin(); ita != o.end(); ita++) { + + for (list1d_t::const_iterator itb = v.begin(); itb != v.end(); itb++) { + + for (list1d_t::const_iterator itc = v.begin(); itc != v.end(); itc++) { + + // Discard element combinations which are not allowed due to the + // permutational symmetry!!! + const index<1>& bidxa = ita->get_block_index(); + const index<1>& idxa = ita->get_in_block_index(); + const index<1>& bidxb = itb->get_block_index(); + const index<1>& idxb = itb->get_in_block_index(); + const index<1>& bidxc = itc->get_block_index(); + const index<1>& idxc = itc->get_in_block_index(); + + if (sym_v && bidxb[0] == bidxc[0] && idxb[0] == idxc[0]) continue; + + double value = ita->get_value() + itb->get_value() + itc->get_value(); + + libtensor::index<3> bidx, idx; + bidx[0] = bidxa[0]; + bidx[1] = bidxb[0]; + bidx[2] = bidxc[0]; + idx[0] = idxa[0]; + idx[1] = idxb[0]; + idx[2] = idxc[0]; + + if (sym_v && bidx[1] > bidx[2]) { + std::swap(bidx[1], bidx[2]); + std::swap(idx[1], idx[2]); + } else if (sym_v && bidx[1] == bidx[2] && idx[1] > idx[2]) { + std::swap(idx[1], idx[2]); + } + + // Ignore blocks where the targeted spin_change is not achieved + mask<3> orb_type; + orb_type[0] = true; + if (base.get_spin_proj(orb_type, bidx) != dm_s) continue; + + // Check if the block is allowed in the symmetry of the guess + orbit<3, double> orb(sym, bidx); + if (!orb.is_allowed()) continue; + + // Find canonical index + abs_index<3> abi(orb.get_acindex(), bidims); + const tensor_transf<3, double>& tr = orb.get_transf(bidx); + bidx = abi.get_index(); + permutation<3> pinv(tr.get_perm(), true); + idx.permute(pinv); + + // Split block index into spin part and spatial part + mask<3> spm; + index<3> spi; + base.split_block_index(bidx, spm, spi); + + to.add_index(value, spm, spi, idx); + } // for itc + } // for itb + } // for ita +} + +size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, + const index_group_h2p& ig, double value, + const symmetry<3, double>& sym, bool a_spin, + bool restricted, bool doublet, index_handler<3>& base) { + bool sym_v; // Are the two occupied indices identical + determine_sym(sym, sym_v); + const unsigned spin = determine_spin(restricted, doublet); // Spin of the symmetry + + if (cur_guess == end) return 0; + + int ms = a_spin ? 1 : -1; + + const index<3>& spidx = ig.get_spatial_bidx(); + const index<3>& idx = ig.get_idx(); + + std::vector>> lv; + + // No specific spin create as many guesses as there are available in the + // index group + if (spin == 0) { + lv.resize(ig.size()); + + // Reform full block indices + size_t i = 0; + std::vector> bidx(ig.size()); + for (index_group_h2p::iterator it = ig.begin(); it != ig.end(); it++, i++) { + base.merge_block_index(ig.get_spin_mask(it), spidx, bidx[i]); + } + + if (ig.size() == 1) { + + static double coeff[1] = {1.0}; + for (size_t i = 0; i < 1; i++) { + for (size_t j = 0; j < 1; j++) + lv[j].push_back(guess_element<3>(bidx[i], idx, coeff[j])); + } + } else if (ig.size() == 3) { + static const double coeff[3][3] = { + // in case of ms == 1 (alpha attachment) + // aaa bab bba + // and in case of ms == -1 (beta attachment) + // bbb aba aab + { 1.0, -1.0, -1.0}, // quartet + { 0.0, -1.0, 1.0}, // doublet 1 + { -2.0, -1.0, -1.0}}; // doublet 2 + + if (ms == 1) { // alpha attachment + for (size_t i = 0; i < 3; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); + } + } else { // beta attachment + for (size_t i = 0; i < 3; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[2-j], idx, coeff[i][j])); + } + } + } else { + // Form spin elements + for (size_t i = 0; i < ig.size(); i++) + lv[i].push_back(guess_element<3>(bidx[i], idx, 1.0)); + } + } + else if (spin == 2) { + // Reform full block indices + size_t i = 0; + std::vector> bidx(ig.size()); + for (index_group_h2p::iterator it = ig.begin(); it != ig.end(); it++, i++) { + base.merge_block_index(ig.get_spin_mask(it), spidx, bidx[i]); + } + + if (ig.size() == 1) { + lv.resize(1); + + static double coeff[1] = {1.0}; + for (size_t i = 0; i < 1; i++) { + for (size_t j = 0; j < 1; j++) + lv[j].push_back(guess_element<3>(bidx[i], idx, coeff[j])); + } + } else if (ig.size() == 3) { + lv.resize(2); + + static const double coeff[2][3] = { + // in case of ms == 1 (alpha attachment) + // aaa bab bba + // and in case of ms == -1 (beta attachment) + // bbb aba aab + { 0.0, -1.0, 1.0}, // doublet 1 + { -2.0, -1.0, -1.0}}; // doublet 2 + + if (ms == 1) { // alpha attachment + for (size_t i = 0; i < 2; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); + } + } else { // beta attachment + for (size_t i = 0; i < 2; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[2-j], idx, coeff[i][j])); + } + } + } else { + // Form spin elements + lv.resize(ig.size()); + for (size_t i = 0; i < ig.size(); i++) { + lv[i].push_back(guess_element<3>(bidx[i], idx, 1.0)); + } + } + } else if (spin == 4) { + // Reform full block indices + size_t i = 0; + std::vector> bidx(ig.size()); + for (index_group_h2p::iterator it = ig.begin(); it != ig.end(); it++, i++) { + base.merge_block_index(ig.get_spin_mask(it), spidx, bidx[i]); + } + + if (ig.size() == 3) { + lv.resize(1); + + static const double coeff[1][3] = { + // in case of ms == 1 (alpha attachment) + // aaa bab bba + // and in case of ms == -1 (beta attachment) + // bbb aba aab + { 1.0, -1.0, -1.0}}; // quartet + + if (ms == 1) { // alpha attachment + for (size_t i = 0; i < 1; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); + } + } else { // beta attachment + for (size_t i = 0; i < 1; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[2-j], idx, coeff[i][j])); + } + } + } else { + // Form spin elements + lv.resize(ig.size()); + for (size_t i = 0; i < ig.size(); i++) { + lv[i].push_back(guess_element<3>(bidx[i], idx, 1.0)); + } + } + } + + + size_t i = 0; + for (; i < lv.size() && cur_guess != end; i++, cur_guess++) { + libtensor::btensor<3, double>& bt = *(cur_guess->first); + { // Setup up the symmetry + libtensor::block_tensor_wr_ctrl<3, double> ctrl(bt); + ctrl.req_zero_all_blocks(); + libtensor::symmetry<3, double>& sym_to = ctrl.req_symmetry(); + libtensor::so_copy<3, double>(sym).perform(sym_to); + } + + // Set the elements + libtensor::btod_set_elem<3> set_op; + for (auto it = lv[i].begin(); it != lv[i].end(); it++) { + set_op.perform(bt, it->bidx, it->idx, it->coeff); + } + + // Normalise + double norm = libtensor::btod_dotprod<3>(bt, bt).calculate(); + if (norm != 1.0) { + libtensor::btod_scale<3>(bt, 1.0 / sqrt(norm)).perform(); + } + cur_guess->second = value; + } + return i; +} + +} // namespace + +size_t ea_adc_guess_d(std::list*, double>>& va, + libtensor::btensor_i<1, double>& d_o, + libtensor::btensor_i<1, double>& d_v, + const libtensor::symmetry<3, double>& sym, + bool a_spin, bool restricted, bool doublet, + const libtensor::sequence<3, std::vector*>& ab, + int dm_s, double degeneracy_tolerance) { + + size_t nguesses = va.size(); + if (nguesses == 0) return 0; + + // TODO sym_v should be stored in an adc_guess_base-like object + // Determine symmetry and spin + bool sym_v; // Are the two occupied indices identical + determine_sym(sym, sym_v); + const unsigned spin = determine_spin(restricted, doublet); // Spin of the symmetry + + size_t ns = nguesses; + index_group_map_h2p igm(degeneracy_tolerance, sym_v); + + bool max_reached = false; + // Create empty 1d symmetry to use with btod_select + symmetry<1, double> sym1(d_o.get_bis()), sym2(d_v.get_bis()); + + // Search for smallest elements until we have found enough. + size_t size = 0; + while (size < nguesses && !max_reached) { + + igm.clear(); + size = 0; + + ns *= 2; + list1d_t ilx_o, ilx_v; + btod_select<1, compare_t>(d_o, sym1).perform(ilx_o, ns); + btod_select<1, compare_t>(d_v, sym2).perform(ilx_v, ns); + + max_reached = ilx_o.size() < ns; + + index_handler<3> base(ab); + transfer_elements(ilx_o, ilx_v, igm, sym, base, dm_s); + ilx_o.clear(); + ilx_v.clear(); + + //size++; // we want to have the real size of the index group guesses + // Count the number of elements + if (spin == 0) { + for (index_group_map_h2p::iterator it = igm.begin(); it != igm.end(); it++) { + size += igm.get_group(it).size(); + } + } else if (spin == 2) { + for (index_group_map_h2p::iterator it = igm.begin(); it != igm.end(); it++) { + if (igm.get_group(it).size() == 3) { + size += 2; // two doublets, one quartet + } else if (igm.get_group(it).size() == 1) { + size += 1; // only a doublet in this case + } + } + } else if (spin == 4) { + for (index_group_map_h2p::iterator it = igm.begin(); it != igm.end(); it++) { + if (igm.get_group(it).size() == 3) { + size += 1; // one quartet + } + } + } + } // while + + // Now form the guess vectors + nguesses = 0; + + list_t::iterator guess = va.begin(); + // Loop until list is empty or we have constructed all guesses + index_group_map_h2p::iterator it = igm.begin(); + while (it != igm.end() && guess != va.end()) { + index_handler<3> base(ab); + nguesses += + build_guesses(guess, va.end(), igm.get_group(it), igm.get_value(it), + sym, a_spin, restricted, doublet, base); + it++; + } + + return nguesses; +} +} // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/guess/ea_adc_guess_d.hh b/libadcc_src/guess/ea_adc_guess_d.hh new file mode 100644 index 000000000..9e943696e --- /dev/null +++ b/libadcc_src/guess/ea_adc_guess_d.hh @@ -0,0 +1,31 @@ +#pragma once + +#include "index_group_h2p.hh" + +namespace libadcc { + +/** \brief Forms a list of doubles guess vectors. + Selects the smallest elements from the provided OV matrices (for Koopman's + guess this should be the delta Fock matrix) and combines two of these + elements to form the doubles guesses. + \param va List of doubles-value pairs to initialize. + \param d_o Fock matrix to construct guesses from (occ.) + \param d_v Fock matrix to construct guesses from. (virt.) + \param sym Symmetry of guess vectors. + \param a_spin If alpha ionization (false: beta) + \param restricted Is this a restricted calculation + \param doublet Doublet or quartet states (only in case of restricted + calculation) + \param ab Alpha/beta spin blocks of occupied orbitals. + \param dm_s Delta m_s, spin-change twice + \return Number of guess vectors created + **/ +size_t ea_adc_guess_d(std::list*, double>>& va, + libtensor::btensor_i<1, double>& d_o, + libtensor::btensor_i<1, double>& d_v, + const libtensor::symmetry<3, double>& sym, + bool a_spin, bool restricted, bool doublet, + const libtensor::sequence<3, std::vector*>& ab, + int dm_s, double degeneracy_tolerance); + +} // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/guess/index_group_h2p.cc b/libadcc_src/guess/index_group_h2p.cc new file mode 100644 index 000000000..1376aee6f --- /dev/null +++ b/libadcc_src/guess/index_group_h2p.cc @@ -0,0 +1,84 @@ +#include "index_group_h2p.hh" +#include "../exceptions.hh" +#include + +namespace libadcc { + +using namespace libtensor; +using libtensor::index; + +libtensor::mask<3> index_group_h2p::get_spin_mask(size_t sp) const { + if (m_s.count(sp) == 0) { + throw runtime_error("Could not find spin state sp ==" + std::to_string(sp) + "."); + } + return compute_spin_mask(sp); +} + +size_t index_group_h2p::compute_spin(const mask<3>& spm) { + + size_t s = 0; + for (size_t i = 0; i < 3; i++) s = s * 2 + (spm[i] ? 1 : 0); + + return s; +} + +mask<3> index_group_h2p::compute_spin_mask(size_t sp) { + + mask<3> m; + size_t i = 0, curbit = 1 << 2; + while (sp != 0 && i < 3) { + m[i++] = (sp & curbit); + curbit >>= 1; + } + return m; +} + +void index_group_map_h2p::add_index(double val, mask<3> spm, index<3> spidx, index<3> idx) { + + find_canonical_index(spm, spidx, idx); + + // Loop over group map and look for similar value + std::multimap::iterator it = m_idxmap.begin(); + for (; it != m_idxmap.end(); it++) { + if (fabs(val - it->first) < m_thresh) break; + } + + // Try to add element to index groups which belong to similar + // values + bool added = false; + while (it != m_idxmap.end() && fabs(val - it->first) < m_thresh && !added) { + + index_group_h2p& grp = it->second; + if (spidx == grp.get_spatial_bidx() && idx == grp.get_idx()) { + grp.add(spm); + added = true; + } + it++; + } + + // If no index group found start a new one. + if (!added) { + std::multimap::iterator ic = m_idxmap.insert( + std::pair(val, index_group_h2p(spidx, idx))); + ic->second.add(spm); + } +} + +void index_group_map_h2p::find_canonical_index(mask<3>& m, index<3>& spidx, + index<3>& idx) const { + + if (m_sym_v) { + if (spidx[1] == spidx[2]) { + if (idx[1] > idx[2]) { + std::swap(idx[1], idx[2]); + std::swap(m[1], m[2]); + } + } else if (spidx[1] > spidx[2]) { + std::swap(spidx[1], spidx[2]); + std::swap(idx[1], idx[2]); + std::swap(m[1], m[2]); + } + } +} + +} // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/guess/index_group_h2p.hh b/libadcc_src/guess/index_group_h2p.hh new file mode 100644 index 000000000..47b353581 --- /dev/null +++ b/libadcc_src/guess/index_group_h2p.hh @@ -0,0 +1,164 @@ +#pragma once +// Change visibility of libtensor singletons to public +#pragma GCC visibility push(default) +#include +#pragma GCC visibility pop +#include +#include + +namespace libadcc { + +/** \brief Group of 3D block tensor elements with common spatial index + An index group is constructed by passing the spatial block index and + in-block index of a 3D block tensor element. Those two indices define + the index group. + The spin states belonging to the index group can be added using the + functions + \code + void add(const libtensor::mask<3> &); + \endcode + \code + void add(size_t); + \endcode + **/ +class index_group_h2p { + public: + typedef std::set::const_iterator iterator; + + enum { + aaa = 0, + aab = 1, + aba = 2, + abb = 3, + baa = 4, + bab = 5, + bba = 6, + bbb = 7, + }; + + private: + libtensor::index<3> m_spidx; + libtensor::index<3> m_idx; + std::set m_s; + + public: + /** \brief Constructor + \param spidx Spatial block index. + \param idx In-block index + **/ + index_group_h2p(const libtensor::index<3>& spidx, const libtensor::index<3>& idx) + : m_spidx(spidx), m_idx(idx) {} + + /** \brief Add spin state to index group + \param s Spin states index (see enum) + **/ + void add(size_t s) { m_s.insert(s); } + + /** \brief Add spin state to index group + \param spm Mask representing the spin states (beta == true) + **/ + void add(const libtensor::mask<3>& spm) { add(compute_spin(spm)); } + + /** \brief Return in-block index of index group + **/ + const libtensor::index<3>& get_idx() const { return m_idx; } + + /** \brief Return spatial block index of index group + **/ + const libtensor::index<3>& get_spatial_bidx() const { return m_spidx; } + + /** \brief Check if the spin state exists for index group + **/ + bool has_spin_state(size_t sp) const { return m_s.find(sp) != m_s.end(); } + + /** \brief Check if the spin state exists for index group + **/ + bool has_spin_state(const libtensor::mask<3>& spm) const { + return has_spin_state(compute_spin(spm)); + } + + /** \brief Return the number of spin states + **/ + size_t size() const { return m_s.size(); } + + /** \brief STL-style iterator to the start of the list of spin states + **/ + iterator begin() const { return m_s.begin(); } + + /** \brief STL-style iterator to the end of the list of spin states + **/ + iterator end() const { return m_s.end(); } + + /** \brief Get current spin state + **/ + size_t get_spin_state(iterator it) const { return *it; } + + /** \brief Get spin state as mask + **/ + libtensor::mask<3> get_spin_mask(size_t sp) const; + + /** \brief Get current spin state as mask + **/ + libtensor::mask<3> get_spin_mask(iterator it) const { return get_spin_mask(*it); } + + private: + static size_t compute_spin(const libtensor::mask<3>& spm); + static libtensor::mask<3> compute_spin_mask(size_t sp); +}; + +/** \brief Map of (value, index group) pairs + \sa ea_adc_guess_d, ea_adc_guess_d + **/ +class index_group_map_h2p { + public: + typedef std::multimap::const_iterator iterator; + + private: + bool m_sym_v; //!< Permutational anti-symmetry of virt indices + double m_thresh; //!< Threshold for identical values + + std::multimap m_idxmap; + + public: + /** \brief Constructor + \param thresh Threshold for identical values + \param sym_v Virt. indices have perm. anti-symmetry + */ + index_group_map_h2p(double thresh, bool sym_v = true) + : m_sym_v(sym_v), m_thresh(thresh) {} + + /** \brief Remove all elements from list + **/ + void clear() { m_idxmap.clear(); } + + /** \brief Add an index to the map + \param val Value assigned to the index + \param spm Spin state mask + \param spidx Spatial block index + \param idx In-block index + **/ + void add_index(double val, libtensor::mask<3> spm, libtensor::index<3> spidx, + libtensor::index<3> idx); + + /** \brief STL-style iterator to first element + **/ + iterator begin() const { return m_idxmap.begin(); } + + /** \brief STL-style iterator to end + **/ + iterator end() const { return m_idxmap.end(); } + + /** \brief Return the value at the current position + **/ + double get_value(iterator it) const { return it->first; } + + /** \brief Return the index group at the current position + **/ + const index_group_h2p& get_group(iterator it) const { return it->second; } + + private: + void find_canonical_index(libtensor::mask<3>& m, libtensor::index<3>& spidx, + libtensor::index<3>& idx) const; +}; + +} // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/guess/index_group_p2h.cc b/libadcc_src/guess/index_group_p2h.cc new file mode 100644 index 000000000..facc992c4 --- /dev/null +++ b/libadcc_src/guess/index_group_p2h.cc @@ -0,0 +1,84 @@ +#include "index_group_p2h.hh" +#include "../exceptions.hh" +#include + +namespace libadcc { + +using namespace libtensor; +using libtensor::index; + +libtensor::mask<3> index_group_p2h::get_spin_mask(size_t sp) const { + if (m_s.count(sp) == 0) { + throw runtime_error("Could not find spin state sp ==" + std::to_string(sp) + "."); + } + return compute_spin_mask(sp); +} + +size_t index_group_p2h::compute_spin(const mask<3>& spm) { + + size_t s = 0; + for (size_t i = 0; i < 3; i++) s = s * 2 + (spm[i] ? 1 : 0); + + return s; +} + +mask<3> index_group_p2h::compute_spin_mask(size_t sp) { + + mask<3> m; + size_t i = 0, curbit = 1 << 2; + while (sp != 0 && i < 3) { + m[i++] = (sp & curbit); + curbit >>= 1; + } + return m; +} + +void index_group_map_p2h::add_index(double val, mask<3> spm, index<3> spidx, index<3> idx) { + + find_canonical_index(spm, spidx, idx); + + // Loop over group map and look for similar value + std::multimap::iterator it = m_idxmap.begin(); + for (; it != m_idxmap.end(); it++) { + if (fabs(val - it->first) < m_thresh) break; + } + + // Try to add element to index groups which belong to similar + // values + bool added = false; + while (it != m_idxmap.end() && fabs(val - it->first) < m_thresh && !added) { + + index_group_p2h& grp = it->second; + if (spidx == grp.get_spatial_bidx() && idx == grp.get_idx()) { + grp.add(spm); + added = true; + } + it++; + } + + // If no index group found start a new one. + if (!added) { + std::multimap::iterator ic = m_idxmap.insert( + std::pair(val, index_group_p2h(spidx, idx))); + ic->second.add(spm); + } +} + +void index_group_map_p2h::find_canonical_index(mask<3>& m, index<3>& spidx, + index<3>& idx) const { + + if (m_sym_o) { + if (spidx[0] == spidx[1]) { + if (idx[0] > idx[1]) { + std::swap(idx[0], idx[1]); + std::swap(m[0], m[1]); + } + } else if (spidx[0] > spidx[1]) { + std::swap(spidx[0], spidx[1]); + std::swap(idx[0], idx[1]); + std::swap(m[0], m[1]); + } + } +} + +} // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/guess/index_group_p2h.hh b/libadcc_src/guess/index_group_p2h.hh new file mode 100644 index 000000000..eb5447421 --- /dev/null +++ b/libadcc_src/guess/index_group_p2h.hh @@ -0,0 +1,164 @@ +#pragma once +// Change visibility of libtensor singletons to public +#pragma GCC visibility push(default) +#include +#pragma GCC visibility pop +#include +#include + +namespace libadcc { + +/** \brief Group of 3D block tensor elements with common spatial index + An index group is constructed by passing the spatial block index and + in-block index of a 3D block tensor element. Those two indices define + the index group. + The spin states belonging to the index group can be added using the + functions + \code + void add(const libtensor::mask<3> &); + \endcode + \code + void add(size_t); + \endcode + **/ +class index_group_p2h { + public: + typedef std::set::const_iterator iterator; + + enum { + aaa = 0, + aab = 1, + aba = 2, + abb = 3, + baa = 4, + bab = 5, + bba = 6, + bbb = 7, + }; + + private: + libtensor::index<3> m_spidx; + libtensor::index<3> m_idx; + std::set m_s; + + public: + /** \brief Constructor + \param spidx Spatial block index. + \param idx In-block index + **/ + index_group_p2h(const libtensor::index<3>& spidx, const libtensor::index<3>& idx) + : m_spidx(spidx), m_idx(idx) {} + + /** \brief Add spin state to index group + \param s Spin states index (see enum) + **/ + void add(size_t s) { m_s.insert(s); } + + /** \brief Add spin state to index group + \param spm Mask representing the spin states (beta == true) + **/ + void add(const libtensor::mask<3>& spm) { add(compute_spin(spm)); } + + /** \brief Return in-block index of index group + **/ + const libtensor::index<3>& get_idx() const { return m_idx; } + + /** \brief Return spatial block index of index group + **/ + const libtensor::index<3>& get_spatial_bidx() const { return m_spidx; } + + /** \brief Check if the spin state exists for index group + **/ + bool has_spin_state(size_t sp) const { return m_s.find(sp) != m_s.end(); } + + /** \brief Check if the spin state exists for index group + **/ + bool has_spin_state(const libtensor::mask<3>& spm) const { + return has_spin_state(compute_spin(spm)); + } + + /** \brief Return the number of spin states + **/ + size_t size() const { return m_s.size(); } + + /** \brief STL-style iterator to the start of the list of spin states + **/ + iterator begin() const { return m_s.begin(); } + + /** \brief STL-style iterator to the end of the list of spin states + **/ + iterator end() const { return m_s.end(); } + + /** \brief Get current spin state + **/ + size_t get_spin_state(iterator it) const { return *it; } + + /** \brief Get spin state as mask + **/ + libtensor::mask<3> get_spin_mask(size_t sp) const; + + /** \brief Get current spin state as mask + **/ + libtensor::mask<3> get_spin_mask(iterator it) const { return get_spin_mask(*it); } + + private: + static size_t compute_spin(const libtensor::mask<3>& spm); + static libtensor::mask<3> compute_spin_mask(size_t sp); +}; + +/** \brief Map of (value, index group) pairs + \sa ip_adc_guess_d, ip_adc_guess_d + **/ +class index_group_map_p2h { + public: + typedef std::multimap::const_iterator iterator; + + private: + bool m_sym_o; //!< Permutational anti-symmetry of occ indices + double m_thresh; //!< Threshold for identical values + + std::multimap m_idxmap; + + public: + /** \brief Constructor + \param thresh Threshold for identical values + \param sym_o Occ. indices have perm. anti-symmetry + */ + index_group_map_p2h(double thresh, bool sym_o = true) + : m_sym_o(sym_o), m_thresh(thresh) {} + + /** \brief Remove all elements from list + **/ + void clear() { m_idxmap.clear(); } + + /** \brief Add an index to the map + \param val Value assigned to the index + \param spm Spin state mask + \param spidx Spatial block index + \param idx In-block index + **/ + void add_index(double val, libtensor::mask<3> spm, libtensor::index<3> spidx, + libtensor::index<3> idx); + + /** \brief STL-style iterator to first element + **/ + iterator begin() const { return m_idxmap.begin(); } + + /** \brief STL-style iterator to end + **/ + iterator end() const { return m_idxmap.end(); } + + /** \brief Return the value at the current position + **/ + double get_value(iterator it) const { return it->first; } + + /** \brief Return the index group at the current position + **/ + const index_group_p2h& get_group(iterator it) const { return it->second; } + + private: + void find_canonical_index(libtensor::mask<3>& m, libtensor::index<3>& spidx, + libtensor::index<3>& idx) const; +}; + +} // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/guess/ip_adc_guess_d.cc b/libadcc_src/guess/ip_adc_guess_d.cc new file mode 100644 index 000000000..328b06d46 --- /dev/null +++ b/libadcc_src/guess/ip_adc_guess_d.cc @@ -0,0 +1,485 @@ +#include "ip_adc_guess_d.hh" +#include "../exceptions.hh" + +// Change visibility of libtensor singletons to public +#pragma GCC visibility push(default) +#include +#include +#include +#include +#include +#include +#pragma GCC visibility pop + +namespace libadcc { + +// TODO This file definitely needs a cleanup. + +using namespace libtensor; +using libtensor::index; + +/** \brief Element type for guess vectors + **/ +template +struct guess_element { + libtensor::index bidx; //!< Block index + libtensor::index idx; //!< In block index + double coeff; //!< Coefficient; + + guess_element(const libtensor::index& bidx_, const libtensor::index& idx_, + const double& coeff_) + : bidx(bidx_), idx(idx_), coeff(coeff_) {} +}; + +/** \brief Base class for guess formation **/ +template +class index_handler { + public: + protected: + libtensor::sequence*> m_ab; //!< Alpha-beta block markers + + private: + libtensor::sequence m_na; //!< Number of alpha spin blocks + + public: + /** \brief Constructor + \param ab Alpha-beta block markers (for N orbital spaces) + \param sym Symmetry of guess vectors + \param ms Spin multiplicity + **/ + index_handler(const libtensor::sequence*>& ab) + : m_ab(ab), m_na(0) { + for (size_t i = 0; i < N; i++) { + for (size_t j = 0; j < m_ab[i]->size(); j++) { + if (!m_ab[i]->at(j)) m_na[i]++; + } + } + } + + /** \brief Calculates the spin projection \f$ m_s \f$ of the block. + \param bidx Block index + \param orb_type Orbital type per dim (true = occupied) + \return -1 or +1 for ionization of an alpha or beta electron + */ + int get_spin_proj(const libtensor::mask& orb_type, + const libtensor::index& bidx) const { + for (size_t i = 0; i < N; i++) { + if (bidx[i] > m_ab[i]->size()) { + throw runtime_error("Block index exceeds dim"); + } + } + + int ms = 0; + for (size_t i = 0; i < N; i++) { + // Left side is true if orbital is occ. + // Right side is true if orbital has beta spin + // Hence it is true for occ. beta orbitals and virt. alpha orbitals + if (orb_type[i] == m_ab[i]->at(bidx[i])) + ms += 1; + else + ms -= 1; + } + return ms; + } + + /** \brief Split block index into spatial part and spin part + \param bidx Input block index + \param sp Spin index (alpha = false, beta = true) + \param sbidx Spatial block index + **/ + void split_block_index(const libtensor::index& bidx, libtensor::mask& sp, + libtensor::index& sbidx) const { + for (size_t i = 0; i < N; i++) { + sp[i] = m_ab[i]->at(bidx[i]); + sbidx[i] = (sp[i] ? bidx[i] - m_na[i] : bidx[i]); + } + } + + /** \brief Merge spatial part and spin part of block index + \param sp Spin index (alpha = false, beta = true) + \param sbidx Spatial block index + \param bidx Input block index + **/ + void merge_block_index(const libtensor::mask& sp, const libtensor::index& sbidx, + libtensor::index& bidx) const { + for (size_t i = 0; i < N; i++) { + bidx[i] = (sp[i] ? sbidx[i] + m_na[i] : sbidx[i]); + } + } +}; + +namespace { +typedef libtensor::compare4min compare_t; +typedef libtensor::btod_select<1, compare_t>::list_type list1d_t; +typedef libtensor::btod_select<3, compare_t>::list_type list3d_t; +typedef std::list*, double>> list_t; + +/** Determine if occupied indices should be symmetrized */ +void determine_sym(const symmetry<3, double>& sym, bool& sym_o) { + + sym_o = false; + for (symmetry<3, double>::iterator it1 = sym.begin(); it1 != sym.end(); it1++) { + + const symmetry_element_set<3, double>& set = sym.get_subset(it1); + const std::string& id = set.get_id(); + + if (id.compare(se_perm<3, double>::k_sym_type) != 0) continue; + if (set.is_empty()) return; + + typedef symmetry_element_set_adapter<3, double, se_perm<3, double>> adapter_t; + + adapter_t ad(set); + for (adapter_t::iterator it2 = ad.begin(); it2 != ad.end(); it2++) { + + const se_perm<3, double>& el = ad.get_elem(it2); + + const permutation<3>& p = el.get_perm(); + sym_o |= (p[0] == 1 && p[1] == 0); + } + } +} + +/** Determine the spin of the guess vectors */ +unsigned determine_spin(bool restricted, bool doublet) { + + if (restricted) { + if (doublet) return 2; + else return 4; + } else { + return 0; + } +} + +/** Transfers the elements of a 1D list to a 3D list */ +void transfer_elements(const list1d_t& o, const list1d_t& v, + index_group_map_p2h& to, const libtensor::symmetry<3, + double>& sym, const index_handler<3>& base, int dm_s) { + + // Determine symmetry + bool sym_o; // Are the two occupied indices identical + determine_sym(sym, sym_o); + + to.clear(); + + dimensions<3> bidims = sym.get_bis().get_block_index_dims(); + + for (list1d_t::const_iterator ita = o.begin(); ita != o.end(); ita++) { + + for (list1d_t::const_iterator itb = o.begin(); itb != o.end(); itb++) { + + for (list1d_t::const_iterator itc = v.begin(); itc != v.end(); itc++) { + + // Discard element combinations which are not allowed due to the + // permutational symmetry!!! + const index<1>& bidxa = ita->get_block_index(); + const index<1>& idxa = ita->get_in_block_index(); + const index<1>& bidxb = itb->get_block_index(); + const index<1>& idxb = itb->get_in_block_index(); + const index<1>& bidxc = itc->get_block_index(); + const index<1>& idxc = itc->get_in_block_index(); + + if (sym_o && bidxa[0] == bidxb[0] && idxa[0] == idxb[0]) continue; + + double value = ita->get_value() + itb->get_value() + itc->get_value(); + + libtensor::index<3> bidx, idx; + bidx[0] = bidxa[0]; + bidx[1] = bidxb[0]; + bidx[2] = bidxc[0]; + idx[0] = idxa[0]; + idx[1] = idxb[0]; + idx[2] = idxc[0]; + + if (sym_o && bidx[0] > bidx[1]) { + std::swap(bidx[0], bidx[1]); + std::swap(idx[0], idx[1]); + } else if (sym_o && bidx[0] == bidx[1] && idx[0] > idx[1]) { + std::swap(idx[0], idx[1]); + } + + // Ignore blocks where the targeted spin_change is not achieved + mask<3> orb_type; + orb_type[0] = true; + orb_type[1] = true; + if (base.get_spin_proj(orb_type, bidx) != dm_s) continue; + + // Check if the block is allowed in the symmetry of the guess + orbit<3, double> orb(sym, bidx); + if (!orb.is_allowed()) continue; + + // Find canonical index + abs_index<3> abi(orb.get_acindex(), bidims); + const tensor_transf<3, double>& tr = orb.get_transf(bidx); + bidx = abi.get_index(); + permutation<3> pinv(tr.get_perm(), true); + idx.permute(pinv); + + // Split block index into spin part and spatial part + mask<3> spm; + index<3> spi; + base.split_block_index(bidx, spm, spi); + + to.add_index(value, spm, spi, idx); + } // for itc + } // for itb + } // for ita +} + +size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, + const index_group_p2h& ig, double value, + const symmetry<3, double>& sym, bool a_spin, + bool restricted, bool doublet, index_handler<3>& base) { + bool sym_o; // Are the two occupied indices identical + determine_sym(sym, sym_o); + const unsigned spin = determine_spin(restricted, doublet); // Spin of the symmetry + + if (cur_guess == end) return 0; + + int ms = a_spin ? -1 : 1; + + const index<3>& spidx = ig.get_spatial_bidx(); + const index<3>& idx = ig.get_idx(); + + std::vector>> lv; + + // No specific spin create as many guesses as there are available in the + // index group + if (spin == 0) { + lv.resize(ig.size()); + + // Reform full block indices + size_t i = 0; + std::vector> bidx(ig.size()); + for (index_group_p2h::iterator it = ig.begin(); it != ig.end(); it++, i++) { + base.merge_block_index(ig.get_spin_mask(it), spidx, bidx[i]); + } + + if (ig.size() == 1) { + + static double coeff[1] = {1.0}; + for (size_t i = 0; i < 1; i++) { + for (size_t j = 0; j < 1; j++) + lv[j].push_back(guess_element<3>(bidx[i], idx, coeff[j])); + } + } else if (ig.size() == 3) { + static const double coeff[3][3] = { + // in case of ms == -1 (alpha ionization) + // aaa abb bab + // and in case of ms == 1 (beta ionization) + // bbb baa aba + { 1.0, -1.0, -1.0}, // quartet + { 0.0, -1.0, 1.0}, // doublet 1 + { -2.0, -1.0, -1.0}}; // doublet 2 + + if (ms == -1) { // alpha ionization + for (size_t i = 0; i < 3; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); + } + } else { // beta ionization + for (size_t i = 0; i < 3; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[2-j], idx, coeff[i][j])); + } + } + } else { + // Form spin elements + for (size_t i = 0; i < ig.size(); i++) + lv[i].push_back(guess_element<3>(bidx[i], idx, 1.0)); + } + } + else if (spin == 2) { + // Reform full block indices + size_t i = 0; + std::vector> bidx(ig.size()); + for (index_group_p2h::iterator it = ig.begin(); it != ig.end(); it++, i++) { + base.merge_block_index(ig.get_spin_mask(it), spidx, bidx[i]); + } + + if (ig.size() == 1) { + lv.resize(1); + + static double coeff[1] = {1.0}; + for (size_t i = 0; i < 1; i++) { + for (size_t j = 0; j < 1; j++) + lv[j].push_back(guess_element<3>(bidx[i], idx, coeff[j])); + } + } else if (ig.size() == 3) { + lv.resize(2); + + static const double coeff[2][3] = { + // in case of ms == -1 (alpha ionization) + // aaa abb bab + // and in case of ms == 1 (beta ionization) + // bbb baa aba + { 0.0, -1.0, 1.0}, // doublet 1 + { -2.0, -1.0, -1.0}}; // doublet 2 + + if (ms == -1) { // alpha ionization + for (size_t i = 0; i < 2; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); + } + } else { // beta ionization + for (size_t i = 0; i < 2; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[2-j], idx, coeff[i][j])); + } + } + } else { + // Form spin elements + lv.resize(ig.size()); + for (size_t i = 0; i < ig.size(); i++) { + lv[i].push_back(guess_element<3>(bidx[i], idx, 1.0)); + } + } + } else if (spin == 4) { + // Reform full block indices + size_t i = 0; + std::vector> bidx(ig.size()); + for (index_group_p2h::iterator it = ig.begin(); it != ig.end(); it++, i++) { + base.merge_block_index(ig.get_spin_mask(it), spidx, bidx[i]); + } + + if (ig.size() == 3) { + lv.resize(1); + + static const double coeff[1][3] = { + // in case of ms == -1 (alpha ionization) + // aaa abb bab + // and in case of ms == 1 (beta ionization) + // bbb baa aba + { 1.0, -1.0, -1.0}}; // quartet + + if (ms == -1) { // alpha ionization + for (size_t i = 0; i < 1; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); + } + } else { // beta ionization + for (size_t i = 0; i < 1; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[2-j], idx, coeff[i][j])); + } + } + } else { + // Form spin elements + lv.resize(ig.size()); + for (size_t i = 0; i < ig.size(); i++) { + lv[i].push_back(guess_element<3>(bidx[i], idx, 1.0)); + } + } + } + + + size_t i = 0; + for (; i < lv.size() && cur_guess != end; i++, cur_guess++) { + libtensor::btensor<3, double>& bt = *(cur_guess->first); + { // Setup up the symmetry + libtensor::block_tensor_wr_ctrl<3, double> ctrl(bt); + ctrl.req_zero_all_blocks(); + libtensor::symmetry<3, double>& sym_to = ctrl.req_symmetry(); + libtensor::so_copy<3, double>(sym).perform(sym_to); + } + + // Set the elements + libtensor::btod_set_elem<3> set_op; + for (auto it = lv[i].begin(); it != lv[i].end(); it++) { + set_op.perform(bt, it->bidx, it->idx, it->coeff); + } + + // Normalise + double norm = libtensor::btod_dotprod<3>(bt, bt).calculate(); + if (norm != 1.0) { + libtensor::btod_scale<3>(bt, 1.0 / sqrt(norm)).perform(); + } + cur_guess->second = value; + } + return i; +} + +} // namespace + +size_t ip_adc_guess_d(std::list*, double>>& va, + libtensor::btensor_i<1, double>& d_o, + libtensor::btensor_i<1, double>& d_v, + const libtensor::symmetry<3, double>& sym, + bool a_spin, bool restricted, bool doublet, + const libtensor::sequence<3, std::vector*>& ab, + int dm_s, double degeneracy_tolerance) { + + size_t nguesses = va.size(); + if (nguesses == 0) return 0; + + // TODO sym_o should be stored in an adc_guess_base-like object + // Determine symmetry and spin + bool sym_o; // Are the two occupied indices identical + determine_sym(sym, sym_o); + const unsigned spin = determine_spin(restricted, doublet); // Spin of the symmetry + + size_t ns = nguesses; + index_group_map_p2h igm(degeneracy_tolerance, sym_o); + + bool max_reached = false; + // Create empty 1d symmetry to use with btod_select + symmetry<1, double> sym1(d_o.get_bis()), sym2(d_v.get_bis()); + + // Search for smallest elements until we have found enough. + size_t size = 0; + while (size < nguesses && !max_reached) { + + igm.clear(); + size = 0; + + ns *= 2; + list1d_t ilx_o, ilx_v; + btod_select<1, compare_t>(d_o, sym1).perform(ilx_o, ns); + btod_select<1, compare_t>(d_v, sym2).perform(ilx_v, ns); + + max_reached = ilx_o.size() < ns; + + index_handler<3> base(ab); + transfer_elements(ilx_o, ilx_v, igm, sym, base, dm_s); + ilx_o.clear(); + ilx_v.clear(); + + //size++; // we want to have the real size of the index group guesses + // Count the number of elements + if (spin == 0) { + for (index_group_map_p2h::iterator it = igm.begin(); it != igm.end(); it++) { + size += igm.get_group(it).size(); + } + } else if (spin == 2) { + for (index_group_map_p2h::iterator it = igm.begin(); it != igm.end(); it++) { + if (igm.get_group(it).size() == 3) { + size += 2; // two doublets, one quartet + } else if (igm.get_group(it).size() == 1) { + size += 1; // only a doublet in this case + } + } + } else if (spin == 4) { + for (index_group_map_p2h::iterator it = igm.begin(); it != igm.end(); it++) { + if (igm.get_group(it).size() == 3) { + size += 1; // one quartet + } + } + } + } // while + + // Now form the guess vectors + nguesses = 0; + + list_t::iterator guess = va.begin(); + // Loop until list is empty or we have constructed all guesses + index_group_map_p2h::iterator it = igm.begin(); + while (it != igm.end() && guess != va.end()) { + index_handler<3> base(ab); + nguesses += + build_guesses(guess, va.end(), igm.get_group(it), igm.get_value(it), + sym, a_spin, restricted, doublet, base); + it++; + } + + return nguesses; +} +} // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/guess/ip_adc_guess_d.hh b/libadcc_src/guess/ip_adc_guess_d.hh new file mode 100644 index 000000000..528d5e3e1 --- /dev/null +++ b/libadcc_src/guess/ip_adc_guess_d.hh @@ -0,0 +1,31 @@ +#pragma once + +#include "index_group_p2h.hh" + +namespace libadcc { + +/** \brief Forms a list of doubles guess vectors. + Selects the smallest elements from the provided OV matrices (for Koopman's + guess this should be the delta Fock matrix) and combines two of these + elements to form the doubles guesses. + \param va List of doubles-value pairs to initialize. + \param d_o Fock matrix to construct guesses from (occ.) + \param d_v Fock matrix to construct guesses from. (virt.) + \param sym Symmetry of guess vectors. + \param a_spin If alpha ionization (false: beta) + \param restricted Is this a restricted calculation + \param doublet Doublet or quartet states (only in case of restricted + calculation) + \param ab Alpha/beta spin blocks of occupied orbitals. + \param dm_s Delta m_s, spin-change twice + \return Number of guess vectors created + **/ +size_t ip_adc_guess_d(std::list*, double>>& va, + libtensor::btensor_i<1, double>& d_o, + libtensor::btensor_i<1, double>& d_v, + const libtensor::symmetry<3, double>& sym, + bool a_spin, bool restricted, bool doublet, + const libtensor::sequence<3, std::vector*>& ab, + int dm_s, double degeneracy_tolerance); + +} // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/pyiface/ExportAdcc.cc b/libadcc_src/pyiface/ExportAdcc.cc index 274ab79ee..69fbf6185 100644 --- a/libadcc_src/pyiface/ExportAdcc.cc +++ b/libadcc_src/pyiface/ExportAdcc.cc @@ -28,6 +28,8 @@ namespace libadcc { void export_AdcMemory(py::module& m); void export_adc_pp(py::module& m); +void export_adc_ip(py::module& m); +void export_adc_ea(py::module& m); void export_HartreeFockProvider(py::module& m); void export_MoIndexTranslation(py::module& m); void export_MoSpaces(py::module& m); @@ -41,6 +43,8 @@ void export_threading(py::module& m); PYBIND11_MODULE(libadcc, m) { libadcc::export_AdcMemory(m); libadcc::export_adc_pp(m); + libadcc::export_adc_ip(m); + libadcc::export_adc_ea(m); libadcc::export_HartreeFockProvider(m); libadcc::export_MoIndexTranslation(m); libadcc::export_MoSpaces(m); diff --git a/libadcc_src/pyiface/export_adc_ea.cc b/libadcc_src/pyiface/export_adc_ea.cc new file mode 100644 index 000000000..83929eb75 --- /dev/null +++ b/libadcc_src/pyiface/export_adc_ea.cc @@ -0,0 +1,56 @@ +// +// Copyright (C) 2019 by the adcc authors +// +// This file is part of adcc. +// +// adcc is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// adcc is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with adcc. If not, see . +// + +#include "../amplitude_vector_enforce_spin_kind.hh" +#include "../fill_ea_doubles_guesses.hh" +#include +#include + +namespace libadcc { + +namespace py = pybind11; +using namespace pybind11::literals; + +void export_adc_ea(py::module& m) { + m.def("amplitude_vector_enforce_spin_kind", &litude_vector_enforce_spin_kind, + "Apply the spin symmetrisation required to make the doubles and higher parts of " + "an amplitude vector consist of components for a particular spin kind only."); + + m.def("fill_ea_doubles_guesses", &fill_ea_doubles_guesses, "guesses_d"_a, + "mospaces"_a, "d_o"_a, "d_v"_a, "a_spin"_a, "restricted"_a, + "doublet"_a, "spin_change_twice"_a, "degeneracy_tolerance"_a, + "Fill the passed vector of doubles blocks with doubles guesses using " + "the O and V matrices, which are the two Fock matrices " + "involved in the doubles block.\n\nguesses_d Vectors of guesses, " + "all elements are assumed to be initialised to zero and the symmetry " + "is assumed to be properly set up.\nmospaces Mospaces object" + "\nd_o Matrix to construct guesses from (occ.)" + "\nd_v Matrix to construct guesses from (virt.)" + "\na_spin If alpha ionization (false: beta)" + "\nrestricted Is this a restricted calculation" + "\ndoublet Doublet or quartet states (only in case of" + "restricted calculation)" + "\nspin_change_twice Twice the value of the spin change to enforce " + "in an excitation.\ndegeneracy_tolerance Tolerance for two entries of " + "the diagonal to be considered degenerate, i.e. identical." + "\nReturns The number of guess vectors which have been properly " + "initialised (the others are invalid and should be discarded)."); +} + +} // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/pyiface/export_adc_ip.cc b/libadcc_src/pyiface/export_adc_ip.cc new file mode 100644 index 000000000..d3454a69c --- /dev/null +++ b/libadcc_src/pyiface/export_adc_ip.cc @@ -0,0 +1,56 @@ +// +// Copyright (C) 2019 by the adcc authors +// +// This file is part of adcc. +// +// adcc is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// adcc is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with adcc. If not, see . +// + +#include "../amplitude_vector_enforce_spin_kind.hh" +#include "../fill_ip_doubles_guesses.hh" +#include +#include + +namespace libadcc { + +namespace py = pybind11; +using namespace pybind11::literals; + +void export_adc_ip(py::module& m) { + m.def("amplitude_vector_enforce_spin_kind", &litude_vector_enforce_spin_kind, + "Apply the spin symmetrisation required to make the doubles and higher parts of " + "an amplitude vector consist of components for a particular spin kind only."); + + m.def("fill_ip_doubles_guesses", &fill_ip_doubles_guesses, "guesses_d"_a, + "mospaces"_a, "d_o"_a, "d_v"_a, "a_spin"_a, "restricted"_a, + "doublet"_a, "spin_change_twice"_a, "degeneracy_tolerance"_a, + "Fill the passed vector of doubles blocks with doubles guesses using " + "the O and V matrices., which are the two Fock matrices " + "involved in the doubles block.\n\nguesses_d Vectors of guesses, " + "all elements are assumed to be initialised to zero and the symmetry " + "is assumed to be properly set up.\nmospaces Mospaces object" + "\nd_o Matrix to construct guesses from (occ.)" + "\nd_v Matrix to construct guesses from (virt.)" + "\na_spin If alpha ionization (false: beta)" + "\nrestricted Is this a restricted calculation" + "\ndoublet Doublet or quartet states (only in case of" + "restricted calculation)" + "\nspin_change_twice Twice the value of the spin change to enforce " + "in an excitation.\ndegeneracy_tolerance Tolerance for two entries of " + "the diagonal to be considered degenerate, i.e. identical." + "\nReturns The number of guess vectors which have been properly " + "initialised (the others are invalid and should be discarded)."); +} + +} // namespace libadcc \ No newline at end of file From 176d9c965ffc1b80460023e63f52e1bb3fce045b Mon Sep 17 00:00:00 2001 From: fedy9 Date: Fri, 6 Mar 2026 10:23:19 +0100 Subject: [PATCH 02/26] added ChargedExcitation class and updated test setup for energies and eigenvectors only for IP/EA ADC --- adcc/ChargedExcitations.py | 193 ++++++++++++++++++ adcc/adc_ea/__init__.py | 0 adcc/adc_ip/__init__.py | 0 adcc/tests/ChargedExcitations_test.py | 111 ++++++++++ adcc/tests/generators/dump_adcc.py | 153 ++++++++------ adcc/tests/generators/generate_adcc_data.py | 159 +++++++++++++-- adcc/tests/generators/generate_adcman_data.py | 109 +++++++++- adcc/tests/generators/import_qchem_data.py | 72 +++++-- adcc/tests/generators/run_qchem.py | 25 ++- 9 files changed, 720 insertions(+), 102 deletions(-) create mode 100644 adcc/ChargedExcitations.py create mode 100644 adcc/adc_ea/__init__.py create mode 100644 adcc/adc_ip/__init__.py create mode 100644 adcc/tests/ChargedExcitations_test.py diff --git a/adcc/ChargedExcitations.py b/adcc/ChargedExcitations.py new file mode 100644 index 000000000..bede8c434 --- /dev/null +++ b/adcc/ChargedExcitations.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab +## --------------------------------------------------------------------- +## +## Copyright (C) 2019 by the adcc authors +## +## This file is part of adcc. +## +## adcc is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## adcc is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with adcc. If not, see . +## +## --------------------------------------------------------------------- +import numpy as np +from scipy import constants +import warnings + +from .import adc_ip, adc_ea +from .ElectronicStates import TableColumn, ElectronicStates, _timer_name +from .functions import dot +from .misc import cached_member_function + + +class ChargedExcitation(ElectronicStates): + + @property + def pole_strength(self) -> np.ndarray: + """Array of pole strengths of all computed states""" + pass + + + @cached_member_function(timer=_timer_name, separate_timings_by_args=False) + def _pole_strength(self, state_n: int) -> np.ndarray: + """Computes the pole strength for a single state""" + pass + + + def describe_helper(self, block_norms=True, excitation_type_name="energy"): + """ + Creates and returns the to be printed columns + + Parameters + ---------- + block_norms : bool, optional + Show the norms of the n particle (n-1) hole blocks of the charged + excited states, by default ``True``. + + excitation_name : str, optional + Defines the name of the energy property. + 'ionization potential'/'electron affinity' for IP/EA + ssq : bool, optional + Show the values of the excited states, by default ``False``. + """ + # Collect the columns to print + columns: list[TableColumn] = [] + values: list[str] = [] + # count the number of states + values.extend(str(i) for i in range(self.size)) + columns.append(TableColumn(header="#", values=values.copy(), unit="")) + values.clear() + # excitation energy in a.u. and eV + eV = constants.value("Hartree energy in eV") + values.extend(f"{e:^13.7g} {e * eV:^13.7g}" for e in self.excitation_energy) + columns.append(TableColumn( + header=excitation_type_name, values=values.copy(), + unit="(au) (eV)" + )) + values.clear() + # vector norm + blocks = self.matrix.axis_blocks + if block_norms and len(blocks) > 0: + values.extend(f"{dot(vec.get(blocks[0]), vec.get(blocks[0])):^9.4f}" + for vec in self.excitation_vector) + columns.append(TableColumn( + header="|v1|^2", values=values.copy(), unit="" + )) + values.clear() + if block_norms and len(blocks) > 1: + values.extend(f"{dot(vec.get(blocks[1]), vec.get(blocks[1])):^9.4f}" + for vec in self.excitation_vector) + columns.append(TableColumn( + header="|v2|^2", values=values.copy(), unit="" + )) + values.clear() + + return columns + + +class DetachedStates(ChargedExcitation): + _module = adc_ip + + def __init__(self, data, is_alpha: bool, method: str = None, + property_method: str = None): + self.is_alpha = is_alpha + super().__init__(data, method, property_method) + + if self.method.adc_type != "ip": + raise ValueError("DetachedStates computes excited state properties" + " for IP-ADC. Got the non-IP-ADC method " + f"{self.method.name}") + + def describe(self, block_norms=True): + """ + Return a string providing a human-readable description of the class + + Parameters + ---------- + block_norms : bool, optional + Show the norms of the n particle (n+1) hole blocks of the charged + excited states, by default ``True``. + """ + assert (self.matrix.axis_blocks == ["h"] + or self.matrix.axis_blocks == ["h", "phh"]) + columns = self.describe_helper( + block_norms=block_norms, + excitation_type_name="ionization potential") + + # Format the state information: kind, spin_change, + # alpha/beta detachment, and convergence + state_info = [] + if hasattr(self, "kind") and self.kind: + state_info.append(self.kind) + spin_type = "alpha" if self.is_alpha else "beta" + state_info.append(f"(ΔMS={self.spin_change:+.1f}), " + f"{spin_type} detachment") + if hasattr(self, "converged"): + conv = "converged" if self.converged else "NOT CONVERGED" + if state_info: # add separator to previous entry + state_info[-1] += "," + state_info.append(conv) + state_info = " ".join(state_info) + return self._describe(columns, state_info) + + +class AttachedStates(ChargedExcitation): + _module = adc_ea + + def __init__(self, data, is_alpha: bool, method: str = None, + property_method: str = None): + self.is_alpha = is_alpha + super().__init__(data, method, property_method) + + if self.method.adc_type != "ea": + raise ValueError("DetachedStates computes excited state properties" + " for EA-ADC. Got the non-EA-ADC method " + f"{self.method.name}") + + def describe(self, block_norms=True): + """ + Return a string providing a human-readable description of the class + + Parameters + ---------- + pole_strengths : bool optional + Show oscillator strengths, by default ``True``. + + state_dipole_moments : bool, optional + Show state dipole moments, by default ``False``. + + block_norms : bool, optional + Show the norms of the n particle (n+1) hole blocks of the charged + excited states, by default ``True``. + """ + assert (self.matrix.axis_blocks == ["p"] + or self.matrix.axis_blocks == ["p", "pph"]) + columns = self.describe_helper( + block_norms=block_norms, + excitation_type_name="electron affinity") + + # Format the state information: kind, spin_change, + # alpha/beta detachment, and convergence + state_info = [] + if hasattr(self, "kind") and self.kind: + state_info.append(self.kind) + spin_type = "alpha" if self.is_alpha else "beta" + state_info.append(f"(ΔMS={self.spin_change:+.1f}), " + f"{spin_type} attachment") + if hasattr(self, "converged"): + conv = "converged" if self.converged else "NOT CONVERGED" + if state_info: # add separator to previous entry + state_info[-1] += "," + state_info.append(conv) + state_info = " ".join(state_info) + return self._describe(columns, state_info) \ No newline at end of file diff --git a/adcc/adc_ea/__init__.py b/adcc/adc_ea/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/adcc/adc_ip/__init__.py b/adcc/adc_ip/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/adcc/tests/ChargedExcitations_test.py b/adcc/tests/ChargedExcitations_test.py new file mode 100644 index 000000000..a3b924178 --- /dev/null +++ b/adcc/tests/ChargedExcitations_test.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +import pytest +from numpy.testing import assert_allclose + +from adcc.AdcMethod import AdcMethod +from adcc.ChargedExcitations import DetachedStates, AttachedStates + +from .testdata_cache import testdata_cache + + +# --------------------------------------------------------------------- +# Shared parametrization (reuse your existing case table if available) +# --------------------------------------------------------------------- + +cases_ip_ea = [ + ("h2o_sto3g", "ip-adc2", "gen", "doublet"), + ("h2o_sto3g", "ea-adc2", "gen", "doublet"), +] + + +# --------------------------------------------------------------------- +# 1. Basic construction + size consistency +# --------------------------------------------------------------------- + +@pytest.mark.parametrize("system,method,case,kind", cases_ip_ea) +def test_ip_ea_basic_interface(system, method, case, kind): + adc_type = AdcMethod(method).adc_type + + if adc_type == "ip": + state = testdata_cache.adcc_states( + system=system, + method=method, + case=case, + kind=kind, + is_alpha=True, + ) + assert isinstance(state, DetachedStates) + elif adc_type == "ea": + state = testdata_cache.adcc_states( + system=system, + method=method, + case=case, + kind=kind, + is_alpha=True, + ) + assert isinstance(state, AttachedStates) + else: + raise AssertionError("Unexpected ADC type") + + # size matches number of excitation vectors + assert state.size == len(state.excitation_vector) + assert state.size == len(state.excitation_energy) + + +# --------------------------------------------------------------------- +# 2. describe() runs without error and contains expected wording +# --------------------------------------------------------------------- + +@pytest.mark.parametrize("system,method,case,kind", cases_ip_ea) +def test_ip_ea_describe(system, method, case, kind): + state = testdata_cache.adcc_states( + system=system, + method=method, + case=case, + kind=kind, + is_alpha=True, + ) + + adc_type = AdcMethod(method).adc_type + # Only test restricted alpha + if adc_type == "ip": + state.spin_change = -0.5 + elif adc_type == "ea": + state.spin_change = 0.5 + + desc = state.describe() + + assert state.kind in desc.lower() + + if adc_type == "ip": + assert "ionization" in desc.lower() + assert "detachment" in desc.lower() + elif adc_type == "ea": + assert "affinity" in desc.lower() + assert "attachment" in desc.lower() + + +# --------------------------------------------------------------------- +# 3. IP/EA hermiticity-style sanity check +# --------------------------------------------------------------------- +# +# Hermiticity of the ADC matrix itself belongs in: +# tests/test_adc_matrix.py +# +# NOT here. +# +# However, what *is* appropriate here: +# Energies must be real-valued. +# + +@pytest.mark.parametrize("system,method,case,kind", cases_ip_ea) +def test_ip_ea_energies_real(system, method, case, kind): + state = testdata_cache.adcc_states( + system=system, + method=method, + case=case, + kind=kind, + is_alpha=True, + ) + + assert_allclose(state.excitation_energy.imag, 0.0) diff --git a/adcc/tests/generators/dump_adcc.py b/adcc/tests/generators/dump_adcc.py index 1d859e34b..a408834f9 100644 --- a/adcc/tests/generators/dump_adcc.py +++ b/adcc/tests/generators/dump_adcc.py @@ -2,6 +2,7 @@ from adcc.AdcMatrix import AdcMatrix from adcc.AmplitudeVector import AmplitudeVector from adcc.ExcitedStates import ExcitedStates +from adcc.ChargedExcitations import DetachedStates, AttachedStates from adcc.hdf5io import emplace_dict from adcc.LazyMp import LazyMp from adcc.State2States import State2States @@ -9,6 +10,7 @@ import numpy as np import h5py +from typing import Union def dump_groundstate(ground_state: LazyMp, hdf5_file: h5py.Group, only_full_mode: bool) -> None: @@ -65,47 +67,17 @@ def dump_groundstate(ground_state: LazyMp, hdf5_file: h5py.Group, hdf5_file.attrs["adcc_version"] = adcc.__version__ -def dump_excited_states(states: ExcitedStates, hdf5_file: h5py.Group, - dump_nstates: int | None = None) -> None: +def _pp_adc_properties(states: ExcitedStates, kind_data: dict, n_states: int) -> None: + """Collects all properties that are unique for PP-ADC """ - Dump the excited states data to the given hdf5 file/group. - The number of states to dump can be given by dump_nstates. By default all states - are dumped. - """ - # ensure that the calculation converged on a nonzero result - assert states.converged # type: ignore - assert all(abs(e) > 1e-12 for e in states.excitation_energy) - - n_states = len(states.excitation_energy) - if dump_nstates is not None: - n_states = min(n_states, dump_nstates) - - dm_bb_a = [] # State diffdm AO basis alpha part - dm_bb_b = [] # State diffdm AO basis beta part. tdm_bb_a = [] # Ground to Excited state tdm AO basis alpha part tdm_bb_b = [] # Ground to Excited state tdm AO basis beta part - # split the eigenvectors according to their excitation degree for all states - eigenvectors: dict[int, list] = {} + for n in range(n_states): - # densities - bb_a, bb_b = states.state_diffdm[n].to_ao_basis(states.reference_state) - dm_bb_a.append(bb_a.to_ndarray()) - dm_bb_b.append(bb_b.to_ndarray()) + # TDMs bb_a, bb_b = states.transition_dm[n].to_ao_basis(states.reference_state) tdm_bb_a.append(bb_a.to_ndarray()) tdm_bb_b.append(bb_b.to_ndarray()) - # eigenvectors - for exdegree, block in enumerate(states.matrix.axis_blocks): - if exdegree + 1 not in eigenvectors: - eigenvectors[exdegree + 1] = [] - eigenvectors[exdegree + 1].append(getattr( - states.excitation_vector[n], block # type: ignore - ).to_ndarray()) - kind_data = {} - # eigenvalues - kind_data["eigenvalues"] = states.excitation_energy[:n_states] - # state and transition dipole moments - kind_data["state_dipole_moments"] = states.state_dipole_moment[:n_states] kind_data["transition_dipole_moments"] = ( states.transition_dipole_moment[:n_states] ) @@ -121,41 +93,96 @@ def dump_excited_states(states: ExcitedStates, hdf5_file: h5py.Group, kind_data[f"transition_quadrupole_moments_{g_origin}"] = ( states.transition_quadrupole_moment(g_origin)[:n_states] ) - # state diffdm and ground to excited state tdm - kind_data["state_diffdm_bb_a"] = np.asarray(dm_bb_a) - kind_data["state_diffdm_bb_b"] = np.asarray(dm_bb_b) + # ground to excited state tdm kind_data["ground_to_excited_tdm_bb_a"] = np.asarray(tdm_bb_a) kind_data["ground_to_excited_tdm_bb_b"] = np.asarray(tdm_bb_b) + + +def _ip_ea_adc_properties(states: Union[DetachedStates, AttachedStates], + kind_data: dict, n_states: int) -> None: + """Collects all properties that are unique for IP/EA-ADC + """ + pass + + +def dump_excited_states( + states: Union[ExcitedStates,DetachedStates, AttachedStates], + hdf5_file: h5py.Group, dump_nstates: int = None) -> None: + """ + Dump the (charged) excited states data to the given hdf5 file/group. + The number of states to dump can be given by dump_nstates. By default all states + are dumped. + """ + # ensure that the calculation converged on a nonzero result + assert states.converged + assert all(abs(e) > 1e-12 for e in states.excitation_energy) + + n_states = len(states.excitation_energy) + if dump_nstates is not None: + n_states = min(n_states, dump_nstates) + + dm_bb_a = [] # State diffdm AO basis alpha part + dm_bb_b = [] # State diffdm AO basis beta part. + # split the eigenvectors according to their excitation degree for all states + eigenvectors: dict[int, list] = {} + for n in range(n_states): + if isinstance(states, ExcitedStates): + # densities + bb_a, bb_b = states.state_diffdm[n].to_ao_basis(states.reference_state) + dm_bb_a.append(bb_a.to_ndarray()) + dm_bb_b.append(bb_b.to_ndarray()) + # eigenvectors + for exdegree, block in enumerate(states.matrix.axis_blocks): + if exdegree + 1 not in eigenvectors: + eigenvectors[exdegree + 1] = [] + eigenvectors[exdegree + 1].append( + getattr(states.excitation_vector[n], block).to_ndarray() + ) + kind_data = {} + + if isinstance(states, ExcitedStates): + _pp_adc_properties(states, kind_data, n_states) + elif isinstance(states, (DetachedStates, AttachedStates)): + _ip_ea_adc_properties(states, kind_data, n_states) + # eigenvalues + kind_data["eigenvalues"] = states.excitation_energy[:n_states] # dump the eigenvectors kind_data["eigenvectors_singles"] = np.asarray(eigenvectors[1]) if 2 in eigenvectors: kind_data["eigenvectors_doubles"] = np.asarray(eigenvectors[2]) - # state to state tdm: not implemented for CVS - if not states.method.is_core_valence_separated: - for ifrom in range(n_states - 1): - state2state = State2States(states, initial=ifrom) - # extract the tdms for the desired states - tdm_bb_a = [] - tdm_bb_b = [] - for j, tdm in enumerate(state2state.transition_dm): - if ifrom + j + 2 > n_states: # ifrom + j + 2 = ito - break - bb_a, bb_b = tdm.to_ao_basis(states.reference_state) - tdm_bb_a.append(bb_a.to_ndarray()) - tdm_bb_b.append(bb_b.to_ndarray()) - kind_data[f"state_to_state/from_{ifrom}/transition_dipole_moments"] = ( - state2state.transition_dipole_moment[:n_states - ifrom - 1] - ) - kind_data[f"state_to_state/from_{ifrom}/state_to_excited_tdm_bb_a"] = ( - np.asarray(tdm_bb_a) - ) - kind_data[f"state_to_state/from_{ifrom}/state_to_excited_tdm_bb_b"] = ( - np.asarray(tdm_bb_b) - ) - # ssq for unrestriced calculation - if not states.reference_state.restricted \ - and not states.method.is_core_valence_separated: - kind_data["state_ssq"] = states.state_ssq + if isinstance(states, ExcitedStates): + # state and transition dipole moments + kind_data["state_dipole_moments"] = states.state_dipole_moment[:n_states] + + # state diffdm and ground to excited state tdm + kind_data["state_diffdm_bb_a"] = np.asarray(dm_bb_a) + kind_data["state_diffdm_bb_b"] = np.asarray(dm_bb_b) + # state to state tdm: not implemented for CVS + if not states.method.is_core_valence_separated: + for ifrom in range(n_states - 1): + state2state = State2States(states, initial=ifrom) + # extract the tdms for the desired states + tdm_bb_a = [] + tdm_bb_b = [] + for j, tdm in enumerate(state2state.transition_dm): + if ifrom + j + 2 > n_states: # ifrom + j + 2 = ito + break + bb_a, bb_b = tdm.to_ao_basis(states.reference_state) + tdm_bb_a.append(bb_a.to_ndarray()) + tdm_bb_b.append(bb_b.to_ndarray()) + kind_data[f"state_to_state/from_{ifrom}/transition_dipole_moments"] = ( + state2state.transition_dipole_moment[:n_states - ifrom - 1] + ) + kind_data[f"state_to_state/from_{ifrom}/state_to_excited_tdm_bb_a"] = ( + np.asarray(tdm_bb_a) + ) + kind_data[f"state_to_state/from_{ifrom}/state_to_excited_tdm_bb_b"] = ( + np.asarray(tdm_bb_b) + ) + # ssq for unrestriced calculation + if not states.reference_state.restricted \ + and not states.method.is_core_valence_separated: + kind_data["state_ssq"] = states.state_ssq # write the data to hdf5 emplace_dict(kind_data, hdf5_file, compression="gzip") hdf5_file.attrs["adcc_version"] = adcc.__version__ diff --git a/adcc/tests/generators/generate_adcc_data.py b/adcc/tests/generators/generate_adcc_data.py index 1b3f37cb1..837b9ba43 100644 --- a/adcc/tests/generators/generate_adcc_data.py +++ b/adcc/tests/generators/generate_adcc_data.py @@ -5,6 +5,7 @@ from adcc.tests import testcases from adcc.AdcMethod import AdcMethod +from adcc.AdcMatrix import AdcMatrixlike from adcc.LazyMp import LazyMp from adcc.workflow import run_adc, validate_state_parameters from adcc import copy as adcc_copy @@ -17,8 +18,11 @@ # the base methods for each adc_type for which to generate data # the different cases (cvs, fc, ...) are handled in the generate functions. +# No need to test ip/ea-adc1 since it is equivalent to ip/ea-adc0 _methods = { - "pp": ("adc0", "adc1", "adc2", "adc2x", "adc3") + "pp": ("adc0", "adc1", "adc2", "adc2x", "adc3"), + "ip": ("ip-adc0", "ip-adc2", "ip-adc2x", "ip-adc3"), + "ea": ("ea-adc0", "ea-adc2", "ea-adc2x", "ea-adc3"), } @@ -26,6 +30,7 @@ def generate_adc(test_case: testcases.TestCase, method: AdcMethod, case: str, gs_density_order: int | None = None, n_states: int | None = None, n_singlets: int | None = None, n_triplets: int | None = None, n_spin_flip: int | None = None, + n_doublets: int | None = None, is_alpha: bool | None = None, dump_nstates: int | None = None, **kwargs) -> None: """ Generate and dump the excited states reference data for the given reference case @@ -39,13 +44,20 @@ def generate_adc(test_case: testcases.TestCase, method: AdcMethod, case: str, # the kind at this point to check if we need to perform a calculation # for this purpose we need the reference state hf = testdata_cache.refstate(system=test_case, case=case) - _, kind = validate_state_parameters( - hf, n_states=n_states, n_singlets=n_singlets, n_triplets=n_triplets, - n_spin_flip=n_spin_flip + matrix = AdcMatrixlike() + matrix.reference_state = hf + matrix.method = method + _, kind, is_alpha = validate_state_parameters( + matrix, n_states=n_states, n_singlets=n_singlets, n_triplets=n_triplets, + n_spin_flip=n_spin_flip, n_doublets=n_doublets, is_alpha=is_alpha ) key = f"{case}/{gs_density_order}" if f"{key}/{kind}" in hdf5_file: return None + if method.adc_type in ("ip", "ea"): + spin = "alpha" if is_alpha else "beta" + if f"{key}/{spin}/{kind}" in hdf5_file: + return None print(f"Generating {method.name} data for {case} {test_case.file_name}.") # prepend cvs to the method if needed (otherwise we will get an error) if "cvs" in case and not method.is_core_valence_separated: @@ -55,13 +67,16 @@ def generate_adc(test_case: testcases.TestCase, method: AdcMethod, case: str, assert gs_density_order is None states = run_adc( hf, method=method, n_states=n_states, n_singlets=n_singlets, - n_triplets=n_triplets, n_spin_flip=n_spin_flip, **kwargs + n_doublets=n_doublets, n_triplets=n_triplets, n_spin_flip=n_spin_flip, + is_alpha=is_alpha, **kwargs ) assert states.kind == kind # maybe we predicted wrong? # type: ignore - if f"{key}/matrix" not in hdf5_file: - # the matrix data is only dumped once for each case. I think it does not - # make sense to dump the data once for a singlet and once for a triplet - # trial vector. + if method.adc_type in ("ip", "ea"): + key = f"{key}/{spin}" + if f"{key}/matrix" not in hdf5_file and spin == "alpha": + # the matrix data is only dumped once for each case and only for alpha. + # I think it does not make sense to dump the data once for a singlet + # and once for a triplet trial vector as well as an alpha and a beta one. matrix_group = hdf5_file.create_group(f"{key}/matrix") trial_vec = adcc_copy(states.excitation_vector[0]).set_random() # type: ignore # noqa: E501 dump_matrix_testdata(states.matrix, trial_vec, matrix_group) @@ -73,6 +88,7 @@ def generate_adc(test_case: testcases.TestCase, method: AdcMethod, case: str, def generate_adc_all(test_case: testcases.TestCase, method: AdcMethod, n_states: int | None = None, n_singlets: int | None = None, n_triplets: int | None = None, n_spin_flip: int | None = None, + n_doublets: int | None = None, is_alpha: bool | None = None, dump_nstates: int | None = None, states_per_case: dict[str, dict[str, int]] | None = None, **kwargs) -> None: @@ -86,11 +102,14 @@ def generate_adc_all(test_case: testcases.TestCase, method: AdcMethod, n_singlets = states_per_case[case].get("n_singlets", None) n_triplets = states_per_case[case].get("n_triplets", None) n_spin_flip = states_per_case[case].get("n_spin_flip", None) + n_doublets = states_per_case[case].get("n_doublets", None) for density_order in test_case.gs_density_orders: generate_adc( - test_case, method, case, n_states=n_states, n_singlets=n_singlets, - n_triplets=n_triplets, n_spin_flip=n_spin_flip, - dump_nstates=dump_nstates, gs_density_order=density_order, **kwargs + test_case, method, case, n_states=n_states, + n_singlets=n_singlets, n_triplets=n_triplets, + n_spin_flip=n_spin_flip, n_doublets=n_doublets, + is_alpha=is_alpha, dump_nstates=dump_nstates, + gs_density_order=density_order, **kwargs ) @@ -141,6 +160,29 @@ def generate_h2o_sto3g(): test_case, method=method, dump_nstates=2, states_per_case=per_case, **n_states, **kwargs ) + + for method in _methods["ip"]: + method = AdcMethod(method) + for n_states in \ + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + n_states = {n_states: 3} + generate_adc_all( + test_case, method=method, dump_nstates=2, is_alpha=True, + **n_states + ) + + for method in _methods["ea"]: + method = AdcMethod(method) + for n_states in \ + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + if method.level < 2: + n_states = {n_states: 1} + else: + n_states = {n_states: 3} + generate_adc_all( + test_case, method=method, dump_nstates=2, is_alpha=True, + **n_states + ) def generate_h2o_def2tzvp(): @@ -158,6 +200,26 @@ def generate_h2o_def2tzvp(): test_case, method=method, dump_nstates=2, states_per_case=None, **n_states ) + + for method in _methods["ip"]: + method = AdcMethod(method) + for n_states in \ + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + n_states = {n_states: 3} + generate_adc_all( + test_case, method=method, dump_nstates=2, is_alpha=True, + **n_states + ) + + for method in _methods["ea"]: + method = AdcMethod(method) + for n_states in \ + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + n_states = {n_states: 3} + generate_adc_all( + test_case, method=method, dump_nstates=2, is_alpha=True, + **n_states + ) def generate_cn_sto3g(): @@ -168,12 +230,39 @@ def generate_cn_sto3g(): method = AdcMethod(method) for n_states in \ testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): - kwargs = {n_states: 3} + n_states = {n_states: 3} generate_adc_all( test_case=test_case, method=method, dump_nstates=2, - states_per_case=None, **kwargs + states_per_case=None, **n_states ) + for is_alpha in [True, False]: + for method in _methods["ip"]: + method = AdcMethod(method) + for n_states in \ + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + if method.level < 2: + n_states = {n_states: 2} + else: + n_states = {n_states: 3} + generate_adc_all( + test_case, method=method, dump_nstates=2, + is_alpha=is_alpha, **n_states + ) + + for method in _methods["ea"]: + method = AdcMethod(method) + for n_states in \ + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + if method.level < 2: + n_states = {n_states: 2} + else: + n_states = {n_states: 3} + generate_adc_all( + test_case, method=method, dump_nstates=2, + is_alpha=is_alpha, **n_states + ) + def generate_cn_ccpvdz(): # UHF, Doublet, 10 basis functions: (7a, 6b) occ, (3a, 4b) virt @@ -189,6 +278,27 @@ def generate_cn_ccpvdz(): **n_states ) + for is_alpha in [True, False]: + for method in _methods["ip"]: + method = AdcMethod(method) + for n_states in \ + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + n_states = {n_states: 3} + generate_adc_all( + test_case, method=method, dump_nstates=2, + is_alpha=is_alpha, **n_states + ) + + for method in _methods["ea"]: + method = AdcMethod(method) + for n_states in \ + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + n_states = {n_states: 3} + generate_adc_all( + test_case, method=method, dump_nstates=2, + is_alpha=is_alpha, **n_states + ) + def generate_hf_631g(): # UHF, Triplet @@ -204,6 +314,27 @@ def generate_hf_631g(): **n_states ) + for is_alpha in [True, False]: + for method in _methods["ip"]: + method = AdcMethod(method) + for n_states in \ + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + n_states = {n_states: 3} + generate_adc_all( + test_case, method=method, dump_nstates=2, + is_alpha=is_alpha, **n_states + ) + + for method in _methods["ea"]: + method = AdcMethod(method) + for n_states in \ + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + n_states = {n_states: 3} + generate_adc_all( + test_case, method=method, dump_nstates=2, + is_alpha=is_alpha, **n_states + ) + def main(): generate_h2o_sto3g() diff --git a/adcc/tests/generators/generate_adcman_data.py b/adcc/tests/generators/generate_adcman_data.py index 088980585..c725faef4 100644 --- a/adcc/tests/generators/generate_adcman_data.py +++ b/adcc/tests/generators/generate_adcman_data.py @@ -12,8 +12,12 @@ # the base methods for each adc_type for which to generate data # the different cases (cvs, fc, ...) are handled in the generate functions. +# No need to test ip/ea-adc1 since it is equivalent to ip/ea-adc0 +# ip/ea-adc2x not implemented in Q-Chem _methods = { - "pp": ("adc0", "adc1", "adc2", "adc2x", "adc3") + "pp": ("adc0", "adc1", "adc2", "adc2x", "adc3"), + "ip": ("ip-adc0", "ip-adc2", "ip-adc3"), + "ea": ("ea-adc0", "ea-adc2", "ea-adc3"), } # Since it seems not possible to only perform an adcman MPn calculation, # the ground state data has to be extracted from an adc(n) calculation. @@ -35,6 +39,8 @@ def generate_adc(test_case: testcases.TestCase, method: AdcMethod, case: str, gs_density_order: int | None = None, n_singlets: int = 0, n_triplets: int = 0, n_spin_flip: int = 0, n_states: int = 0, + n_ip_states: tuple[int, int] = (0, 0), + n_ea_states: tuple[int, int] = (0, 0), dump_nstates: int | None = None, **kwargs) -> None: """ Generate and dump the excited state reference data for the given reference case @@ -45,7 +51,8 @@ def generate_adc(test_case: testcases.TestCase, method: AdcMethod, case: str, datadir = Path(__file__).parent.parent / _testdata_dirname datafile = datadir / test_case.adcdata_file_name("adcman", method.name) hdf5_file = h5py.File(datafile, "a") # Read/write if exists, create otherwise - if f"{case}/{gs_density_order}" in hdf5_file: + key = f"{case}/{gs_density_order}" + if key in hdf5_file: return None # skip cvs-adc(0), since it is not available in qchem. if "cvs" in case and method.level == 0: @@ -61,9 +68,10 @@ def generate_adc(test_case: testcases.TestCase, method: AdcMethod, case: str, method = AdcMethod(f"cvs-{method.name}") state_data, _ = run_qchem( test_case, method, case, import_states=True, import_gs=False, - n_singlets=n_singlets, n_triplets=n_triplets, n_spin_flip=n_spin_flip, - n_states=n_states, import_nstates=dump_nstates, - gs_density_order=gs_density_order, **kwargs + n_singlets=n_singlets, n_triplets=n_triplets, n_ip_states=n_ip_states, + n_ea_states=n_ea_states, n_spin_flip=n_spin_flip, n_states=n_states, + import_nstates=dump_nstates, gs_density_order=gs_density_order, + **kwargs ) # the data returned from run_qchem should have already been imported # using the correct keys -> just dump them @@ -74,6 +82,8 @@ def generate_adc(test_case: testcases.TestCase, method: AdcMethod, case: str, def generate_adc_all(test_case: testcases.TestCase, method: AdcMethod, n_singlets: int = 0, n_triplets: int = 0, n_spin_flip: int = 0, n_states: int = 0, + n_ip_states: tuple[int, int] = (0, 0), + n_ea_states: tuple[int, int] = (0, 0), dump_nstates: int | None = None, states_per_case: dict[str, dict[str, int]] | None = None, **kwargs) -> None: @@ -87,12 +97,16 @@ def generate_adc_all(test_case: testcases.TestCase, method: AdcMethod, n_singlets = states_per_case[case].get("n_singlets", 0) n_triplets = states_per_case[case].get("n_triplets", 0) n_spin_flip = states_per_case[case].get("n_spin_flip", 0) + n_ea_states = states_per_case[case].get("n_ea_states", (0, 0)) + n_ip_states = states_per_case[case].get("n_ip_states", (0, 0)) n_states = states_per_case[case].get("n_states", 0) for density_order in test_case.gs_density_orders: generate_adc( test_case, method, case, n_singlets=n_singlets, - n_triplets=n_triplets, n_spin_flip=n_spin_flip, n_states=n_states, - dump_nstates=dump_nstates, gs_density_order=density_order, + n_triplets=n_triplets, n_spin_flip=n_spin_flip, + n_states=n_states, n_ea_states=n_ea_states, + n_ip_states=n_ip_states, dump_nstates=dump_nstates, + gs_density_order=density_order, **kwargs ) @@ -171,6 +185,25 @@ def generate_h2o_sto3g(): states_per_case=states.get(method.name, None), **n_states ) + for method in _methods["ip"]: + method = AdcMethod(method) + n_states = {"n_ip_states": (3, 0)} + generate_adc_all( + test_case, method=method, dump_nstates=2, + states_per_case=states.get(method.name, None), **n_states + ) + + for method in _methods["ea"]: + method = AdcMethod(method) + if method.level < 2: + n_states = {"n_ea_states": (1, 0)} + else: + n_states = {"n_ea_states": (3, 0)} + generate_adc_all( + test_case, method=method, dump_nstates=2, + states_per_case=states.get(method.name, None), **n_states + ) + def generate_h2o_def2tzvp(): # RHF, Singlet, 43 basis functions: 5 occ, 38 virt. @@ -187,6 +220,20 @@ def generate_h2o_def2tzvp(): **n_states ) + for method in _methods["ip"]: + method = AdcMethod(method) + n_states = {"n_ip_states": (3, 0)} + generate_adc_all( + test_case, method=method, dump_nstates=2, **n_states + ) + + for method in _methods["ea"]: + method = AdcMethod(method) + n_states = {"n_ea_states": (3, 0)} + generate_adc_all( + test_case, method=method, dump_nstates=2, **n_states + ) + def generate_cn_sto3g(): # UHF, Doublet, 10 basis functions: (7a, 6b) occ, (3a, 4b) virt @@ -201,6 +248,26 @@ def generate_cn_sto3g(): **n_states ) + for method in _methods["ip"]: + method = AdcMethod(method) + if method.level < 2: + n_states = {"n_ip_states": (2, 2)} + else: + n_states = {"n_ip_states": (3, 3)} + generate_adc_all( + test_case, method=method, dump_nstates=2, **n_states + ) + + for method in _methods["ea"]: + method = AdcMethod(method) + if method.level < 2: + n_states = {"n_ea_states": (2, 2)} + else: + n_states = {"n_ea_states": (3, 3)} + generate_adc_all( + test_case, method=method, dump_nstates=2, **n_states + ) + def generate_cn_ccpvdz(): # UHF, Doublet @@ -215,6 +282,20 @@ def generate_cn_ccpvdz(): **n_states ) + for method in _methods["ip"]: + method = AdcMethod(method) + n_states = {"n_ip_states": (3, 3)} + generate_adc_all( + test_case, method=method, dump_nstates=2, **n_states + ) + + for method in _methods["ea"]: + method = AdcMethod(method) + n_states = {"n_ea_states": (3, 3)} + generate_adc_all( + test_case, method=method, dump_nstates=2, **n_states + ) + def generate_hf_631g(): # UHF, Triplet @@ -229,6 +310,20 @@ def generate_hf_631g(): **n_states ) + for method in _methods["ip"]: + method = AdcMethod(method) + n_states = {"n_ip_states": (3, 3)} + generate_adc_all( + test_case, method=method, dump_nstates=2, **n_states + ) + + for method in _methods["ea"]: + method = AdcMethod(method) + n_states = {"n_ea_states": (3, 3)} + generate_adc_all( + test_case, method=method, dump_nstates=2, **n_states + ) + def generate_formaldehyde_pe(): for test_case in testcases.get(n_expected_cases=2, name="formaldehyde"): diff --git a/adcc/tests/generators/import_qchem_data.py b/adcc/tests/generators/import_qchem_data.py index b9f2840fd..fdbbbe9ff 100644 --- a/adcc/tests/generators/import_qchem_data.py +++ b/adcc/tests/generators/import_qchem_data.py @@ -73,21 +73,35 @@ def import_excited_states(context: h5py.File, method: AdcMethod, """ # define the possible state kinds to import for each adc variant. state_kinds = { - "pp": [ - ("singlets", True), ("triplets", True), # restricted + "pp": { + True: ["singlets", "triplets"], # restricted # uhf and spin flip are located in the same ".../uhf/..." subtree - ("any_or_spinflip", False), # unrestricted - ] + False: ["any_or_spinflip"], # unrestricted + }, + "ip": { + True: [""], # restricted + False: ["alphas", "betas"], # unrestricted + }, + "ea": { + True: [""], # restricted + False: ["alphas", "betas"], # unrestricted + }, } # Of course the kinds have to have a slightly different name in adcc.... - kind_map = {"singlets": "singlet", "triplets": "triplet"} + kind_map = {"singlets": "singlet", "triplets": "triplet", + "alphas": "alpha", "betas": "beta"} # also the adcc methods have to be translated - method_name: str = method.name.replace("-", "_") # cvs-adcn -> cvs_adcn - if method_name.endswith("adc2"): # adc2 -> adc2s - method_name += "s" + if method.adc_type == "pp": + method_name: str = method.name.replace("-", "_") # cvs-adcn -> cvs_adcn + if method_name.endswith("adc2"): # adc2 -> adc2s (Only for PP) + method_name += "s" + else: + method_name: str = method.name.split('-')[-1] # No cvs (yet) + restricted = "rhf" in context[f"adc_{method.adc_type}"][method_name].keys() + # go through the different possible state kinds and import the states. data = {} - for kind, restricted in state_kinds[method.adc_type]: + for kind in state_kinds[method.adc_type][restricted]: states = _import_excited_states( context, method=method_name, only_full_mode=only_full_mode, adc_type=method.adc_type, import_nstates=import_nstates, @@ -107,7 +121,13 @@ def import_excited_states(context: h5py.File, method: AdcMethod, if kind == "any_or_spinflip": kind = "spin_flip" if is_spin_flip else "any" - data[kind_map.get(kind, kind)] = states + if method.adc_type == "pp": + data[kind_map.get(kind, kind)] = states + elif method.adc_type in ("ip", "ea"): + if restricted: + data["alpha"] = {"doublet": states} + else: + data[kind_map.get(kind, kind)] = {"any": states} if not data: raise RuntimeError(f"Could not find any states for {method.name} in " f"{context.filename}.") @@ -141,7 +161,8 @@ def _import_excited_states(context: h5py.File, method: str, only_full_mode: bool Only import the first n states from the context. state_kind: str, optional The multiplicity of the states, e.g., singlet or triplet for restricted - pp-adc calculations. + pp-adc calculations. In case of an unrestricted IP/EA-ADC calc. it is + "alphas" or "betas" restricted: bool, optional Whether the adc calculation is based on a restricted reference state. dims_pref: str, optional @@ -149,13 +170,18 @@ def _import_excited_states(context: h5py.File, method: str, only_full_mode: bool tensors are exported too. The dimensions can be found by adding the given prefix to the context tree of the object. """ - # build the path under which to find the exicted states + # build the path under which to find the excited states tree = [f"adc_{adc_type}", method] if restricted: - assert state_kind is not None # needs to be defined for restricted calcs - tree.extend(["rhf", state_kind]) + tree.append("rhf") + if adc_type == "pp": + assert state_kind is not None # needs to be defined for restricted calcs + tree.append(state_kind) else: tree.append("uhf") + if adc_type in ("ip", "ea"): + assert state_kind is not None + tree.append(state_kind) tree.append("0") # we assume that we only have a single irrep!! tree = "/".join(tree) # check that we have states to read and return if not @@ -170,7 +196,12 @@ def _import_excited_states(context: h5py.File, method: str, only_full_mode: bool # context. data_to_read = {} for n in range(n_states): - state_tree = tree + f"/es{n}" + if adc_type == "pp": + state_tree = tree + f"/es{n}" + elif adc_type == "ip": + state_tree = tree + f"/ip{n}" + elif adc_type == "ea": + state_tree = tree + f"/ea{n}" # ensure that the state is converged _, converged = _extract_dataset(context[f"{state_tree}/converged"]) if not converged: @@ -247,10 +278,15 @@ def _import_state_to_state_data(context: h5py.File, method: str, # build the path under which to look for the state-to-state data. tree = [f"adc_{adc_type}", method] if restricted: - assert state_kind is not None # needs to be defined for restricted calcs - tree.extend(["rhf", "isr", state_kind]) + tree.extend(["rhf", "isr"]) + if adc_type == "pp": + assert state_kind is not None # needs to be defined for restricted calcs + tree.append(state_kind) else: tree.extend(["uhf", "isr"]) + if adc_type in ("ip", "ea"): + assert state_kind is not None + tree.append(state_kind) tree.append("0-0") # we assume that we only have a single irrep! tree = "/".join(tree) if tree not in context: @@ -352,6 +388,8 @@ def import_data(context: h5py.File, dims_pref: str = "dims/", "optdm/dm_bb_b": "ground_to_excited_tdm_bb_b", # transition dipole moment (vector): only when we have a optdm "tprop/dipole": "transition_dipole_moments", + # Pole strengths for IP/EA-ADC + "pole_strength": "pole_strengths", # doubles and triples part of the amplitude vector "u2": "eigenvectors_doubles", "u3": "eigenvectors_triples", diff --git a/adcc/tests/generators/run_qchem.py b/adcc/tests/generators/run_qchem.py index 559f81da1..62f5f9f81 100644 --- a/adcc/tests/generators/run_qchem.py +++ b/adcc/tests/generators/run_qchem.py @@ -29,6 +29,8 @@ def run_qchem(test_case: testcases.TestCase, method: AdcMethod, case: str, run_qchem_scf: bool = False, import_nstates: int | None = None, n_states: int = 0, n_singlets: int = 0, n_triplets: int = 0, n_spin_flip: int = 0, + n_ip_states: tuple[int, int] = (0, 0), + n_ea_states: tuple[int, int] = (0, 0), **kwargs) -> tuple[dict | None, dict | None]: """ Run a qchem calculation for the given test case and method on top @@ -120,6 +122,7 @@ def run_qchem(test_case: testcases.TestCase, method: AdcMethod, case: str, n_core_orbitals=n_core_orbitals, n_frozen_core=n_frozen_core, n_frozen_virtual=n_frozen_virtual, any_states=n_states, singlet_states=n_singlets, triplet_states=n_triplets, + ip_states=n_ip_states, ea_states=n_ea_states, sf_states=n_spin_flip, run_qchem_scf=run_qchem_scf, **args ) # call qchem and wait for completion @@ -349,6 +352,8 @@ def generate_qchem_input_file(infile: str | Path, adc_method: AdcMethod, basis: bohr: bool = True, any_states: int = 0, singlet_states: int = 0, triplet_states: int = 0, sf_states: int = 0, + ip_states: tuple[int, int] = (0,0), + ea_states: tuple[int, int] = (0,0), maxiter: int = 160, conv_tol: int = 10, n_core_orbitals: int | None = None, n_frozen_core: int | None = None, @@ -405,6 +410,10 @@ def generate_qchem_input_file(infile: str | Path, adc_method: AdcMethod, basis: singlet_states=singlet_states, triplet_states=triplet_states, sf_states=sf_states, + ip_states_alpha=ip_states[0], + ip_states_beta=ip_states[1], + ea_states_alpha=ea_states[0], + ea_states_beta=ea_states[1], n_guesses=nguess_singles, bohr=bohr, maxiter=maxiter, @@ -441,6 +450,10 @@ def generate_qchem_input_file(infile: str | Path, adc_method: AdcMethod, basis: ee_singlets {singlet_states} ee_triplets {triplet_states} sf_states {sf_states} +eom_ip_alpha {ip_states_alpha} +eom_ip_beta {ip_states_beta} +eom_ea_alpha {ea_states_alpha} +eom_ea_beta {ea_states_beta} input_bohr {bohr} sym_ignore true adc_davidson_maxiter {maxiter} @@ -497,7 +510,17 @@ def generate_qchem_input_file(infile: str | Path, adc_method: AdcMethod, basis: "cvs-adc1": "cvs-adc(1)", "cvs-adc2": "cvs-adc(2)", "cvs-adc2x": "cvs-adc(2)-x", - "cvs-adc3": "cvs-adc(3)" + "cvs-adc3": "cvs-adc(3)", + "ip-adc0": "adc(0)", + "ip-adc1": "adc(1)", + "ip-adc2": "adc(2)", + "ip-adc2x": "adc(2)-x", + "ip-adc3": "adc(3)", + "ea-adc0": "adc(0)", + "ea-adc1": "adc(1)", + "ea-adc2": "adc(2)", + "ea-adc2x": "adc(2)-x", + "ea-adc3": "adc(3)", } _gs_density_order_dict = { From 46b160b60c0a27726d90e510c0f4ca29ab11f07b Mon Sep 17 00:00:00 2001 From: fedy9 Date: Fri, 6 Mar 2026 10:31:44 +0100 Subject: [PATCH 03/26] updated printout for ElectronicStates parent class --- adcc/ChargedExcitations.py | 1 - adcc/ElectronicStates.py | 55 +++++++++++++++++++++++++------------- adcc/ExcitedStates.py | 21 +++++++++++++-- 3 files changed, 56 insertions(+), 21 deletions(-) diff --git a/adcc/ChargedExcitations.py b/adcc/ChargedExcitations.py index bede8c434..aeaca0471 100644 --- a/adcc/ChargedExcitations.py +++ b/adcc/ChargedExcitations.py @@ -22,7 +22,6 @@ ## --------------------------------------------------------------------- import numpy as np from scipy import constants -import warnings from .import adc_ip, adc_ea from .ElectronicStates import TableColumn, ElectronicStates, _timer_name diff --git a/adcc/ElectronicStates.py b/adcc/ElectronicStates.py index 0ff92f447..23983a971 100644 --- a/adcc/ElectronicStates.py +++ b/adcc/ElectronicStates.py @@ -72,7 +72,8 @@ def __init__(self, data, method: str = None, self._timed_objects.append((datakey, data)) # Copy some optional attributes - for optattr in ["converged", "spin_change", "kind", "n_iter"]: + for optattr in ["converged", "spin_change", "kind", "n_iter", + "is_alpha"]: if hasattr(data, optattr): setattr(self, optattr, getattr(data, optattr)) @@ -109,7 +110,7 @@ def __init__(self, data, method: str = None, self._excitation_energy_uncorrected = \ data.excitation_energy.copy() if hasattr(data, "excitation_energy_uncorrected"): - self._excitation_energy_uncorrected =\ + self._excitation_energy_uncorrected = \ data.excitation_energy_uncorrected.copy() if hasattr(data, "excitation_vector"): self._excitation_vector = data.excitation_vector @@ -455,7 +456,7 @@ def convert_x_units(spectrum: Spectrum): plots = spectrum.plot(style="discrete", **kwargs) return plots - def _describe(self, columns: list["TableColumn"]): + def _describe(self, columns: list["TableColumn"], state_info: list[str]): """ Return a string providing a human-readable description of the class @@ -481,25 +482,11 @@ def _describe(self, columns: list["TableColumn"]): columns[-1] = columns[-1].with_width(new_width) table_width = corr_width - # - Format the header # Format the method method = self.method.name if self.property_method != self.method: method += f" ({self.property_method.name})" - # Format the state information: kind, spin_change and convergence - state_info = [] - if hasattr(self, "kind") and self.kind: - state_info.append(self.kind) - if hasattr(self, "spin_change") and self.spin_change is not None and \ - self.spin_change != 0: - state_info.append(f"(ΔMS={self.spin_change:+2d})") - if hasattr(self, "converged"): - conv = "converged" if self.converged else "NOT CONVERGED" - if state_info: # add separator to previous entry - state_info[-1] += "," - state_info.append(conv) - state_info = " ".join(state_info) - # actually format the header + # Format the header if table_width > len(method) + 4: header = ( # -4 for the spaces f"{method:s} {state_info:>{str(table_width - len(method) - 4)}s}" @@ -744,6 +731,38 @@ def format(self, amplitude: AmplitudeVector) -> list[str]: + spin_coeff_gap + self.value_format ) } + elif self.matrix.axis_blocks == ["h"]: + formats = {"o": ( + "{} -> " + idx_spin_gap + "{}->" + + spin_coeff_gap + self.value_format + )} + elif self.matrix.axis_blocks == ["h", "phh"]: + formats = { + "o": ( + empty_idx + " {} -> " + empty_idx + idx_spin_gap + + " {}-> " + spin_coeff_gap + self.value_format + ), + "oov": ( + "{} {} -> {}" + idx_spin_gap + "{}{}->{}" + + spin_coeff_gap + self.value_format + ) + } + elif self.matrix.axis_blocks == ["p"]: + formats = {"v": ( + " -> {}" + idx_spin_gap + "->{}" + + spin_coeff_gap + self.value_format + )} + elif self.matrix.axis_blocks == ["p", "pph"]: + formats = { + "v": ( + empty_idx + " -> {} " + empty_idx + idx_spin_gap + + " ->{} " + spin_coeff_gap + self.value_format + ), + "ovv": ( + "{} -> {} {}" + idx_spin_gap + + "{}->{}{}" + spin_coeff_gap + self.value_format + ) + } else: raise NotImplementedError("Unknown ADC matrix structure") diff --git a/adcc/ExcitedStates.py b/adcc/ExcitedStates.py index ab3e82d86..5fa324cbc 100644 --- a/adcc/ExcitedStates.py +++ b/adcc/ExcitedStates.py @@ -147,7 +147,7 @@ def describe(self, oscillator_strengths=True, rotatory_strengths=False, unit="x(au) y(au) z(au) abs(au)" )) values.clear() - values.clear() + # values if ssq and not self.reference_state.restricted: values.extend(f"{ssq:^9.4f}" for ssq in self.state_ssq) @@ -155,7 +155,24 @@ def describe(self, oscillator_strengths=True, rotatory_strengths=False, header="", values=values.copy(), unit="(au)" )) values.clear() - return self._describe(columns) + + # Format the state information: kind, spin_change and convergence + state_info = [] + if hasattr(self, "kind") and self.kind: + state_info.append(self.kind) + if hasattr(self, "spin_change") and self.spin_change is not None and \ + self.spin_change != 0: + # For PP, spin_change can only be integer values + spin_change = int(self.spin_change) + state_info.append(f"(ΔMS={spin_change:+2d})") + if hasattr(self, "converged"): + conv = "converged" if self.converged else "NOT CONVERGED" + if state_info: # add separator to previous entry + state_info[-1] += "," + state_info.append(conv) + state_info = " ".join(state_info) + + return self._describe(columns, state_info) def to_qcvars(self, properties=False, recurse=False): """ From bf9004dba4b0829ad46bcf98e6fd87a28bd118bf Mon Sep 17 00:00:00 2001 From: fedy9 Date: Fri, 6 Mar 2026 11:05:51 +0100 Subject: [PATCH 04/26] comment in ip/ea doubles guess setup --- libadcc_src/guess/ea_adc_guess_d.cc | 8 ++++++-- libadcc_src/guess/ip_adc_guess_d.cc | 9 ++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/libadcc_src/guess/ea_adc_guess_d.cc b/libadcc_src/guess/ea_adc_guess_d.cc index 9f432d69c..cd3e889d4 100644 --- a/libadcc_src/guess/ea_adc_guess_d.cc +++ b/libadcc_src/guess/ea_adc_guess_d.cc @@ -199,7 +199,9 @@ void transfer_elements(const list1d_t& o, const list1d_t& v, // Ignore blocks where the targeted spin_change is not achieved mask<3> orb_type; - orb_type[0] = true; + orb_type[0] = true; // occ. + orb_type[1] = false; // virt. + orb_type[2] = false; // virt. if (base.get_spin_proj(orb_type, bidx) != dm_s) continue; // Check if the block is allowed in the symmetry of the guess @@ -481,4 +483,6 @@ size_t ea_adc_guess_d(std::list*, double return nguesses; } -} // namespace libadcc \ No newline at end of file + +} // namespace libadcc + diff --git a/libadcc_src/guess/ip_adc_guess_d.cc b/libadcc_src/guess/ip_adc_guess_d.cc index 328b06d46..658253056 100644 --- a/libadcc_src/guess/ip_adc_guess_d.cc +++ b/libadcc_src/guess/ip_adc_guess_d.cc @@ -199,8 +199,9 @@ void transfer_elements(const list1d_t& o, const list1d_t& v, // Ignore blocks where the targeted spin_change is not achieved mask<3> orb_type; - orb_type[0] = true; - orb_type[1] = true; + orb_type[0] = true; // occ. + orb_type[1] = true; // occ. + orb_type[2] = false; // virt. if (base.get_spin_proj(orb_type, bidx) != dm_s) continue; // Check if the block is allowed in the symmetry of the guess @@ -482,4 +483,6 @@ size_t ip_adc_guess_d(std::list*, double return nguesses; } -} // namespace libadcc \ No newline at end of file + +} // namespace libadcc + From be15aa19ab6e95fe80d32cb80e0c293227521141 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Fri, 6 Mar 2026 11:19:54 +0100 Subject: [PATCH 05/26] linting --- adcc/AdcMatrix.py | 8 ++++---- adcc/AdcMethod.py | 8 ++++---- adcc/ChargedExcitations.py | 10 +++++----- adcc/tests/AdcMatrix_test.py | 2 +- adcc/tests/generators/generate_adcc_data.py | 6 +++--- adcc/tests/generators/generate_adcman_data.py | 6 +++--- adcc/tests/generators/import_qchem_data.py | 2 +- adcc/tests/guess_test.py | 12 ++++++------ adcc/tests/testdata_cache.py | 18 +++++++++--------- adcc/tests/workflow_test.py | 16 ++++++++-------- adcc/workflow.py | 16 ++++++++-------- 11 files changed, 52 insertions(+), 52 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 1e9fe7c64..da83ad01a 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -185,7 +185,7 @@ def _is_valid_space(cls, space: str, method: AdcMethod) -> bool: if method.adc_type == "pp": return n_particle == n_hole elif method.adc_type == "ip": - return n_particle == n_hole - 1 + return n_particle == n_hole - 1 elif method.adc_type == "ea": return n_particle == n_hole + 1 raise ValueError(f"Unknown adc type {method.adc_type} for method " @@ -270,9 +270,9 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, block_dispatch_fun = BLOCK_DISPATCH[self.method.adc_type] blocks = { block: block_dispatch_fun(self.ground_state, block.split("_"), - order=order, - intermediates=self.intermediates, - variant=variant) + order=order, + intermediates=self.intermediates, + variant=variant) for block, order in self.block_orders.items() if order is not None } diff --git a/adcc/AdcMethod.py b/adcc/AdcMethod.py index 347ed7c4e..254cec472 100644 --- a/adcc/AdcMethod.py +++ b/adcc/AdcMethod.py @@ -27,9 +27,9 @@ def get_valid_methods(): valid_adc_types = ["ip", "ea"] valid_bases = ["adc0", "adc1", "adc2", "adc2x", "adc3"] - ret = (valid_bases - + [p + "-" + m for p in valid_prefixes for m in valid_bases] - + [t + "-" + m for t in valid_adc_types for m in valid_bases] + ret = (valid_bases + + [p + "-" + m for p in valid_prefixes for m in valid_bases] + + [t + "-" + m for t in valid_adc_types for m in valid_bases] ) return ret @@ -46,7 +46,7 @@ def __init__(self, method): self.__base_method = split[-1] split = split[:-1] self.is_core_valence_separated = "cvs" in split - + if "ip" in split: self.adc_type = "ip" elif "ea" in split: diff --git a/adcc/ChargedExcitations.py b/adcc/ChargedExcitations.py index aeaca0471..46cbbe3ef 100644 --- a/adcc/ChargedExcitations.py +++ b/adcc/ChargedExcitations.py @@ -53,8 +53,8 @@ def describe_helper(self, block_norms=True, excitation_type_name="energy"): Show the norms of the n particle (n-1) hole blocks of the charged excited states, by default ``True``. - excitation_name : str, optional - Defines the name of the energy property. + excitation_type_name : str, optional + Defines the name of the energy property. 'ionization potential'/'electron affinity' for IP/EA ssq : bool, optional Show the values of the excited states, by default ``False``. @@ -117,7 +117,7 @@ def describe(self, block_norms=True): Show the norms of the n particle (n+1) hole blocks of the charged excited states, by default ``True``. """ - assert (self.matrix.axis_blocks == ["h"] + assert (self.matrix.axis_blocks == ["h"] or self.matrix.axis_blocks == ["h", "phh"]) columns = self.describe_helper( block_norms=block_norms, @@ -137,7 +137,7 @@ def describe(self, block_norms=True): state_info[-1] += "," state_info.append(conv) state_info = " ".join(state_info) - return self._describe(columns, state_info) + return self._describe(columns, state_info) class AttachedStates(ChargedExcitation): @@ -169,7 +169,7 @@ def describe(self, block_norms=True): Show the norms of the n particle (n+1) hole blocks of the charged excited states, by default ``True``. """ - assert (self.matrix.axis_blocks == ["p"] + assert (self.matrix.axis_blocks == ["p"] or self.matrix.axis_blocks == ["p", "pph"]) columns = self.describe_helper( block_norms=block_norms, diff --git a/adcc/tests/AdcMatrix_test.py b/adcc/tests/AdcMatrix_test.py index b23247fa1..a41164008 100644 --- a/adcc/tests/AdcMatrix_test.py +++ b/adcc/tests/AdcMatrix_test.py @@ -565,7 +565,7 @@ def test_matmul(self, system: str, method: str): spin_change = -0.5 elif matrix.method.adc_type == "ea": spin_change = 0.5 - + vec = adcc.guess_zero(matrix, spin_change=spin_change) vec.set_random() diff --git a/adcc/tests/generators/generate_adcc_data.py b/adcc/tests/generators/generate_adcc_data.py index 837b9ba43..4706edc56 100644 --- a/adcc/tests/generators/generate_adcc_data.py +++ b/adcc/tests/generators/generate_adcc_data.py @@ -75,7 +75,7 @@ def generate_adc(test_case: testcases.TestCase, method: AdcMethod, case: str, key = f"{key}/{spin}" if f"{key}/matrix" not in hdf5_file and spin == "alpha": # the matrix data is only dumped once for each case and only for alpha. - # I think it does not make sense to dump the data once for a singlet + # I think it does not make sense to dump the data once for a singlet # and once for a triplet trial vector as well as an alpha and a beta one. matrix_group = hdf5_file.create_group(f"{key}/matrix") trial_vec = adcc_copy(states.excitation_vector[0]).set_random() # type: ignore # noqa: E501 @@ -160,7 +160,7 @@ def generate_h2o_sto3g(): test_case, method=method, dump_nstates=2, states_per_case=per_case, **n_states, **kwargs ) - + for method in _methods["ip"]: method = AdcMethod(method) for n_states in \ @@ -200,7 +200,7 @@ def generate_h2o_def2tzvp(): test_case, method=method, dump_nstates=2, states_per_case=None, **n_states ) - + for method in _methods["ip"]: method = AdcMethod(method) for n_states in \ diff --git a/adcc/tests/generators/generate_adcman_data.py b/adcc/tests/generators/generate_adcman_data.py index c725faef4..54419b634 100644 --- a/adcc/tests/generators/generate_adcman_data.py +++ b/adcc/tests/generators/generate_adcman_data.py @@ -16,7 +16,7 @@ # ip/ea-adc2x not implemented in Q-Chem _methods = { "pp": ("adc0", "adc1", "adc2", "adc2x", "adc3"), - "ip": ("ip-adc0", "ip-adc2", "ip-adc3"), + "ip": ("ip-adc0", "ip-adc2", "ip-adc3"), "ea": ("ea-adc0", "ea-adc2", "ea-adc3"), } # Since it seems not possible to only perform an adcman MPn calculation, @@ -103,7 +103,7 @@ def generate_adc_all(test_case: testcases.TestCase, method: AdcMethod, for density_order in test_case.gs_density_orders: generate_adc( test_case, method, case, n_singlets=n_singlets, - n_triplets=n_triplets, n_spin_flip=n_spin_flip, + n_triplets=n_triplets, n_spin_flip=n_spin_flip, n_states=n_states, n_ea_states=n_ea_states, n_ip_states=n_ip_states, dump_nstates=dump_nstates, gs_density_order=density_order, @@ -192,7 +192,7 @@ def generate_h2o_sto3g(): test_case, method=method, dump_nstates=2, states_per_case=states.get(method.name, None), **n_states ) - + for method in _methods["ea"]: method = AdcMethod(method) if method.level < 2: diff --git a/adcc/tests/generators/import_qchem_data.py b/adcc/tests/generators/import_qchem_data.py index fdbbbe9ff..f9dbfdf84 100644 --- a/adcc/tests/generators/import_qchem_data.py +++ b/adcc/tests/generators/import_qchem_data.py @@ -98,7 +98,7 @@ def import_excited_states(context: h5py.File, method: AdcMethod, else: method_name: str = method.name.split('-')[-1] # No cvs (yet) restricted = "rhf" in context[f"adc_{method.adc_type}"][method_name].keys() - + # go through the different possible state kinds and import the states. data = {} for kind in state_kinds[method.adc_type][restricted]: diff --git a/adcc/tests/guess_test.py b/adcc/tests/guess_test.py index ea4f1b9e8..41b2a4cd2 100644 --- a/adcc/tests/guess_test.py +++ b/adcc/tests/guess_test.py @@ -459,7 +459,7 @@ def assert_guess_values(self, matrix, block, guesses, spin_flip=False, # Make a list of diagonal indices, ordered by the corresponding # diagonal values diagonal = matrix.diagonal().get(block).to_ndarray() - + # Doubles guesses are constructed from the 0th order diagonal if matrix.method.level > 1 and not matrix.method.name.endswith("adc2"): if block == "pphh": @@ -474,7 +474,7 @@ def assert_guess_values(self, matrix, block, guesses, spin_flip=False, diagonal = adcc.adc_ea.matrix.diagonal_pph_pph_0( matrix.reference_state ).pph.to_ndarray() - + # Build list of indices, which would sort the diagonal order = np.argsort(diagonal.ravel()) sidcs = list(zip(*np.unravel_index(order, diagonal.shape))) @@ -542,7 +542,7 @@ def assert_guess_values(self, matrix, block, guesses, spin_flip=False, else: sidcs = [ idx for idx in sidcs - # aba / baa / bbb + # aba / baa / bbb if any(( idx[0] < noa and idx[1] >= nCa and idx[2] < nva, idx[0] >= noa and idx[1] < nCa and idx[2] < nva, @@ -646,7 +646,7 @@ def base_test_ip(self, system: str, case: str, method: str, block: str, spin_change = -0.5 if is_alpha else +0.5 for n_guesses in range(3, max_guesses + 1): guesses = adcc.guess.guesses_from_diagonal( - matrix, n_guesses, block=block, spin_change=spin_change, + matrix, n_guesses, block=block, spin_change=spin_change, is_alpha=is_alpha ) assert len(guesses) == n_guesses @@ -836,7 +836,7 @@ def test_doubles_hf_ea(self, method: str, case: str, is_alpha: bool): "hf_631g", case, method, block="pph", is_alpha=is_alpha, max_guesses=5 ) - + # # Tests against reference values @@ -1102,7 +1102,7 @@ def test_reference_h2o_ea(self, method: str): # against hard coded reference data. The test against numpy above should be # sufficient. - # Current workaround: Compare guess energies rather than exact ordering for + # Current workaround: Compare guess energies rather than exact ordering for # these cases by calling 'base_reference_degenerate_{adc_type}()' @pytest.mark.parametrize("method", doubles_methods_pp) diff --git a/adcc/tests/testdata_cache.py b/adcc/tests/testdata_cache.py index a4679b2a4..f55eb1801 100644 --- a/adcc/tests/testdata_cache.py +++ b/adcc/tests/testdata_cache.py @@ -211,9 +211,9 @@ def _make_mock_adc_state( is_alpha: Optional[bool] = None ) -> ExcitedStates | AttachedStates | DetachedStates: """ - Create an ExcitedStates/AttachedStates/DetachedStates instance for the - given test case, method (adcn), reference case (gen/cvs/fc/...), - state kind (singlet/triplet/any/...) and optionally gs_density_order + Create an ExcitedStates/AttachedStates/DetachedStates instance for the + given test case, method (adcn), reference case (gen/cvs/fc/...), + state kind (singlet/triplet/any/...) and optionally gs_density_order (2/3/sigma4+) and optionally is_alpha for IP/EA. Source refers to the source with which the data were generated (adcman/adcc). The states object is build on top of the loaded HF data and @@ -298,9 +298,9 @@ def adcc_states(self, system: str, method: str, kind: str, is_alpha: Optional[bool] = None ) -> ExcitedStates | AttachedStates | DetachedStates: """ - Create an ExcitedStates/AttachedStates/DetachedStates instance for the - given test case, method (adcn), reference case (gen/cvs/fc/...), - state kind (singlet/triplet/any/...) and optionally gs_density_order + Create an ExcitedStates/AttachedStates/DetachedStates instance for the + given test case, method (adcn), reference case (gen/cvs/fc/...), + state kind (singlet/triplet/any/...) and optionally gs_density_order (2/3/sigma4+) using the adcc eigenstates and eigenvalues. """ if ("ip" in method or "ea" in method) and is_alpha is None: @@ -315,9 +315,9 @@ def adcman_states(self, system: str, method: str, kind: str, is_alpha: Optional[bool] = None ) -> ExcitedStates | AttachedStates | DetachedStates: """ - Create an ExcitedStates/AttachedStates/DetachedStates instance for the - given test case, method (adcn), reference case (gen/cvs/fc/...), - state kind (singlet/triplet/any/...) and optionally gs_density_order + Create an ExcitedStates/AttachedStates/DetachedStates instance for the + given test case, method (adcn), reference case (gen/cvs/fc/...), + state kind (singlet/triplet/any/...) and optionally gs_density_order (2/3/sigma4+) using the adcman eigenstates and eigenvalues. """ if ("ip" in method or "ea" in method) and is_alpha is None: diff --git a/adcc/tests/workflow_test.py b/adcc/tests/workflow_test.py index 169a5abef..9775ae98c 100644 --- a/adcc/tests/workflow_test.py +++ b/adcc/tests/workflow_test.py @@ -115,7 +115,7 @@ def test_validate_state_parameters_rhf_ip(self): # Build empty AdcMatrixlike object and assign ref_state and method matrix = AdcMatrixlike() matrix.reference_state = testdata_cache.refstate("h2o_sto3g", case="gen") - matrix.method = AdcMethod("ip-adc2") + matrix.method = AdcMethod("ip-adc2") assert (3, "any", True) == (validate_state_parameters( matrix, n_states=3)) @@ -156,7 +156,7 @@ def test_validate_state_parameters_uhf_ip(self): # Build empty AdcMatrixlike object and assign ref_state and method matrix = AdcMatrixlike() matrix.reference_state = testdata_cache.refstate("cn_sto3g", case="gen") - matrix.method = AdcMethod("ip-adc2") + matrix.method = AdcMethod("ip-adc2") assert (3, "any", True) == validate_state_parameters( matrix, n_states=3, kind="any") @@ -181,7 +181,7 @@ def test_validate_state_parameters_uhf_ip(self): for case in invalid_cases: with pytest.raises(InputError): validate_state_parameters(matrix, **case) - + def test_validate_state_parameters_rhf_ea(self): from adcc.workflow import validate_state_parameters from adcc.AdcMatrix import AdcMatrixlike @@ -192,7 +192,7 @@ def test_validate_state_parameters_rhf_ea(self): matrix.reference_state = testdata_cache.refstate("h2o_sto3g", case="gen") # IP - matrix.method = AdcMethod("ea-adc2") + matrix.method = AdcMethod("ea-adc2") assert (3, "any", True) == (validate_state_parameters( matrix, n_states=3)) @@ -233,7 +233,7 @@ def test_validate_state_parameters_uhf_ea(self): # Build empty AdcMatrixlike object and assign ref_state and method matrix = AdcMatrixlike() matrix.reference_state = testdata_cache.refstate("cn_sto3g", case="gen") - matrix.method = AdcMethod("ea-adc2") + matrix.method = AdcMethod("ea-adc2") assert (3, "any", True) == validate_state_parameters( matrix, n_states=3, kind="any") @@ -526,7 +526,7 @@ def test_obtain_guesses_by_inspection_pp(self): assert len(res) == 20 # Only doubles guesses - res = obtain_guesses_by_inspection(matrix2, n_guesses=4, + res = obtain_guesses_by_inspection(matrix2, n_guesses=4, kind="singlet", n_guesses_doubles=4) assert len(res) == 4 @@ -565,7 +565,7 @@ def test_obtain_guesses_by_inspection_ip(self): assert len(res) == 20 # Only doubles guesses - res = obtain_guesses_by_inspection(matrix2, n_guesses=4, + res = obtain_guesses_by_inspection(matrix2, n_guesses=4, kind="doublet", spin_change=-0.5, is_alpha=True, n_guesses_doubles=4) @@ -608,7 +608,7 @@ def test_obtain_guesses_by_inspection_ea(self): assert len(res) == 20 # Only doubles guesses - res = obtain_guesses_by_inspection(matrix2, n_guesses=4, + res = obtain_guesses_by_inspection(matrix2, n_guesses=4, kind="doublet", spin_change=0.5, is_alpha=True, n_guesses_doubles=4) diff --git a/adcc/workflow.py b/adcc/workflow.py index 694f70bc6..8b720a7c9 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -36,7 +36,7 @@ from .AmplitudeVector import AmplitudeVector from .exceptions import InputError from .ExcitedStates import ExcitedStates -# from .ChargedExcitations import DetachedStates, AttachedStates +from .ChargedExcitations import DetachedStates, AttachedStates from .ReferenceState import ReferenceState as adcc_ReferenceState from .solver.lanczos import lanczos from .solver.davidson import jacobi_davidson @@ -221,7 +221,7 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, matrix, n_states=n_states, n_singlets=n_singlets, n_doublets=n_doublets, n_triplets=n_triplets, n_spin_flip=n_spin_flip, kind=kind, is_alpha=is_alpha) - + # Setup environment coupling terms and energy corrections env_matrix_term, env_energy_corrections = setup_environment(matrix, environment) @@ -229,7 +229,7 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, if env_matrix_term: matrix += env_matrix_term - # Construct guesses and determine spin_change + # Construct guesses and determine spin_change if guesses is None: spin_change = determine_spin_change(matrix.method, kind, is_alpha) guesses = construct_guesses( @@ -258,10 +258,10 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, if matrix.method.adc_type == "pp": exstates = ExcitedStates(diagres) - # elif matrix.method.adc_type == "ip": - # exstates = DetachedStates(diagres, is_alpha) - # elif matrix.method.adc_type == "ea": - # exstates = AttachedStates(diagres, is_alpha) + elif matrix.method.adc_type == "ip": + exstates = DetachedStates(diagres, is_alpha) + elif matrix.method.adc_type == "ea": + exstates = AttachedStates(diagres, is_alpha) else: raise ValueError(f"Unknown ADC method: {matrix.method.name}") @@ -514,7 +514,7 @@ def construct_guesses( return obtain_guesses_by_inspection( matrix, n_guesses, kind, n_guesses_doubles, is_alpha, spin_change ) - + def diagonalise_adcmatrix(matrix, n_states, guesses, kind="any", conv_tol=None, eigensolver="davidson", output=sys.stdout, From 65bbd06836b747bb0d9d6e4273bf0917920076b9 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Fri, 6 Mar 2026 18:27:53 +0100 Subject: [PATCH 06/26] linting --- adcc/AdcMethod.py | 7 ++++--- adcc/ChargedExcitations.py | 14 +++++++------- adcc/__init__.py | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/adcc/AdcMethod.py b/adcc/AdcMethod.py index 254cec472..8ba83d227 100644 --- a/adcc/AdcMethod.py +++ b/adcc/AdcMethod.py @@ -27,9 +27,10 @@ def get_valid_methods(): valid_adc_types = ["ip", "ea"] valid_bases = ["adc0", "adc1", "adc2", "adc2x", "adc3"] - ret = (valid_bases - + [p + "-" + m for p in valid_prefixes for m in valid_bases] - + [t + "-" + m for t in valid_adc_types for m in valid_bases] + ret = ( + valid_bases + + [p + "-" + m for p in valid_prefixes for m in valid_bases] + + [t + "-" + m for t in valid_adc_types for m in valid_bases] ) return ret diff --git a/adcc/ChargedExcitations.py b/adcc/ChargedExcitations.py index 46cbbe3ef..c4ad5670e 100644 --- a/adcc/ChargedExcitations.py +++ b/adcc/ChargedExcitations.py @@ -36,13 +36,11 @@ def pole_strength(self) -> np.ndarray: """Array of pole strengths of all computed states""" pass - @cached_member_function(timer=_timer_name, separate_timings_by_args=False) def _pole_strength(self, state_n: int) -> np.ndarray: """Computes the pole strength for a single state""" pass - def describe_helper(self, block_norms=True, excitation_type_name="energy"): """ Creates and returns the to be printed columns @@ -129,8 +127,9 @@ def describe(self, block_norms=True): if hasattr(self, "kind") and self.kind: state_info.append(self.kind) spin_type = "alpha" if self.is_alpha else "beta" - state_info.append(f"(ΔMS={self.spin_change:+.1f}), " - f"{spin_type} detachment") + state_info.append( + f"(ΔMS={self.spin_change:+.1f}), {spin_type} detachment" + ) if hasattr(self, "converged"): conv = "converged" if self.converged else "NOT CONVERGED" if state_info: # add separator to previous entry @@ -181,12 +180,13 @@ def describe(self, block_norms=True): if hasattr(self, "kind") and self.kind: state_info.append(self.kind) spin_type = "alpha" if self.is_alpha else "beta" - state_info.append(f"(ΔMS={self.spin_change:+.1f}), " - f"{spin_type} attachment") + state_info.append( + f"(ΔMS={self.spin_change:+.1f}), {spin_type} attachment" + ) if hasattr(self, "converged"): conv = "converged" if self.converged else "NOT CONVERGED" if state_info: # add separator to previous entry state_info[-1] += "," state_info.append(conv) state_info = " ".join(state_info) - return self._describe(columns, state_info) \ No newline at end of file + return self._describe(columns, state_info) diff --git a/adcc/__init__.py b/adcc/__init__.py index d96501da2..946d8665f 100644 --- a/adcc/__init__.py +++ b/adcc/__init__.py @@ -64,7 +64,7 @@ "linear_combination", "zeros_like", "direct_sum", "memory_pool", "set_n_threads", "get_n_threads", "AmplitudeVector", "HartreeFockProvider", "ExcitedStates", "State2States", - "ChargedExcitation", "DetachedStates", "AttachedStates" + "ChargedExcitation", "DetachedStates", "AttachedStates", "Excitation", "ElectronicTransition", "Tensor", "DictHfProvider", "DataHfProvider", "OneParticleOperator", "OneParticleDensity", "TwoParticleOperator", "TwoParticleDensity", "OperatorSymmetry", From db45a074284bd20a4ca14fd4794e4612562dff86 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Mon, 4 May 2026 10:28:44 +0200 Subject: [PATCH 07/26] updated testdata --- adcc/tests/data/SHA256SUMS | 62 ++++++++++++++++++++++++++++++ adcc/tests/data/update_testdata.sh | 2 +- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/adcc/tests/data/SHA256SUMS b/adcc/tests/data/SHA256SUMS index 2d85c23d9..9d7c8c0be 100644 --- a/adcc/tests/data/SHA256SUMS +++ b/adcc/tests/data/SHA256SUMS @@ -5,12 +5,26 @@ eb433c83b7bde2693ef2ae9a6adc16ddfe21ae89dbb86778ff8227be919d237c cn_ccpvdz_adcc dd3b0776c3a1dd46e3720c7f2a2db8dfe26aae30a6a9bf9f14da2e9345742870 cn_ccpvdz_adcc_adc2.hdf5 d28c1ffd6a8bc5574db71889b171c5c535c3558fcfd81bb77d26560ebd7bbce8 cn_ccpvdz_adcc_adc2x.hdf5 8502e612dcf7b18261df569c12924949c1cd348455048da00f8bef70a28f1d68 cn_ccpvdz_adcc_adc3.hdf5 +3fc2e2acb0eeb91d761824a09275086d8d0f33ff02a3a5f414d908c6dce4d78e cn_ccpvdz_adcc_ea-adc0.hdf5 +5b3421798228b41b1edfcd03747ad820ccbdabc97908baa326e0d8c58f6e0100 cn_ccpvdz_adcc_ea-adc2.hdf5 +4e90e4a31e71d1e371138b44179708f240efceb41f47b4bdfccb1a066789ef69 cn_ccpvdz_adcc_ea-adc2x.hdf5 +dd41e5ff7f96ac8386611e35f13fac9da1d0bf5e50d90287436db8a4afa86c9b cn_ccpvdz_adcc_ea-adc3.hdf5 +2256d5cdbd446b5a5aebc357078d66658cb029966feb1637d3272e0515aaa4b6 cn_ccpvdz_adcc_ip-adc0.hdf5 +9e283d8d15258b5155ea188be480344c8389946547cd8f3e45e6caff2065e7c4 cn_ccpvdz_adcc_ip-adc2.hdf5 +767333f9f0dc0366a567e2b4cdd27b54d7608fc2fde2e6be0a56cf7d056ab8eb cn_ccpvdz_adcc_ip-adc2x.hdf5 +8f0ec3a22dd30d174a5c067040c14862f0b845e099319ab3c5ed71c5d0618ea9 cn_ccpvdz_adcc_ip-adc3.hdf5 4635cfbf161652c9f83379b119b2e1b7e520dc2a22be5693015820c865efe705 cn_ccpvdz_adcc_mpdata.hdf5 8f8c3c86cb9f4b22f8bcfff10e7092fcec4258fbf2a451e3cd6456b1adb7ba85 cn_ccpvdz_adcman_adc0.hdf5 be3f5e6090ba4d6fef72760e982bc268dc49f870db1afc101af60e08be559423 cn_ccpvdz_adcman_adc1.hdf5 259b19562741b25ff08491c446e69af92a34ad320d204b7dcfe5a6a4928a9072 cn_ccpvdz_adcman_adc2.hdf5 8a8349b6d5b7f9eaf1b51f9d68f0ae4bb1b36b5d74ec9a9bac580e27cf77aedc cn_ccpvdz_adcman_adc2x.hdf5 1efce537b0cc5d7cb227acee19241f6ce11ce7d4e29a3cd9570f869c2b2c3f7c cn_ccpvdz_adcman_adc3.hdf5 +3a245b724cb53d058ffd76f664eabed5c93b124f3077de82ab361369d3f5fc0d cn_ccpvdz_adcman_ea-adc0.hdf5 +80d3bb9122f0fd31a65a0c651479e4af8d04968c974faee1ba7d89bd6c7231dc cn_ccpvdz_adcman_ea-adc2.hdf5 +af48346ca95af1a4795d56a43dedd9c76404f52f0babd78d4947c75700dcb509 cn_ccpvdz_adcman_ea-adc3.hdf5 +51b34f119b24040e98d1fa750630146079c8e039948994149b93f6cd0dd45de3 cn_ccpvdz_adcman_ip-adc0.hdf5 +4979368df74815035adf6e4b84ef5da3068a4d7fbbe6e014d3902d28d4e4ef80 cn_ccpvdz_adcman_ip-adc2.hdf5 +a8743b12ccf3ba67716008972be04d58298c28f4b4624d4377861e0a2bc13ff5 cn_ccpvdz_adcman_ip-adc3.hdf5 0a2d6ebae22d84b9a4513e52c85f0f0294b6162bc25662c743d154bc81fa4128 cn_ccpvdz_adcman_mpdata.hdf5 db27db84b2dea991a4e24b6ce7433f2619769505267bd486723b1b9bd017a5f3 cn_ccpvdz_hfdata.hdf5 8c3f128cd46df98eaaba3658b849150459f738e9b3cc1cc42f2b5ff6ad288c9f cn_ccpvdz_hfimport.hdf5 @@ -19,12 +33,26 @@ db27db84b2dea991a4e24b6ce7433f2619769505267bd486723b1b9bd017a5f3 cn_ccpvdz_hfda 08f1ff54122f543cce6fb23d8e594ea58e342693ac94956f35d79781f790b6b9 cn_sto3g_adcc_adc2.hdf5 fe4da3d8dad6fb394329d6d409860641e99465b8d53f0efc880129238dfb4efd cn_sto3g_adcc_adc2x.hdf5 2a041d39ac6d1bbc1bc0365ba4e191a44f2e059c9c706891746500bb09dd2eb6 cn_sto3g_adcc_adc3.hdf5 +0c761fd68c9430a24f6e44df3acb458030d8df12d32b4e7540e0171a61718cf7 cn_sto3g_adcc_ea-adc0.hdf5 +6f1e4b0da533444c876ea8246332d7447d89b7d43fa5156f7c72ee9231c0adfe cn_sto3g_adcc_ea-adc2.hdf5 +90d62339fd7a39f89dbb8fe5869d7cee826ed663f8384f6da9fa100dd4aba469 cn_sto3g_adcc_ea-adc2x.hdf5 +f585ac33e3757e16306bdeb5f8e0c6a26a94799ff974a87464dc2da79f3357fe cn_sto3g_adcc_ea-adc3.hdf5 +c8daf37f81564682e93a1b6edee13fc51f55604c423434e7b9c12850344120d9 cn_sto3g_adcc_ip-adc0.hdf5 +d1c54c83b79509dd54dedac1dfc6f5530e6de782ac8d1cfb892a616f73180b14 cn_sto3g_adcc_ip-adc2.hdf5 +1f02e1c648759dc2562ad8a4e092c927617a7124e884e56539a4ecc7a2288722 cn_sto3g_adcc_ip-adc2x.hdf5 +384d99c0c585e59210f5d323a8ae8bcfcc9a163cb3ad93f5332bded421b057e2 cn_sto3g_adcc_ip-adc3.hdf5 582cc08bb5b63599f67511b4a262c3c7d88d80d3bb99cb306b06865e94116451 cn_sto3g_adcc_mpdata.hdf5 f6974049ccf8a732ea584dbbebf608f0e17d28bd5320c0c63e1d1a5db80f54a7 cn_sto3g_adcman_adc0.hdf5 9950c0720546a0b313e755945e7fd0d5462967a4366f60470c4a85f500bffb5f cn_sto3g_adcman_adc1.hdf5 a866f1c06508e1bf48567c2546938e471b83c82ecce7dfce6815288c1e0226a6 cn_sto3g_adcman_adc2.hdf5 7b6e09a40fd22f211119afe8f6f46d2c573ca905fa69c52f6f5d053e2c26447e cn_sto3g_adcman_adc2x.hdf5 2312e925cb33a47bf476c5528531c0c6908b8803a5d5f6785d1ff0f7da946353 cn_sto3g_adcman_adc3.hdf5 +333b8b76527bc1e017230aa6e7d030faad001cfa0d5d382677264699dc3b3081 cn_sto3g_adcman_ea-adc0.hdf5 +ef78680d939d2e5204bb7217481c980c2d46cbccbc9da6375b0db975e92b34f4 cn_sto3g_adcman_ea-adc2.hdf5 +4ec353b931bd0986dad3cc9823a3ec6634d39ee2ebd1bea6c800ba047c32012b cn_sto3g_adcman_ea-adc3.hdf5 +b36e8397ee7af7eb627be2ab355a6b20e90b3a417f42b60ead0b5e8bde16c9d8 cn_sto3g_adcman_ip-adc0.hdf5 +cfc731c423999b1b1f79929e180f7d3cc76199079b4fc69dca1b2e0ccf9672ba cn_sto3g_adcman_ip-adc2.hdf5 +99b1ba20f1bc57be2acc083edd5ce31dcd1d2eedb6cb8e286862821bc4adb629 cn_sto3g_adcman_ip-adc3.hdf5 e9a94d14102eba9e6b6aaf0728615e5bfecacde501743032813f5b0df283bd7e cn_sto3g_adcman_mpdata.hdf5 7d843dd9981ef78f11a78051aea1ea61af8f12dc244b93e3f76fd70602c8f86e cn_sto3g_hfdata.hdf5 73adda09433dab2263e3dc2b37e10d5127e4c54a4296fdf2574ab71ec44d172a cn_sto3g_hfimport.hdf5 @@ -43,12 +71,26 @@ b579b2e62d4d02c0e5d16ab8b8e30009582152dba63ac12cea77f7dbbd80c29d formaldehyde_s 28e018d79c5481938f77177fbeb6f65e6c04122ef7f8fc3db478a5d31592da6c h2o_def2tzvp_adcc_adc2.hdf5 4a35c56f21d7c0ae424225bc756ac9b8a9f9b2f32b90e5aea45351b4249196f2 h2o_def2tzvp_adcc_adc2x.hdf5 6157f5d41dec84612de81005b17cdcc531efc74d5da42e1a49d2cb53492f3248 h2o_def2tzvp_adcc_adc3.hdf5 +1810c6f136e8ae097609e4e9f43d2d7fbd3c419bfdd9e9d401a9ce16768a4f26 h2o_def2tzvp_adcc_ea-adc0.hdf5 +d86c4f3b686e3afcfad1019abbf7edffbab889f03e0e685265cfcaa5df09a88d h2o_def2tzvp_adcc_ea-adc2.hdf5 +3805e99fddbf4f28f441c7cbd75bdced72eaab045e04a2ca867d81ea24416aae h2o_def2tzvp_adcc_ea-adc2x.hdf5 +3f253a182662f2be98fe82aac930929cd128cf1e42b21aab8846f87496865ffd h2o_def2tzvp_adcc_ea-adc3.hdf5 +cb8dc08120716cf28ccf9b04d5c69a16f125317fae79a7ee5a3317220f79ef97 h2o_def2tzvp_adcc_ip-adc0.hdf5 +a7f58daa5fd1d8f4c3e54b5bd8bc957dd309b3df386744dab9271aa3982e600a h2o_def2tzvp_adcc_ip-adc2.hdf5 +fb26959b3beb45ac6222c542263fb74dbc448ac0f842073edae2bf467a51a141 h2o_def2tzvp_adcc_ip-adc2x.hdf5 +90fdfbbb3f2ca4e69bd6329c111a30cea3484f467b3c010722fb6c717cdce1ff h2o_def2tzvp_adcc_ip-adc3.hdf5 bb7850aad29b723e8b2317cdb2fa45229eab6ff5772c7c2eab681ec527c3bf93 h2o_def2tzvp_adcc_mpdata.hdf5 2a88059266ac32a34cc352e1d2f782565317cec8573bdd81c4e4019ecc9adb65 h2o_def2tzvp_adcman_adc0.hdf5 dcf8ae501f7875705191abf754b33cf82f14065db2c95ab84ef3004b70ee9e75 h2o_def2tzvp_adcman_adc1.hdf5 7cd549b23a8939f5c989882b250284d5f6b20d14e97055f5caba7e6a6138f4b2 h2o_def2tzvp_adcman_adc2.hdf5 24394b48a8218eed94d2c9462df8ecd065ec3ad985e215389fee535268584c2f h2o_def2tzvp_adcman_adc2x.hdf5 86b875b3f7e7695477f2fc34b3860b9736e5d23c8803278f88cda7eadf9d833f h2o_def2tzvp_adcman_adc3.hdf5 +373dad86be62b2f255a58d1d2371d1b6ca4adb8c33bf2a168ad729138973f76f h2o_def2tzvp_adcman_ea-adc0.hdf5 +cae121c0658e8cef2b79d0adc119d4397787a8a5c5f8f4608225ba0266e6a0d0 h2o_def2tzvp_adcman_ea-adc2.hdf5 +cde62e824358e468d10a44b2b8271afec44eb0b350ad2b54f4168c0b90b32e54 h2o_def2tzvp_adcman_ea-adc3.hdf5 +d06b4f8c48e072009cf4f830cf882f9b4b887fe5eeb02bc7d72b69cfc4c3795b h2o_def2tzvp_adcman_ip-adc0.hdf5 +1f29b185958fddd686782425030b3014ddf4e363e8e73a146d9e3f7a20b114d5 h2o_def2tzvp_adcman_ip-adc2.hdf5 +8fc2922530d65cb23d821e662f9dedee62d91ad2508bf246ed3cbba640dbf75c h2o_def2tzvp_adcman_ip-adc3.hdf5 a07821177dd07f8f95a64aa008b7317e68357e45761742727394823fa242afb5 h2o_def2tzvp_adcman_mpdata.hdf5 36b3b93aa1071e61611d96b6b59ed13d37ec718303b8ef60a6c10aef07276717 h2o_def2tzvp_hfdata.hdf5 8a447ed3025662a2a71d866c697d345dbace45655e14c8777c10190f7f4c5967 h2o_def2tzvp_hfimport.hdf5 @@ -57,12 +99,26 @@ b56e74c8e71a9ab5ef62d44131c721e8d5ce236f9ed69d82869d702468aff7a8 h2o_sto3g_adcc 18c78aae03b0315cb3fcd932ce1ee1f3040db355ed38a61b46e2142d8e28b8ef h2o_sto3g_adcc_adc2.hdf5 8e17428b0d7e93e91e3ba366f7d2a9aded91a5d8d37b47fcc790c20b5b150872 h2o_sto3g_adcc_adc2x.hdf5 de9a094a6da75d063a7d1a4137347409c7dd2ced6ae1bdd2ebece6b2712506d6 h2o_sto3g_adcc_adc3.hdf5 +a4a1ca45633ea71d3e3a760090bfdb229c0ad5f4ad5120b6d93e16a34a183834 h2o_sto3g_adcc_ea-adc0.hdf5 +b2c91bc8cae5a99c51296684f7f418d21c7a47b668e19541f3c80383ab0fef8a h2o_sto3g_adcc_ea-adc2.hdf5 +97beb080141eca34d564eb58162a46d4a2b0d7932ffd217f0fe472b28995e113 h2o_sto3g_adcc_ea-adc2x.hdf5 +9d5d0f5d2ce1363176002a299c94262d8ef02288feb5030f78fedadfd67c0280 h2o_sto3g_adcc_ea-adc3.hdf5 +f51fdacc11e39356304682ee43e889d51affc7f56eae5650bc571625385d9c7c h2o_sto3g_adcc_ip-adc0.hdf5 +017667fb59059c4bcef47f12dea8f996789a469d35e78d04a641635d2196fdb1 h2o_sto3g_adcc_ip-adc2.hdf5 +8df1ef913ee40f87cd86d301aa4125f952b2660e7fe12dda7a10f7ea90fd6ed6 h2o_sto3g_adcc_ip-adc2x.hdf5 +c952684e6d8ac66038d53cb44e3c5fe9c5a3621733c7dc8ca391d40c3a50cd3c h2o_sto3g_adcc_ip-adc3.hdf5 32d3b2aa4b40ae58b4c1b71b102bdd72e21d42b9f4050fba734a2d18eb6ffa02 h2o_sto3g_adcc_mpdata.hdf5 cc957d99adaf872877b992860c9f0c9c8337f783b9c3ad20b36694391627188f h2o_sto3g_adcman_adc0.hdf5 4b36ac6cce98cf250ad268dc6e51e849acbd8c9383aa16aff233a2cf04118fcd h2o_sto3g_adcman_adc1.hdf5 3e63750574a0ba8a8137dcea7405a9dd045a1712bda043d7f227262146a881b8 h2o_sto3g_adcman_adc2.hdf5 dbf6b078e8096f8283cc64d1de98dff91b5edebd1d2f1df4f5739cfcecd98db7 h2o_sto3g_adcman_adc2x.hdf5 7a65c053bea13cc431ad127120578d27118ee9f9c026f6402dc07953cfb96c23 h2o_sto3g_adcman_adc3.hdf5 +84e1e96fdb30971f2e5f1a9501e9445a8dce031f8e36d6e46360a132440269f7 h2o_sto3g_adcman_ea-adc0.hdf5 +fe9806e6f42104c25c4e022484e670588c343bb55761baca599e01cd98b145f9 h2o_sto3g_adcman_ea-adc2.hdf5 +10e6b9d7e3b5d088233b61dd599946f55a579c96cefd0d21feee9c4893bc88c4 h2o_sto3g_adcman_ea-adc3.hdf5 +c936ae6eb312921adcf1a3fc8bac19453eebbe29f8de4a574ac981b2ef55fffa h2o_sto3g_adcman_ip-adc0.hdf5 +abeaeacf2248d3c779a605eaf5d8313029593e22f8656c1baa5bcb0a983e4fcb h2o_sto3g_adcman_ip-adc2.hdf5 +dd39481ff664de5c228919687ea3fbcf1effaa0aaddff4750d008cb25066b956 h2o_sto3g_adcman_ip-adc3.hdf5 00abfa6fcd237571167e42e3dfd956550944a5f6d68f462e6017629c175f4730 h2o_sto3g_adcman_mpdata.hdf5 6c64153a6e708edad4c8a07e0ed6e5e1379e8e9c721e49f2aa0cc4ecb9817082 h2o_sto3g_hfdata.hdf5 74733b03dd12561479132a768c2b330b4b8dbf1fe60fe0b14caa4d3fd4da7fce h2o_sto3g_hfimport.hdf5 @@ -77,6 +133,12 @@ d14ede49f1fb6e9700d7306db23001bb2cd7469ae7a14849179f683eee5cafe4 hf_631g_adcman 5083d69135cf29738881568e270a6e2866d6e8b44c6d4e548180bf999f8ada80 hf_631g_adcman_adc2.hdf5 ff8cba744d8820c1877104985454b611ea54e7f1bbbac683079911ef19b9dd25 hf_631g_adcman_adc2x.hdf5 3889abfa9dca22999d2aaefc9b4efc64d503ecc2953b33ecf507f5328742832b hf_631g_adcman_adc3.hdf5 +61bb4bc0b207e917ef700659b18a2a2d9cdcdb818b82acf9dffefd55be3d7466 hf_631g_adcman_ea-adc0.hdf5 +8b4e09136695ebce7ba2f1e726d371ccf5d47d6946af683cb7c5010125f4117f hf_631g_adcman_ea-adc2.hdf5 +dc260e8d63efb307b7e19ec4d9d67efaba9c4fa55806b2b0876246de3cf5acc1 hf_631g_adcman_ea-adc3.hdf5 +d985ebce808b05b8fd9c57d4775bcc84bfb5d6c0ec30ec9c1606282acedeb449 hf_631g_adcman_ip-adc0.hdf5 +407a7ef96eed211ff9b591ab9cd4eaa6b09acc504b2a9fc77499e1ddbff97dcf hf_631g_adcman_ip-adc2.hdf5 +55132fbeeb9a63d77590b56276a416d906118a417bdb62c0c10aabf0846ea3e3 hf_631g_adcman_ip-adc3.hdf5 1d7b0dd4e61aea9faf713706e5ec143426e97a55da4df575d6bd72e5dfcd872b hf_631g_adcman_mpdata.hdf5 a1eab1dd706a3f8f7532889b9a4754fa627a47ce5cfb9eb7d95a8365774850f2 hf_631g_hfdata.hdf5 0e86aef221cc541cbade47e99471fa7c0bc1955c80783be4b32a8ea58902b2f6 r2methyloxirane_sto3g_hfdata.hdf5 diff --git a/adcc/tests/data/update_testdata.sh b/adcc/tests/data/update_testdata.sh index 325a85fa6..616febaab 100755 --- a/adcc/tests/data/update_testdata.sh +++ b/adcc/tests/data/update_testdata.sh @@ -4,7 +4,7 @@ trap "exit 1" TERM export SCRIPT_PID=$$ -SOURCE="https://wwwagdreuw.iwr.uni-heidelberg.de/adcc_test_data/0.7.4/" +SOURCE="https://wwwagdreuw.iwr.uni-heidelberg.de/adcc_test_data/0.8.0/" SHAFILE="SHA256SUMS" From a0ac6003585f067cb8e844346dfe3bdc8891a523 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Mon, 4 May 2026 13:08:32 +0200 Subject: [PATCH 08/26] new SHASUMS --- adcc/tests/data/SHA256SUMS | 200 +++++++++++++++++++++++++++++++++++-- 1 file changed, 190 insertions(+), 10 deletions(-) diff --git a/adcc/tests/data/SHA256SUMS b/adcc/tests/data/SHA256SUMS index 9d7c8c0be..343807e95 100644 --- a/adcc/tests/data/SHA256SUMS +++ b/adcc/tests/data/SHA256SUMS @@ -1,5 +1,5 @@ -63f70983becdfcabde6b309e6d0f7029be38f6784233733392f85c1974d74b85 ch2nh2_sto3g_hfdata.hdf5 -f5f1f76c1dfe30636d2752c9bba6c9747637e96feea6870ee73995351d169a28 ch2nh2_sto3g_hfimport.hdf5 +d382636d873f26f9872a9165b398317ae0bf81179f4c77e6cbf227af29ee9635 ch2nh2_sto3g_hfdata.hdf5 +424db3133e90ba113ef2800e115054a382099202976bf58dc560da53389f7da5 ch2nh2_sto3g_hfimport.hdf5 eb433c83b7bde2693ef2ae9a6adc16ddfe21ae89dbb86778ff8227be919d237c cn_ccpvdz_adcc_adc0.hdf5 9f3210e752534b0bbab375733e163416d3b7d80b69250293bcee9946f9c3a7e6 cn_ccpvdz_adcc_adc1.hdf5 dd3b0776c3a1dd46e3720c7f2a2db8dfe26aae30a6a9bf9f14da2e9345742870 cn_ccpvdz_adcc_adc2.hdf5 @@ -14,6 +14,16 @@ dd41e5ff7f96ac8386611e35f13fac9da1d0bf5e50d90287436db8a4afa86c9b cn_ccpvdz_adcc 767333f9f0dc0366a567e2b4cdd27b54d7608fc2fde2e6be0a56cf7d056ab8eb cn_ccpvdz_adcc_ip-adc2x.hdf5 8f0ec3a22dd30d174a5c067040c14862f0b845e099319ab3c5ed71c5d0618ea9 cn_ccpvdz_adcc_ip-adc3.hdf5 4635cfbf161652c9f83379b119b2e1b7e520dc2a22be5693015820c865efe705 cn_ccpvdz_adcc_mpdata.hdf5 +f4bd184d86c7a27cdc2bcb99fc073790e541c97e9b92ff9b4d0452ccadc63f64 cn_ccpvdz_adcc_reference_adc0.hdf5 +9adf1e7ec2b0aa1c34a92354f27943f5a1d3d941a0f566ae249bc11895121629 cn_ccpvdz_adcc_reference_adc1.hdf5 +c6bc0b7cbcca51c1f6eee1363a18ba13bed9ff5d5a4681d8941bdf1a0dbb0164 cn_ccpvdz_adcc_reference_adc2.hdf5 +735bd5f7d121efe6aef15eecf96dcc51681b6f98815f55b261f1c840fc826add cn_ccpvdz_adcc_reference_adc2x.hdf5 +dbc64230873e766238bdf242d6ce8b4b7e19c336f84839dd4c85b54ba0d874f5 cn_ccpvdz_adcc_reference_adc3.hdf5 +61f733c80b2e0dd7483e4e9b3041c365caa4235c77420fd9492bea71cfeaf25b cn_ccpvdz_adcc_reference_cvs_adc0.hdf5 +f43c9bd7c097af3937efb4ac6abb42b442ada4e0fc7c36e34a9ea62a8b7a2c7f cn_ccpvdz_adcc_reference_cvs_adc1.hdf5 +371746977181c6e71ab4fca02a73463a8ecc9e010fe2dd9b1deb41f5a01db083 cn_ccpvdz_adcc_reference_cvs_adc2.hdf5 +cf9d2eedb6f16b5d5a6267d20e414460b916e84142ae4f3f1f40dd97c20311df cn_ccpvdz_adcc_reference_cvs_adc2x.hdf5 +5158edbe44f804b88ca4c7953fdda435189d27f0cbe586216b66dba4cc9dd83a cn_ccpvdz_adcc_reference_cvs_adc3.hdf5 8f8c3c86cb9f4b22f8bcfff10e7092fcec4258fbf2a451e3cd6456b1adb7ba85 cn_ccpvdz_adcman_adc0.hdf5 be3f5e6090ba4d6fef72760e982bc268dc49f870db1afc101af60e08be559423 cn_ccpvdz_adcman_adc1.hdf5 259b19562741b25ff08491c446e69af92a34ad320d204b7dcfe5a6a4928a9072 cn_ccpvdz_adcman_adc2.hdf5 @@ -26,8 +36,18 @@ af48346ca95af1a4795d56a43dedd9c76404f52f0babd78d4947c75700dcb509 cn_ccpvdz_adcm 4979368df74815035adf6e4b84ef5da3068a4d7fbbe6e014d3902d28d4e4ef80 cn_ccpvdz_adcman_ip-adc2.hdf5 a8743b12ccf3ba67716008972be04d58298c28f4b4624d4377861e0a2bc13ff5 cn_ccpvdz_adcman_ip-adc3.hdf5 0a2d6ebae22d84b9a4513e52c85f0f0294b6162bc25662c743d154bc81fa4128 cn_ccpvdz_adcman_mpdata.hdf5 -db27db84b2dea991a4e24b6ce7433f2619769505267bd486723b1b9bd017a5f3 cn_ccpvdz_hfdata.hdf5 -8c3f128cd46df98eaaba3658b849150459f738e9b3cc1cc42f2b5ff6ad288c9f cn_ccpvdz_hfimport.hdf5 +37b3af4a4f1b70c7bc7588f5bfcd542fc96056ccb7fe2e8c9fbda93b546788a0 cn_ccpvdz_hfdata.hdf5 +a15001b874327b1cec2c84bfe4a24695c71633f31aaf618dc25744dda2c1022b cn_ccpvdz_hfimport.hdf5 +7e3a04d69a8110a78c90ada8e3dbccefaf83bee20fc1bf15fbef1d0a610edd44 cn_ccpvdz_reference_adc0.hdf5 +ddcfbff7d784eca92ff8150f6938cf765444f41d2fb6140ba462b54378e6bdd5 cn_ccpvdz_reference_adc1.hdf5 +d0486fe1ce6605d26ad210e35620b3fb9ffe6ecbce91b89f0f1b845781ef9adf cn_ccpvdz_reference_adc2.hdf5 +a424ae21c0b391c66b5326b70c740f1f2350c67121c1c110afee129d374eda75 cn_ccpvdz_reference_adc2x.hdf5 +61ff02b79c536f0c45cc1f1616ea1d2249424377db920e87cc9f746e0f969763 cn_ccpvdz_reference_adc3.hdf5 +8d8ad88e46d06fbec47fcfbc714a6240830c46826ecb0b2c918536fa140aafa3 cn_ccpvdz_reference_cvs_adc0.hdf5 +3deb56a97eb9f75b1a643e0443c8af7116b9ce701f86da321cd7614da0e0bf54 cn_ccpvdz_reference_cvs_adc1.hdf5 +f4c11821f40518fc137b88c7975b83b7e273bef578e5663a6072265d295015d1 cn_ccpvdz_reference_cvs_adc2.hdf5 +aedb569d840eb1194e64049c7ed215a6a3f7796c33509ce402a6ccdab8c51238 cn_ccpvdz_reference_cvs_adc2x.hdf5 +231e170e5b79aee977b010761030be61d4769ea6757f2f6eaea9b7059bbbb796 cn_ccpvdz_reference_cvs_adc3.hdf5 94665e00317ec7a20540f934dd0306403acda144a581f7c5a1abab14cc599354 cn_sto3g_adcc_adc0.hdf5 54020255ce72d4700758c6790207c62c61a25091380d8bd8b6cf4b21c6dfcfc9 cn_sto3g_adcc_adc1.hdf5 08f1ff54122f543cce6fb23d8e594ea58e342693ac94956f35d79781f790b6b9 cn_sto3g_adcc_adc2.hdf5 @@ -42,6 +62,40 @@ d1c54c83b79509dd54dedac1dfc6f5530e6de782ac8d1cfb892a616f73180b14 cn_sto3g_adcc_ 1f02e1c648759dc2562ad8a4e092c927617a7124e884e56539a4ecc7a2288722 cn_sto3g_adcc_ip-adc2x.hdf5 384d99c0c585e59210f5d323a8ae8bcfcc9a163cb3ad93f5332bded421b057e2 cn_sto3g_adcc_ip-adc3.hdf5 582cc08bb5b63599f67511b4a262c3c7d88d80d3bb99cb306b06865e94116451 cn_sto3g_adcc_mpdata.hdf5 +f20b093bb9c549930dc975cfad5e91bba483a4abb55003b572709d3c032eabea cn_sto3g_adcc_reference_adc0.hdf5 +ce65f98abae7695f88ade37a9b222ab00b72748cced3b30c9c3114fed78e917a cn_sto3g_adcc_reference_adc1.hdf5 +bb96ca05a83db0eb64017267c112032e247796f2efdf14a4b74f8b095b177326 cn_sto3g_adcc_reference_adc2.hdf5 +ef41cb609e3d80934a7c4f1ea4e83480406c96c87d61ce59fd68b823e0e43b7d cn_sto3g_adcc_reference_adc2x.hdf5 +d6f8e6a8bf4ac0a38bb7c81dad2a41e6fed814255f3937326c2fb153754a6d04 cn_sto3g_adcc_reference_adc3.hdf5 +ea5099cc8350d8bb3532c0aa9f4c0f2f726e2e2bd96c03a2c3249cade70d22c1 cn_sto3g_adcc_reference_alpha_ea_adc0.hdf5 +20b01b7ef16d75807fb33d334be053bc0cd3593eda19ff5664eab74412d3214c cn_sto3g_adcc_reference_alpha_ea_adc1.hdf5 +dd0d4ea21bb42c4eef9293464790f0b699cbc29aea3054b2b30ee63f6834eebb cn_sto3g_adcc_reference_alpha_ea_adc2.hdf5 +d8a3cf92315c500525bd0d18493f964906d89d2095997a124443ba9647c08350 cn_sto3g_adcc_reference_alpha_ea_adc2x.hdf5 +d86ce652e662a5ac01199dccd39cfe226cb9e05163f6a26f573e5759399669ab cn_sto3g_adcc_reference_alpha_ea_adc3.hdf5 +e299922c4939c1ad1f1bcc14cb9f6ef63d3014c17ba7e39611d9bd88e4071213 cn_sto3g_adcc_reference_alpha_ip_adc0.hdf5 +4237b58b634eedd5b0017241515a96251ee4b163fc1f772d19ae614a4f588bb2 cn_sto3g_adcc_reference_alpha_ip_adc1.hdf5 +c2b91b123c49b864657499b55d3e8e40123552d4a301e8b71726cdd984cd71d8 cn_sto3g_adcc_reference_alpha_ip_adc2.hdf5 +4942f21574b0c9b4e39891f86e5a2b4fa3cd0040a9caca90f04ebc1bb211e8d8 cn_sto3g_adcc_reference_alpha_ip_adc2x.hdf5 +38a6aa1f9cb60cb01dd46820d2aaeeea33a3efd5a3e86cf3012c5c43b688de83 cn_sto3g_adcc_reference_alpha_ip_adc3.hdf5 +b442db51ed1461f2c7ad1a0e903ea5577d79439c053684c244d053985ec57c4e cn_sto3g_adcc_reference_beta_ea_adc0.hdf5 +ead2b116e0ec42d46b55c8d548fcfef4d80b79d62d8ce4edfed833086f397e3c cn_sto3g_adcc_reference_beta_ea_adc1.hdf5 +34c1984d008befbe6acb376de87ced00addce0e1c916af915890d857d957aeec cn_sto3g_adcc_reference_beta_ea_adc2.hdf5 +ec62f66eeefaf5c68823be506d17144d281b9c3010ff3bcf69876cd9c4de7607 cn_sto3g_adcc_reference_beta_ea_adc2x.hdf5 +791dd15dcfbfc0524cb7435385b3e4c8c204252d3be75b22ce2b0651dbcb5a5b cn_sto3g_adcc_reference_beta_ea_adc3.hdf5 +64ccde2cc69bf2e286ddb4c53dedaf446e2836d6095a37b9aa81c0197b3c0bf9 cn_sto3g_adcc_reference_beta_ip_adc0.hdf5 +94774f17c987205e2c0eca3f1d4bd561262d208faa1e9c5f4fca6882980b6320 cn_sto3g_adcc_reference_beta_ip_adc1.hdf5 +5fd98a5538264f56870e899cbdb0b4ec291006813107bf7e3b37cd74daf11c0f cn_sto3g_adcc_reference_beta_ip_adc2.hdf5 +3c8a2f79348163433e6b4f867dcdf7e0c2f03f3faf1dcef163977dc7a3096d3b cn_sto3g_adcc_reference_beta_ip_adc2x.hdf5 +490af368d0e4ceee9e570cc199299e7648913a027fafa3c5d70b02e40549113e cn_sto3g_adcc_reference_beta_ip_adc3.hdf5 +0b9afd705e310c23f9ce1e4d6b7a7796a7976f13db93523ed3bf9774b08f9134 cn_sto3g_adcc_reference_cvs_adc0.hdf5 +a421c00c313cc4db1bcebdc83278dca80196623ecac6f957721d4c6afdeff039 cn_sto3g_adcc_reference_cvs_adc1.hdf5 +575c3229ebe0a8015e4b62e7cd0a62b6d0f4dc4abd71f27b2dd1441abfcad6e3 cn_sto3g_adcc_reference_cvs_adc2.hdf5 +d3428482d4b4a073c663d8dd7bfd09e532de36855d9276aa0774051b0861fa2c cn_sto3g_adcc_reference_cvs_adc2x.hdf5 +bb85cf1cc5c6e95f5b12588fa865d92911215250f6386ae6e7f05bd28beec0a0 cn_sto3g_adcc_reference_cvs_adc3.hdf5 +99da6cef0bc4a98504408cfb8cad849d7269054d879e9baff9355e928ddd36c3 cn_sto3g_adcc_reference_fc_adc2.hdf5 +36be0f5ffd45babd12f9d3cdfe29ecd1333925aaeba28a3d6d0f5d28e55ac85d cn_sto3g_adcc_reference_fc_fv_adc2.hdf5 +00ab8825070cf6848119208b1a29237e3f855d7f09694c7e21cd71854dd5cae3 cn_sto3g_adcc_reference_fv_adc2x.hdf5 +7f76d4d0e31c4bfe4a4b7c2e0540a29d54149b78cd0809f66c831006d3b51f02 cn_sto3g_adcc_reference_fv_cvs_adc2x.hdf5 f6974049ccf8a732ea584dbbebf608f0e17d28bd5320c0c63e1d1a5db80f54a7 cn_sto3g_adcman_adc0.hdf5 9950c0720546a0b313e755945e7fd0d5462967a4366f60470c4a85f500bffb5f cn_sto3g_adcman_adc1.hdf5 a866f1c06508e1bf48567c2546938e471b83c82ecce7dfce6815288c1e0226a6 cn_sto3g_adcman_adc2.hdf5 @@ -54,8 +108,22 @@ b36e8397ee7af7eb627be2ab355a6b20e90b3a417f42b60ead0b5e8bde16c9d8 cn_sto3g_adcma cfc731c423999b1b1f79929e180f7d3cc76199079b4fc69dca1b2e0ccf9672ba cn_sto3g_adcman_ip-adc2.hdf5 99b1ba20f1bc57be2acc083edd5ce31dcd1d2eedb6cb8e286862821bc4adb629 cn_sto3g_adcman_ip-adc3.hdf5 e9a94d14102eba9e6b6aaf0728615e5bfecacde501743032813f5b0df283bd7e cn_sto3g_adcman_mpdata.hdf5 -7d843dd9981ef78f11a78051aea1ea61af8f12dc244b93e3f76fd70602c8f86e cn_sto3g_hfdata.hdf5 -73adda09433dab2263e3dc2b37e10d5127e4c54a4296fdf2574ab71ec44d172a cn_sto3g_hfimport.hdf5 +31cea3a0bdfe517d1be6615c98849578704377f2ad6134ce127531798e43ad95 cn_sto3g_hfdata.hdf5 +a305861c174acddcdffaa886775ee79c8ed5f41aa19493bbe7f053a7e6996a5f cn_sto3g_hfimport.hdf5 +f28b3a47be0cbe025a002762b7d2f4617116f3c9d7e2485c5d9152409046c2c5 cn_sto3g_reference_adc0.hdf5 +0a9142c913e54f2e424fa7acc97677d06b81b93b25c45733e6336ff9be64ea56 cn_sto3g_reference_adc1.hdf5 +fe788ddb20a175f10090aae392c0a290911f1bdf85bd1d848a0c71fad5fb469a cn_sto3g_reference_adc2.hdf5 +1d41863238d18734a9623d74c064005ec69dc86cf50ac783ad448e348e66d3fe cn_sto3g_reference_adc2x.hdf5 +52ae9edbf07d367354fcb567193178f0a66e6cbcb062bfdacc7417a264923208 cn_sto3g_reference_adc3.hdf5 +d78c5a44cdecf441bb0a14ceb0eb0f23c144ea96f98b4b1ceeb956a735909729 cn_sto3g_reference_cvs_adc0.hdf5 +7cb6d961414737c3722980f6793670e3b2046b7c81a20889c849bf9b5b207a15 cn_sto3g_reference_cvs_adc1.hdf5 +b565ab09c86ecefbe2d71157ce41e093c421e04697d0056e7f6cdfb0195b7dd9 cn_sto3g_reference_cvs_adc2.hdf5 +17f7ac21b49de29d8a282b9c38b158e24e1865376ebf7bbfe73ea4b53da71555 cn_sto3g_reference_cvs_adc2x.hdf5 +d26bfe00e8b3cfa8891028e0d3d3dd85b7de3f21450bae6eb11f8ad2db340678 cn_sto3g_reference_cvs_adc3.hdf5 +573c343fa28cd3c58b271cbc94dc281bfa14a992a3fd3de392a29fd165ce7367 cn_sto3g_reference_fc_adc2.hdf5 +5a5442ee98f2b5b34474a030764036d227e17efb9c2c728d0bbb672e1149ff0d cn_sto3g_reference_fc_fv_adc2.hdf5 +9b7cfc0c05478837fb919202ddda2d7a0a4a767d1081dc2056fb425dd29b7acf cn_sto3g_reference_fv_adc2x.hdf5 +16a2362662192d9ffa1f7e8762ef8e3713bce6a92bc2028668c6679d3f646806 cn_sto3g_reference_fv_cvs_adc2x.hdf5 8c7d88af083003a190a5561d31d1c9c231c0cb1e149a00f8b6cd92d020871332 formaldehyde_ccpvdz_adcman_adc0.hdf5 c55ccf23bd94fcd2e3660cc73915e4994c59143eaf92ea96a99df295bfd90be6 formaldehyde_ccpvdz_adcman_adc1.hdf5 3271fcb5f13f512cba10a807c13258e95db25d5854e4008e91ac0b0e0481ba77 formaldehyde_ccpvdz_adcman_adc2.hdf5 @@ -80,6 +148,16 @@ a7f58daa5fd1d8f4c3e54b5bd8bc957dd309b3df386744dab9271aa3982e600a h2o_def2tzvp_a fb26959b3beb45ac6222c542263fb74dbc448ac0f842073edae2bf467a51a141 h2o_def2tzvp_adcc_ip-adc2x.hdf5 90fdfbbb3f2ca4e69bd6329c111a30cea3484f467b3c010722fb6c717cdce1ff h2o_def2tzvp_adcc_ip-adc3.hdf5 bb7850aad29b723e8b2317cdb2fa45229eab6ff5772c7c2eab681ec527c3bf93 h2o_def2tzvp_adcc_mpdata.hdf5 +2022ff858b4640036b32b66086afedf44832e161fb9128d278d4a37ea4bc8259 h2o_def2tzvp_adcc_reference_adc0.hdf5 +88aef14092089f6645002cd2c47366cf93860124dd96625d983dfd856419a9e7 h2o_def2tzvp_adcc_reference_adc1.hdf5 +0321b11dcd78b9fb1e781deb8f17390b4a03a4bec5877aa717317f7e71adfe78 h2o_def2tzvp_adcc_reference_adc2.hdf5 +0510127944907a35a29034e3a1d3ffec86b16c6e092a8ab0b6eb9c29593bb553 h2o_def2tzvp_adcc_reference_adc2x.hdf5 +c8219c649979db34650b2804ab7c737cf7b1bd2bde5987ac3b3ce782e676ab25 h2o_def2tzvp_adcc_reference_adc3.hdf5 +56eef33b7035b7f62fd048c435dc75559bedc4bee90ee7c629d2ad68f161d933 h2o_def2tzvp_adcc_reference_cvs_adc0.hdf5 +5092e81d5ffa1fe24e6a121a08fd9e3ccae64c212563d88d48f8c2093fe45ddb h2o_def2tzvp_adcc_reference_cvs_adc1.hdf5 +5f56c5e8e1295aeaf776bd9a7d6efc2022cc1386018151916b7ee84a9d407962 h2o_def2tzvp_adcc_reference_cvs_adc2.hdf5 +17d531af3b39970ee81217f40d41949252ff9f6e7d7e5860b8e6a46b91930289 h2o_def2tzvp_adcc_reference_cvs_adc2x.hdf5 +0f402d16d1018144fe67ec3a3e49853db3bf06dcb66deca4ab9a0792c7fcd34f h2o_def2tzvp_adcc_reference_cvs_adc3.hdf5 2a88059266ac32a34cc352e1d2f782565317cec8573bdd81c4e4019ecc9adb65 h2o_def2tzvp_adcman_adc0.hdf5 dcf8ae501f7875705191abf754b33cf82f14065db2c95ab84ef3004b70ee9e75 h2o_def2tzvp_adcman_adc1.hdf5 7cd549b23a8939f5c989882b250284d5f6b20d14e97055f5caba7e6a6138f4b2 h2o_def2tzvp_adcman_adc2.hdf5 @@ -92,8 +170,18 @@ d06b4f8c48e072009cf4f830cf882f9b4b887fe5eeb02bc7d72b69cfc4c3795b h2o_def2tzvp_a 1f29b185958fddd686782425030b3014ddf4e363e8e73a146d9e3f7a20b114d5 h2o_def2tzvp_adcman_ip-adc2.hdf5 8fc2922530d65cb23d821e662f9dedee62d91ad2508bf246ed3cbba640dbf75c h2o_def2tzvp_adcman_ip-adc3.hdf5 a07821177dd07f8f95a64aa008b7317e68357e45761742727394823fa242afb5 h2o_def2tzvp_adcman_mpdata.hdf5 -36b3b93aa1071e61611d96b6b59ed13d37ec718303b8ef60a6c10aef07276717 h2o_def2tzvp_hfdata.hdf5 -8a447ed3025662a2a71d866c697d345dbace45655e14c8777c10190f7f4c5967 h2o_def2tzvp_hfimport.hdf5 +60bd2f9e165ba7dde03fc7ddb36279fd58fc48a70fd196b131d56cff46f4da55 h2o_def2tzvp_hfdata.hdf5 +d1ae1621fa8ccaaba07ec6127b331c5e9e356708d16fd77a965a351d504ca74d h2o_def2tzvp_hfimport.hdf5 +ec9c7fa17528f2858f96e36d73e18cbfec41a97923ecc4adaafe249510e087e2 h2o_def2tzvp_reference_adc0.hdf5 +2a0833a0f62c5d77d3ca1222d0f94763c03e10eb6c0d5a4224ea02faaa374ca9 h2o_def2tzvp_reference_adc1.hdf5 +051d2e6415ff014d5e6a8d06c70ed8ceea7e0c948839e8dc5d1afd00d46ee4d3 h2o_def2tzvp_reference_adc2.hdf5 +2938aede785e3e108e0c3c5d6e71b02457f588563bd231e38f76b66ad29fafc5 h2o_def2tzvp_reference_adc2x.hdf5 +ded6e11ac23516170d0abf8c5e654d4c55aeb5ca79d4e5b77d933ce50f6167a7 h2o_def2tzvp_reference_adc3.hdf5 +7fa446e792136c198bef4f12c393895e111232302aed0e3dece077bdd026f93d h2o_def2tzvp_reference_cvs_adc0.hdf5 +374b5f69e70b9524b99b1ecdc11ccee9d7f0275e6a36d14f2e3d207229937a18 h2o_def2tzvp_reference_cvs_adc1.hdf5 +c5f6330421604e47e573512d51b1de20cac9b7931a1c129d9b64e41a702929aa h2o_def2tzvp_reference_cvs_adc2.hdf5 +0726b458f688ec93f77a0ed2a238d14a497b937b02bd2b7056c784aaaf12b165 h2o_def2tzvp_reference_cvs_adc2x.hdf5 +370369bb87640cdbdbae03fa45466409900301c8d785bcd8de0aeb14ee9a8792 h2o_def2tzvp_reference_cvs_adc3.hdf5 b56e74c8e71a9ab5ef62d44131c721e8d5ce236f9ed69d82869d702468aff7a8 h2o_sto3g_adcc_adc0.hdf5 3b2c9a095201c17bbe565de1b499b719b6d2ecde6ebb64d6b8e9f8ff2220c05f h2o_sto3g_adcc_adc1.hdf5 18c78aae03b0315cb3fcd932ce1ee1f3040db355ed38a61b46e2142d8e28b8ef h2o_sto3g_adcc_adc2.hdf5 @@ -108,6 +196,30 @@ f51fdacc11e39356304682ee43e889d51affc7f56eae5650bc571625385d9c7c h2o_sto3g_adcc 8df1ef913ee40f87cd86d301aa4125f952b2660e7fe12dda7a10f7ea90fd6ed6 h2o_sto3g_adcc_ip-adc2x.hdf5 c952684e6d8ac66038d53cb44e3c5fe9c5a3621733c7dc8ca391d40c3a50cd3c h2o_sto3g_adcc_ip-adc3.hdf5 32d3b2aa4b40ae58b4c1b71b102bdd72e21d42b9f4050fba734a2d18eb6ffa02 h2o_sto3g_adcc_mpdata.hdf5 +a156aaa0c31e7bb746a29025e3ac258e00df6cb9e4b1fe21bc9744a8edbd0eb1 h2o_sto3g_adcc_reference_adc0.hdf5 +bece668dfda506ca1dde71e6dc16cbf7540af9cc014e106a5785df55a631c79d h2o_sto3g_adcc_reference_adc1.hdf5 +a0518f1fa8dd393325635b6c1daac2d33e3493ab0fbbf4d02d817902b1522dd9 h2o_sto3g_adcc_reference_adc2.hdf5 +bde4c248e17463a60a502ea9f28652b6ed487b6104f0fe5e7143ed2f91f22f4a h2o_sto3g_adcc_reference_adc2x.hdf5 +13535b83fc703ae175ee9990a5bed358c6d48eed683899ef51d4694112a81f25 h2o_sto3g_adcc_reference_adc3.hdf5 +2e2cc8d2a073961a8dc58ceb57e7b36914415cecac07a3fa6e7bfa2b4a4d781f h2o_sto3g_adcc_reference_cvs_adc0.hdf5 +0fbcf5d973695001ebda3bf66b6d52d838c4d39f96d32369b990167573ff3c00 h2o_sto3g_adcc_reference_cvs_adc1.hdf5 +302867ba9bb468aaf06e7495de14eed9f8eff6c96f05076f2798a505730065a1 h2o_sto3g_adcc_reference_cvs_adc2.hdf5 +2b233e72d18ca474284decb8df4f56f2de61416615e3c6652384d0f386e91960 h2o_sto3g_adcc_reference_cvs_adc2x.hdf5 +52866bf50fafbb929c62f2ff4b5a289dc27dfea7c7ce08e88fb3d536caf3bcad h2o_sto3g_adcc_reference_cvs_adc3.hdf5 +e692ce11389e0e50d7dc0966c81268db985a3a7d3af2cd688e7101d5bdc03eb6 h2o_sto3g_adcc_reference_ea_adc0.hdf5 +e0977505fbc3e5de7da59d74cdba7591234a587cf4cbf696b42ab72526bc20af h2o_sto3g_adcc_reference_ea_adc1.hdf5 +77a06b949514a833812fe997de3dca3a997b1c6962c44255b4207ef91e7ea35f h2o_sto3g_adcc_reference_ea_adc2.hdf5 +cafb8ac1cfbbf1d56c788ef2bb28696835da79d8b3ad24cb6a890a650b8acb8d h2o_sto3g_adcc_reference_ea_adc2x.hdf5 +1d2a18a3417ccb6d8411b21dd5420b10e33f30198b03e0af45e659b22621e6cd h2o_sto3g_adcc_reference_ea_adc3.hdf5 +3cb59100c3b1d3fa378c136f241e8fc1105d52d93c0b0b453749fb4bfd949e5e h2o_sto3g_adcc_reference_fc_adc2.hdf5 +873c121e0118eed261a5f342553e7e195ce320ccce3feff4adffea4cb2307c2c h2o_sto3g_adcc_reference_fc_fv_adc2.hdf5 +e363fa64b8d1b516891dd1b4b517dd7c82472c0b10e5b50de739edc7962819fa h2o_sto3g_adcc_reference_fv_adc2x.hdf5 +1d1522a9bafbca2a3e8941e9019a9c72288f35f263dbf80c2063c045d50d01f2 h2o_sto3g_adcc_reference_fv_cvs_adc2x.hdf5 +9296e53a9e46f84a6469aee5ca8d1dbc06f4bcf0c093785023fb02117a0763ea h2o_sto3g_adcc_reference_ip_adc0.hdf5 +651ef5c89dce12965ac35ef729cfdf78d5281a7f007de5df42f1d588cabab2a5 h2o_sto3g_adcc_reference_ip_adc1.hdf5 +7c0e508e2fce55beb6780e0e52613d7737c8fe69871b8cecf1df098cf3e7d3db h2o_sto3g_adcc_reference_ip_adc2.hdf5 +0638302a44585564df3a604f43959382ea8735484df92f4d131a6e49628a0f6a h2o_sto3g_adcc_reference_ip_adc2x.hdf5 +ea4e6f354ca1a5d78761880759b54764e203895b140656ae40189a125d37dd95 h2o_sto3g_adcc_reference_ip_adc3.hdf5 cc957d99adaf872877b992860c9f0c9c8337f783b9c3ad20b36694391627188f h2o_sto3g_adcman_adc0.hdf5 4b36ac6cce98cf250ad268dc6e51e849acbd8c9383aa16aff233a2cf04118fcd h2o_sto3g_adcman_adc1.hdf5 3e63750574a0ba8a8137dcea7405a9dd045a1712bda043d7f227262146a881b8 h2o_sto3g_adcman_adc2.hdf5 @@ -120,8 +232,55 @@ c936ae6eb312921adcf1a3fc8bac19453eebbe29f8de4a574ac981b2ef55fffa h2o_sto3g_adcm abeaeacf2248d3c779a605eaf5d8313029593e22f8656c1baa5bcb0a983e4fcb h2o_sto3g_adcman_ip-adc2.hdf5 dd39481ff664de5c228919687ea3fbcf1effaa0aaddff4750d008cb25066b956 h2o_sto3g_adcman_ip-adc3.hdf5 00abfa6fcd237571167e42e3dfd956550944a5f6d68f462e6017629c175f4730 h2o_sto3g_adcman_mpdata.hdf5 -6c64153a6e708edad4c8a07e0ed6e5e1379e8e9c721e49f2aa0cc4ecb9817082 h2o_sto3g_hfdata.hdf5 -74733b03dd12561479132a768c2b330b4b8dbf1fe60fe0b14caa4d3fd4da7fce h2o_sto3g_hfimport.hdf5 +e0e17c4cc9618edbba2be2ad77c41c4b212deb9e88b458a2666a2a803a0cdba5 h2o_sto3g_hfdata.hdf5 +9bf19ad2a3e3f8d70188c7bdba0b39b044a760ddc668ae4a419ef2fb41337d9e h2o_sto3g_hfimport.hdf5 +41311ff99ea440583fb653108263bba37e644fba99507cc1b228818c535d21bf h2o_sto3g_reference_adc0.hdf5 +27a4844e53e5af0d9e4e318958e8b0b4377ab959d9303743d9ee030490818f57 h2o_sto3g_reference_adc1.hdf5 +dc62e796373ba77f78ac11d558961be4b5964f7400a733316540f0d70993de07 h2o_sto3g_reference_adc2.hdf5 +415c6a1c8fecd19a73637d33fe7b832d7bf0ae9563235e02524651c28116bd6e h2o_sto3g_reference_adc2x.hdf5 +79a32fecf709cbe9917d2645c5ea61a9e072f5860e67ff067a05fe03b2eb1540 h2o_sto3g_reference_adc3.hdf5 +7b0f67ef8bf16b0b04947007be1598f99bb2483743c75a7d89370526424f23ff h2o_sto3g_reference_cvs_adc0.hdf5 +a650d19fcdb1fbce6a2d9d5b13c0f1efbd43522fc59d267269ee1d6b2e9d3657 h2o_sto3g_reference_cvs_adc1.hdf5 +8432affb30a28d1fa2df91c83cd1360add4b26fe7c0f91ae742b0aec171a66dd h2o_sto3g_reference_cvs_adc2.hdf5 +c32008939174db29392760f46e75826ad266bcdced4c7bddddfa6408f14f3a02 h2o_sto3g_reference_cvs_adc2x.hdf5 +05174a726d4e6df7b5821fff76e759a82ec2eca946c4956de0821ccadcae451b h2o_sto3g_reference_cvs_adc3.hdf5 +0a89740716819c5c2e90c3b3ee1b4eb0fa0adf415064abcdd4acf245dddf2da2 h2o_sto3g_reference_fc_adc2.hdf5 +de7fea54d9f21e3a5c28aa724a35946f8bb65c49563025e0446e3ad6f09e8689 h2o_sto3g_reference_fc_fv_adc2.hdf5 +20d40379e52c8b5abe2c618f23da82c4f275722601e3484b3b5ed813ab98c69c h2o_sto3g_reference_fv_adc2x.hdf5 +db372645e0519fad40508402484bc53aaae62e1c46b74ad118e042498fd2907c h2o_sto3g_reference_fv_cvs_adc2x.hdf5 +93c87725ff53541e54d6a4ded03a83907bb75b391c353a32cf3eff962e2affd9 h2s_6311g_adcc_reference_adc2.hdf5 +b31a2a128ebbe7d2c2e2d1c0591fff27372f54b3ef8e7be344c022bdbba5df54 h2s_6311g_adcc_reference_cvs_adc2x.hdf5 +e7be7ce9275aab3669c0395f512616c2f3eb3d3f9b4ba1c57a4cf5df22fb4d00 h2s_6311g_adcc_reference_fc_adc2.hdf5 +7e07f4449d0a052785d63e6a173da483c5b6c084f268e39c3f42f4b99da68af5 h2s_6311g_adcc_reference_fc_cvs_adc2x.hdf5 +84c3888aa0e3780e60d975758ef20efa30593f75797d15096eec983500ddbfd2 h2s_6311g_adcc_reference_fc_fv_adc2.hdf5 +72c92b8e360f90a9743191ccd6b536aae9d1df1b8777257c7d035c57bfbe7a23 h2s_6311g_adcc_reference_fc_fv_cvs_adc2x.hdf5 +c481a49f3a754b2cf16873d8e6f3af589835f5449694326e37d79a79985fd8d2 h2s_6311g_adcc_reference_fv_adc2.hdf5 +8a6e8ab01541fe36d6402bee2f9af5c0aafe17838574f2a77c88f3e64d3713eb h2s_6311g_adcc_reference_fv_cvs_adc2x.hdf5 +057ef124dc4b3fdfe4958f74009500a6507c8f2bb2a24f0a9cb4bc8119334c10 h2s_6311g_hfdata.hdf5 +457e5a4cc77d25fe6021065c43a7cb52fe353e4c0bff12c805650e93c2256d30 h2s_6311g_reference_adc2.hdf5 +de4b464d8275e0941c76f047d3ea21b2c3396b1b27fbf870fa715c9c2a0d5b62 h2s_6311g_reference_cvs_adc2x.hdf5 +ba02b69f3c320e4d1eee7caf1201e310b344ce749e7bfa9451a99953281ad0bf h2s_6311g_reference_fc_adc2.hdf5 +e75f1193409af67502ea4b0e44cc5077d219d1efa96e054713bc2c693f8ee035 h2s_6311g_reference_fc_cvs_adc2x.hdf5 +60262b06d393ae29ebeb2639c2c78df057d5d3c915845507cd635556fb1ffe81 h2s_6311g_reference_fc_fv_adc2.hdf5 +f1aae3bc870c1a11c2f3eded009369ba4502577542819bd40d018f9db015a62c h2s_6311g_reference_fc_fv_cvs_adc2x.hdf5 +6721731cbd54998f325e49878f142a6ffea3fa4022f76e8e2af185ce83c80935 h2s_6311g_reference_fv_adc2.hdf5 +5be5c174e016eb8a5482b016409285ea52ee3ecc3580a85ae7c426307633735f h2s_6311g_reference_fv_cvs_adc2x.hdf5 +76a24f902aba79c034e974fcdfaf244cca1f595673f6d625461fc80c6f19ffba h2s_sto3g_adcc_reference_fc_cvs_adc2.hdf5 +5abaac90d766b61e7f6b2c3f4d559ac7f50088303e42566ded4213dc18c97ac4 h2s_sto3g_adcc_reference_fc_fv_cvs_adc2x.hdf5 +4354d9a8f676370454deb43116775f111d9e1a32508ef7e0adaa0cca67c1a87d h2s_sto3g_hfdata.hdf5 +63312155f2dcdb8a342014506e10897e738bf5adb23215d12664d3f90e512ad5 h2s_sto3g_reference_fc_cvs_adc2.hdf5 +16c373e332f9d845d35f02a7c0b20050127ef4ddaadb8ae89e98131370ce958d h2s_sto3g_reference_fc_fv_cvs_adc2x.hdf5 +4e3bf60040ee4520918411c7c1148888c9fac21b416a04693ba7e9641d927dcc hf3_631g_adcc_reference_adc0.hdf5 +3d6f3b8f497a1309a03160b546bc33c88a23a57e3ad443f0958df65b7d55f255 hf3_631g_adcc_reference_adc1.hdf5 +c2a26aafd78e64b3be4d77af3962a5309276b5fbff4be738cade7d5c1aaa139c hf3_631g_adcc_reference_adc2.hdf5 +271907a527f439aba1e2fc63c3c506390815eee44f814109a1b70aae846752e9 hf3_631g_adcc_reference_adc2x.hdf5 +0b46241370261b2dfb48df1d89ba42f16d26462930149517455ba2d5de953bbd hf3_631g_adcc_reference_adc3.hdf5 +93bf9f69b8ab2c524b499bc60790f245401ab0d65cc342e92479c68051b0f34d hf3_631g_hfdata.hdf5 +a3d40fe926cd3bf4a0d8e81747cc449238aecc3b34366e0d4214b2a2ad61130f hf3_631g_reference_adc0.hdf5 +0c11fdcb42b1004844c85f9da7427a611f77d3315040c3abccc8b60033275ea6 hf3_631g_reference_adc1.hdf5 +4c950719db7ae7b799cb55c82235c87c811b2a9ca8bb99bb727196a334ecee99 hf3_631g_reference_adc2.hdf5 +5d43387bda90cd51e2da2a904a34e63ad5ddd3e3b891d41ac306ee1233b2a293 hf3_631g_reference_adc2x.hdf5 +a64f0d44387dcc56a73c986da34070a4ec5888191cb42511b5c4ef6acbc324b4 hf3_631g_reference_adc3.hdf5 ca45224813877e28332fd27c445320200e34447fca4e1f3dd2fe318948caf3ae hf_631g_adcc_adc0.hdf5 c5b7f42d403183e7e44289d7b3bca21cec217a9cf01fb6bf4e0ae33d17884d21 hf_631g_adcc_adc1.hdf5 d6809c1474a375141a3cd065ceabf84e26ade89e79973c6403110bb77c6a0e7d hf_631g_adcc_adc2.hdf5 @@ -141,4 +300,25 @@ d985ebce808b05b8fd9c57d4775bcc84bfb5d6c0ec30ec9c1606282acedeb449 hf_631g_adcman 55132fbeeb9a63d77590b56276a416d906118a417bdb62c0c10aabf0846ea3e3 hf_631g_adcman_ip-adc3.hdf5 1d7b0dd4e61aea9faf713706e5ec143426e97a55da4df575d6bd72e5dfcd872b hf_631g_adcman_mpdata.hdf5 a1eab1dd706a3f8f7532889b9a4754fa627a47ce5cfb9eb7d95a8365774850f2 hf_631g_hfdata.hdf5 +b0667a516263af22a4a89b95bc82f646dec87ba5a07c9ff22aa225f9cf276fa3 methox_sto3g_adcc_reference_adc0.hdf5 +2e55b34a015ed239427ed684b08e29bf2efe4103cd504cb446ba68538fb3e40d methox_sto3g_adcc_reference_adc1.hdf5 +5439aee010add74452c2cc490c882377a54e8e65f7b0105c6b4db81ab57a88d4 methox_sto3g_adcc_reference_adc2.hdf5 +f075aff7f9f2dac98e291f0a2c88a5e69e51dcc14cdccd33e0c4148188c2c1d3 methox_sto3g_adcc_reference_adc2x.hdf5 +8c5d1e9acce8f117f32f823553b38703cc2ff78af732995984a0d7283fa2e6ed methox_sto3g_adcc_reference_adc3.hdf5 +6e8ab22aa359f2055ed46208b2b2ce21dfb579741bc47ea39e5598256f76b29f methox_sto3g_adcc_reference_cvs_adc0.hdf5 +5777832284772cb1ee3c36f84923f53069d00aa572963ddb899bcbf0cebceed4 methox_sto3g_adcc_reference_cvs_adc1.hdf5 +e03fcaeb5c47fbe141cd250d3e314a1eed73ceb218ada5bd2d44647e9ef6d39a methox_sto3g_adcc_reference_cvs_adc2.hdf5 +085040675f53c3e306ca7b2e5df0727f9c32bb452918bf2336b53893ec7b330a methox_sto3g_adcc_reference_cvs_adc2x.hdf5 +c2b33dc2e074b54b20f398b83f8deedae26e32c524d1c0fa43f6c62eb98ba95a methox_sto3g_adcc_reference_cvs_adc3.hdf5 +5877bf4fc4554759b2f0ba35c58cee1e9a7ae2de4d47673a98793d4769eec841 methox_sto3g_hfdata.hdf5 +fd0e374f6590b5145bb255e77763f96217abc5912764091e0fc97fb387b9c643 methox_sto3g_reference_adc0.hdf5 +0f617bf0ff98c15f7d514197391b1da4737758d948ce2c865f1d8bf76b8af6af methox_sto3g_reference_adc1.hdf5 +9064a58a5f27827f480f396848b1cbbf419078cb957a3d293d6652db5c5f338b methox_sto3g_reference_adc2.hdf5 +3d768b2b217e06605961e0b94475a264017463a4480db6a15cfe720012b5ecbc methox_sto3g_reference_adc2x.hdf5 +a1a4d87899df29556f7cbb6c27069b350bd597132013ba7f88e987e7f5ba4dd1 methox_sto3g_reference_adc3.hdf5 +e85bcde56f355ae01ab93cd0234b2885dd2a79db9835614d29f178960f75f6ba methox_sto3g_reference_cvs_adc0.hdf5 +1030ea26fce674dc7e319627e4521e5f27d23b1c097e7c331896bc5f83f435a9 methox_sto3g_reference_cvs_adc1.hdf5 +9c6983ca3d94769836a48ad8ce39d99ee509cc18d104f5e4570f9809ef16153e methox_sto3g_reference_cvs_adc2.hdf5 +5f822408ea4f6b2650078bb68b7a577c64019ae691d97a162d744419b221d018 methox_sto3g_reference_cvs_adc2x.hdf5 +f348fe9b0556c2b75e712e4e33f891e88de0744d10d1bed8e83739c463b58024 methox_sto3g_reference_cvs_adc3.hdf5 0e86aef221cc541cbade47e99471fa7c0bc1955c80783be4b32a8ea58902b2f6 r2methyloxirane_sto3g_hfdata.hdf5 From 4646a87232ca909a887df4c5bfc5e3672ee60f3e Mon Sep 17 00:00:00 2001 From: fedy9 Date: Mon, 4 May 2026 14:08:29 +0200 Subject: [PATCH 09/26] updated SHASUMS --- adcc/tests/data/SHA256SUMS | 176 ------------------------------------- 1 file changed, 176 deletions(-) diff --git a/adcc/tests/data/SHA256SUMS b/adcc/tests/data/SHA256SUMS index 343807e95..a665812d9 100644 --- a/adcc/tests/data/SHA256SUMS +++ b/adcc/tests/data/SHA256SUMS @@ -14,16 +14,6 @@ dd41e5ff7f96ac8386611e35f13fac9da1d0bf5e50d90287436db8a4afa86c9b cn_ccpvdz_adcc 767333f9f0dc0366a567e2b4cdd27b54d7608fc2fde2e6be0a56cf7d056ab8eb cn_ccpvdz_adcc_ip-adc2x.hdf5 8f0ec3a22dd30d174a5c067040c14862f0b845e099319ab3c5ed71c5d0618ea9 cn_ccpvdz_adcc_ip-adc3.hdf5 4635cfbf161652c9f83379b119b2e1b7e520dc2a22be5693015820c865efe705 cn_ccpvdz_adcc_mpdata.hdf5 -f4bd184d86c7a27cdc2bcb99fc073790e541c97e9b92ff9b4d0452ccadc63f64 cn_ccpvdz_adcc_reference_adc0.hdf5 -9adf1e7ec2b0aa1c34a92354f27943f5a1d3d941a0f566ae249bc11895121629 cn_ccpvdz_adcc_reference_adc1.hdf5 -c6bc0b7cbcca51c1f6eee1363a18ba13bed9ff5d5a4681d8941bdf1a0dbb0164 cn_ccpvdz_adcc_reference_adc2.hdf5 -735bd5f7d121efe6aef15eecf96dcc51681b6f98815f55b261f1c840fc826add cn_ccpvdz_adcc_reference_adc2x.hdf5 -dbc64230873e766238bdf242d6ce8b4b7e19c336f84839dd4c85b54ba0d874f5 cn_ccpvdz_adcc_reference_adc3.hdf5 -61f733c80b2e0dd7483e4e9b3041c365caa4235c77420fd9492bea71cfeaf25b cn_ccpvdz_adcc_reference_cvs_adc0.hdf5 -f43c9bd7c097af3937efb4ac6abb42b442ada4e0fc7c36e34a9ea62a8b7a2c7f cn_ccpvdz_adcc_reference_cvs_adc1.hdf5 -371746977181c6e71ab4fca02a73463a8ecc9e010fe2dd9b1deb41f5a01db083 cn_ccpvdz_adcc_reference_cvs_adc2.hdf5 -cf9d2eedb6f16b5d5a6267d20e414460b916e84142ae4f3f1f40dd97c20311df cn_ccpvdz_adcc_reference_cvs_adc2x.hdf5 -5158edbe44f804b88ca4c7953fdda435189d27f0cbe586216b66dba4cc9dd83a cn_ccpvdz_adcc_reference_cvs_adc3.hdf5 8f8c3c86cb9f4b22f8bcfff10e7092fcec4258fbf2a451e3cd6456b1adb7ba85 cn_ccpvdz_adcman_adc0.hdf5 be3f5e6090ba4d6fef72760e982bc268dc49f870db1afc101af60e08be559423 cn_ccpvdz_adcman_adc1.hdf5 259b19562741b25ff08491c446e69af92a34ad320d204b7dcfe5a6a4928a9072 cn_ccpvdz_adcman_adc2.hdf5 @@ -38,16 +28,6 @@ a8743b12ccf3ba67716008972be04d58298c28f4b4624d4377861e0a2bc13ff5 cn_ccpvdz_adcm 0a2d6ebae22d84b9a4513e52c85f0f0294b6162bc25662c743d154bc81fa4128 cn_ccpvdz_adcman_mpdata.hdf5 37b3af4a4f1b70c7bc7588f5bfcd542fc96056ccb7fe2e8c9fbda93b546788a0 cn_ccpvdz_hfdata.hdf5 a15001b874327b1cec2c84bfe4a24695c71633f31aaf618dc25744dda2c1022b cn_ccpvdz_hfimport.hdf5 -7e3a04d69a8110a78c90ada8e3dbccefaf83bee20fc1bf15fbef1d0a610edd44 cn_ccpvdz_reference_adc0.hdf5 -ddcfbff7d784eca92ff8150f6938cf765444f41d2fb6140ba462b54378e6bdd5 cn_ccpvdz_reference_adc1.hdf5 -d0486fe1ce6605d26ad210e35620b3fb9ffe6ecbce91b89f0f1b845781ef9adf cn_ccpvdz_reference_adc2.hdf5 -a424ae21c0b391c66b5326b70c740f1f2350c67121c1c110afee129d374eda75 cn_ccpvdz_reference_adc2x.hdf5 -61ff02b79c536f0c45cc1f1616ea1d2249424377db920e87cc9f746e0f969763 cn_ccpvdz_reference_adc3.hdf5 -8d8ad88e46d06fbec47fcfbc714a6240830c46826ecb0b2c918536fa140aafa3 cn_ccpvdz_reference_cvs_adc0.hdf5 -3deb56a97eb9f75b1a643e0443c8af7116b9ce701f86da321cd7614da0e0bf54 cn_ccpvdz_reference_cvs_adc1.hdf5 -f4c11821f40518fc137b88c7975b83b7e273bef578e5663a6072265d295015d1 cn_ccpvdz_reference_cvs_adc2.hdf5 -aedb569d840eb1194e64049c7ed215a6a3f7796c33509ce402a6ccdab8c51238 cn_ccpvdz_reference_cvs_adc2x.hdf5 -231e170e5b79aee977b010761030be61d4769ea6757f2f6eaea9b7059bbbb796 cn_ccpvdz_reference_cvs_adc3.hdf5 94665e00317ec7a20540f934dd0306403acda144a581f7c5a1abab14cc599354 cn_sto3g_adcc_adc0.hdf5 54020255ce72d4700758c6790207c62c61a25091380d8bd8b6cf4b21c6dfcfc9 cn_sto3g_adcc_adc1.hdf5 08f1ff54122f543cce6fb23d8e594ea58e342693ac94956f35d79781f790b6b9 cn_sto3g_adcc_adc2.hdf5 @@ -62,40 +42,6 @@ d1c54c83b79509dd54dedac1dfc6f5530e6de782ac8d1cfb892a616f73180b14 cn_sto3g_adcc_ 1f02e1c648759dc2562ad8a4e092c927617a7124e884e56539a4ecc7a2288722 cn_sto3g_adcc_ip-adc2x.hdf5 384d99c0c585e59210f5d323a8ae8bcfcc9a163cb3ad93f5332bded421b057e2 cn_sto3g_adcc_ip-adc3.hdf5 582cc08bb5b63599f67511b4a262c3c7d88d80d3bb99cb306b06865e94116451 cn_sto3g_adcc_mpdata.hdf5 -f20b093bb9c549930dc975cfad5e91bba483a4abb55003b572709d3c032eabea cn_sto3g_adcc_reference_adc0.hdf5 -ce65f98abae7695f88ade37a9b222ab00b72748cced3b30c9c3114fed78e917a cn_sto3g_adcc_reference_adc1.hdf5 -bb96ca05a83db0eb64017267c112032e247796f2efdf14a4b74f8b095b177326 cn_sto3g_adcc_reference_adc2.hdf5 -ef41cb609e3d80934a7c4f1ea4e83480406c96c87d61ce59fd68b823e0e43b7d cn_sto3g_adcc_reference_adc2x.hdf5 -d6f8e6a8bf4ac0a38bb7c81dad2a41e6fed814255f3937326c2fb153754a6d04 cn_sto3g_adcc_reference_adc3.hdf5 -ea5099cc8350d8bb3532c0aa9f4c0f2f726e2e2bd96c03a2c3249cade70d22c1 cn_sto3g_adcc_reference_alpha_ea_adc0.hdf5 -20b01b7ef16d75807fb33d334be053bc0cd3593eda19ff5664eab74412d3214c cn_sto3g_adcc_reference_alpha_ea_adc1.hdf5 -dd0d4ea21bb42c4eef9293464790f0b699cbc29aea3054b2b30ee63f6834eebb cn_sto3g_adcc_reference_alpha_ea_adc2.hdf5 -d8a3cf92315c500525bd0d18493f964906d89d2095997a124443ba9647c08350 cn_sto3g_adcc_reference_alpha_ea_adc2x.hdf5 -d86ce652e662a5ac01199dccd39cfe226cb9e05163f6a26f573e5759399669ab cn_sto3g_adcc_reference_alpha_ea_adc3.hdf5 -e299922c4939c1ad1f1bcc14cb9f6ef63d3014c17ba7e39611d9bd88e4071213 cn_sto3g_adcc_reference_alpha_ip_adc0.hdf5 -4237b58b634eedd5b0017241515a96251ee4b163fc1f772d19ae614a4f588bb2 cn_sto3g_adcc_reference_alpha_ip_adc1.hdf5 -c2b91b123c49b864657499b55d3e8e40123552d4a301e8b71726cdd984cd71d8 cn_sto3g_adcc_reference_alpha_ip_adc2.hdf5 -4942f21574b0c9b4e39891f86e5a2b4fa3cd0040a9caca90f04ebc1bb211e8d8 cn_sto3g_adcc_reference_alpha_ip_adc2x.hdf5 -38a6aa1f9cb60cb01dd46820d2aaeeea33a3efd5a3e86cf3012c5c43b688de83 cn_sto3g_adcc_reference_alpha_ip_adc3.hdf5 -b442db51ed1461f2c7ad1a0e903ea5577d79439c053684c244d053985ec57c4e cn_sto3g_adcc_reference_beta_ea_adc0.hdf5 -ead2b116e0ec42d46b55c8d548fcfef4d80b79d62d8ce4edfed833086f397e3c cn_sto3g_adcc_reference_beta_ea_adc1.hdf5 -34c1984d008befbe6acb376de87ced00addce0e1c916af915890d857d957aeec cn_sto3g_adcc_reference_beta_ea_adc2.hdf5 -ec62f66eeefaf5c68823be506d17144d281b9c3010ff3bcf69876cd9c4de7607 cn_sto3g_adcc_reference_beta_ea_adc2x.hdf5 -791dd15dcfbfc0524cb7435385b3e4c8c204252d3be75b22ce2b0651dbcb5a5b cn_sto3g_adcc_reference_beta_ea_adc3.hdf5 -64ccde2cc69bf2e286ddb4c53dedaf446e2836d6095a37b9aa81c0197b3c0bf9 cn_sto3g_adcc_reference_beta_ip_adc0.hdf5 -94774f17c987205e2c0eca3f1d4bd561262d208faa1e9c5f4fca6882980b6320 cn_sto3g_adcc_reference_beta_ip_adc1.hdf5 -5fd98a5538264f56870e899cbdb0b4ec291006813107bf7e3b37cd74daf11c0f cn_sto3g_adcc_reference_beta_ip_adc2.hdf5 -3c8a2f79348163433e6b4f867dcdf7e0c2f03f3faf1dcef163977dc7a3096d3b cn_sto3g_adcc_reference_beta_ip_adc2x.hdf5 -490af368d0e4ceee9e570cc199299e7648913a027fafa3c5d70b02e40549113e cn_sto3g_adcc_reference_beta_ip_adc3.hdf5 -0b9afd705e310c23f9ce1e4d6b7a7796a7976f13db93523ed3bf9774b08f9134 cn_sto3g_adcc_reference_cvs_adc0.hdf5 -a421c00c313cc4db1bcebdc83278dca80196623ecac6f957721d4c6afdeff039 cn_sto3g_adcc_reference_cvs_adc1.hdf5 -575c3229ebe0a8015e4b62e7cd0a62b6d0f4dc4abd71f27b2dd1441abfcad6e3 cn_sto3g_adcc_reference_cvs_adc2.hdf5 -d3428482d4b4a073c663d8dd7bfd09e532de36855d9276aa0774051b0861fa2c cn_sto3g_adcc_reference_cvs_adc2x.hdf5 -bb85cf1cc5c6e95f5b12588fa865d92911215250f6386ae6e7f05bd28beec0a0 cn_sto3g_adcc_reference_cvs_adc3.hdf5 -99da6cef0bc4a98504408cfb8cad849d7269054d879e9baff9355e928ddd36c3 cn_sto3g_adcc_reference_fc_adc2.hdf5 -36be0f5ffd45babd12f9d3cdfe29ecd1333925aaeba28a3d6d0f5d28e55ac85d cn_sto3g_adcc_reference_fc_fv_adc2.hdf5 -00ab8825070cf6848119208b1a29237e3f855d7f09694c7e21cd71854dd5cae3 cn_sto3g_adcc_reference_fv_adc2x.hdf5 -7f76d4d0e31c4bfe4a4b7c2e0540a29d54149b78cd0809f66c831006d3b51f02 cn_sto3g_adcc_reference_fv_cvs_adc2x.hdf5 f6974049ccf8a732ea584dbbebf608f0e17d28bd5320c0c63e1d1a5db80f54a7 cn_sto3g_adcman_adc0.hdf5 9950c0720546a0b313e755945e7fd0d5462967a4366f60470c4a85f500bffb5f cn_sto3g_adcman_adc1.hdf5 a866f1c06508e1bf48567c2546938e471b83c82ecce7dfce6815288c1e0226a6 cn_sto3g_adcman_adc2.hdf5 @@ -110,20 +56,6 @@ cfc731c423999b1b1f79929e180f7d3cc76199079b4fc69dca1b2e0ccf9672ba cn_sto3g_adcma e9a94d14102eba9e6b6aaf0728615e5bfecacde501743032813f5b0df283bd7e cn_sto3g_adcman_mpdata.hdf5 31cea3a0bdfe517d1be6615c98849578704377f2ad6134ce127531798e43ad95 cn_sto3g_hfdata.hdf5 a305861c174acddcdffaa886775ee79c8ed5f41aa19493bbe7f053a7e6996a5f cn_sto3g_hfimport.hdf5 -f28b3a47be0cbe025a002762b7d2f4617116f3c9d7e2485c5d9152409046c2c5 cn_sto3g_reference_adc0.hdf5 -0a9142c913e54f2e424fa7acc97677d06b81b93b25c45733e6336ff9be64ea56 cn_sto3g_reference_adc1.hdf5 -fe788ddb20a175f10090aae392c0a290911f1bdf85bd1d848a0c71fad5fb469a cn_sto3g_reference_adc2.hdf5 -1d41863238d18734a9623d74c064005ec69dc86cf50ac783ad448e348e66d3fe cn_sto3g_reference_adc2x.hdf5 -52ae9edbf07d367354fcb567193178f0a66e6cbcb062bfdacc7417a264923208 cn_sto3g_reference_adc3.hdf5 -d78c5a44cdecf441bb0a14ceb0eb0f23c144ea96f98b4b1ceeb956a735909729 cn_sto3g_reference_cvs_adc0.hdf5 -7cb6d961414737c3722980f6793670e3b2046b7c81a20889c849bf9b5b207a15 cn_sto3g_reference_cvs_adc1.hdf5 -b565ab09c86ecefbe2d71157ce41e093c421e04697d0056e7f6cdfb0195b7dd9 cn_sto3g_reference_cvs_adc2.hdf5 -17f7ac21b49de29d8a282b9c38b158e24e1865376ebf7bbfe73ea4b53da71555 cn_sto3g_reference_cvs_adc2x.hdf5 -d26bfe00e8b3cfa8891028e0d3d3dd85b7de3f21450bae6eb11f8ad2db340678 cn_sto3g_reference_cvs_adc3.hdf5 -573c343fa28cd3c58b271cbc94dc281bfa14a992a3fd3de392a29fd165ce7367 cn_sto3g_reference_fc_adc2.hdf5 -5a5442ee98f2b5b34474a030764036d227e17efb9c2c728d0bbb672e1149ff0d cn_sto3g_reference_fc_fv_adc2.hdf5 -9b7cfc0c05478837fb919202ddda2d7a0a4a767d1081dc2056fb425dd29b7acf cn_sto3g_reference_fv_adc2x.hdf5 -16a2362662192d9ffa1f7e8762ef8e3713bce6a92bc2028668c6679d3f646806 cn_sto3g_reference_fv_cvs_adc2x.hdf5 8c7d88af083003a190a5561d31d1c9c231c0cb1e149a00f8b6cd92d020871332 formaldehyde_ccpvdz_adcman_adc0.hdf5 c55ccf23bd94fcd2e3660cc73915e4994c59143eaf92ea96a99df295bfd90be6 formaldehyde_ccpvdz_adcman_adc1.hdf5 3271fcb5f13f512cba10a807c13258e95db25d5854e4008e91ac0b0e0481ba77 formaldehyde_ccpvdz_adcman_adc2.hdf5 @@ -148,16 +80,6 @@ a7f58daa5fd1d8f4c3e54b5bd8bc957dd309b3df386744dab9271aa3982e600a h2o_def2tzvp_a fb26959b3beb45ac6222c542263fb74dbc448ac0f842073edae2bf467a51a141 h2o_def2tzvp_adcc_ip-adc2x.hdf5 90fdfbbb3f2ca4e69bd6329c111a30cea3484f467b3c010722fb6c717cdce1ff h2o_def2tzvp_adcc_ip-adc3.hdf5 bb7850aad29b723e8b2317cdb2fa45229eab6ff5772c7c2eab681ec527c3bf93 h2o_def2tzvp_adcc_mpdata.hdf5 -2022ff858b4640036b32b66086afedf44832e161fb9128d278d4a37ea4bc8259 h2o_def2tzvp_adcc_reference_adc0.hdf5 -88aef14092089f6645002cd2c47366cf93860124dd96625d983dfd856419a9e7 h2o_def2tzvp_adcc_reference_adc1.hdf5 -0321b11dcd78b9fb1e781deb8f17390b4a03a4bec5877aa717317f7e71adfe78 h2o_def2tzvp_adcc_reference_adc2.hdf5 -0510127944907a35a29034e3a1d3ffec86b16c6e092a8ab0b6eb9c29593bb553 h2o_def2tzvp_adcc_reference_adc2x.hdf5 -c8219c649979db34650b2804ab7c737cf7b1bd2bde5987ac3b3ce782e676ab25 h2o_def2tzvp_adcc_reference_adc3.hdf5 -56eef33b7035b7f62fd048c435dc75559bedc4bee90ee7c629d2ad68f161d933 h2o_def2tzvp_adcc_reference_cvs_adc0.hdf5 -5092e81d5ffa1fe24e6a121a08fd9e3ccae64c212563d88d48f8c2093fe45ddb h2o_def2tzvp_adcc_reference_cvs_adc1.hdf5 -5f56c5e8e1295aeaf776bd9a7d6efc2022cc1386018151916b7ee84a9d407962 h2o_def2tzvp_adcc_reference_cvs_adc2.hdf5 -17d531af3b39970ee81217f40d41949252ff9f6e7d7e5860b8e6a46b91930289 h2o_def2tzvp_adcc_reference_cvs_adc2x.hdf5 -0f402d16d1018144fe67ec3a3e49853db3bf06dcb66deca4ab9a0792c7fcd34f h2o_def2tzvp_adcc_reference_cvs_adc3.hdf5 2a88059266ac32a34cc352e1d2f782565317cec8573bdd81c4e4019ecc9adb65 h2o_def2tzvp_adcman_adc0.hdf5 dcf8ae501f7875705191abf754b33cf82f14065db2c95ab84ef3004b70ee9e75 h2o_def2tzvp_adcman_adc1.hdf5 7cd549b23a8939f5c989882b250284d5f6b20d14e97055f5caba7e6a6138f4b2 h2o_def2tzvp_adcman_adc2.hdf5 @@ -172,16 +94,6 @@ d06b4f8c48e072009cf4f830cf882f9b4b887fe5eeb02bc7d72b69cfc4c3795b h2o_def2tzvp_a a07821177dd07f8f95a64aa008b7317e68357e45761742727394823fa242afb5 h2o_def2tzvp_adcman_mpdata.hdf5 60bd2f9e165ba7dde03fc7ddb36279fd58fc48a70fd196b131d56cff46f4da55 h2o_def2tzvp_hfdata.hdf5 d1ae1621fa8ccaaba07ec6127b331c5e9e356708d16fd77a965a351d504ca74d h2o_def2tzvp_hfimport.hdf5 -ec9c7fa17528f2858f96e36d73e18cbfec41a97923ecc4adaafe249510e087e2 h2o_def2tzvp_reference_adc0.hdf5 -2a0833a0f62c5d77d3ca1222d0f94763c03e10eb6c0d5a4224ea02faaa374ca9 h2o_def2tzvp_reference_adc1.hdf5 -051d2e6415ff014d5e6a8d06c70ed8ceea7e0c948839e8dc5d1afd00d46ee4d3 h2o_def2tzvp_reference_adc2.hdf5 -2938aede785e3e108e0c3c5d6e71b02457f588563bd231e38f76b66ad29fafc5 h2o_def2tzvp_reference_adc2x.hdf5 -ded6e11ac23516170d0abf8c5e654d4c55aeb5ca79d4e5b77d933ce50f6167a7 h2o_def2tzvp_reference_adc3.hdf5 -7fa446e792136c198bef4f12c393895e111232302aed0e3dece077bdd026f93d h2o_def2tzvp_reference_cvs_adc0.hdf5 -374b5f69e70b9524b99b1ecdc11ccee9d7f0275e6a36d14f2e3d207229937a18 h2o_def2tzvp_reference_cvs_adc1.hdf5 -c5f6330421604e47e573512d51b1de20cac9b7931a1c129d9b64e41a702929aa h2o_def2tzvp_reference_cvs_adc2.hdf5 -0726b458f688ec93f77a0ed2a238d14a497b937b02bd2b7056c784aaaf12b165 h2o_def2tzvp_reference_cvs_adc2x.hdf5 -370369bb87640cdbdbae03fa45466409900301c8d785bcd8de0aeb14ee9a8792 h2o_def2tzvp_reference_cvs_adc3.hdf5 b56e74c8e71a9ab5ef62d44131c721e8d5ce236f9ed69d82869d702468aff7a8 h2o_sto3g_adcc_adc0.hdf5 3b2c9a095201c17bbe565de1b499b719b6d2ecde6ebb64d6b8e9f8ff2220c05f h2o_sto3g_adcc_adc1.hdf5 18c78aae03b0315cb3fcd932ce1ee1f3040db355ed38a61b46e2142d8e28b8ef h2o_sto3g_adcc_adc2.hdf5 @@ -196,30 +108,6 @@ f51fdacc11e39356304682ee43e889d51affc7f56eae5650bc571625385d9c7c h2o_sto3g_adcc 8df1ef913ee40f87cd86d301aa4125f952b2660e7fe12dda7a10f7ea90fd6ed6 h2o_sto3g_adcc_ip-adc2x.hdf5 c952684e6d8ac66038d53cb44e3c5fe9c5a3621733c7dc8ca391d40c3a50cd3c h2o_sto3g_adcc_ip-adc3.hdf5 32d3b2aa4b40ae58b4c1b71b102bdd72e21d42b9f4050fba734a2d18eb6ffa02 h2o_sto3g_adcc_mpdata.hdf5 -a156aaa0c31e7bb746a29025e3ac258e00df6cb9e4b1fe21bc9744a8edbd0eb1 h2o_sto3g_adcc_reference_adc0.hdf5 -bece668dfda506ca1dde71e6dc16cbf7540af9cc014e106a5785df55a631c79d h2o_sto3g_adcc_reference_adc1.hdf5 -a0518f1fa8dd393325635b6c1daac2d33e3493ab0fbbf4d02d817902b1522dd9 h2o_sto3g_adcc_reference_adc2.hdf5 -bde4c248e17463a60a502ea9f28652b6ed487b6104f0fe5e7143ed2f91f22f4a h2o_sto3g_adcc_reference_adc2x.hdf5 -13535b83fc703ae175ee9990a5bed358c6d48eed683899ef51d4694112a81f25 h2o_sto3g_adcc_reference_adc3.hdf5 -2e2cc8d2a073961a8dc58ceb57e7b36914415cecac07a3fa6e7bfa2b4a4d781f h2o_sto3g_adcc_reference_cvs_adc0.hdf5 -0fbcf5d973695001ebda3bf66b6d52d838c4d39f96d32369b990167573ff3c00 h2o_sto3g_adcc_reference_cvs_adc1.hdf5 -302867ba9bb468aaf06e7495de14eed9f8eff6c96f05076f2798a505730065a1 h2o_sto3g_adcc_reference_cvs_adc2.hdf5 -2b233e72d18ca474284decb8df4f56f2de61416615e3c6652384d0f386e91960 h2o_sto3g_adcc_reference_cvs_adc2x.hdf5 -52866bf50fafbb929c62f2ff4b5a289dc27dfea7c7ce08e88fb3d536caf3bcad h2o_sto3g_adcc_reference_cvs_adc3.hdf5 -e692ce11389e0e50d7dc0966c81268db985a3a7d3af2cd688e7101d5bdc03eb6 h2o_sto3g_adcc_reference_ea_adc0.hdf5 -e0977505fbc3e5de7da59d74cdba7591234a587cf4cbf696b42ab72526bc20af h2o_sto3g_adcc_reference_ea_adc1.hdf5 -77a06b949514a833812fe997de3dca3a997b1c6962c44255b4207ef91e7ea35f h2o_sto3g_adcc_reference_ea_adc2.hdf5 -cafb8ac1cfbbf1d56c788ef2bb28696835da79d8b3ad24cb6a890a650b8acb8d h2o_sto3g_adcc_reference_ea_adc2x.hdf5 -1d2a18a3417ccb6d8411b21dd5420b10e33f30198b03e0af45e659b22621e6cd h2o_sto3g_adcc_reference_ea_adc3.hdf5 -3cb59100c3b1d3fa378c136f241e8fc1105d52d93c0b0b453749fb4bfd949e5e h2o_sto3g_adcc_reference_fc_adc2.hdf5 -873c121e0118eed261a5f342553e7e195ce320ccce3feff4adffea4cb2307c2c h2o_sto3g_adcc_reference_fc_fv_adc2.hdf5 -e363fa64b8d1b516891dd1b4b517dd7c82472c0b10e5b50de739edc7962819fa h2o_sto3g_adcc_reference_fv_adc2x.hdf5 -1d1522a9bafbca2a3e8941e9019a9c72288f35f263dbf80c2063c045d50d01f2 h2o_sto3g_adcc_reference_fv_cvs_adc2x.hdf5 -9296e53a9e46f84a6469aee5ca8d1dbc06f4bcf0c093785023fb02117a0763ea h2o_sto3g_adcc_reference_ip_adc0.hdf5 -651ef5c89dce12965ac35ef729cfdf78d5281a7f007de5df42f1d588cabab2a5 h2o_sto3g_adcc_reference_ip_adc1.hdf5 -7c0e508e2fce55beb6780e0e52613d7737c8fe69871b8cecf1df098cf3e7d3db h2o_sto3g_adcc_reference_ip_adc2.hdf5 -0638302a44585564df3a604f43959382ea8735484df92f4d131a6e49628a0f6a h2o_sto3g_adcc_reference_ip_adc2x.hdf5 -ea4e6f354ca1a5d78761880759b54764e203895b140656ae40189a125d37dd95 h2o_sto3g_adcc_reference_ip_adc3.hdf5 cc957d99adaf872877b992860c9f0c9c8337f783b9c3ad20b36694391627188f h2o_sto3g_adcman_adc0.hdf5 4b36ac6cce98cf250ad268dc6e51e849acbd8c9383aa16aff233a2cf04118fcd h2o_sto3g_adcman_adc1.hdf5 3e63750574a0ba8a8137dcea7405a9dd045a1712bda043d7f227262146a881b8 h2o_sto3g_adcman_adc2.hdf5 @@ -234,53 +122,9 @@ dd39481ff664de5c228919687ea3fbcf1effaa0aaddff4750d008cb25066b956 h2o_sto3g_adcm 00abfa6fcd237571167e42e3dfd956550944a5f6d68f462e6017629c175f4730 h2o_sto3g_adcman_mpdata.hdf5 e0e17c4cc9618edbba2be2ad77c41c4b212deb9e88b458a2666a2a803a0cdba5 h2o_sto3g_hfdata.hdf5 9bf19ad2a3e3f8d70188c7bdba0b39b044a760ddc668ae4a419ef2fb41337d9e h2o_sto3g_hfimport.hdf5 -41311ff99ea440583fb653108263bba37e644fba99507cc1b228818c535d21bf h2o_sto3g_reference_adc0.hdf5 -27a4844e53e5af0d9e4e318958e8b0b4377ab959d9303743d9ee030490818f57 h2o_sto3g_reference_adc1.hdf5 -dc62e796373ba77f78ac11d558961be4b5964f7400a733316540f0d70993de07 h2o_sto3g_reference_adc2.hdf5 -415c6a1c8fecd19a73637d33fe7b832d7bf0ae9563235e02524651c28116bd6e h2o_sto3g_reference_adc2x.hdf5 -79a32fecf709cbe9917d2645c5ea61a9e072f5860e67ff067a05fe03b2eb1540 h2o_sto3g_reference_adc3.hdf5 -7b0f67ef8bf16b0b04947007be1598f99bb2483743c75a7d89370526424f23ff h2o_sto3g_reference_cvs_adc0.hdf5 -a650d19fcdb1fbce6a2d9d5b13c0f1efbd43522fc59d267269ee1d6b2e9d3657 h2o_sto3g_reference_cvs_adc1.hdf5 -8432affb30a28d1fa2df91c83cd1360add4b26fe7c0f91ae742b0aec171a66dd h2o_sto3g_reference_cvs_adc2.hdf5 -c32008939174db29392760f46e75826ad266bcdced4c7bddddfa6408f14f3a02 h2o_sto3g_reference_cvs_adc2x.hdf5 -05174a726d4e6df7b5821fff76e759a82ec2eca946c4956de0821ccadcae451b h2o_sto3g_reference_cvs_adc3.hdf5 -0a89740716819c5c2e90c3b3ee1b4eb0fa0adf415064abcdd4acf245dddf2da2 h2o_sto3g_reference_fc_adc2.hdf5 -de7fea54d9f21e3a5c28aa724a35946f8bb65c49563025e0446e3ad6f09e8689 h2o_sto3g_reference_fc_fv_adc2.hdf5 -20d40379e52c8b5abe2c618f23da82c4f275722601e3484b3b5ed813ab98c69c h2o_sto3g_reference_fv_adc2x.hdf5 -db372645e0519fad40508402484bc53aaae62e1c46b74ad118e042498fd2907c h2o_sto3g_reference_fv_cvs_adc2x.hdf5 -93c87725ff53541e54d6a4ded03a83907bb75b391c353a32cf3eff962e2affd9 h2s_6311g_adcc_reference_adc2.hdf5 -b31a2a128ebbe7d2c2e2d1c0591fff27372f54b3ef8e7be344c022bdbba5df54 h2s_6311g_adcc_reference_cvs_adc2x.hdf5 -e7be7ce9275aab3669c0395f512616c2f3eb3d3f9b4ba1c57a4cf5df22fb4d00 h2s_6311g_adcc_reference_fc_adc2.hdf5 -7e07f4449d0a052785d63e6a173da483c5b6c084f268e39c3f42f4b99da68af5 h2s_6311g_adcc_reference_fc_cvs_adc2x.hdf5 -84c3888aa0e3780e60d975758ef20efa30593f75797d15096eec983500ddbfd2 h2s_6311g_adcc_reference_fc_fv_adc2.hdf5 -72c92b8e360f90a9743191ccd6b536aae9d1df1b8777257c7d035c57bfbe7a23 h2s_6311g_adcc_reference_fc_fv_cvs_adc2x.hdf5 -c481a49f3a754b2cf16873d8e6f3af589835f5449694326e37d79a79985fd8d2 h2s_6311g_adcc_reference_fv_adc2.hdf5 -8a6e8ab01541fe36d6402bee2f9af5c0aafe17838574f2a77c88f3e64d3713eb h2s_6311g_adcc_reference_fv_cvs_adc2x.hdf5 057ef124dc4b3fdfe4958f74009500a6507c8f2bb2a24f0a9cb4bc8119334c10 h2s_6311g_hfdata.hdf5 -457e5a4cc77d25fe6021065c43a7cb52fe353e4c0bff12c805650e93c2256d30 h2s_6311g_reference_adc2.hdf5 -de4b464d8275e0941c76f047d3ea21b2c3396b1b27fbf870fa715c9c2a0d5b62 h2s_6311g_reference_cvs_adc2x.hdf5 -ba02b69f3c320e4d1eee7caf1201e310b344ce749e7bfa9451a99953281ad0bf h2s_6311g_reference_fc_adc2.hdf5 -e75f1193409af67502ea4b0e44cc5077d219d1efa96e054713bc2c693f8ee035 h2s_6311g_reference_fc_cvs_adc2x.hdf5 -60262b06d393ae29ebeb2639c2c78df057d5d3c915845507cd635556fb1ffe81 h2s_6311g_reference_fc_fv_adc2.hdf5 -f1aae3bc870c1a11c2f3eded009369ba4502577542819bd40d018f9db015a62c h2s_6311g_reference_fc_fv_cvs_adc2x.hdf5 -6721731cbd54998f325e49878f142a6ffea3fa4022f76e8e2af185ce83c80935 h2s_6311g_reference_fv_adc2.hdf5 -5be5c174e016eb8a5482b016409285ea52ee3ecc3580a85ae7c426307633735f h2s_6311g_reference_fv_cvs_adc2x.hdf5 -76a24f902aba79c034e974fcdfaf244cca1f595673f6d625461fc80c6f19ffba h2s_sto3g_adcc_reference_fc_cvs_adc2.hdf5 -5abaac90d766b61e7f6b2c3f4d559ac7f50088303e42566ded4213dc18c97ac4 h2s_sto3g_adcc_reference_fc_fv_cvs_adc2x.hdf5 4354d9a8f676370454deb43116775f111d9e1a32508ef7e0adaa0cca67c1a87d h2s_sto3g_hfdata.hdf5 -63312155f2dcdb8a342014506e10897e738bf5adb23215d12664d3f90e512ad5 h2s_sto3g_reference_fc_cvs_adc2.hdf5 -16c373e332f9d845d35f02a7c0b20050127ef4ddaadb8ae89e98131370ce958d h2s_sto3g_reference_fc_fv_cvs_adc2x.hdf5 -4e3bf60040ee4520918411c7c1148888c9fac21b416a04693ba7e9641d927dcc hf3_631g_adcc_reference_adc0.hdf5 -3d6f3b8f497a1309a03160b546bc33c88a23a57e3ad443f0958df65b7d55f255 hf3_631g_adcc_reference_adc1.hdf5 -c2a26aafd78e64b3be4d77af3962a5309276b5fbff4be738cade7d5c1aaa139c hf3_631g_adcc_reference_adc2.hdf5 -271907a527f439aba1e2fc63c3c506390815eee44f814109a1b70aae846752e9 hf3_631g_adcc_reference_adc2x.hdf5 -0b46241370261b2dfb48df1d89ba42f16d26462930149517455ba2d5de953bbd hf3_631g_adcc_reference_adc3.hdf5 93bf9f69b8ab2c524b499bc60790f245401ab0d65cc342e92479c68051b0f34d hf3_631g_hfdata.hdf5 -a3d40fe926cd3bf4a0d8e81747cc449238aecc3b34366e0d4214b2a2ad61130f hf3_631g_reference_adc0.hdf5 -0c11fdcb42b1004844c85f9da7427a611f77d3315040c3abccc8b60033275ea6 hf3_631g_reference_adc1.hdf5 -4c950719db7ae7b799cb55c82235c87c811b2a9ca8bb99bb727196a334ecee99 hf3_631g_reference_adc2.hdf5 -5d43387bda90cd51e2da2a904a34e63ad5ddd3e3b891d41ac306ee1233b2a293 hf3_631g_reference_adc2x.hdf5 -a64f0d44387dcc56a73c986da34070a4ec5888191cb42511b5c4ef6acbc324b4 hf3_631g_reference_adc3.hdf5 ca45224813877e28332fd27c445320200e34447fca4e1f3dd2fe318948caf3ae hf_631g_adcc_adc0.hdf5 c5b7f42d403183e7e44289d7b3bca21cec217a9cf01fb6bf4e0ae33d17884d21 hf_631g_adcc_adc1.hdf5 d6809c1474a375141a3cd065ceabf84e26ade89e79973c6403110bb77c6a0e7d hf_631g_adcc_adc2.hdf5 @@ -300,25 +144,5 @@ d985ebce808b05b8fd9c57d4775bcc84bfb5d6c0ec30ec9c1606282acedeb449 hf_631g_adcman 55132fbeeb9a63d77590b56276a416d906118a417bdb62c0c10aabf0846ea3e3 hf_631g_adcman_ip-adc3.hdf5 1d7b0dd4e61aea9faf713706e5ec143426e97a55da4df575d6bd72e5dfcd872b hf_631g_adcman_mpdata.hdf5 a1eab1dd706a3f8f7532889b9a4754fa627a47ce5cfb9eb7d95a8365774850f2 hf_631g_hfdata.hdf5 -b0667a516263af22a4a89b95bc82f646dec87ba5a07c9ff22aa225f9cf276fa3 methox_sto3g_adcc_reference_adc0.hdf5 -2e55b34a015ed239427ed684b08e29bf2efe4103cd504cb446ba68538fb3e40d methox_sto3g_adcc_reference_adc1.hdf5 -5439aee010add74452c2cc490c882377a54e8e65f7b0105c6b4db81ab57a88d4 methox_sto3g_adcc_reference_adc2.hdf5 -f075aff7f9f2dac98e291f0a2c88a5e69e51dcc14cdccd33e0c4148188c2c1d3 methox_sto3g_adcc_reference_adc2x.hdf5 -8c5d1e9acce8f117f32f823553b38703cc2ff78af732995984a0d7283fa2e6ed methox_sto3g_adcc_reference_adc3.hdf5 -6e8ab22aa359f2055ed46208b2b2ce21dfb579741bc47ea39e5598256f76b29f methox_sto3g_adcc_reference_cvs_adc0.hdf5 -5777832284772cb1ee3c36f84923f53069d00aa572963ddb899bcbf0cebceed4 methox_sto3g_adcc_reference_cvs_adc1.hdf5 -e03fcaeb5c47fbe141cd250d3e314a1eed73ceb218ada5bd2d44647e9ef6d39a methox_sto3g_adcc_reference_cvs_adc2.hdf5 -085040675f53c3e306ca7b2e5df0727f9c32bb452918bf2336b53893ec7b330a methox_sto3g_adcc_reference_cvs_adc2x.hdf5 -c2b33dc2e074b54b20f398b83f8deedae26e32c524d1c0fa43f6c62eb98ba95a methox_sto3g_adcc_reference_cvs_adc3.hdf5 5877bf4fc4554759b2f0ba35c58cee1e9a7ae2de4d47673a98793d4769eec841 methox_sto3g_hfdata.hdf5 -fd0e374f6590b5145bb255e77763f96217abc5912764091e0fc97fb387b9c643 methox_sto3g_reference_adc0.hdf5 -0f617bf0ff98c15f7d514197391b1da4737758d948ce2c865f1d8bf76b8af6af methox_sto3g_reference_adc1.hdf5 -9064a58a5f27827f480f396848b1cbbf419078cb957a3d293d6652db5c5f338b methox_sto3g_reference_adc2.hdf5 -3d768b2b217e06605961e0b94475a264017463a4480db6a15cfe720012b5ecbc methox_sto3g_reference_adc2x.hdf5 -a1a4d87899df29556f7cbb6c27069b350bd597132013ba7f88e987e7f5ba4dd1 methox_sto3g_reference_adc3.hdf5 -e85bcde56f355ae01ab93cd0234b2885dd2a79db9835614d29f178960f75f6ba methox_sto3g_reference_cvs_adc0.hdf5 -1030ea26fce674dc7e319627e4521e5f27d23b1c097e7c331896bc5f83f435a9 methox_sto3g_reference_cvs_adc1.hdf5 -9c6983ca3d94769836a48ad8ce39d99ee509cc18d104f5e4570f9809ef16153e methox_sto3g_reference_cvs_adc2.hdf5 -5f822408ea4f6b2650078bb68b7a577c64019ae691d97a162d744419b221d018 methox_sto3g_reference_cvs_adc2x.hdf5 -f348fe9b0556c2b75e712e4e33f891e88de0744d10d1bed8e83739c463b58024 methox_sto3g_reference_cvs_adc3.hdf5 0e86aef221cc541cbade47e99471fa7c0bc1955c80783be4b32a8ea58902b2f6 r2methyloxirane_sto3g_hfdata.hdf5 From 62684e9da6edd1419a7ce31c1f393a64741cd2bd Mon Sep 17 00:00:00 2001 From: fedy9 Date: Tue, 12 May 2026 14:29:52 +0200 Subject: [PATCH 10/26] code style --- adcc/AdcMethod.py | 2 +- adcc/adc_ea/matrix.py | 6 +- adcc/adc_ip/matrix.py | 4 +- adcc/adc_pp/matrix.py | 2 +- adcc/guess/__init__.py | 10 ++-- adcc/guess/guess_zero.py | 3 +- adcc/guess/util.py | 8 +-- adcc/projection.py | 8 +-- adcc/tests/AdcMatrix_test.py | 27 +++++---- adcc/tests/generators/dump_adcc.py | 26 ++++++--- adcc/tests/generators/generate_adcc_data.py | 58 +++++++++---------- adcc/tests/generators/generate_adcman_data.py | 10 ++-- adcc/tests/generators/import_qchem_data.py | 18 +++--- adcc/tests/generators/run_qchem.py | 8 +-- adcc/tests/guess_test.py | 40 ++++++------- adcc/tests/properties_test.py | 6 +- adcc/tests/testdata_cache.py | 13 ++--- adcc/tests/workflow_test.py | 8 +-- adcc/workflow.py | 43 ++++++-------- 19 files changed, 154 insertions(+), 146 deletions(-) diff --git a/adcc/AdcMethod.py b/adcc/AdcMethod.py index 7b15e1d82..b7f4cc3f3 100644 --- a/adcc/AdcMethod.py +++ b/adcc/AdcMethod.py @@ -80,7 +80,7 @@ def __init__(self, method: str): # validate prefix split = split[:-1] - valid_prefixes: tuple[str, ...] = ("cvs","ip", "ea") + valid_prefixes: tuple[str, ...] = ("cvs", "ip", "ea") # Can be only "ip" or "ea", so number of prefixes is one less if len(split) > len(valid_prefixes) - 1: raise ValueError("Invalid number of method prefixes provided " diff --git a/adcc/adc_ea/matrix.py b/adcc/adc_ea/matrix.py index 63deeb6c6..d3ddff4d1 100644 --- a/adcc/adc_ea/matrix.py +++ b/adcc/adc_ea/matrix.py @@ -51,7 +51,7 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): intermediates = Intermediates(ground_state) fn = b.get_block_name(spaces, order, variant, - ground_state.has_core_occupied_space) + ground_state.has_core_occupied_space) if fn not in globals(): raise ValueError("Could not dispatch: " @@ -121,7 +121,7 @@ def block_p_pph_1(hf, mp, intermediates): # M_{12} def apply(ampl): return AmplitudeVector(p=( - - 1 / sqrt(2) * einsum("jabc,jbc->a", hf.ovvv, ampl.pph))) + - 1 / sqrt(2) * einsum("jabc,jbc->a", hf.ovvv, ampl.pph))) return AdcBlock(apply, 0) @@ -157,7 +157,7 @@ def block_p_pph_2(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(p=( - + 1 / sqrt(2) * einsum("jabc,jbc->a", i2, ampl.pph))) + + 1 / sqrt(2) * einsum("jabc,jbc->a", i2, ampl.pph))) return AdcBlock(apply, 0) diff --git a/adcc/adc_ip/matrix.py b/adcc/adc_ip/matrix.py index 9b2a56606..9845fb7e8 100644 --- a/adcc/adc_ip/matrix.py +++ b/adcc/adc_ip/matrix.py @@ -51,7 +51,7 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): intermediates = Intermediates(ground_state) fn = b.get_block_name(spaces, order, variant, - ground_state.has_core_occupied_space) + ground_state.has_core_occupied_space) if fn not in globals(): raise ValueError("Could not dispatch: " @@ -167,7 +167,7 @@ def block_h_phh_2(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(h=( - + 1 / sqrt(2) * einsum("jkib,jkb->i", i2, ampl.phh))) + + 1 / sqrt(2) * einsum("jkib,jkb->i", i2, ampl.phh))) return AdcBlock(apply, 0) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 2a9befdb6..759b90345 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -65,7 +65,7 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): intermediates = Intermediates(ground_state) fn = b.get_block_name(spaces, order, variant, - ground_state.has_core_occupied_space) + ground_state.has_core_occupied_space) if fn not in globals(): raise ValueError("Could not dispatch: " diff --git a/adcc/guess/__init__.py b/adcc/guess/__init__.py index 8fb0aaae4..0a835cd6c 100644 --- a/adcc/guess/__init__.py +++ b/adcc/guess/__init__.py @@ -24,7 +24,7 @@ from .guesses_from_diagonal import guesses_from_diagonal from .util import estimate_n_guesses, determine_spin_change -__all__ = ["guess_zero", "guesses_from_diagonal", +__all__ = ["guess_zero", "guesses_from_diagonal", "get_spin_block_symmetrisation", "guesses_doublet", "guesses_singlet", "guesses_triplet", "guesses_any", "guesses_spin_flip", "guess_symmetries", @@ -40,7 +40,7 @@ def get_spin_block_symmetrisation(kind: str) -> str: "singlet": "symmetric", "doublet": "none", "triplet": "antisymmetric", - "spin_flip":"none", + "spin_flip": "none", "any": "none" } try: @@ -91,7 +91,7 @@ def guesses_doublet(matrix, n_guesses, block="h", is_alpha=True, **kwargs): return guesses_from_diagonal( matrix, n_guesses, block=block, is_alpha=is_alpha, spin_change=spin_change, - spin_block_symmetrisation= get_spin_block_symmetrisation("doublet"), + spin_block_symmetrisation=get_spin_block_symmetrisation("doublet"), **kwargs ) @@ -112,7 +112,7 @@ def guesses_triplet(matrix, n_guesses, block="ph", **kwargs): """ return guesses_from_diagonal( matrix, n_guesses, block=block, spin_change=0, - spin_block_symmetrisation= get_spin_block_symmetrisation("triplet"), + spin_block_symmetrisation=get_spin_block_symmetrisation("triplet"), **kwargs ) @@ -137,6 +137,6 @@ def guesses_spin_flip(matrix, n_guesses, block="ph", **kwargs): """ return guesses_from_diagonal( matrix, n_guesses, block=block, spin_change=-1, - spin_block_symmetrisation= get_spin_block_symmetrisation("spin_flip"), + spin_block_symmetrisation=get_spin_block_symmetrisation("spin_flip"), **kwargs ) \ No newline at end of file diff --git a/adcc/guess/guess_zero.py b/adcc/guess/guess_zero.py index 4ed50dc21..20d50c58a 100644 --- a/adcc/guess/guess_zero.py +++ b/adcc/guess/guess_zero.py @@ -84,7 +84,8 @@ def guess_symmetries(matrix, spin_change=0, spin_block_symmetrisation="none"): " You passed {}".format(spin_change)) max_spin_change = 0.5 * len(matrix.axis_blocks[-1]) - valid_spin_changes = [max_spin_change - i for i in range(int(2 * max_spin_change + 1))] + valid_spin_changes = [ + max_spin_change - i for i in range(int(2 * max_spin_change + 1))] if spin_change not in valid_spin_changes: raise ValueError("spin_change for may only be one of " diff --git a/adcc/guess/util.py b/adcc/guess/util.py index aa0eb7ebc..a707c18e3 100644 --- a/adcc/guess/util.py +++ b/adcc/guess/util.py @@ -45,7 +45,7 @@ def determine_spin_change(method: AdcMethod, kind: str, def estimate_n_guesses(matrix, n_states, n_guesses_per_state=2, - singles_only=True) -> int: + singles_only=True) -> int: """ Implementation of a basic heuristic to find a good number of guess vectors to be searched for using the find_guesses function. @@ -73,12 +73,12 @@ def estimate_n_guesses(matrix, n_states, n_guesses_per_state=2, n_virt_a = mospaces.n_orbs_alpha("v1") n_occ_a = mospaces.n_orbs_alpha(sp_occ) estimate = n_occ_a * n_virt_a - if matrix.method.level < 2 and matrix.method.adc_type != "pp": + if matrix.method.level.to_int() < 2 and matrix.method.adc_type != "pp": # Adjustment for IP- and EA-ADC(0/1) calculations - estimate = (n_occ_a if matrix.method.adc_type == "ip" + estimate = (n_occ_a if matrix.method.adc_type == "ip" else n_virt_a) n_guesses = min(n_guesses, estimate) # Adjust if we overshoot the maximal number of sensible singles block # guesses, but make sure we get at least n_states guesses - return max(n_states, n_guesses) \ No newline at end of file + return max(n_states, n_guesses) diff --git a/adcc/projection.py b/adcc/projection.py index b3b7fa749..a25fa5fc7 100644 --- a/adcc/projection.py +++ b/adcc/projection.py @@ -346,10 +346,10 @@ def transfer_cvs_to_full(state_matrix_cvs, matrix_full, vector=None, kind=None, "ExcitedStates object.") if isinstance(vector, list): return [transfer_cvs_to_full( - state_matrix_cvs, matrix_full, v, kind, - spin_change=determine_spin_change(matrix_full.method, kind), - spin_block_symmetrisation=get_spin_block_symmetrisation(kind) - ) for v in vector] + state_matrix_cvs, matrix_full, v, kind, + spin_change=determine_spin_change(matrix_full.method, kind), + spin_block_symmetrisation=get_spin_block_symmetrisation(kind) + ) for v in vector] if isinstance(state_matrix_cvs, AdcMatrixlike): mospaces_cvs = state_matrix_cvs.mospaces diff --git a/adcc/tests/AdcMatrix_test.py b/adcc/tests/AdcMatrix_test.py index e2914afbb..d01662e77 100644 --- a/adcc/tests/AdcMatrix_test.py +++ b/adcc/tests/AdcMatrix_test.py @@ -115,8 +115,8 @@ def test_validate_space_pp(self): def test_validate_block_orders_ip_ea(self): # Valid IP block orders valid_ip = ( - {"h_h": 0}, # ip-adc0 - {"h_h": 2, "h_phh": 1, "phh_h": 1, "phh_phh": 0}, # ip-adc2 + {"h_h": 0}, # ip-adc0 + {"h_h": 2, "h_phh": 1, "phh_h": 1, "phh_phh": 0}, # ip-adc2 ) for block_orders in valid_ip: AdcMatrixlike._validate_block_orders( @@ -125,8 +125,8 @@ def test_validate_block_orders_ip_ea(self): # Valid EA block orders valid_ea = ( - {"p_p": 0}, # ea-adc0 - {"p_p": 2, "p_pph": 1, "pph_p": 1, "pph_pph": 0}, # ea-adc2 + {"p_p": 0}, # ea-adc0 + {"p_p": 2, "p_pph": 1, "pph_p": 1, "pph_pph": 0}, # ea-adc2 ) for block_orders in valid_ea: AdcMatrixlike._validate_block_orders( @@ -229,10 +229,12 @@ def test_matvec(self, system: str, case: str, method: str): kind = "any" # we don't do the test for spin flip trial_vec = self.construct_trial_vec(system, case, method, kind) result = matrix @ trial_vec - assert_allclose(matdata["matvec_singles"], result.get(matrix.axis_blocks[0]).to_ndarray(), + assert_allclose(matdata["matvec_singles"], + result.get(matrix.axis_blocks[0]).to_ndarray(), rtol=1e-10, atol=1e-12) if "matvec_doubles" in matdata: - assert_allclose(matdata["matvec_doubles"], result.get(matrix.axis_blocks[1]).to_ndarray(), + assert_allclose(matdata["matvec_doubles"], + result.get(matrix.axis_blocks[1]).to_ndarray(), rtol=1e-10, atol=1e-12) def test_compute_block(self, system: str, case: str, method: str): @@ -345,7 +347,7 @@ def test_axis_structure_all_types(self, system, case, method): assert matrix.mospaces == reference_state.mospaces @pytest.mark.parametrize("method", pp_methods + ip_methods + ea_methods) - @pytest.mark.parametrize("case", ["gen"]) # No CVS for IP/EA + @pytest.mark.parametrize("case", ["gen"]) # No CVS for IP/EA @pytest.mark.parametrize("system", ["h2o_sto3g"]) def test_properties(self, system: str, case: str, method: str): reference_state = testdata_cache.refstate(system=system, case=case) @@ -434,7 +436,8 @@ def test_matvec_adc2(self, method: str): elif matrix.method.adc_type == "ea": spin_change = 0.5 - vectors = [adcc.guess_zero(matrix, spin_change=spin_change) for _ in range(3)] + vectors = [ + adcc.guess_zero(matrix, spin_change=spin_change) for _ in range(3)] for vec in vectors: vec.set_random() v, w, x = vectors @@ -469,8 +472,10 @@ def test_matvec_adc2(self, method: str): assert diffv.get(blocks[1]).dot(diffv.get(blocks[1])) < 1e-12 # Test apply - resv[blocks[0]] = matrix.block_apply(f"{blocks[0]}_{blocks[0]}", v.get(blocks[0])) - resv[blocks[0]] += matrix.block_apply(f"{blocks[0]}_{blocks[1]}", v.get(blocks[1])) + resv[blocks[0]] = matrix.block_apply(f"{blocks[0]}_{blocks[0]}", + v.get(blocks[0])) + resv[blocks[0]] += matrix.block_apply(f"{blocks[0]}_{blocks[1]}", + v.get(blocks[1])) refv = matrix.matvec(v) diffv = resv.get(blocks[0]) - refv.get(blocks[0]) assert diffv.dot(diffv) < 1e-12 @@ -545,7 +550,7 @@ def apply(invec): @pytest.mark.parametrize("system", ["h2o_sto3g", "cn_sto3g"]) @pytest.mark.parametrize("method", ["adc3", "ip-adc3", "ea-adc3"]) class TestAdcMatrixShifted: - def construct_matrices(self, system:str, method: str, shift: float): + def construct_matrices(self, system: str, method: str, shift: float): reference_state = testdata_cache.refstate(system, case="gen") ground_state = adcc.LazyMp(reference_state) matrix = adcc.AdcMatrix(method, ground_state) diff --git a/adcc/tests/generators/dump_adcc.py b/adcc/tests/generators/dump_adcc.py index a408834f9..1d3745957 100644 --- a/adcc/tests/generators/dump_adcc.py +++ b/adcc/tests/generators/dump_adcc.py @@ -12,6 +12,7 @@ from typing import Union + def dump_groundstate(ground_state: LazyMp, hdf5_file: h5py.Group, only_full_mode: bool) -> None: """ @@ -67,7 +68,8 @@ def dump_groundstate(ground_state: LazyMp, hdf5_file: h5py.Group, hdf5_file.attrs["adcc_version"] = adcc.__version__ -def _pp_adc_properties(states: ExcitedStates, kind_data: dict, n_states: int) -> None: +def _pp_adc_properties(states: ExcitedStates, kind_data: dict, n_states: int + ) -> None: """Collects all properties that are unique for PP-ADC """ tdm_bb_a = [] # Ground to Excited state tdm AO basis alpha part @@ -106,8 +108,8 @@ def _ip_ea_adc_properties(states: Union[DetachedStates, AttachedStates], def dump_excited_states( - states: Union[ExcitedStates,DetachedStates, AttachedStates], - hdf5_file: h5py.Group, dump_nstates: int = None) -> None: + states: Union[ExcitedStates, DetachedStates, AttachedStates], + hdf5_file: h5py.Group, dump_nstates: int = None) -> None: """ Dump the (charged) excited states data to the given hdf5 file/group. The number of states to dump can be given by dump_nstates. By default all states @@ -170,14 +172,20 @@ def dump_excited_states( bb_a, bb_b = tdm.to_ao_basis(states.reference_state) tdm_bb_a.append(bb_a.to_ndarray()) tdm_bb_b.append(bb_b.to_ndarray()) - kind_data[f"state_to_state/from_{ifrom}/transition_dipole_moments"] = ( - state2state.transition_dipole_moment[:n_states - ifrom - 1] + kind_data[ + f"state_to_state/from_{ifrom}/transition_dipole_moments" + ] = ( + state2state.transition_dipole_moment[:n_states - ifrom - 1] ) - kind_data[f"state_to_state/from_{ifrom}/state_to_excited_tdm_bb_a"] = ( - np.asarray(tdm_bb_a) + kind_data[ + f"state_to_state/from_{ifrom}/state_to_excited_tdm_bb_a" + ] = ( + np.asarray(tdm_bb_a) ) - kind_data[f"state_to_state/from_{ifrom}/state_to_excited_tdm_bb_b"] = ( - np.asarray(tdm_bb_b) + kind_data[ + f"state_to_state/from_{ifrom}/state_to_excited_tdm_bb_b" + ] = ( + np.asarray(tdm_bb_b) ) # ssq for unrestriced calculation if not states.reference_state.restricted \ diff --git a/adcc/tests/generators/generate_adcc_data.py b/adcc/tests/generators/generate_adcc_data.py index 4706edc56..6eb0e6b70 100644 --- a/adcc/tests/generators/generate_adcc_data.py +++ b/adcc/tests/generators/generate_adcc_data.py @@ -145,7 +145,7 @@ def generate_h2o_sto3g(): for n_states in \ testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): per_case = None - if method.level < 2: # adc0/adc1 + if method.to_int() < 2: # adc0/adc1 per_case = { case: {n_states: n} for case, n in states_per_case.items() } @@ -154,7 +154,7 @@ def generate_h2o_sto3g(): # 4 states available # -> reduce n_guesses for all ADC(2) and ADC(3) calculations kwargs = {} - if method.level >= 2: + if method.level.to_int() >= 2: kwargs = {"n_guesses": 4} generate_adc_all( test_case, method=method, dump_nstates=2, states_per_case=per_case, @@ -167,21 +167,21 @@ def generate_h2o_sto3g(): testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): n_states = {n_states: 3} generate_adc_all( - test_case, method=method, dump_nstates=2, is_alpha=True, - **n_states + test_case, method=method, dump_nstates=2, is_alpha=True, + **n_states ) for method in _methods["ea"]: method = AdcMethod(method) for n_states in \ testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): - if method.level < 2: + if method.level.to_int() < 2: n_states = {n_states: 1} else: n_states = {n_states: 3} generate_adc_all( - test_case, method=method, dump_nstates=2, is_alpha=True, - **n_states + test_case, method=method, dump_nstates=2, is_alpha=True, + **n_states ) @@ -207,8 +207,8 @@ def generate_h2o_def2tzvp(): testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): n_states = {n_states: 3} generate_adc_all( - test_case, method=method, dump_nstates=2, is_alpha=True, - **n_states + test_case, method=method, dump_nstates=2, is_alpha=True, + **n_states ) for method in _methods["ea"]: @@ -217,8 +217,8 @@ def generate_h2o_def2tzvp(): testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): n_states = {n_states: 3} generate_adc_all( - test_case, method=method, dump_nstates=2, is_alpha=True, - **n_states + test_case, method=method, dump_nstates=2, is_alpha=True, + **n_states ) @@ -240,27 +240,27 @@ def generate_cn_sto3g(): for method in _methods["ip"]: method = AdcMethod(method) for n_states in \ - testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): - if method.level < 2: + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + if method.level.to_int() < 2: n_states = {n_states: 2} else: n_states = {n_states: 3} generate_adc_all( - test_case, method=method, dump_nstates=2, - is_alpha=is_alpha, **n_states + test_case, method=method, dump_nstates=2, + is_alpha=is_alpha, **n_states ) for method in _methods["ea"]: method = AdcMethod(method) for n_states in \ - testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): - if method.level < 2: + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + if method.level.to_int() < 2: n_states = {n_states: 2} else: n_states = {n_states: 3} generate_adc_all( - test_case, method=method, dump_nstates=2, - is_alpha=is_alpha, **n_states + test_case, method=method, dump_nstates=2, + is_alpha=is_alpha, **n_states ) @@ -274,29 +274,29 @@ def generate_cn_ccpvdz(): testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): n_states = {n_states: 3} generate_adc_all( - test_case, method=method, dump_nstates=2, states_per_case=None, - **n_states + test_case, method=method, dump_nstates=2, + states_per_case=None, **n_states ) for is_alpha in [True, False]: for method in _methods["ip"]: method = AdcMethod(method) for n_states in \ - testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): n_states = {n_states: 3} generate_adc_all( - test_case, method=method, dump_nstates=2, - is_alpha=is_alpha, **n_states + test_case, method=method, dump_nstates=2, + is_alpha=is_alpha, **n_states ) for method in _methods["ea"]: method = AdcMethod(method) for n_states in \ - testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): n_states = {n_states: 3} generate_adc_all( - test_case, method=method, dump_nstates=2, - is_alpha=is_alpha, **n_states + test_case, method=method, dump_nstates=2, + is_alpha=is_alpha, **n_states ) @@ -318,7 +318,7 @@ def generate_hf_631g(): for method in _methods["ip"]: method = AdcMethod(method) for n_states in \ - testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): n_states = {n_states: 3} generate_adc_all( test_case, method=method, dump_nstates=2, @@ -328,7 +328,7 @@ def generate_hf_631g(): for method in _methods["ea"]: method = AdcMethod(method) for n_states in \ - testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): n_states = {n_states: 3} generate_adc_all( test_case, method=method, dump_nstates=2, diff --git a/adcc/tests/generators/generate_adcman_data.py b/adcc/tests/generators/generate_adcman_data.py index 54419b634..0d87fa8ee 100644 --- a/adcc/tests/generators/generate_adcman_data.py +++ b/adcc/tests/generators/generate_adcman_data.py @@ -55,11 +55,11 @@ def generate_adc(test_case: testcases.TestCase, method: AdcMethod, case: str, if key in hdf5_file: return None # skip cvs-adc(0), since it is not available in qchem. - if "cvs" in case and method.level == 0: + if "cvs" in case and method.level.to_int() == 0: return None # gs_density_order is only available for adc(3) and adc(4) # and it is not available for cvs-adc - if gs_density_order is not None and (method.level < 3 or "cvs" in case): + if gs_density_order is not None and (method.level.to_int() < 3 or "cvs" in case): return None print(f"Generating {method.name} (gs_density_order={gs_density_order}) " f"data for {case} {test_case.file_name}.") @@ -195,7 +195,7 @@ def generate_h2o_sto3g(): for method in _methods["ea"]: method = AdcMethod(method) - if method.level < 2: + if method.to_int() < 2: n_states = {"n_ea_states": (1, 0)} else: n_states = {"n_ea_states": (3, 0)} @@ -250,7 +250,7 @@ def generate_cn_sto3g(): for method in _methods["ip"]: method = AdcMethod(method) - if method.level < 2: + if method.level.to_int() < 2: n_states = {"n_ip_states": (2, 2)} else: n_states = {"n_ip_states": (3, 3)} @@ -260,7 +260,7 @@ def generate_cn_sto3g(): for method in _methods["ea"]: method = AdcMethod(method) - if method.level < 2: + if method.level.to_int() < 2: n_states = {"n_ea_states": (2, 2)} else: n_states = {"n_ea_states": (3, 3)} diff --git a/adcc/tests/generators/import_qchem_data.py b/adcc/tests/generators/import_qchem_data.py index f9dbfdf84..279db9874 100644 --- a/adcc/tests/generators/import_qchem_data.py +++ b/adcc/tests/generators/import_qchem_data.py @@ -74,29 +74,29 @@ def import_excited_states(context: h5py.File, method: AdcMethod, # define the possible state kinds to import for each adc variant. state_kinds = { "pp": { - True: ["singlets", "triplets"], # restricted + True: ["singlets", "triplets"], # restricted # uhf and spin flip are located in the same ".../uhf/..." subtree False: ["any_or_spinflip"], # unrestricted }, "ip": { - True: [""], # restricted - False: ["alphas", "betas"], # unrestricted + True: [""], # restricted + False: ["alphas", "betas"], # unrestricted }, "ea": { - True: [""], # restricted - False: ["alphas", "betas"], # unrestricted + True: [""], # restricted + False: ["alphas", "betas"], # unrestricted }, } # Of course the kinds have to have a slightly different name in adcc.... kind_map = {"singlets": "singlet", "triplets": "triplet", - "alphas": "alpha", "betas": "beta"} + "alphas": "alpha", "betas": "beta"} # also the adcc methods have to be translated if method.adc_type == "pp": method_name: str = method.name.replace("-", "_") # cvs-adcn -> cvs_adcn if method_name.endswith("adc2"): # adc2 -> adc2s (Only for PP) method_name += "s" else: - method_name: str = method.name.split('-')[-1] # No cvs (yet) + method_name: str = method.name.split('-')[-1] # No cvs (yet) restricted = "rhf" in context[f"adc_{method.adc_type}"][method_name].keys() # go through the different possible state kinds and import the states. @@ -175,7 +175,7 @@ def _import_excited_states(context: h5py.File, method: str, only_full_mode: bool if restricted: tree.append("rhf") if adc_type == "pp": - assert state_kind is not None # needs to be defined for restricted calcs + assert state_kind is not None # needs to be defined for restricted tree.append(state_kind) else: tree.append("uhf") @@ -280,7 +280,7 @@ def _import_state_to_state_data(context: h5py.File, method: str, if restricted: tree.extend(["rhf", "isr"]) if adc_type == "pp": - assert state_kind is not None # needs to be defined for restricted calcs + assert state_kind is not None # needs to be defined for restricted tree.append(state_kind) else: tree.extend(["uhf", "isr"]) diff --git a/adcc/tests/generators/run_qchem.py b/adcc/tests/generators/run_qchem.py index 62f5f9f81..b7525ce67 100644 --- a/adcc/tests/generators/run_qchem.py +++ b/adcc/tests/generators/run_qchem.py @@ -64,11 +64,11 @@ def run_qchem(test_case: testcases.TestCase, method: AdcMethod, case: str, """ # sanitize the input assert method.is_core_valence_separated == ("cvs" in case) - if method.is_core_valence_separated and method.level == 0: + if method.is_core_valence_separated and method.level.to_int() == 0: raise ValueError("CVS-ADC(0) is not available in adcman.") if kwargs.get("gs_density_order", None) is not None: - if method.level < 3: + if method.level.to_int() < 3: raise ValueError("gs_density_order is only available for ADC(n) with " "n > 2") if method.is_core_valence_separated: @@ -352,8 +352,8 @@ def generate_qchem_input_file(infile: str | Path, adc_method: AdcMethod, basis: bohr: bool = True, any_states: int = 0, singlet_states: int = 0, triplet_states: int = 0, sf_states: int = 0, - ip_states: tuple[int, int] = (0,0), - ea_states: tuple[int, int] = (0,0), + ip_states: tuple[int, int] = (0, 0), + ea_states: tuple[int, int] = (0, 0), maxiter: int = 160, conv_tol: int = 10, n_core_orbitals: int | None = None, n_frozen_core: int | None = None, diff --git a/adcc/tests/guess_test.py b/adcc/tests/guess_test.py index 41b2a4cd2..d00077f5c 100644 --- a/adcc/tests/guess_test.py +++ b/adcc/tests/guess_test.py @@ -58,7 +58,6 @@ def test_determine_spin_change_pp(self): spin = determine_spin_change(method, kind="spin_flip") assert spin == -1.0 - def test_determine_spin_change_ip(self): from adcc.guess import determine_spin_change @@ -461,7 +460,7 @@ def assert_guess_values(self, matrix, block, guesses, spin_flip=False, diagonal = matrix.diagonal().get(block).to_ndarray() # Doubles guesses are constructed from the 0th order diagonal - if matrix.method.level > 1 and not matrix.method.name.endswith("adc2"): + if matrix.method.level.to_int() > 1 and not matrix.method.name.endswith("adc2"): if block == "pphh": diagonal = adcc.adc_pp.matrix.diagonal_pphh_pphh_0( matrix.reference_state @@ -483,7 +482,7 @@ def assert_guess_values(self, matrix, block, guesses, spin_flip=False, if block == "ph": if spin_flip: sidcs = [idx for idx in sidcs - if idx[0] < nCa and idx[1] >= nva] + if idx[0] < nCa and idx[1] >= nva] else: sidcs = [ idx for idx in sidcs @@ -517,8 +516,8 @@ def assert_guess_values(self, matrix, block, guesses, spin_flip=False, # cover the ccvv block in CVS-ADC. if triplet and not mospaces.has_core_occupied_space: sidcs = [idx for idx in sidcs - if abs(idx[0] - idx[1]) != noa - or abs(idx[2] - idx[3]) != nva] + if abs(idx[0] - idx[1]) != noa + or abs(idx[2] - idx[3]) != nva] sidcs = [idx for idx in sidcs if idx[2] != idx[3]] if not matrix.is_core_valence_separated: sidcs = [idx for idx in sidcs if idx[0] != idx[1]] @@ -535,17 +534,17 @@ def assert_guess_values(self, matrix, block, guesses, spin_flip=False, idx for idx in sidcs # aaa / abb / bab if any(( - idx[0] < noa and idx[1] < nCa and idx[2] < nva, - idx[0] < noa and idx[1] >= nCa and idx[2] >= nva, - idx[0] >= noa and idx[1] < nCa and idx[2] >= nva)) + idx[0] < noa and idx[1] < nCa and idx[2] < nva, + idx[0] < noa and idx[1] >= nCa and idx[2] >= nva, + idx[0] >= noa and idx[1] < nCa and idx[2] >= nva)) ] else: sidcs = [ idx for idx in sidcs # aba / baa / bbb if any(( - idx[0] < noa and idx[1] >= nCa and idx[2] < nva, - idx[0] >= noa and idx[1] < nCa and idx[2] < nva, + idx[0] < noa and idx[1] >= nCa and idx[2] < nva, + idx[0] >= noa and idx[1] < nCa and idx[2] < nva, idx[0] >= noa and idx[1] >= nCa and idx[2] >= nva)) ] sidcs = [idx for idx in sidcs if idx[0] != idx[1]] @@ -561,16 +560,16 @@ def assert_guess_values(self, matrix, block, guesses, spin_flip=False, sidcs = [ idx for idx in sidcs if any(( - idx[0] < noa and idx[1] < nva and idx[2] < nva, - idx[0] >= noa and idx[1] < nva and idx[2] >= nva, - idx[0] >= noa and idx[1] >= nva and idx[2] < nva)) + idx[0] < noa and idx[1] < nva and idx[2] < nva, + idx[0] >= noa and idx[1] < nva and idx[2] >= nva, + idx[0] >= noa and idx[1] >= nva and idx[2] < nva)) ] else: sidcs = [ idx for idx in sidcs if any(( - idx[0] < noa and idx[1] < nva and idx[2] >= nva, - idx[0] < noa and idx[1] >= nva and idx[2] < nva, + idx[0] < noa and idx[1] < nva and idx[2] >= nva, + idx[0] < noa and idx[1] >= nva and idx[2] < nva, idx[0] >= noa and idx[1] >= nva and idx[2] >= nva)) ] sidcs = [idx for idx in sidcs if idx[1] != idx[2]] @@ -761,7 +760,7 @@ def test_singles_cn_ip(self, method: str, case: str, is_alpha: bool): @pytest.mark.parametrize("case", cn_sto3g.filter_cases("ip")) @pytest.mark.parametrize("is_alpha", [True, False]) def test_doubles_cn_ip(self, method: str, case: str, is_alpha: bool): - case="gen" + case = "gen" self.base_test_ip( "cn_sto3g", case, method, block="phh", is_alpha=is_alpha, max_guesses=5 @@ -837,7 +836,6 @@ def test_doubles_hf_ea(self, method: str, case: str, is_alpha: bool): max_guesses=5 ) - # # Tests against reference values # @@ -1123,7 +1121,8 @@ def test_reference_cn_ip(self, method: str, is_alpha: bool): hf = testdata_cache.refstate("cn_sto3g", case="gen") matrix = adcc.AdcMatrix(method=method, hf_or_mp=hf) if not method.endswith("adc2"): - matrix._diagonal = adcc.AdcMatrix(method="ip-adc2", hf_or_mp=hf).diagonal() + matrix._diagonal = adcc.AdcMatrix( + method="ip-adc2", hf_or_mp=hf).diagonal() self.base_reference_degenerate_ip( matrix=matrix, ref=self.get_ref_cn_ip(), is_alpha=is_alpha ) @@ -1134,7 +1133,8 @@ def test_reference_cn_ea(self, method: str, is_alpha: bool): hf = testdata_cache.refstate("cn_sto3g", case="gen") matrix = adcc.AdcMatrix(method=method, hf_or_mp=hf) if not method.endswith("adc2"): - matrix._diagonal = adcc.AdcMatrix(method="ea-adc2", hf_or_mp=hf).diagonal() + matrix._diagonal = adcc.AdcMatrix( + method="ea-adc2", hf_or_mp=hf).diagonal() self.base_reference_degenerate_ea( matrix=matrix, ref=self.get_ref_cn_ea(), is_alpha=is_alpha ) @@ -1364,4 +1364,4 @@ def get_ref_cn_ea(self): ([(5, 0, 3), (5, 3, 0)], asymm), # occ. 4 and 5 are degenerate ([(4, 1, 3), (4, 3, 1)], asymm1) ], - } \ No newline at end of file + } diff --git a/adcc/tests/properties_test.py b/adcc/tests/properties_test.py index 14ffd2c84..f5d90dd33 100644 --- a/adcc/tests/properties_test.py +++ b/adcc/tests/properties_test.py @@ -62,7 +62,7 @@ class TestProperties: @pytest.mark.parametrize("system,case,kind", cases) def test_transition_dipole_moments(self, system: str, case: str, kind: str, method: str, generator: str): - if "cvs" in case and AdcMethod(method).level == 0 and generator == "adcman": + if "cvs" in case and AdcMethod(method).level.to_int() == 0 and generator == "adcman": pytest.skip("No CVS-ADC(0) adcman reference data available.") refdata = testdata_cache._load_data( @@ -95,7 +95,7 @@ def test_transition_dipole_moments(self, system: str, case: str, kind: str, @pytest.mark.parametrize("system,case,kind", cases) def test_oscillator_strengths(self, system: str, case: str, kind: str, method: str, generator: str): - if "cvs" in case and AdcMethod(method).level == 0 and generator == "adcman": + if "cvs" in case and AdcMethod(method).level.to_int() == 0 and generator == "adcman": pytest.skip("No CVS-ADC(0) adcman reference data available.") refdata = testdata_cache._load_data( @@ -126,7 +126,7 @@ def test_oscillator_strengths(self, system: str, case: str, kind: str, @pytest.mark.parametrize("system,case,kind", cases) def test_state_dipole_moments(self, system: str, case: str, kind: str, method: str, generator: str): - if "cvs" in case and AdcMethod(method).level == 0 and generator == "adcman": + if "cvs" in case and AdcMethod(method).level.to_int() == 0 and generator == "adcman": pytest.skip("No CVS-ADC(0) adcman reference data available.") refdata = testdata_cache._load_data( diff --git a/adcc/tests/testdata_cache.py b/adcc/tests/testdata_cache.py index f55eb1801..ccf9080fa 100644 --- a/adcc/tests/testdata_cache.py +++ b/adcc/tests/testdata_cache.py @@ -203,13 +203,12 @@ def adcman_data(self, system: str, method: str, case: str, ) @cached_member_function() - def _make_mock_adc_state( - self, system: Union[str, testcases.TestCase], - method: str, case: str, - kind: str, source: str, - gs_density_order: Optional[int] = None, - is_alpha: Optional[bool] = None - ) -> ExcitedStates | AttachedStates | DetachedStates: + def _make_mock_adc_state(self, system: Union[str, testcases.TestCase], + method: str, case: str, + kind: str, source: str, + gs_density_order: Optional[int] = None, + is_alpha: Optional[bool] = None + ) -> ExcitedStates | AttachedStates | DetachedStates: """ Create an ExcitedStates/AttachedStates/DetachedStates instance for the given test case, method (adcn), reference case (gen/cvs/fc/...), diff --git a/adcc/tests/workflow_test.py b/adcc/tests/workflow_test.py index 9775ae98c..c2f3603c7 100644 --- a/adcc/tests/workflow_test.py +++ b/adcc/tests/workflow_test.py @@ -418,7 +418,7 @@ def test_diagonalise_adcmatrix_pp(self): with pytest.raises(InputError): # Too low tolerance # SCF tolerance = 1e-14 currently res = diagonalise_adcmatrix(matrix, n_states=9, kind=kind, - guesses=guesses,eigensolver="davidson", + guesses=guesses, eigensolver="davidson", conv_tol=1e-15) with pytest.raises(InputError): # Wrong solver method @@ -454,7 +454,7 @@ def test_diagonalise_adcmatrix_ip(self): with pytest.raises(InputError): # Too low tolerance # SCF tolerance = 1e-14 currently res = diagonalise_adcmatrix(matrix, n_states=9, kind=kind, - guesses=guesses,eigensolver="davidson", + guesses=guesses, eigensolver="davidson", conv_tol=1e-15) with pytest.raises(InputError): # Wrong solver method @@ -489,7 +489,7 @@ def test_diagonalise_adcmatrix_ea(self): with pytest.raises(InputError): # Too low tolerance # SCF tolerance = 1e-14 currently res = diagonalise_adcmatrix(matrix, n_states=9, kind=kind, - guesses=guesses,eigensolver="davidson", + guesses=guesses, eigensolver="davidson", conv_tol=1e-15) with pytest.raises(InputError): # Wrong solver method @@ -673,4 +673,4 @@ def test_construct_guesses_lanczos(self): eigensolver="lanczos" ) - assert len(res) == 2 \ No newline at end of file + assert len(res) == 2 diff --git a/adcc/workflow.py b/adcc/workflow.py index 8b720a7c9..5331b8254 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -238,19 +238,17 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, ) else: if len(guesses) < n_states: - raise InputError("Less guesses provided via guesses (== {}) " - "than states to be computed (== {})" - "".format(len(guesses), n_states)) + raise InputError(f"Less guesses provided via guesses (=={len(guesses)}" + f") than states to be computed (=={n_states})") if n_guesses is not None: warnings.warn("Ignoring n_guesses parameter, since guesses are " - "explicitly provided.") + "explicitly provided.") if n_guesses_doubles is not None: warnings.warn("Ignoring n_guesses_doubles parameter, since guesses" - " are explicitly provided.") + " are explicitly provided.") # Set spin_change to None since we don't know if guesses are provided spin_change = None - diagres = diagonalise_adcmatrix( matrix, n_states, guesses, kind=kind, conv_tol=conv_tol, output=output, eigensolver=eigensolver, is_alpha=is_alpha, @@ -471,32 +469,30 @@ def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles = n_guesses - len(guesses) if n_guesses_doubles > 0: - if matrix.method.level < 2: + if matrix.method.level.to_int() < 2: raise InputError("n_guesses_doubles > 0 is only sensible if the " - "ADC method has a doubles block (i.e. it is *not*" - " ADC(0), ADC(1) or a variant thereof.") + "ADC method has a doubles block (i.e. it is *not*" + " ADC(0), ADC(1) or a variant thereof.") guesses += guesses_from_diagonal( matrix, n_guesses_doubles, matrix.axis_blocks[1], kind, is_alpha, spin_change, spin_block_symmetrisation) if len(guesses) < n_guesses: - raise InputError("Less guesses found than requested: {} found, " - "{} requested".format( - len(guesses), n_guesses)) + raise InputError(f"Less guesses found ({len(guesses)}) " + f"than requested {n_guesses}") return guesses -def construct_guesses( - matrix: AdcMatrix, - n_states: int, - kind: str, - spin_change: float, - n_guesses: int, - n_guesses_doubles: Optional[int] = None, - is_alpha: Optional[bool] = None, - eigensolver: Optional[str] = "davidson" - ) -> list[AmplitudeVector]: +def construct_guesses(matrix: AdcMatrix, + n_states: int, + kind: str, + spin_change: float, + n_guesses: int, + n_guesses_doubles: Optional[int] = None, + is_alpha: Optional[bool] = None, + eigensolver: Optional[str] = "davidson" + ) -> list[AmplitudeVector]: """ This function constructs appropriate guesses if not given. Returns a :class:`Guesses` object containing all crucial guess information. @@ -508,8 +504,7 @@ def construct_guesses( n_guesses_per_state = 2 else: n_guesses_per_state = 1 - n_guesses = estimate_n_guesses(matrix, n_states, - n_guesses_per_state) + n_guesses = estimate_n_guesses(matrix, n_states, n_guesses_per_state) return obtain_guesses_by_inspection( matrix, n_guesses, kind, n_guesses_doubles, is_alpha, spin_change From ce7de9dc58917a94f0679fc7f216d1f86a07f6bb Mon Sep 17 00:00:00 2001 From: fedy9 Date: Tue, 12 May 2026 16:57:01 +0200 Subject: [PATCH 11/26] code style + python3.9 compatibility --- adcc/adc_ip/matrix.py | 2 +- adcc/guess/__init__.py | 2 +- adcc/guess/util.py | 4 ++-- adcc/tests/generators/dump_adcc.py | 12 +++--------- adcc/tests/generators/generate_adcc_data.py | 8 ++++---- adcc/tests/generators/generate_adcman_data.py | 3 ++- adcc/tests/guess_test.py | 3 ++- adcc/tests/properties_test.py | 9 ++++++--- 8 files changed, 21 insertions(+), 22 deletions(-) diff --git a/adcc/adc_ip/matrix.py b/adcc/adc_ip/matrix.py index 9845fb7e8..81d02d978 100644 --- a/adcc/adc_ip/matrix.py +++ b/adcc/adc_ip/matrix.py @@ -131,7 +131,7 @@ def block_h_phh_1(hf, mp, intermediates): # M_{12} def apply(ampl): return AmplitudeVector(h=( - + 1 / sqrt(2) * einsum("jkib,jkb->i", hf.ooov, ampl.phh))) + + 1 / sqrt(2) * einsum("jkib,jkb->i", hf.ooov, ampl.phh))) return AdcBlock(apply, 0) diff --git a/adcc/guess/__init__.py b/adcc/guess/__init__.py index 0a835cd6c..2efae072f 100644 --- a/adcc/guess/__init__.py +++ b/adcc/guess/__init__.py @@ -139,4 +139,4 @@ def guesses_spin_flip(matrix, n_guesses, block="ph", **kwargs): matrix, n_guesses, block=block, spin_change=-1, spin_block_symmetrisation=get_spin_block_symmetrisation("spin_flip"), **kwargs - ) \ No newline at end of file + ) diff --git a/adcc/guess/util.py b/adcc/guess/util.py index a707c18e3..b3cd578f7 100644 --- a/adcc/guess/util.py +++ b/adcc/guess/util.py @@ -22,11 +22,11 @@ ## --------------------------------------------------------------------- from ..AdcMethod import AdcMethod -from typing import Optional +from typing import Optional, Union def determine_spin_change(method: AdcMethod, kind: str, - is_alpha: Optional[bool] = None) -> int | float: + is_alpha: Optional[bool] = None) -> Union[int, float]: if method.adc_type == "pp": if kind == "spin_flip": return -1 diff --git a/adcc/tests/generators/dump_adcc.py b/adcc/tests/generators/dump_adcc.py index 1d3745957..5d2f578f6 100644 --- a/adcc/tests/generators/dump_adcc.py +++ b/adcc/tests/generators/dump_adcc.py @@ -174,19 +174,13 @@ def dump_excited_states( tdm_bb_b.append(bb_b.to_ndarray()) kind_data[ f"state_to_state/from_{ifrom}/transition_dipole_moments" - ] = ( - state2state.transition_dipole_moment[:n_states - ifrom - 1] - ) + ] = (state2state.transition_dipole_moment[:n_states - ifrom - 1]) kind_data[ f"state_to_state/from_{ifrom}/state_to_excited_tdm_bb_a" - ] = ( - np.asarray(tdm_bb_a) - ) + ] = (np.asarray(tdm_bb_a)) kind_data[ f"state_to_state/from_{ifrom}/state_to_excited_tdm_bb_b" - ] = ( - np.asarray(tdm_bb_b) - ) + ] = (np.asarray(tdm_bb_b)) # ssq for unrestriced calculation if not states.reference_state.restricted \ and not states.method.is_core_valence_separated: diff --git a/adcc/tests/generators/generate_adcc_data.py b/adcc/tests/generators/generate_adcc_data.py index 6eb0e6b70..8961d37c3 100644 --- a/adcc/tests/generators/generate_adcc_data.py +++ b/adcc/tests/generators/generate_adcc_data.py @@ -321,8 +321,8 @@ def generate_hf_631g(): testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): n_states = {n_states: 3} generate_adc_all( - test_case, method=method, dump_nstates=2, - is_alpha=is_alpha, **n_states + test_case, method=method, dump_nstates=2, + is_alpha=is_alpha, **n_states ) for method in _methods["ea"]: @@ -331,8 +331,8 @@ def generate_hf_631g(): testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): n_states = {n_states: 3} generate_adc_all( - test_case, method=method, dump_nstates=2, - is_alpha=is_alpha, **n_states + test_case, method=method, dump_nstates=2, + is_alpha=is_alpha, **n_states ) diff --git a/adcc/tests/generators/generate_adcman_data.py b/adcc/tests/generators/generate_adcman_data.py index 0d87fa8ee..e69bee8e8 100644 --- a/adcc/tests/generators/generate_adcman_data.py +++ b/adcc/tests/generators/generate_adcman_data.py @@ -59,7 +59,8 @@ def generate_adc(test_case: testcases.TestCase, method: AdcMethod, case: str, return None # gs_density_order is only available for adc(3) and adc(4) # and it is not available for cvs-adc - if gs_density_order is not None and (method.level.to_int() < 3 or "cvs" in case): + if gs_density_order is not None \ + and (method.level.to_int() < 3 or "cvs" in case): return None print(f"Generating {method.name} (gs_density_order={gs_density_order}) " f"data for {case} {test_case.file_name}.") diff --git a/adcc/tests/guess_test.py b/adcc/tests/guess_test.py index d00077f5c..df0acb592 100644 --- a/adcc/tests/guess_test.py +++ b/adcc/tests/guess_test.py @@ -460,7 +460,8 @@ def assert_guess_values(self, matrix, block, guesses, spin_flip=False, diagonal = matrix.diagonal().get(block).to_ndarray() # Doubles guesses are constructed from the 0th order diagonal - if matrix.method.level.to_int() > 1 and not matrix.method.name.endswith("adc2"): + if matrix.method.level.to_int() > 1 \ + and not matrix.method.name.endswith("adc2"): if block == "pphh": diagonal = adcc.adc_pp.matrix.diagonal_pphh_pphh_0( matrix.reference_state diff --git a/adcc/tests/properties_test.py b/adcc/tests/properties_test.py index f5d90dd33..8a38501b0 100644 --- a/adcc/tests/properties_test.py +++ b/adcc/tests/properties_test.py @@ -62,7 +62,8 @@ class TestProperties: @pytest.mark.parametrize("system,case,kind", cases) def test_transition_dipole_moments(self, system: str, case: str, kind: str, method: str, generator: str): - if "cvs" in case and AdcMethod(method).level.to_int() == 0 and generator == "adcman": + if "cvs" in case and AdcMethod(method).level.to_int() == 0 \ + and generator == "adcman": pytest.skip("No CVS-ADC(0) adcman reference data available.") refdata = testdata_cache._load_data( @@ -95,7 +96,8 @@ def test_transition_dipole_moments(self, system: str, case: str, kind: str, @pytest.mark.parametrize("system,case,kind", cases) def test_oscillator_strengths(self, system: str, case: str, kind: str, method: str, generator: str): - if "cvs" in case and AdcMethod(method).level.to_int() == 0 and generator == "adcman": + if "cvs" in case and AdcMethod(method).level.to_int() == 0 \ + and generator == "adcman": pytest.skip("No CVS-ADC(0) adcman reference data available.") refdata = testdata_cache._load_data( @@ -126,7 +128,8 @@ def test_oscillator_strengths(self, system: str, case: str, kind: str, @pytest.mark.parametrize("system,case,kind", cases) def test_state_dipole_moments(self, system: str, case: str, kind: str, method: str, generator: str): - if "cvs" in case and AdcMethod(method).level.to_int() == 0 and generator == "adcman": + if "cvs" in case and AdcMethod(method).level.to_int() == 0 \ + and generator == "adcman": pytest.skip("No CVS-ADC(0) adcman reference data available.") refdata = testdata_cache._load_data( From 76aa6a1a30694be870bfd2d414a49a5733fe4f20 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Tue, 12 May 2026 17:39:11 +0200 Subject: [PATCH 12/26] testdata and small fix --- adcc/tests/data/SHA256SUMS | 84 +++++++++---------- adcc/tests/generators/generate_adcc_data.py | 2 +- adcc/tests/generators/generate_adcman_data.py | 2 +- 3 files changed, 44 insertions(+), 44 deletions(-) diff --git a/adcc/tests/data/SHA256SUMS b/adcc/tests/data/SHA256SUMS index a665812d9..294b48aa5 100644 --- a/adcc/tests/data/SHA256SUMS +++ b/adcc/tests/data/SHA256SUMS @@ -1,18 +1,18 @@ -d382636d873f26f9872a9165b398317ae0bf81179f4c77e6cbf227af29ee9635 ch2nh2_sto3g_hfdata.hdf5 -424db3133e90ba113ef2800e115054a382099202976bf58dc560da53389f7da5 ch2nh2_sto3g_hfimport.hdf5 +63f70983becdfcabde6b309e6d0f7029be38f6784233733392f85c1974d74b85 ch2nh2_sto3g_hfdata.hdf5 +f5f1f76c1dfe30636d2752c9bba6c9747637e96feea6870ee73995351d169a28 ch2nh2_sto3g_hfimport.hdf5 eb433c83b7bde2693ef2ae9a6adc16ddfe21ae89dbb86778ff8227be919d237c cn_ccpvdz_adcc_adc0.hdf5 9f3210e752534b0bbab375733e163416d3b7d80b69250293bcee9946f9c3a7e6 cn_ccpvdz_adcc_adc1.hdf5 dd3b0776c3a1dd46e3720c7f2a2db8dfe26aae30a6a9bf9f14da2e9345742870 cn_ccpvdz_adcc_adc2.hdf5 d28c1ffd6a8bc5574db71889b171c5c535c3558fcfd81bb77d26560ebd7bbce8 cn_ccpvdz_adcc_adc2x.hdf5 8502e612dcf7b18261df569c12924949c1cd348455048da00f8bef70a28f1d68 cn_ccpvdz_adcc_adc3.hdf5 -3fc2e2acb0eeb91d761824a09275086d8d0f33ff02a3a5f414d908c6dce4d78e cn_ccpvdz_adcc_ea-adc0.hdf5 -5b3421798228b41b1edfcd03747ad820ccbdabc97908baa326e0d8c58f6e0100 cn_ccpvdz_adcc_ea-adc2.hdf5 -4e90e4a31e71d1e371138b44179708f240efceb41f47b4bdfccb1a066789ef69 cn_ccpvdz_adcc_ea-adc2x.hdf5 -dd41e5ff7f96ac8386611e35f13fac9da1d0bf5e50d90287436db8a4afa86c9b cn_ccpvdz_adcc_ea-adc3.hdf5 -2256d5cdbd446b5a5aebc357078d66658cb029966feb1637d3272e0515aaa4b6 cn_ccpvdz_adcc_ip-adc0.hdf5 -9e283d8d15258b5155ea188be480344c8389946547cd8f3e45e6caff2065e7c4 cn_ccpvdz_adcc_ip-adc2.hdf5 -767333f9f0dc0366a567e2b4cdd27b54d7608fc2fde2e6be0a56cf7d056ab8eb cn_ccpvdz_adcc_ip-adc2x.hdf5 -8f0ec3a22dd30d174a5c067040c14862f0b845e099319ab3c5ed71c5d0618ea9 cn_ccpvdz_adcc_ip-adc3.hdf5 +f9e07576076f1bffd9c439aeec1abb49427eb99366e65d7a0d5ffb9f451c498b cn_ccpvdz_adcc_ea-adc0.hdf5 +d8e0843e97317c214b3f3ed8dd1209c2d097df1a202d843d336b1d995733d48b cn_ccpvdz_adcc_ea-adc2.hdf5 +485ff9a90c0ae4ddb56a68dfaa3f733ffa3fdb35ef2e5e443160a2555de3c0a5 cn_ccpvdz_adcc_ea-adc2x.hdf5 +39f71c4399a2d36acb9bf556bec2c403d296b26ff418ce0e8eaae35e596eec0e cn_ccpvdz_adcc_ea-adc3.hdf5 +843ef518e0cf3e97c88dd5e1658442d4c0c77c0773aa10b66495d1d5986efe5f cn_ccpvdz_adcc_ip-adc0.hdf5 +52759d5ce71664e9bb5cb33599f3c069a5fb685e7c14df6b82df897f267b559a cn_ccpvdz_adcc_ip-adc2.hdf5 +57efe521e669d773a1a3676f2c3438e49874a59f7c5036346ba5bf331dc76237 cn_ccpvdz_adcc_ip-adc2x.hdf5 +43eac99f5bdc36f8da695dca9d49f45b5c85caf39f925754ac2a2b82e513a765 cn_ccpvdz_adcc_ip-adc3.hdf5 4635cfbf161652c9f83379b119b2e1b7e520dc2a22be5693015820c865efe705 cn_ccpvdz_adcc_mpdata.hdf5 8f8c3c86cb9f4b22f8bcfff10e7092fcec4258fbf2a451e3cd6456b1adb7ba85 cn_ccpvdz_adcman_adc0.hdf5 be3f5e6090ba4d6fef72760e982bc268dc49f870db1afc101af60e08be559423 cn_ccpvdz_adcman_adc1.hdf5 @@ -26,21 +26,21 @@ af48346ca95af1a4795d56a43dedd9c76404f52f0babd78d4947c75700dcb509 cn_ccpvdz_adcm 4979368df74815035adf6e4b84ef5da3068a4d7fbbe6e014d3902d28d4e4ef80 cn_ccpvdz_adcman_ip-adc2.hdf5 a8743b12ccf3ba67716008972be04d58298c28f4b4624d4377861e0a2bc13ff5 cn_ccpvdz_adcman_ip-adc3.hdf5 0a2d6ebae22d84b9a4513e52c85f0f0294b6162bc25662c743d154bc81fa4128 cn_ccpvdz_adcman_mpdata.hdf5 -37b3af4a4f1b70c7bc7588f5bfcd542fc96056ccb7fe2e8c9fbda93b546788a0 cn_ccpvdz_hfdata.hdf5 -a15001b874327b1cec2c84bfe4a24695c71633f31aaf618dc25744dda2c1022b cn_ccpvdz_hfimport.hdf5 +db27db84b2dea991a4e24b6ce7433f2619769505267bd486723b1b9bd017a5f3 cn_ccpvdz_hfdata.hdf5 +8c3f128cd46df98eaaba3658b849150459f738e9b3cc1cc42f2b5ff6ad288c9f cn_ccpvdz_hfimport.hdf5 94665e00317ec7a20540f934dd0306403acda144a581f7c5a1abab14cc599354 cn_sto3g_adcc_adc0.hdf5 54020255ce72d4700758c6790207c62c61a25091380d8bd8b6cf4b21c6dfcfc9 cn_sto3g_adcc_adc1.hdf5 08f1ff54122f543cce6fb23d8e594ea58e342693ac94956f35d79781f790b6b9 cn_sto3g_adcc_adc2.hdf5 fe4da3d8dad6fb394329d6d409860641e99465b8d53f0efc880129238dfb4efd cn_sto3g_adcc_adc2x.hdf5 2a041d39ac6d1bbc1bc0365ba4e191a44f2e059c9c706891746500bb09dd2eb6 cn_sto3g_adcc_adc3.hdf5 -0c761fd68c9430a24f6e44df3acb458030d8df12d32b4e7540e0171a61718cf7 cn_sto3g_adcc_ea-adc0.hdf5 -6f1e4b0da533444c876ea8246332d7447d89b7d43fa5156f7c72ee9231c0adfe cn_sto3g_adcc_ea-adc2.hdf5 -90d62339fd7a39f89dbb8fe5869d7cee826ed663f8384f6da9fa100dd4aba469 cn_sto3g_adcc_ea-adc2x.hdf5 -f585ac33e3757e16306bdeb5f8e0c6a26a94799ff974a87464dc2da79f3357fe cn_sto3g_adcc_ea-adc3.hdf5 -c8daf37f81564682e93a1b6edee13fc51f55604c423434e7b9c12850344120d9 cn_sto3g_adcc_ip-adc0.hdf5 -d1c54c83b79509dd54dedac1dfc6f5530e6de782ac8d1cfb892a616f73180b14 cn_sto3g_adcc_ip-adc2.hdf5 -1f02e1c648759dc2562ad8a4e092c927617a7124e884e56539a4ecc7a2288722 cn_sto3g_adcc_ip-adc2x.hdf5 -384d99c0c585e59210f5d323a8ae8bcfcc9a163cb3ad93f5332bded421b057e2 cn_sto3g_adcc_ip-adc3.hdf5 +ffeae310f386671ef10ba52d637576494bdc6ff2ff7e0f027027064405a044fa cn_sto3g_adcc_ea-adc0.hdf5 +f3a17cc0733110ab0bfc7b683cfa042acdf63789cee50447d83fb7d4d677803e cn_sto3g_adcc_ea-adc2.hdf5 +278528ece296da0b4fef7d08a5047db3487bb4210bbb755ebb2304b0a9a7a98f cn_sto3g_adcc_ea-adc2x.hdf5 +65e90b3bddfa7e9b70beb6f4b14f5ce928eba5c97b66faf6926fcaea8cad4c0b cn_sto3g_adcc_ea-adc3.hdf5 +c850181d00e405c6c97cd1d1340b13a19909482d61c63981c8ceb5c0301dbfcd cn_sto3g_adcc_ip-adc0.hdf5 +c14114493b7c977102fa553480e6c10230ab2d0d37a3769a65e5ce15c4f66c97 cn_sto3g_adcc_ip-adc2.hdf5 +0a152dc0b1661d42dc05257847bcbd91702ae9de70bbf6295d086c50cf531f66 cn_sto3g_adcc_ip-adc2x.hdf5 +df0f40da670c7dbed7883e3a7af67b93773101de8f77e170abbdeeec18160579 cn_sto3g_adcc_ip-adc3.hdf5 582cc08bb5b63599f67511b4a262c3c7d88d80d3bb99cb306b06865e94116451 cn_sto3g_adcc_mpdata.hdf5 f6974049ccf8a732ea584dbbebf608f0e17d28bd5320c0c63e1d1a5db80f54a7 cn_sto3g_adcman_adc0.hdf5 9950c0720546a0b313e755945e7fd0d5462967a4366f60470c4a85f500bffb5f cn_sto3g_adcman_adc1.hdf5 @@ -54,8 +54,8 @@ b36e8397ee7af7eb627be2ab355a6b20e90b3a417f42b60ead0b5e8bde16c9d8 cn_sto3g_adcma cfc731c423999b1b1f79929e180f7d3cc76199079b4fc69dca1b2e0ccf9672ba cn_sto3g_adcman_ip-adc2.hdf5 99b1ba20f1bc57be2acc083edd5ce31dcd1d2eedb6cb8e286862821bc4adb629 cn_sto3g_adcman_ip-adc3.hdf5 e9a94d14102eba9e6b6aaf0728615e5bfecacde501743032813f5b0df283bd7e cn_sto3g_adcman_mpdata.hdf5 -31cea3a0bdfe517d1be6615c98849578704377f2ad6134ce127531798e43ad95 cn_sto3g_hfdata.hdf5 -a305861c174acddcdffaa886775ee79c8ed5f41aa19493bbe7f053a7e6996a5f cn_sto3g_hfimport.hdf5 +7d843dd9981ef78f11a78051aea1ea61af8f12dc244b93e3f76fd70602c8f86e cn_sto3g_hfdata.hdf5 +73adda09433dab2263e3dc2b37e10d5127e4c54a4296fdf2574ab71ec44d172a cn_sto3g_hfimport.hdf5 8c7d88af083003a190a5561d31d1c9c231c0cb1e149a00f8b6cd92d020871332 formaldehyde_ccpvdz_adcman_adc0.hdf5 c55ccf23bd94fcd2e3660cc73915e4994c59143eaf92ea96a99df295bfd90be6 formaldehyde_ccpvdz_adcman_adc1.hdf5 3271fcb5f13f512cba10a807c13258e95db25d5854e4008e91ac0b0e0481ba77 formaldehyde_ccpvdz_adcman_adc2.hdf5 @@ -71,14 +71,14 @@ b579b2e62d4d02c0e5d16ab8b8e30009582152dba63ac12cea77f7dbbd80c29d formaldehyde_s 28e018d79c5481938f77177fbeb6f65e6c04122ef7f8fc3db478a5d31592da6c h2o_def2tzvp_adcc_adc2.hdf5 4a35c56f21d7c0ae424225bc756ac9b8a9f9b2f32b90e5aea45351b4249196f2 h2o_def2tzvp_adcc_adc2x.hdf5 6157f5d41dec84612de81005b17cdcc531efc74d5da42e1a49d2cb53492f3248 h2o_def2tzvp_adcc_adc3.hdf5 -1810c6f136e8ae097609e4e9f43d2d7fbd3c419bfdd9e9d401a9ce16768a4f26 h2o_def2tzvp_adcc_ea-adc0.hdf5 -d86c4f3b686e3afcfad1019abbf7edffbab889f03e0e685265cfcaa5df09a88d h2o_def2tzvp_adcc_ea-adc2.hdf5 -3805e99fddbf4f28f441c7cbd75bdced72eaab045e04a2ca867d81ea24416aae h2o_def2tzvp_adcc_ea-adc2x.hdf5 -3f253a182662f2be98fe82aac930929cd128cf1e42b21aab8846f87496865ffd h2o_def2tzvp_adcc_ea-adc3.hdf5 -cb8dc08120716cf28ccf9b04d5c69a16f125317fae79a7ee5a3317220f79ef97 h2o_def2tzvp_adcc_ip-adc0.hdf5 -a7f58daa5fd1d8f4c3e54b5bd8bc957dd309b3df386744dab9271aa3982e600a h2o_def2tzvp_adcc_ip-adc2.hdf5 -fb26959b3beb45ac6222c542263fb74dbc448ac0f842073edae2bf467a51a141 h2o_def2tzvp_adcc_ip-adc2x.hdf5 -90fdfbbb3f2ca4e69bd6329c111a30cea3484f467b3c010722fb6c717cdce1ff h2o_def2tzvp_adcc_ip-adc3.hdf5 +528c73962158a58994c1cfd46ef92f05a34b66ca511cde4842290c0e6da8f2ff h2o_def2tzvp_adcc_ea-adc0.hdf5 +fd1d0720f62ff186d5227beb215d25eca9734ac5c132049c718ce38797f7cc18 h2o_def2tzvp_adcc_ea-adc2.hdf5 +74bd9ecc78eef2d6201c98d7cedef1cc0c6adfef61be00e08851def5ec83cebf h2o_def2tzvp_adcc_ea-adc2x.hdf5 +fa33a3ad777cd745b579adeae4dac7b04d5b56764ed2c44a1c7b37eabc2713e2 h2o_def2tzvp_adcc_ea-adc3.hdf5 +4a0776fb0ac75c0203bc41e9c25e3be5d80e1e5323071fde500fedb84711bd4c h2o_def2tzvp_adcc_ip-adc0.hdf5 +0381025325cdcb7b6f63ad6acb5901f5609316c1a85baf8475a7aba94457c337 h2o_def2tzvp_adcc_ip-adc2.hdf5 +0eee0844cd64e9068e177af180bce92147c8296d7f7d42a41a92b03588f98437 h2o_def2tzvp_adcc_ip-adc2x.hdf5 +18659119cb22c676ea5d9245c0bb4d9c5cf1a44f4fe795d995684987ac138a02 h2o_def2tzvp_adcc_ip-adc3.hdf5 bb7850aad29b723e8b2317cdb2fa45229eab6ff5772c7c2eab681ec527c3bf93 h2o_def2tzvp_adcc_mpdata.hdf5 2a88059266ac32a34cc352e1d2f782565317cec8573bdd81c4e4019ecc9adb65 h2o_def2tzvp_adcman_adc0.hdf5 dcf8ae501f7875705191abf754b33cf82f14065db2c95ab84ef3004b70ee9e75 h2o_def2tzvp_adcman_adc1.hdf5 @@ -92,21 +92,21 @@ d06b4f8c48e072009cf4f830cf882f9b4b887fe5eeb02bc7d72b69cfc4c3795b h2o_def2tzvp_a 1f29b185958fddd686782425030b3014ddf4e363e8e73a146d9e3f7a20b114d5 h2o_def2tzvp_adcman_ip-adc2.hdf5 8fc2922530d65cb23d821e662f9dedee62d91ad2508bf246ed3cbba640dbf75c h2o_def2tzvp_adcman_ip-adc3.hdf5 a07821177dd07f8f95a64aa008b7317e68357e45761742727394823fa242afb5 h2o_def2tzvp_adcman_mpdata.hdf5 -60bd2f9e165ba7dde03fc7ddb36279fd58fc48a70fd196b131d56cff46f4da55 h2o_def2tzvp_hfdata.hdf5 -d1ae1621fa8ccaaba07ec6127b331c5e9e356708d16fd77a965a351d504ca74d h2o_def2tzvp_hfimport.hdf5 +36b3b93aa1071e61611d96b6b59ed13d37ec718303b8ef60a6c10aef07276717 h2o_def2tzvp_hfdata.hdf5 +8a447ed3025662a2a71d866c697d345dbace45655e14c8777c10190f7f4c5967 h2o_def2tzvp_hfimport.hdf5 b56e74c8e71a9ab5ef62d44131c721e8d5ce236f9ed69d82869d702468aff7a8 h2o_sto3g_adcc_adc0.hdf5 3b2c9a095201c17bbe565de1b499b719b6d2ecde6ebb64d6b8e9f8ff2220c05f h2o_sto3g_adcc_adc1.hdf5 18c78aae03b0315cb3fcd932ce1ee1f3040db355ed38a61b46e2142d8e28b8ef h2o_sto3g_adcc_adc2.hdf5 8e17428b0d7e93e91e3ba366f7d2a9aded91a5d8d37b47fcc790c20b5b150872 h2o_sto3g_adcc_adc2x.hdf5 de9a094a6da75d063a7d1a4137347409c7dd2ced6ae1bdd2ebece6b2712506d6 h2o_sto3g_adcc_adc3.hdf5 -a4a1ca45633ea71d3e3a760090bfdb229c0ad5f4ad5120b6d93e16a34a183834 h2o_sto3g_adcc_ea-adc0.hdf5 -b2c91bc8cae5a99c51296684f7f418d21c7a47b668e19541f3c80383ab0fef8a h2o_sto3g_adcc_ea-adc2.hdf5 -97beb080141eca34d564eb58162a46d4a2b0d7932ffd217f0fe472b28995e113 h2o_sto3g_adcc_ea-adc2x.hdf5 -9d5d0f5d2ce1363176002a299c94262d8ef02288feb5030f78fedadfd67c0280 h2o_sto3g_adcc_ea-adc3.hdf5 -f51fdacc11e39356304682ee43e889d51affc7f56eae5650bc571625385d9c7c h2o_sto3g_adcc_ip-adc0.hdf5 -017667fb59059c4bcef47f12dea8f996789a469d35e78d04a641635d2196fdb1 h2o_sto3g_adcc_ip-adc2.hdf5 -8df1ef913ee40f87cd86d301aa4125f952b2660e7fe12dda7a10f7ea90fd6ed6 h2o_sto3g_adcc_ip-adc2x.hdf5 -c952684e6d8ac66038d53cb44e3c5fe9c5a3621733c7dc8ca391d40c3a50cd3c h2o_sto3g_adcc_ip-adc3.hdf5 +83b8899a0b7851e18b3e246b802a7e8679d91881c3838797ee6fdb596e54f977 h2o_sto3g_adcc_ea-adc0.hdf5 +c166cd64e6a4a8eac9f761c133d685646124b291887407b89a31b02e197eafe7 h2o_sto3g_adcc_ea-adc2.hdf5 +7cea6e0a05023696d0c816b6a02056e4ab2f09cfef167ead5db516be79b5f4d0 h2o_sto3g_adcc_ea-adc2x.hdf5 +d27ba85d21f84bd90200f099cc4ab56de2da09dbcd1a3290daf62c10768051e1 h2o_sto3g_adcc_ea-adc3.hdf5 +d2f58d41610467bcda51778d979d9b0d39bcd11146ce7ec6780f7e04134c95ef h2o_sto3g_adcc_ip-adc0.hdf5 +9e7d019e831be4c71a9acd50b27794c31ced89b3877d859df43a5e033a2d9fa4 h2o_sto3g_adcc_ip-adc2.hdf5 +76e1892fb59dc93d0346f7f8cd85c1f7c27784115a920c2f746717f0807a575b h2o_sto3g_adcc_ip-adc2x.hdf5 +a40e06f209a0720a5068df06cfc0a05654a5554c54d41c7164ec949ea12dc4b4 h2o_sto3g_adcc_ip-adc3.hdf5 32d3b2aa4b40ae58b4c1b71b102bdd72e21d42b9f4050fba734a2d18eb6ffa02 h2o_sto3g_adcc_mpdata.hdf5 cc957d99adaf872877b992860c9f0c9c8337f783b9c3ad20b36694391627188f h2o_sto3g_adcman_adc0.hdf5 4b36ac6cce98cf250ad268dc6e51e849acbd8c9383aa16aff233a2cf04118fcd h2o_sto3g_adcman_adc1.hdf5 @@ -120,8 +120,8 @@ c936ae6eb312921adcf1a3fc8bac19453eebbe29f8de4a574ac981b2ef55fffa h2o_sto3g_adcm abeaeacf2248d3c779a605eaf5d8313029593e22f8656c1baa5bcb0a983e4fcb h2o_sto3g_adcman_ip-adc2.hdf5 dd39481ff664de5c228919687ea3fbcf1effaa0aaddff4750d008cb25066b956 h2o_sto3g_adcman_ip-adc3.hdf5 00abfa6fcd237571167e42e3dfd956550944a5f6d68f462e6017629c175f4730 h2o_sto3g_adcman_mpdata.hdf5 -e0e17c4cc9618edbba2be2ad77c41c4b212deb9e88b458a2666a2a803a0cdba5 h2o_sto3g_hfdata.hdf5 -9bf19ad2a3e3f8d70188c7bdba0b39b044a760ddc668ae4a419ef2fb41337d9e h2o_sto3g_hfimport.hdf5 +6c64153a6e708edad4c8a07e0ed6e5e1379e8e9c721e49f2aa0cc4ecb9817082 h2o_sto3g_hfdata.hdf5 +74733b03dd12561479132a768c2b330b4b8dbf1fe60fe0b14caa4d3fd4da7fce h2o_sto3g_hfimport.hdf5 057ef124dc4b3fdfe4958f74009500a6507c8f2bb2a24f0a9cb4bc8119334c10 h2s_6311g_hfdata.hdf5 4354d9a8f676370454deb43116775f111d9e1a32508ef7e0adaa0cca67c1a87d h2s_sto3g_hfdata.hdf5 93bf9f69b8ab2c524b499bc60790f245401ab0d65cc342e92479c68051b0f34d hf3_631g_hfdata.hdf5 diff --git a/adcc/tests/generators/generate_adcc_data.py b/adcc/tests/generators/generate_adcc_data.py index 8961d37c3..951fccaa6 100644 --- a/adcc/tests/generators/generate_adcc_data.py +++ b/adcc/tests/generators/generate_adcc_data.py @@ -145,7 +145,7 @@ def generate_h2o_sto3g(): for n_states in \ testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): per_case = None - if method.to_int() < 2: # adc0/adc1 + if method.level.to_int() < 2: # adc0/adc1 per_case = { case: {n_states: n} for case, n in states_per_case.items() } diff --git a/adcc/tests/generators/generate_adcman_data.py b/adcc/tests/generators/generate_adcman_data.py index e69bee8e8..0ebfdab4b 100644 --- a/adcc/tests/generators/generate_adcman_data.py +++ b/adcc/tests/generators/generate_adcman_data.py @@ -196,7 +196,7 @@ def generate_h2o_sto3g(): for method in _methods["ea"]: method = AdcMethod(method) - if method.to_int() < 2: + if method.level.to_int() < 2: n_states = {"n_ea_states": (1, 0)} else: n_states = {"n_ea_states": (3, 0)} From 40c0d342463f2b37428268b164c9c4bfde793c29 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Wed, 13 May 2026 14:24:49 +0200 Subject: [PATCH 13/26] updated testdata list and c++ code style --- adcc/tests/data/update_testdata.sh | 80 ++++++ .../amplitude_vector_enforce_spin_kind.cc | 15 +- .../amplitude_vector_enforce_spin_kind.hh | 6 +- libadcc_src/fill_ea_doubles_guesses.cc | 17 +- libadcc_src/fill_ea_doubles_guesses.hh | 17 +- libadcc_src/fill_ip_doubles_guesses.cc | 17 +- libadcc_src/fill_ip_doubles_guesses.hh | 20 +- libadcc_src/guess/ea_adc_guess_d.cc | 239 +++++++++--------- libadcc_src/guess/ea_adc_guess_d.hh | 10 +- libadcc_src/guess/index_group_h2p.cc | 5 +- libadcc_src/guess/index_group_h2p.hh | 4 +- libadcc_src/guess/index_group_p2h.cc | 7 +- libadcc_src/guess/index_group_p2h.hh | 4 +- libadcc_src/guess/ip_adc_guess_d.cc | 230 +++++++++-------- libadcc_src/guess/ip_adc_guess_d.hh | 10 +- libadcc_src/pyiface/export_adc_ea.cc | 6 +- libadcc_src/pyiface/export_adc_ip.cc | 6 +- 17 files changed, 377 insertions(+), 316 deletions(-) diff --git a/adcc/tests/data/update_testdata.sh b/adcc/tests/data/update_testdata.sh index 616febaab..4a52be823 100755 --- a/adcc/tests/data/update_testdata.sh +++ b/adcc/tests/data/update_testdata.sh @@ -16,12 +16,28 @@ DATAFILES=( cn_sto3g_adcc_adc2.hdf5 cn_sto3g_adcc_adc2x.hdf5 cn_sto3g_adcc_adc3.hdf5 + cn_sto3g_adcc_ip-adc0.hdf5 + cn_sto3g_adcc_ip-adc2.hdf5 + cn_sto3g_adcc_ip-adc2x.hdf5 + cn_sto3g_adcc_ip-adc3.hdf5 + cn_sto3g_adcc_ea-adc0.hdf5 + cn_sto3g_adcc_ea-adc2.hdf5 + cn_sto3g_adcc_ea-adc2x.hdf5 + cn_sto3g_adcc_ea-adc3.hdf5 cn_sto3g_adcc_mpdata.hdf5 cn_sto3g_adcman_adc0.hdf5 cn_sto3g_adcman_adc1.hdf5 cn_sto3g_adcman_adc2.hdf5 cn_sto3g_adcman_adc2x.hdf5 cn_sto3g_adcman_adc3.hdf5 + cn_sto3g_adcman_ip-adc0.hdf5 + cn_sto3g_adcman_ip-adc2.hdf5 + cn_sto3g_adcman_ip-adc2x.hdf5 + cn_sto3g_adcman_ip-adc3.hdf5 + cn_sto3g_adcman_ea-adc0.hdf5 + cn_sto3g_adcman_ea-adc2.hdf5 + cn_sto3g_adcman_ea-adc2x.hdf5 + cn_sto3g_adcman_ea-adc3.hdf5 cn_sto3g_adcman_mpdata.hdf5 cn_sto3g_hfdata.hdf5 cn_sto3g_hfimport.hdf5 @@ -35,12 +51,28 @@ DATAFILES=( h2o_sto3g_adcc_adc2.hdf5 h2o_sto3g_adcc_adc2x.hdf5 h2o_sto3g_adcc_adc3.hdf5 + h2o_sto3g_adcc_ip-adc0.hdf5 + h2o_sto3g_adcc_ip-adc2.hdf5 + h2o_sto3g_adcc_ip-adc2x.hdf5 + h2o_sto3g_adcc_ip-adc3.hdf5 + h2o_sto3g_adcc_ea-adc0.hdf5 + h2o_sto3g_adcc_ea-adc2.hdf5 + h2o_sto3g_adcc_ea-adc2x.hdf5 + h2o_sto3g_adcc_ea-adc3.hdf5 h2o_sto3g_adcc_mpdata.hdf5 h2o_sto3g_adcman_adc0.hdf5 h2o_sto3g_adcman_adc1.hdf5 h2o_sto3g_adcman_adc2.hdf5 h2o_sto3g_adcman_adc2x.hdf5 h2o_sto3g_adcman_adc3.hdf5 + h2o_sto3g_adcman_ip-adc0.hdf5 + h2o_sto3g_adcman_ip-adc2.hdf5 + h2o_sto3g_adcman_ip-adc2x.hdf5 + h2o_sto3g_adcman_ip-adc3.hdf5 + h2o_sto3g_adcman_ea-adc0.hdf5 + h2o_sto3g_adcman_ea-adc2.hdf5 + h2o_sto3g_adcman_ea-adc2x.hdf5 + h2o_sto3g_adcman_ea-adc3.hdf5 h2o_sto3g_adcman_mpdata.hdf5 h2o_sto3g_hfdata.hdf5 h2o_sto3g_hfimport.hdf5 @@ -49,12 +81,28 @@ DATAFILES=( hf_631g_adcc_adc2.hdf5 hf_631g_adcc_adc2x.hdf5 hf_631g_adcc_adc3.hdf5 + hf_631g_adcc_ip-adc0.hdf5 + hf_631g_adcc_ip-adc2.hdf5 + hf_631g_adcc_ip-adc2x.hdf5 + hf_631g_adcc_ip-adc3.hdf5 + hf_631g_adcc_ea-adc0.hdf5 + hf_631g_adcc_ea-adc2.hdf5 + hf_631g_adcc_ea-adc2x.hdf5 + hf_631g_adcc_ea-adc3.hdf5 hf_631g_adcc_mpdata.hdf5 hf_631g_adcman_adc0.hdf5 hf_631g_adcman_adc1.hdf5 hf_631g_adcman_adc2.hdf5 hf_631g_adcman_adc2x.hdf5 hf_631g_adcman_adc3.hdf5 + hf_631g_adcman_ip-adc0.hdf5 + hf_631g_adcman_ip-adc2.hdf5 + hf_631g_adcman_ip-adc2x.hdf5 + hf_631g_adcman_ip-adc3.hdf5 + hf_631g_adcman_ea-adc0.hdf5 + hf_631g_adcman_ea-adc2.hdf5 + hf_631g_adcman_ea-adc2x.hdf5 + hf_631g_adcman_ea-adc3.hdf5 hf_631g_adcman_mpdata.hdf5 hf_631g_hfdata.hdf5 r2methyloxirane_sto3g_hfdata.hdf5 @@ -65,12 +113,28 @@ DATAFILES_FULL=( cn_ccpvdz_adcc_adc2.hdf5 cn_ccpvdz_adcc_adc2x.hdf5 cn_ccpvdz_adcc_adc3.hdf5 + cn_ccpvdz_adcc_ip-adc0.hdf5 + cn_ccpvdz_adcc_ip-adc2.hdf5 + cn_ccpvdz_adcc_ip-adc2x.hdf5 + cn_ccpvdz_adcc_ip-adc3.hdf5 + cn_ccpvdz_adcc_ea-adc0.hdf5 + cn_ccpvdz_adcc_ea-adc2.hdf5 + cn_ccpvdz_adcc_ea-adc2x.hdf5 + cn_ccpvdz_adcc_ea-adc3.hdf5 cn_ccpvdz_adcc_mpdata.hdf5 cn_ccpvdz_adcman_adc0.hdf5 cn_ccpvdz_adcman_adc1.hdf5 cn_ccpvdz_adcman_adc2.hdf5 cn_ccpvdz_adcman_adc2x.hdf5 cn_ccpvdz_adcman_adc3.hdf5 + cn_ccpvdz_adcman_ip-adc0.hdf5 + cn_ccpvdz_adcman_ip-adc2.hdf5 + cn_ccpvdz_adcman_ip-adc2x.hdf5 + cn_ccpvdz_adcman_ip-adc3.hdf5 + cn_ccpvdz_adcman_ea-adc0.hdf5 + cn_ccpvdz_adcman_ea-adc2.hdf5 + cn_ccpvdz_adcman_ea-adc2x.hdf5 + cn_ccpvdz_adcman_ea-adc3.hdf5 cn_ccpvdz_adcman_mpdata.hdf5 cn_ccpvdz_hfdata.hdf5 cn_ccpvdz_hfimport.hdf5 @@ -84,12 +148,28 @@ DATAFILES_FULL=( h2o_def2tzvp_adcc_adc2.hdf5 h2o_def2tzvp_adcc_adc2x.hdf5 h2o_def2tzvp_adcc_adc3.hdf5 + h2o_def2tzvp_adcc_ip-adc0.hdf5 + h2o_def2tzvp_adcc_ip-adc2.hdf5 + h2o_def2tzvp_adcc_ip-adc2x.hdf5 + h2o_def2tzvp_adcc_ip-adc3.hdf5 + h2o_def2tzvp_adcc_ea-adc0.hdf5 + h2o_def2tzvp_adcc_ea-adc2.hdf5 + h2o_def2tzvp_adcc_ea-adc2x.hdf5 + h2o_def2tzvp_adcc_ea-adc3.hdf5 h2o_def2tzvp_adcc_mpdata.hdf5 h2o_def2tzvp_adcman_adc0.hdf5 h2o_def2tzvp_adcman_adc1.hdf5 h2o_def2tzvp_adcman_adc2.hdf5 h2o_def2tzvp_adcman_adc2x.hdf5 h2o_def2tzvp_adcman_adc3.hdf5 + h2o_def2tzvp_adcman_ip-adc0.hdf5 + h2o_def2tzvp_adcman_ip-adc2.hdf5 + h2o_def2tzvp_adcman_ip-adc2x.hdf5 + h2o_def2tzvp_adcman_ip-adc3.hdf5 + h2o_def2tzvp_adcman_ea-adc0.hdf5 + h2o_def2tzvp_adcman_ea-adc2.hdf5 + h2o_def2tzvp_adcman_ea-adc2x.hdf5 + h2o_def2tzvp_adcman_ea-adc3.hdf5 h2o_def2tzvp_adcman_mpdata.hdf5 h2o_def2tzvp_hfdata.hdf5 h2o_def2tzvp_hfimport.hdf5 diff --git a/libadcc_src/amplitude_vector_enforce_spin_kind.cc b/libadcc_src/amplitude_vector_enforce_spin_kind.cc index fb4d6f04a..ac3a3acba 100644 --- a/libadcc_src/amplitude_vector_enforce_spin_kind.cc +++ b/libadcc_src/amplitude_vector_enforce_spin_kind.cc @@ -31,8 +31,8 @@ namespace libadcc { namespace lt = libtensor; void amplitude_vector_enforce_spin_kind(std::shared_ptr doubles_tensor, - std::string block, - std::string spin_kind, bool is_ip) { + std::string block, std::string spin_kind, + bool is_ip) { // Nothing to do for singles block if (block == "s") return; @@ -96,7 +96,8 @@ void amplitude_vector_enforce_spin_kind(std::shared_ptr doubles_tensor, // Construct the orbit corresponding to this index (i.e. the iterator // running over all elements equivalent by symmetry // Ignore spin-forbidden, i.e. zero orbits - lt::short_orbit<3, scalar_type> orbi(sym, ii, /* compute_if_allowed_orbit = */ true); + lt::short_orbit<3, scalar_type> orbi(sym, ii, + /* compute_if_allowed_orbit = */ true); if (!orbi.is_allowed()) continue; // get_acindex -> get absolute canonical index @@ -115,7 +116,7 @@ void amplitude_vector_enforce_spin_kind(std::shared_ptr doubles_tensor, lt::index<3> i1(ci); lt::index<3> i2(ci); - if (is_ip) { // IP-ADC calculation + if (is_ip) { // IP-ADC calculation // set i1 to (alpha, beta, beta) equivalent of the canonical index // pinned by ai and orbi i1[1] += bidims_alpha[1]; @@ -125,7 +126,7 @@ void amplitude_vector_enforce_spin_kind(std::shared_ptr doubles_tensor, i2[0] += bidims_alpha[0]; i2[2] += bidims_alpha[2]; - } else { // EA-ADC calculation + } else { // EA-ADC calculation // set i1 to (beta, alpha, beta) equivalent of the canonical index // pinned by ai and orbi i1[0] += bidims_alpha[0]; @@ -155,7 +156,7 @@ void amplitude_vector_enforce_spin_kind(std::shared_ptr doubles_tensor, // Set (alpha, alpha, alpha) to zero // This effectively filters out the quartet components with zero blocks // in (alpha, beta, beta) and (beta, alpha, beta) (IP-ADC) erroneously - // introduced due to numerical errors. (beta, alpha, beta) and + // introduced due to numerical errors. (beta, alpha, beta) and // (beta, beta, alpha) blocks for EA-ADC. ctrl.req_zero_block(ci); continue; @@ -174,7 +175,7 @@ void amplitude_vector_enforce_spin_kind(std::shared_ptr doubles_tensor, // IP: (beta, alpha, beta) / EA: (beta, beta, alpha) is not zero lt::dense_tensor_rd_i<3, scalar_type>& blk2 = ctrl.req_const_block(ci2); - // Assign if (alpha, beta, beta) for IP- and (beta, alpha, beta) for + // Assign if (alpha, beta, beta) for IP- and (beta, alpha, beta) for // EA-ADC is zero, else += lt::tod_copy<3>(blk2, orb2.get_transf(i2)).perform(zero1, blk); ctrl.ret_const_block(ci2); diff --git a/libadcc_src/amplitude_vector_enforce_spin_kind.hh b/libadcc_src/amplitude_vector_enforce_spin_kind.hh index 7e6eaddfb..0ac1a04e8 100644 --- a/libadcc_src/amplitude_vector_enforce_spin_kind.hh +++ b/libadcc_src/amplitude_vector_enforce_spin_kind.hh @@ -36,9 +36,7 @@ namespace libadcc { * @param block The block of an amplitude this tensor represents * @param spin_kind The kind of spin to enforce */ -void amplitude_vector_enforce_spin_kind(std::shared_ptr tensor, - std::string block, - std::string spin_kind, - bool is_ip); +void amplitude_vector_enforce_spin_kind(std::shared_ptr tensor, std::string block, + std::string spin_kind, bool is_ip); ///@} } // namespace libadcc diff --git a/libadcc_src/fill_ea_doubles_guesses.cc b/libadcc_src/fill_ea_doubles_guesses.cc index b3d5c2f32..9b820ac4b 100644 --- a/libadcc_src/fill_ea_doubles_guesses.cc +++ b/libadcc_src/fill_ea_doubles_guesses.cc @@ -25,11 +25,9 @@ namespace libadcc { size_t fill_ea_doubles_guesses(std::vector> guesses_d, std::shared_ptr mospaces, - std::shared_ptr d_o, - std::shared_ptr d_v, + std::shared_ptr d_o, std::shared_ptr d_v, bool a_spin, bool restricted, bool doublet, - int spin_change_twice, - scalar_type degeneracy_tolerance) { + int spin_change_twice, scalar_type degeneracy_tolerance) { size_t n_guesses = guesses_d.size(); if (n_guesses == 0) return 0; @@ -66,13 +64,12 @@ size_t fill_ea_doubles_guesses(std::vector> guesses_d, } if (abs(spin_change_twice) != 1){ - throw not_implemented_error("spin_change ==" + - std::to_string(spin_change_twice) + " has not been tested."); + throw not_implemented_error("spin_change ==" + std::to_string(spin_change_twice) + + " has not been tested."); } - return ea_adc_guess_d(guesspairs, asbt1(d_o), asbt1(d_v), sym_s, a_spin, - restricted, doublet, ab_d, spin_change_twice, - degeneracy_tolerance); + return ea_adc_guess_d(guesspairs, asbt1(d_o), asbt1(d_v), sym_s, a_spin, restricted, + doublet, ab_d, spin_change_twice, degeneracy_tolerance); } -} // namespace libadcc \ No newline at end of file +} // namespace libadcc diff --git a/libadcc_src/fill_ea_doubles_guesses.hh b/libadcc_src/fill_ea_doubles_guesses.hh index 4d6c3c072..e4c449d29 100644 --- a/libadcc_src/fill_ea_doubles_guesses.hh +++ b/libadcc_src/fill_ea_doubles_guesses.hh @@ -25,17 +25,16 @@ namespace libadcc { /** Fill the passed vector of doubles blocks with doubles guesses using * the O and V matrices. - * * - * guesses_d Vectors of guesses, all elements are assumed to be initialised to zero - * and the symmetry is assumed to be properly set up. + * guesses_d Vectors of guesses, all elements are assumed to be initialised + * to zero and the symmetry is assumed to be properly set up. * mospaces Mospaces object * d_o Fock matrix to construct guesses from (occ.) * d_v Fock matrix to construct guesses from (virt.) * a_spin If alpha ionization (false: beta) * restricted Is this a restricted calculation - * doublet Doublet or quartet states (only in case of restricted calculation) - * spin_change_twice Twice the value of the spin change to enforce in an excitation. + * doublet Doublet or quartet states (only in case of restricted calc.) + * spin_change_twice Twice the value of spin change to enforce in an excitation. * degeneracy_tolerance Tolerance for two entries of the diagonal to be considered * degenerate, i.e. identical. * @@ -44,10 +43,8 @@ namespace libadcc { */ size_t fill_ea_doubles_guesses(std::vector> guesses_d, std::shared_ptr mospaces, - std::shared_ptr d_o, - std::shared_ptr d_v, - bool a_spin, bool restricted, bool doublet, - int spin_change_twice, - scalar_type degeneracy_tolerance); + std::shared_ptr d_o, std::shared_ptr d_v, + bool a_spin, bool restricted, bool doublet, + int spin_change_twice, scalar_type degeneracy_tolerance); } // namespace libadcc \ No newline at end of file diff --git a/libadcc_src/fill_ip_doubles_guesses.cc b/libadcc_src/fill_ip_doubles_guesses.cc index aa8885d02..9adec42d7 100644 --- a/libadcc_src/fill_ip_doubles_guesses.cc +++ b/libadcc_src/fill_ip_doubles_guesses.cc @@ -25,11 +25,9 @@ namespace libadcc { size_t fill_ip_doubles_guesses(std::vector> guesses_d, std::shared_ptr mospaces, - std::shared_ptr d_o, - std::shared_ptr d_v, + std::shared_ptr d_o, std::shared_ptr d_v, bool a_spin, bool restricted, bool doublet, - int spin_change_twice, - scalar_type degeneracy_tolerance) { + int spin_change_twice, scalar_type degeneracy_tolerance) { size_t n_guesses = guesses_d.size(); if (n_guesses == 0) return 0; @@ -66,13 +64,12 @@ size_t fill_ip_doubles_guesses(std::vector> guesses_d, } if (abs(spin_change_twice) != 1){ - throw not_implemented_error("spin_change ==" + - std::to_string(spin_change_twice) + " has not been tested."); + throw not_implemented_error("spin_change ==" + std::to_string(spin_change_twice) + + " has not been tested."); } - return ip_adc_guess_d(guesspairs, asbt1(d_o), asbt1(d_v), sym_s, a_spin, - restricted, doublet, ab_d, spin_change_twice, - degeneracy_tolerance); + return ip_adc_guess_d(guesspairs, asbt1(d_o), asbt1(d_v), sym_s, a_spin, restricted, + doublet, ab_d, spin_change_twice, degeneracy_tolerance); } -} // namespace libadcc \ No newline at end of file +} // namespace libadcc diff --git a/libadcc_src/fill_ip_doubles_guesses.hh b/libadcc_src/fill_ip_doubles_guesses.hh index 10bbf1d2d..ee0f37f1b 100644 --- a/libadcc_src/fill_ip_doubles_guesses.hh +++ b/libadcc_src/fill_ip_doubles_guesses.hh @@ -25,17 +25,17 @@ namespace libadcc { /** Fill the passed vector of doubles blocks with doubles guesses using * the O and V matrices. - * * - * guesses_d Vectors of guesses, all elements are assumed to be initialised to zero - * and the symmetry is assumed to be properly set up. + * + * guesses_d Vectors of guesses, all elements are assumed to be initialised + * to zero and the symmetry is assumed to be properly set up. * mospaces Mospaces object * d_o Fock matrix to construct guesses from (occ.) * d_v Fock matrix to construct guesses from (virt.) * a_spin If alpha ionization (false: beta) * restricted Is this a restricted calculation - * doublet Doublet or quartet states (only in case of restricted calculation) - * spin_change_twice Twice the value of the spin change to enforce in an excitation. + * doublet Doublet or quartet states (only in case of restricted calc.) + * spin_change_twice Twice the value of spin change to enforce in an excitation. * degeneracy_tolerance Tolerance for two entries of the diagonal to be considered * degenerate, i.e. identical. * @@ -44,10 +44,8 @@ namespace libadcc { */ size_t fill_ip_doubles_guesses(std::vector> guesses_d, std::shared_ptr mospaces, - std::shared_ptr d_o, - std::shared_ptr d_v, - bool a_spin, bool restricted, bool doublet, - int spin_change_twice, - scalar_type degeneracy_tolerance); + std::shared_ptr d_o, std::shared_ptr d_v, + bool a_spin, bool restricted, bool doublet, + int spin_change_twice, scalar_type degeneracy_tolerance); -} // namespace libadcc \ No newline at end of file +} // namespace libadcc diff --git a/libadcc_src/guess/ea_adc_guess_d.cc b/libadcc_src/guess/ea_adc_guess_d.cc index cd3e889d4..4b2c2cb0c 100644 --- a/libadcc_src/guess/ea_adc_guess_d.cc +++ b/libadcc_src/guess/ea_adc_guess_d.cc @@ -143,17 +143,19 @@ void determine_sym(const symmetry<3, double>& sym, bool& sym_v) { unsigned determine_spin(bool restricted, bool doublet) { if (restricted) { - if (doublet) return 2; - else return 4; + if (doublet) + return 2; + else + return 4; } else { - return 0; + return 0; } } /** Transfers the elements of a 1D list to a 3D list */ -void transfer_elements(const list1d_t& o, const list1d_t& v, - index_group_map_h2p& to, const libtensor::symmetry<3, - double>& sym, const index_handler<3>& base, int dm_s) { +void transfer_elements(const list1d_t& o, const list1d_t& v, index_group_map_h2p& to, + const libtensor::symmetry<3, double>& sym, + const index_handler<3>& base, int dm_s) { // Determine symmetry bool sym_v; // Are the two virtual indices identical @@ -169,58 +171,58 @@ void transfer_elements(const list1d_t& o, const list1d_t& v, for (list1d_t::const_iterator itc = v.begin(); itc != v.end(); itc++) { - // Discard element combinations which are not allowed due to the - // permutational symmetry!!! - const index<1>& bidxa = ita->get_block_index(); - const index<1>& idxa = ita->get_in_block_index(); - const index<1>& bidxb = itb->get_block_index(); - const index<1>& idxb = itb->get_in_block_index(); - const index<1>& bidxc = itc->get_block_index(); - const index<1>& idxc = itc->get_in_block_index(); - - if (sym_v && bidxb[0] == bidxc[0] && idxb[0] == idxc[0]) continue; - - double value = ita->get_value() + itb->get_value() + itc->get_value(); - - libtensor::index<3> bidx, idx; - bidx[0] = bidxa[0]; - bidx[1] = bidxb[0]; - bidx[2] = bidxc[0]; - idx[0] = idxa[0]; - idx[1] = idxb[0]; - idx[2] = idxc[0]; - - if (sym_v && bidx[1] > bidx[2]) { - std::swap(bidx[1], bidx[2]); - std::swap(idx[1], idx[2]); - } else if (sym_v && bidx[1] == bidx[2] && idx[1] > idx[2]) { - std::swap(idx[1], idx[2]); - } + // Discard element combinations which are not allowed due to the + // permutational symmetry!!! + const index<1>& bidxa = ita->get_block_index(); + const index<1>& idxa = ita->get_in_block_index(); + const index<1>& bidxb = itb->get_block_index(); + const index<1>& idxb = itb->get_in_block_index(); + const index<1>& bidxc = itc->get_block_index(); + const index<1>& idxc = itc->get_in_block_index(); + + if (sym_v && bidxb[0] == bidxc[0] && idxb[0] == idxc[0]) continue; + + double value = ita->get_value() + itb->get_value() + itc->get_value(); + + libtensor::index<3> bidx, idx; + bidx[0] = bidxa[0]; + bidx[1] = bidxb[0]; + bidx[2] = bidxc[0]; + idx[0] = idxa[0]; + idx[1] = idxb[0]; + idx[2] = idxc[0]; + + if (sym_v && bidx[1] > bidx[2]) { + std::swap(bidx[1], bidx[2]); + std::swap(idx[1], idx[2]); + } else if (sym_v && bidx[1] == bidx[2] && idx[1] > idx[2]) { + std::swap(idx[1], idx[2]); + } - // Ignore blocks where the targeted spin_change is not achieved - mask<3> orb_type; - orb_type[0] = true; // occ. - orb_type[1] = false; // virt. - orb_type[2] = false; // virt. - if (base.get_spin_proj(orb_type, bidx) != dm_s) continue; - - // Check if the block is allowed in the symmetry of the guess - orbit<3, double> orb(sym, bidx); - if (!orb.is_allowed()) continue; - - // Find canonical index - abs_index<3> abi(orb.get_acindex(), bidims); - const tensor_transf<3, double>& tr = orb.get_transf(bidx); - bidx = abi.get_index(); - permutation<3> pinv(tr.get_perm(), true); - idx.permute(pinv); - - // Split block index into spin part and spatial part - mask<3> spm; - index<3> spi; - base.split_block_index(bidx, spm, spi); - - to.add_index(value, spm, spi, idx); + // Ignore blocks where the targeted spin_change is not achieved + mask<3> orb_type; + orb_type[0] = true; // occ. + orb_type[1] = false; // virt. + orb_type[2] = false; // virt. + if (base.get_spin_proj(orb_type, bidx) != dm_s) continue; + + // Check if the block is allowed in the symmetry of the guess + orbit<3, double> orb(sym, bidx); + if (!orb.is_allowed()) continue; + + // Find canonical index + abs_index<3> abi(orb.get_acindex(), bidims); + const tensor_transf<3, double>& tr = orb.get_transf(bidx); + bidx = abi.get_index(); + permutation<3> pinv(tr.get_perm(), true); + idx.permute(pinv); + + // Split block index into spin part and spatial part + mask<3> spm; + index<3> spi; + base.split_block_index(bidx, spm, spi); + + to.add_index(value, spm, spi, idx); } // for itc } // for itb } // for ita @@ -228,8 +230,8 @@ void transfer_elements(const list1d_t& o, const list1d_t& v, size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, const index_group_h2p& ig, double value, - const symmetry<3, double>& sym, bool a_spin, - bool restricted, bool doublet, index_handler<3>& base) { + const symmetry<3, double>& sym, bool a_spin, bool restricted, + bool doublet, index_handler<3>& base) { bool sym_v; // Are the two occupied indices identical determine_sym(sym, sym_v); const unsigned spin = determine_spin(restricted, doublet); // Spin of the symmetry @@ -263,33 +265,31 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, lv[j].push_back(guess_element<3>(bidx[i], idx, coeff[j])); } } else if (ig.size() == 3) { - static const double coeff[3][3] = { - // in case of ms == 1 (alpha attachment) - // aaa bab bba - // and in case of ms == -1 (beta attachment) - // bbb aba aab - { 1.0, -1.0, -1.0}, // quartet - { 0.0, -1.0, 1.0}, // doublet 1 - { -2.0, -1.0, -1.0}}; // doublet 2 - - if (ms == 1) { // alpha attachment - for (size_t i = 0; i < 3; i++) { - for (size_t j = 0; j < 3; j++) - lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); - } - } else { // beta attachment - for (size_t i = 0; i < 3; i++) { - for (size_t j = 0; j < 3; j++) - lv[i].push_back(guess_element<3>(bidx[2-j], idx, coeff[i][j])); - } + static const double coeff[3][3] = {// in case of ms == 1 (alpha attachment) + // aaa bab bba + // and in case of ms == -1 (beta attachment) + // bbb aba aab + { 1.0, -1.0, -1.0}, // quartet + { 0.0, -1.0, 1.0}, // doublet 1 + { -2.0, -1.0, -1.0}}; // doublet 2 + + if (ms == 1) { // alpha attachment + for (size_t i = 0; i < 3; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); + } + } else { // beta attachment + for (size_t i = 0; i < 3; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[2 - j], idx, coeff[i][j])); } + } } else { // Form spin elements for (size_t i = 0; i < ig.size(); i++) lv[i].push_back(guess_element<3>(bidx[i], idx, 1.0)); } - } - else if (spin == 2) { + } else if (spin == 2) { // Reform full block indices size_t i = 0; std::vector> bidx(ig.size()); @@ -308,24 +308,23 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, } else if (ig.size() == 3) { lv.resize(2); - static const double coeff[2][3] = { - // in case of ms == 1 (alpha attachment) - // aaa bab bba - // and in case of ms == -1 (beta attachment) - // bbb aba aab - { 0.0, -1.0, 1.0}, // doublet 1 - { -2.0, -1.0, -1.0}}; // doublet 2 + static const double coeff[2][3] = {// in case of ms == 1 (alpha attachment) + // aaa bab bba + // and in case of ms == -1 (beta attachment) + // bbb aba aab + { 0.0, -1.0, 1.0}, // doublet 1 + { -2.0, -1.0, -1.0}}; // doublet 2 - if (ms == 1) { // alpha attachment + if (ms == 1) { // alpha attachment for (size_t i = 0; i < 2; i++) { - for (size_t j = 0; j < 3; j++) - lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); - } - } else { // beta attachment + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); + } + } else { // beta attachment for (size_t i = 0; i < 2; i++) { for (size_t j = 0; j < 3; j++) - lv[i].push_back(guess_element<3>(bidx[2-j], idx, coeff[i][j])); - } + lv[i].push_back(guess_element<3>(bidx[2 - j], idx, coeff[i][j])); + } } } else { // Form spin elements @@ -345,23 +344,22 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, if (ig.size() == 3) { lv.resize(1); - static const double coeff[1][3] = { - // in case of ms == 1 (alpha attachment) - // aaa bab bba - // and in case of ms == -1 (beta attachment) - // bbb aba aab - { 1.0, -1.0, -1.0}}; // quartet + static const double coeff[1][3] = {// in case of ms == 1 (alpha attachment) + // aaa bab bba + // and in case of ms == -1 (beta attachment) + // bbb aba aab + { 1.0, -1.0, -1.0}}; // quartet - if (ms == 1) { // alpha attachment + if (ms == 1) { // alpha attachment for (size_t i = 0; i < 1; i++) { - for (size_t j = 0; j < 3; j++) - lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); - } - } else { // beta attachment + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); + } + } else { // beta attachment for (size_t i = 0; i < 1; i++) { for (size_t j = 0; j < 3; j++) - lv[i].push_back(guess_element<3>(bidx[2-j], idx, coeff[i][j])); - } + lv[i].push_back(guess_element<3>(bidx[2 - j], idx, coeff[i][j])); + } } } else { // Form spin elements @@ -372,7 +370,6 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, } } - size_t i = 0; for (; i < lv.size() && cur_guess != end; i++, cur_guess++) { libtensor::btensor<3, double>& bt = *(cur_guess->first); @@ -404,10 +401,10 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, size_t ea_adc_guess_d(std::list*, double>>& va, libtensor::btensor_i<1, double>& d_o, libtensor::btensor_i<1, double>& d_v, - const libtensor::symmetry<3, double>& sym, - bool a_spin, bool restricted, bool doublet, - const libtensor::sequence<3, std::vector*>& ab, - int dm_s, double degeneracy_tolerance) { + const libtensor::symmetry<3, double>& sym, bool a_spin, + bool restricted, bool doublet, + const libtensor::sequence<3, std::vector*>& ab, int dm_s, + double degeneracy_tolerance) { size_t nguesses = va.size(); if (nguesses == 0) return 0; @@ -444,24 +441,24 @@ size_t ea_adc_guess_d(std::list*, double ilx_o.clear(); ilx_v.clear(); - //size++; // we want to have the real size of the index group guesses - // Count the number of elements + // size++; // we want to have the real size of the index group guesses + // Count the number of elements if (spin == 0) { for (index_group_map_h2p::iterator it = igm.begin(); it != igm.end(); it++) { size += igm.get_group(it).size(); - } + } } else if (spin == 2) { for (index_group_map_h2p::iterator it = igm.begin(); it != igm.end(); it++) { if (igm.get_group(it).size() == 3) { - size += 2; // two doublets, one quartet + size += 2; // two doublets, one quartet } else if (igm.get_group(it).size() == 1) { - size += 1; // only a doublet in this case + size += 1; // only a doublet in this case } } } else if (spin == 4) { for (index_group_map_h2p::iterator it = igm.begin(); it != igm.end(); it++) { if (igm.get_group(it).size() == 3) { - size += 1; // one quartet + size += 1; // one quartet } } } @@ -475,9 +472,8 @@ size_t ea_adc_guess_d(std::list*, double index_group_map_h2p::iterator it = igm.begin(); while (it != igm.end() && guess != va.end()) { index_handler<3> base(ab); - nguesses += - build_guesses(guess, va.end(), igm.get_group(it), igm.get_value(it), - sym, a_spin, restricted, doublet, base); + nguesses += build_guesses(guess, va.end(), igm.get_group(it), igm.get_value(it), sym, + a_spin, restricted, doublet, base); it++; } @@ -485,4 +481,3 @@ size_t ea_adc_guess_d(std::list*, double } } // namespace libadcc - diff --git a/libadcc_src/guess/ea_adc_guess_d.hh b/libadcc_src/guess/ea_adc_guess_d.hh index 9e943696e..9a0806783 100644 --- a/libadcc_src/guess/ea_adc_guess_d.hh +++ b/libadcc_src/guess/ea_adc_guess_d.hh @@ -23,9 +23,9 @@ namespace libadcc { size_t ea_adc_guess_d(std::list*, double>>& va, libtensor::btensor_i<1, double>& d_o, libtensor::btensor_i<1, double>& d_v, - const libtensor::symmetry<3, double>& sym, - bool a_spin, bool restricted, bool doublet, - const libtensor::sequence<3, std::vector*>& ab, - int dm_s, double degeneracy_tolerance); + const libtensor::symmetry<3, double>& sym, bool a_spin, + bool restricted, bool doublet, + const libtensor::sequence<3, std::vector*>& ab, int dm_s, + double degeneracy_tolerance); -} // namespace libadcc \ No newline at end of file +} // namespace libadcc diff --git a/libadcc_src/guess/index_group_h2p.cc b/libadcc_src/guess/index_group_h2p.cc index 1376aee6f..e5a86ab86 100644 --- a/libadcc_src/guess/index_group_h2p.cc +++ b/libadcc_src/guess/index_group_h2p.cc @@ -33,7 +33,8 @@ mask<3> index_group_h2p::compute_spin_mask(size_t sp) { return m; } -void index_group_map_h2p::add_index(double val, mask<3> spm, index<3> spidx, index<3> idx) { +void index_group_map_h2p::add_index(double val, mask<3> spm, index<3> spidx, + index<3> idx) { find_canonical_index(spm, spidx, idx); @@ -65,7 +66,7 @@ void index_group_map_h2p::add_index(double val, mask<3> spm, index<3> spidx, ind } void index_group_map_h2p::find_canonical_index(mask<3>& m, index<3>& spidx, - index<3>& idx) const { + index<3>& idx) const { if (m_sym_v) { if (spidx[1] == spidx[2]) { diff --git a/libadcc_src/guess/index_group_h2p.hh b/libadcc_src/guess/index_group_h2p.hh index 47b353581..fe25fad77 100644 --- a/libadcc_src/guess/index_group_h2p.hh +++ b/libadcc_src/guess/index_group_h2p.hh @@ -114,8 +114,8 @@ class index_group_map_h2p { typedef std::multimap::const_iterator iterator; private: - bool m_sym_v; //!< Permutational anti-symmetry of virt indices - double m_thresh; //!< Threshold for identical values + bool m_sym_v; //!< Permutational anti-symmetry of virt indices + double m_thresh; //!< Threshold for identical values std::multimap m_idxmap; diff --git a/libadcc_src/guess/index_group_p2h.cc b/libadcc_src/guess/index_group_p2h.cc index facc992c4..873f560b3 100644 --- a/libadcc_src/guess/index_group_p2h.cc +++ b/libadcc_src/guess/index_group_p2h.cc @@ -33,7 +33,8 @@ mask<3> index_group_p2h::compute_spin_mask(size_t sp) { return m; } -void index_group_map_p2h::add_index(double val, mask<3> spm, index<3> spidx, index<3> idx) { +void index_group_map_p2h::add_index(double val, mask<3> spm, index<3> spidx, + index<3> idx) { find_canonical_index(spm, spidx, idx); @@ -65,7 +66,7 @@ void index_group_map_p2h::add_index(double val, mask<3> spm, index<3> spidx, ind } void index_group_map_p2h::find_canonical_index(mask<3>& m, index<3>& spidx, - index<3>& idx) const { + index<3>& idx) const { if (m_sym_o) { if (spidx[0] == spidx[1]) { @@ -81,4 +82,4 @@ void index_group_map_p2h::find_canonical_index(mask<3>& m, index<3>& spidx, } } -} // namespace libadcc \ No newline at end of file +} // namespace libadcc diff --git a/libadcc_src/guess/index_group_p2h.hh b/libadcc_src/guess/index_group_p2h.hh index eb5447421..af80da81c 100644 --- a/libadcc_src/guess/index_group_p2h.hh +++ b/libadcc_src/guess/index_group_p2h.hh @@ -114,8 +114,8 @@ class index_group_map_p2h { typedef std::multimap::const_iterator iterator; private: - bool m_sym_o; //!< Permutational anti-symmetry of occ indices - double m_thresh; //!< Threshold for identical values + bool m_sym_o; //!< Permutational anti-symmetry of occ indices + double m_thresh; //!< Threshold for identical values std::multimap m_idxmap; diff --git a/libadcc_src/guess/ip_adc_guess_d.cc b/libadcc_src/guess/ip_adc_guess_d.cc index 658253056..d8455450d 100644 --- a/libadcc_src/guess/ip_adc_guess_d.cc +++ b/libadcc_src/guess/ip_adc_guess_d.cc @@ -143,17 +143,19 @@ void determine_sym(const symmetry<3, double>& sym, bool& sym_o) { unsigned determine_spin(bool restricted, bool doublet) { if (restricted) { - if (doublet) return 2; - else return 4; + if (doublet) + return 2; + else + return 4; } else { - return 0; + return 0; } } /** Transfers the elements of a 1D list to a 3D list */ -void transfer_elements(const list1d_t& o, const list1d_t& v, - index_group_map_p2h& to, const libtensor::symmetry<3, - double>& sym, const index_handler<3>& base, int dm_s) { +void transfer_elements(const list1d_t& o, const list1d_t& v, index_group_map_p2h& to, + const libtensor::symmetry<3, double>& sym, + const index_handler<3>& base, int dm_s) { // Determine symmetry bool sym_o; // Are the two occupied indices identical @@ -169,58 +171,58 @@ void transfer_elements(const list1d_t& o, const list1d_t& v, for (list1d_t::const_iterator itc = v.begin(); itc != v.end(); itc++) { - // Discard element combinations which are not allowed due to the - // permutational symmetry!!! - const index<1>& bidxa = ita->get_block_index(); - const index<1>& idxa = ita->get_in_block_index(); - const index<1>& bidxb = itb->get_block_index(); - const index<1>& idxb = itb->get_in_block_index(); - const index<1>& bidxc = itc->get_block_index(); - const index<1>& idxc = itc->get_in_block_index(); - - if (sym_o && bidxa[0] == bidxb[0] && idxa[0] == idxb[0]) continue; - - double value = ita->get_value() + itb->get_value() + itc->get_value(); - - libtensor::index<3> bidx, idx; - bidx[0] = bidxa[0]; - bidx[1] = bidxb[0]; - bidx[2] = bidxc[0]; - idx[0] = idxa[0]; - idx[1] = idxb[0]; - idx[2] = idxc[0]; - - if (sym_o && bidx[0] > bidx[1]) { - std::swap(bidx[0], bidx[1]); - std::swap(idx[0], idx[1]); - } else if (sym_o && bidx[0] == bidx[1] && idx[0] > idx[1]) { - std::swap(idx[0], idx[1]); - } + // Discard element combinations which are not allowed due to the + // permutational symmetry!!! + const index<1>& bidxa = ita->get_block_index(); + const index<1>& idxa = ita->get_in_block_index(); + const index<1>& bidxb = itb->get_block_index(); + const index<1>& idxb = itb->get_in_block_index(); + const index<1>& bidxc = itc->get_block_index(); + const index<1>& idxc = itc->get_in_block_index(); + + if (sym_o && bidxa[0] == bidxb[0] && idxa[0] == idxb[0]) continue; + + double value = ita->get_value() + itb->get_value() + itc->get_value(); + + libtensor::index<3> bidx, idx; + bidx[0] = bidxa[0]; + bidx[1] = bidxb[0]; + bidx[2] = bidxc[0]; + idx[0] = idxa[0]; + idx[1] = idxb[0]; + idx[2] = idxc[0]; + + if (sym_o && bidx[0] > bidx[1]) { + std::swap(bidx[0], bidx[1]); + std::swap(idx[0], idx[1]); + } else if (sym_o && bidx[0] == bidx[1] && idx[0] > idx[1]) { + std::swap(idx[0], idx[1]); + } - // Ignore blocks where the targeted spin_change is not achieved - mask<3> orb_type; - orb_type[0] = true; // occ. - orb_type[1] = true; // occ. - orb_type[2] = false; // virt. - if (base.get_spin_proj(orb_type, bidx) != dm_s) continue; - - // Check if the block is allowed in the symmetry of the guess - orbit<3, double> orb(sym, bidx); - if (!orb.is_allowed()) continue; - - // Find canonical index - abs_index<3> abi(orb.get_acindex(), bidims); - const tensor_transf<3, double>& tr = orb.get_transf(bidx); - bidx = abi.get_index(); - permutation<3> pinv(tr.get_perm(), true); - idx.permute(pinv); - - // Split block index into spin part and spatial part - mask<3> spm; - index<3> spi; - base.split_block_index(bidx, spm, spi); - - to.add_index(value, spm, spi, idx); + // Ignore blocks where the targeted spin_change is not achieved + mask<3> orb_type; + orb_type[0] = true; // occ. + orb_type[1] = true; // occ. + orb_type[2] = false; // virt. + if (base.get_spin_proj(orb_type, bidx) != dm_s) continue; + + // Check if the block is allowed in the symmetry of the guess + orbit<3, double> orb(sym, bidx); + if (!orb.is_allowed()) continue; + + // Find canonical index + abs_index<3> abi(orb.get_acindex(), bidims); + const tensor_transf<3, double>& tr = orb.get_transf(bidx); + bidx = abi.get_index(); + permutation<3> pinv(tr.get_perm(), true); + idx.permute(pinv); + + // Split block index into spin part and spatial part + mask<3> spm; + index<3> spi; + base.split_block_index(bidx, spm, spi); + + to.add_index(value, spm, spi, idx); } // for itc } // for itb } // for ita @@ -228,7 +230,7 @@ void transfer_elements(const list1d_t& o, const list1d_t& v, size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, const index_group_p2h& ig, double value, - const symmetry<3, double>& sym, bool a_spin, + const symmetry<3, double>& sym, bool a_spin, bool restricted, bool doublet, index_handler<3>& base) { bool sym_o; // Are the two occupied indices identical determine_sym(sym, sym_o); @@ -263,33 +265,31 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, lv[j].push_back(guess_element<3>(bidx[i], idx, coeff[j])); } } else if (ig.size() == 3) { - static const double coeff[3][3] = { - // in case of ms == -1 (alpha ionization) - // aaa abb bab - // and in case of ms == 1 (beta ionization) - // bbb baa aba - { 1.0, -1.0, -1.0}, // quartet - { 0.0, -1.0, 1.0}, // doublet 1 - { -2.0, -1.0, -1.0}}; // doublet 2 - - if (ms == -1) { // alpha ionization + static const double coeff[3][3] = {// in case of ms == -1 (alpha ionization) + // aaa abb bab + // and in case of ms == 1 (beta ionization) + // bbb baa aba + { 1.0, -1.0, -1.0}, // quartet + { 0.0, -1.0, 1.0}, // doublet 1 + { -2.0, -1.0, -1.0}}; // doublet 2 + + if (ms == -1) { // alpha ionization for (size_t i = 0; i < 3; i++) { - for (size_t j = 0; j < 3; j++) - lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); - } - } else { // beta ionization + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); + } + } else { // beta ionization for (size_t i = 0; i < 3; i++) { for (size_t j = 0; j < 3; j++) - lv[i].push_back(guess_element<3>(bidx[2-j], idx, coeff[i][j])); - } + lv[i].push_back(guess_element<3>(bidx[2 - j], idx, coeff[i][j])); + } } } else { // Form spin elements for (size_t i = 0; i < ig.size(); i++) lv[i].push_back(guess_element<3>(bidx[i], idx, 1.0)); } - } - else if (spin == 2) { + } else if (spin == 2) { // Reform full block indices size_t i = 0; std::vector> bidx(ig.size()); @@ -308,24 +308,23 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, } else if (ig.size() == 3) { lv.resize(2); - static const double coeff[2][3] = { - // in case of ms == -1 (alpha ionization) - // aaa abb bab - // and in case of ms == 1 (beta ionization) - // bbb baa aba - { 0.0, -1.0, 1.0}, // doublet 1 - { -2.0, -1.0, -1.0}}; // doublet 2 + static const double coeff[2][3] = {// in case of ms == -1 (alpha ionization) + // aaa abb bab + // and in case of ms == 1 (beta ionization) + // bbb baa aba + { 0.0, -1.0, 1.0}, // doublet 1 + { -2.0, -1.0, -1.0}}; // doublet 2 - if (ms == -1) { // alpha ionization + if (ms == -1) { // alpha ionization for (size_t i = 0; i < 2; i++) { - for (size_t j = 0; j < 3; j++) - lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); - } - } else { // beta ionization + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); + } + } else { // beta ionization for (size_t i = 0; i < 2; i++) { for (size_t j = 0; j < 3; j++) - lv[i].push_back(guess_element<3>(bidx[2-j], idx, coeff[i][j])); - } + lv[i].push_back(guess_element<3>(bidx[2 - j], idx, coeff[i][j])); + } } } else { // Form spin elements @@ -345,23 +344,22 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, if (ig.size() == 3) { lv.resize(1); - static const double coeff[1][3] = { - // in case of ms == -1 (alpha ionization) - // aaa abb bab - // and in case of ms == 1 (beta ionization) - // bbb baa aba - { 1.0, -1.0, -1.0}}; // quartet + static const double coeff[1][3] = {// in case of ms == -1 (alpha ionization) + // aaa abb bab + // and in case of ms == 1 (beta ionization) + // bbb baa aba + { 1.0, -1.0, -1.0}}; // quartet - if (ms == -1) { // alpha ionization + if (ms == -1) { // alpha ionization for (size_t i = 0; i < 1; i++) { - for (size_t j = 0; j < 3; j++) - lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); - } - } else { // beta ionization + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); + } + } else { // beta ionization for (size_t i = 0; i < 1; i++) { for (size_t j = 0; j < 3; j++) - lv[i].push_back(guess_element<3>(bidx[2-j], idx, coeff[i][j])); - } + lv[i].push_back(guess_element<3>(bidx[2 - j], idx, coeff[i][j])); + } } } else { // Form spin elements @@ -404,10 +402,10 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, size_t ip_adc_guess_d(std::list*, double>>& va, libtensor::btensor_i<1, double>& d_o, libtensor::btensor_i<1, double>& d_v, - const libtensor::symmetry<3, double>& sym, - bool a_spin, bool restricted, bool doublet, - const libtensor::sequence<3, std::vector*>& ab, - int dm_s, double degeneracy_tolerance) { + const libtensor::symmetry<3, double>& sym, bool a_spin, + bool restricted, bool doublet, + const libtensor::sequence<3, std::vector*>& ab, int dm_s, + double degeneracy_tolerance) { size_t nguesses = va.size(); if (nguesses == 0) return 0; @@ -444,24 +442,24 @@ size_t ip_adc_guess_d(std::list*, double ilx_o.clear(); ilx_v.clear(); - //size++; // we want to have the real size of the index group guesses - // Count the number of elements + // size++; // we want to have the real size of the index group guesses + // Count the number of elements if (spin == 0) { for (index_group_map_p2h::iterator it = igm.begin(); it != igm.end(); it++) { size += igm.get_group(it).size(); - } + } } else if (spin == 2) { for (index_group_map_p2h::iterator it = igm.begin(); it != igm.end(); it++) { if (igm.get_group(it).size() == 3) { - size += 2; // two doublets, one quartet + size += 2; // two doublets, one quartet } else if (igm.get_group(it).size() == 1) { - size += 1; // only a doublet in this case + size += 1; // only a doublet in this case } } } else if (spin == 4) { for (index_group_map_p2h::iterator it = igm.begin(); it != igm.end(); it++) { if (igm.get_group(it).size() == 3) { - size += 1; // one quartet + size += 1; // one quartet } } } @@ -475,9 +473,8 @@ size_t ip_adc_guess_d(std::list*, double index_group_map_p2h::iterator it = igm.begin(); while (it != igm.end() && guess != va.end()) { index_handler<3> base(ab); - nguesses += - build_guesses(guess, va.end(), igm.get_group(it), igm.get_value(it), - sym, a_spin, restricted, doublet, base); + nguesses += build_guesses(guess, va.end(), igm.get_group(it), igm.get_value(it), sym, + a_spin, restricted, doublet, base); it++; } @@ -485,4 +482,3 @@ size_t ip_adc_guess_d(std::list*, double } } // namespace libadcc - diff --git a/libadcc_src/guess/ip_adc_guess_d.hh b/libadcc_src/guess/ip_adc_guess_d.hh index 528d5e3e1..9661b59b3 100644 --- a/libadcc_src/guess/ip_adc_guess_d.hh +++ b/libadcc_src/guess/ip_adc_guess_d.hh @@ -23,9 +23,9 @@ namespace libadcc { size_t ip_adc_guess_d(std::list*, double>>& va, libtensor::btensor_i<1, double>& d_o, libtensor::btensor_i<1, double>& d_v, - const libtensor::symmetry<3, double>& sym, - bool a_spin, bool restricted, bool doublet, - const libtensor::sequence<3, std::vector*>& ab, - int dm_s, double degeneracy_tolerance); + const libtensor::symmetry<3, double>& sym, bool a_spin, + bool restricted, bool doublet, + const libtensor::sequence<3, std::vector*>& ab, int dm_s, + double degeneracy_tolerance); -} // namespace libadcc \ No newline at end of file +} // namespace libadcc diff --git a/libadcc_src/pyiface/export_adc_ea.cc b/libadcc_src/pyiface/export_adc_ea.cc index 83929eb75..9c59c9463 100644 --- a/libadcc_src/pyiface/export_adc_ea.cc +++ b/libadcc_src/pyiface/export_adc_ea.cc @@ -32,9 +32,9 @@ void export_adc_ea(py::module& m) { "Apply the spin symmetrisation required to make the doubles and higher parts of " "an amplitude vector consist of components for a particular spin kind only."); - m.def("fill_ea_doubles_guesses", &fill_ea_doubles_guesses, "guesses_d"_a, - "mospaces"_a, "d_o"_a, "d_v"_a, "a_spin"_a, "restricted"_a, - "doublet"_a, "spin_change_twice"_a, "degeneracy_tolerance"_a, + m.def("fill_ea_doubles_guesses", &fill_ea_doubles_guesses, "guesses_d"_a, "mospaces"_a, + "d_o"_a, "d_v"_a, "a_spin"_a, "restricted"_a, "doublet"_a, "spin_change_twice"_a, + "degeneracy_tolerance"_a, "Fill the passed vector of doubles blocks with doubles guesses using " "the O and V matrices, which are the two Fock matrices " "involved in the doubles block.\n\nguesses_d Vectors of guesses, " diff --git a/libadcc_src/pyiface/export_adc_ip.cc b/libadcc_src/pyiface/export_adc_ip.cc index d3454a69c..e8e099690 100644 --- a/libadcc_src/pyiface/export_adc_ip.cc +++ b/libadcc_src/pyiface/export_adc_ip.cc @@ -32,9 +32,9 @@ void export_adc_ip(py::module& m) { "Apply the spin symmetrisation required to make the doubles and higher parts of " "an amplitude vector consist of components for a particular spin kind only."); - m.def("fill_ip_doubles_guesses", &fill_ip_doubles_guesses, "guesses_d"_a, - "mospaces"_a, "d_o"_a, "d_v"_a, "a_spin"_a, "restricted"_a, - "doublet"_a, "spin_change_twice"_a, "degeneracy_tolerance"_a, + m.def("fill_ip_doubles_guesses", &fill_ip_doubles_guesses, "guesses_d"_a, "mospaces"_a, + "d_o"_a, "d_v"_a, "a_spin"_a, "restricted"_a, "doublet"_a, "spin_change_twice"_a, + "degeneracy_tolerance"_a, "Fill the passed vector of doubles blocks with doubles guesses using " "the O and V matrices., which are the two Fock matrices " "involved in the doubles block.\n\nguesses_d Vectors of guesses, " From 3725db98e3ec47584fe9842ca92ac5300e1d8876 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Wed, 13 May 2026 14:38:00 +0200 Subject: [PATCH 14/26] updated testdata list and c++ code style --- adcc/tests/data/update_testdata.sh | 10 ------- libadcc_src/fill_ea_doubles_guesses.cc | 2 +- libadcc_src/fill_ea_doubles_guesses.hh | 1 - libadcc_src/fill_ip_doubles_guesses.cc | 2 +- libadcc_src/fill_ip_doubles_guesses.hh | 1 - libadcc_src/guess/ea_adc_guess_d.cc | 12 ++++---- libadcc_src/guess/ip_adc_guess_d.cc | 39 +++++++++++++------------- 7 files changed, 27 insertions(+), 40 deletions(-) diff --git a/adcc/tests/data/update_testdata.sh b/adcc/tests/data/update_testdata.sh index 4a52be823..336dc2694 100755 --- a/adcc/tests/data/update_testdata.sh +++ b/adcc/tests/data/update_testdata.sh @@ -32,11 +32,9 @@ DATAFILES=( cn_sto3g_adcman_adc3.hdf5 cn_sto3g_adcman_ip-adc0.hdf5 cn_sto3g_adcman_ip-adc2.hdf5 - cn_sto3g_adcman_ip-adc2x.hdf5 cn_sto3g_adcman_ip-adc3.hdf5 cn_sto3g_adcman_ea-adc0.hdf5 cn_sto3g_adcman_ea-adc2.hdf5 - cn_sto3g_adcman_ea-adc2x.hdf5 cn_sto3g_adcman_ea-adc3.hdf5 cn_sto3g_adcman_mpdata.hdf5 cn_sto3g_hfdata.hdf5 @@ -67,11 +65,9 @@ DATAFILES=( h2o_sto3g_adcman_adc3.hdf5 h2o_sto3g_adcman_ip-adc0.hdf5 h2o_sto3g_adcman_ip-adc2.hdf5 - h2o_sto3g_adcman_ip-adc2x.hdf5 h2o_sto3g_adcman_ip-adc3.hdf5 h2o_sto3g_adcman_ea-adc0.hdf5 h2o_sto3g_adcman_ea-adc2.hdf5 - h2o_sto3g_adcman_ea-adc2x.hdf5 h2o_sto3g_adcman_ea-adc3.hdf5 h2o_sto3g_adcman_mpdata.hdf5 h2o_sto3g_hfdata.hdf5 @@ -97,11 +93,9 @@ DATAFILES=( hf_631g_adcman_adc3.hdf5 hf_631g_adcman_ip-adc0.hdf5 hf_631g_adcman_ip-adc2.hdf5 - hf_631g_adcman_ip-adc2x.hdf5 hf_631g_adcman_ip-adc3.hdf5 hf_631g_adcman_ea-adc0.hdf5 hf_631g_adcman_ea-adc2.hdf5 - hf_631g_adcman_ea-adc2x.hdf5 hf_631g_adcman_ea-adc3.hdf5 hf_631g_adcman_mpdata.hdf5 hf_631g_hfdata.hdf5 @@ -129,11 +123,9 @@ DATAFILES_FULL=( cn_ccpvdz_adcman_adc3.hdf5 cn_ccpvdz_adcman_ip-adc0.hdf5 cn_ccpvdz_adcman_ip-adc2.hdf5 - cn_ccpvdz_adcman_ip-adc2x.hdf5 cn_ccpvdz_adcman_ip-adc3.hdf5 cn_ccpvdz_adcman_ea-adc0.hdf5 cn_ccpvdz_adcman_ea-adc2.hdf5 - cn_ccpvdz_adcman_ea-adc2x.hdf5 cn_ccpvdz_adcman_ea-adc3.hdf5 cn_ccpvdz_adcman_mpdata.hdf5 cn_ccpvdz_hfdata.hdf5 @@ -164,11 +156,9 @@ DATAFILES_FULL=( h2o_def2tzvp_adcman_adc3.hdf5 h2o_def2tzvp_adcman_ip-adc0.hdf5 h2o_def2tzvp_adcman_ip-adc2.hdf5 - h2o_def2tzvp_adcman_ip-adc2x.hdf5 h2o_def2tzvp_adcman_ip-adc3.hdf5 h2o_def2tzvp_adcman_ea-adc0.hdf5 h2o_def2tzvp_adcman_ea-adc2.hdf5 - h2o_def2tzvp_adcman_ea-adc2x.hdf5 h2o_def2tzvp_adcman_ea-adc3.hdf5 h2o_def2tzvp_adcman_mpdata.hdf5 h2o_def2tzvp_hfdata.hdf5 diff --git a/libadcc_src/fill_ea_doubles_guesses.cc b/libadcc_src/fill_ea_doubles_guesses.cc index 9b820ac4b..c0e5ce9bc 100644 --- a/libadcc_src/fill_ea_doubles_guesses.cc +++ b/libadcc_src/fill_ea_doubles_guesses.cc @@ -63,7 +63,7 @@ size_t fill_ea_doubles_guesses(std::vector> guesses_d, guesspairs.emplace_back(&(asbt3(guesses_d[i])), 0.0); } - if (abs(spin_change_twice) != 1){ + if (abs(spin_change_twice) != 1) { throw not_implemented_error("spin_change ==" + std::to_string(spin_change_twice) + " has not been tested."); } diff --git a/libadcc_src/fill_ea_doubles_guesses.hh b/libadcc_src/fill_ea_doubles_guesses.hh index e4c449d29..815c8e32d 100644 --- a/libadcc_src/fill_ea_doubles_guesses.hh +++ b/libadcc_src/fill_ea_doubles_guesses.hh @@ -22,7 +22,6 @@ namespace libadcc { - /** Fill the passed vector of doubles blocks with doubles guesses using * the O and V matrices. * diff --git a/libadcc_src/fill_ip_doubles_guesses.cc b/libadcc_src/fill_ip_doubles_guesses.cc index 9adec42d7..2b223469c 100644 --- a/libadcc_src/fill_ip_doubles_guesses.cc +++ b/libadcc_src/fill_ip_doubles_guesses.cc @@ -63,7 +63,7 @@ size_t fill_ip_doubles_guesses(std::vector> guesses_d, guesspairs.emplace_back(&(asbt3(guesses_d[i])), 0.0); } - if (abs(spin_change_twice) != 1){ + if (abs(spin_change_twice) != 1) { throw not_implemented_error("spin_change ==" + std::to_string(spin_change_twice) + " has not been tested."); } diff --git a/libadcc_src/fill_ip_doubles_guesses.hh b/libadcc_src/fill_ip_doubles_guesses.hh index ee0f37f1b..944f6cc92 100644 --- a/libadcc_src/fill_ip_doubles_guesses.hh +++ b/libadcc_src/fill_ip_doubles_guesses.hh @@ -22,7 +22,6 @@ namespace libadcc { - /** Fill the passed vector of doubles blocks with doubles guesses using * the O and V matrices. * diff --git a/libadcc_src/guess/ea_adc_guess_d.cc b/libadcc_src/guess/ea_adc_guess_d.cc index 4b2c2cb0c..1002cd9c5 100644 --- a/libadcc_src/guess/ea_adc_guess_d.cc +++ b/libadcc_src/guess/ea_adc_guess_d.cc @@ -269,9 +269,9 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, // aaa bab bba // and in case of ms == -1 (beta attachment) // bbb aba aab - { 1.0, -1.0, -1.0}, // quartet - { 0.0, -1.0, 1.0}, // doublet 1 - { -2.0, -1.0, -1.0}}; // doublet 2 + {1.0, -1.0, -1.0}, // quartet + {0.0, -1.0, 1.0}, // doublet 1 + {-2.0, -1.0, -1.0}}; // doublet 2 if (ms == 1) { // alpha attachment for (size_t i = 0; i < 3; i++) { @@ -312,8 +312,8 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, // aaa bab bba // and in case of ms == -1 (beta attachment) // bbb aba aab - { 0.0, -1.0, 1.0}, // doublet 1 - { -2.0, -1.0, -1.0}}; // doublet 2 + {0.0, -1.0, 1.0}, // doublet 1 + {-2.0, -1.0, -1.0}}; // doublet 2 if (ms == 1) { // alpha attachment for (size_t i = 0; i < 2; i++) { @@ -348,7 +348,7 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, // aaa bab bba // and in case of ms == -1 (beta attachment) // bbb aba aab - { 1.0, -1.0, -1.0}}; // quartet + {1.0, -1.0, -1.0}}; // quartet if (ms == 1) { // alpha attachment for (size_t i = 0; i < 1; i++) { diff --git a/libadcc_src/guess/ip_adc_guess_d.cc b/libadcc_src/guess/ip_adc_guess_d.cc index d8455450d..0e5b6f37a 100644 --- a/libadcc_src/guess/ip_adc_guess_d.cc +++ b/libadcc_src/guess/ip_adc_guess_d.cc @@ -230,8 +230,8 @@ void transfer_elements(const list1d_t& o, const list1d_t& v, index_group_map_p2h size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, const index_group_p2h& ig, double value, - const symmetry<3, double>& sym, bool a_spin, - bool restricted, bool doublet, index_handler<3>& base) { + const symmetry<3, double>& sym, bool a_spin, bool restricted, + bool doublet, index_handler<3>& base) { bool sym_o; // Are the two occupied indices identical determine_sym(sym, sym_o); const unsigned spin = determine_spin(restricted, doublet); // Spin of the symmetry @@ -269,21 +269,21 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, // aaa abb bab // and in case of ms == 1 (beta ionization) // bbb baa aba - { 1.0, -1.0, -1.0}, // quartet - { 0.0, -1.0, 1.0}, // doublet 1 - { -2.0, -1.0, -1.0}}; // doublet 2 - - if (ms == -1) { // alpha ionization - for (size_t i = 0; i < 3; i++) { - for (size_t j = 0; j < 3; j++) - lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); - } - } else { // beta ionization - for (size_t i = 0; i < 3; i++) { - for (size_t j = 0; j < 3; j++) - lv[i].push_back(guess_element<3>(bidx[2 - j], idx, coeff[i][j])); - } + {1.0, -1.0, -1.0}, // quartet + {0.0, -1.0, 1.0}, // doublet 1 + {-2.0, -1.0, -1.0}}; // doublet 2 + + if (ms == -1) { // alpha ionization + for (size_t i = 0; i < 3; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[j], idx, coeff[i][j])); + } + } else { // beta ionization + for (size_t i = 0; i < 3; i++) { + for (size_t j = 0; j < 3; j++) + lv[i].push_back(guess_element<3>(bidx[2 - j], idx, coeff[i][j])); } + } } else { // Form spin elements for (size_t i = 0; i < ig.size(); i++) @@ -312,8 +312,8 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, // aaa abb bab // and in case of ms == 1 (beta ionization) // bbb baa aba - { 0.0, -1.0, 1.0}, // doublet 1 - { -2.0, -1.0, -1.0}}; // doublet 2 + {0.0, -1.0, 1.0}, // doublet 1 + {-2.0, -1.0, -1.0}}; // doublet 2 if (ms == -1) { // alpha ionization for (size_t i = 0; i < 2; i++) { @@ -348,7 +348,7 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, // aaa abb bab // and in case of ms == 1 (beta ionization) // bbb baa aba - { 1.0, -1.0, -1.0}}; // quartet + {1.0, -1.0, -1.0}}; // quartet if (ms == -1) { // alpha ionization for (size_t i = 0; i < 1; i++) { @@ -370,7 +370,6 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, } } - size_t i = 0; for (; i < lv.size() && cur_guess != end; i++, cur_guess++) { libtensor::btensor<3, double>& bt = *(cur_guess->first); From 2abd7b907ec926592f58e0f2c1d66be7bf0fb3f1 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Wed, 13 May 2026 14:40:59 +0200 Subject: [PATCH 15/26] c++ code style --- libadcc_src/guess/ea_adc_guess_d.cc | 4 ++-- libadcc_src/guess/ip_adc_guess_d.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libadcc_src/guess/ea_adc_guess_d.cc b/libadcc_src/guess/ea_adc_guess_d.cc index 1002cd9c5..ea5e047df 100644 --- a/libadcc_src/guess/ea_adc_guess_d.cc +++ b/libadcc_src/guess/ea_adc_guess_d.cc @@ -312,7 +312,7 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, // aaa bab bba // and in case of ms == -1 (beta attachment) // bbb aba aab - {0.0, -1.0, 1.0}, // doublet 1 + {0.0, -1.0, 1.0}, // doublet 1 {-2.0, -1.0, -1.0}}; // doublet 2 if (ms == 1) { // alpha attachment @@ -348,7 +348,7 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, // aaa bab bba // and in case of ms == -1 (beta attachment) // bbb aba aab - {1.0, -1.0, -1.0}}; // quartet + {1.0, -1.0, -1.0}}; // quartet if (ms == 1) { // alpha attachment for (size_t i = 0; i < 1; i++) { diff --git a/libadcc_src/guess/ip_adc_guess_d.cc b/libadcc_src/guess/ip_adc_guess_d.cc index 0e5b6f37a..b710874af 100644 --- a/libadcc_src/guess/ip_adc_guess_d.cc +++ b/libadcc_src/guess/ip_adc_guess_d.cc @@ -270,7 +270,7 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, // and in case of ms == 1 (beta ionization) // bbb baa aba {1.0, -1.0, -1.0}, // quartet - {0.0, -1.0, 1.0}, // doublet 1 + {0.0, -1.0, 1.0}, // doublet 1 {-2.0, -1.0, -1.0}}; // doublet 2 if (ms == -1) { // alpha ionization @@ -312,7 +312,7 @@ size_t build_guesses(list_t::iterator& cur_guess, list_t::iterator end, // aaa abb bab // and in case of ms == 1 (beta ionization) // bbb baa aba - {0.0, -1.0, 1.0}, // doublet 1 + {0.0, -1.0, 1.0}, // doublet 1 {-2.0, -1.0, -1.0}}; // doublet 2 if (ms == -1) { // alpha ionization From c08e25ecd73292cdacc3d1076f8a213cb97b17db Mon Sep 17 00:00:00 2001 From: fedy9 Date: Wed, 13 May 2026 15:08:40 +0200 Subject: [PATCH 16/26] removed spin-flip only testcase HF-631G for IP/EA --- adcc/tests/data/update_testdata.sh | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/adcc/tests/data/update_testdata.sh b/adcc/tests/data/update_testdata.sh index 336dc2694..8226d478a 100755 --- a/adcc/tests/data/update_testdata.sh +++ b/adcc/tests/data/update_testdata.sh @@ -77,26 +77,12 @@ DATAFILES=( hf_631g_adcc_adc2.hdf5 hf_631g_adcc_adc2x.hdf5 hf_631g_adcc_adc3.hdf5 - hf_631g_adcc_ip-adc0.hdf5 - hf_631g_adcc_ip-adc2.hdf5 - hf_631g_adcc_ip-adc2x.hdf5 - hf_631g_adcc_ip-adc3.hdf5 - hf_631g_adcc_ea-adc0.hdf5 - hf_631g_adcc_ea-adc2.hdf5 - hf_631g_adcc_ea-adc2x.hdf5 - hf_631g_adcc_ea-adc3.hdf5 hf_631g_adcc_mpdata.hdf5 hf_631g_adcman_adc0.hdf5 hf_631g_adcman_adc1.hdf5 hf_631g_adcman_adc2.hdf5 hf_631g_adcman_adc2x.hdf5 hf_631g_adcman_adc3.hdf5 - hf_631g_adcman_ip-adc0.hdf5 - hf_631g_adcman_ip-adc2.hdf5 - hf_631g_adcman_ip-adc3.hdf5 - hf_631g_adcman_ea-adc0.hdf5 - hf_631g_adcman_ea-adc2.hdf5 - hf_631g_adcman_ea-adc3.hdf5 hf_631g_adcman_mpdata.hdf5 hf_631g_hfdata.hdf5 r2methyloxirane_sto3g_hfdata.hdf5 From 789a1a44f4c752ecad68bad77387c250f14c52f9 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Wed, 13 May 2026 16:39:21 +0200 Subject: [PATCH 17/26] moved hf ip/ea reference generators to potential TODO --- adcc/tests/generators/generate_adcc_data.py | 46 ++++++++++--------- adcc/tests/generators/generate_adcman_data.py | 27 +++++------ 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/adcc/tests/generators/generate_adcc_data.py b/adcc/tests/generators/generate_adcc_data.py index 951fccaa6..06acbc401 100644 --- a/adcc/tests/generators/generate_adcc_data.py +++ b/adcc/tests/generators/generate_adcc_data.py @@ -73,7 +73,10 @@ def generate_adc(test_case: testcases.TestCase, method: AdcMethod, case: str, assert states.kind == kind # maybe we predicted wrong? # type: ignore if method.adc_type in ("ip", "ea"): key = f"{key}/{spin}" - if f"{key}/matrix" not in hdf5_file and spin == "alpha": + dump_matrix = (f"{key}/matrix" not in hdf5_file and ( + method.adc_type == "pp" or (method.adc_type in ("ip", "ea") and is_alpha)) + ) + if dump_matrix: # the matrix data is only dumped once for each case and only for alpha. # I think it does not make sense to dump the data once for a singlet # and once for a triplet trial vector as well as an alpha and a beta one. @@ -314,26 +317,27 @@ def generate_hf_631g(): **n_states ) - for is_alpha in [True, False]: - for method in _methods["ip"]: - method = AdcMethod(method) - for n_states in \ - testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): - n_states = {n_states: 3} - generate_adc_all( - test_case, method=method, dump_nstates=2, - is_alpha=is_alpha, **n_states - ) - - for method in _methods["ea"]: - method = AdcMethod(method) - for n_states in \ - testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): - n_states = {n_states: 3} - generate_adc_all( - test_case, method=method, dump_nstates=2, - is_alpha=is_alpha, **n_states - ) + # TODO: PP spin-flip only + # for is_alpha in [True, False]: + # for method in _methods["ip"]: + # method = AdcMethod(method) + # for n_states in \ + # testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + # n_states = {n_states: 3} + # generate_adc_all( + # test_case, method=method, dump_nstates=2, + # is_alpha=is_alpha, **n_states + # ) + + # for method in _methods["ea"]: + # method = AdcMethod(method) + # for n_states in \ + # testcases.kinds_to_nstates(test_case.kinds[method.adc_type]): + # n_states = {n_states: 3} + # generate_adc_all( + # test_case, method=method, dump_nstates=2, + # is_alpha=is_alpha, **n_states + # ) def main(): diff --git a/adcc/tests/generators/generate_adcman_data.py b/adcc/tests/generators/generate_adcman_data.py index 0ebfdab4b..8c55fdfb7 100644 --- a/adcc/tests/generators/generate_adcman_data.py +++ b/adcc/tests/generators/generate_adcman_data.py @@ -311,19 +311,20 @@ def generate_hf_631g(): **n_states ) - for method in _methods["ip"]: - method = AdcMethod(method) - n_states = {"n_ip_states": (3, 3)} - generate_adc_all( - test_case, method=method, dump_nstates=2, **n_states - ) - - for method in _methods["ea"]: - method = AdcMethod(method) - n_states = {"n_ea_states": (3, 3)} - generate_adc_all( - test_case, method=method, dump_nstates=2, **n_states - ) + # TODO: PP spin-flip only + # for method in _methods["ip"]: + # method = AdcMethod(method) + # n_states = {"n_ip_states": (3, 3)} + # generate_adc_all( + # test_case, method=method, dump_nstates=2, **n_states + # ) + + # for method in _methods["ea"]: + # method = AdcMethod(method) + # n_states = {"n_ea_states": (3, 3)} + # generate_adc_all( + # test_case, method=method, dump_nstates=2, **n_states + # ) def generate_formaldehyde_pe(): From 13e8582070a51ed13247c0956fa8a3544ae61f94 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Wed, 13 May 2026 16:40:30 +0200 Subject: [PATCH 18/26] updated SHASUMS --- adcc/tests/data/SHA256SUMS | 6 ------ 1 file changed, 6 deletions(-) diff --git a/adcc/tests/data/SHA256SUMS b/adcc/tests/data/SHA256SUMS index 294b48aa5..0f2f98804 100644 --- a/adcc/tests/data/SHA256SUMS +++ b/adcc/tests/data/SHA256SUMS @@ -136,12 +136,6 @@ d14ede49f1fb6e9700d7306db23001bb2cd7469ae7a14849179f683eee5cafe4 hf_631g_adcman 5083d69135cf29738881568e270a6e2866d6e8b44c6d4e548180bf999f8ada80 hf_631g_adcman_adc2.hdf5 ff8cba744d8820c1877104985454b611ea54e7f1bbbac683079911ef19b9dd25 hf_631g_adcman_adc2x.hdf5 3889abfa9dca22999d2aaefc9b4efc64d503ecc2953b33ecf507f5328742832b hf_631g_adcman_adc3.hdf5 -61bb4bc0b207e917ef700659b18a2a2d9cdcdb818b82acf9dffefd55be3d7466 hf_631g_adcman_ea-adc0.hdf5 -8b4e09136695ebce7ba2f1e726d371ccf5d47d6946af683cb7c5010125f4117f hf_631g_adcman_ea-adc2.hdf5 -dc260e8d63efb307b7e19ec4d9d67efaba9c4fa55806b2b0876246de3cf5acc1 hf_631g_adcman_ea-adc3.hdf5 -d985ebce808b05b8fd9c57d4775bcc84bfb5d6c0ec30ec9c1606282acedeb449 hf_631g_adcman_ip-adc0.hdf5 -407a7ef96eed211ff9b591ab9cd4eaa6b09acc504b2a9fc77499e1ddbff97dcf hf_631g_adcman_ip-adc2.hdf5 -55132fbeeb9a63d77590b56276a416d906118a417bdb62c0c10aabf0846ea3e3 hf_631g_adcman_ip-adc3.hdf5 1d7b0dd4e61aea9faf713706e5ec143426e97a55da4df575d6bd72e5dfcd872b hf_631g_adcman_mpdata.hdf5 a1eab1dd706a3f8f7532889b9a4754fa627a47ce5cfb9eb7d95a8365774850f2 hf_631g_hfdata.hdf5 5877bf4fc4554759b2f0ba35c58cee1e9a7ae2de4d47673a98793d4769eec841 methox_sto3g_hfdata.hdf5 From 5460d7a7dc7558b0d6e57e867a677fd930941b08 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Sat, 20 Jun 2026 17:03:28 +0200 Subject: [PATCH 19/26] applied PR reviews --- adcc/ChargedExcitations.py | 6 ++-- adcc/adc_ea/matrix.py | 21 ++++---------- adcc/adc_ea/util.py | 2 +- adcc/adc_ip/matrix.py | 18 ++++-------- adcc/adc_ip/util.py | 2 +- adcc/block.py | 1 - adcc/guess/__init__.py | 8 ++---- adcc/guess/guess_zero.py | 16 +++++------ adcc/guess/util.py | 8 +++--- adcc/tests/ChargedExcitations_test.py | 39 -------------------------- adcc/tests/guess_test.py | 11 +++----- libadcc_src/fill_ea_doubles_guesses.cc | 2 +- libadcc_src/fill_ea_doubles_guesses.hh | 2 +- libadcc_src/fill_ip_doubles_guesses.cc | 2 +- libadcc_src/fill_ip_doubles_guesses.hh | 2 +- libadcc_src/fill_pp_doubles_guesses.cc | 2 +- libadcc_src/fill_pp_doubles_guesses.hh | 2 +- libadcc_src/pyiface/export_adc_ea.cc | 2 +- libadcc_src/pyiface/export_adc_ip.cc | 2 +- 19 files changed, 42 insertions(+), 106 deletions(-) diff --git a/adcc/ChargedExcitations.py b/adcc/ChargedExcitations.py index b82944dae..f16bd64d5 100644 --- a/adcc/ChargedExcitations.py +++ b/adcc/ChargedExcitations.py @@ -2,7 +2,7 @@ ## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab ## --------------------------------------------------------------------- ## -## Copyright (C) 2019 by the adcc authors +## Copyright (C) 2026 by the adcc authors ## ## This file is part of adcc. ## @@ -42,7 +42,7 @@ def _pole_strength(self, state_n: int) -> np.ndarray: """Computes the pole strength for a single state""" pass - def describe_helper(self, block_norms=True, excitation_type_name="energy"): + def _describe_helper(self, block_norms=True, excitation_type_name="energy"): """ Creates and returns the to be printed columns @@ -55,8 +55,6 @@ def describe_helper(self, block_norms=True, excitation_type_name="energy"): excitation_type_name : str, optional Defines the name of the energy property. 'ionization potential'/'electron affinity' for IP/EA - ssq : bool, optional - Show the values of the excited states, by default ``False``. """ # Collect the columns to print columns: list[TableColumn] = [] diff --git a/adcc/adc_ea/matrix.py b/adcc/adc_ea/matrix.py index d3ddff4d1..bdd778aa7 100644 --- a/adcc/adc_ea/matrix.py +++ b/adcc/adc_ea/matrix.py @@ -2,7 +2,7 @@ ## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab ## --------------------------------------------------------------------- ## -## Copyright (C) 2020 by the adcc authors +## Copyright (C) 2026 by the adcc authors ## ## This file is part of adcc. ## @@ -29,7 +29,7 @@ from adcc.AmplitudeVector import AmplitudeVector # -# Dispatch routine lives in 'adc_pp/matrix.py' +# An explanation of the dispatch structure can be found in `adc_pp/matrix.py` # __all__ = ["block"] @@ -38,7 +38,7 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): """ - Gets ground state, potentially intermediates, spaces (ph, pphh and so on) + Gets ground state, potentially intermediates, spaces (p, pph and so on) and the perturbation theory order for the block, variant is "cvs" or sth like that. @@ -65,7 +65,6 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): # 0th order main # def block_p_p_0(hf, mp, intermediates): - # M_{11} def apply(ampl): return AmplitudeVector(p=einsum("ab,b->a", hf.fvv, ampl.p)) diagonal = AmplitudeVector(p=hf.fvv.diagonal()) @@ -79,7 +78,6 @@ def diagonal_pph_pph_0(hf): def block_pph_pph_0(hf, mp, intermediates): - # M_{22} def apply(ampl): return AmplitudeVector(pph=( - einsum("jab,ij->iab", ampl.pph, hf.foo) @@ -92,7 +90,7 @@ def apply(ampl): # 1st order main # def block_p_p_1(hf, mp, intermediates): - # M_{11}, same as ADC(0) + # Same as ADC(0) return block_p_p_0(hf, mp, intermediates) @@ -102,7 +100,6 @@ def diagonal_pph_pph_1(hf): def block_pph_pph_1(hf, mp, intermediates): - # M_{22} def apply(ampl): return AmplitudeVector(pph=( - einsum("jab,ij->iab", ampl.pph, hf.foo) @@ -118,7 +115,6 @@ def apply(ampl): # 1st order coupling # def block_p_pph_1(hf, mp, intermediates): - # M_{12} def apply(ampl): return AmplitudeVector(p=( - 1 / sqrt(2) * einsum("jabc,jbc->a", hf.ovvv, ampl.pph))) @@ -126,7 +122,6 @@ def apply(ampl): def block_pph_p_1(hf, mp, intermediates): - # M_{21} def apply(ampl): return AmplitudeVector(pph=( - 1 / sqrt(2) * einsum("icab,c->iab", hf.ovvv, ampl.p))) @@ -137,7 +132,6 @@ def apply(ampl): # 2nd order main # def block_p_p_2(hf, mp, intermediates): - # M_{11} # Intermediate can be found in 'adc_pp/matrix.py' i1 = intermediates.adc2_i1 diagonal = AmplitudeVector(p=i1.diagonal()) @@ -151,7 +145,6 @@ def apply(ampl): # 2nd order coupling # def block_p_pph_2(hf, mp, intermediates): - # M_{12} # Intermediate can be found in 'adc_pp/matrix.py' i2 = - intermediates.adc3_pib @@ -162,7 +155,6 @@ def apply(ampl): def block_pph_p_2(hf, mp, intermediates): - # M_{21} # Intermediate can be found in 'adc_pp/matrix.py' i2 = - intermediates.adc3_pib @@ -176,7 +168,6 @@ def apply(ampl): # 3rd order main # def block_p_p_3(hf, mp, intermediates): - # M_{11} i1 = intermediates.adc3_ea_i1 diagonal = AmplitudeVector(p=i1.diagonal()) @@ -199,12 +190,12 @@ def adc3_ea_i1(hf, mp, intermediates): + einsum("ijbc,jiac->ab", mp.t2oo, mp.t2eri(b.oovv, b.ov)) - 2 * einsum("ijbc,jica->ab", mp.t2oo, mp.t2eri(b.oovv, b.ov)) ).symmetrise() - + intermediates.sigma_vv + + intermediates.sigma_inf_vv ) @register_as_intermediate -def sigma_vv(hf, mp, intermediates): +def sigma_inf_vv(hf, mp, intermediates): # Static self-energy, oo part \Sigma_{ij}(\infty) p0 = mp.mp2_diffdm return (einsum("iajb,ij->ab", hf.ovov, p0.oo) diff --git a/adcc/adc_ea/util.py b/adcc/adc_ea/util.py index 951a5212c..b14097495 100644 --- a/adcc/adc_ea/util.py +++ b/adcc/adc_ea/util.py @@ -2,7 +2,7 @@ ## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab ## --------------------------------------------------------------------- ## -## Copyright (C) 2020 by the adcc authors +## Copyright (C) 2026 by the adcc authors ## ## This file is part of adcc. ## diff --git a/adcc/adc_ip/matrix.py b/adcc/adc_ip/matrix.py index 81d02d978..b144215a4 100644 --- a/adcc/adc_ip/matrix.py +++ b/adcc/adc_ip/matrix.py @@ -2,7 +2,7 @@ ## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab ## --------------------------------------------------------------------- ## -## Copyright (C) 2020 by the adcc authors +## Copyright (C) 2026 by the adcc authors ## ## This file is part of adcc. ## @@ -29,7 +29,7 @@ from adcc.AmplitudeVector import AmplitudeVector # -# Dispatch routine lives in 'adc_pp/matrix.py' +# An explanation of the dispatch structure can be found in `adc_pp/matrix.py` # __all__ = ["block"] @@ -38,7 +38,7 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): """ - Gets ground state, potentially intermediates, spaces (ph, pphh and so on) + Gets ground state, potentially intermediates, spaces (h, phh and so on) and the perturbation theory order for the block, variant is "cvs" or sth like that. @@ -65,7 +65,6 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): # 0th order main # def block_h_h_0(hf, mp, intermediates): - # M_{11} def apply(ampl): return AmplitudeVector(h=-einsum("ij,j->i", hf.foo, ampl.h)) diagonal = AmplitudeVector(h=-hf.foo.diagonal()) @@ -80,7 +79,6 @@ def diagonal_phh_phh_0(hf): def block_phh_phh_0(hf, mp, intermediates): - # M_{22} def apply(ampl): return AmplitudeVector(phh=( + einsum("ab,ijb->ija", hf.fvv, ampl.phh) @@ -93,7 +91,7 @@ def apply(ampl): # 1st order main # def block_h_h_1(hf, mp, intermediates): - # M_{11}, same as ADC(0) + # Same as ADC(0) return block_h_h_0(hf, mp, intermediates) @@ -112,7 +110,6 @@ def diagonal_phh_phh_1(hf): def block_phh_phh_1(hf, mp, intermediates): - # M_{22} def apply(ampl): return AmplitudeVector(phh=( + einsum("ac,ijc->ija", hf.fvv, ampl.phh) @@ -128,7 +125,6 @@ def apply(ampl): # 1st order coupling # def block_h_phh_1(hf, mp, intermediates): - # M_{12} def apply(ampl): return AmplitudeVector(h=( + 1 / sqrt(2) * einsum("jkib,jkb->i", hf.ooov, ampl.phh))) @@ -136,7 +132,6 @@ def apply(ampl): def block_phh_h_1(hf, mp, intermediates): - # M_{21} def apply(ampl): return AmplitudeVector(phh=( + 1 / sqrt(2) * einsum("ijka,k->ija", hf.ooov, ampl.h))) @@ -147,7 +142,6 @@ def apply(ampl): # 2nd order main # def block_h_h_2(hf, mp, intermediates): - # M_{11} # Intermediate can be found in 'adc_pp/matrix.py' i1 = - intermediates.adc2_i2 diagonal = AmplitudeVector(h=i1.diagonal()) @@ -209,12 +203,12 @@ def adc3_ip_i1(hf, mp, intermediates): + einsum("jkab,kiab->ij", mp.t2oo, mp.t2eri(b.oovv, b.ov)) - 2 * einsum("jkab,ikab->ij", mp.t2oo, mp.t2eri(b.oovv, b.ov)) ).symmetrise() - - intermediates.sigma_oo + - intermediates.sigma_inf_oo ) @register_as_intermediate -def sigma_oo(hf, mp, intermediates): +def sigma_inf_oo(hf, mp, intermediates): # Static self-energy, oo part \Sigma_{ij}(\infty) p0 = mp.mp2_diffdm return (einsum("ikjl,kl->ij", hf.oooo, p0.oo) diff --git a/adcc/adc_ip/util.py b/adcc/adc_ip/util.py index 8b13a9459..3ef042586 100644 --- a/adcc/adc_ip/util.py +++ b/adcc/adc_ip/util.py @@ -2,7 +2,7 @@ ## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab ## --------------------------------------------------------------------- ## -## Copyright (C) 2020 by the adcc authors +## Copyright (C) 2026 by the adcc authors ## ## This file is part of adcc. ## diff --git a/adcc/block.py b/adcc/block.py index 5124aa85c..b5a9d52f8 100644 --- a/adcc/block.py +++ b/adcc/block.py @@ -181,7 +181,6 @@ def invert_transpose_tuple(p: tuple[int, ...]) -> tuple[int, ...]: def get_block_name(spaces, order, variant, has_core_occupied_space): """ Assembles name of block function with variant, spaces, and order. - Does some sanity checks. """ if isinstance(variant, str): variant = [variant] diff --git a/adcc/guess/__init__.py b/adcc/guess/__init__.py index 19ac04c01..05c5e937f 100644 --- a/adcc/guess/__init__.py +++ b/adcc/guess/__init__.py @@ -60,8 +60,6 @@ def guesses_singlet(matrix, n_guesses, block="ph", **kwargs): vectors are returned if this many could not be found. block Diagonal block to use for obtaining the guesses (typically "ph" or "pphh"). - is_alpha Is the detached/attached electron alpha spin for the respective - IP-/EA-ADC calculation. kwargs Any other argument understood by guesses_from_diagonal. """ return guesses_from_diagonal( @@ -89,6 +87,8 @@ def guesses_doublet(matrix, n_guesses, block="h", is_alpha=True, **kwargs): spin_change = -0.5 elif matrix.method.adc_type is AdcType.EA: spin_change = 0.5 + else: + raise ValueError(f"Invalid doublet ADC type: {matrix.method.adc_type}") return guesses_from_diagonal( matrix, n_guesses, block=block, is_alpha=is_alpha, spin_change=spin_change, @@ -107,8 +107,6 @@ def guesses_triplet(matrix, n_guesses, block="ph", **kwargs): vectors are returned if this many could not be found. block Diagonal block to use for obtaining the guesses (typically "ph" or "pphh"). - is_alpha Is the detached/attached electron alpha spin for the respective - IP-/EA-ADC calculation. kwargs Any other argument understood by guesses_from_diagonal. """ return guesses_from_diagonal( @@ -132,8 +130,6 @@ def guesses_spin_flip(matrix, n_guesses, block="ph", **kwargs): vectors are returned if this many could not be found. block Diagonal block to use for obtaining the guesses (typically "ph" or "pphh"). - is_alpha Is the detached/attached electron alpha spin for the respective - IP-/EA-ADC calculation. kwargs Any other argument understood by guesses_from_diagonal. """ return guesses_from_diagonal( diff --git a/adcc/guess/guess_zero.py b/adcc/guess/guess_zero.py index a0fa73d51..e0e58dd0c 100644 --- a/adcc/guess/guess_zero.py +++ b/adcc/guess/guess_zero.py @@ -81,8 +81,8 @@ def guess_symmetries(matrix, spin_change=0, spin_block_symmetrisation="none"): "ADC calculations on top of restricted reference " "states.") if int(spin_change * 2) / 2 != spin_change: - raise ValueError("Only integer or half-integer spin_change is allowed." - " You passed {}".format(spin_change)) + raise ValueError("Only integer or half-integer spin_change is allowed. " + "You passed {}".format(spin_change)) max_spin_change = 0.5 * len(matrix.axis_blocks[-1]) valid_spin_changes = [ @@ -93,16 +93,16 @@ def guess_symmetries(matrix, spin_change=0, spin_block_symmetrisation="none"): f"{valid_spin_changes}, and not {spin_change}.") symmetries = {} - block = matrix.axis_blocks[0] - symmetries[block] = guess_symmetry_singles( + singles = matrix.axis_blocks[0] + symmetries[singles] = guess_symmetry_singles( matrix, spin_change=spin_change, - spin_block_symmetrisation=spin_block_symmetrisation, block=block + spin_block_symmetrisation=spin_block_symmetrisation, block=singles ) if len(matrix.axis_blocks) >= 2: - block = matrix.axis_blocks[1] - symmetries[block] = guess_symmetry_doubles( + doubles = matrix.axis_blocks[1] + symmetries[doubles] = guess_symmetry_doubles( matrix, spin_change=spin_change, - spin_block_symmetrisation=spin_block_symmetrisation, block=block + spin_block_symmetrisation=spin_block_symmetrisation, block=doubles ) if "ppphhh" in matrix.axis_blocks: symmetries["ppphhh"] = guess_symmetry_triples( diff --git a/adcc/guess/util.py b/adcc/guess/util.py index bc5f208ce..00e0783b2 100644 --- a/adcc/guess/util.py +++ b/adcc/guess/util.py @@ -2,7 +2,7 @@ ## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab ## --------------------------------------------------------------------- ## -## Copyright (C) 2018 by the adcc authors +## Copyright (C) 2026 by the adcc authors ## ## This file is part of adcc. ## @@ -26,12 +26,12 @@ def determine_spin_change(method: AdcMethod, kind: str, - is_alpha: Optional[bool] = None) -> Union[int, float]: + is_alpha: Optional[bool] = None) -> float: if method.adc_type is AdcType.PP: if kind == "spin_flip": - return -1 + return -1.0 else: - return 0 + return 0.0 elif method.adc_type is AdcType.IP: if is_alpha is None: raise TypeError("'is_alpha' has to be True|False for IP-ADC") diff --git a/adcc/tests/ChargedExcitations_test.py b/adcc/tests/ChargedExcitations_test.py index d7f0bd89e..2d1063472 100644 --- a/adcc/tests/ChargedExcitations_test.py +++ b/adcc/tests/ChargedExcitations_test.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 import pytest -from numpy.testing import assert_allclose from adcc.AdcMethod import AdcMethod, AdcType from adcc.ChargedExcitations import DetachedStates, AttachedStates @@ -8,20 +7,12 @@ from .testdata_cache import testdata_cache -# --------------------------------------------------------------------- -# Shared parametrization (reuse your existing case table if available) -# --------------------------------------------------------------------- - cases_ip_ea = [ ("h2o_sto3g", "ip-adc2", "gen", "doublet"), ("h2o_sto3g", "ea-adc2", "gen", "doublet"), ] -# --------------------------------------------------------------------- -# 1. Basic construction + size consistency -# --------------------------------------------------------------------- - @pytest.mark.parametrize("system,method,case,kind", cases_ip_ea) def test_ip_ea_basic_interface(system, method, case, kind): adc_type = AdcMethod(method).adc_type @@ -52,10 +43,6 @@ def test_ip_ea_basic_interface(system, method, case, kind): assert state.size == len(state.excitation_energy) -# --------------------------------------------------------------------- -# 2. describe() runs without error and contains expected wording -# --------------------------------------------------------------------- - @pytest.mark.parametrize("system,method,case,kind", cases_ip_ea) def test_ip_ea_describe(system, method, case, kind): state = testdata_cache.adcc_states( @@ -83,29 +70,3 @@ def test_ip_ea_describe(system, method, case, kind): elif adc_type is AdcType.EA: assert "affinity" in desc.lower() assert "attachment" in desc.lower() - - -# --------------------------------------------------------------------- -# 3. IP/EA hermiticity-style sanity check -# --------------------------------------------------------------------- -# -# Hermiticity of the ADC matrix itself belongs in: -# tests/test_adc_matrix.py -# -# NOT here. -# -# However, what *is* appropriate here: -# Energies must be real-valued. -# - -@pytest.mark.parametrize("system,method,case,kind", cases_ip_ea) -def test_ip_ea_energies_real(system, method, case, kind): - state = testdata_cache.adcc_states( - system=system, - method=method, - case=case, - kind=kind, - is_alpha=True, - ) - - assert_allclose(state.excitation_energy.imag, 0.0) diff --git a/adcc/tests/guess_test.py b/adcc/tests/guess_test.py index bdce7c247..3954b1110 100644 --- a/adcc/tests/guess_test.py +++ b/adcc/tests/guess_test.py @@ -28,6 +28,7 @@ import adcc import adcc.guess +from adcc.guess import determine_spin_change, estimate_n_guesses from adcc.AdcMethod import AdcType from .testdata_cache import testdata_cache @@ -49,18 +50,18 @@ class TestGuess: def test_determine_spin_change_pp(self): - from adcc.guess import determine_spin_change - method = adcc.AdcMethod("adc2") spin = determine_spin_change(method, kind="singlet") assert spin == 0.0 + spin = determine_spin_change(method, kind="triplet") + assert spin == 0.0 + spin = determine_spin_change(method, kind="spin_flip") assert spin == -1.0 def test_determine_spin_change_ip(self): - from adcc.guess import determine_spin_change method = adcc.AdcMethod("ip-adc2") @@ -74,7 +75,6 @@ def test_determine_spin_change_ip(self): determine_spin_change(method, kind="any", is_alpha=None) def test_determine_spin_change_ea(self): - from adcc.guess import determine_spin_change method = adcc.AdcMethod("ea-adc2") @@ -88,7 +88,6 @@ def test_determine_spin_change_ea(self): determine_spin_change(method, kind="any", is_alpha=None) def test_determine_spin_change_unknown_adc_type(self): - from adcc.guess import determine_spin_change method = adcc.AdcMethod("adc2") method.adc_type = "bla" @@ -111,8 +110,6 @@ def test_estimate_n_guesses_pp(self): assert i <= estimate_n_guesses(matrix, n_states=i, singles_only=True) def test_estimate_n_guesses_ip(self): - from adcc.guess import estimate_n_guesses - refstate = testdata_cache.refstate("h2o_sto3g", case="gen") ground_state = adcc.LazyMp(refstate) matrix = adcc.AdcMatrix("ip-adc2", ground_state) diff --git a/libadcc_src/fill_ea_doubles_guesses.cc b/libadcc_src/fill_ea_doubles_guesses.cc index c0e5ce9bc..f2e616f1d 100644 --- a/libadcc_src/fill_ea_doubles_guesses.cc +++ b/libadcc_src/fill_ea_doubles_guesses.cc @@ -1,5 +1,5 @@ // -// Copyright (C) 2020 by the adcc authors +// Copyright (C) 2026 by the adcc authors // // This file is part of adcc. // diff --git a/libadcc_src/fill_ea_doubles_guesses.hh b/libadcc_src/fill_ea_doubles_guesses.hh index 815c8e32d..15ad46442 100644 --- a/libadcc_src/fill_ea_doubles_guesses.hh +++ b/libadcc_src/fill_ea_doubles_guesses.hh @@ -1,5 +1,5 @@ // -// Copyright (C) 2020 by the adcc authors +// Copyright (C) 2026 by the adcc authors // // This file is part of adcc. // diff --git a/libadcc_src/fill_ip_doubles_guesses.cc b/libadcc_src/fill_ip_doubles_guesses.cc index 2b223469c..4ca9bc217 100644 --- a/libadcc_src/fill_ip_doubles_guesses.cc +++ b/libadcc_src/fill_ip_doubles_guesses.cc @@ -1,5 +1,5 @@ // -// Copyright (C) 2020 by the adcc authors +// Copyright (C) 2026 by the adcc authors // // This file is part of adcc. // diff --git a/libadcc_src/fill_ip_doubles_guesses.hh b/libadcc_src/fill_ip_doubles_guesses.hh index 944f6cc92..2ff123c22 100644 --- a/libadcc_src/fill_ip_doubles_guesses.hh +++ b/libadcc_src/fill_ip_doubles_guesses.hh @@ -1,5 +1,5 @@ // -// Copyright (C) 2020 by the adcc authors +// Copyright (C) 2026 by the adcc authors // // This file is part of adcc. // diff --git a/libadcc_src/fill_pp_doubles_guesses.cc b/libadcc_src/fill_pp_doubles_guesses.cc index 2bd3ebaf8..b5baec553 100644 --- a/libadcc_src/fill_pp_doubles_guesses.cc +++ b/libadcc_src/fill_pp_doubles_guesses.cc @@ -1,5 +1,5 @@ // -// Copyright (C) 2020 by the adcc authors +// Copyright (C) 2026 by the adcc authors // // This file is part of adcc. // diff --git a/libadcc_src/fill_pp_doubles_guesses.hh b/libadcc_src/fill_pp_doubles_guesses.hh index b2e401581..355c1df86 100644 --- a/libadcc_src/fill_pp_doubles_guesses.hh +++ b/libadcc_src/fill_pp_doubles_guesses.hh @@ -1,5 +1,5 @@ // -// Copyright (C) 2020 by the adcc authors +// Copyright (C) 2026 by the adcc authors // // This file is part of adcc. // diff --git a/libadcc_src/pyiface/export_adc_ea.cc b/libadcc_src/pyiface/export_adc_ea.cc index 9c59c9463..6a095ad26 100644 --- a/libadcc_src/pyiface/export_adc_ea.cc +++ b/libadcc_src/pyiface/export_adc_ea.cc @@ -1,5 +1,5 @@ // -// Copyright (C) 2019 by the adcc authors +// Copyright (C) 2026 by the adcc authors // // This file is part of adcc. // diff --git a/libadcc_src/pyiface/export_adc_ip.cc b/libadcc_src/pyiface/export_adc_ip.cc index e8e099690..6b45334d3 100644 --- a/libadcc_src/pyiface/export_adc_ip.cc +++ b/libadcc_src/pyiface/export_adc_ip.cc @@ -1,5 +1,5 @@ // -// Copyright (C) 2019 by the adcc authors +// Copyright (C) 2026 by the adcc authors // // This file is part of adcc. // From 1d3830daa7c6b2ee2ba4cbf7b7323a787b3ac78b Mon Sep 17 00:00:00 2001 From: fedy9 Date: Sat, 20 Jun 2026 17:10:40 +0200 Subject: [PATCH 20/26] flake8 --- adcc/AdcMatrix.py | 9 ++++++--- adcc/AdcMethod.py | 2 -- adcc/guess/guess_zero.py | 4 ++-- adcc/guess/util.py | 6 +++--- adcc/tests/generators/dump_adcc.py | 4 ++-- adcc/tests/guess_test.py | 7 ------- 6 files changed, 13 insertions(+), 19 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index f1141bc61..5e957a92c 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -469,7 +469,8 @@ def construct_symmetrisation_for_blocks(self): def symmetrise_generic_adc_doubles(invec): # doubles part is antisymmetric wrt. (i,j,a,b) <-> (i,j,b,a) # doubles part is antisymmetric wrt. (i,j,a,b) <-> (j,i,a,b) - scratch = invec.antisymmetrise([(0, 1)]).antisymmetrise([(2, 3)]) + scratch = invec.antisymmetrise([(0, 1)]).antisymmetrise( + [(2, 3)]) # doubles part is symmetric wrt. (i,j,a,b) <-> (j,i,b,a) return scratch.symmetrise([(0, 1), (2, 3)]) ret["pphh"] = symmetrise_generic_adc_doubles @@ -478,14 +479,16 @@ def symmetrise_generic_adc_triples(invec): # triples part is antisymmetric wrt. permutations of (i,j,k) # and wrt. permutations of (a,b,c) scratch = ( - invec.antisymmetrise([(0, 1, 2)]).antisymmetrise([(3, 4, 5)]) + invec.antisymmetrise([(0, 1, 2)]).antisymmetrise( + [(3, 4, 5)]) ) # triples part is symmetric wrt. permutations of (i,j,k) and # permutations of (a,b,c) # NOTE: The doubles fix the (numerical) symmetry with a single # symmetrise call. This is not possible for triples, since the # following symmetrise call only covers 6 of the 18 - # even permutations generated by the 2 antisymmetrise calls above: + # even permutations generated by the 2 antisymmetrise calls + # above: # ijkabc + ikjacb + jikbac + jkibca + kijcab + kjicba return scratch.symmetrise([(0, 1, 2), (3, 4, 5)]) ret["ppphhh"] = symmetrise_generic_adc_triples diff --git a/adcc/AdcMethod.py b/adcc/AdcMethod.py index 22098e9bc..c3257a3c4 100644 --- a/adcc/AdcMethod.py +++ b/adcc/AdcMethod.py @@ -236,8 +236,6 @@ def base_method(self: T) -> T: The base (full) method, i.e. with all approximations such as CVS stripped off. """ - # if self.adc_type is not AdcType.PP: - # return self.__class__(self.adc_type.to_str() + "-" + self._base_method) return self.__class__(self._base_method) def at_level(self: T, newlevel: Union[int, str]) -> T: diff --git a/adcc/guess/guess_zero.py b/adcc/guess/guess_zero.py index e0e58dd0c..a3ddf8ba1 100644 --- a/adcc/guess/guess_zero.py +++ b/adcc/guess/guess_zero.py @@ -128,8 +128,8 @@ def guess_symmetry_singles(matrix, spin_change=0, symmetry.spin_blocks_forbidden = ["ab", "ba"] elif ( - matrix.method.adc_type is not AdcType.PP and - matrix.reference_state.restricted + matrix.method.adc_type is not AdcType.PP + and matrix.reference_state.restricted ): # IP- and EA-ADC # attach/detach alpha electron diff --git a/adcc/guess/util.py b/adcc/guess/util.py index 00e0783b2..9ddf59a77 100644 --- a/adcc/guess/util.py +++ b/adcc/guess/util.py @@ -22,7 +22,7 @@ ## --------------------------------------------------------------------- from ..AdcMethod import AdcMethod, AdcType -from typing import Optional, Union +from typing import Optional def determine_spin_change(method: AdcMethod, kind: str, @@ -74,8 +74,8 @@ def estimate_n_guesses(matrix, n_states, n_guesses_per_state=2, n_occ_a = mospaces.n_orbs_alpha(sp_occ) estimate = n_occ_a * n_virt_a if ( - matrix.method.level.to_int() < 2 and - matrix.method.adc_type is not AdcType.PP + matrix.method.level.to_int() < 2 + and matrix.method.adc_type is not AdcType.PP ): # Adjustment for IP- and EA-ADC(0/1) calculations estimate = (n_occ_a if matrix.method.adc_type is AdcType.IP diff --git a/adcc/tests/generators/dump_adcc.py b/adcc/tests/generators/dump_adcc.py index 403eb98fa..716bcd501 100644 --- a/adcc/tests/generators/dump_adcc.py +++ b/adcc/tests/generators/dump_adcc.py @@ -202,8 +202,8 @@ def dump_excited_states( ] = (np.asarray(tdm_bb_b)) # ssq for unrestriced calculation if ( - not states.reference_state.restricted - and not states.ground_state.has_core_occupied_space + not states.reference_state.restricted + and not states.ground_state.has_core_occupied_space ): kind_data["state_ssq"] = states.state_ssq # write the data to hdf5 diff --git a/adcc/tests/guess_test.py b/adcc/tests/guess_test.py index 3954b1110..7b9bcfa3f 100644 --- a/adcc/tests/guess_test.py +++ b/adcc/tests/guess_test.py @@ -62,7 +62,6 @@ def test_determine_spin_change_pp(self): assert spin == -1.0 def test_determine_spin_change_ip(self): - method = adcc.AdcMethod("ip-adc2") spin_alpha = determine_spin_change(method, kind="any", is_alpha=True) @@ -75,7 +74,6 @@ def test_determine_spin_change_ip(self): determine_spin_change(method, kind="any", is_alpha=None) def test_determine_spin_change_ea(self): - method = adcc.AdcMethod("ea-adc2") spin_alpha = determine_spin_change(method, kind="any", is_alpha=True) @@ -88,7 +86,6 @@ def test_determine_spin_change_ea(self): determine_spin_change(method, kind="any", is_alpha=None) def test_determine_spin_change_unknown_adc_type(self): - method = adcc.AdcMethod("adc2") method.adc_type = "bla" @@ -96,8 +93,6 @@ def test_determine_spin_change_unknown_adc_type(self): determine_spin_change(method, kind="any") def test_estimate_n_guesses_pp(self): - from adcc.guess import estimate_n_guesses - refstate = testdata_cache.refstate("h2o_sto3g", case="gen") ground_state = adcc.LazyMp(refstate) matrix = adcc.AdcMatrix("adc2", ground_state) @@ -128,8 +123,6 @@ def test_estimate_n_guesses_ip(self): assert 10 == estimate_n_guesses(matrix, n_states=10, singles_only=True) def test_estimate_n_guesses_ea(self): - from adcc.guess import estimate_n_guesses - refstate = testdata_cache.refstate("h2o_sto3g", case="gen") ground_state = adcc.LazyMp(refstate) matrix = adcc.AdcMatrix("ea-adc2", ground_state) From 5deaeda757cfebcbd5c9c7e54c2c8127f9f43b64 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Sat, 20 Jun 2026 17:27:30 +0200 Subject: [PATCH 21/26] adapted function rename --- adcc/ChargedExcitations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adcc/ChargedExcitations.py b/adcc/ChargedExcitations.py index f16bd64d5..1178cbf7e 100644 --- a/adcc/ChargedExcitations.py +++ b/adcc/ChargedExcitations.py @@ -116,7 +116,7 @@ def describe(self, block_norms=True): """ assert (self.matrix.axis_blocks == ["h"] or self.matrix.axis_blocks == ["h", "phh"]) - columns = self.describe_helper( + columns = self._describe_helper( block_norms=block_norms, excitation_type_name="ionization potential") @@ -169,7 +169,7 @@ def describe(self, block_norms=True): """ assert (self.matrix.axis_blocks == ["p"] or self.matrix.axis_blocks == ["p", "pph"]) - columns = self.describe_helper( + columns = self._describe_helper( block_norms=block_norms, excitation_type_name="electron affinity") From 5d7d86137817435fdcbfbd594ced09feb0e79ca1 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Mon, 29 Jun 2026 18:26:10 +0200 Subject: [PATCH 22/26] updated 1st order double diagonals of IP/EA-ADC --- adcc/adc_ea/matrix.py | 15 ++++++++++----- adcc/adc_ip/matrix.py | 22 +++++++++------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/adcc/adc_ea/matrix.py b/adcc/adc_ea/matrix.py index bdd778aa7..21e4e0aa4 100644 --- a/adcc/adc_ea/matrix.py +++ b/adcc/adc_ea/matrix.py @@ -95,20 +95,25 @@ def block_p_p_1(hf, mp, intermediates): def diagonal_pph_pph_1(hf): - # TODO - pass + return AmplitudeVector(pph=( + + 2.0 * direct_sum( + "-ia+b->iab", einsum("iaia->ia", hf.ovov), hf.fvv.diagonal()) + + 1.0 * direct_sum( + "-i+ab->iab", hf.foo.diagonal(), einsum("abab->ab", hf.vvvv)) + ).symmetrise(1, 2) + ) def block_pph_pph_1(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(pph=( - einsum("jab,ij->iab", ampl.pph, hf.foo) - + 2 * einsum("ac,icb->iab", hf.fvv, ampl.pph).antisymmetrise(1, 2) + + 2.0 * einsum("ac,icb->iab", hf.fvv, ampl.pph).antisymmetrise(1, 2) + 0.5 * einsum("abcd,icd->iab", hf.vvvv, ampl.pph) - - 2 * einsum("icka,kcb->iab", hf.ovov, ampl.pph + - 2.0 * einsum("icka,kcb->iab", hf.ovov, ampl.pph ).antisymmetrise(1, 2) )) - return AdcBlock(apply, diagonal_pph_pph_0(hf)) + return AdcBlock(apply, diagonal_pph_pph_1(hf)) # diff --git a/adcc/adc_ip/matrix.py b/adcc/adc_ip/matrix.py index b144215a4..4b779a132 100644 --- a/adcc/adc_ip/matrix.py +++ b/adcc/adc_ip/matrix.py @@ -96,26 +96,22 @@ def block_h_h_1(hf, mp, intermediates): def diagonal_phh_phh_1(hf): - fCC = hf.fcc if hf.has_core_occupied_space else hf.foo - i1 = direct_sum("-i-J+a->iJa", - hf.foo.diagonal(), fCC.diagonal(), hf.fvv.diagonal()) - - # Build Kronecker delta - d_vv = zeros_like(hf.fvv) - d_vv.set_mask("aa", 1.0) - - i2 = einsum("ijij,aa->ija", hf.oooo, d_vv) - res = i1 + i2 - return AmplitudeVector(phh=res.symmetrise(0, 1)) + return AmplitudeVector(phh=( + - 2.0 * direct_sum( + "i+ja->ija", hf.foo.diagonal(), einsum("iaia->ia", hf.ovov)) + + 1.0 * direct_sum( + "ij+a->ija", einsum("ijij->ij", hf.oooo), hf.fvv.diagonal()) + ).symmetrise(0, 1) + ) def block_phh_phh_1(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(phh=( + einsum("ac,ijc->ija", hf.fvv, ampl.phh) - - 2 * einsum("ik,kja->ija", hf.foo, ampl.phh).antisymmetrise(0, 1) + - 2.0 * einsum("ik,kja->ija", hf.foo, ampl.phh).antisymmetrise(0, 1) + 0.5 * einsum("ijkl,kla->ija", hf.oooo, ampl.phh) - - 2 * einsum("kaic,kjc->ija", hf.ovov, ampl.phh + - 2.0 * einsum("kaic,kjc->ija", hf.ovov, ampl.phh ).antisymmetrise(0, 1) )) return AdcBlock(apply, diagonal_phh_phh_1(hf)) From 90b981496d4b54014d4b73a6c1b0769af13f3dce Mon Sep 17 00:00:00 2001 From: fedy9 Date: Mon, 29 Jun 2026 18:26:56 +0200 Subject: [PATCH 23/26] reverted doc change --- adcc/guess/guess_zero.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/adcc/guess/guess_zero.py b/adcc/guess/guess_zero.py index a3ddf8ba1..653e5eab1 100644 --- a/adcc/guess/guess_zero.py +++ b/adcc/guess/guess_zero.py @@ -35,14 +35,14 @@ def guess_zero(matrix, spin_change=0, spin_block_symmetrisation="none"): spin_change The spin change to enforce in an excitation. Typical values are 0 (singlet/triplet/any) and -1 (spin-flip). spin_block_symmetrisation - Symmetrisation to enforce between equivalent spin blocks, - which all yield the desired spin_change. E.g. if - spin_change == 0, then both the alpha->alpha and beta->beta - blocks of the singles part of the excitation vector achieve a - spin change of 0. The symmetry specified with this parameter - will then be imposed between the a-a and b-b blocks. Valid - values are "none", "symmetric" and "antisymmetric", where - "none" enforces no particular symmetry. + Symmetrisation to enforce between equivalent spin blocks, which + all yield the desired spin_change. E.g. if spin_change == 0, + then both the alpha->alpha and beta->beta blocks of the singles + part of the excitation vector achieve a spin change of 0. + The symmetry specified with this parameter will then be imposed + between the a-a and b-b blocks. Valid values are "none", + "symmetric" and "antisymmetric", where "none" enforces + no particular symmetry. """ return AmplitudeVector(**{ block: Tensor(sym) for block, sym in guess_symmetries( @@ -61,14 +61,14 @@ def guess_symmetries(matrix, spin_change=0, spin_block_symmetrisation="none"): spin_change The spin change to enforce in an excitation. Typical values are 0 (singlet/triplet/any) and -1 (spin-flip). spin_block_symmetrisation - Symmetrisation to enforce between equivalent spin blocks, - which all yield the desired spin_change. E.g. if - spin_change == 0, then both the alpha->alpha and beta->beta - blocks of the singles part of the excitation vector achieve a - spin change of 0. The symmetry specified with this parameter - will then be imposed between the a-a and b-b blocks. Valid - values are "none", "symmetric" and "antisymmetric", where - "none" enforces no particular symmetry. + Symmetrisation to enforce between equivalent spin blocks, which + all yield the desired spin_change. E.g. if spin_change == 0, + then both the alpha->alpha and beta->beta blocks of the singles + part of the excitation vector achieve a spin change of 0. + The symmetry specified with this parameter will then be imposed + between the a-a and b-b blocks. Valid values are "none", + "symmetric" and "antisymmetric", where "none" enforces + no particular symmetry. """ if not isinstance(matrix, AdcMatrixlike): raise TypeError("matrix needs to be of type AdcMatrixlike") From 5f783a4bfac849b4f3bf57aee25925bdcf532eec Mon Sep 17 00:00:00 2001 From: fedy9 Date: Mon, 29 Jun 2026 18:27:20 +0200 Subject: [PATCH 24/26] removed describe function test --- adcc/tests/ChargedExcitations_test.py | 29 --------------------------- 1 file changed, 29 deletions(-) diff --git a/adcc/tests/ChargedExcitations_test.py b/adcc/tests/ChargedExcitations_test.py index 2d1063472..16d149d87 100644 --- a/adcc/tests/ChargedExcitations_test.py +++ b/adcc/tests/ChargedExcitations_test.py @@ -41,32 +41,3 @@ def test_ip_ea_basic_interface(system, method, case, kind): # size matches number of excitation vectors assert state.size == len(state.excitation_vector) assert state.size == len(state.excitation_energy) - - -@pytest.mark.parametrize("system,method,case,kind", cases_ip_ea) -def test_ip_ea_describe(system, method, case, kind): - state = testdata_cache.adcc_states( - system=system, - method=method, - case=case, - kind=kind, - is_alpha=True, - ) - - adc_type = AdcMethod(method).adc_type - # Only test restricted alpha - if adc_type is AdcType.IP: - state.spin_change = -0.5 - elif adc_type is AdcType.EA: - state.spin_change = 0.5 - - desc = state.describe() - - assert state.kind in desc.lower() - - if adc_type is AdcType.IP: - assert "ionization" in desc.lower() - assert "detachment" in desc.lower() - elif adc_type is AdcType.EA: - assert "affinity" in desc.lower() - assert "attachment" in desc.lower() From 92f3af035a85329f1c6a2c5b27cd49c66036b654 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Mon, 29 Jun 2026 19:10:13 +0200 Subject: [PATCH 25/26] merged upstream changes --- adcc/tests/generators/dump_adcc.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/adcc/tests/generators/dump_adcc.py b/adcc/tests/generators/dump_adcc.py index 716bcd501..b408b59c1 100644 --- a/adcc/tests/generators/dump_adcc.py +++ b/adcc/tests/generators/dump_adcc.py @@ -119,7 +119,7 @@ def _pp_adc_properties(states: ExcitedStates, kind_data: dict, n_states: int kind_data["ground_to_excited_tdm_bb_b"] = np.asarray(tdm_bb_b) -def _ip_ea_adc_properties(states: Union[DetachedStates, AttachedStates], +def _ip_ea_adc_properties(states: DetachedStates | AttachedStates, kind_data: dict, n_states: int) -> None: """Collects all properties that are unique for IP/EA-ADC """ @@ -127,12 +127,16 @@ def _ip_ea_adc_properties(states: Union[DetachedStates, AttachedStates], def dump_excited_states( - states: Union[ExcitedStates, DetachedStates, AttachedStates], - hdf5_file: h5py.Group, dump_nstates: int = None) -> None: + states: ExcitedStates | DetachedStates | AttachedStates, + hdf5_file: h5py.Group, only_full_mode: bool, + dump_nstates: int = None) -> None: """ Dump the (charged) excited states data to the given hdf5 file/group. The number of states to dump can be given by dump_nstates. By default all states are dumped. + The only_full_mode flag indicates whether the underlying test case is only + run in full mode. Typically tests that run in full mode are larger + and therefore not all test data might be dumped in that case. """ # ensure that the calculation converged on a nonzero result assert states.converged @@ -171,6 +175,9 @@ def dump_excited_states( kind_data["eigenvectors_singles"] = np.asarray(eigenvectors[1]) if 2 in eigenvectors: kind_data["eigenvectors_doubles"] = np.asarray(eigenvectors[2]) + # only dump triples for small systems + if 3 in eigenvectors and not only_full_mode: + kind_data["eigenvectors_triples"] = np.asarray(eigenvectors[3]) if isinstance(states, ExcitedStates): # state and transition dipole moments kind_data["state_dipole_moments"] = states.state_dipole_moment[:n_states] From 165181a1d9b2c4a4319396a25cb0dbae306067e4 Mon Sep 17 00:00:00 2001 From: fedy9 Date: Tue, 30 Jun 2026 09:59:59 +0200 Subject: [PATCH 26/26] testdata, flake8 and excluded AmplitudeVector from block_apply fun --- adcc/AdcMatrix.py | 3 +-- adcc/adc_ea/matrix.py | 6 ++--- adcc/adc_ip/matrix.py | 8 +++---- adcc/tests/data/SHA256SUMS | 36 +++++++++++++++--------------- adcc/tests/generators/dump_adcc.py | 2 -- 5 files changed, 24 insertions(+), 31 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 5e957a92c..a0643881b 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -396,8 +396,7 @@ def block_apply(self, block, tensor): with another AmplitudeVector or Tensor. Non-matching blocks in the AmplitudeVector will be ignored. """ - # TODO: Allow AmplitudeVector? - if not isinstance(tensor, (libadcc.Tensor, AmplitudeVector)): + if not isinstance(tensor, libadcc.Tensor): raise TypeError("tensor should be an adcc.Tensor") with self.timer.record(f"apply/{block}"): diff --git a/adcc/adc_ea/matrix.py b/adcc/adc_ea/matrix.py index 21e4e0aa4..ac2fcd93a 100644 --- a/adcc/adc_ea/matrix.py +++ b/adcc/adc_ea/matrix.py @@ -100,8 +100,7 @@ def diagonal_pph_pph_1(hf): "-ia+b->iab", einsum("iaia->ia", hf.ovov), hf.fvv.diagonal()) + 1.0 * direct_sum( "-i+ab->iab", hf.foo.diagonal(), einsum("abab->ab", hf.vvvv)) - ).symmetrise(1, 2) - ) + ).symmetrise(1, 2)) def block_pph_pph_1(hf, mp, intermediates): @@ -110,8 +109,7 @@ def apply(ampl): - einsum("jab,ij->iab", ampl.pph, hf.foo) + 2.0 * einsum("ac,icb->iab", hf.fvv, ampl.pph).antisymmetrise(1, 2) + 0.5 * einsum("abcd,icd->iab", hf.vvvv, ampl.pph) - - 2.0 * einsum("icka,kcb->iab", hf.ovov, ampl.pph - ).antisymmetrise(1, 2) + - 2.0 * einsum("icka,kcb->iab", hf.ovov, ampl.pph).antisymmetrise(1, 2) )) return AdcBlock(apply, diagonal_pph_pph_1(hf)) diff --git a/adcc/adc_ip/matrix.py b/adcc/adc_ip/matrix.py index 4b779a132..74ca28147 100644 --- a/adcc/adc_ip/matrix.py +++ b/adcc/adc_ip/matrix.py @@ -24,7 +24,7 @@ from collections import namedtuple from adcc import block as b -from adcc.functions import direct_sum, einsum, zeros_like +from adcc.functions import direct_sum, einsum from adcc.Intermediates import Intermediates, register_as_intermediate from adcc.AmplitudeVector import AmplitudeVector @@ -101,8 +101,7 @@ def diagonal_phh_phh_1(hf): "i+ja->ija", hf.foo.diagonal(), einsum("iaia->ia", hf.ovov)) + 1.0 * direct_sum( "ij+a->ija", einsum("ijij->ij", hf.oooo), hf.fvv.diagonal()) - ).symmetrise(0, 1) - ) + ).symmetrise(0, 1)) def block_phh_phh_1(hf, mp, intermediates): @@ -111,8 +110,7 @@ def apply(ampl): + einsum("ac,ijc->ija", hf.fvv, ampl.phh) - 2.0 * einsum("ik,kja->ija", hf.foo, ampl.phh).antisymmetrise(0, 1) + 0.5 * einsum("ijkl,kla->ija", hf.oooo, ampl.phh) - - 2.0 * einsum("kaic,kjc->ija", hf.ovov, ampl.phh - ).antisymmetrise(0, 1) + - 2.0 * einsum("kaic,kjc->ija", hf.ovov, ampl.phh).antisymmetrise(0, 1) )) return AdcBlock(apply, diagonal_phh_phh_1(hf)) diff --git a/adcc/tests/data/SHA256SUMS b/adcc/tests/data/SHA256SUMS index 275c48aee..22e907bda 100644 --- a/adcc/tests/data/SHA256SUMS +++ b/adcc/tests/data/SHA256SUMS @@ -7,12 +7,12 @@ d28c1ffd6a8bc5574db71889b171c5c535c3558fcfd81bb77d26560ebd7bbce8 cn_ccpvdz_adcc 8502e612dcf7b18261df569c12924949c1cd348455048da00f8bef70a28f1d68 cn_ccpvdz_adcc_adc3.hdf5 f9e07576076f1bffd9c439aeec1abb49427eb99366e65d7a0d5ffb9f451c498b cn_ccpvdz_adcc_ea-adc0.hdf5 d8e0843e97317c214b3f3ed8dd1209c2d097df1a202d843d336b1d995733d48b cn_ccpvdz_adcc_ea-adc2.hdf5 -485ff9a90c0ae4ddb56a68dfaa3f733ffa3fdb35ef2e5e443160a2555de3c0a5 cn_ccpvdz_adcc_ea-adc2x.hdf5 -39f71c4399a2d36acb9bf556bec2c403d296b26ff418ce0e8eaae35e596eec0e cn_ccpvdz_adcc_ea-adc3.hdf5 +f8d01a40d3052b87c22f14ab9c643dfb31fbf48d8b0cbad712c57956e972fc85 cn_ccpvdz_adcc_ea-adc2x.hdf5 +7b3ea86eb2b84ba8b69c8c3d985e69fe1fe1c2e40637b480965fdd5651fe98a1 cn_ccpvdz_adcc_ea-adc3.hdf5 843ef518e0cf3e97c88dd5e1658442d4c0c77c0773aa10b66495d1d5986efe5f cn_ccpvdz_adcc_ip-adc0.hdf5 52759d5ce71664e9bb5cb33599f3c069a5fb685e7c14df6b82df897f267b559a cn_ccpvdz_adcc_ip-adc2.hdf5 -57efe521e669d773a1a3676f2c3438e49874a59f7c5036346ba5bf331dc76237 cn_ccpvdz_adcc_ip-adc2x.hdf5 -43eac99f5bdc36f8da695dca9d49f45b5c85caf39f925754ac2a2b82e513a765 cn_ccpvdz_adcc_ip-adc3.hdf5 +5019358f1070122e9b23ce8d551016438b36e84ce91788531da19a2940273c62 cn_ccpvdz_adcc_ip-adc2x.hdf5 +a7f7181990ed30cf38cda0a0b88421672b1bf974fa7fa7d5fb2d1955b3d4e4bc cn_ccpvdz_adcc_ip-adc3.hdf5 cefa3b262340d035749887bc065200c20e67de70625ef6f6ebc022b979921d07 cn_ccpvdz_adcc_mpdata.hdf5 8f8c3c86cb9f4b22f8bcfff10e7092fcec4258fbf2a451e3cd6456b1adb7ba85 cn_ccpvdz_adcman_adc0.hdf5 be3f5e6090ba4d6fef72760e982bc268dc49f870db1afc101af60e08be559423 cn_ccpvdz_adcman_adc1.hdf5 @@ -33,15 +33,15 @@ db27db84b2dea991a4e24b6ce7433f2619769505267bd486723b1b9bd017a5f3 cn_ccpvdz_hfda 08f1ff54122f543cce6fb23d8e594ea58e342693ac94956f35d79781f790b6b9 cn_sto3g_adcc_adc2.hdf5 fe4da3d8dad6fb394329d6d409860641e99465b8d53f0efc880129238dfb4efd cn_sto3g_adcc_adc2x.hdf5 2a041d39ac6d1bbc1bc0365ba4e191a44f2e059c9c706891746500bb09dd2eb6 cn_sto3g_adcc_adc3.hdf5 -535d755aa052698f40e62a80acc59044c6ef29b2d1f5bbace59055fa43edb57c cn_sto3g_adcc_adc4.hdf5 +7e56ecaf37b8d58da2e98519542623466d51c253be8cc189a0281b2b419390f7 cn_sto3g_adcc_adc4.hdf5 ffeae310f386671ef10ba52d637576494bdc6ff2ff7e0f027027064405a044fa cn_sto3g_adcc_ea-adc0.hdf5 f3a17cc0733110ab0bfc7b683cfa042acdf63789cee50447d83fb7d4d677803e cn_sto3g_adcc_ea-adc2.hdf5 -278528ece296da0b4fef7d08a5047db3487bb4210bbb755ebb2304b0a9a7a98f cn_sto3g_adcc_ea-adc2x.hdf5 -65e90b3bddfa7e9b70beb6f4b14f5ce928eba5c97b66faf6926fcaea8cad4c0b cn_sto3g_adcc_ea-adc3.hdf5 +8f79f2c1acfdf7b9789cb0d4c4ca1c62fc4284814c0a89620fe65527c5a8e594 cn_sto3g_adcc_ea-adc2x.hdf5 +a911fec9a9a8ee92e91facdab68be0afa0d6d4c7a725b53b6abf1d21631632ac cn_sto3g_adcc_ea-adc3.hdf5 c850181d00e405c6c97cd1d1340b13a19909482d61c63981c8ceb5c0301dbfcd cn_sto3g_adcc_ip-adc0.hdf5 c14114493b7c977102fa553480e6c10230ab2d0d37a3769a65e5ce15c4f66c97 cn_sto3g_adcc_ip-adc2.hdf5 -0a152dc0b1661d42dc05257847bcbd91702ae9de70bbf6295d086c50cf531f66 cn_sto3g_adcc_ip-adc2x.hdf5 -df0f40da670c7dbed7883e3a7af67b93773101de8f77e170abbdeeec18160579 cn_sto3g_adcc_ip-adc3.hdf5 +87feeadbd2eb2e5dcc0500c7c90f6af89494188efae718e38df48deca679e82b cn_sto3g_adcc_ip-adc2x.hdf5 +940d655a575c48ab26257b445e7a2822f75de3b49cf1d887d1ce164d260a6303 cn_sto3g_adcc_ip-adc3.hdf5 17994daf530ea179f872cd73695a7e492a0cd4c39947ded1c97afb663d90fa58 cn_sto3g_adcc_mpdata.hdf5 f6974049ccf8a732ea584dbbebf608f0e17d28bd5320c0c63e1d1a5db80f54a7 cn_sto3g_adcman_adc0.hdf5 9950c0720546a0b313e755945e7fd0d5462967a4366f60470c4a85f500bffb5f cn_sto3g_adcman_adc1.hdf5 @@ -76,12 +76,12 @@ b579b2e62d4d02c0e5d16ab8b8e30009582152dba63ac12cea77f7dbbd80c29d formaldehyde_s 6157f5d41dec84612de81005b17cdcc531efc74d5da42e1a49d2cb53492f3248 h2o_def2tzvp_adcc_adc3.hdf5 528c73962158a58994c1cfd46ef92f05a34b66ca511cde4842290c0e6da8f2ff h2o_def2tzvp_adcc_ea-adc0.hdf5 fd1d0720f62ff186d5227beb215d25eca9734ac5c132049c718ce38797f7cc18 h2o_def2tzvp_adcc_ea-adc2.hdf5 -74bd9ecc78eef2d6201c98d7cedef1cc0c6adfef61be00e08851def5ec83cebf h2o_def2tzvp_adcc_ea-adc2x.hdf5 -fa33a3ad777cd745b579adeae4dac7b04d5b56764ed2c44a1c7b37eabc2713e2 h2o_def2tzvp_adcc_ea-adc3.hdf5 +40c91d44899604ad3e31558593ce20e0d3bff316bcf231f95f57a51c474f9992 h2o_def2tzvp_adcc_ea-adc2x.hdf5 +3e1f6ea7e08e4c51dba686f730cbaa84d33e5270ddd6e044f32889f1823dcb11 h2o_def2tzvp_adcc_ea-adc3.hdf5 4a0776fb0ac75c0203bc41e9c25e3be5d80e1e5323071fde500fedb84711bd4c h2o_def2tzvp_adcc_ip-adc0.hdf5 0381025325cdcb7b6f63ad6acb5901f5609316c1a85baf8475a7aba94457c337 h2o_def2tzvp_adcc_ip-adc2.hdf5 -0eee0844cd64e9068e177af180bce92147c8296d7f7d42a41a92b03588f98437 h2o_def2tzvp_adcc_ip-adc2x.hdf5 -18659119cb22c676ea5d9245c0bb4d9c5cf1a44f4fe795d995684987ac138a02 h2o_def2tzvp_adcc_ip-adc3.hdf5 +68c1e39d23afbea60a4a4a4fd6c5dec43d4c7304ecd73b51f8b18308b18ca4c2 h2o_def2tzvp_adcc_ip-adc2x.hdf5 +33800f9d0b2a0edec35710bea483631bdfa29582a99f7ed2ef2f54da7a9321e0 h2o_def2tzvp_adcc_ip-adc3.hdf5 96eefd0a6314651f96cb4480a34652fc866984496aa365313b1521680c984fdd h2o_def2tzvp_adcc_mpdata.hdf5 2a88059266ac32a34cc352e1d2f782565317cec8573bdd81c4e4019ecc9adb65 h2o_def2tzvp_adcman_adc0.hdf5 dcf8ae501f7875705191abf754b33cf82f14065db2c95ab84ef3004b70ee9e75 h2o_def2tzvp_adcman_adc1.hdf5 @@ -102,15 +102,15 @@ b56e74c8e71a9ab5ef62d44131c721e8d5ce236f9ed69d82869d702468aff7a8 h2o_sto3g_adcc 18c78aae03b0315cb3fcd932ce1ee1f3040db355ed38a61b46e2142d8e28b8ef h2o_sto3g_adcc_adc2.hdf5 8e17428b0d7e93e91e3ba366f7d2a9aded91a5d8d37b47fcc790c20b5b150872 h2o_sto3g_adcc_adc2x.hdf5 de9a094a6da75d063a7d1a4137347409c7dd2ced6ae1bdd2ebece6b2712506d6 h2o_sto3g_adcc_adc3.hdf5 -750750d58f45c4c0f6243384e589ce84d332bda9ceea12d68503ee14cb2ddb07 h2o_sto3g_adcc_adc4.hdf5 +14b54dcc669aeb76815481e881d47dce8f7b949de2dba3ab6e7108e73ae508e5 h2o_sto3g_adcc_adc4.hdf5 83b8899a0b7851e18b3e246b802a7e8679d91881c3838797ee6fdb596e54f977 h2o_sto3g_adcc_ea-adc0.hdf5 c166cd64e6a4a8eac9f761c133d685646124b291887407b89a31b02e197eafe7 h2o_sto3g_adcc_ea-adc2.hdf5 -7cea6e0a05023696d0c816b6a02056e4ab2f09cfef167ead5db516be79b5f4d0 h2o_sto3g_adcc_ea-adc2x.hdf5 -d27ba85d21f84bd90200f099cc4ab56de2da09dbcd1a3290daf62c10768051e1 h2o_sto3g_adcc_ea-adc3.hdf5 +5badc1e98c937938c5e0cdebdc50fa11dd4b9d3e0f989e02e558ee867df04fb0 h2o_sto3g_adcc_ea-adc2x.hdf5 +c6ec9963d3cf5fad77ed6c57a4af166864c207f8af20189e3583b4f7233e2605 h2o_sto3g_adcc_ea-adc3.hdf5 d2f58d41610467bcda51778d979d9b0d39bcd11146ce7ec6780f7e04134c95ef h2o_sto3g_adcc_ip-adc0.hdf5 9e7d019e831be4c71a9acd50b27794c31ced89b3877d859df43a5e033a2d9fa4 h2o_sto3g_adcc_ip-adc2.hdf5 -76e1892fb59dc93d0346f7f8cd85c1f7c27784115a920c2f746717f0807a575b h2o_sto3g_adcc_ip-adc2x.hdf5 -a40e06f209a0720a5068df06cfc0a05654a5554c54d41c7164ec949ea12dc4b4 h2o_sto3g_adcc_ip-adc3.hdf5 +82982e49f56fe32b8fe60a71fd31a3375e6669bae4830a1ebf6f554b11f88764 h2o_sto3g_adcc_ip-adc2x.hdf5 +d0067822d5061ac91091e303d1ad586a415d7c6190af3abce573fbb3a971d998 h2o_sto3g_adcc_ip-adc3.hdf5 4751e791a2ce1968106ffdb5128984a777554d8d03aef6585ef378efe1f396ef h2o_sto3g_adcc_mpdata.hdf5 cc957d99adaf872877b992860c9f0c9c8337f783b9c3ad20b36694391627188f h2o_sto3g_adcman_adc0.hdf5 4b36ac6cce98cf250ad268dc6e51e849acbd8c9383aa16aff233a2cf04118fcd h2o_sto3g_adcman_adc1.hdf5 diff --git a/adcc/tests/generators/dump_adcc.py b/adcc/tests/generators/dump_adcc.py index b408b59c1..10b9d9c60 100644 --- a/adcc/tests/generators/dump_adcc.py +++ b/adcc/tests/generators/dump_adcc.py @@ -10,8 +10,6 @@ import numpy as np import h5py -from typing import Union - def dump_groundstate(ground_state: LazyMp, hdf5_file: h5py.Group, only_full_mode: bool) -> None: