Skip to content

IP/EA ADC(0-3) implementation without properties - #206

Open
fedy9 wants to merge 29 commits into
adc-connect:masterfrom
fedy9:ip_ea_impl
Open

IP/EA ADC(0-3) implementation without properties#206
fedy9 wants to merge 29 commits into
adc-connect:masterfrom
fedy9:ip_ea_impl

Conversation

@fedy9

@fedy9 fedy9 commented Mar 6, 2026

Copy link
Copy Markdown
Contributor
  • added IP/EA ADC folders like adc_pp
  • extended adc_type attribute of AdcMethod to EA/IP (e.g. method.adc_type="ip"). Accessible wherever it's necessary
  • IP/EA ADC matrices up to 3rd order yielding excitation energies
  • Modified guess setup that separates guess construction from diagonalisation of the AdcMatrix
  • new guess setup for IP/EA: appropriate estimate of guesses, symmetry setup, doubles guess construction on C++ side
  • generalisation of multiple PP specific routines, e.g. the singles block is selected dynamically (p/h/ph) and no longer automatically "ph"
  • New and adapted error handling
  • modified 'validate_state_parameters()' function in workflow to check for valid IP/EA inputs and returns a new Boolean 'is_alpha' which is None for PP and specifies whether an alpha or beta electron is attached/detached for EA/IP. Default value is "True". Added keyword 'n_doublets' specifying the doublet states for IP/EA
  • spin_change calculation was moved to utility function in guess folder
  • New enforce_spin_kind routines for IP/EA
  • New ChargedExcitation class with AttachedStates and DetachedStates in analogy to ExcitedStates
  • modified describe function of ElectronicStates to be compatible with IP/EA and PP states
  • adapted printouts
  • Updated max_spin_change evaluation in guess_zero.py to check against the actual valid values
  • Updated guess tests for degenerate orbital cases based on guess energy rather than exact indices so CN guess tests work now
  • added Hermiticity test for ADC matrix
  • Updated tests and reference data generators, all tests passed

New methods on user side:
ip-adc{0..3}()
ea-adc{0..3}()
New keywords on user side:
n_doublets, is_alpha
e.g. ip_adc3(scf_result, n_doublets=5, is_alpha=True)
run_adc(method="ip-adc2", ...)

Remarks:

No simultaneous calculation of alpha and beta states to simplify the code and error handling and to keep consistency with PP calculations. If alpha and beta would be computed in the same adcc instance, intermediates could be reused.
But they can also be reused by providing the AdcMatrix instead of the HF data

Quartets not yet implemented since they are pure doubles states which are only Hartree-Fock orbital differences.

@frieschneider

Copy link
Copy Markdown
Contributor

Please rebase onto the latest master and ensure the code style CI passes. :)

@frieschneider frieschneider left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked through the Python files, but not yet the C++ ones.

Comment thread adcc/adc_ea/matrix.py Outdated
Comment thread adcc/adc_ea/matrix.py Outdated
Comment thread adcc/adc_ea/matrix.py Outdated
Comment thread adcc/adc_ea/matrix.py Outdated
Comment thread adcc/adc_ea/matrix.py Outdated
Comment thread adcc/tests/guess_test.py Outdated
Comment thread adcc/tests/guess_test.py
Comment thread adcc/tests/guess_test.py
Comment thread adcc/tests/guess_test.py
Comment thread adcc/AdcMethod.py Outdated

@jonasleitner jonasleitner left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went through the python files only. I try to continue with the cpp part once I find the time :)

Comment thread adcc/adc_ea/matrix.py
#
__all__ = ["block"]

AdcBlock = namedtuple("AdcBlock", ["apply", "diagonal"])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like defining AdcBlock three times. Maybe we can add the definition to block.py or AdcMatrix.py and import it for PP, IP, and EA. Also we probably want it rather to be a dataclass.

Comment thread adcc/adc_ea/matrix.py
#
def block_p_p_2(hf, mp, intermediates):
# Intermediate can be found in 'adc_pp/matrix.py'
i1 = intermediates.adc2_i1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think adc2_i1 (and adc2_i2) should be renamed to ea/ip_adc2_m11 and moved to the ip/ea matrix.py

Comment thread adcc/adc_ea/matrix.py
# 3rd order main
#
def block_p_p_3(hf, mp, intermediates):
i1 = intermediates.adc3_ea_i1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename the intermediate to ea_adc3_m11

Comment thread adcc/adc_ea/matrix.py
Comment on lines +197 to +203
@register_as_intermediate
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)
+ 2 * einsum("iacb,ic->ab", hf.ovvv, p0.ov)
+ einsum("acbd,cd->ab", hf.vvvv, p0.vv)).symmetrise()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this definition should be implemented on the GroundState class similar to sigma_inf_ov.

Comment thread adcc/adc_ea/util.py
Comment on lines +37 to +39
raise ValueError("ADC(0) level and "
"beyond expects an excitation amplitude with a "
"singles part.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels weird to duplicate like all the code from adc_pp.util.py twice. Maybe we can avoid that and use one common implementation that lives somewhere else - not in the adc_pp, adc_ip, adc_ea modules?

Comment thread adcc/workflow.py
Comment on lines +505 to +506
else:
n_guesses_per_state = 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be an explicit check for lanczos with an else that raises an exception

Comment thread adcc/workflow.py
n_states: int,
kind: str,
spin_change: float,
n_guesses: int,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
n_guesses: int,
n_guesses: Optional[int],

Comment thread adcc/workflow.py
Comment on lines +522 to +526
matrix : AdcMatrix
n_states : int
guesses : list[AmplitudeVector]
A list of guess vectors
kind : str

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add the remaining arguments :)

Comment thread adcc/workflow.py
Comment on lines +574 to 576
kstr = ""
if kind != "any":
kstr = " " + kind

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about removing the space here 😅
It would be way easier and cleaner to collect all required information in a list anyway and simply use " ".join for the formatting.

Comment on lines +1 to +43
#!/usr/bin/env python3
import pytest

from adcc.AdcMethod import AdcMethod, AdcType
from adcc.ChargedExcitations import DetachedStates, AttachedStates

from .testdata_cache import testdata_cache


cases_ip_ea = [
("h2o_sto3g", "ip-adc2", "gen", "doublet"),
("h2o_sto3g", "ea-adc2", "gen", "doublet"),
]


@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 is AdcType.IP:
state = testdata_cache.adcc_states(
system=system,
method=method,
case=case,
kind=kind,
is_alpha=True,
)
assert isinstance(state, DetachedStates)
elif adc_type is AdcType.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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get the purpose of this test. I think we can remove this file?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants