diff --git a/adcc/ElectronicTransition.py b/adcc/ElectronicTransition.py
index b9d5cb13..c2f7e4a0 100644
--- a/adcc/ElectronicTransition.py
+++ b/adcc/ElectronicTransition.py
@@ -23,14 +23,13 @@
import warnings
import numpy as np
-from .misc import cached_property
+from .misc import cached_property, requires_module
from .timings import Timer, timed_member_call
from .visualisation import ExcitationSpectrum
from .OneParticleOperator import product_trace
from .AdcMethod import AdcMethod
from scipy import constants
-from matplotlib import pyplot as plt
from .Excitation import mark_excitation_property
from .solver.SolverStateBase import EigenSolverStateBase
@@ -237,6 +236,7 @@ def cross_section(self):
prefac = 2.0 * np.pi ** 2 / fine_structure_au
return prefac * self.oscillator_strength
+ @requires_module("matplotlib")
def plot_spectrum(self, broadening="lorentzian", xaxis="eV",
yaxis="cross_section", width=0.01, **kwargs):
"""One-shot plotting function for the spectrum generated by all states
@@ -265,6 +265,7 @@ def plot_spectrum(self, broadening="lorentzian", xaxis="eV",
gamma parameter. The value should be given in atomic units
and will be converted to the unit of the energy axis.
"""
+ from matplotlib import pyplot as plt
if xaxis == "eV":
eV = constants.value("Hartree energy in eV")
energies = self.excitation_energy * eV
diff --git a/adcc/ExcitedStates.py b/adcc/ExcitedStates.py
index 0e5e45d5..4614da46 100644
--- a/adcc/ExcitedStates.py
+++ b/adcc/ExcitedStates.py
@@ -22,13 +22,12 @@
## ---------------------------------------------------------------------
import warnings
import numpy as np
-import pandas as pd
from adcc import dot
from scipy import constants
from . import adc_pp
-from .misc import cached_property
+from .misc import cached_property, requires_module
from .timings import timed_member_call
from .Excitation import Excitation, mark_excitation_property
from .FormatIndex import (FormatIndexAdcc, FormatIndexBase,
@@ -439,11 +438,13 @@ def describe_amplitudes(self, tolerance=0.01, index_format=None):
ret += separator
return ret[:-1]
+ @requires_module("pandas")
def to_dataframe(self):
"""
Exports the ExcitedStates object as :class:`pandas.DataFrame`.
Atomic units are used for all values.
"""
+ import pandas as pd
propkeys = self.excitation_property_keys
propkeys.extend([k.name for k in self._excitation_energy_corrections])
data = {
diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py
index ae8d4f9c..70e4eb43 100644
--- a/adcc/LazyMp.py
+++ b/adcc/LazyMp.py
@@ -36,7 +36,7 @@
class LazyMp:
def __init__(self, hf):
"""
- Initialise the class dealing with the M/oller-Plesset ground state.
+ Initialise the class dealing with the Møller-Plesset ground state.
"""
if isinstance(hf, libadcc.HartreeFockSolution_i):
hf = ReferenceState(hf)
diff --git a/adcc/__init__.py b/adcc/__init__.py
index 6b74935a..d3ea8257 100644
--- a/adcc/__init__.py
+++ b/adcc/__init__.py
@@ -28,6 +28,7 @@
from .LazyMp import LazyMp
from .Tensor import Tensor
from .Symmetry import Symmetry
+from .MoSpaces import MoSpaces
from .AdcMatrix import AdcBlockView, AdcMatrix
from .AdcMethod import AdcMethod
from .functions import (contract, copy, direct_sum, dot, einsum, empty_like,
@@ -36,6 +37,8 @@
from .memory_pool import memory_pool
from .State2States import State2States
from .ExcitedStates import ExcitedStates
+from .Excitation import Excitation
+from .ElectronicTransition import ElectronicTransition
from .DataHfProvider import DataHfProvider, DictHfProvider
from .ReferenceState import ReferenceState
from .AmplitudeVector import AmplitudeVector
@@ -49,13 +52,14 @@
from .exceptions import InputError
__all__ = ["run_adc", "InputError", "AdcMatrix", "AdcBlockView",
- "AdcMethod", "Symmetry", "ReferenceState",
+ "AdcMethod", "Symmetry", "ReferenceState", "MoSpaces",
"einsum", "contract", "copy", "dot", "empty_like", "evaluate",
"lincomb", "nosym_like", "ones_like", "transpose",
"linear_combination", "zeros_like", "direct_sum",
"memory_pool", "set_n_threads", "get_n_threads", "AmplitudeVector",
"HartreeFockProvider", "ExcitedStates", "State2States",
- "Tensor", "DictHfProvider", "DataHfProvider", "OneParticleOperator",
+ "Excitation", "ElectronicTransition", "Tensor", "DictHfProvider",
+ "DataHfProvider", "OneParticleOperator",
"guesses_singlet", "guesses_triplet", "guesses_any",
"guess_symmetries", "guesses_spin_flip", "guess_zero", "LazyMp",
"adc0", "cis", "adc1", "adc2", "adc2x", "adc3",
diff --git a/adcc/backends/__init__.py b/adcc/backends/__init__.py
index 025e8ae8..8abc3424 100644
--- a/adcc/backends/__init__.py
+++ b/adcc/backends/__init__.py
@@ -24,41 +24,11 @@
import h5py
import warnings
-from pkg_resources import parse_version
+from ..misc import is_module_available
__all__ = ["import_scf_results", "run_hf", "have_backend", "available"]
-def is_module_available(module, min_version=None):
- """Check using importlib if a module is available."""
- import importlib
-
- try:
- mod = importlib.import_module(module)
- except ImportError:
- return False
-
- if not min_version: # No version check
- return True
-
- if not hasattr(mod, "__version__"):
- warnings.warn(
- "Could not check host program {} minimal version, "
- "since __version__ tag not found. Proceeding anyway."
- "".format(module)
- )
- return True
-
- if parse_version(mod.__version__) < parse_version(min_version):
- warnings.warn(
- "Found host program module {}, but its version {} is below "
- "the least required (== {}). This host program will be ignored."
- "".format(module, mod.__version__, min_version)
- )
- return False
- return True
-
-
# Cache for the list of available backends ... cannot be filled right now,
# since this can lead to import loops when adcc is e.g. used from Psi4
__status = dict()
diff --git a/adcc/misc.py b/adcc/misc.py
index b73f22c8..353e0bc7 100644
--- a/adcc/misc.py
+++ b/adcc/misc.py
@@ -20,8 +20,10 @@
## along with adcc. If not, see .
##
## ---------------------------------------------------------------------
+import warnings
import numpy as np
from functools import wraps
+from pkg_resources import parse_version
def cached_property(f):
@@ -115,6 +117,54 @@ def caller(self, fctn=fctn, args=args):
return inner_decorator
+def is_module_available(module, min_version=None):
+ """Check using importlib if a module is available."""
+ import importlib
+
+ try:
+ mod = importlib.import_module(module)
+ except ImportError:
+ return False
+
+ if not min_version: # No version check
+ return True
+
+ if not hasattr(mod, "__version__"):
+ warnings.warn(
+ f"Could not check module {module} minimal version, "
+ "since __version__ tag not found. Proceeding anyway."
+ )
+ return True
+
+ if parse_version(mod.__version__) < parse_version(min_version):
+ warnings.warn(
+ f"Found module {module}, but its version {mod.__version__} is below "
+ f"the least required (== {min_version}). This module will be ignored."
+ )
+ return False
+ return True
+
+
+def requires_module(name, min_version=None):
+ """
+ Decorator to check if the module 'name' is available,
+ throw ModuleNotFoundError on call if not.
+ """
+ def inner(function):
+ def wrapper(*args, **kwargs):
+ fname = function.__name__
+ if not is_module_available(name, min_version):
+ raise ModuleNotFoundError(
+ f"Function '{fname}' needs module {name}, but it was "
+ f"not found. Solve by running 'pip install {name}' or "
+ f"'conda install {name}' on your system."
+ )
+ return function(*args, **kwargs)
+ wrapper.__doc__ = function.__doc__
+ return wrapper
+ return inner
+
+
def assert_allclose_signfix(actual, desired, atol=0, **kwargs):
"""
Call assert_allclose, but beforehand normalise the sign
diff --git a/adcc/visualisation/Spectrum.py b/adcc/visualisation/Spectrum.py
index 84855e1a..70d08016 100644
--- a/adcc/visualisation/Spectrum.py
+++ b/adcc/visualisation/Spectrum.py
@@ -23,7 +23,7 @@
import numpy as np
from . import shapefctns
-from matplotlib import pyplot as plt
+from ..misc import requires_module
class Spectrum:
@@ -127,6 +127,7 @@ def copy(self):
cpy.ylabel = self.ylabel
return cpy
+ @requires_module("matplotlib")
def plot(self, *args, style=None, **kwargs):
"""Plot the Spectrum represented by this class.
@@ -139,6 +140,7 @@ def plot(self, *args, style=None, **kwargs):
types of spectra commonly plotted. Valid are "discrete" and
"continuous". By default no special style is chosen.
"""
+ from matplotlib import pyplot as plt
if style == "discrete":
p = plt.plot(self.x, self.y, "x", *args, **kwargs)
plt.vlines(self.x, 0, self.y, linestyle="dashed",
diff --git a/docs/calculations.rst b/docs/calculations.rst
index 478e3a56..80ebfc3f 100644
--- a/docs/calculations.rst
+++ b/docs/calculations.rst
@@ -324,6 +324,10 @@ obtained using the function ``adcc.get_n_threads()``.
Plotting spectra
----------------
+.. note::
+ For plotting spectra, `Matplotlib `_
+ needs to be installed. See :ref:`optional-dependencies` for details.
+
Having computed a set of ADC excited states as discussed in the
previous sections, these can be visualised
in a simulated absorption spectrum
@@ -354,7 +358,7 @@ as shown in the next example.
state.plot_spectrum()
plt.show()
-This code uses the :func:`adcc.ExcitedStates.plot_spectrum`
+This code uses the :func:`adcc.ElectronicTransition.plot_spectrum`
function and the `Matplotlib `_ package
to produce a plot such as
@@ -364,13 +368,13 @@ In this image crosses represent the actual computed value
for the absorption cross section for the obtained excited states.
To form the actual spectrum (solid blue line) these discrete
peaks are artificially broadened with an empirical broadening parameter.
-Notice, that the :func:`adcc.ExcitedStates.plot_spectrum`
+Notice, that the :func:`adcc.ElectronicTransition.plot_spectrum`
function does only prepare the spectrum inside Matplotlib,
such that ``plt.show()`` needs to be called in order to actuall *see* the plot.
This allows to *simulaneously* plot the spectrum from multiple
calculations in one figure if desired.
-The :func:`adcc.ExcitedStates.plot_spectrum` function takes a number
+The :func:`adcc.ElectronicTransition.plot_spectrum` function takes a number
of parameters to alter the default plotting behaviour:
- **Broadening parameters**: The default broadening can be completely disabled
@@ -401,7 +405,7 @@ of parameters to alter the default plotting behaviour:
See the `Matplotlib documentation `_ for details.
In the same manner, one can model the ECD spectrum of chiral molecules
-with the :func:`adcc.ExcitedStates.plot_spectrum` function. An example
+with the :func:`adcc.ElectronicTransition.plot_spectrum` function. An example
script for obtaining the ECD spectrum of (R)- and (S)-2-methyloxirane with ADC(2) can be
found in the `examples folder `_.
The only difference to plotting a UV/Vis spectrum as shown above is to specify
diff --git a/docs/conf.py b/docs/conf.py
index f16145ef..70cfb16e 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -222,6 +222,9 @@ def determine_tag():
napoleon_numpy_docstring = True
napoleon_include_init_with_doc = True
+# __init__ docstring appears in documentation
+autoclass_content = "init"
+
# Breathe settings
libadcc_doc_dir = (
os.path.abspath(os.path.dirname(__file__))
diff --git a/docs/installation.rst b/docs/installation.rst
index 4d60229d..3333e65e 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -18,6 +18,9 @@ Please get in touch
by `opening an issue `_
if you cannot get adcc to work.
+Some specialty features of adcc require extra dependencies (e.g., Pandas and
+Matplotlib), see :ref:`optional-dependencies` for details.
+
Installing adcc
---------------
@@ -154,6 +157,28 @@ the :ref:`devnotes` will provide
you with some useful pointers to get started.
+.. _optional-dependencies:
+
+Optional dependencies for analysis features
+-------------------------------------------
+
+- **Matplotlib**: Plotting spectra with :func:`adcc.ElectronicTransition.plot_spectrum`
+
+
+- **Pandas**: Export to `pandas.DataFrame` via :func:`adcc.ExcitedStates.to_dataframe`
+
+
+Installation of optional packages
+.................................
+
+- Using pip: Either install optional dependencies directly with adcc via
+ ``pip install adcc[analysis]`` or run manually for each package, e.g., ``pip install matplotlib``
+
+- Using conda: Install each package manually, e.g., ``conda install matplotlib``.
+
+Note that all other core features of adcc still work without
+these packages installed.
+
.. _troubleshooting:
diff --git a/examples/hydrogen_fluoride/psi4_631g_sf_adc2.py b/examples/hydrogen_fluoride/psi4_631g_sf_adc2.py
index 61aa02de..8ed7e367 100755
--- a/examples/hydrogen_fluoride/psi4_631g_sf_adc2.py
+++ b/examples/hydrogen_fluoride/psi4_631g_sf_adc2.py
@@ -14,7 +14,7 @@
no_com
""")
-psi4.set_num_threads(adcc.thread_pool.n_cores)
+psi4.set_num_threads(adcc.get_n_threads())
psi4.core.be_quiet()
psi4.set_options({'basis': "6-31g",
'e_convergence': 1e-14,
diff --git a/examples/hydrogen_fluoride/pyscf_631g_sf_adc2.py b/examples/hydrogen_fluoride/pyscf_631g_sf_adc2.py
index 64629754..4ede8463 100755
--- a/examples/hydrogen_fluoride/pyscf_631g_sf_adc2.py
+++ b/examples/hydrogen_fluoride/pyscf_631g_sf_adc2.py
@@ -14,6 +14,7 @@
scfres = scf.UHF(mol)
scfres.conv_tol = 1e-14
scfres.conv_tol_grad = 1e-10
+scfres.max_cycle = 500
scfres.kernel()
# Run solver and print results
diff --git a/examples/hydrogen_fluoride/pyscf_631g_sf_adc2_dissociation.py b/examples/hydrogen_fluoride/pyscf_631g_sf_adc2_dissociation.py
index f27fc049..831c7281 100755
--- a/examples/hydrogen_fluoride/pyscf_631g_sf_adc2_dissociation.py
+++ b/examples/hydrogen_fluoride/pyscf_631g_sf_adc2_dissociation.py
@@ -26,7 +26,7 @@ def run_spin_flip(distance):
states = adcc.adc2(scfres, n_spin_flip=1)
ene = scfres.energy_tot() + states.ground_state.energy_correction(2)
- return ene + states.eigenvalues[0]
+ return ene + states.excitation_energy[0]
def run_progression(outfile="631g_adc2_dissociation.nptxt"):
diff --git a/examples/neon/test_cg_alpha.py b/examples/neon/test_cg_alpha.py
index 1910ff20..bd448d9e 100644
--- a/examples/neon/test_cg_alpha.py
+++ b/examples/neon/test_cg_alpha.py
@@ -5,20 +5,16 @@
from pyscf import gto, scf
from adcc.solver.preconditioner import JacobiPreconditioner
-from adcc.AmplitudeVector import AmplitudeVector
from adcc.solver import IndexSymmetrisation
from adcc.solver.conjugate_gradient import conjugate_gradient, default_print
-from adcc.modified_transition_moments import compute_modified_transition_moments
+from adcc.adc_pp.modified_transition_moments import modified_transition_moments
class ShiftedMat(adcc.AdcMatrix):
def __init__(self, method, mp_results, omega=0.0):
self.omega = omega
super().__init__(method, mp_results)
- diagonal = AmplitudeVector(*tuple(
- self.diagonal(block) for block in self.blocks
- ))
- self.omegamat = adcc.ones_like(diagonal) * omega
+ self.omegamat = adcc.ones_like(self.diagonal()) * omega
def __matmul__(self, other):
return super().__matmul__(other) - self.omegamat * other
@@ -39,9 +35,8 @@ def __matmul__(self, other):
refstate = adcc.ReferenceState(scfres)
matrix = ShiftedMat("adc3", refstate, omega=0.0)
-rhs = compute_modified_transition_moments(
- matrix, refstate.operators.electric_dipole[0], "adc2"
-)
+rhs = modified_transition_moments("adc2", matrix.ground_state,
+ refstate.operators.electric_dipole[0])
preconditioner = JacobiPreconditioner(matrix)
freq = 0.0
preconditioner.update_shifts(freq)
diff --git a/examples/water/pyscf_ccpvdz_fv_adc3.py b/examples/water/pyscf_ccpvdz_fv_adc3.py
index 41cdc86b..79ddd11c 100755
--- a/examples/water/pyscf_ccpvdz_fv_adc3.py
+++ b/examples/water/pyscf_ccpvdz_fv_adc3.py
@@ -16,15 +16,6 @@
scfres.conv_tol = 1e-13
scfres.kernel()
-#
-# Some more advanced memory tampering options
-#
-# Initialise ADC memory (512 MiB)
-# Use a tensor block size parameter of 16 and
-# a specific allocator (in this case std::allocator)
-adcc.memory_pool.initialise(max_memory=512 * 1024 * 1024,
- tensor_block_size=12, allocator="standard")
-
# Run an adc3 calculation:
singlets = adcc.adc3(scfres, frozen_virtual=3, n_singlets=3)
triplets = adcc.adc3(singlets.matrix, n_triplets=3)
diff --git a/examples/water/pyscf_sto3g_adc2.py b/examples/water/pyscf_sto3g_adc2.py
index 74045d7b..e5ab8bb2 100755
--- a/examples/water/pyscf_sto3g_adc2.py
+++ b/examples/water/pyscf_sto3g_adc2.py
@@ -17,12 +17,6 @@
scfres.conv_tol_grad = 1e-10
scfres.kernel()
-# Explicitly initialise ADC virtual memory pool to (256 MiB)
-# (if this call is missing than only RAM is used. This allows
-# to dump data to disk as well such that maximally up to the
-# specified amount of data (in bytes) will reside in RAM)
-adcc.memory_pool.initialise(max_memory=256 * 1024 * 1024)
-
# Run an adc2 calculation:
singlets = adcc.adc2(scfres, n_singlets=5)
triplets = adcc.adc2(singlets.matrix, n_triplets=3)
diff --git a/examples/water/pyscf_sto3g_uhf_adc2.py b/examples/water/pyscf_sto3g_uhf_adc2.py
index 4bd69312..1c7c44f6 100755
--- a/examples/water/pyscf_sto3g_uhf_adc2.py
+++ b/examples/water/pyscf_sto3g_uhf_adc2.py
@@ -20,16 +20,7 @@
scfres.verbose = 4
scfres.kernel()
-# Explicitly initialise ADC virtual memory pool to (256 MiB)
-# (if this call is missing than only RAM is used. This allows
-# to dump data to disk as well such that maximally up to the
-# specified amount of data (in bytes) will reside in RAM)
-adcc.memory_pool.initialise(max_memory=256 * 1024 * 1024)
-
# Run an adc2 calculation:
singlets = adcc.adc2(scfres, n_states=5)
-# triplets = adcc.adc2(singlets.matrix, n_triplets=3)
print(singlets.describe())
-print()
-# print(triplets.describe())
diff --git a/examples/water/tpa.py b/examples/water/tpa.py
index bf2a975b..842c2489 100644
--- a/examples/water/tpa.py
+++ b/examples/water/tpa.py
@@ -8,7 +8,6 @@
from pyscf import gto, scf
from adcc.solver.preconditioner import JacobiPreconditioner
-from adcc.AmplitudeVector import AmplitudeVector
from adcc.solver import IndexSymmetrisation
from adcc.solver.conjugate_gradient import conjugate_gradient, default_print
from adcc.adc_pp.modified_transition_moments import modified_transition_moments
@@ -20,10 +19,7 @@ class ShiftedMat(adcc.AdcMatrix):
def __init__(self, method, mp_results, omega=0.0):
self.omega = omega
super().__init__(method, mp_results)
- diagonal = AmplitudeVector(*tuple(
- self.diagonal(block) for block in self.blocks
- ))
- self.omegamat = adcc.ones_like(diagonal) * omega
+ self.omegamat = adcc.ones_like(self.diagonal()) * omega
def __matmul__(self, other):
return super().__matmul__(other) - self.omegamat * other
diff --git a/setup.py b/setup.py
index 74b8d5c8..0e3600ee 100755
--- a/setup.py
+++ b/setup.py
@@ -534,15 +534,14 @@ def read_readme():
"opt_einsum >= 3.0",
"numpy >= 1.14",
"scipy >= 1.2",
- "matplotlib >= 3.0",
"h5py >= 2.9",
"tqdm >= 4.30",
- "pandas >= 0.25.0",
],
- tests_require=["pytest", "pytest-cov", "pyyaml"],
+ tests_require=["pytest", "pytest-cov", "pyyaml", "pandas >= 0.25.0"],
extras_require={
"build_docs": ["sphinx>=2", "breathe", "sphinxcontrib-bibtex",
"sphinx-automodapi", "sphinx-rtd-theme"],
+ "analysis": ["matplotlib >= 3.0", "pandas >= 0.25.0"],
},
#
cmdclass={"build_ext": build_ext, "pytest": PyTest,