77
88from rydstate .angular import NotSet
99from rydstate .angular .angular_ket import AngularKetBase , AngularKetFJ , AngularKetJJ , AngularKetLS
10- from rydstate .angular .utils import is_not_set
10+ from rydstate .angular .utils import (
11+ InvalidQuantumNumbersError ,
12+ Unknown ,
13+ get_possible_quantum_number_values ,
14+ is_not_set ,
15+ is_unknown ,
16+ try_trivial_spin_addition ,
17+ )
1118from rydstate .basis .basis_base import BasisBase
1219from rydstate .rydberg import (
1320 RydbergStateSQDT ,
1421)
1522from rydstate .species import SpeciesObjectSQDT
23+ from rydstate .species .mqdt .species_object_mqdt import SpeciesObjectMQDT
1624
1725if TYPE_CHECKING :
1826 from rydstate .angular .utils import AllKnown , CouplingScheme
@@ -131,10 +139,22 @@ def _add_states_jj(self, n: int, l_r: int, m_range: tuple[float, float] | None |
131139 state = RydbergStateSQDT .from_angular_ket (self .species , angular_ket , n = n )
132140 self .states .append (state ) # type: ignore [arg-type]
133141
134- def _add_states_fj (self , n : int , l_r : int , m_range : tuple [float , float ] | None | NotSet = NotSet ) -> None :
142+ def _add_states_fj (
143+ self ,
144+ n : int ,
145+ l_r : int ,
146+ m_range : tuple [float , float ] | None | NotSet = NotSet ,
147+ l_c : int | Unknown = 0 ,
148+ j_c : float | Unknown = Unknown ,
149+ f_c : float | Unknown = Unknown ,
150+ f_tot : float | Unknown = Unknown ,
151+ allow_unknown : bool = False ,
152+ ) -> None :
135153 i_c = self .species .i_c_number
136154 s_r = 0.5
137- s_c = j_c = 0 if self .species .number_valence_electrons == 1 else 0.5
155+ s_c = 0 if self .species .number_valence_electrons == 1 else 0.5
156+ j_c = try_trivial_spin_addition (l_c , s_c , j_c )
157+ f_c = try_trivial_spin_addition (j_c , i_c , f_c )
138158 s_tot_list = np .arange (s_r - s_c , s_r + s_c + 1 )
139159
140160 allowed = [self .species .is_allowed_shell (n , l_r , s ) for s in s_tot_list ]
@@ -147,13 +167,28 @@ def _add_states_fj(self, n: int, l_r: int, m_range: tuple[float, float] | None |
147167 )
148168 return
149169
150- for j_r in np .arange (abs (l_r - s_r ), l_r + s_r + 1 ):
151- for f_c in np .arange (abs (j_c - i_c ), j_c + i_c + 1 ):
152- for f_tot in np .arange (abs (f_c - j_r ), f_c + j_r + 1 ):
153- for m in self ._get_m_range (m_range , f_tot ):
154- angular_ket = AngularKetFJ (l_r = l_r , j_r = j_r , f_c = f_c , f_tot = f_tot , m = m , species = self .species )
170+ for j_r in get_possible_quantum_number_values (l_r , s_r , Unknown ):
171+ for _f_c in get_possible_quantum_number_values (j_c , i_c , f_c ):
172+ for _f_tot in get_possible_quantum_number_values (_f_c , j_r , f_tot ):
173+ if is_unknown (_f_tot ):
174+ raise ValueError (
175+ "Cannot determine f_tot for FJ coupling. Please provide more specific MQDT parameters."
176+ )
177+ for m in self ._get_m_range (m_range , _f_tot ):
178+ try :
179+ angular_ket = AngularKetFJ ( # type: ignore [call-overload]
180+ l_r = l_r ,
181+ j_r = j_r ,
182+ f_c = _f_c ,
183+ f_tot = _f_tot ,
184+ m = m ,
185+ species = self .species ,
186+ allow_unknown = allow_unknown ,
187+ )
188+ except InvalidQuantumNumbersError :
189+ continue
155190 state = RydbergStateSQDT .from_angular_ket (self .species , angular_ket , n = n )
156- self .states .append (state ) # type: ignore [arg-type]
191+ self .states .append (state )
157192
158193 def _get_m_range (self , m_range : tuple [float , float ] | None | NotSet , f_tot : float ) -> list [NotSet | float ]:
159194 if is_not_set (m_range ):
@@ -164,3 +199,57 @@ def _get_m_range(self, m_range: tuple[float, float] | None | NotSet, f_tot: floa
164199 m_min = max (- f_tot , m_range [0 ])
165200 m_max = min (f_tot , m_range [1 ])
166201 return [float (_m ) for _m in np .arange (m_min , m_max + 1 )]
202+
203+
204+ class BasisSQDTMultiChannels (BasisSQDT [AngularKetFJ [Any ]]):
205+ species : SpeciesObjectMQDT
206+
207+ def __init__ (
208+ self ,
209+ species : str | SpeciesObjectMQDT ,
210+ n : tuple [int , int ],
211+ m : tuple [float , float ] | None | NotSet = NotSet ,
212+ * ,
213+ coupling_scheme : CouplingScheme = "FJ" ,
214+ ) -> None :
215+ self .species = SpeciesObjectMQDT .from_name (species ) if isinstance (species , str ) else species
216+
217+ self ._init_states (n , m , coupling_scheme )
218+
219+ def _init_states (
220+ self , n_range : tuple [int , int ], m_range : tuple [float , float ] | None | NotSet , coupling_scheme : CouplingScheme
221+ ) -> None :
222+ if coupling_scheme != "FJ" :
223+ raise NotImplementedError ("Only FJ coupling is implemented for multi-channel SQDT basis" )
224+ self .coupling_scheme = coupling_scheme
225+ self .states = []
226+
227+ for core_ket in self .species ._ionization_threshold_dict : # noqa: SLF001
228+ if core_ket .contains_unknown :
229+ # we will handle these below
230+ continue
231+ logger .info ("Generating states for core ket: %s" , core_ket )
232+
233+ for n in range (n_range [0 ], n_range [1 ] + 1 ):
234+ for l_r in range (n ):
235+ self ._add_states_fj (
236+ n , l_r , m_range , l_c = core_ket .l_c , j_c = core_ket .j_c , f_c = core_ket .f_c , allow_unknown = True
237+ )
238+
239+ # add all addition series, which are defined in the mqdt parameters but have dummy core kets
240+ for fmodel in self .species .models :
241+ for angular_ket in fmodel .outer_channels :
242+ if not angular_ket .contains_unknown :
243+ # handled above
244+ continue
245+ for n in range (n_range [0 ], n_range [1 ] + 1 ):
246+ self ._add_states_fj (
247+ n ,
248+ angular_ket .l_r ,
249+ m_range ,
250+ l_c = angular_ket .l_c ,
251+ j_c = angular_ket .j_c ,
252+ f_c = angular_ket .f_c ,
253+ f_tot = angular_ket .f_tot ,
254+ allow_unknown = True ,
255+ )
0 commit comments