Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IP/EA-ADC up to 3rd order #168

Open
wants to merge 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a894098
empty ADC-IP matrix file
Aug 24, 2022
0228ea4
IP-ADC equations and modified files to support IP-ADC in adcc.
fedy9 Sep 28, 2022
228e34b
Working IP-ADC(0/1/2) implementation
fedy9 Oct 18, 2022
e67996a
Merge branch 'adc-connect:master' into master
fedy9 Oct 18, 2022
c0c3fc4
Merge branch 'master' of github.com:fedy9/adccAdrian
fedy9 Oct 18, 2022
bbd1aa4
Working EA-ADC(0/1/2) equations
fedy9 Oct 25, 2022
007e95c
libadcc guess setup for EA-ADC doubles
fedy9 Oct 25, 2022
6e4fec9
Merge branch 'adc-connect:master' into master
fedy9 Oct 25, 2022
5682cc4
removed f12 etm intermediate, flake8 adaptations
fedy9 Nov 17, 2022
54b722c
sort out code, minor changes
fedy9 Dec 1, 2022
5d1eb88
added pole strength parameter to ExStates class
fedy9 Dec 2, 2022
e741a77
comment correction
fedy9 Dec 2, 2022
2c2b2ac
IP/EA ADC(3) energies and pole strengths + generalisation of 'AdcMeth…
fedy9 Jul 17, 2023
47402bb
Merge branch 'adc-connect:master' into master
fedy9 Jul 17, 2023
8282d04
Update authors
fedy9 Jul 17, 2023
7f76e3e
corrected ea_adc s2s tdm
fedy9 Oct 16, 2023
34e84a5
Updated validate_state_parameters including tests
fedy9 Oct 20, 2023
2de6a19
updated test_workflow&extended generate_reference
fedy9 Oct 26, 2023
f388516
enforce double symm only if there are doubles
fedy9 Mar 5, 2024
15d726f
renamed intermediates in ip/ea matrix.py
fedy9 Mar 7, 2024
9cfce9a
modified AdcMockStates and ref data for IP/EA
fedy9 Mar 5, 2024
d8ee7cf
enabled ip/ea_adc2x calculations
fedy9 Mar 6, 2024
d898c48
make ip/ea_adc2x known to AdcMethod
fedy9 Mar 6, 2024
c976f79
detect adc level of ip/ea_adc2x
fedy9 Mar 6, 2024
8ec6aa3
PP state_densities tests run again
fedy9 Mar 6, 2024
f753dde
pole strengths cannot be tranformed to ao
fedy9 Mar 6, 2024
bcc05e6
working pp/ip/ea state_densities tests
fedy9 Mar 6, 2024
3181cbb
corrected import line
fedy9 Mar 7, 2024
15e443a
adapted test_ExcitedStates, old test run again
fedy9 Mar 7, 2024
e6a922b
added cn_sto3g ref data + state_density tests
fedy9 Mar 8, 2024
8f5b3c0
alpha/beta ref data generation + con. prop. tests
fedy9 Mar 8, 2024
269f58e
added '.' to imported modules where I removed it before
fedy9 Mar 9, 2024
8e73f1d
removed doubly defined intermediate definitions
fedy9 Mar 11, 2024
6d03562
blacklisted excitation properties w.r.t. adc type
fedy9 Mar 11, 2024
d36f561
corr. exc. prop. blacklist and ip/ea adc2x tests
fedy9 Mar 11, 2024
4029b99
tests for ip/ea single and double guesses
fedy9 Mar 12, 2024
79f14b7
typo in test function
fedy9 May 27, 2024
b52b2f9
reduce code duplication in matrix files
fedy9 Sep 6, 2024
ff1085f
clean up of matrix file for code reusability
fedy9 Sep 6, 2024
3f4f504
added tests against Q-Chem reference
fedy9 Sep 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 46 additions & 18 deletions adcc/AdcMatrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,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
Expand Down Expand Up @@ -73,14 +75,25 @@ class AdcMatrixlike:


class AdcMatrix(AdcMatrixlike):
# Default perturbation-theory orders for the matrix blocks (== standard ADC-PP).
# Default perturbation-theory orders for the matrix blocks
# (== standard ADC-PP).
default_block_orders = {
# ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None),
"adc0": dict(ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501
"adc1": dict(ph_ph=1, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501
"adc2": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=0), # noqa: E501
"adc2x": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=1), # noqa: E501
"adc3": dict(ph_ph=3, ph_pphh=2, pphh_ph=2, pphh_pphh=1), # noqa: E501
"adc0": dict(ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501
"adc1": dict(ph_ph=1, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501
"adc2": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=0), # noqa: E501
"adc2x": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=1), # noqa: E501
"adc3": dict(ph_ph=3, ph_pphh=2, pphh_ph=2, pphh_pphh=1), # noqa: E501
"ip_adc0": dict(h_h=0, h_phh=None, phh_h=None, phh_phh=None),
"ip_adc1": dict(h_h=1, h_phh=None, phh_h=None, phh_phh=None),
"ip_adc2": dict(h_h=2, h_phh=1, phh_h=1, phh_phh=0),
"ip_adc2x": dict(h_h=2, h_phh=1, phh_h=1, phh_phh=1),
"ip_adc3": dict(h_h=3, h_phh=2, phh_h=2, phh_phh=1),
"ea_adc0": dict(p_p=0, p_pph=None, pph_p=None, pph_pph=None),
"ea_adc1": dict(p_p=1, p_pph=None, pph_p=None, pph_pph=None),
"ea_adc2": dict(p_p=2, p_pph=1, pph_p=1, pph_pph=0),
"ea_adc2x": dict(p_p=2, p_pph=1, pph_p=1, pph_pph=1),
"ea_adc3": dict(p_p=3, p_pph=2, pph_p=2, pph_pph=1),
Copy link
Contributor

Choose a reason for hiding this comment

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

See my comment in AdcMethod.py. I think we should consistently use ip-adcn and ea-adcn internally.

}

def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None,
Expand All @@ -96,7 +109,8 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None,
HF reference or MP ground state
block_orders : optional
The order of perturbation theory to employ for each matrix block.
If not set, defaults according to the selected ADC method are chosen.
If not set, defaults according to the selected ADC method are
chosen.
intermediates : adcc.Intermediates or NoneType
Allows to pass intermediates to re-use to this class.
diagonal_precomputed: adcc.AmplitudeVector
Expand All @@ -123,6 +137,12 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None,

self.timer = Timer()
self.method = method
if "ip" in method.name:
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should be handled within the AdcMethod class. Maybe add a adc_type / adc_variant / ... member variable to the class. You can also keep a copy (probably with matching name) on the matrix class for better accessibility.

self.type = "ip"
elif "ea" in method.name:
self.type = "ea"
else:
self.type = "pp"
self.ground_state = hf_or_mp
self.reference_state = hf_or_mp.reference_state
self.mospaces = hf_or_mp.reference_state.mospaces
Expand All @@ -143,27 +163,35 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None,
block_orders = tmp_orders

# Sanity checks on block_orders
valid_blocks = {"pp": ("ph_ph", "ph_pphh", "pphh_ph", "pphh_pphh"),
"ip": ("h_h", "h_phh", "phh_h", "phh_phh"),
"ea": ("p_p", "p_pph", "pph_p", "pph_pph")}
for block in block_orders.keys():
if block not in ("ph_ph", "ph_pphh", "pphh_ph", "pphh_pphh"):
if block not in valid_blocks[self.type]:
raise ValueError(f"Invalid block order key: {block}")
if block_orders["ph_pphh"] != block_orders["pphh_ph"]:
raise ValueError("ph_pphh and pphh_ph should always have "
"the same order")
if block_orders["ph_pphh"] is not None \
and block_orders["pphh_pphh"] is None:
raise ValueError("pphh_pphh cannot be None if ph_pphh isn't.")
if (block_orders[valid_blocks[self.type][1]]
!= block_orders[valid_blocks[self.type][2]]):
raise ValueError(f"{valid_blocks[self.type][1]} and "
f"{valid_blocks[self.type][2]} should always "
"have the same order")
if block_orders[valid_blocks[self.type][1]] is not None \
and block_orders[valid_blocks[self.type][3]] is None:
raise ValueError(f"{valid_blocks[self.type][3]} cannot be None if "
f"{valid_blocks[self.type][1]} isn't.")
self.block_orders = block_orders

# Build the blocks and diagonals
with self.timer.record("build"):
variant = None
if self.is_core_valence_separated:
variant = "cvs"
matrix = {"pp": ppmatrix, "ip": ipmatrix, "ea": eamatrix}
blocks = {
block: ppmatrix.block(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
block: matrix[self.type].block(
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
}
# TODO Rename to self.block in 0.16.0
self.blocks_ph = {bl: blocks[bl].apply for bl in blocks}
Expand Down
26 changes: 16 additions & 10 deletions adcc/AdcMethod.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@

def get_valid_methods():
valid_prefixes = ["cvs"]
valid_bases = ["adc0", "adc1", "adc2", "adc2x", "adc3"]
valid_bases = ["adc0", "adc1", "adc2", "adc2x", "adc3",
"ip_adc0", "ip_adc1", "ip_adc2", "ip_adc2x", "ip_adc3",
"ea_adc0", "ea_adc1", "ea_adc2", "ea_adc2x", "ea_adc3"]
Copy link
Contributor

Choose a reason for hiding this comment

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

Intuitively I would expect ip-adc2 to be a valid method (- vs _). Probably it would be best to accept both (for instance with a replace in AdcMethod.__init__). However, internally we should only use one form. I think ip-adcn to stay consistent with cvs-adcn.


# CVS-IP calculations not yet implemented
ret = valid_bases + [p + "-" + m for p in valid_prefixes
for m in valid_bases]
for m in valid_bases[:5]]
return ret


Expand All @@ -37,15 +40,15 @@ class AdcMethod:
def __init__(self, method):
if method not in self.available_methods:
raise ValueError("Invalid method " + str(method) + ". Only "
+ ",".join(self.available_methods) + " are known.")
+ ",".join(self.available_methods)
+ " are known.")

split = method.split("-")
self.__base_method = split[-1]
split = split[:-1]
self.__base_method = split.pop()
Copy link
Contributor

Choose a reason for hiding this comment

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

I think base_method should just be adc2. The adc variant (pp/ip/ea) should be stored in a separate variable that needs to be taken into account below (at_level and name) to reassemble the correct method string.

self.is_core_valence_separated = "cvs" in split

try:
if self.__base_method == "adc2x":
if self.__base_method in ["adc2x", "ip_adc2x", "ea_adc2x"]:
self.level = 2
else:
self.level = int(self.__base_method[-1])
Expand All @@ -57,10 +60,13 @@ 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)
"""
if self.is_core_valence_separated:
return AdcMethod("cvs-adc" + str(newlevel))
else:
return AdcMethod("adc" + str(newlevel))
try:
int(self.name[-1])
except ValueError:
raise NotImplementedError("This method is not implemented for ADC "
"methods that do not end with a number "
"like 'adc2x'.")
return AdcMethod(self.name[:-1] + str(newlevel))

@property
def name(self):
Expand Down
30 changes: 17 additions & 13 deletions adcc/AmplitudeVector.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ class AmplitudeVector(dict):
def __init__(self, *args, **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)``
"""
if args:
warnings.warn("Using the list interface of AmplitudeVector is "
"deprecated and will be removed in version 0.16.0. Use "
"AmplitudeVector(ph=tensor_singles, pphh=tensor_doubles) "
"instead.")
"deprecated and will be removed in version 0.16.0. "
"Use AmplitudeVector(ph=tensor_singles, "
"pphh=tensor_doubles) instead.")
if len(args) == 1:
super().__init__(ph=args[0])
elif len(args) == 2:
Expand All @@ -54,11 +56,11 @@ def __setattr__(self, key, item):
@property
def blocks(self):
warnings.warn("The blocks attribute will change behaviour in 0.16.0.")
if sorted(self.blocks_ph) == ["ph", "pphh"]:
if sorted(self.blocks_ph) == ["ph", "pphh"] or sorted(self.blocks_ph) == ["h", "phh"]:
return ["s", "d"]
if sorted(self.blocks_ph) == ["pphh"]:
elif sorted(self.blocks_ph) == ["pphh"] or sorted(self.blocks_ph) == ["phh"]:
return ["d"]
elif sorted(self.blocks_ph) == ["ph"]:
elif sorted(self.blocks_ph) == ["ph"] or sorted(self.blocks_ph) == ["h"]:
return ["s"]
elif sorted(self.blocks_ph) == []:
return []
Expand All @@ -76,8 +78,8 @@ def blocks_ph(self):
def __getitem__(self, index):
if index in (0, 1, "s", "d"):
warnings.warn("Using the list interface of AmplitudeVector is "
"deprecated and will be removed in version 0.16.0. Use "
"block labels like 'ph', 'pphh' instead.")
"deprecated and will be removed in version 0.16.0. "
"Use block labels like 'ph', 'pphh' instead.")
if index in (0, "s"):
return self.__getitem__("ph")
elif index in (1, "d"):
Expand All @@ -90,8 +92,8 @@ def __getitem__(self, index):
def __setitem__(self, index, item):
if index in (0, 1, "s", "d"):
warnings.warn("Using the list interface of AmplitudeVector is "
"deprecated and will be removed in version 0.16.0. Use "
"block labels like 'ph', 'pphh' instead.")
"deprecated and will be removed in version 0.16.0. "
"Use block labels like 'ph', 'pphh' instead.")
if index in (0, "s"):
return self.__setitem__("ph", item)
elif index in (1, "d"):
Expand Down Expand Up @@ -144,7 +146,8 @@ def dot(self, other):
if isinstance(other, list):
# Make a list where the first index is all singles parts,
# the second is all doubles parts and so on
return sum(self[b].dot([av[b] for av in other]) for b in self.keys())
return sum(self[b].dot([av[b] for av in other])
for b in self.keys())
else:
return sum(self[b].dot(other[b]) for b in self.keys())

Expand All @@ -164,7 +167,8 @@ def __forward_to_blocks(self, fname, other):
ret = {k: getattr(tensor, fname)(other[k])
for k, tensor in self.items()}
else:
ret = {k: getattr(tensor, fname)(other) for k, tensor in self.items()}
ret = {k: getattr(tensor, fname)(other)
for k, tensor in self.items()}
if any(r == NotImplemented for r in ret.values()):
return NotImplemented
else:
Expand Down
3 changes: 2 additions & 1 deletion adcc/ElectronicTransition.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,8 @@ def rotatory_strength(self):
@property
@mark_excitation_property()
def cross_section(self):
"""List of one-photon absorption cross sections of all computed states"""
"""List of one-photon absorption cross sections of all computed
states"""
# TODO Source?
fine_structure = constants.fine_structure
fine_structure_au = 1 / fine_structure
Expand Down
Loading