dipolmol 1.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dipolmol-1.1.1/PKG-INFO +88 -0
- dipolmol-1.1.1/README.md +66 -0
- dipolmol-1.1.1/pyproject.toml +48 -0
- dipolmol-1.1.1/setup.cfg +4 -0
- dipolmol-1.1.1/src/dipolmol/__init__.py +0 -0
- dipolmol-1.1.1/src/dipolmol/calculate.py +405 -0
- dipolmol-1.1.1/src/dipolmol/constants.py +106 -0
- dipolmol-1.1.1/src/dipolmol/hamiltonian.py +422 -0
- dipolmol-1.1.1/src/dipolmol.egg-info/PKG-INFO +88 -0
- dipolmol-1.1.1/src/dipolmol.egg-info/SOURCES.txt +11 -0
- dipolmol-1.1.1/src/dipolmol.egg-info/dependency_links.txt +1 -0
- dipolmol-1.1.1/src/dipolmol.egg-info/requires.txt +4 -0
- dipolmol-1.1.1/src/dipolmol.egg-info/top_level.txt +1 -0
dipolmol-1.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dipolmol
|
|
3
|
+
Version: 1.1.1
|
|
4
|
+
Summary: DiPolMol: tools for calculating molecular dipole moments, polarisabilities, and Hamiltonians
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: quantum physics,molecular physics,dipole moment,polarizability,AMO physics,CaF
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Intended Audience :: Science/Research
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: numpy>=1.23
|
|
19
|
+
Requires-Dist: scipy>=1.9
|
|
20
|
+
Requires-Dist: sympy>=1.11
|
|
21
|
+
Requires-Dist: matplotlib>=3.6
|
|
22
|
+
|
|
23
|
+
DiPolMol-Py
|
|
24
|
+
===========
|
|
25
|
+
A Python package to calculate the rotational and hyperfine structure of doublet-Sigma molecules (e.g., CaF, BaF, SrF) in the presence of external fields.
|
|
26
|
+
|
|
27
|
+
DiPolMol-Py is licensed under a BSD 3 clause license, a copy can be found `See the [LICENSE](LICENSE) file'.
|
|
28
|
+
If you use our work for academic purposes you can cite us using:
|
|
29
|
+
|
|
30
|
+
B.Humphreys *et al.* DiPolMol-Py: A Python package for calculations for $^{2}{\Sigma}$ ground-state molecules (https://arxiv.org/pdf/2503.21663).
|
|
31
|
+
|
|
32
|
+
Installation
|
|
33
|
+
----------
|
|
34
|
+
|
|
35
|
+
run: pip install dipolmol
|
|
36
|
+
|
|
37
|
+
This installs the latest stable release and all required dependencies.
|
|
38
|
+
|
|
39
|
+
Package structure
|
|
40
|
+
-------------
|
|
41
|
+
**Programme Files:**
|
|
42
|
+
|
|
43
|
+
*Hamiltonian* – used to build the required Hamiltonian using matrix representation, including the field-free Hamiltonian and in the presence of magnetic, dc electric and off-resonant light fields.
|
|
44
|
+
|
|
45
|
+
*Calculate* – uses the eigenstates and eigenenergies found from diagonalising the Hamiltonian to run various calculations. Can be used to identify quantum numbers of eigenstates, calculate transition dipole moments and polarisabilities.
|
|
46
|
+
|
|
47
|
+
*Constants* – includes all known constants for CaF, BaF and SrF.
|
|
48
|
+
|
|
49
|
+
**Examples:**
|
|
50
|
+
|
|
51
|
+
There are three example files for calculating the energy structure in the presence of a magnetic, off-resonant light and electric field (*example_Bfield, example_ac_Efield, example_dc_Efield*).
|
|
52
|
+
|
|
53
|
+
There are two files to calculate the electric and magnetic moments of states (*example_electric_moment, example_magnetic_moment*).
|
|
54
|
+
|
|
55
|
+
We provide *example_polarisability* to calculate the polarisability for a given wavelength and polarisation of light.
|
|
56
|
+
|
|
57
|
+
Finally, *example_tdm* can be used to calculate the transition dipole moment.
|
|
58
|
+
|
|
59
|
+
Example
|
|
60
|
+
-------
|
|
61
|
+
.. code-block:: python
|
|
62
|
+
|
|
63
|
+
import numpy as np
|
|
64
|
+
import dipolmol.hamiltonian as hamiltonian
|
|
65
|
+
import dipolmol.calculate as calc
|
|
66
|
+
from dipolmol.constants import SrF
|
|
67
|
+
|
|
68
|
+
Nmax=4 #Identify the maximum N
|
|
69
|
+
H0,H_B,H_dc,H_ac
|
|
70
|
+
= hamiltonian.build
|
|
71
|
+
(Nmax,SrF,zeeman=True,Edc=False
|
|
72
|
+
,Eac=False)
|
|
73
|
+
|
|
74
|
+
B = np.linspace(0,100,5000)*1e-4 #Tesla
|
|
75
|
+
|
|
76
|
+
H = H_0[..., None] + H_B[..., None]*B
|
|
77
|
+
H = H.transpose(2,0,1)
|
|
78
|
+
|
|
79
|
+
energies, states, label_list =
|
|
80
|
+
calc.solve(H, Nmax, SrF,label=True, B)
|
|
81
|
+
|
|
82
|
+
Resulting plot of above code
|
|
83
|
+
|
|
84
|
+
.. image:: Images/zeeman_SrF_plot.png
|
|
85
|
+
:width: 400
|
|
86
|
+
:alt: Resulting plot of above example
|
|
87
|
+
|
|
88
|
+
For more examples of usage, see the ``./Examples`` module.
|
dipolmol-1.1.1/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
DiPolMol-Py
|
|
2
|
+
===========
|
|
3
|
+
A Python package to calculate the rotational and hyperfine structure of doublet-Sigma molecules (e.g., CaF, BaF, SrF) in the presence of external fields.
|
|
4
|
+
|
|
5
|
+
DiPolMol-Py is licensed under a BSD 3 clause license, a copy can be found `See the [LICENSE](LICENSE) file'.
|
|
6
|
+
If you use our work for academic purposes you can cite us using:
|
|
7
|
+
|
|
8
|
+
B.Humphreys *et al.* DiPolMol-Py: A Python package for calculations for $^{2}{\Sigma}$ ground-state molecules (https://arxiv.org/pdf/2503.21663).
|
|
9
|
+
|
|
10
|
+
Installation
|
|
11
|
+
----------
|
|
12
|
+
|
|
13
|
+
run: pip install dipolmol
|
|
14
|
+
|
|
15
|
+
This installs the latest stable release and all required dependencies.
|
|
16
|
+
|
|
17
|
+
Package structure
|
|
18
|
+
-------------
|
|
19
|
+
**Programme Files:**
|
|
20
|
+
|
|
21
|
+
*Hamiltonian* – used to build the required Hamiltonian using matrix representation, including the field-free Hamiltonian and in the presence of magnetic, dc electric and off-resonant light fields.
|
|
22
|
+
|
|
23
|
+
*Calculate* – uses the eigenstates and eigenenergies found from diagonalising the Hamiltonian to run various calculations. Can be used to identify quantum numbers of eigenstates, calculate transition dipole moments and polarisabilities.
|
|
24
|
+
|
|
25
|
+
*Constants* – includes all known constants for CaF, BaF and SrF.
|
|
26
|
+
|
|
27
|
+
**Examples:**
|
|
28
|
+
|
|
29
|
+
There are three example files for calculating the energy structure in the presence of a magnetic, off-resonant light and electric field (*example_Bfield, example_ac_Efield, example_dc_Efield*).
|
|
30
|
+
|
|
31
|
+
There are two files to calculate the electric and magnetic moments of states (*example_electric_moment, example_magnetic_moment*).
|
|
32
|
+
|
|
33
|
+
We provide *example_polarisability* to calculate the polarisability for a given wavelength and polarisation of light.
|
|
34
|
+
|
|
35
|
+
Finally, *example_tdm* can be used to calculate the transition dipole moment.
|
|
36
|
+
|
|
37
|
+
Example
|
|
38
|
+
-------
|
|
39
|
+
.. code-block:: python
|
|
40
|
+
|
|
41
|
+
import numpy as np
|
|
42
|
+
import dipolmol.hamiltonian as hamiltonian
|
|
43
|
+
import dipolmol.calculate as calc
|
|
44
|
+
from dipolmol.constants import SrF
|
|
45
|
+
|
|
46
|
+
Nmax=4 #Identify the maximum N
|
|
47
|
+
H0,H_B,H_dc,H_ac
|
|
48
|
+
= hamiltonian.build
|
|
49
|
+
(Nmax,SrF,zeeman=True,Edc=False
|
|
50
|
+
,Eac=False)
|
|
51
|
+
|
|
52
|
+
B = np.linspace(0,100,5000)*1e-4 #Tesla
|
|
53
|
+
|
|
54
|
+
H = H_0[..., None] + H_B[..., None]*B
|
|
55
|
+
H = H.transpose(2,0,1)
|
|
56
|
+
|
|
57
|
+
energies, states, label_list =
|
|
58
|
+
calc.solve(H, Nmax, SrF,label=True, B)
|
|
59
|
+
|
|
60
|
+
Resulting plot of above code
|
|
61
|
+
|
|
62
|
+
.. image:: Images/zeeman_SrF_plot.png
|
|
63
|
+
:width: 400
|
|
64
|
+
:alt: Resulting plot of above example
|
|
65
|
+
|
|
66
|
+
For more examples of usage, see the ``./Examples`` module.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "dipolmol"
|
|
7
|
+
version = "1.1.1"
|
|
8
|
+
description = "DiPolMol: tools for calculating molecular dipole moments, polarisabilities, and Hamiltonians"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
keywords = [
|
|
15
|
+
"quantum physics",
|
|
16
|
+
"molecular physics",
|
|
17
|
+
"dipole moment",
|
|
18
|
+
"polarizability",
|
|
19
|
+
"AMO physics",
|
|
20
|
+
"CaF"
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
classifiers = [
|
|
24
|
+
"Development Status :: 3 - Alpha",
|
|
25
|
+
"Intended Audience :: Science/Research",
|
|
26
|
+
"License :: OSI Approved :: MIT License",
|
|
27
|
+
"Programming Language :: Python :: 3",
|
|
28
|
+
"Programming Language :: Python :: 3.9",
|
|
29
|
+
"Programming Language :: Python :: 3.10",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Topic :: Scientific/Engineering :: Physics",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
dependencies = [
|
|
36
|
+
"numpy>=1.23",
|
|
37
|
+
"scipy>=1.9",
|
|
38
|
+
"sympy>=1.11",
|
|
39
|
+
"matplotlib>=3.6",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# ---------------- setuptools config ---------------- #
|
|
43
|
+
|
|
44
|
+
[tool.setuptools]
|
|
45
|
+
package-dir = {"" = "src"}
|
|
46
|
+
|
|
47
|
+
[tool.setuptools.packages.find]
|
|
48
|
+
where = ["src"]
|
dipolmol-1.1.1/setup.cfg
ADDED
|
File without changes
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
from . import hamiltonian as hamiltonian
|
|
2
|
+
from .constants import CaF
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import scipy.constants
|
|
6
|
+
from sympy.physics.wigner import wigner_3j as sympy_wig_3j
|
|
7
|
+
from sympy.physics.wigner import wigner_6j as sympy_wig_6j
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
###############################################################################
|
|
10
|
+
# Start by definining a bunch of constants that are needed for the code #
|
|
11
|
+
###############################################################################
|
|
12
|
+
|
|
13
|
+
'''
|
|
14
|
+
Important note!
|
|
15
|
+
|
|
16
|
+
All units in this code are SI i.e. elements in the Hamiltonian have units
|
|
17
|
+
of Joules. Outputs will be on the order of 1e-30
|
|
18
|
+
|
|
19
|
+
'''
|
|
20
|
+
|
|
21
|
+
h = scipy.constants.h
|
|
22
|
+
muN = scipy.constants.physical_constants['nuclear magneton'][0]
|
|
23
|
+
bohr = scipy.constants.physical_constants['Bohr radius'][0]
|
|
24
|
+
epsilon_0 = scipy.constants.epsilon_0
|
|
25
|
+
c = scipy.constants.c
|
|
26
|
+
|
|
27
|
+
#Defining Wigner symbols to ensure on float errors
|
|
28
|
+
def wigner_3j(a, b, c, d, e, f):
|
|
29
|
+
a = float(a)
|
|
30
|
+
b = float(b)
|
|
31
|
+
c = float(c)
|
|
32
|
+
d = float(d)
|
|
33
|
+
e = float(e)
|
|
34
|
+
f = float(f)
|
|
35
|
+
return sympy_wig_3j(a,b,c,d,e,f)
|
|
36
|
+
|
|
37
|
+
def wigner_6j(a, b, c, d, e, f):
|
|
38
|
+
a = float(a)
|
|
39
|
+
b = float(b)
|
|
40
|
+
c = float(c)
|
|
41
|
+
d = float(d)
|
|
42
|
+
e = float(e)
|
|
43
|
+
f = float(f)
|
|
44
|
+
return sympy_wig_6j(a,b,c,d,e,f)
|
|
45
|
+
|
|
46
|
+
#Frequencies of each vibrational transition needed for polarisability calculation
|
|
47
|
+
def XXfreq(v1,v2,consts):
|
|
48
|
+
f = 100*c*(consts['XT'][v2]+2*consts['XB'][v2] - consts['XT'][v1])
|
|
49
|
+
return f
|
|
50
|
+
def XAfreq(Xv,Av,Omega,consts):
|
|
51
|
+
f = 100*c*(consts['AT'][Av]+consts['AA'][Av]*(Omega-1) - consts['XT'][Xv])
|
|
52
|
+
return f
|
|
53
|
+
def XBfreq(Xv,Bv,consts):
|
|
54
|
+
f = 100*c*consts['BT'][Bv] - consts['XT'][Xv]
|
|
55
|
+
return f
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def alpha_012(l, consts):
|
|
61
|
+
'''Calculates the polarisability of the X(v=0) state for a given wavelength l.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
l (float): Wavelength at which to calculate the polarisability at. (m)
|
|
65
|
+
consts (dict): Dictionary of constants for the molecule to be calculated.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
alpha (list): List containing alpha_0, alpha_1 and alpha_2, the scalar, vector
|
|
69
|
+
and tensor components of molecular polarisability respectively.'''
|
|
70
|
+
|
|
71
|
+
f=c/(l*10**-9)
|
|
72
|
+
XXmu = consts['d0']
|
|
73
|
+
XXvibmu = consts['XXvibmu']
|
|
74
|
+
XAmu = consts['XAmu']
|
|
75
|
+
XBmu = consts['XBmu']
|
|
76
|
+
XAfc = consts['XAfc']
|
|
77
|
+
XBfc = consts['XBfc']
|
|
78
|
+
#There are three alpha terms: alpha parallel (Sigma overlap) and alpha perpendicular (Pi overlap for both Omega values)
|
|
79
|
+
parallel_terms = [[XXfreq(0,0,consts),XXmu],[XXfreq(0,1,consts),XXvibmu],[XBfreq(0, 0,consts),XBmu*(XBfc[0][0]**0.5)]]
|
|
80
|
+
perp_terms1 = [[XAfreq(0,0,0.5,consts), XAmu*(XAfc[0][0]**0.5)],[XAfreq(0,1,0.5,consts), XAmu*(XAfc[0][1]**0.5)]]
|
|
81
|
+
perp_terms3 = [[XAfreq(0,0,1.5,consts), XAmu*(XAfc[0][0]**0.5)],[XAfreq(0,1,1.5,consts), XAmu*(XAfc[0][1]**0.5)]]
|
|
82
|
+
a_par = 0
|
|
83
|
+
a_perp1 = 0
|
|
84
|
+
a_perp3 = 0
|
|
85
|
+
for i in range(len(parallel_terms)):
|
|
86
|
+
a_par += 1/h*(1/(parallel_terms[i][0]+f)+1/(parallel_terms[i][0]-f))*parallel_terms[i][1]**2
|
|
87
|
+
for i in range(len(perp_terms1)):
|
|
88
|
+
a_perp1 += 1/h*(1/(perp_terms1[i][0]+f)+1/(perp_terms1[i][0]-f))*perp_terms1[i][1]**2
|
|
89
|
+
a_perp3 += 1/h*(1/(perp_terms3[i][0]+f)+1/(perp_terms3[i][0]-f))*perp_terms3[i][1]**2
|
|
90
|
+
alpha_0 = 1/3*(a_par+a_perp1+a_perp3)*10**4/(2*c*epsilon_0)*10**-4
|
|
91
|
+
alpha_1 = 1/2*(f/XAfreq(0,0,0.5,consts)*a_perp1-f/XAfreq(0,0,1.5,consts)*a_perp3)*10**4/(2*c*epsilon_0)*10**-4
|
|
92
|
+
alpha_2 = 1/3*(2*a_par - a_perp1 - a_perp3)*10**4/(2*c*epsilon_0)*10**-4
|
|
93
|
+
return [alpha_0,alpha_1, alpha_2]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def dipole(Nmax,Consts,M):
|
|
97
|
+
''' Generates the induced dipole moment operator for a Rigid rotor.
|
|
98
|
+
Expanded to cover state vectors in the uncoupled hyperfine basis.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
Nmax (int): maximum rotational states.
|
|
102
|
+
Consts (dict): Dictionary of constants for the molecule to be calculated.
|
|
103
|
+
M (float): index indicating the helicity of the dipole field. -1 = S+, 0 = Pi, +1 = S-
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Dmat (np.ndarray): - dipole matrix
|
|
107
|
+
'''
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
I = Consts['I']
|
|
111
|
+
S = Consts['S']
|
|
112
|
+
Ishape = int(2*I+1)
|
|
113
|
+
Sshape = int(2*S+1)
|
|
114
|
+
shape = np.sum(np.array([2*x+1 for x in range(0,Nmax+1)]))
|
|
115
|
+
dmat = np.zeros((shape,shape),dtype= complex)
|
|
116
|
+
dmat = (np.kron(dmat,np.kron(np.identity(Ishape),
|
|
117
|
+
np.identity(Sshape))))
|
|
118
|
+
i=0
|
|
119
|
+
j=0
|
|
120
|
+
|
|
121
|
+
for N1 in range(0,Nmax+1):
|
|
122
|
+
for J1 in np.arange(np.abs(N1-S),(N1+S+1),1):
|
|
123
|
+
for F1 in np.arange(J1-I,(J1+I+1),1):
|
|
124
|
+
for mF1 in np.arange(-F1,(F1+1),1):
|
|
125
|
+
for N2 in range(0,Nmax+1):
|
|
126
|
+
for J2 in np.arange(np.abs(N2-S),(N2+S+1),1):
|
|
127
|
+
for F2 in np.arange(J2-I,(J2+I+1),1):
|
|
128
|
+
for mF2 in np.arange(-F2,+(F2+1),1):
|
|
129
|
+
dmat[i,j] = np.round((-1+0j)**(F2-mF2+J1+J2+F1+N2+1))*\
|
|
130
|
+
((2*F1+1)*(2*F2+1)*(2*J1+1)*(2*J2+1))**0.5*((2*N1+1)*(2*N2+1))**0.5*(-1)**N2*wigner_3j(N2,1,N1,0,0,0)*\
|
|
131
|
+
wigner_3j(F2,1,F1,-mF2,M,mF1)*wigner_6j(J1,F1,I,F2,J2,1)*wigner_6j(N1,J1,0.5,J2,N2,1)
|
|
132
|
+
|
|
133
|
+
i+=1
|
|
134
|
+
i=0
|
|
135
|
+
j+=1
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
return dmat
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def transition_dipole_moment(Nmax,Consts,M,states,gs,locs=None):
|
|
142
|
+
''' Function to calculate the Transition Dipole Moment between a state gs
|
|
143
|
+
and a range of states. Returns the TDM in units of the permanent dipole
|
|
144
|
+
moment (d0).
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
Nmax (int): Maximum rotational quantum number in original calculations
|
|
148
|
+
Consts (dict): Dictionary of constants for the molecule to be calculated
|
|
149
|
+
M (float): Helicity of Transition, -1 = S+, 0 = Pi, +1 = S-
|
|
150
|
+
States (np.ndarray): matrix for eigenstates of problem output from np.linalg.eig
|
|
151
|
+
gs (int): index of ground state.
|
|
152
|
+
|
|
153
|
+
kwargs:
|
|
154
|
+
locs (list of ints): optional argument to calculate for subset of States, should be an
|
|
155
|
+
array-like.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
TDM (list of floats): transition dipole moment between gs and States.
|
|
159
|
+
|
|
160
|
+
'''
|
|
161
|
+
states = states[0]
|
|
162
|
+
dipole_op = dipole(Nmax,Consts,M)
|
|
163
|
+
|
|
164
|
+
gs = np.conj(states[:,gs])
|
|
165
|
+
if locs != None :
|
|
166
|
+
states = states[:,locs]
|
|
167
|
+
|
|
168
|
+
tdm = np.einsum('i,ij,jk->k',gs,dipole_op,states).real
|
|
169
|
+
|
|
170
|
+
return tdm
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def magnetic_moment(States, Nmax, Consts):
|
|
174
|
+
'''Returns the magnetic moments of each eigenstate.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
States (np.ndarray): matrix for eigenstates of problem output from np.linalg.eig
|
|
178
|
+
Nmax (int): Maximum rotational quantum number in original calculations
|
|
179
|
+
Consts (dict): Dictionary of constants for the molecule to be calculated
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
mu (list of floats): magnetic moment for each eigenstate in States.
|
|
183
|
+
|
|
184
|
+
'''
|
|
185
|
+
|
|
186
|
+
muz = -1*hamiltonian.H_zee(Nmax,Consts,Consts['I'],Consts['S'])
|
|
187
|
+
|
|
188
|
+
mu =np.einsum('ijk,jl,ilk->ik',
|
|
189
|
+
np.conjugate(States),muz,
|
|
190
|
+
States)
|
|
191
|
+
return mu
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def electric_moment(States, Nmax, Consts):
|
|
195
|
+
'''Returns the electric dipole moments of each eigenstate
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
States (np.ndarray): matrix for eigenstates of problem output from np.linalg.eig
|
|
199
|
+
Nmax (int): Maximum rotational quantum number in original calculations
|
|
200
|
+
Consts (dict): Dictionary of constants for the molecule to be calculated
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
d (list of floats): electric dipole moment for each eigenstate in States
|
|
204
|
+
'''
|
|
205
|
+
|
|
206
|
+
dz = -1*hamiltonian.H_dc(Nmax,Consts,Consts['I'],Consts['S'])
|
|
207
|
+
|
|
208
|
+
d =np.einsum('ijk,jl,ilk->ik',
|
|
209
|
+
np.conjugate(States),dz,
|
|
210
|
+
States)
|
|
211
|
+
return d
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def sort_smooth(energy, states):
|
|
216
|
+
''' Sort states to remove false avoided crossings.
|
|
217
|
+
This is a function to ensure that all eigenstates plotted change
|
|
218
|
+
adiabatically, it does this by assuming that step to step the eigenstates
|
|
219
|
+
should vary by only a small amount (i.e. that the step size is fine) and
|
|
220
|
+
arranging states to maximise the overlap one step to the next.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
Energy (np.ndarray) : np.ndarray containing the eigenergies, as from np.linalg.eig
|
|
224
|
+
States (np.ndarray): np.ndarray containing the states, in the same order as Energy
|
|
225
|
+
Returns:
|
|
226
|
+
Energy (np.ndarray) : np.ndarray containing the eigenergies, as from np.linalg.eig
|
|
227
|
+
States (np.ndarray): np.ndarray containing the states, in the same order as Energy E[x,i] -> States[x,:,i]
|
|
228
|
+
'''
|
|
229
|
+
ls = np.arange(states.shape[2],dtype="int") #from 0 to number of states
|
|
230
|
+
|
|
231
|
+
number_iterations = len(energy[:,0]) #ie the number of B field values
|
|
232
|
+
|
|
233
|
+
for i in range(2, number_iterations):
|
|
234
|
+
'''
|
|
235
|
+
This loop sorts the eigenstates such that they maintain some
|
|
236
|
+
continuity. Each eigenstate should be chosen to maximise the overlap
|
|
237
|
+
with the previous.
|
|
238
|
+
'''
|
|
239
|
+
#calculate the overlap of the ith and jth eigenstates
|
|
240
|
+
overlaps = np.einsum('ij,ik->jk',
|
|
241
|
+
np.conjugate(states[i-1,:,:]),states[i,:,:])
|
|
242
|
+
orig2 = states[i,:,:].copy()
|
|
243
|
+
orig1 = energy[i,:].copy()
|
|
244
|
+
#insert location of maximums into array ls
|
|
245
|
+
np.argmax(np.abs(overlaps),axis=1,out=ls)
|
|
246
|
+
|
|
247
|
+
for k in range(states.shape[2]):
|
|
248
|
+
l = ls[k]
|
|
249
|
+
if l!=k:
|
|
250
|
+
energy[i,k] = orig1[l].copy()
|
|
251
|
+
states[i,:,k] = orig2[:,l].copy()
|
|
252
|
+
return energy, states
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def label_FmF_states(States, Nmax, consts, B):
|
|
259
|
+
"""
|
|
260
|
+
Function that takes the array of eigenstates as generated from the hamiltonian
|
|
261
|
+
(and processed with sort_smooth to avoid false avoided crossings) and assigns
|
|
262
|
+
each an (N, F, mF) label.
|
|
263
|
+
|
|
264
|
+
Inputs:
|
|
265
|
+
States (np.ndarray) : np.ndarray containing the eigenstates, as from np.linalg.eig
|
|
266
|
+
Nmax (int) : Maximum N state to consider in the calculations
|
|
267
|
+
B (int/float/list/array) : Magnetic field values used to calculate the eigenstates
|
|
268
|
+
consts (dict): Dictionary of constants for the molecule to be calculated
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
FmF_labels or F_labels (list): list of [N, F, mF] or [N, F] labels, one for each of the eigenstates in the input
|
|
272
|
+
"""
|
|
273
|
+
|
|
274
|
+
S = consts['S']
|
|
275
|
+
I= consts['I']
|
|
276
|
+
|
|
277
|
+
#initialise error counters to 0
|
|
278
|
+
zeromagfielderror = 0
|
|
279
|
+
F_error = 0
|
|
280
|
+
mF_error = 0
|
|
281
|
+
|
|
282
|
+
startingstateindex = 0
|
|
283
|
+
|
|
284
|
+
#check the B fields input by the user, select a non-zero B field to label at
|
|
285
|
+
if type(B) == float or type(B) == int:
|
|
286
|
+
States = States[0]
|
|
287
|
+
if B == 0:
|
|
288
|
+
zeromagfielderror += 1
|
|
289
|
+
else:
|
|
290
|
+
if B[0] != 0:
|
|
291
|
+
States = States[0]
|
|
292
|
+
else:
|
|
293
|
+
if len(States) > 1:
|
|
294
|
+
States = States[1]
|
|
295
|
+
startingstateindex += 1
|
|
296
|
+
else:
|
|
297
|
+
States = States[0]
|
|
298
|
+
zeromagfielderror += 1
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
#generate a list of the possible (N, J, F, mF) states
|
|
303
|
+
statelist = []
|
|
304
|
+
for N1 in range(0,Nmax+1):
|
|
305
|
+
for J1 in np.arange(np.abs(N1-S),(N1+S+1),1):
|
|
306
|
+
for F1 in np.arange(J1-I,(J1+I+1),1):
|
|
307
|
+
for mF1 in np.arange(-F1,(F1+1),1):
|
|
308
|
+
state = [N1, J1, F1, mF1]
|
|
309
|
+
statelist.append(state)
|
|
310
|
+
|
|
311
|
+
FmF_labels = []
|
|
312
|
+
F_labels = []
|
|
313
|
+
|
|
314
|
+
#now match the (N,J,F,mF) labels with the eigenstates
|
|
315
|
+
for i in range(len(States)): #For each of the eigenstates
|
|
316
|
+
statebreakdown = []
|
|
317
|
+
for n in range(len(States)): #loop through each value in the eigenstate
|
|
318
|
+
if np.round(States[:,i][n],1) != 0: #consider the non-zero coeff
|
|
319
|
+
statebreakdown.append(statelist[n])
|
|
320
|
+
#following can be useful to check states
|
|
321
|
+
#print(f'{States[:,i][n]} coeff of {statelist[n]}' )
|
|
322
|
+
|
|
323
|
+
#Confirming consistency in the F,mF labelling of the states
|
|
324
|
+
if len(statebreakdown) > 1: #only do this if there are multiple (F,mF) states to consider
|
|
325
|
+
avgF = np.mean(statebreakdown, axis=0)[2]
|
|
326
|
+
avgmF = np.mean(statebreakdown, axis=0)[3]
|
|
327
|
+
|
|
328
|
+
if avgF != statebreakdown[0][2] or statebreakdown[0][2] != statebreakdown[1][2]:
|
|
329
|
+
F_error += 1
|
|
330
|
+
|
|
331
|
+
if avgmF != statebreakdown[0][3] or statebreakdown[0][3] != statebreakdown[1][3]:
|
|
332
|
+
mF_error += 1
|
|
333
|
+
F_labels.append([int(statebreakdown[0][0]), int(statebreakdown[0][2])])
|
|
334
|
+
|
|
335
|
+
if avgF == statebreakdown[0][2] and statebreakdown[0][2] == statebreakdown[1][2] and avgmF == statebreakdown[0][3] and statebreakdown[0][3] == statebreakdown[1][3]:
|
|
336
|
+
FmF_labels.append([int(statebreakdown[0][0]), int(statebreakdown[0][2]), int(statebreakdown[0][3])])
|
|
337
|
+
F_labels.append([int(statebreakdown[0][0]), int(statebreakdown[0][2])])
|
|
338
|
+
|
|
339
|
+
else: #if we are only considering one state
|
|
340
|
+
FmF_labels.append([int(statebreakdown[0][0]), int(statebreakdown[0][2]), int(statebreakdown[0][3])])
|
|
341
|
+
F_labels.append([int(statebreakdown[0][0]), int(statebreakdown[0][2])])
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
#Various error messages
|
|
345
|
+
if zeromagfielderror != 0:
|
|
346
|
+
if mF_error != 0 and F_error == 0:
|
|
347
|
+
print('mF labelling failed as attempted to label at zero magnetic field: please input a non-zero magnetic field. Returning N,F labels instead.')
|
|
348
|
+
if F_error != 0:
|
|
349
|
+
print('mF, F labelling failed as attempted to label at too high a magnetic field: please input a smaller starting magnetic field value.')
|
|
350
|
+
else:
|
|
351
|
+
if F_error != 0:
|
|
352
|
+
if startingstateindex == 0:
|
|
353
|
+
print('mF, F labelling failed as attempted to label at too high a magnetic field: please input a smaller starting magnetic field value.')
|
|
354
|
+
else:
|
|
355
|
+
print('mF, F labelling failed as attempted to label at too high a magnetic field: please reduce the spacing between your input magnetic field values. Alternatively, start plotting at a small, non-zero magnetic field.')
|
|
356
|
+
if mF_error != 0 and F_error ==0:
|
|
357
|
+
if startingstateindex == 0:
|
|
358
|
+
print('mF labelling failed as attempted to label at too high a magnetic field: please input a smaller starting magnetic field value. Returning N,F labels instead.')
|
|
359
|
+
else:
|
|
360
|
+
print('mF labelling failed as attempted to label at too high a magnetic field: please reduce the spacing between your input magnetic field values. Alternatively, start plotting at a small, non-zero magnetic field. Returning N,F labels instead.')
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
if F_error == 0 and mF_error == 0:
|
|
364
|
+
return FmF_labels
|
|
365
|
+
if F_error == 0 and mF_error != 0:
|
|
366
|
+
return F_labels
|
|
367
|
+
if F_error != 0:
|
|
368
|
+
return None
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def solve(H, Nmax, consts, label, B=None):
|
|
374
|
+
"""Function that combines the diagonalisation of the Hamiltonian generated by the
|
|
375
|
+
hamiltonian.build method, and the sorting of the states performed by the
|
|
376
|
+
calculate.sort_smooth function. Generates the sorted set of eigenstates and
|
|
377
|
+
eigenenergies for the given Hamiltonian.
|
|
378
|
+
|
|
379
|
+
Inputs:
|
|
380
|
+
H (np.ndarray): Hamiltonian to generate the eigenstates and eigenenergies for,
|
|
381
|
+
as generated by hamiltonian.build method.
|
|
382
|
+
Nmax (int): Maximum rotational state to consider in the calculations.
|
|
383
|
+
consts (dict): Dictionary of constants for the molecule to be calculated
|
|
384
|
+
B (list/array): Magnetic field values used to calculate the eigenstates
|
|
385
|
+
label (boolean): If True, return the F, mF state labels for the eigenstates
|
|
386
|
+
"""
|
|
387
|
+
#generate the eigenenergies and eigenstates
|
|
388
|
+
energies, states = np.linalg.eigh(H)
|
|
389
|
+
#apply sort_smooth to the eigenstates to maintain consistency with the labelling
|
|
390
|
+
energies, states = sort_smooth(energies, states)
|
|
391
|
+
|
|
392
|
+
if label:
|
|
393
|
+
if type(B) == float or type(B) == int or type(B) == list or type(B) == np.ndarray:
|
|
394
|
+
#ie if a suitable type of B value is provided
|
|
395
|
+
label_list = label_FmF_states(states, Nmax, consts, B)
|
|
396
|
+
|
|
397
|
+
return energies, states, label_list
|
|
398
|
+
|
|
399
|
+
else:
|
|
400
|
+
print('Please provide magnetic field value(s) to label at.')
|
|
401
|
+
return energies, states, None
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
else:
|
|
405
|
+
return energies, states
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import scipy.constants
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
###############################################################################
|
|
5
|
+
# Molecular Constants
|
|
6
|
+
###############################################################################
|
|
7
|
+
#Here are some starting dictionaries for various molecules.
|
|
8
|
+
#References given, but check up to date if precision needed!
|
|
9
|
+
e = scipy.constants.e
|
|
10
|
+
h = scipy.constants.h
|
|
11
|
+
muN = scipy.constants.physical_constants['nuclear magneton'][0]
|
|
12
|
+
muB = scipy.constants.physical_constants['Bohr magneton'][0]
|
|
13
|
+
bohr = scipy.constants.physical_constants['Bohr radius'][0]
|
|
14
|
+
eps0 = scipy.constants.epsilon_0
|
|
15
|
+
a0 = scipy.constants.physical_constants['atomic unit of length'][0]
|
|
16
|
+
c = scipy.constants.c
|
|
17
|
+
DebyeSI = 3.33564e-30
|
|
18
|
+
inv_cm_to_Hz = 100*c
|
|
19
|
+
|
|
20
|
+
CaF = {"I":0.5,
|
|
21
|
+
"S":0.5,
|
|
22
|
+
"d0": 1.02041*10**-29,#3.07*DebyeSI,
|
|
23
|
+
"Brot":10267.5387*10**6*h,
|
|
24
|
+
"Drot":0.01406*10**6*h,
|
|
25
|
+
"gamma": 39.65891*10**6*h,
|
|
26
|
+
"b" : 109.1839*10**6*h,
|
|
27
|
+
"c": 40.119*10**6*h,
|
|
28
|
+
"CC": 28.76*10**3*h,
|
|
29
|
+
"MuS": 2.002*muB,
|
|
30
|
+
"MuL": -1.86*10**-3*muB, #10.1103/PhysRevLett.124.063001
|
|
31
|
+
"MuN": 5.585*muN,
|
|
32
|
+
"MuR": -5.13*10**-5*muB, #10.1103/PhysRevLett.124.063001
|
|
33
|
+
"alpha_0":1.4*10**-3*h, #h*Hz/(W/m^2) at 780 nm calculated in Caldwell2020 https://arxiv.org/abs/1910.10689 NB these are alpha/2ce_0
|
|
34
|
+
"alpha_1":3*10**-5*h,
|
|
35
|
+
"alpha_2":-8*10**-4*h,
|
|
36
|
+
"Beta":0,#angle of polarisation for polarisability function
|
|
37
|
+
#Transition dipole moments
|
|
38
|
+
"XXvibmu" : 0.0999*e*a0, #
|
|
39
|
+
"XAmu" : 2.03*1e-29,#2.3439*e*a0, #P. J. Dagdigian, H. W. Cruse, and R. N. Zare, J. Chem. Phys. 60, 2330 (1974).
|
|
40
|
+
"XBmu" : 1.71*e*a0, # P. J. Dagdigian, H. W. Cruse, and R. N. Zare, J. Chem. Phys. 60, 2330 (1974).
|
|
41
|
+
#FC factors from M. Pelegrini, C. S. Vivacqua, O. Roberto-Neto, F. R. Ornellas, and F. B. Machado. Braz. J. Phys. 35.4 A (2005), pp. 950–956. X-A
|
|
42
|
+
# X-B M. Dulick, P. F. Bernath, and R. W. Field. Can. J. Phys. 58.5 (1980), pp. 703–712.
|
|
43
|
+
"XAfc" : [[0.964,0.036,0],[0.035,0.895,0.07],[0.001,0.065,0.83]],
|
|
44
|
+
"XBfc" : [[9.992*10**-1,7.27*10**-4,3.809*10**-5],[7.396*10**-4,9.973*10**-1,1.814*10**-3],[2.473*10**-5,1.873*10**-3,9.945*10**-1]],
|
|
45
|
+
#Terms for vibrational transitions [v=0,1,2] Journal of Molecular Spectroscopy 197, 289–296 (1999)
|
|
46
|
+
#Only need for v=0 in X and B, and then V=0,1 in A
|
|
47
|
+
#In cm^-1
|
|
48
|
+
"XT" : np.array([0,582.8478,1159.9473]), #electronic splitting
|
|
49
|
+
"XB" : np.array([0.34248818,0.34005359, 0.33762897]),#rotational constants
|
|
50
|
+
"AT" : np.array([16529.653,17118.103,17700.49]), #electronic splitting
|
|
51
|
+
"AA" : np.array([71.491, 71.614, 71.737]), #Lambda doubling
|
|
52
|
+
"BT" : np.array([18832.031, 19398.254, 19958.276]), #electronic splitting
|
|
53
|
+
"BB" : np.array([0.341058, 0.338469, 0.335903]), # rotational constants
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
BaF = {"I":0.5,
|
|
57
|
+
"S":0.5,
|
|
58
|
+
"d0": 1.05739e-29,#3.17*DebyeSI,
|
|
59
|
+
"Brot":6473.9586572e6*h,# from PRA 94, 063415 (2016) https://journals.aps.org/pra/pdf/10.1103/PhysRevA.94.063415
|
|
60
|
+
"Drot":5.5296816e3*h,#1085*10**7*29979.2458*h,
|
|
61
|
+
"gamma": 80.95547199999999e6*h,
|
|
62
|
+
"b" : 63.509e6*h,
|
|
63
|
+
"c": 8.224e6*h,
|
|
64
|
+
"CC": 0,
|
|
65
|
+
"MuS": 2.002*muB,
|
|
66
|
+
"MuL": -0.028*muB,#Where did I get this?
|
|
67
|
+
"MuN": 5.585*muN,
|
|
68
|
+
"MuR" : 0,
|
|
69
|
+
#"alpha_0":,
|
|
70
|
+
#"alpha_1":,
|
|
71
|
+
#"alpha_2":,
|
|
72
|
+
"Beta":0 #angle of polarisation for polarisability function
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
SrF = {"I": 0.5,
|
|
77
|
+
"S": 0.5,
|
|
78
|
+
"d0": 3.4963*DebyeSI,
|
|
79
|
+
"Brot": 7487.6*10**6*h,
|
|
80
|
+
"Drot":0.0075*10**6*h,
|
|
81
|
+
"gamma": 74.795*10**6*h,
|
|
82
|
+
"b" : 97.0827*10**6*h,
|
|
83
|
+
"c":30.2675*10**6*h,
|
|
84
|
+
"CC": 0.0023*10**6*h,
|
|
85
|
+
"MuS": 2.002*muB, # g_s * muB
|
|
86
|
+
"MuN": 5.585*muN,
|
|
87
|
+
"MuL": -4.97*10**-3*muB, #10.1103/PhysRevLett.124.063001
|
|
88
|
+
"MuR": -4.77*10**-5*muB, #10.1103/PhysRevLett.124.063001
|
|
89
|
+
"Beta":0, #angle of polarisation for polarisability function
|
|
90
|
+
#Transition dipole moments
|
|
91
|
+
"XXvibmu" : 0.0999*e*a0, #MISSING
|
|
92
|
+
"XAmu" : 2.45*e*a0, #P. J. Dagdigian, H. W. Cruse, and R. N. Zare, J. Chem. Phys. 60, 2330 (1974).
|
|
93
|
+
"XBmu" : 1.94*e*a0, # Berg, L. E. et al. Chem. Phys. Lett. 248, (1996)
|
|
94
|
+
#FC factors from Hao et al. J. Chem. Phys. 151, 034302 (2019)
|
|
95
|
+
"XAfc" : [[0.9789,0.02054,4*10**-4],[0.02102,0.9377,0.03969],[2.72*10**-5,4.158*10**-2,8.978*10**-1]],
|
|
96
|
+
"XBfc" : [[9.961*10**-1,3.866*10**-3,3.604*10**-6],[3.856*10**-3,9.881*10**-1,8*10**-3],[1.343*10**-5,7.959*10**-3,9.796*10**-1]],
|
|
97
|
+
#Terms for vibrational transitions [v=0,1,2] - for v=0 find in J. Barry thesis table 2.6
|
|
98
|
+
"XT" : np.array([0,0,0])*inv_cm_to_Hz, #electronic splitting
|
|
99
|
+
"XB" : np.array([7.4876,7.44123,7.395])*10**9,#rotational constants J. Barry thesis table 2.8
|
|
100
|
+
|
|
101
|
+
"AT" : np.array([15072.09,0,0])*inv_cm_to_Hz, #electronic splitting
|
|
102
|
+
"AA" : np.array([281.46138,0,0])*inv_cm_to_Hz, #Lambda doubling
|
|
103
|
+
|
|
104
|
+
"BT" : np.array([17267.41,0,0])*inv_cm_to_Hz, #electronic splitting
|
|
105
|
+
"BB" : np.array([0.249396,0,0])*inv_cm_to_Hz, # rotational constants
|
|
106
|
+
}
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from sympy.physics.wigner import wigner_3j as sympy_wig_3j
|
|
3
|
+
from sympy.physics.wigner import wigner_6j as sympy_wig_6j
|
|
4
|
+
|
|
5
|
+
from sympy import KroneckerDelta
|
|
6
|
+
from scipy.linalg import block_diag
|
|
7
|
+
import scipy.constants
|
|
8
|
+
from scipy.special import sph_harm
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
'''
|
|
11
|
+
This module contains the main code to calculate the hyperfine structure of
|
|
12
|
+
singlet -sigma molecules. In usual circumstances most of the functions within
|
|
13
|
+
are not user-oriented.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
Basic usage of this module is for accessing the eigenstates and
|
|
17
|
+
eigenvalues of the molecule in question. This is most easily done
|
|
18
|
+
by combining this module with the user's favourite linear algebra module.
|
|
19
|
+
For instance to find the zero-field hyperfine states of Molecule::
|
|
20
|
+
|
|
21
|
+
$ from diatom import Hamiltonian
|
|
22
|
+
$ from np import linalg as la
|
|
23
|
+
$ H0,Hz,HDC,HAC = Hamiltonian.Build_Hamiltonians(5,Molecule)
|
|
24
|
+
$ ev,es = la.eigh(H0)
|
|
25
|
+
'''
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
###############################################################################
|
|
29
|
+
# Start by definining constants that are needed for the code #
|
|
30
|
+
###############################################################################
|
|
31
|
+
|
|
32
|
+
'''
|
|
33
|
+
Important note!
|
|
34
|
+
|
|
35
|
+
All units in this code are SI i.e. elements in the Hamiltonian have units
|
|
36
|
+
of Joules. Outputs will be on the order of 1e-30
|
|
37
|
+
|
|
38
|
+
'''
|
|
39
|
+
|
|
40
|
+
h = scipy.constants.h
|
|
41
|
+
muN = scipy.constants.physical_constants['nuclear magneton'][0]
|
|
42
|
+
bohr = scipy.constants.physical_constants['Bohr radius'][0]
|
|
43
|
+
eps0 = scipy.constants.epsilon_0
|
|
44
|
+
c = scipy.constants.c
|
|
45
|
+
pi = np.pi
|
|
46
|
+
|
|
47
|
+
DebyeSI = 3.33564e-30
|
|
48
|
+
""" Conversion factor from debyes to J/V/m """
|
|
49
|
+
|
|
50
|
+
###############################################################################
|
|
51
|
+
# Functions for the calculations to use #
|
|
52
|
+
###############################################################################
|
|
53
|
+
|
|
54
|
+
def wigner_3j(a, b, c, d, e, f):
|
|
55
|
+
a = float(a)
|
|
56
|
+
b = float(b)
|
|
57
|
+
c = float(c)
|
|
58
|
+
d = float(d)
|
|
59
|
+
e = float(e)
|
|
60
|
+
f = float(f)
|
|
61
|
+
return sympy_wig_3j(a,b,c,d,e,f)
|
|
62
|
+
|
|
63
|
+
def wigner_6j(a, b, c, d, e, f):
|
|
64
|
+
a = float(a)
|
|
65
|
+
b = float(b)
|
|
66
|
+
c = float(c)
|
|
67
|
+
d = float(d)
|
|
68
|
+
e = float(e)
|
|
69
|
+
f = float(f)
|
|
70
|
+
return sympy_wig_6j(a,b,c,d,e,f)
|
|
71
|
+
|
|
72
|
+
def wigner_D(l, m, alpha, beta, gamma):
|
|
73
|
+
''' The Wigner D matrix with labels l and m.
|
|
74
|
+
|
|
75
|
+
Calculates the Wigner D Matrix for the given Alpha,beta,gamma in radians.
|
|
76
|
+
The wigner-D matrices represent rotations of angular momentum operators.
|
|
77
|
+
The indices l and m determine the value of the matrix.
|
|
78
|
+
The second index (m') is always zero.
|
|
79
|
+
|
|
80
|
+
The input angles are the x-z-x euler angles
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
l (int) : order of wigner Matrix
|
|
84
|
+
m (float): first index of Wigner Matrix
|
|
85
|
+
alpha,beta,gamma (float) : x,z,x Euler angles in radians
|
|
86
|
+
Returns:
|
|
87
|
+
D (float) : Value of the wigner-D matrix
|
|
88
|
+
'''
|
|
89
|
+
prefactor = np.sqrt((4*np.pi)/(2*l+1))
|
|
90
|
+
function = np.conj(sph_harm(m,l,alpha,beta))
|
|
91
|
+
return prefactor*function
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def H_rot(Nmax,consts,I,S):
|
|
95
|
+
''' Calculates <N,J,F,mF| H_rot |N',J',F', mF'>. The rotational hamiltonian H_rot
|
|
96
|
+
is diagonal in this basis, and this function iterates over N, J, F, mF, N',
|
|
97
|
+
J', F', mF' to build a matrix and evaluate the diagonal elements.
|
|
98
|
+
This function is based off Jesus Aldegunde's FORTRAN 77 code.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
Nmax (int): Maximum rotational quantum number to calculate.
|
|
102
|
+
I (float): Nuclear spin. We only consider molecules where one consistuent
|
|
103
|
+
atom has no nuclear spin, so this is the non zero I of the other.
|
|
104
|
+
S (float): Electronic spin.
|
|
105
|
+
consts (dict): Dictionary of constants for the molecule to be calculated.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
H (np.ndarray): Hamiltonian in joules
|
|
109
|
+
'''
|
|
110
|
+
Ishape = int(2*I+1)
|
|
111
|
+
Sshape = int(2*S+1)
|
|
112
|
+
shape = np.sum(np.array([2*x+1 for x in range(0,Nmax+1)]))
|
|
113
|
+
H_ROT = np.zeros((shape,shape), dtype= complex)
|
|
114
|
+
H_ROT = (np.kron(H_ROT,np.kron(np.identity(Ishape),
|
|
115
|
+
np.identity(Sshape))))
|
|
116
|
+
|
|
117
|
+
i=0
|
|
118
|
+
j=0
|
|
119
|
+
for N1 in range(0,Nmax+1):
|
|
120
|
+
for J1 in np.arange(np.abs(N1-S),(N1+S+1),1):
|
|
121
|
+
for F1 in np.arange(J1-I,(J1+I+1),1):
|
|
122
|
+
for mF1 in np.arange(-F1,(F1+1),1):
|
|
123
|
+
for N2 in range(0,Nmax+1):
|
|
124
|
+
for J2 in np.arange(np.abs(N2-S),(N2+S+1),1):
|
|
125
|
+
for F2 in np.arange(J2-I,(J2+I+1),1):
|
|
126
|
+
for mF2 in np.arange(-F2,+(F2+1),1):
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
H_ROT[i,j]= consts['Brot']*(N1*(N1+1))*KroneckerDelta(N1,N2)*\
|
|
130
|
+
KroneckerDelta(J1,J2)*KroneckerDelta(F1,F2)*KroneckerDelta(mF1,mF2)+\
|
|
131
|
+
consts['Drot']*(N1*(N1+1))**2*KroneckerDelta(N1,N2)*\
|
|
132
|
+
KroneckerDelta(J1,J2)*KroneckerDelta(F1,F2)*KroneckerDelta(mF1,mF2)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
i+=1
|
|
136
|
+
i=0
|
|
137
|
+
j+=1
|
|
138
|
+
|
|
139
|
+
#final check for NaN errors, mostly this is due to division by zero or
|
|
140
|
+
# multiplication by a small prefactor. it is safe to set these terms to 0
|
|
141
|
+
H_ROT[np.isnan(H_ROT)] =0
|
|
142
|
+
|
|
143
|
+
#return the matrix, in the full coupled basis.
|
|
144
|
+
|
|
145
|
+
return H_ROT
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def H_hf(Nmax,consts,I,S):
|
|
149
|
+
''' Calculates <N,J,F,mF| H_hf |N',J',F', mF'> by iterating over N, J, F, mF, N',
|
|
150
|
+
J', F', mF' to build a matrix and evaluate the elements.
|
|
151
|
+
This function is based off Jesus Aldegunde's FORTRAN 77 code.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
|
|
155
|
+
Nmax (int) - maximum rotational quantum number to calculate
|
|
156
|
+
I (float): Nuclear spin. We only consider molecules where one consistuent
|
|
157
|
+
atom has no nuclear spin, so this is the non zero I of the other.
|
|
158
|
+
S (float): Electronic spin.
|
|
159
|
+
consts (dict): Dictionary of constants for the molecule to be calculated.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
H (np.ndarray): Hamiltonian in joules
|
|
163
|
+
'''
|
|
164
|
+
|
|
165
|
+
Ishape = int(2*I+1)
|
|
166
|
+
Sshape = int(2*S+1)
|
|
167
|
+
shape = np.sum(np.array([2*x+1 for x in range(0,Nmax+1)]))
|
|
168
|
+
H_HF = np.zeros((shape,shape),dtype= complex)
|
|
169
|
+
H_HF = (np.kron(H_HF,np.kron(np.identity(Ishape),
|
|
170
|
+
np.identity(Sshape))))
|
|
171
|
+
|
|
172
|
+
i=0
|
|
173
|
+
j=0
|
|
174
|
+
for N1 in range(0,Nmax+1):
|
|
175
|
+
for J1 in np.arange(np.abs(N1-S),(N1+S+1),1):
|
|
176
|
+
for F1 in np.arange(J1-I,(J1+I+1),1):
|
|
177
|
+
for mF1 in np.arange(-F1,(F1+1),1):
|
|
178
|
+
for N2 in range(0,Nmax+1):
|
|
179
|
+
for J2 in np.arange(np.abs(N2-S),(N2+S+1),1):
|
|
180
|
+
for F2 in np.arange(J2-I,(J2+I+1),1):
|
|
181
|
+
for mF2 in np.arange(-F2,+(F2+1),1):
|
|
182
|
+
H_HF[i,j]= consts['gamma']/2*(J1*(J1+1)-N1*(N1+1)-S*(S+1))*KroneckerDelta(N1,N2)*\
|
|
183
|
+
KroneckerDelta(J1,J2)*KroneckerDelta(F1,F2)*KroneckerDelta(mF1,mF2)+\
|
|
184
|
+
(consts['b']+consts['c']/3)*(np.round((-1+0j)**(J1+J2+F1+N1),0)*\
|
|
185
|
+
KroneckerDelta(N1,N2)*KroneckerDelta(F1,F2)*KroneckerDelta(mF1,mF2)*\
|
|
186
|
+
3/2*((2*J2+1)*(2*J1+1))**0.5*wigner_6j(F1,0.5,J2,1,J1,0.5)*\
|
|
187
|
+
wigner_6j(0.5, J2, N1, J1, 0.5, 1)) +\
|
|
188
|
+
10**0.5*consts['c']*np.round((-1+0j)**(J1+F1+N2+0.5),0)*KroneckerDelta(F1,F2)*KroneckerDelta(mF1,mF2)*\
|
|
189
|
+
((2*N2+1)*(2*N1+1)*(2*J2+1)*(2*J1+1))**0.5*wigner_6j(F1,0.5,J2,1,J1,0.5)*\
|
|
190
|
+
wigner_6j(J2,J1,1,0.5,1.5,N1)*wigner_6j(N1,N2,2,0.5,1.5,J2)*wigner_3j(N2,2,N1,0,0,0)+\
|
|
191
|
+
consts['CC']*KroneckerDelta(N1,N2)*KroneckerDelta(F1,F2)*KroneckerDelta(mF1,mF2)*\
|
|
192
|
+
np.round((-1+0j)**(2*J1+N2+F2),0)*(3/2)**0.5*((2*J2+1)*(2*J1+1)*N1*(N1+1)*(2*N1+1))**0.5*\
|
|
193
|
+
wigner_6j(F1,0.5,J2,1,J1,0.5)*wigner_6j(N2,J2,0.5,J1,N1,1)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
i+=1
|
|
197
|
+
i=0
|
|
198
|
+
j+=1
|
|
199
|
+
|
|
200
|
+
#final check for NaN errors, mostly this is due to division by zero or
|
|
201
|
+
# multiplication by a small prefactor. it is safe to set these terms to 0
|
|
202
|
+
H_HF[np.isnan(H_HF)] =0
|
|
203
|
+
|
|
204
|
+
#return the matrix, in the full coupled basis.
|
|
205
|
+
return H_HF
|
|
206
|
+
|
|
207
|
+
def H_dc(Nmax,consts,I,S):
|
|
208
|
+
''' Calculates the effect of the anisotropic DC light shift for a rigid-rotor
|
|
209
|
+
like molecule, , <N,J,F,mF| H_dc |N',J',F', mF'>.
|
|
210
|
+
|
|
211
|
+
This function based on Jesus Aldegunde's FORTRAN 77 code and iterates
|
|
212
|
+
over N, J, F, mF, N', J', F', mF' to build a matrix and evaluate the elements.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
Nmax (int) - maximum rotational quantum number to calculate
|
|
216
|
+
I (float): Nuclear spin. We only consider molecules where one consistuent
|
|
217
|
+
atom has no nuclear spin, so this is the non zero I of the other.
|
|
218
|
+
S (float): Electronic spin.
|
|
219
|
+
constst (dict): Dictionary of constants for the molecule to be calculated.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
H (np.ndarray): Hamiltonian in joules
|
|
223
|
+
'''
|
|
224
|
+
|
|
225
|
+
Ishape = int(2*I+1)
|
|
226
|
+
Sshape = int(2*S+1)
|
|
227
|
+
shape = np.sum(np.array([2*x+1 for x in range(0,Nmax+1)]))
|
|
228
|
+
HDC = np.zeros((shape,shape),dtype= complex)
|
|
229
|
+
HDC = (np.kron(HDC,np.kron(np.identity(Ishape),
|
|
230
|
+
np.identity(Sshape))))
|
|
231
|
+
i=0
|
|
232
|
+
j=0
|
|
233
|
+
|
|
234
|
+
for N1 in range(0,Nmax+1):
|
|
235
|
+
for J1 in np.arange(np.abs(N1-S),(N1+S+1),1):
|
|
236
|
+
for F1 in np.arange(J1-I,(J1+I+1),1):
|
|
237
|
+
for mF1 in np.arange(-F1,(F1+1),1):
|
|
238
|
+
for N2 in range(0,Nmax+1):
|
|
239
|
+
for J2 in np.arange(np.abs(N2-S),(N2+S+1),1):
|
|
240
|
+
for F2 in np.arange(J2-I,(J2+I+1),1):
|
|
241
|
+
for mF2 in np.arange(-F2,+(F2+1),1):
|
|
242
|
+
HDC[i,j] = -1*consts['d0']*np.round((-1+0j)**(F2-mF2+J1+J2+F1+N2+1))*\
|
|
243
|
+
((2*F1+1)*(2*F2+1)*(2*J1+1)*(2*J2+1))**0.5*((2*N1+1)*(2*N2+1))**0.5*(-1)**N2*wigner_3j(N2,1,N1,0,0,0)*\
|
|
244
|
+
wigner_3j(F2,1,F1,-mF2,0,mF1)*wigner_6j(J1,F1,I,F2,J2,1)*wigner_6j(N1,J1,0.5,J2,N2,1)
|
|
245
|
+
|
|
246
|
+
i+=1
|
|
247
|
+
i=0
|
|
248
|
+
j+=1
|
|
249
|
+
#final check for NaN errors, mostly this is due to division by zero or
|
|
250
|
+
# multiplication by a small prefactor. it is safe to set these terms to 0
|
|
251
|
+
HDC[np.isnan(HDC)] =0
|
|
252
|
+
|
|
253
|
+
#return the matrix, in the full coupled basis.
|
|
254
|
+
return HDC
|
|
255
|
+
|
|
256
|
+
def H_ac(Nmax,consts,I,S):
|
|
257
|
+
''' Calculates the effect of the anisotropic light shift for a rigid-rotor
|
|
258
|
+
like molecule, , <N,J,F,mF| H_ac |N',J',F', mF'>.
|
|
259
|
+
|
|
260
|
+
This function based on Jesus Aldegunde's FORTRAN 77 code and iterates
|
|
261
|
+
over N, J, F, mF, N', J', F', mF' to build a matrix and evaluate the elements.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
|
|
265
|
+
Nmax (int) - maximum rotational quantum number to calculate
|
|
266
|
+
I (float): Nuclear spin. We only consider molecules where one consistuent
|
|
267
|
+
atom has no nuclear spin, so this is the non zero I of the other.
|
|
268
|
+
S (float): Electronic spin.
|
|
269
|
+
constst (dict): Dictionary of constants for the molecule to be calculated.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
H (np.ndarray): Hamiltonian in joules
|
|
273
|
+
'''
|
|
274
|
+
Ishape = int(2*I+1)
|
|
275
|
+
Sshape = int(2*S+1)
|
|
276
|
+
shape = np.sum(np.array([2*x+1 for x in range(0,Nmax+1)]))
|
|
277
|
+
HAC = np.zeros((shape,shape),dtype= complex)
|
|
278
|
+
HAC = (np.kron(HAC,np.kron(np.identity(Ishape),
|
|
279
|
+
np.identity(Sshape))))
|
|
280
|
+
i=0
|
|
281
|
+
j=0
|
|
282
|
+
for N1 in range(0,Nmax+1):
|
|
283
|
+
for J1 in np.arange(np.abs(N1-S),(N1+S+1),1):
|
|
284
|
+
for F1 in np.arange(J1-I,(J1+I+1),1):
|
|
285
|
+
for mF1 in np.arange(-F1,(F1+1),1):
|
|
286
|
+
for N2 in range(0,Nmax+1):
|
|
287
|
+
for J2 in np.arange(np.abs(N2-S),(N2+S+1),1):
|
|
288
|
+
for F2 in np.arange(J2-I,(J2+I+1),1):
|
|
289
|
+
for mF2 in np.arange(-F2,+(F2+1),1):
|
|
290
|
+
M=mF2-mF1
|
|
291
|
+
HAC[i,j]= -1*consts['alpha_2']*\
|
|
292
|
+
(wigner_D(2,M,0,consts['Beta'],0)*\
|
|
293
|
+
((-1)**(N1+N2)+1)*np.round((-1+0j)**(F2-mF2+F1-J2+J1+I+0.5),0)*((2*F1+1)*(2*F2+1))**0.5*\
|
|
294
|
+
((2*N1+1)*(2*N2+1)*(2*J1+1)*(2*J2+1))**0.5*wigner_6j(J2,F2,I,F1,J1,2)*\
|
|
295
|
+
wigner_3j(F2,2,F1,-mF2,M,mF1)*wigner_3j(J1,0.5,N1,-0.5,0.5,0)*\
|
|
296
|
+
wigner_3j(J2,0.5,N2,-0.5,0.5,0)*wigner_3j(J2,2,J1,-0.5,0,0.5)) -\
|
|
297
|
+
consts['alpha_1']*(wigner_D(2,M,0,consts['Beta'],0)*((-1)**(N1+N2)+1)*np.round((-1+0j)**(F2-mF2+F1-J2+J1+I+0.5),0)*\
|
|
298
|
+
((2*F1+1)*(2*F2+1))**0.5*((2*N1+1)*(2*N2+1)*(2*J1+1)*(2*J2+1))**0.5*\
|
|
299
|
+
wigner_6j(J2,F2,I,F1,J1,1)*wigner_3j(F2,1,F1,-mF2,M,mF1)*wigner_3j(J1,0.5,N1,-0.5,0.5,0)*\
|
|
300
|
+
wigner_3j(J2,0.5,N2,-0.5,0.5,0)*wigner_3j(J2,1,J1,-0.5,0,0.5)) -\
|
|
301
|
+
consts['alpha_0']*(((-1)**(N1+N2)+1)*np.round((-1+0j)**(F2-mF2+F1-J2+J1+I+0.5),0)*\
|
|
302
|
+
((2*F1+1)*(2*F2+1))**0.5*((2*N1+1)*(2*N2+1)*(2*J1+1)*(2*J2+1))**0.5*\
|
|
303
|
+
wigner_6j(J2,F2,I,F1,J1,0)*wigner_3j(F2,0,F1,-mF2,M,mF1)*wigner_3j(J1,0.5,N1,-0.5,0.5,0)*\
|
|
304
|
+
wigner_3j(J2,0.5,N2,-0.5,0.5,0)*wigner_3j(J2,0,J1,-0.5,0,0.5))
|
|
305
|
+
i+=1
|
|
306
|
+
i=0
|
|
307
|
+
j+=1
|
|
308
|
+
|
|
309
|
+
#final check for NaN errors, mostly this is due to division by zero or
|
|
310
|
+
# multiplication by a small prefactor. it is safe to set these terms to 0
|
|
311
|
+
HAC[np.isnan(HAC)] =0
|
|
312
|
+
|
|
313
|
+
#return the matrix, in the full coupled basis.
|
|
314
|
+
return HAC
|
|
315
|
+
|
|
316
|
+
def H_B(Nmax,consts,I,S):
|
|
317
|
+
''' Calculates the Zeeman shift for a molecule in a magnetic field, <N,J,F,mF| H_zee |N',J',F', mF'>
|
|
318
|
+
|
|
319
|
+
This function based on Jesus Aldegunde's FORTRAN 77 code and iterates
|
|
320
|
+
over N, J, F, mF, N', J', F', mF' to build a matrix and evaluate the elements.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
|
|
324
|
+
Nmax (int) - maximum rotational quantum number to calculate
|
|
325
|
+
I (float): Nuclear spin. We only consider molecules where one consistuent
|
|
326
|
+
atom has no nuclear spin, so this is the non zero I of the other.
|
|
327
|
+
S (float): Electronic spin.
|
|
328
|
+
constst (dict): Dictionary of constants for the molecule to be calculated.
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
H (np.ndarray): Hamiltonian in joules
|
|
332
|
+
'''
|
|
333
|
+
Ishape = int(2*I+1)
|
|
334
|
+
Sshape = int(2*S+1)
|
|
335
|
+
shape = np.sum(np.array([2*x+1 for x in range(0,Nmax+1)]))
|
|
336
|
+
HB = np.zeros((shape,shape),dtype= complex)
|
|
337
|
+
HB = (np.kron(HB,np.kron(np.identity(Ishape),
|
|
338
|
+
np.identity(Sshape))))
|
|
339
|
+
|
|
340
|
+
i=0
|
|
341
|
+
j=0
|
|
342
|
+
for N1 in range(0,Nmax+1):
|
|
343
|
+
for J1 in np.arange(np.abs(N1-S),(N1+S+1),1):
|
|
344
|
+
for F1 in np.arange(J1-I,(J1+I+1),1):
|
|
345
|
+
for mF1 in np.arange(-F1,(F1+1),1):
|
|
346
|
+
for N2 in range(0,Nmax+1):
|
|
347
|
+
for J2 in np.arange(np.abs(N2-S),(N2+S+1),1):
|
|
348
|
+
for F2 in np.arange(J2-I,(J2+I+1),1):
|
|
349
|
+
for mF2 in np.arange(-F2,+(F2+1),1):
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
HB[i,j]= (consts['MuS']+consts['MuL'])*((-1+0j)**(F2-mF2+2*J2+F1+N1+1)*KroneckerDelta(N1,N2)*\
|
|
353
|
+
(3/2*(2*F1+1)*(2*F2+1)*(2*J1+1)*(2*J2+1))**0.5*\
|
|
354
|
+
wigner_6j(J1,F1,0.5,F2,J2,1)*wigner_6j(0.5,J2,N1,J1,0.5,1)*\
|
|
355
|
+
wigner_3j(F2,1,F1,-mF2,0,mF1)) +\
|
|
356
|
+
consts['MuL']*(np.round((-1+0j)**(F1+F2-mF2+2*J2+N1+N2+0.5),0)*\
|
|
357
|
+
((2*F1+1)*(2*F2+1)*(2*J1+1)*(2*J2+1)*(2*N1+1)*(2*N2+1))**0.5*\
|
|
358
|
+
wigner_3j(F2,1,F1,-mF2,0,mF1)*wigner_6j(J1,F1,I,F2,J2,1)*\
|
|
359
|
+
np.sum((-1)**(O)*\
|
|
360
|
+
wigner_3j(J2,1,J1,-O,0,O)*wigner_3j(J2,S,N2,O,-O,0)*\
|
|
361
|
+
wigner_3j(J1,S,N1,O,-O,0)*O for O in [-0.5,0.5]))+\
|
|
362
|
+
consts['MuR']*((-1+0j)**(F1-mF1+J1+J2+F1+N1+3)*\
|
|
363
|
+
((2*F1+1)*(2*F2+1)*(2*J1+1)*(2*J2+1))**0.5*\
|
|
364
|
+
wigner_6j(J1,F1,0.5,F2,J2,1)*wigner_6j(N1,J1,0.5,J2,N2,1)*\
|
|
365
|
+
wigner_3j(F1,1,F2,-mF1,0,mF2)) +\
|
|
366
|
+
consts['MuN']*((-1+0j)**(2*F1-mF1+J1+3/2)*\
|
|
367
|
+
(3/2*(2*F1+1)*(2*F2+1))**0.5*KroneckerDelta(J1,J2)\
|
|
368
|
+
*wigner_6j(0.5,F2,J2,F1,0.5,1)*\
|
|
369
|
+
wigner_3j(F1,1,F2,-mF1,0,mF2))
|
|
370
|
+
|
|
371
|
+
i+=1
|
|
372
|
+
i=0
|
|
373
|
+
j+=1
|
|
374
|
+
#final check for NaN errors, mostly this is due to division by zero or
|
|
375
|
+
# multiplication by a small prefactor. it is safe to set these terms to 0
|
|
376
|
+
HB[np.isnan(HB)] =0
|
|
377
|
+
|
|
378
|
+
#return the matrix, in the full coupled basis.
|
|
379
|
+
|
|
380
|
+
return HB
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
# This is the main build function and one that the user will actually have to use.
|
|
385
|
+
def build(Nmax,constants,zeeman=False,Edc=False,ac=False):
|
|
386
|
+
''' Return the hyperfine hamiltonian.
|
|
387
|
+
|
|
388
|
+
This function builds the hamiltonian matrices for evaluation so that
|
|
389
|
+
the user doesn't have to rebuild them every time and we can benefit from
|
|
390
|
+
np's ability to do distributed multiplication.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
Nmax (int) - Maximum rotational level to include
|
|
394
|
+
Constants (Dictionary) - Dictionary of constants for the molecule to be calculated.
|
|
395
|
+
B,EDC,AC (Boolean) - Switches for turning off parts of the total Hamiltonian.
|
|
396
|
+
This can save significant time on calculations where DC
|
|
397
|
+
and AC fields are not required due to nested for loops
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
H0,HB,HDC,HAC (np.ndarray): Each of the terms in the Hamiltonian.
|
|
401
|
+
'''
|
|
402
|
+
|
|
403
|
+
I = constants['I']
|
|
404
|
+
S = constants['S']
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
H0 = H_rot(Nmax, constants, I, S) + H_hf(Nmax,constants,I,S)
|
|
408
|
+
if zeeman:
|
|
409
|
+
HB = H_B(Nmax,constants,I,S)
|
|
410
|
+
else:
|
|
411
|
+
HB =0.
|
|
412
|
+
if Edc:
|
|
413
|
+
Hdc = H_dc(Nmax,constants,I,S)
|
|
414
|
+
else:
|
|
415
|
+
Hdc =0.
|
|
416
|
+
if ac:
|
|
417
|
+
Hac = H_ac(Nmax,constants,I,S)
|
|
418
|
+
else:
|
|
419
|
+
Hac =0.
|
|
420
|
+
return H0,HB,Hdc,Hac
|
|
421
|
+
|
|
422
|
+
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dipolmol
|
|
3
|
+
Version: 1.1.1
|
|
4
|
+
Summary: DiPolMol: tools for calculating molecular dipole moments, polarisabilities, and Hamiltonians
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: quantum physics,molecular physics,dipole moment,polarizability,AMO physics,CaF
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Intended Audience :: Science/Research
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: numpy>=1.23
|
|
19
|
+
Requires-Dist: scipy>=1.9
|
|
20
|
+
Requires-Dist: sympy>=1.11
|
|
21
|
+
Requires-Dist: matplotlib>=3.6
|
|
22
|
+
|
|
23
|
+
DiPolMol-Py
|
|
24
|
+
===========
|
|
25
|
+
A Python package to calculate the rotational and hyperfine structure of doublet-Sigma molecules (e.g., CaF, BaF, SrF) in the presence of external fields.
|
|
26
|
+
|
|
27
|
+
DiPolMol-Py is licensed under a BSD 3 clause license, a copy can be found `See the [LICENSE](LICENSE) file'.
|
|
28
|
+
If you use our work for academic purposes you can cite us using:
|
|
29
|
+
|
|
30
|
+
B.Humphreys *et al.* DiPolMol-Py: A Python package for calculations for $^{2}{\Sigma}$ ground-state molecules (https://arxiv.org/pdf/2503.21663).
|
|
31
|
+
|
|
32
|
+
Installation
|
|
33
|
+
----------
|
|
34
|
+
|
|
35
|
+
run: pip install dipolmol
|
|
36
|
+
|
|
37
|
+
This installs the latest stable release and all required dependencies.
|
|
38
|
+
|
|
39
|
+
Package structure
|
|
40
|
+
-------------
|
|
41
|
+
**Programme Files:**
|
|
42
|
+
|
|
43
|
+
*Hamiltonian* – used to build the required Hamiltonian using matrix representation, including the field-free Hamiltonian and in the presence of magnetic, dc electric and off-resonant light fields.
|
|
44
|
+
|
|
45
|
+
*Calculate* – uses the eigenstates and eigenenergies found from diagonalising the Hamiltonian to run various calculations. Can be used to identify quantum numbers of eigenstates, calculate transition dipole moments and polarisabilities.
|
|
46
|
+
|
|
47
|
+
*Constants* – includes all known constants for CaF, BaF and SrF.
|
|
48
|
+
|
|
49
|
+
**Examples:**
|
|
50
|
+
|
|
51
|
+
There are three example files for calculating the energy structure in the presence of a magnetic, off-resonant light and electric field (*example_Bfield, example_ac_Efield, example_dc_Efield*).
|
|
52
|
+
|
|
53
|
+
There are two files to calculate the electric and magnetic moments of states (*example_electric_moment, example_magnetic_moment*).
|
|
54
|
+
|
|
55
|
+
We provide *example_polarisability* to calculate the polarisability for a given wavelength and polarisation of light.
|
|
56
|
+
|
|
57
|
+
Finally, *example_tdm* can be used to calculate the transition dipole moment.
|
|
58
|
+
|
|
59
|
+
Example
|
|
60
|
+
-------
|
|
61
|
+
.. code-block:: python
|
|
62
|
+
|
|
63
|
+
import numpy as np
|
|
64
|
+
import dipolmol.hamiltonian as hamiltonian
|
|
65
|
+
import dipolmol.calculate as calc
|
|
66
|
+
from dipolmol.constants import SrF
|
|
67
|
+
|
|
68
|
+
Nmax=4 #Identify the maximum N
|
|
69
|
+
H0,H_B,H_dc,H_ac
|
|
70
|
+
= hamiltonian.build
|
|
71
|
+
(Nmax,SrF,zeeman=True,Edc=False
|
|
72
|
+
,Eac=False)
|
|
73
|
+
|
|
74
|
+
B = np.linspace(0,100,5000)*1e-4 #Tesla
|
|
75
|
+
|
|
76
|
+
H = H_0[..., None] + H_B[..., None]*B
|
|
77
|
+
H = H.transpose(2,0,1)
|
|
78
|
+
|
|
79
|
+
energies, states, label_list =
|
|
80
|
+
calc.solve(H, Nmax, SrF,label=True, B)
|
|
81
|
+
|
|
82
|
+
Resulting plot of above code
|
|
83
|
+
|
|
84
|
+
.. image:: Images/zeeman_SrF_plot.png
|
|
85
|
+
:width: 400
|
|
86
|
+
:alt: Resulting plot of above example
|
|
87
|
+
|
|
88
|
+
For more examples of usage, see the ``./Examples`` module.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/dipolmol/__init__.py
|
|
4
|
+
src/dipolmol/calculate.py
|
|
5
|
+
src/dipolmol/constants.py
|
|
6
|
+
src/dipolmol/hamiltonian.py
|
|
7
|
+
src/dipolmol.egg-info/PKG-INFO
|
|
8
|
+
src/dipolmol.egg-info/SOURCES.txt
|
|
9
|
+
src/dipolmol.egg-info/dependency_links.txt
|
|
10
|
+
src/dipolmol.egg-info/requires.txt
|
|
11
|
+
src/dipolmol.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dipolmol
|