22
33import logging
44import math
5- from typing import TYPE_CHECKING , Any , overload
5+ from typing import TYPE_CHECKING , Any , Literal , overload
66
7+ from rydstate .angular .angular_ket import AngularKetLS
8+ from rydstate .angular .utils import is_unknown
9+ from rydstate .species .element_properties import get_element_properties
10+ from rydstate .species .sqdt import get_sqdt
711from rydstate .units import MatrixElementOperatorRanks , ureg
812
913if TYPE_CHECKING :
@@ -31,6 +35,7 @@ def __init__(
3135 ) -> None :
3236 r"""Initialize the Rydberg state."""
3337 self .species = species
38+ self .element_properties = get_element_properties (species )
3439 self .angular = angular
3540 self .radial = radial
3641
@@ -56,7 +61,7 @@ def calc_reduced_matrix_element(
5661 @overload
5762 def calc_reduced_matrix_element (self , other : RydbergKet , operator : MatrixElementOperator , unit : str ) -> float : ...
5863
59- def calc_reduced_matrix_element (
64+ def calc_reduced_matrix_element ( # noqa: C901, PLR0912
6065 self , other : RydbergKet , operator : MatrixElementOperator , unit : str | None = None
6166 ) -> PintFloat | float :
6267 r"""Calculate the reduced matrix element.
@@ -69,6 +74,9 @@ def calc_reduced_matrix_element(
6974 where \hat{O}^{(k_{angular})} is the operator of rank k_angular for which to calculate the matrix element.
7075 k_radial and k_angular are determined from the operator automatically.
7176
77+ For the "electric_dipole" operator, the matrix element of "electric_dipole_rydberg" and "electric_dipole_core"
78+ are calculated separately and added together.
79+
7280 Args:
7381 other: The other Rydberg state for which to calculate the matrix element.
7482 operator: The operator for which to calculate the matrix element.
@@ -80,6 +88,12 @@ def calc_reduced_matrix_element(
8088 The reduced matrix element for the given operator.
8189
8290 """
91+ if operator == "electric_dipole" :
92+ matrix_element = self .calc_reduced_matrix_element (other , "electric_dipole_rydberg" , unit )
93+ if self .element_properties .number_valence_electrons == 2 :
94+ matrix_element += self .calc_reduced_matrix_element (other , "electric_dipole_core" , unit )
95+ return matrix_element
96+
8397 try :
8498 k_radial , k_angular = MatrixElementOperatorRanks [operator ]
8599 except KeyError as err :
@@ -99,20 +113,31 @@ def calc_reduced_matrix_element(
99113 # as the same dimensionality as the Bohr magneton (mu = - mu_B (g_l l + g_s s_tot))
100114 # such that - mu * B (where the magnetic field B is given in dimension Tesla) is an energy
101115
102- elif operator in ["electric_dipole" , "electric_quadrupole" , "electric_octupole" , "electric_quadrupole_zero" ]:
116+ elif operator .startswith ("electric_" ):
117+ angular_operator : Literal ["spherical" , "spherical_core" ]
118+ angular_operator = "spherical" if "core" not in operator else "spherical_core"
103119 # Electric multipole operator: p_{k,q} = e r^k_radial * sqrt(4pi / (2k+1)) * Y_{k_angular,q}(\theta, phi)
104- angular_matrix_element = self .angular .calc_reduced_matrix_element (other .angular , "spherical" , k_angular )
120+ angular_matrix_element = self .angular .calc_reduced_matrix_element (
121+ other .angular , angular_operator , k_angular
122+ )
105123 # Prefactor sqrt(4 pi / (2 k_angular + 1)) for the electric multipole operators, precomputed for performance
106124 prefactor = ELECTRIC_MULTIPOLE_PREFACTORS [k_angular ]
107125
108126 else :
109127 raise NotImplementedError (f"Operator { operator } not implemented." )
110128
111129 if angular_matrix_element == 0 :
112- matrix_element = 0.0
113- else :
130+ return 0
131+
132+ if "core" not in operator :
114133 radial_matrix_element = self .radial .calc_matrix_element (other .radial , k_radial , unit = "a.u." )
115- matrix_element = prefactor * radial_matrix_element * angular_matrix_element
134+ matrix_element = prefactor * angular_matrix_element * radial_matrix_element
135+ else :
136+ core_radial_matrix_element = self ._calc_core_radial_matrix_element_au (other , k_radial )
137+ if core_radial_matrix_element == 0 :
138+ return 0
139+ rydberg_radial_overlap = self .radial .calc_overlap (other .radial )
140+ matrix_element = prefactor * angular_matrix_element * core_radial_matrix_element * rydberg_radial_overlap
116141
117142 if unit == "a.u." :
118143 return matrix_element
@@ -121,7 +146,7 @@ def calc_reduced_matrix_element(
121146 matrix_element_unit : PintFloat
122147 if operator == "magnetic_dipole" :
123148 matrix_element_unit = radial_unit * ureg .Quantity (2 , "bohr_magneton" )
124- elif operator in [ "electric_dipole" , "electric_quadrupole" , "electric_octupole" , "electric_quadrupole_zero" ] :
149+ elif operator . startswith ( "electric_" ) :
125150 matrix_element_unit = radial_unit * ureg .Quantity (1 , "e" )
126151 else :
127152 raise NotImplementedError (f"Operator { operator } not implemented." )
@@ -130,6 +155,52 @@ def calc_reduced_matrix_element(
130155 return matrix_element * matrix_element_unit .to_base_units () # type: ignore [no-any-return]
131156 return matrix_element * matrix_element_unit .to (unit ).magnitude
132157
158+ def _calc_core_radial_matrix_element_au (self , other : RydbergKet , k_radial : int ) -> float :
159+ r"""Calculate the radial matrix element :math:`\langle self_c | r^{k_{radial}} | other_c \rangle` in a.u.
160+
161+ The core electron is treated as the low-lying valence electron of the corresponding singly charged SQDT ion
162+ (e.g. Yb174_ion for Yb174):
163+ the Rydberg electron is ignored and the radial matrix element is calculated between the two ion states,
164+ where the principal quantum number of each core electron is given by the lowest allowed shell of the ion
165+ for the given l_c.
166+ """
167+ from rydstate .rydberg_state .rydberg_sqdt import RydbergStateSQDT # noqa: PLC0415
168+
169+ species = self .species
170+ ion_species = f"{ species } _ion"
171+ try :
172+ ion_sqdt = get_sqdt (ion_species )
173+ except ValueError :
174+ logger .warning (
175+ "No SQDT data available for the ion species of %s "
176+ "returning 0 for the dipole matrix elemnet core contribution." ,
177+ species ,
178+ )
179+ return 0.0
180+
181+ kets = {"self" : self , "other" : other }
182+ ion_states : dict [str , RydbergStateSQDT [Any ]] = {}
183+ for ket_name , ket in kets .items ():
184+ l_c = ket .angular .l_c
185+ j_c = ket .angular .get_qn ("j_c" , allow_unknown = True )
186+ f_c = ket .angular .get_qn ("f_c" , allow_unknown = True )
187+ if is_unknown (l_c ) or is_unknown (j_c ) or is_unknown (f_c ):
188+ return 0.0
189+
190+ angular_ket = AngularKetLS (l_r = l_c , j_tot = j_c , f_tot = f_c , species = ion_species )
191+
192+ # TODO: we should probably also store n_c for the core angular ket in the future
193+ # for now, it is correct to assume that the core electron is
194+ # in the lowest allowed shell of the ion for the given l_c
195+ for n_c in range (l_c + 1 , l_c + 15 ):
196+ if ion_sqdt .is_allowed_shell (n_c , l_c , 0.5 ):
197+ ion_states [ket_name ] = RydbergStateSQDT (ion_species , n_c , angular_ket = angular_ket , sqdt = ion_sqdt )
198+ break
199+ else : # no break
200+ raise ValueError (f"No allowed shell found for ion species { ion_species } with l_c={ l_c } ." )
201+
202+ return ion_states ["self" ].radial .calc_matrix_element (ion_states ["other" ].radial , k_radial , unit = "a.u." )
203+
133204 @overload
134205 def calc_matrix_element (
135206 self , other : RydbergKet , operator : MatrixElementOperator , q : int , unit : None = None
0 commit comments