Skip to content

Zeeman Matrix elements are off-diagonal with with magnetic fields - #221

Open
kiliansinger wants to merge 8 commits into
nikolasibalic:masterfrom
kiliansinger:master
Open

Zeeman Matrix elements are off-diagonal with with magnetic fields#221
kiliansinger wants to merge 8 commits into
nikolasibalic:masterfrom
kiliansinger:master

Conversation

@kiliansinger

Copy link
Copy Markdown

Zeeman Matrix elements are off-diagonal with with small magnetic field present
as it typically dominates Spin-Orbit coupling due to the latter scaling with 1/n^3. So (ml1 + gs * ms1) is off-diagonal due to |n l j mj> are containing different admixtures of ml and ms states given by Clebsch-Gordan coefficients.

Modified class StarkMap to take this into consideration.

The error is still present in class LevelPlot.

…d present

as it typically dominates Spin-Orbit coupling due to the latter scaling with 1/n^3.
So (ml1 + gs * ms1) is off-diagonal due to |n l j mj>
are containing different admixtures of ml and ms states
given by Clebsch-Gordan coefficients.

Modified class StarkMap to take this into consideration.

The error is still present in class LevelPlot.
Copilot AI review requested due to automatic review settings May 13, 2026 08:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the single-atom Stark map calculation to account for off-diagonal Zeeman Hamiltonian terms in the |n l j mⱼ⟩ basis when a nonzero magnetic field (B_z) is applied, by introducing an additional interaction matrix component.

Changes:

  • Add an off-diagonal Zeeman shift helper (getZeemanEnergyShiftOffDiagonal) to compute paramagnetic Zeeman couplings between different |l j mⱼ⟩ states.
  • Extend StarkMap to build and include a third matrix (mat3) representing Zeeman off-diagonal couplings when Bz != 0.
  • Thread Bz into getState and update diagonalisation to incorporate mat3 when applicable.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
arc/calculations_atom_single.py Adds mat3 and Bz-dependent basis/matrix construction and uses mat3 during diagonalisation/state extraction.
arc/alkali_atom_functions.py Introduces a new method for off-diagonal Zeeman energy shift calculation in the |l j mⱼ⟩ basis.
Comments suppressed due to low confidence (5)

arc/calculations_atom_single.py:873

  • mat3 is intended to store the off-diagonal Zeeman coupling (coupling2), but the code assigns coupling (the Stark off-diagonal /E term) into mat3. This makes the Zeeman contribution incorrect whenever Bz != 0. Assign coupling2 to self.mat3[...] here (and consider renaming variables to avoid mixups).
                if Bz!=0:
                    coupling2=0
                    if states[ii][0]==states[jj][0] and states[ii][1]==states[jj][1]:
                        coupling2 = self.atom.getZeemanEnergyShiftOffDiagonal(
                            states[ii][1],
                            states[ii][2],
                            states[ii][3],
                            states[jj][2],
                            states[jj][3],
                            self.Bz,
                            s=self.s
                        )/ C_h* 1.0e-9

                    self.mat3[jj][ii] = coupling
                    self.mat3[ii][jj] = coupling

arc/calculations_atom_single.py:774

  • When Bz != 0, the basis generation adds states with m_j spanning mj-1 .. mj+1, but both the Stark coupling (_eFieldCouplingDivE) and the added Zeeman coupling conserve m_j (so these extra m_j blocks are uncoupled). This increases the matrix dimension and runtime substantially without affecting results. Consider keeping m_j fixed and only extending the basis in j (or, if you intend to model m_j-changing terms, the Hamiltonian needs the corresponding couplings).
        else:
            for tn in xrange(nMin, nMax + 1):
                for tl in xrange(min(maxL + 1, tn)):
                    for tj in np.linspace(tl - s, tl + s, round(2 * s + 1)):
                        for tmj in np.linspace(mj-1,mj+1,round(2 * s + 2)): # we need a bit more mj states as we do not know ml                                
                            if (abs(tmj) - 0.1 <= tj) and (
                                tn >= self.atom.groundStateN
                                or [tn, tl, tj] in self.atom.extraLevels
                            ):
                                states.append([tn, tl, tj, tmj])

arc/calculations_atom_single.py:803

  • defineBasis only allocates self.mat3 when Bz != 0. If defineBasis is called once with Bz != 0 and later with Bz == 0, self.mat3 will retain the old array from the previous call, which can be confusing for users inspecting the object. Consider resetting self.mat3 to an empty list/None (or a zero matrix) in the Bz == 0 path for a consistent post-condition.
        self.mat1 = np.zeros((dimension, dimension), dtype=np.double)
        self.mat2 = np.zeros((dimension, dimension), dtype=np.double)
        if Bz!=0:
            self.mat3 = np.zeros((dimension, dimension), dtype=np.double)

arc/calculations_atom_single.py:712

  • defineBasis now accepts Bz, but if it was inserted as a new positional argument before existing optional args, that can silently break external code that passes progressOutput/debugOutput positionally (it would now be interpreted as Bz). Consider making Bz keyword-only or placing it after the existing optional parameters to preserve backwards compatibility.
        second part :obj:`mat2` corresponds to off-diagonal elements that are
        propotional to electric field. Overall interaction matrix for
        electric field `eField` can be then obtained as
        with Bz equal to zero:
        `fullStarkMatrix` = :obj:`mat1` + :obj:`mat2` *`eField`
        with Bz not equal to zero:
        `fullStarkMatrix` = :obj:`mat1` + :obj:`mat2` *`eField` + :obj:`mat3`

arc/calculations_atom_single.py:1029

  • The new Bz != 0 code path (building mat3 and using it in diagonalise) isn’t covered by the existing StarkMap tests (which currently only exercise Bz==0). Adding a unit/integration test that runs defineBasis(..., Bz=...) and verifies expected Zeeman mixing/energies (or at least that the Hamiltonian changes vs Bz=0) would help prevent regressions in this physics-critical logic.
                    "\r%d%%" % (float(progress) / float(len(eFieldList)) * 100)
                )
                sys.stdout.flush()
            if self.Bz==0:
                m = self.mat1 + self.mat2 * eField
            else:
                m = self.mat1 + self.mat2 * eField + self.mat3


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread arc/calculations_atom_single.py Outdated
Comment on lines 590 to 596
self.mat3 = []
"""
off-diagonal elements of Zeeman-Matrix divided multiplied by magnetic
field value. Only relevant if Bz is not zero.
Full Stark matrix is obtained as
`fullStarkMatrix` = :obj:`mat1` + :obj:`mat2` *`eField` + :obj:`mat3`. Calculated by
:obj:`defineBasis` in the basis :obj:`basisStates`.
s: float = 0.5,
) -> float:
r"""
Retuns off diagonal linear (paramagnetic) Zeeman shift.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants