Atomic system
¶
atomiclevel
¶
This is the class that implements the atomic levels.
An atomiclevel object is used as a container for the three quantum numbers n, l and j of the considered level
Note
Some method to define an order between levels is implemented, even though it is not in use right now.
Attributes:
Name | Type | Description |
---|---|---|
n |
int |
Principal quantum number. |
l |
int |
Azimuthal (orbital angular momentum) quantum number. |
j |
int or half-int) |
Total angular momentum quantum number. |
Examples:
In order to create an atomic level, one can just write:
>>> level = atomiclevel(6,P,1/2)
>>> print(level)
6P1/2
__str__(self)
special
¶
str : Formatted print of an atomic level, using the usual S, P, D... notation.
Source code in nanotrappy\trapping\atomicsystem.py
def __str__(self):
"""str : Formatted print of an atomic level, using the usual S, P, D... notation."""
if self.l == 0:
return str(self.n) + "S" + asfraction(self.j)
elif self.l == 1:
return str(self.n) + "P" + asfraction(self.j)
elif self.l == 2:
return str(self.n) + "D" + asfraction(self.j)
atomicsystem
¶
This is the class that implements the atomic system under study. It calls the ARC Rydberg calculator in the background to collect all the datas available for the chosen atom.
All the information of the states to which the ground state and the excited state couple to are stored in a array of dictionnaries. dicoatom is the dictionnary for the selected state The coupled states are given by a triplet (n,l,j).
Note
Some method to define an order between levels is implemented, even though it is not in use right now.
Attributes:
Name | Type | Description |
---|---|---|
atom |
atom |
The atom selected among ARC catalog (ex: Caesium(), Rubidium87()...). |
state |
atomiclevel |
The atomic level considered as the state of interest. |
f |
int |
Hyperfine level. |
Nground |
int |
the principal quantum number of the ground state (for computationnal use). |
listlevels |
list[atomiclevel] |
list of the atomic levels coupled to the state. |
dicoatom |
list[dict] |
dictionnary of the physical quantities, to avoid using ARC methods every time. |
Note
The format of the dictionnaries is the following: To each atomic level is associated a triplet (f,rde,gamma) where f is the transition frequency to this level, rde is the reduced matrix element of the transition and gamma is the transition rate.
Examples:
An atomic system is created as follow:
>>> atomic_system = atomicsystem(Rubidium87(),atomiclevel(5,S,1/2),f=2)
Additionnaly, a parser is provided so that the following is also valid:
>>> atomic_system = atomicsystem(Rubidium87(),"5S1/2",f=2)
alpha_scalar(self, lmbda)
¶
This function returns the scalar polarizability of the given state and hyperfine state F of the atom, at the wavelength lambda. Documentation on how this contribution is calculated is available in the main documentation.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
lmbda |
float |
Wavelength at which to compute the polarizability. |
required |
Returns:
Type | Description |
---|---|
float |
Scalar polarizability for the given parameters. |
Examples:
>>> syst.alpha0(780e-9)/AMU
-329596.9660348868
Source code in nanotrappy\trapping\atomicsystem.py
def alpha_scalar(self, lmbda):
"""This function returns the scalar polarizability of the given state and hyperfine state F of the atom, at the wavelength lambda.
Documentation on how this contribution is calculated is available in the main documentation.
Args:
lmbda (float): Wavelength at which to compute the polarizability.
Returns:
float: Scalar polarizability for the given parameters.
Example:
>>> syst.alpha0(780e-9)/AMU
-329596.9660348868
"""
if self.f > self.I + self.state.j or self.f < abs(self.I - self.state.j):
raise ValueError("F should be in the interval [|I-J|,I+J]")
tot = 0.0
freq = 0.0
sign = 1.0
coupledlevels = self.listlevels
couplings = self.dicoatom
for level in coupledlevels:
c = couplings[(level.n, level.l, level.j)]
gamma = c[2]
omega = 2 * np.pi * c[0]
rde = abs(c[1])
freq = np.real(
1 / (sign * omega - (2 * np.pi * cc / lmbda) - 1j * gamma / 2)
+ 1 / (sign * omega + (2 * np.pi * cc / lmbda) + 1j * gamma / 2)
)
for fsum in np.arange(abs(level.j - self.I), abs(level.j + self.I) + 1, 1):
tot += (
rde
* np.conj(rde)
* freq
* (2 * fsum + 1)
* (self.wigner6j[int(level.j - 1 / 2), int(self.f - abs(self.state.j - self.I)), int(fsum - 1)])
** 2
)
tot = (1 / (3 * hbar)) * au ** 2 * tot
if self.state == self.groundstate:
tot += self.alpha_core * AMU
return tot
alpha_tensor(self, lmbda)
¶
This function returns the tensor polarizability of the given state and hyperfine state F of the atom, at the wavelength lambda. Documentation on how this contribution is calculated is available in the main documentation.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
lmbda |
float |
Wavelength at which to compute the polarizability. |
required |
Returns:
Type | Description |
---|---|
float |
Tensor polarizability for the given parameters. |
Source code in nanotrappy\trapping\atomicsystem.py
def alpha_tensor(self, lmbda):
"""This function returns the tensor polarizability of the given state and hyperfine state F of the atom, at the wavelength lambda.
Documentation on how this contribution is calculated is available in the main documentation.
Args:
lmbda (float): Wavelength at which to compute the polarizability.
Returns:
float: Tensor polarizability for the given parameters.
"""
if self.f > self.I + self.state.j or self.f < abs(self.I - self.state.j):
raise ValueError("F should be in the interval [|I-J|,I+J]")
tot = 0.0
freq = 0.0
sign = 1.0
coupledlevels = self.listlevels
couplings = self.dicoatom
for level in coupledlevels:
c = couplings[(level.n, level.l, level.j)]
gamma = c[2]
omega = 2 * np.pi * c[0]
rde = abs(c[1])
freq = np.real(
1 / (sign * omega - (2 * np.pi * cc / lmbda) - 1j * gamma / 2)
+ 1 / (sign * omega + (2 * np.pi * cc / lmbda) + 1j * gamma / 2)
)
for fsum in np.arange(abs(level.j - self.I), abs(level.j + self.I) + 1, 1):
tot += (
rde
* np.conj(rde)
* (-1) ** (self.f + fsum)
* np.sqrt(10 * self.f * (2 * self.f + 1) * (2 * self.f - 1) / (3 * (self.f + 1) * (2 * self.f + 3)))
* wigner6j12(1, 1, 2, self.f, self.f, fsum)
* freq
* (2 * fsum + 1)
* self.wigner6j[int(level.j - 1 / 2), int(self.f - abs(self.state.j - self.I)), int(fsum - 1)] ** 2
)
tot = (1 / (hbar)) * au ** 2 * tot
return tot
alpha_vector(self, lmbda)
¶
This function returns the vector polarizability of the given state and hyperfine state F of the atom, at the wavelength lambda. Documentation on how this contribution is calculated is available in the main documentation.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
lmbda |
float |
Wavelength at which to compute the polarizability. |
required |
Returns:
Type | Description |
---|---|
float |
Vector polarizability for the given parameters. |
Source code in nanotrappy\trapping\atomicsystem.py
def alpha_vector(self, lmbda):
"""This function returns the vector polarizability of the given state and hyperfine state F of the atom, at the wavelength lambda.
Documentation on how this contribution is calculated is available in the main documentation.
Args:
lmbda (float): Wavelength at which to compute the polarizability.
Returns:
float: Vector polarizability for the given parameters.
"""
if self.f > self.I + self.state.j or self.f < abs(self.I - self.state.j):
raise ValueError("F should be in the interval [|I-J|,I+J]")
tot = 0.0
freq = 0.0
sign = 1.0
coupledlevels = self.listlevels
couplings = self.dicoatom
for level in coupledlevels:
c = couplings[(level.n, level.l, level.j)]
gamma = c[2]
omega = 2 * np.pi * c[0]
rde = abs(c[1])
freq = np.real(
1 / (sign * omega - (2 * np.pi * cc / lmbda) - 1j * gamma / 2)
- 1 / (sign * omega + (2 * np.pi * cc / lmbda) + 1j * gamma / 2)
)
for fsum in np.arange(abs(level.j - self.I), abs(level.j + self.I) + 1, 1):
tot += (
rde
* np.conj(rde)
* (-1) ** (self.f + fsum)
* np.sqrt(6 * self.f * (2 * self.f + 1) / (self.f + 1))
* wigner6j12(1, 1, 1, self.f, self.f, fsum)
* freq
* (2 * fsum + 1)
* self.wigner6j[int(level.j - 1 / 2), int(self.f - abs(self.state.j - self.I)), int(fsum - 1)] ** 2
)
tot = (1 / hbar) * au ** 2 * tot / 2
return tot
get_C3(self, material, state, units='SI')
¶
Method to compute C3 coefficient of Casimir-Polder interactions. The calculation assumes an infinite plane wall of the given material and with the atom defined by atomicsystem. A database for the refractive of a few materials is already implemented but you can add the one you want on the "refractiveindexes" folder.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
material |
material |
required | |
state |
atomiclevel |
Atomic level from which we want to compute C3. |
required |
units |
str,optional |
"SI" or "au" atomic units. Default is SI |
'SI' |
Examples:
>>>self.get_C3(self.structure.material,self.groundstate)
>>>5.0093046822224805e-49
Source code in nanotrappy\trapping\atomicsystem.py
def get_C3(self, material, state, units="SI"):
"""
Method to compute C3 coefficient of Casimir-Polder interactions. The calculation assumes an infinite plane
wall of the given material and with the atom defined by atomicsystem. A database for the refractive of a few
materials is already implemented but you can add the one you want on the "refractiveindexes" folder.
Args:
material (material):
state (atomiclevel): Atomic level from which we want to compute C3.
units (str,optional): "SI" or "au" atomic units. Default is SI
Examples:
>>>self.get_C3(self.structure.material,self.groundstate)
>>>5.0093046822224805e-49
"""
if str(material.__class__.__name__) != "str":
material = str(material.__class__.__name__)
xi = np.logspace(1, 18, 400, base=10) # lambdalist = [(2*np.pi*cc)/k for k in xi]
alphaim = self.alphaim0(state, xi) * (a0 ** 3)
if material == "metal":
trap = [(xi[k + 1] - xi[k]) * (alphaim[k] + alphaim[k + 1]) / 2 for k in range(len(xi) - 1)]
else:
n = np.load(
utils_path + r"/refractiveindexes/" + material + ".npy"
) # these files contain 3 columns : lambda, Re(n), Im(n) # Imeps = 2*n[:,1]*n[:,2]
Reeps = n[:, 1] ** 2 - n[:, 2] ** 2
omegalist = 2 * np.pi * cc / (n[:, 0] * 1e-6)
trap = np.zeros(
(len(omegalist), len(xi))
) # If the extinction factor is 0, this expression has to be used instead of the standard one
def integrand_real(k, i):
return (Reeps[k] - 1) * xi[i] / (omegalist[k] ** 2 + xi[i] ** 2)
def integrandC3(k):
return alphaim[k] * ((epsilontot[k] - 1) / (epsilontot[k] + 1))
for k in range(len(omegalist) - 1):
for i in range(len(xi)):
trap[k][i] = (
(omegalist[k + 1] - omegalist[k]) * (integrand_real(k, i) + integrand_real(k + 1, i)) / 2
)
I = np.sum(trap, axis=0)
epsilontot = 1 + 2 / np.pi * (-I)
trap = [(xi[k + 1] - xi[k]) * (integrandC3(k) + integrandC3(k + 1)) / 2 for k in range(len(xi) - 1)]
if units == "SI":
C3 = hbar / (4 * np.pi) * np.sum(trap)
if units == "au":
C3 = hbar / ((4 * np.pi) * np.sum(trap) * (a0 ** 3 * EH))
return C3
islower(self, atlevel1, atlevel2)
¶
This function checks if atlevel1 is lower in energy than atlevel2. Only used if one of atlevel1 or atlevel2 is either the ground or the excited state
Parameters:
Name | Type | Description | Default |
---|---|---|---|
atlevel1 |
atomiclevel |
atomic level. |
required |
atlevel2 |
atomiclevel |
atomic level. |
required |
Returns:
Type | Description |
---|---|
bool |
True if successful, False otherwise. |
Examples:
>>> syst.islower(atomiclevel(5,S,1/2),atomiclevel(5,P,3/2))
True
Source code in nanotrappy\trapping\atomicsystem.py
def islower(self, atlevel1, atlevel2):
"""This function checks if atlevel1 is lower in energy than atlevel2. Only used if one of atlevel1 or atlevel2 is either the ground or the excited state
Args:
atlevel1 (atomiclevel): atomic level.
atlevel2 (atomiclevel): atomic level.
Returns:
bool: True if successful, False otherwise.
Example:
>>> syst.islower(atomiclevel(5,S,1/2),atomiclevel(5,P,3/2))
True
"""
if atlevel2 == self.groundstate:
return False
elif atlevel2 == self.excitedstate:
if atlevel1 == self.groundstate:
return True
else:
return False
potential(self, Ep, Em, E0)
¶
This function computes the trapping potential energy for a given electric field and the given state and hyperfine state F of the atom, at the wavelength lambda. Documentation on how this potential is calculated is available in the main documentation.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
Ep |
float |
required | |
Em |
float |
required | |
E0 |
float |
required |
Returns:
Type | Description |
---|---|
float |
Trapping potential for the given parameters. |
Source code in nanotrappy\trapping\atomicsystem.py
def potential(self, Ep, Em, E0):
"""This function computes the trapping potential energy for a given electric field and the given state and hyperfine state F of the atom, at the wavelength lambda.
Documentation on how this potential is calculated is available in the main documentation.
Args:
Ep (float):
Em (float):
E0 (float):
Returns:
float: Trapping potential for the given parameters.
"""
Htemp = self.totalshift(Ep, Em, E0) / (mK * kB)
vals, vec = LA.eig(Htemp)
idx = vals.argsort()
return vals[idx], vec[idx]
potential_partial(self, Ep, Em, E0, contrib='scalar', show=False)
¶
This function computes the trapping potential energy for a given electric field and the given state and hyperfine state F of the atom, at the wavelength lambda. Documentation on how this potential is calculated is available in the main documentation.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
Ep |
float |
required | |
Em |
float |
required | |
E0 |
float |
required |
Returns:
Type | Description |
---|---|
float |
Trapping potential for the given parameters. |
Source code in nanotrappy\trapping\atomicsystem.py
def potential_partial(self, Ep, Em, E0, contrib="scalar", show=False):
"""This function computes the trapping potential energy for a given electric field and the given state and hyperfine state F of the atom, at the wavelength lambda.
Documentation on how this potential is calculated is available in the main documentation.
Args:
Ep (float):
Em (float):
E0 (float):
Returns:
float: Trapping potential for the given parameters.
"""
if contrib == "scalar":
Htemp = self.scalarshift(Ep, Em, E0) / (mK * kB)
elif contrib == "vector":
Htemp = (self.scalarshift(Ep, Em, E0) + self.vectorshift(Ep, Em, E0)) / (mK * kB)
elif contrib == "tensor":
Htemp = (self.scalarshift(Ep, Em, E0) + self.vectorshift(Ep, Em, E0) + self.tensorshift(Ep, Em, E0)) / (
mK * kB
)
else:
raise (ValueError("This contribution does not exist."))
if show:
print("\n".join(["\t".join([str(5e-3 * cell) for cell in row]) for row in Htemp]))
vals, vec = LA.eig(Htemp)
idx = vals.argsort()
return vals[idx], vec[idx]
convert_to_float(frac_str)
¶
Source code in nanotrappy\trapping\atomicsystem.py
def convert_to_float(frac_str):
"""float: util function to convert string of fraction to float."""
try:
return float(frac_str)
except ValueError:
num, denom = frac_str.split("/")
try:
leading, num = num.split(" ")
whole = float(leading)
except ValueError:
whole = 0
frac = float(num) / float(denom)
return whole - frac if whole < 0 else whole + frac
string_to_level(str)
¶
Source code in nanotrappy\trapping\atomicsystem.py
def string_to_level(str):
"""atomiclevel: parse an input string into an atomic level object."""
j = convert_to_float(str[-3::])
ltemp = str[-4::-3]
if ltemp == "S":
l = 0
elif ltemp == "P":
l = 1
elif ltemp == "D":
l = 2
elif ltemp == "F":
l = 3
elif ltemp == "G":
l = 4
else:
raise "Has not been implemented yet."
n = int(str[0::-4])
return atomiclevel(n, l, j)