aton 0.2.4__tar.gz → 0.3.0__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.
- {aton-0.2.4 → aton-0.3.0}/PKG-INFO +1 -1
- {aton-0.2.4 → aton-0.3.0}/aton/_version.py +1 -1
- {aton-0.2.4 → aton-0.3.0}/aton/qrotor/__init__.py +15 -7
- {aton-0.2.4 → aton-0.3.0}/aton/qrotor/plot.py +5 -5
- aton-0.3.0/aton/qrotor/solve.py +255 -0
- {aton-0.2.4 → aton-0.3.0}/aton/qrotor/system.py +23 -14
- {aton-0.2.4 → aton-0.3.0}/aton/qrotor/systems.py +98 -50
- {aton-0.2.4 → aton-0.3.0}/aton.egg-info/PKG-INFO +1 -1
- {aton-0.2.4 → aton-0.3.0}/tests/test_qrotor.py +3 -0
- aton-0.2.4/aton/qrotor/solve.py +0 -172
- {aton-0.2.4 → aton-0.3.0}/LICENSE +0 -0
- {aton-0.2.4 → aton-0.3.0}/README.md +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/__init__.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/interface/__init__.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/interface/castep.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/interface/phonopy.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/interface/qe.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/interface/slurm.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/phys/__init__.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/phys/atoms.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/phys/functions.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/phys/units.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/qrotor/constants.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/qrotor/potential.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/qrotor/rotate.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/spx/__init__.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/spx/classes.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/spx/deuterium.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/spx/fit.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/spx/normalize.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/spx/plot.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/spx/samples.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/st/__init__.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/st/alias.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/st/call.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/st/file.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/txt/__init__.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/txt/edit.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/txt/extract.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton/txt/find.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton.egg-info/SOURCES.txt +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton.egg-info/dependency_links.txt +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton.egg-info/requires.txt +0 -0
- {aton-0.2.4 → aton-0.3.0}/aton.egg-info/top_level.txt +0 -0
- {aton-0.2.4 → aton-0.3.0}/setup.cfg +0 -0
- {aton-0.2.4 → aton-0.3.0}/setup.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/tests/__init__.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/tests/test_edit.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/tests/test_extract.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/tests/test_file.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/tests/test_find.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/tests/test_qe.py +0 -0
- {aton-0.2.4 → aton-0.3.0}/tests/test_spx.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: aton
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: The Ab-iniTiO & Neutron research toolbox, or ATON, provides powerful and comprehensive tools for cutting-edge materials research, focused on (but not limited to) neutron science.
|
|
5
5
|
Author: Pablo Gila-Herranz
|
|
6
6
|
Author-email: pgila001@ikasle.ehu.eus
|
|
@@ -97,25 +97,33 @@ qr.plot.energies(system)
|
|
|
97
97
|
|
|
98
98
|
## Tunnel splittings and excitations
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
When the quantum System is solved, tunnel splittings and excitations are also calculated:
|
|
100
|
+
When the quantum System is solved, tunnel splittings,
|
|
101
|
+
excitations and energy level degeneracy
|
|
102
|
+
below the potential maximum are also calculated:
|
|
105
103
|
|
|
106
104
|
```python
|
|
107
105
|
system.solve()
|
|
108
106
|
system.splittings
|
|
109
107
|
system.excitations
|
|
108
|
+
system.deg
|
|
110
109
|
```
|
|
111
110
|
|
|
112
|
-
To export the tunnel splittings of several calculations to a CSV file:
|
|
111
|
+
To export the energies and the tunnel splittings of several calculations to a CSV file:
|
|
113
112
|
|
|
114
113
|
```python
|
|
115
114
|
calculations = [system1, system2, system3]
|
|
116
|
-
qr.systems.
|
|
115
|
+
qr.systems.save_energies(calculations)
|
|
116
|
+
qr.systems.save_splittings(calculations)
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
+
Excitations are calculated using the mean for each energy level
|
|
120
|
+
with respect to the ground state.
|
|
121
|
+
Tunnel splittings for each level are calculated as the difference between A and E,
|
|
122
|
+
considering the mean of the eigenvalues for each sublevel.
|
|
123
|
+
See [R. M. Dimeo, American Journal of Physics 71, 885–893 (2003)](https://doi.org/10.1119/1.1538575)
|
|
124
|
+
and [A. J. Horsewill, Progress in Nuclear Magnetic Resonance Spectroscopy 35, 359–389 (1999)](https://doi.org/10.1016/S0079-6565(99)00016-3)
|
|
125
|
+
for further reference.
|
|
126
|
+
|
|
119
127
|
|
|
120
128
|
Check the API documentation for more details.
|
|
121
129
|
|
|
@@ -151,7 +151,7 @@ def reduced_energies(
|
|
|
151
151
|
|
|
152
152
|
systems.as_list(data)
|
|
153
153
|
title = title if title else (data[0].comment if data[0].comment else 'Reduced energies')
|
|
154
|
-
number_of_levels = data[0].
|
|
154
|
+
number_of_levels = data[0].searched_E
|
|
155
155
|
x = []
|
|
156
156
|
for system in data:
|
|
157
157
|
potential_max_B = system.potential_max / system.B
|
|
@@ -198,7 +198,7 @@ def wavefunction(
|
|
|
198
198
|
Specific wavefunctions can be overlapped with `overlap` as a list with the target indexes.
|
|
199
199
|
The `overlap` value can also be the max number of wavefunctions to add.
|
|
200
200
|
All found wavefunctions can be added together with `overlap = True`;
|
|
201
|
-
but note that this overlap is limited by the number of System.
|
|
201
|
+
but note that this overlap is limited by the number of System.searched_E,
|
|
202
202
|
that must be specified before solving the system.
|
|
203
203
|
Setting `overlap` will ignore the `levels` argument.
|
|
204
204
|
|
|
@@ -258,7 +258,7 @@ def convergence(data:list) -> None:
|
|
|
258
258
|
gridsizes = [system.gridsize for system in data]
|
|
259
259
|
runtimes = [system.runtime for system in data]
|
|
260
260
|
deviations = [] # List of lists, containing all eigenvalue deviations for every system
|
|
261
|
-
|
|
261
|
+
searched_E = data[0].searched_E
|
|
262
262
|
for system in data:
|
|
263
263
|
deviation_list = []
|
|
264
264
|
for i, eigenvalue in enumerate(system.eigenvalues):
|
|
@@ -277,8 +277,8 @@ def convergence(data:list) -> None:
|
|
|
277
277
|
ax2.set_ylabel('Runtime / s')
|
|
278
278
|
ax2.set_yscale('log')
|
|
279
279
|
ax2.plot(gridsizes, runtimes, color='tab:grey', label='Runtime', linestyle='--')
|
|
280
|
-
colors = plt.cm.viridis(np.linspace(0, 1,
|
|
281
|
-
for i in range(
|
|
280
|
+
colors = plt.cm.viridis(np.linspace(0, 1, searched_E)) # Should be searched_E-1 but we want to avoid lighter colors
|
|
281
|
+
for i in range(searched_E-1):
|
|
282
282
|
if i % 2 == 0: # Ignore even numbers, since those levels are degenerated.
|
|
283
283
|
continue
|
|
284
284
|
ax1.plot(gridsizes, [dev[i] for dev in deviations], label=f'$E_{{{int((i+1)/2)}}}$', color=colors[i])
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# Description
|
|
3
|
+
|
|
4
|
+
This module is used to solve any given quantum system.
|
|
5
|
+
|
|
6
|
+
Although the functions of this module can be used independently,
|
|
7
|
+
it is highly recommended to use the `System.solve()` method instead,
|
|
8
|
+
which does all the solving automatically (see `aton.qrotor.system.System.solve()`).
|
|
9
|
+
This documentation page is left for reference and for advanced users.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Index
|
|
13
|
+
|
|
14
|
+
| | |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `energies()` | Solve the quantum system, including eigenvalues and eigenvectors |
|
|
17
|
+
| `potential()` | Solve the potential values of the system |
|
|
18
|
+
| `schrodinger()` | Solve the Schrödiger equation for the system |
|
|
19
|
+
| `hamiltonian_matrix()` | Calculate the hamiltonian matrix of the system |
|
|
20
|
+
| `laplacian_matrix()` | Calculate the second derivative matrix for a given grid |
|
|
21
|
+
| `excitations()` | Get excitation levels and tunnel splitting energies |
|
|
22
|
+
| `E_levels` | Group a list of degenerated eigenvalues by energy levels |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
from .system import System
|
|
29
|
+
from .potential import solve as solve_potential
|
|
30
|
+
from .potential import interpolate
|
|
31
|
+
import time
|
|
32
|
+
import numpy as np
|
|
33
|
+
from scipy import sparse
|
|
34
|
+
import aton
|
|
35
|
+
from aton._version import __version__
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def energies(system:System, filename:str=None) -> System:
|
|
39
|
+
"""Solves the quantum `system`.
|
|
40
|
+
|
|
41
|
+
This includes solving the potential, the eigenvalues and the eigenvectors.
|
|
42
|
+
|
|
43
|
+
The resulting System object is saved with pickle to `filename` if specified.
|
|
44
|
+
"""
|
|
45
|
+
if not any(system.grid):
|
|
46
|
+
system.set_grid()
|
|
47
|
+
system = potential(system)
|
|
48
|
+
system = schrodinger(system)
|
|
49
|
+
if filename:
|
|
50
|
+
aton.st.file.save(system, filename)
|
|
51
|
+
return system
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def potential(system:System) -> System:
|
|
55
|
+
"""Solves the potential values of the `system`.
|
|
56
|
+
|
|
57
|
+
It interpolates the potential if `system.gridsize` is larger than the current grid.
|
|
58
|
+
It solves the potential according to the potential name,
|
|
59
|
+
by calling `aton.qrotor.potential.solve()`.
|
|
60
|
+
Then it applies extra operations, such as removing the potential offset
|
|
61
|
+
if `system.correct_potential_offset = True`.
|
|
62
|
+
"""
|
|
63
|
+
if system.gridsize and any(system.grid):
|
|
64
|
+
if system.gridsize > len(system.grid):
|
|
65
|
+
system = interpolate(system)
|
|
66
|
+
V = solve_potential(system)
|
|
67
|
+
if system.correct_potential_offset is True:
|
|
68
|
+
offset = min(V)
|
|
69
|
+
V = V - offset
|
|
70
|
+
system.potential_offset = offset
|
|
71
|
+
system.potential_values = V
|
|
72
|
+
return system
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def schrodinger(system:System) -> System:
|
|
76
|
+
"""Solves the Schrödinger equation for a given `system`.
|
|
77
|
+
|
|
78
|
+
Uses ARPACK in shift-inverse mode to solve the hamiltonian sparse matrix.
|
|
79
|
+
"""
|
|
80
|
+
time_start = time.time()
|
|
81
|
+
V = system.potential_values
|
|
82
|
+
H = hamiltonian_matrix(system)
|
|
83
|
+
print('Solving Schrodinger equation...')
|
|
84
|
+
# Solve eigenvalues with ARPACK in shift-inverse mode, with a sparse matrix
|
|
85
|
+
eigenvalues, eigenvectors = sparse.linalg.eigsh(H, system.searched_E, which='LM', sigma=0, maxiter=10000)
|
|
86
|
+
if any(eigenvalues) is None:
|
|
87
|
+
print('WARNING: Not all eigenvalues were found.\n')
|
|
88
|
+
else: print('Done.')
|
|
89
|
+
system.version = __version__
|
|
90
|
+
system.runtime = time.time() - time_start
|
|
91
|
+
system.eigenvalues = eigenvalues
|
|
92
|
+
system.potential_max = max(V)
|
|
93
|
+
system.potential_min = min(V)
|
|
94
|
+
system.energy_barrier = max(V) - min(eigenvalues)
|
|
95
|
+
# Solve excitations and tunnel splittings, assuming triplet degeneracy
|
|
96
|
+
system = excitations(system)
|
|
97
|
+
# Do we really need to save eigenvectors?
|
|
98
|
+
if system.save_eigenvectors == True:
|
|
99
|
+
system.eigenvectors = np.transpose(eigenvectors)
|
|
100
|
+
return system
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def hamiltonian_matrix(system:System):
|
|
104
|
+
"""Calculates the Hamiltonian sparse matrix for a given `system`."""
|
|
105
|
+
print(f'Creating Hamiltonian matrix of size {system.gridsize}...')
|
|
106
|
+
V = system.potential_values.tolist()
|
|
107
|
+
potential = sparse.diags(V, format='lil')
|
|
108
|
+
B = system.B
|
|
109
|
+
x = system.grid
|
|
110
|
+
H = -B * laplacian_matrix(x) + potential
|
|
111
|
+
return H
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def laplacian_matrix(grid):
|
|
115
|
+
"""Calculates the Laplacian (second derivative) matrix for a given `grid`."""
|
|
116
|
+
x = grid
|
|
117
|
+
diagonals = [-2*np.ones(len(x)), np.ones(len(x)), np.ones(len(x))]
|
|
118
|
+
laplacian_matrix = sparse.spdiags(diagonals, [0, -1, 1], format='lil')
|
|
119
|
+
# Periodic boundary conditions
|
|
120
|
+
laplacian_matrix[0, -1] = 1
|
|
121
|
+
laplacian_matrix[-1, 0] = 1
|
|
122
|
+
dx = x[1] - x[0]
|
|
123
|
+
laplacian_matrix /= dx**2
|
|
124
|
+
return laplacian_matrix
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def excitations(system: System) -> System:
|
|
128
|
+
"""Calculate the excitation levels and the tunnel splitting energies of a system.
|
|
129
|
+
|
|
130
|
+
Automatically detects degenerated energy levels by looking at significant jumps
|
|
131
|
+
between consecutive eigenvalues. Within each level, finds two subgroups
|
|
132
|
+
to calculate tunnel splittings. Stops when energies reach the maximum potential.
|
|
133
|
+
|
|
134
|
+
Excitations are calculated as the energy difference between the mean energy of the
|
|
135
|
+
ground state level and the mean energy of each excited level.
|
|
136
|
+
|
|
137
|
+
Tunnel splittings are calculated as the difference between the medians of
|
|
138
|
+
the two subgroups within each degenerate level.
|
|
139
|
+
"""
|
|
140
|
+
# Get eigenvalues, stop before any possible None value
|
|
141
|
+
eigenvalues = system.eigenvalues
|
|
142
|
+
if not isinstance(eigenvalues, (list, np.ndarray)) or len(eigenvalues) == 0:
|
|
143
|
+
return system
|
|
144
|
+
if None in eigenvalues:
|
|
145
|
+
none_index = eigenvalues.tolist().index(None)
|
|
146
|
+
eigenvalues = eigenvalues[:none_index]
|
|
147
|
+
if len(eigenvalues) < 3:
|
|
148
|
+
return system
|
|
149
|
+
# Group degenerated eigenvalues into energy levels
|
|
150
|
+
levels, degeneracy = E_levels(eigenvalues, system.potential_max)
|
|
151
|
+
system.E_levels = levels
|
|
152
|
+
system.deg = degeneracy
|
|
153
|
+
# Calculate excitations and splittings
|
|
154
|
+
ground_energy = np.mean(levels[0]) # Mean of ground state level
|
|
155
|
+
excitations = []
|
|
156
|
+
tunnel_splittings = []
|
|
157
|
+
for level in levels:
|
|
158
|
+
level_mean = np.mean(level)
|
|
159
|
+
excitations.append(level_mean - ground_energy)
|
|
160
|
+
# Get the tunnel splitting within the level
|
|
161
|
+
if len(level) > 1:
|
|
162
|
+
# Find the largest gap within the level to split into two subgroups
|
|
163
|
+
internal_gaps = np.diff(level)
|
|
164
|
+
split_idx = np.argmax(internal_gaps) + 1
|
|
165
|
+
# Split into two subgroups
|
|
166
|
+
subgroup1 = level[:split_idx]
|
|
167
|
+
subgroup2 = level[split_idx:]
|
|
168
|
+
# Medians of subgroups
|
|
169
|
+
median1 = np.median(subgroup1)
|
|
170
|
+
median2 = np.median(subgroup2)
|
|
171
|
+
# Tunnel splitting is the difference between medians
|
|
172
|
+
tunnel_splittings.append(abs(median2 - median1))
|
|
173
|
+
else:
|
|
174
|
+
tunnel_splittings.append(0)
|
|
175
|
+
system.excitations = excitations[1:] # Exclude ground state
|
|
176
|
+
system.splittings = tunnel_splittings
|
|
177
|
+
return system
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def E_levels(eigenvalues, vmax:float=None) -> list:
|
|
181
|
+
"""Group a list of degenerated eigenvalues by energy levels.
|
|
182
|
+
|
|
183
|
+
Automatically detects degenerated energy levels by
|
|
184
|
+
looking at significant jumps between consecutive eigenvalues.
|
|
185
|
+
|
|
186
|
+
An optional `vmax` can be specified,
|
|
187
|
+
to avoid including too many eigenvalues
|
|
188
|
+
above a certain potential maximum.
|
|
189
|
+
Only two more eigenvalues are considered after `vmax`,
|
|
190
|
+
to properly detect energy levels around the maximum.
|
|
191
|
+
|
|
192
|
+
Example:
|
|
193
|
+
```python
|
|
194
|
+
levels, deg = qr.solve.E_levels(array([1.1, 1.2, 1.3, 5.4, 5.5, 5.6]))
|
|
195
|
+
levels # [array([1.1, 1.2, 1.3]), array([5.4, 5.5, 5.6])]
|
|
196
|
+
deg # 3
|
|
197
|
+
```
|
|
198
|
+
"""
|
|
199
|
+
if vmax: # Include all eigenvalues below Vmax plus 3 more eigenvalues
|
|
200
|
+
index_first_above_Vmax = np.where(eigenvalues > vmax)[0][0]
|
|
201
|
+
eigenvalues = eigenvalues[:(index_first_above_Vmax + 2)]
|
|
202
|
+
# Group degenerated eigenvalues into energy levels
|
|
203
|
+
for scale in np.arange(2, 4, 0.25): # First search going to bigger scales
|
|
204
|
+
levels, degeneracy = _get_E_levels_by_gap(eigenvalues, scale)
|
|
205
|
+
if (degeneracy > 1) and (degeneracy % 1 == 0):
|
|
206
|
+
break
|
|
207
|
+
else:
|
|
208
|
+
levels, degeneracy = None, None
|
|
209
|
+
if not degeneracy: # If it didn't work, search with tighter values
|
|
210
|
+
for scale in np.arange(0.75, 2, 0.25):
|
|
211
|
+
levels, degeneracy = _get_E_levels_by_gap(eigenvalues, scale)
|
|
212
|
+
if (degeneracy > 1) and (degeneracy % 1 == 0):
|
|
213
|
+
break
|
|
214
|
+
if not (degeneracy > 1) and not (degeneracy % 1) == 0:
|
|
215
|
+
return levels, degeneracy # I give up
|
|
216
|
+
# Correct the last two levels
|
|
217
|
+
if len(levels) >= 2 and len(levels[-2]) != degeneracy:
|
|
218
|
+
levels[-2] = np.concatenate((levels[-2], levels[-1]))
|
|
219
|
+
levels.pop(-1)
|
|
220
|
+
# Split last level into groups of size = degeneracy
|
|
221
|
+
last_level = levels[-1]
|
|
222
|
+
additional_levels = len(last_level) // degeneracy
|
|
223
|
+
if additional_levels > 0:
|
|
224
|
+
# Replace last level with list of complete degeneracy groups
|
|
225
|
+
complete_groups = [last_level[i:i+degeneracy] for i in range(0, additional_levels*degeneracy, degeneracy)]
|
|
226
|
+
levels.pop(-1) # Remove original last level
|
|
227
|
+
levels.extend(complete_groups) # Add all complete groups
|
|
228
|
+
else:
|
|
229
|
+
levels.pop(-1) # Remove incomplete last level
|
|
230
|
+
return levels, degeneracy
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _get_E_levels_by_gap(eigenvalues, scale:float=2) -> tuple:
|
|
234
|
+
"""Split a list of eigenvalues into energy levels by looking at gaps.
|
|
235
|
+
|
|
236
|
+
If the gap is bigger than the average gap times `scale`, it is considered a new level.
|
|
237
|
+
|
|
238
|
+
Returns a tuple with the estimated levels and the average degeneracy.
|
|
239
|
+
The last two levels are not taken into account to estimate the degeneracy.
|
|
240
|
+
"""
|
|
241
|
+
# Find gaps between consecutive eigenvalues
|
|
242
|
+
gaps = np.diff(eigenvalues)
|
|
243
|
+
# Use mean gap times scale as threshold to distinguish energy levels
|
|
244
|
+
med_gap = np.mean(gaps)
|
|
245
|
+
level_breaks = np.where(gaps > scale * med_gap)[0] + 1
|
|
246
|
+
levels = np.split(eigenvalues, level_breaks)
|
|
247
|
+
# Calculate average degeneracy excluding last two levels if possible
|
|
248
|
+
if len(levels) > 2:
|
|
249
|
+
avg_degeneracy = float(np.mean([len(level) for level in levels[:-2]]))
|
|
250
|
+
else:
|
|
251
|
+
avg_degeneracy = float(len(levels[0]))
|
|
252
|
+
if avg_degeneracy % 1 == 0:
|
|
253
|
+
avg_degeneracy = int(avg_degeneracy)
|
|
254
|
+
return levels, avg_degeneracy
|
|
255
|
+
|
|
@@ -25,7 +25,7 @@ class System:
|
|
|
25
25
|
self,
|
|
26
26
|
comment: str = None,
|
|
27
27
|
group: str = 'CH3',
|
|
28
|
-
|
|
28
|
+
searched_E: int = 21,
|
|
29
29
|
correct_potential_offset: bool = True,
|
|
30
30
|
save_eigenvectors: bool = True,
|
|
31
31
|
gridsize: int = 200000,
|
|
@@ -43,8 +43,8 @@ class System:
|
|
|
43
43
|
self.group: str = group
|
|
44
44
|
"""Chemical group, methyl or amine: `'CH3'`, `'CD3'`, `'NH3'`, `'ND3'`."""
|
|
45
45
|
self.set_group(group) # Normalise the group name, and set the value of B
|
|
46
|
-
self.
|
|
47
|
-
"""Number of energy
|
|
46
|
+
self.searched_E: int = searched_E
|
|
47
|
+
"""Number of energy eigenvalues to be searched."""
|
|
48
48
|
self.correct_potential_offset: bool = correct_potential_offset
|
|
49
49
|
"""Correct the potential offset as `V - min(V)` or not."""
|
|
50
50
|
self.save_eigenvectors: bool = save_eigenvectors
|
|
@@ -82,19 +82,28 @@ class System:
|
|
|
82
82
|
self.potential_max: float = None
|
|
83
83
|
"""`max(V)`"""
|
|
84
84
|
# Energies
|
|
85
|
-
self.eigenvalues = []
|
|
86
|
-
"""Calculated eigenvalues of the system. Should be in meV."""
|
|
87
85
|
self.eigenvectors = []
|
|
88
86
|
"""Eigenvectors, if `save_eigenvectors` is True. Beware of the file size."""
|
|
87
|
+
self.eigenvalues = []
|
|
88
|
+
"""Calculated eigenvalues of the system. In meV."""
|
|
89
|
+
self.E_levels: list = []
|
|
90
|
+
"""List of `eigenvalues` grouped by energy levels, found below `potential_max`."""
|
|
91
|
+
self.deg: float = None
|
|
92
|
+
"""Estimated degeneracy of the `E_levels` found below `potential_max`."""
|
|
93
|
+
self.excitations: list = []
|
|
94
|
+
"""Torsional excitations, as the difference between each energy level with respect to the ground state.
|
|
95
|
+
|
|
96
|
+
Considers the means between degenerated eigenvalues for all energy levels below `potential_max`.
|
|
97
|
+
"""
|
|
98
|
+
self.splittings: list = []
|
|
99
|
+
"""Tunnel splitting energies, for every degenerated energy level.
|
|
100
|
+
|
|
101
|
+
Calculated for all `E_levels` as the difference between
|
|
102
|
+
the mean of the eigenvalues from A and the mean of the eigenvalues from E,
|
|
103
|
+
see [R. M. Dimeo, American Journal of Physics 71, 885–893 (2003)](https://doi.org/10.1119/1.1538575).
|
|
104
|
+
"""
|
|
89
105
|
self.energy_barrier: float = None
|
|
90
106
|
"""Activation energy or energy barrier, from the ground torsional state to the top of the potential barrier, `max(V) - min(eigenvalues)`"""
|
|
91
|
-
self.excitations: list = None
|
|
92
|
-
"""Torsional excitations, as eigenvalues with respect to the ground state.
|
|
93
|
-
|
|
94
|
-
Considers the lowest eigenvalue from each degenerated energy level.
|
|
95
|
-
"""
|
|
96
|
-
self.splittings: list = None
|
|
97
|
-
"""Tunnel splitting energies, for every degenerated energy level."""
|
|
98
107
|
self.runtime: float = None
|
|
99
108
|
"""Time taken to solve the eigenvalues."""
|
|
100
109
|
|
|
@@ -113,8 +122,8 @@ class System:
|
|
|
113
122
|
def change_phase(self, phase:float, calculate:bool=True):
|
|
114
123
|
"""Apply a phase shift to the grid and potential values.
|
|
115
124
|
|
|
116
|
-
The `phase` should be a multiple of
|
|
117
|
-
The resulting grid will be expressed between $-2
|
|
125
|
+
The `phase` should be a multiple of $\\pi$ (e.g., 3/2 for $3\\pi/2$).
|
|
126
|
+
The resulting grid will be expressed between $-2\\pi$ and $2\\pi$.
|
|
118
127
|
|
|
119
128
|
The System is solved immediately after the phase change.
|
|
120
129
|
This last step ensures that all eigenvalues and wavefunctions are correct.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# Description
|
|
3
3
|
|
|
4
4
|
This module contains utility functions to handle multiple `aton.qrotor.system` calculations.
|
|
5
|
+
These are commonly used as a list of `System` objects.
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
# Index
|
|
@@ -9,14 +10,15 @@ This module contains utility functions to handle multiple `aton.qrotor.system` c
|
|
|
9
10
|
| | |
|
|
10
11
|
| --- | --- |
|
|
11
12
|
| `as_list()` | Ensures that a list only contains System objects |
|
|
13
|
+
| `save_energies()` | Save the energy eigenvalues for all systems to a CSV |
|
|
14
|
+
| `save_splittings()` | Save the tunnel splitting energies for all systems to a CSV |
|
|
12
15
|
| `get_energies()` | Get the eigenvalues from all systems |
|
|
13
16
|
| `get_gridsizes()` | Get all gridsizes |
|
|
14
17
|
| `get_runtimes()` | Get all runtimes |
|
|
15
18
|
| `get_groups()` | Get the chemical groups in use |
|
|
19
|
+
| `get_ideal_E()` | Calculate the ideal energy for a specified level |
|
|
16
20
|
| `sort_by_gridsize()` | Sort systems by gridsize |
|
|
17
21
|
| `reduce_size()` | Discard data that takes too much space |
|
|
18
|
-
| `get_ideal_E()` | Calculate the ideal energy for a specified level |
|
|
19
|
-
| `splittings()` | Get the first tunnel splitting energies for all systems |
|
|
20
22
|
|
|
21
23
|
---
|
|
22
24
|
"""
|
|
@@ -45,8 +47,88 @@ def as_list(systems) -> None:
|
|
|
45
47
|
return systems
|
|
46
48
|
|
|
47
49
|
|
|
50
|
+
def save_energies(
|
|
51
|
+
systems:list,
|
|
52
|
+
comment:str='',
|
|
53
|
+
filepath:str='eigenvalues.csv',
|
|
54
|
+
) -> pd.DataFrame:
|
|
55
|
+
"""Save the energy eigenvalues for all `systems` to a eigenvalues.csv file.
|
|
56
|
+
|
|
57
|
+
Returns a Pandas Dataset with `System.comment` columns and `System.eigenvalues` values.
|
|
58
|
+
|
|
59
|
+
The output file can be changed with `filepath`,
|
|
60
|
+
or set to null to avoid saving the dataset.
|
|
61
|
+
A `comment` can be included at the top of the file.
|
|
62
|
+
Note that `System.comment` must not include commas (`,`).
|
|
63
|
+
"""
|
|
64
|
+
as_list(systems)
|
|
65
|
+
version = systems[0].version
|
|
66
|
+
E = {}
|
|
67
|
+
# Find max length of eigenvalues
|
|
68
|
+
max_len = max((len(s.eigenvalues) if s.eigenvalues is not None else 0) for s in systems)
|
|
69
|
+
for s in systems:
|
|
70
|
+
if s.eigenvalues is not None:
|
|
71
|
+
# Filter out None values and replace with NaN
|
|
72
|
+
valid_eigenvalues = [float('nan') if e is None else e for e in s.eigenvalues]
|
|
73
|
+
padded_eigenvalues = valid_eigenvalues + [float('nan')] * (max_len - len(s.eigenvalues))
|
|
74
|
+
else:
|
|
75
|
+
padded_eigenvalues = [float('nan')] * max_len
|
|
76
|
+
E[s.comment] = padded_eigenvalues
|
|
77
|
+
df = pd.DataFrame(E)
|
|
78
|
+
if not filepath:
|
|
79
|
+
return df
|
|
80
|
+
# Else save to file
|
|
81
|
+
df.to_csv(filepath, sep=',', index=False)
|
|
82
|
+
# Include a comment at the top of the file
|
|
83
|
+
file_comment = f'# {comment}\n' if comment else f''
|
|
84
|
+
file_comment += f'# Energy eigenvalues\n'
|
|
85
|
+
file_comment += f'# Calculated with ATON {version}\n'
|
|
86
|
+
file_comment += f'# https://pablogila.github.io/ATON\n#'
|
|
87
|
+
txt.edit.insert_at(filepath, file_comment, 0)
|
|
88
|
+
print(f'Energy eigenvalues saved to {filepath}')
|
|
89
|
+
return df
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def save_splittings(
|
|
93
|
+
systems:list,
|
|
94
|
+
comment:str='',
|
|
95
|
+
filepath:str='tunnel_splittings.csv',
|
|
96
|
+
) -> pd.DataFrame:
|
|
97
|
+
"""Save the tunnel splitting energies for all `systems` to a tunnel_splittings.csv file.
|
|
98
|
+
|
|
99
|
+
Returns a Pandas Dataset with `System.comment` columns and `System.splittings` values.
|
|
100
|
+
|
|
101
|
+
The output file can be changed with `filepath`,
|
|
102
|
+
or set to null to avoid saving the dataset.
|
|
103
|
+
A `comment` can be included at the top of the file.
|
|
104
|
+
Note that `System.comment` must not include commas (`,`).
|
|
105
|
+
Different splitting lengths across systems are allowed - missing values will be NaN.
|
|
106
|
+
"""
|
|
107
|
+
as_list(systems)
|
|
108
|
+
version = systems[0].version
|
|
109
|
+
tunnelling_E = {}
|
|
110
|
+
# Find max length of splittings
|
|
111
|
+
max_len = max(len(s.splittings) for s in systems)
|
|
112
|
+
for s in systems: # Pad shorter splittings with NaN
|
|
113
|
+
padded_splittings = s.splittings + [float('nan')] * (max_len - len(s.splittings))
|
|
114
|
+
tunnelling_E[s.comment] = padded_splittings
|
|
115
|
+
df = pd.DataFrame(tunnelling_E)
|
|
116
|
+
if not filepath:
|
|
117
|
+
return df
|
|
118
|
+
# Else save to file
|
|
119
|
+
df.to_csv(filepath, sep=',', index=False)
|
|
120
|
+
# Include a comment at the top of the file
|
|
121
|
+
file_comment = f'# {comment}\n' if comment else f''
|
|
122
|
+
file_comment += f'# Tunnel splitting energies\n'
|
|
123
|
+
file_comment += f'# Calculated with ATON {version}\n'
|
|
124
|
+
file_comment += f'# https://pablogila.github.io/ATON\n#'
|
|
125
|
+
txt.edit.insert_at(filepath, file_comment, 0)
|
|
126
|
+
print(f'Tunnel splitting energies saved to {filepath}')
|
|
127
|
+
return df
|
|
128
|
+
|
|
129
|
+
|
|
48
130
|
def get_energies(systems:list) -> list:
|
|
49
|
-
"""Get a list with all eigenvalues from all systems.
|
|
131
|
+
"""Get a list with all lists of eigenvalues from all systems.
|
|
50
132
|
|
|
51
133
|
If no eigenvalues are present for a particular system, appends None.
|
|
52
134
|
"""
|
|
@@ -100,25 +182,6 @@ def get_groups(systems:list) -> list:
|
|
|
100
182
|
return groups
|
|
101
183
|
|
|
102
184
|
|
|
103
|
-
def sort_by_gridsize(systems:list) -> list:
|
|
104
|
-
"""Sorts a list of System objects by `System.gridsize`."""
|
|
105
|
-
as_list(systems)
|
|
106
|
-
systems = sorted(systems, key=lambda sys: sys.gridsize)
|
|
107
|
-
return systems
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def reduce_size(systems:list) -> list:
|
|
111
|
-
"""Discard data that takes too much space.
|
|
112
|
-
|
|
113
|
-
Removes eigenvectors, potential values and grids,
|
|
114
|
-
for all System values inside the `systems` list.
|
|
115
|
-
"""
|
|
116
|
-
as_list(systems)
|
|
117
|
-
for dataset in systems:
|
|
118
|
-
dataset = dataset.reduce_size()
|
|
119
|
-
return systems
|
|
120
|
-
|
|
121
|
-
|
|
122
185
|
def get_ideal_E(E_level:int) -> int:
|
|
123
186
|
"""Calculates the ideal energy for a specified `E_level`.
|
|
124
187
|
|
|
@@ -133,36 +196,21 @@ def get_ideal_E(E_level:int) -> int:
|
|
|
133
196
|
return ideal_E
|
|
134
197
|
|
|
135
198
|
|
|
136
|
-
def
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
"""Save the tunnel splitting energies for all `systems` to a tunnel_splittings.csv file.
|
|
199
|
+
def sort_by_gridsize(systems:list) -> list:
|
|
200
|
+
"""Sorts a list of System objects by `System.gridsize`."""
|
|
201
|
+
as_list(systems)
|
|
202
|
+
systems = sorted(systems, key=lambda sys: sys.gridsize)
|
|
203
|
+
return systems
|
|
142
204
|
|
|
143
|
-
Returns a Pandas Dataset with `System.comment` columns and `System.splittings` values.
|
|
144
205
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
206
|
+
def reduce_size(systems:list) -> list:
|
|
207
|
+
"""Discard data that takes too much space.
|
|
208
|
+
|
|
209
|
+
Removes eigenvectors, potential values and grids,
|
|
210
|
+
for all System values inside the `systems` list.
|
|
149
211
|
"""
|
|
150
212
|
as_list(systems)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
tunnelling_E[s.comment] = s.splittings
|
|
155
|
-
df = pd.DataFrame(tunnelling_E)
|
|
156
|
-
if not filepath:
|
|
157
|
-
return df
|
|
158
|
-
# Else save to file
|
|
159
|
-
df.to_csv(filepath, sep=',', index=False)
|
|
160
|
-
# Include a comment at the top of the file
|
|
161
|
-
file_comment = f'# {comment}\n' if comment else f''
|
|
162
|
-
file_comment += f'# Tunnel splitting energies\n'
|
|
163
|
-
file_comment += f'# Calculated with ATON {version}\n'
|
|
164
|
-
file_comment += f'# https://pablogila.github.io/ATON\n#'
|
|
165
|
-
txt.edit.insert_at(filepath, file_comment, 0)
|
|
166
|
-
print(f'Tunnel splitting energies saved to {filepath}')
|
|
167
|
-
return df
|
|
213
|
+
for dataset in systems:
|
|
214
|
+
dataset = dataset.reduce_size()
|
|
215
|
+
return systems
|
|
168
216
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: aton
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: The Ab-iniTiO & Neutron research toolbox, or ATON, provides powerful and comprehensive tools for cutting-edge materials research, focused on (but not limited to) neutron science.
|
|
5
5
|
Author: Pablo Gila-Herranz
|
|
6
6
|
Author-email: pgila001@ikasle.ehu.eus
|
|
@@ -3,6 +3,7 @@ import aton.interface as interface
|
|
|
3
3
|
import aton.txt.extract as extract
|
|
4
4
|
import aton.st.file as file
|
|
5
5
|
import numpy as np
|
|
6
|
+
import warnings
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
folder = 'tests/samples/'
|
|
@@ -87,4 +88,6 @@ def test_phase():
|
|
|
87
88
|
sys.change_phase(-1)
|
|
88
89
|
assert round(sys.grid[0], 2) == round(-np.pi/2, 2)
|
|
89
90
|
assert round(sys.potential_values[0], 2) == 0.5
|
|
91
|
+
# Were eigenvalues calculated?
|
|
92
|
+
assert len(sys.eigenvalues) > 0
|
|
90
93
|
|
aton-0.2.4/aton/qrotor/solve.py
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
# Description
|
|
3
|
-
|
|
4
|
-
This module is used to solve any given quantum system.
|
|
5
|
-
|
|
6
|
-
Although the functions of this module can be used independently,
|
|
7
|
-
it is highly recommended to use the `System.solve()` method instead,
|
|
8
|
-
which does all the solving automatically (see `aton.qrotor.system.System.solve()`).
|
|
9
|
-
However, advanced users might want to use some of these functions independently;
|
|
10
|
-
for example, if your system energy levels are not degenerated in triplets,
|
|
11
|
-
you might want to use `excitations()` to solve the energy excitations and tunnel splittings with the proper degeneracy.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# Index
|
|
15
|
-
|
|
16
|
-
| | |
|
|
17
|
-
| --- | --- |
|
|
18
|
-
| `energies()` | Solve the quantum system, including eigenvalues and eigenvectors |
|
|
19
|
-
| `potential()` | Solve the potential values of the system |
|
|
20
|
-
| `schrodinger()` | Solve the Schrödiger equation for the system |
|
|
21
|
-
| `hamiltonian_matrix()` | Calculate the hamiltonian matrix of the system |
|
|
22
|
-
| `laplacian_matrix()` | Calculate the second derivative matrix for a given grid |
|
|
23
|
-
| `excitations()` | Get excitation levels and tunnel splitting energies for a set of eigenvalues |
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
from .system import System
|
|
30
|
-
from .potential import solve as solve_potential
|
|
31
|
-
from .potential import interpolate
|
|
32
|
-
import time
|
|
33
|
-
import numpy as np
|
|
34
|
-
from scipy import sparse
|
|
35
|
-
import aton
|
|
36
|
-
from aton._version import __version__
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def energies(system:System, filename:str=None) -> System:
|
|
40
|
-
"""Solves the quantum `system`.
|
|
41
|
-
|
|
42
|
-
This includes solving the potential, the eigenvalues and the eigenvectors.
|
|
43
|
-
|
|
44
|
-
The resulting System object is saved with pickle to `filename` if specified.
|
|
45
|
-
"""
|
|
46
|
-
if not any(system.grid):
|
|
47
|
-
system.set_grid()
|
|
48
|
-
system = potential(system)
|
|
49
|
-
system = schrodinger(system)
|
|
50
|
-
if filename:
|
|
51
|
-
aton.st.file.save(system, filename)
|
|
52
|
-
return system
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def potential(system:System) -> System:
|
|
56
|
-
"""Solves the potential values of the `system`.
|
|
57
|
-
|
|
58
|
-
It interpolates the potential if `system.gridsize` is larger than the current grid.
|
|
59
|
-
It solves the potential according to the potential name,
|
|
60
|
-
by calling `aton.qrotor.potential.solve()`.
|
|
61
|
-
Then it applies extra operations, such as removing the potential offset
|
|
62
|
-
if `system.correct_potential_offset = True`.
|
|
63
|
-
"""
|
|
64
|
-
if system.gridsize and any(system.grid):
|
|
65
|
-
if system.gridsize > len(system.grid):
|
|
66
|
-
system = interpolate(system)
|
|
67
|
-
V = solve_potential(system)
|
|
68
|
-
if system.correct_potential_offset is True:
|
|
69
|
-
offset = min(V)
|
|
70
|
-
V = V - offset
|
|
71
|
-
system.potential_offset = offset
|
|
72
|
-
system.potential_values = V
|
|
73
|
-
return system
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def schrodinger(system:System) -> System:
|
|
77
|
-
"""Solves the Schrödinger equation for a given `system`.
|
|
78
|
-
|
|
79
|
-
Uses ARPACK in shift-inverse mode to solve the hamiltonian sparse matrix.
|
|
80
|
-
"""
|
|
81
|
-
time_start = time.time()
|
|
82
|
-
V = system.potential_values
|
|
83
|
-
H = hamiltonian_matrix(system)
|
|
84
|
-
print('Solving Schrodinger equation...')
|
|
85
|
-
# Solve eigenvalues with ARPACK in shift-inverse mode, with a sparse matrix
|
|
86
|
-
eigenvalues, eigenvectors = sparse.linalg.eigsh(H, system.E_levels, which='LM', sigma=0, maxiter=10000)
|
|
87
|
-
if any(eigenvalues) is None:
|
|
88
|
-
print('WARNING: Not all eigenvalues were found.\n')
|
|
89
|
-
else: print('Done.')
|
|
90
|
-
system.version = __version__
|
|
91
|
-
system.runtime = time.time() - time_start
|
|
92
|
-
system.eigenvalues = eigenvalues
|
|
93
|
-
system.potential_max = max(V)
|
|
94
|
-
system.potential_min = min(V)
|
|
95
|
-
system.energy_barrier = max(V) - min(eigenvalues)
|
|
96
|
-
# Solve excitations and tunnel splittings, assuming triplet degeneracy
|
|
97
|
-
system = excitations(system, deg=3)
|
|
98
|
-
# Do we really need to save eigenvectors?
|
|
99
|
-
if system.save_eigenvectors == True:
|
|
100
|
-
system.eigenvectors = np.transpose(eigenvectors)
|
|
101
|
-
return system
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def hamiltonian_matrix(system:System):
|
|
105
|
-
"""Calculates the Hamiltonian sparse matrix for a given `system`."""
|
|
106
|
-
print(f'Creating Hamiltonian matrix of size {system.gridsize}...')
|
|
107
|
-
V = system.potential_values.tolist()
|
|
108
|
-
potential = sparse.diags(V, format='lil')
|
|
109
|
-
B = system.B
|
|
110
|
-
x = system.grid
|
|
111
|
-
H = -B * laplacian_matrix(x) + potential
|
|
112
|
-
return H
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def laplacian_matrix(grid):
|
|
116
|
-
"""Calculates the Laplacian (second derivative) matrix for a given `grid`."""
|
|
117
|
-
x = grid
|
|
118
|
-
diagonals = [-2*np.ones(len(x)), np.ones(len(x)), np.ones(len(x))]
|
|
119
|
-
laplacian_matrix = sparse.spdiags(diagonals, [0, -1, 1], format='lil')
|
|
120
|
-
# Periodic boundary conditions
|
|
121
|
-
laplacian_matrix[0, -1] = 1
|
|
122
|
-
laplacian_matrix[-1, 0] = 1
|
|
123
|
-
dx = x[1] - x[0]
|
|
124
|
-
laplacian_matrix /= dx**2
|
|
125
|
-
return laplacian_matrix
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def excitations(
|
|
129
|
-
system:System,
|
|
130
|
-
deg:int=3,
|
|
131
|
-
) -> tuple:
|
|
132
|
-
"""Calculate the excitation levels and the tunnel splitting energies of a system.
|
|
133
|
-
|
|
134
|
-
Stops the calculation when energies reach the maximum potential.
|
|
135
|
-
Assumes that eigenvalues are degenerated in triplets;
|
|
136
|
-
this degeneracy can be specified with `deg`.
|
|
137
|
-
"""
|
|
138
|
-
eigenvalues = system.eigenvalues
|
|
139
|
-
V_max = system.potential_max
|
|
140
|
-
ground_energy = min(eigenvalues)
|
|
141
|
-
excitations = []
|
|
142
|
-
tunnel_splittings = []
|
|
143
|
-
i = 0
|
|
144
|
-
while (i + deg-1) <= len(eigenvalues):
|
|
145
|
-
# Get the eigenvalues corresponding to this triplet (or whatever degeneracy)
|
|
146
|
-
i_max = i + deg # Index indicating the end of this triplet
|
|
147
|
-
triplet = eigenvalues[i:i_max]
|
|
148
|
-
# Check that we are still below the potential max
|
|
149
|
-
if any(triplet) > V_max:
|
|
150
|
-
break
|
|
151
|
-
# Check that all eigenvalues are valid, and not None
|
|
152
|
-
if any(triplet) is None:
|
|
153
|
-
break
|
|
154
|
-
# Get the excitation energy, by comparing with the ground state
|
|
155
|
-
if i != 0: # Skip the ground energy level
|
|
156
|
-
excitations.append(min(triplet) - ground_energy)
|
|
157
|
-
# Check the energy differences inside each triplet (or whatever degeneracy)
|
|
158
|
-
E_diff = []
|
|
159
|
-
for j in range(deg-1): # 0, 1 (not 2)
|
|
160
|
-
E_0 = triplet[j]
|
|
161
|
-
E_1 = triplet[j+1]
|
|
162
|
-
diff = E_1 - E_0
|
|
163
|
-
E_diff.append(diff)
|
|
164
|
-
# Get the maximum energy difference inside the triplet, which is the tunnel splitting
|
|
165
|
-
tunnel_splittings.append(max(E_diff))
|
|
166
|
-
# Move to the next triplet
|
|
167
|
-
i += deg
|
|
168
|
-
# Set the energy excitations and tunnel splittings
|
|
169
|
-
system.excitations = excitations
|
|
170
|
-
system.splittings = tunnel_splittings
|
|
171
|
-
return system
|
|
172
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|