Source code for strongcoca.response.excitations
import numpy as np
from .base import BaseResponse
from ..types import Array
from ..units import au_to_eV, eV_to_au, au_to_eA, eA_to_au
from .utilities import Broadening, NoArtificialBroadening, broaden
[docs]
class Excitations(BaseResponse):
"""Objects of this class represent discrete excitation spectra.
Parameters
----------
energies
Excitation energies; array with shape {n}.
Units are eV by default, optionally atomic units (see :attr:`units`).
transition_dipole_moments
Transition dipole moments of the excitations; array with shape {n}x{3}.
Units are eÅ by default, optionally atomic units (see :attr:`units`).
broadening
Artificial broadening used for the continuous response;
defaults to no broadening.
units
`eVA` to specify energies in eV and transition_dipole_moments in eÅ
or `au` to specify inputs in atomic units.
This parameter determines whether conversion should be performed during
initialization and has no effect on instance methods and variables.
name
Name of response.
"""
def __init__(self,
energies: np.ndarray,
transition_dipole_moments: np.ndarray,
broadening: Broadening = NoArtificialBroadening(),
units: str = 'eVA',
name: str = 'Excitations') -> None:
super().__init__(broadening=broadening, pbc=False, name=name)
if units == 'eVA':
energies = energies * eV_to_au
transition_dipole_moments = transition_dipole_moments * eA_to_au
elif units != 'au':
raise ValueError(f"units has to be 'eVA' or 'au', not '{units}'")
self._energies = energies
self._transition_dipole_moments = transition_dipole_moments
@property
def energies(self) -> np.ndarray:
"""Excitation energies in units of eV; array with shape {n}."""
return self._energies * au_to_eV # type: ignore
@property
def transition_dipole_moments(self) -> np.ndarray:
"""Transition dipole moments of the excitations in units of eÅ;
array with shape {n}x{3}.
"""
return self._transition_dipole_moments * au_to_eA # type: ignore
@property
def oscillator_strengths(self) -> np.ndarray:
"""Unitless oscillator strengths of the excitations;
array with shape {n}.
"""
osc_nv = self.oscillator_strength_vectors
osc_n = np.average(osc_nv, axis=1)
return osc_n # type: ignore
@property
def oscillator_strength_vectors(self) -> np.ndarray:
"""Unitless oscillator strength vectors of the excitations;
array with shape {n}x3.
"""
omega_n = self._energies
mu_nv = self._transition_dipole_moments
osc_nv = 2 * omega_n[:, np.newaxis] * mu_nv**2
return osc_nv # type: ignore
@property
def oscillator_strength_tensors(self) -> np.ndarray:
"""Unitless oscillator strength tensors of the excitations;
array with shape {n}x3x3.
"""
omega_n = self._energies
mu_nv = self._transition_dipole_moments
osc_nvv = 2 * np.einsum('n,nx,ny->nxy', omega_n, mu_nv, mu_nv, optimize=True)
return osc_nvv # type: ignore
[docs]
def get_dipole_strength_function(self, frequencies: Array) -> np.ndarray:
if isinstance(self._broadening, NoArtificialBroadening):
raise ValueError(
'get_dipole_strength_function requires broadening for discrete excitation '
'spectra. Pass e.g. LorentzianBroadening(0.1) when constructing the '
'response or calculator.')
return super().get_dipole_strength_function(frequencies)
def _get_dynamic_polarizability(self, frequencies: Array) -> np.ndarray:
freq_w = np.asarray(frequencies)
omega_I = self._energies
osc_Ivv = self.oscillator_strength_tensors
dm_wvv = broaden(omega_I, osc_Ivv, freq_w, broadening=self._broadening)
return dm_wvv
def _get_dynamic_polarizability_imaginary_frequency(
self, frequencies: Array) -> np.ndarray:
freq_w = np.asarray(frequencies)
omega_I = self._energies
osc_Ivv = self.oscillator_strength_tensors
dm_wvv = broaden(omega_I, osc_Ivv, freq_w, freq_axis='imag')
return dm_wvv