Skip to content

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)