Source code for quantarhei.builders.aggregate_spectroscopy
"""Class comprising the aggregate methods for support of spectroscopic simulations
Class Details
-------------
"""
from __future__ import annotations
from typing import Any
import numpy
from ..core.managers import eigenbasis_of
from ..exceptions import ImplementationError, QuantarheiError
from ..qm.liouvillespace.supopunity import SOpUnity
from ..spectroscopy import diagramatics as diag
from .aggregate_base import AggregateBase
[docs]
class AggregateSpectroscopy(AggregateBase):
"""Class comprising the aggregate methods for support of spectroscopic simulations"""
########################################################################
#
# SPECTROSCOPY
#
########################################################################
[docs]
def liouville_pathways_3(
self,
ptype: str | tuple | list = "R3g",
dtol: float = 0.01,
ptol: float = 1.0e-3,
lab: object = None,
verbose: int = 0,
) -> list:
"""Generator of Liouville pathways"""
ham = self.get_Hamiltonian()
self.lab = lab
return self.liouville_pathways_3T(
ptype,
dtol=dtol,
ptol=ptol,
lab=lab,
eUt=SOpUnity(dim=ham.dim),
verbose=verbose,
)
[docs]
def liouville_pathways_3T(
self,
ptype: str | tuple | list = "R3g",
eUt: Any = None,
ham: Any = None,
t2: float = 0.0,
dtol: float = 1.0e-12,
ptol: float = 1.0e-3,
etol: float = 1.0e-6,
verbose: int = 0,
lab: Any = None,
) -> list:
"""Generator of Liouville pathways with energy transfer
Parameters
----------
ptype : tuple, list, str
List of strings or a string representing one or more
Liouville pathway types that are to be calculated
eUt : EvolutionSuperOperator
Evolution superoperator representing the energy
transfer in the system
t2 : float
Waiting time at which the spectrum is calculated
dtol : float
Minimum acceptable strength of the transition from ground
to excited state, relative to the maximum dipole strength
available in the system
ptol : float
Minimum acceptable population of the ground state (e.g. states
not thermally populated are excluded)
lab : LaboratorySetup
Object representing laboratory setup - number of pulses,
polarization etc.
Returns
-------
lst : list
List of LiouvillePathway objects
"""
self.lab = lab
if self._diagonalized:
if verbose > 0:
print("Diagonalizing aggregate")
self.diagonalize()
if verbose > 0:
print("..done")
pop_tol = ptol
dip_tol = numpy.sqrt(self.D2_max) * dtol
evf_tol = etol
# Check if the ptype is a tuple
ptype_tuple: Any
if not isinstance(ptype, (tuple, list)):
ptype_tuple = (ptype,)
else:
ptype_tuple = ptype
lst: list[Any] = []
if verbose > 0:
print("Pathways", ptype_tuple)
#
# data of the evolution superoperator in eigenstate basis
#
try:
# either the eUt is a complete evolution superoperator
eUt2 = eUt.at(t2)
#
# TODO: Check this part I had before without eigenbasis of and used the evolution superoperator values directly
# ----------------------------------------------
eUt2_dat = numpy.zeros(eUt2.data.shape, dtype=eUt2.data.dtype) #
HH = eUt.get_Hamiltonian() #
with eigenbasis_of(HH): #
eUt2_dat[:, :, :, :] = eUt2.data #
# ----------------------------------------------
except AttributeError:
# or it is only a super operator at a given time t2
# in this case 'ham' must be specified
eUt2 = eUt
eUt2_dat = numpy.zeros(eUt2.data.shape, dtype=eUt2.data.dtype)
with eigenbasis_of(ham):
eUt2_dat[:, :, :, :] = eUt2.data
for ptp in ptype_tuple:
if ptp == "R1g":
generate_R1g(self, lst, eUt2_dat, pop_tol, dip_tol, evf_tol, verbose)
elif ptp == "R2g":
generate_R2g(self, lst, eUt2_dat, pop_tol, dip_tol, evf_tol, verbose)
elif ptp == "R3g":
generate_R3g(self, lst, eUt2_dat, pop_tol, dip_tol, verbose)
elif ptp == "R4g":
generate_R4g(self, lst, eUt2_dat, pop_tol, dip_tol, verbose)
elif ptp == "R1f*":
generate_R1f(self, lst, eUt2_dat, pop_tol, dip_tol, evf_tol, verbose)
elif ptp == "R2f*":
generate_R2f(self, lst, eUt2_dat, pop_tol, dip_tol, evf_tol, verbose)
elif ptp == "R1gE":
generate_R1gE(self, lst, eUt2_dat, pop_tol, dip_tol, evf_tol, verbose)
elif ptp == "R2gE":
generate_R2gE(self, lst, eUt2_dat, pop_tol, dip_tol, evf_tol, verbose)
elif ptp == "R1f*E":
generate_R1fE(self, lst, eUt2_dat, pop_tol, dip_tol, evf_tol, verbose)
elif ptp == "R2f*E":
generate_R2fE(self, lst, eUt2_dat, pop_tol, dip_tol, evf_tol, verbose)
else:
raise QuantarheiError("Unknown pythway type: " + str(ptp))
if lab is not None:
for l in lst:
l.orientational_averaging(lab)
return lst
[docs]
def liouville_pathways_1(
self,
eUt: object = None,
ham: object = None,
dtol: float = 0.01,
ptol: float = 1.0e-3,
etol: float = 1.0e-6,
verbose: int = 0,
lab: object = None,
) -> list:
"""Generator of the first order Liouville pathways
Generator of the pathways for an absorption spectrum
calculation.
Parameters
----------
eUt : EvolutionSuperOperator
Evolution superoperator representing the evolution of optical
coherence in the system
dtol : float
Minimum acceptable strength of the transition from ground
to excited state, relative to the maximum dipole strength
available in the system
ptol : float
Minimum acceptable population of the ground state (e.g. states
not thermally populated are excluded)
lab : LaboratorySetup
Object representing laboratory setup - number of pulses,
polarization etc.
Returns
-------
lst : list
List of LiouvillePathway objects
"""
if self._diagonalized:
if verbose > 0:
print("Diagonalizing aggregate")
self.diagonalize()
if verbose > 0:
print("..done")
pop_tol = ptol
dip_tol = numpy.sqrt(self.D2_max) * dtol
evf_tol = etol
if eUt is None:
# secular absorption spectrum calculation
eUt2_dat = None
sec = True
else:
raise ImplementationError("Not implemented yet")
lst: list[Any] = []
if sec:
generate_1orderP_sec(self, lst, pop_tol, dip_tol, verbose)
else:
raise ImplementationError("Not implemented yet")
if lab is not None:
for l in lst:
l.orientational_averaging(lab)
return lst
[docs]
def generate_R1g(
self: Any,
lst: list,
eUt2: numpy.ndarray,
pop_tol: float,
dip_tol: float,
evf_tol: float,
verbose: int = 0,
) -> None:
ngs = self.get_electronic_groundstate()
nes = self.get_excitonic_band(band=1)
ver = verbose
if verbose > 0:
print("Liouville pathway R1g")
print("Population tolerance: ", pop_tol)
print("Dipole tolerance: ", dip_tol)
print("Evolution amplitude: ", evf_tol)
k = 0
l = 0
for i1g in ngs:
if verbose > 0:
print("Ground state: ", i1g, "of", len(ngs))
# Only thermally allowed starting states are considered
if self.rho0[i1g, i1g] > pop_tol:
for i2e in nes:
if verbose > 1:
print("Excited state: ", i2e, "of", len(nes))
if self.D2[i2e, i1g] > dip_tol:
for i3e in nes:
if self.D2[i3e, i1g] > dip_tol:
for i2d in nes:
for i3d in nes:
evf = eUt2[i2d, i3d, i2e, i3e]
if abs(evf) > evf_tol:
for i4g in ngs:
if (self.D2[i4g, i3d] > dip_tol) and (
self.D2[i4g, i2d] > dip_tol
):
l += 1
lp = _generate_R1g(
self,
i1g,
i2e,
i3e,
i2d,
i3d,
i4g,
evf,
verbose=ver,
)
lp.build()
lst.append(lp)
k += 1
def _generate_R1g(
self: Any,
i1g: int,
i2e: int,
i3e: int,
i2d: int,
i3d: int,
i4g: int,
evf: complex,
verbose: int = 0,
) -> Any:
# Diagram R1g
#
#
# |g_i4> <g_i4|
# <----|-----------|
# |d_i2> <g_i4|
# |-----------|---->
# |d_i2> <d_i3|
# |***********|
# |e_i2> <e_i3|
# |-----------|<----
# |e_i2> <g_i1|
# ---->|-----------|
# |g_i1> <g_i1|
try:
if verbose > 5:
print(" * Generating R1g", i1g, i2e, i3e)
lp = diag.liouville_pathway(
"NR", i1g, aggregate=self, order=3, pname="R1g", popt_band=1, relax_order=1
)
# first transition lineshape
width1 = self.get_transition_width((i2e, i1g))
deph1 = self.get_transition_dephasing((i2e, i1g))
# third transition lineshape
width3 = self.get_transition_width((i2d, i4g))
deph3 = self.get_transition_dephasing((i2d, i4g))
# |g_i1> <g_i1|
lp.add_transition((i2e, i1g), +1, interval=1, width=width1, deph=deph1)
# |e_i2> <g_i1|
lp.add_transition((i3e, i1g), -1)
# |e_i2> <e_i3|
lp.add_transfer(((i2d, i3d)), (i2e, i3e))
lp.set_evolution_factor(evf)
# |d_i2> <d_i3|
lp.add_transition((i4g, i3d), -1)
# |d_i2> <g_i4|
lp.add_transition((i4g, i2d), +1, interval=3, width=width3, deph=deph3)
# |g_i4> <g_i4|
except Exception:
raise QuantarheiError("Pathway generation failed")
return lp
[docs]
def generate_R1gE(
self: Any,
lst: list,
eUt2: numpy.ndarray,
pop_tol: float,
dip_tol: float,
evf_tol: float,
verbose: int = 0,
) -> None:
ngs = self.get_electronic_groundstate()
nes = self.get_excitonic_band(band=1)
if verbose > 0:
print("Liouville pathway R1g_ETICS")
print("Population tolerance: ", pop_tol)
print("Dipole tolerance: ", dip_tol)
print("Evolution amplitude: ", evf_tol)
k = 0
l = 0
for i1g in ngs:
if verbose > 0:
print("Ground state: ", i1g, "of", len(ngs))
# Only thermally allowed starting states are considered
if self.rho0[i1g, i1g] > pop_tol:
for i2e in nes:
if verbose > 1:
print("Excited state: ", i2e, "of", len(nes))
if self.D2[i2e, i1g] > dip_tol:
for i3e in nes:
if self.D2[i3e, i1g] > dip_tol:
for i4g in ngs:
for i5g in ngs:
evf = eUt2[i4g, i5g, i2e, i3e]
if abs(evf) > evf_tol:
for i6e in nes:
if (self.D2[i4g, i6e] > dip_tol) and (
self.D2[i5g, i6e] > dip_tol
):
# if ((self.D2[i5g,i2e] > dip_tol)
# and (self.D2[i5g,i3e] > dip_tol)):
l += 1
# Diagram R1g_ETICS
# (Compensates R3g)
#
#
# |g_i5> <g_i5|
# <----|-----------|
# |e_i6> <g_i5|
# ---->|-----------|
# |g_i4> <g_i5|
# |***********|
# |e_i2> <e_i3|
# |-----------|<----
# |e_i2> <g_i1|
# ---->|-----------|
# |g_i1> <g_i1|
try:
if verbose > 5:
print(
" * Generating R1g_ETICS",
i1g,
i2e,
i3e,
)
lp = diag.liouville_pathway(
"NR",
i1g,
aggregate=self,
order=3,
pname="R1gE",
popt_band=1,
relax_order=1,
)
# first transition lineshape
width1 = self.get_transition_width(
(i2e, i1g)
)
deph1 = (
self.get_transition_dephasing(
(i2e, i1g)
)
)
# third transition lineshape
width3 = self.get_transition_width(
(i6e, i4g)
)
deph3 = (
self.get_transition_dephasing(
(i6e, i4g)
)
)
# |g_i1> <g_i1|
lp.add_transition(
(i2e, i1g),
+1,
interval=1,
width=width1,
deph=deph1,
)
# |e_i2> <g_i1|
lp.add_transition((i3e, i1g), -1)
# |e_i2> <e_i3|
lp.add_transfer(
((i4g, i5g)), (i2e, i3e)
)
lp.set_evolution_factor(evf)
lp.add_transition((i6e, i4g), +1)
# |e_i6> <g_i4|
lp.add_transition(
(i5g, i6e),
+1,
interval=3,
width=width3,
deph=deph3,
)
# |g_i4> <g_i4|
except Exception:
raise QuantarheiError()
break
lp.build()
lst.append(lp)
k += 1
[docs]
def generate_R2g(
self: Any,
lst: list,
eUt2: numpy.ndarray,
pop_tol: float,
dip_tol: float,
evf_tol: float,
verbose: int = 0,
) -> None:
ngs = self.get_electronic_groundstate()
nes = self.get_excitonic_band(band=1)
if verbose > 0:
print("Liouville pathway R2g")
print("Population tolerance: ", pop_tol)
print("Dipole tolerance: ", dip_tol)
print("Evolution amplitude: ", evf_tol)
k = 0
l = 0
for i1g in ngs:
if verbose > 0:
print("Ground state: ", i1g, "of", len(ngs))
# Only thermally allowed starting states are considered
if self.rho0[i1g, i1g] > pop_tol:
for i2e in nes:
if verbose > 1:
print("Excited state: ", i2e, "of", len(nes))
if self.D2[i2e, i1g] > dip_tol:
for i3e in nes:
if self.D2[i3e, i1g] > dip_tol:
for i3d in nes:
for i2d in nes:
evf = eUt2[i3d, i2d, i3e, i2e]
if abs(evf) > evf_tol:
for i4g in ngs:
if (self.D2[i4g, i2e] > dip_tol) and (
self.D2[i4g, i3e] > dip_tol
):
l += 1
# Diagram R2g
#
#
# |g_i4> <g_i4|
# <----|-----------|
# |d_i3> <g_i4|
# |-----------|---->
# |d_i3> <d_i2|
# |***********|
# |e_i3> <e_i2|
# ---->|-----------|
# |g_i1> <e_i2|
# |-----------|<----
# |g_i1> <g_i1|
try:
if verbose > 5:
print(
" * Generating R2g",
i1g,
i2e,
i3e,
)
lp = diag.liouville_pathway(
"R",
i1g,
aggregate=self,
order=3,
pname="R2g",
popt_band=1,
relax_order=1,
)
# first transition lineshape
width1 = self.get_transition_width(
(i2e, i1g)
)
deph1 = (
self.get_transition_dephasing(
(i2e, i1g)
)
)
# third transition lineshape
width3 = self.get_transition_width(
(i3d, i4g)
)
deph3 = (
self.get_transition_dephasing(
(i3d, i4g)
)
)
# |g_i1> <g_i1|
lp.add_transition(
(i2e, i1g),
-1,
interval=1,
width=width1,
deph=deph1,
)
# |g_i1> <e_i2|
lp.add_transition((i3e, i1g), +1)
# |e_i3> <e_i2|
lp.add_transfer(
((i3d, i2d)), (i3e, i2e)
)
lp.set_evolution_factor(evf)
lp.add_transition((i4g, i2d), -1)
# |e_i3> <g_i4|
lp.add_transition(
(i4g, i3d),
+1,
interval=3,
width=width3,
deph=deph3,
)
# |g_i4> <g_i4|
except Exception:
raise QuantarheiError()
break
lp.build()
lst.append(lp)
k += 1
[docs]
def generate_R2gE(
self: Any,
lst: list,
eUt2: numpy.ndarray,
pop_tol: float,
dip_tol: float,
evf_tol: float,
verbose: int = 0,
) -> None:
ngs = self.get_electronic_groundstate()
nes = self.get_excitonic_band(band=1)
if verbose > 0:
print("Liouville pathway R2g_ETICS")
print("Population tolerance: ", pop_tol)
print("Dipole tolerance: ", dip_tol)
print("Evolution amplitude: ", evf_tol)
k = 0
l = 0
for i1g in ngs:
if verbose > 0:
print("Ground state: ", i1g, "of", len(ngs))
# Only thermally allowed starting states are considered
if self.rho0[i1g, i1g] > pop_tol:
for i2e in nes:
if verbose > 1:
print("Excited state: ", i2e, "of", len(nes))
if self.D2[i2e, i1g] > dip_tol:
for i3e in nes:
if self.D2[i3e, i1g] > dip_tol:
for i4g in ngs:
for i5g in ngs:
evf = eUt2[i4g, i5g, i3e, i2e]
if verbose > 4:
print(
"Evolution factor", i4g, i5g, i3e, i2e, evf
)
if abs(evf) > evf_tol:
for i6e in nes:
if (self.D2[i4g, i6e] > dip_tol) and (
self.D2[i5g, i6e] > dip_tol
):
# if ((self.D2[i5g,i2e] > dip_tol)
# and (self.D2[i5g,i3e] > dip_tol)):
l += 1
# Diagram R2g_ETICS
# (Compensates R3g)
#
#
# |g_i5> <g_i5|
# <----|-----------|
# |e_i6> <g_i5|
# ---->|-----------|
# |g_i4> <g_i5|
# |***********|
# |e_i3> <e_i2|
# ---->|-----------|
# |g_i1> <e_i2|
# |-----------|<----
# |g_i1> <g_i1|
try:
if verbose > 5:
print(
" * Generating R2g_ETICS",
i1g,
i2e,
i3e,
)
lp = diag.liouville_pathway(
"R",
i1g,
aggregate=self,
order=3,
pname="R2gE",
popt_band=1,
relax_order=1,
)
# first transition lineshape
width1 = self.get_transition_width(
(i2e, i1g)
)
deph1 = (
self.get_transition_dephasing(
(i2e, i1g)
)
)
# third transition lineshape
width3 = self.get_transition_width(
(i6e, i4g)
)
deph3 = (
self.get_transition_dephasing(
(i6e, i4g)
)
)
# |g_i1> <g_i1|
lp.add_transition(
(i2e, i1g),
-1,
interval=1,
width=width1,
deph=deph1,
)
# |g_i1> <e_i2|
lp.add_transition((i3e, i1g), +1)
# |e_i3> <e_i2|
lp.add_transfer(
((i4g, i5g)), (i3e, i2e)
)
lp.set_evolution_factor(evf)
lp.add_transition((i6e, i4g), +1)
# |e_i3> <g_i4|
lp.add_transition(
(i5g, i6e),
+1,
interval=3,
width=width3,
deph=deph3,
)
# |g_i4> <g_i4|
except Exception:
raise QuantarheiError()
break
lp.build()
lst.append(lp)
k += 1
[docs]
def generate_R3g(
self: Any,
lst: list,
eUt2: numpy.ndarray,
pop_tol: float,
dip_tol: float,
verbose: int = 0,
) -> None:
ngs = self.get_electronic_groundstate()
nes = self.get_excitonic_band(band=1)
if verbose > 0:
print("Liouville pathway R3g")
print("Population tolerance: ", pop_tol)
print("Dipole tolerance: ", dip_tol)
k = 0
l = 0
for i1g in ngs:
if verbose > 0:
print("Ground state: ", i1g, "of", len(ngs))
# Only thermally allowed starting states are considered
if self.rho0[i1g, i1g] > pop_tol:
for i2e in nes:
if verbose > 1:
print("Excited state: ", i2e, "of", len(nes))
if self.D2[i2e, i1g] > dip_tol:
for i3g in ngs:
if self.D2[i3g, i2e] > dip_tol:
evf = eUt2[i1g, i3g, i1g, i3g]
for i4e in nes:
if (self.D2[i4e, i1g] > dip_tol) and (
self.D2[i3g, i4e] > dip_tol
):
l += 1
# Diagram R3g
#
#
# |g_i3> <g_i3|
# <----|-----------|
# |e_i4> <g_i3|
# ---->|-----------|
# |g_i1> <g_i3|
# |-----------|---->
# |g_i1> <e_i2|
# |-----------|<----
# |g_i1> <g_i1|
try:
if verbose > 5:
print(" * Generating R3g", i1g, i2e)
lp = diag.liouville_pathway(
"R",
i1g,
aggregate=self,
order=3,
pname="R3g",
)
# first transition lineshape
width1 = self.get_transition_width((i2e, i1g))
deph1 = self.get_transition_dephasing(
(i2e, i1g)
)
# third transition lineshape
width3 = self.get_transition_width((i4e, i3g))
deph3 = self.get_transition_dephasing(
(i4e, i3g)
)
# |g_i1> <g_i1|
lp.add_transition(
(i2e, i1g),
-1,
interval=1,
width=width1,
deph=deph1,
)
# |g_i1> <e_i2|
lp.add_transition((i3g, i2e), -1)
# |g_i1> <g_i3|
lp.add_transition((i4e, i1g), +1)
# |e_i5> <g_i3|
lp.add_transition(
(i3g, i4e),
+1,
interval=3,
width=width3,
deph=deph3,
)
# |g_i3> <g_i3|
lp.set_evolution_factor(evf)
except Exception:
raise QuantarheiError(
"Generation of pathway failed"
)
lp.build()
lst.append(lp)
k += 1
[docs]
def generate_R4g(
self: Any,
lst: list,
eUt2: numpy.ndarray,
pop_tol: float,
dip_tol: float,
verbose: int = 0,
) -> None:
ngs = self.get_electronic_groundstate()
nes = self.get_excitonic_band(band=1)
if verbose > 0:
print("Liouville pathway R4g")
print("Population tolerance: ", pop_tol)
print("Dipole tolerance: ", dip_tol)
k = 0
l = 0
for i1g in ngs:
if verbose > 0:
print("Ground state: ", i1g, "of", len(ngs))
# Only thermally allowed starting states are considered
if self.rho0[i1g, i1g] > pop_tol:
for i2e in nes:
if verbose > 1:
print("Excited state: ", i2e, "of", len(nes))
if self.D2[i2e, i1g] > dip_tol:
for i3g in ngs:
if self.D2[i3g, i2e] > dip_tol:
evf = eUt2[i1g, i3g, i1g, i3g]
for i4e in nes:
if (self.D2[i4e, i3g] > dip_tol) and (
self.D2[i1g, i4e] > dip_tol
):
l += 1
# Diagram R4g
#
#
# |g_i1> <g_i1|
# <----|-----------|
# |e_i4> <g_i1|
# ---->|-----------|
# |g_i3> <g_i1|
# <----|-----------|
# |e_i2> <g_i1|
# ---->|-----------|
# |g_i1> <g_i1|
try:
if verbose > 5:
print(" * Generating R4g", i1g, i2e)
lp = diag.liouville_pathway(
"NR",
i1g,
aggregate=self,
order=3,
pname="R4g",
)
# first transition lineshape
width1 = self.get_transition_width((i2e, i1g))
deph1 = self.get_transition_dephasing(
(i2e, i1g)
)
# third transition lineshape
width3 = self.get_transition_width((i4e, i1g))
deph3 = self.get_transition_dephasing(
(i4e, i1g)
)
# |g_i1> <g_i1|
lp.add_transition(
(i2e, i1g),
+1,
interval=1,
width=width1,
deph=deph1,
)
# |e_i2> <g_i1|
lp.add_transition((i3g, i2e), +1)
# |g_i3> <g_i1|
lp.add_transition((i4e, i3g), +1)
# |e_i4> <g_i1|
lp.add_transition(
(i1g, i4e),
+1,
interval=3,
width=width3,
deph=deph3,
)
# |g_i1> <g_i1|
lp.set_evolution_factor(evf)
except Exception:
break
lp.build()
lst.append(lp)
k += 1
# if verbose == 10:
# print("////")
# qr.stop()
[docs]
def generate_R1f(
self: Any,
lst: list,
eUt2: numpy.ndarray,
pop_tol: float,
dip_tol: float,
evf_tol: float,
verbose: int = 0,
) -> None:
ngs = self.get_electronic_groundstate()
nes = self.get_excitonic_band(band=1)
try:
nfs = self.get_excitonic_band(band=2)
except Exception:
raise QuantarheiError(
"Excited states not available for R1f* pathway generation"
)
if verbose > 0:
print("Liouville pathway R1f*")
print("Population tolerance: ", pop_tol)
print("Dipole tolerance: ", dip_tol)
print("Evolution amplitude: ", evf_tol)
k = 0
l = 0
for i1g in ngs:
if verbose > 0:
print("Ground state: ", i1g, "of", len(ngs))
# Only thermally allowed starting states are considered
if self.rho0[i1g, i1g] > pop_tol:
for i2e in nes:
if self.D2[i2e, i1g] > dip_tol:
for i3e in nes:
if self.D2[i3e, i1g] > dip_tol:
if verbose > 2:
print(
"Excited state: ", i2e, i3e, "of", nes[len(nes) - 1]
)
for i3d in nes:
for i2d in nes:
evf = eUt2[i3d, i2d, i3e, i2e]
if abs(evf) > evf_tol:
for i4f in nfs:
if (self.D2[i4f, i3d] > dip_tol) and (
self.D2[i2d, i4f] > dip_tol
):
l += 1
# Diagram R1f*
#
#
# |d_i2> <d_i2|
# <----|-----------|
# |f_i4> <d_i2|
# ---->|-----------|
# |d_i3> <d_i2|
# |***********|
# |e_i3> <e_i2|
# ---->|-----------|
# |g_i1> <e_i2|
# |-----------|<----
# |g_i1> <g_i1|
try:
if verbose > 5:
print(
" * Generating R1f*",
i1g,
i2e,
)
lp = diag.liouville_pathway(
"R",
i1g,
aggregate=self,
order=3,
pname="R1f*",
popt_band=1,
relax_order=1,
)
# first transition lineshape
width1 = self.get_transition_width(
(i2e, i1g)
)
deph1 = (
self.get_transition_dephasing(
(i2e, i1g)
)
)
# third transition lineshape
width3 = self.get_transition_width(
(i4f, i2d)
)
deph3 = (
self.get_transition_dephasing(
(i4f, i2d)
)
)
# |g_i1> <g_i1|
lp.add_transition(
(i2e, i1g),
-1,
interval=1,
width=width1,
deph=deph1,
)
# |g_i1> <e_i2|
lp.add_transition((i3e, i1g), +1)
# |e_i3> <e_i2|
lp.add_transfer(
((i3d, i2d)), (i3e, i2e)
)
lp.set_evolution_factor(evf)
# |d_i3> <d_i2|
lp.add_transition((i4f, i3d), +1)
# |f_i4> <d_i2|
lp.add_transition(
(i2d, i4f),
+1,
interval=3,
width=width3,
deph=deph3,
)
# |d_i2> <d_i2|
except Exception:
raise QuantarheiError(
"Construction"
"relaxation pathway failed"
)
lp.build()
lst.append(lp)
k += 1
[docs]
def generate_R2f(
self: Any,
lst: list,
eUt2: numpy.ndarray,
pop_tol: float,
dip_tol: float,
evf_tol: float,
verbose: int = 0,
) -> None:
ngs = self.get_electronic_groundstate()
nes = self.get_excitonic_band(band=1)
try:
nfs = self.get_excitonic_band(band=2)
except Exception:
raise QuantarheiError(
"Excited states not available for R2f* pathway generation"
)
if verbose > 0:
print("Liouville pathway R2f*")
print("Population tolerance: ", pop_tol)
print("Dipole tolerance: ", dip_tol)
print("Evolution amplitude: ", evf_tol)
k = 0
l = 0
for i1g in ngs:
if verbose > 0:
print("Ground state: ", i1g, "of", len(ngs))
# Only thermally allowed starting states are considered
if self.rho0[i1g, i1g] > pop_tol:
for i2e in nes:
if self.D2[i2e, i1g] > dip_tol:
for i3e in nes:
if self.D2[i3e, i1g] > dip_tol:
if verbose > 2:
print(
"Excited state: ", i2e, i3e, "of", nes[len(nes) - 1]
)
for i2d in nes:
for i3d in nes:
evf = eUt2[i2d, i3d, i2e, i3e]
if abs(evf) > evf_tol:
for i4f in nfs:
if (self.D2[i4f, i2d] > dip_tol) and (
self.D2[i3d, i4f] > dip_tol
):
l += 1
# Diagram R2f*
#
#
# |d_i3> <d_i3|
# <----|-----------|
# |f_i4> <d_i3|
# ---->|-----------|
# |d_i2> <d_i3|
# |***********|
# |e_i2> <e_i3|
# |-----------|<----
# |e_i2> <g_i1|
# ---->|-----------|
# |g_i1> <g_i1|
try:
if verbose > 5:
print(
" * Generating R1f*",
i1g,
i2e,
)
lp = diag.liouville_pathway(
"NR",
i1g,
aggregate=self,
order=3,
pname="R2f*",
popt_band=1,
relax_order=1,
)
# first transition lineshape
width1 = self.get_transition_width(
(i2e, i1g)
)
deph1 = (
self.get_transition_dephasing(
(i2e, i1g)
)
)
# third transition lineshape
width3 = self.get_transition_width(
(i4f, i3d)
)
deph3 = (
self.get_transition_dephasing(
(i4f, i3d)
)
)
# |g_i1> <g_i1|
lp.add_transition(
(i2e, i1g),
+1,
interval=1,
width=width1,
deph=deph1,
)
# |e_i2> <g_i1|
lp.add_transition((i3e, i1g), -1)
# |e_i2> <e_i3|
lp.add_transfer(
((i2d, i3d)), (i2e, i3e)
)
lp.set_evolution_factor(evf)
# |d_i2> <d_i3|
lp.add_transition((i4f, i2d), +1)
# |f_i4> <d_i3|
lp.add_transition(
(i3d, i4f),
+1,
interval=3,
width=width3,
deph=deph3,
)
# |d_i3> <d_i3|
except Exception:
break
lp.build()
lst.append(lp)
k += 1
[docs]
def generate_R1fE(
self: Any,
lst: list,
eUt2: numpy.ndarray,
pop_tol: float,
dip_tol: float,
evf_tol: float,
verbose: int = 0,
) -> None:
ngs = self.get_electronic_groundstate()
nes = self.get_excitonic_band(band=1)
if verbose > 0:
print("Liouville pathway R1f*E")
print("Population tolerance: ", pop_tol)
print("Dipole tolerance: ", dip_tol)
print("Evolution amplitude: ", evf_tol)
k = 0
l = 0
for i1g in ngs:
if verbose > 0:
print("Ground state: ", i1g, "of", len(ngs))
# Only thermally allowed starting states are considered
if self.rho0[i1g, i1g] > pop_tol:
for i2e in nes:
if self.D2[i2e, i1g] > dip_tol:
for i3e in nes:
if self.D2[i3e, i1g] > dip_tol:
if verbose > 2:
print(
"Excited state: ", i2e, i3e, "of", nes[len(nes) - 1]
)
for i3g in ngs:
for i2g in ngs:
evf = eUt2[i3g, i2g, i3e, i2e]
if abs(evf) > evf_tol:
for i4e in nes:
if (self.D2[i4e, i3g] > dip_tol) and (
self.D2[i2g, i4e] > dip_tol
):
l += 1
# Diagram R1f*
#
#
# |g_i2> <g_i2|
# <----|-----------|
# |e_i4> <g_i2|
# ---->|-----------|
# |g_i3> <g_i2|
# |***********|
# |e_i3> <e_i2|
# ---->|-----------|
# |g_i1> <e_i2|
# |-----------|<----
# |g_i1> <g_i1|
try:
if verbose > 5:
print(
" * Generating R1f*E",
i1g,
i2e,
)
lp = diag.liouville_pathway(
"R",
i1g,
aggregate=self,
order=3,
pname="R1f*E",
popt_band=1,
relax_order=1,
)
# first transition lineshape
width1 = self.get_transition_width(
(i2e, i1g)
)
deph1 = (
self.get_transition_dephasing(
(i2e, i1g)
)
)
# third transition lineshape
width3 = self.get_transition_width(
(i4e, i2g)
)
deph3 = (
self.get_transition_dephasing(
(i4e, i2g)
)
)
# |g_i1> <g_i1|
lp.add_transition(
(i2e, i1g),
-1,
interval=1,
width=width1,
deph=deph1,
)
# |g_i1> <e_i2|
lp.add_transition((i3e, i1g), +1)
# |e_i3> <e_i2|
lp.add_transfer(
((i3g, i2g)), (i3e, i2e)
)
lp.set_evolution_factor(evf)
# |g_i3> <g_i2|
lp.add_transition((i4e, i3g), +1)
# |e_i4> <g_i2|
lp.add_transition(
(i2g, i4e),
+1,
interval=3,
width=width3,
deph=deph3,
)
# |g_i2> <g_i2|
except Exception:
raise QuantarheiError(
"Construction"
"relaxation pathway failed"
)
lp.build()
lst.append(lp)
k += 1
[docs]
def generate_R2fE(
self: Any,
lst: list,
eUt2: numpy.ndarray,
pop_tol: float,
dip_tol: float,
evf_tol: float,
verbose: int = 0,
) -> None:
ngs = self.get_electronic_groundstate()
nes = self.get_excitonic_band(band=1)
if verbose > 0:
print("Liouville pathway R2f*E")
print("Population tolerance: ", pop_tol)
print("Dipole tolerance: ", dip_tol)
print("Evolution amplitude: ", evf_tol)
k = 0
l = 0
for i1g in ngs:
if verbose > 0:
print("Ground state: ", i1g, "of", len(ngs))
# Only thermally allowed starting states are considered
if self.rho0[i1g, i1g] > pop_tol:
for i2e in nes:
if self.D2[i2e, i1g] > dip_tol:
for i3e in nes:
if self.D2[i3e, i1g] > dip_tol:
if verbose > 2:
print(
"Excited state: ", i2e, i3e, "of", nes[len(nes) - 1]
)
for i2g in ngs:
for i3g in ngs:
evf = eUt2[i2g, i3g, i2e, i3e]
if abs(evf) > evf_tol:
for i4e in nes:
if (self.D2[i4e, i2g] > dip_tol) and (
self.D2[i3g, i4e] > dip_tol
):
l += 1
# Diagram R2f*E
#
#
# |g_i3> <g_i3|
# <----|-----------|
# |e_i4> <g_i3|
# ---->|-----------|
# |g_i2> <g_i3|
# |***********|
# |e_i2> <e_i3|
# |-----------|<----
# |e_i2> <g_i1|
# ---->|-----------|
# |g_i1> <g_i1|
try:
if verbose > 5:
print(
" * Generating R1f*E",
i1g,
i2e,
)
lp = diag.liouville_pathway(
"NR",
i1g,
aggregate=self,
order=3,
pname="R2f*E",
popt_band=1,
relax_order=1,
)
# first transition lineshape
width1 = self.get_transition_width(
(i2e, i1g)
)
deph1 = (
self.get_transition_dephasing(
(i2e, i1g)
)
)
# third transition lineshape
width3 = self.get_transition_width(
(i4e, i3g)
)
deph3 = (
self.get_transition_dephasing(
(i4e, i3g)
)
)
# |g_i1> <g_i1|
lp.add_transition(
(i2e, i1g),
+1,
interval=1,
width=width1,
deph=deph1,
)
# |e_i2> <g_i1|
lp.add_transition((i3e, i1g), -1)
# |e_i2> <e_i3|
lp.add_transfer(
((i2g, i3g)), (i2e, i3e)
)
lp.set_evolution_factor(evf)
# |g_i2> <g_i3|
lp.add_transition((i4e, i2g), +1)
# |e_i4> <g_i3|
lp.add_transition(
(i3g, i4e),
+1,
interval=3,
width=width3,
deph=deph3,
)
# |g_i3> <g_i3|
except Exception:
break
lp.build()
lst.append(lp)
k += 1
[docs]
def generate_1orderP_sec(
self: Any, lst: list, pop_tol: float, dip_tol: float, verbose: int = 0
) -> None:
ngs = self.get_electronic_groundstate()
nes = self.get_excitonic_band(band=1)
if verbose > 0:
print("Liouville pathway of first order")
print("Population tolerance: ", pop_tol)
print("Dipole tolerance: ", dip_tol)
k = 0
l = 0
for i1g in ngs:
if verbose > 0:
print("Ground state: ", i1g, "of", len(ngs))
# Only thermally allowed starting states are considered
if self.rho0[i1g, i1g] > pop_tol:
for i2e in nes:
if self.D2[i2e, i1g] > dip_tol:
l += 1
# Diagram P1
#
#
# |g_i1> <g_i1|
# <----|-----------|
# |e_i2> <g_i1|
# ---->|-----------|
# |g_i1> <g_i1|
try:
if verbose > 5:
print(" * Generating P1", i1g, i2e)
lp = diag.liouville_pathway(
"NR",
i1g,
aggregate=self,
order=1,
pname="P1",
popt_band=1,
relax_order=1,
)
# first transition lineshape
width1 = self.get_transition_width((i2e, i1g))
deph1 = self.get_transition_dephasing((i2e, i1g))
# |g_i1> <g_i1|
lp.add_transition(
(i2e, i1g), +1, interval=1, width=width1, deph=deph1
)
# |e_i2> <g_i1|
lp.add_transition(
(i1g, i2e), +1, interval=1, width=width1, deph=deph1
)
# |g_i1> <g_i1|
except Exception:
break
lp.build()
lst.append(lp)
k += 1