quant-met 0.0.1__tar.gz → 0.0.2__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.
- {quant_met-0.0.1 → quant_met-0.0.2}/PKG-INFO +16 -8
- {quant_met-0.0.1 → quant_met-0.0.2}/README.md +13 -5
- {quant_met-0.0.1 → quant_met-0.0.2}/pyproject.toml +10 -6
- {quant_met-0.0.1 → quant_met-0.0.2}/src/quant_met/__init__.py +4 -0
- quant_met-0.0.2/src/quant_met/hamiltonians/__init__.py +14 -0
- quant_met-0.0.2/src/quant_met/hamiltonians/_base_hamiltonian.py +172 -0
- quant_met-0.0.2/src/quant_met/hamiltonians/_eg_x.py +124 -0
- quant_met-0.0.2/src/quant_met/hamiltonians/_free_energy.py +39 -0
- quant_met-0.0.2/src/quant_met/hamiltonians/_graphene.py +93 -0
- quant_met-0.0.2/src/quant_met/hamiltonians/_superfluid_weight.py +130 -0
- quant_met-0.0.2/src/quant_met/hamiltonians/_utils.py +10 -0
- quant_met-0.0.2/src/quant_met/plotting/__init__.py +7 -0
- {quant_met-0.0.1 → quant_met-0.0.2}/src/quant_met/plotting/_plotting.py +59 -83
- quant_met-0.0.2/src/quant_met/utils.py +28 -0
- quant_met-0.0.1/src/quant_met/__about__.py +0 -4
- quant_met-0.0.1/src/quant_met/bcs/__init__.py +0 -0
- quant_met-0.0.1/src/quant_met/bcs/find_fixpoint.py +0 -65
- quant_met-0.0.1/src/quant_met/bcs/gap_equation.py +0 -43
- quant_met-0.0.1/src/quant_met/cli.py +0 -16
- quant_met-0.0.1/src/quant_met/configuration.py +0 -53
- quant_met-0.0.1/src/quant_met/hamiltonians.py +0 -174
- quant_met-0.0.1/src/quant_met/plotting/__init__.py +0 -2
- {quant_met-0.0.1 → quant_met-0.0.2}/LICENSE.txt +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: quant-met
|
3
|
-
Version: 0.0.
|
4
|
-
Summary:
|
3
|
+
Version: 0.0.2
|
4
|
+
Summary: Calculate superconductivity in flat-band systems.
|
5
5
|
Author: Tjark Sievers
|
6
6
|
Author-email: tsievers@physnet.uni-hamburg.de
|
7
7
|
Requires-Python: >=3.10,<4.0
|
@@ -9,7 +9,7 @@ Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.10
|
10
10
|
Classifier: Programming Language :: Python :: 3.11
|
11
11
|
Classifier: Programming Language :: Python :: 3.12
|
12
|
-
Requires-Dist:
|
12
|
+
Requires-Dist: h5py (>=3.11.0,<4.0.0)
|
13
13
|
Requires-Dist: matplotlib (>=3.8.4,<4.0.0)
|
14
14
|
Requires-Dist: numpy (>=1.26.4,<2.0.0)
|
15
15
|
Requires-Dist: pandas (>=2.2.2,<3.0.0)
|
@@ -21,18 +21,28 @@ Description-Content-Type: text/markdown
|
|
21
21
|
|
22
22
|
[](https://github.com/Ruberhauptmann/quant-met/actions/workflows/test.yml)
|
23
23
|
[](https://coveralls.io/github/Ruberhauptmann/quant-met?branch=main)
|
24
|
+
[](https://pypi.org/project/quant-met/)
|
25
|
+
[](https://pypi.org/project/quant-met/)
|
26
|
+
|
27
|
+
This is a python package to treat superconductivity in flat-band systems.
|
24
28
|
|
25
29
|
* Documentation: [quant-met.readthedocs.io](https://quant-met.readthedocs.io/en/latest/)
|
26
30
|
|
27
31
|
## Installation
|
28
32
|
|
29
33
|
The package can be installed via
|
34
|
+
```shell
|
35
|
+
pip install quant-met
|
36
|
+
```
|
30
37
|
|
31
38
|
## Usage
|
32
39
|
|
40
|
+
For usage examples see [documentation](https://quant-met.readthedocs.io/en/latest/examples.html).
|
41
|
+
|
33
42
|
## Contributing
|
34
43
|
|
35
|
-
|
44
|
+
This is a personal project, very geared to the work I did in my master's thesis.
|
45
|
+
If someone is using this and experiencing bugs or want the software extended, feel free to open an issue!
|
36
46
|
|
37
47
|
### Developing
|
38
48
|
|
@@ -49,13 +59,11 @@ Set up the development environment:
|
|
49
59
|
* run `make environment`
|
50
60
|
* now activate the conda environment `conda activate quant-met-dev`
|
51
61
|
|
52
|
-
Now you can create a separate branch to work on the project.
|
53
|
-
|
54
62
|
You can manually run tests using for example `tox -e py312` (for running against python 3.12).
|
55
|
-
After pushing your branch, all tests will also be run via
|
63
|
+
After pushing your branch, all tests will also be run via Github Actions.
|
56
64
|
|
57
65
|
Using `pre-commit`, automatic linting and formatting is done before every commit, which may cause the first commit to fail.
|
58
66
|
A second try should then succeed.
|
59
67
|
|
60
|
-
After you are done working on an issue and all tests are running successful, you can add a new piece of changelog via `scriv create` and make a
|
68
|
+
After you are done working on an issue and all tests are running successful, you can add a new piece of changelog via `scriv create` and make a pull request.
|
61
69
|
|
@@ -2,18 +2,28 @@
|
|
2
2
|
|
3
3
|
[](https://github.com/Ruberhauptmann/quant-met/actions/workflows/test.yml)
|
4
4
|
[](https://coveralls.io/github/Ruberhauptmann/quant-met?branch=main)
|
5
|
+
[](https://pypi.org/project/quant-met/)
|
6
|
+
[](https://pypi.org/project/quant-met/)
|
7
|
+
|
8
|
+
This is a python package to treat superconductivity in flat-band systems.
|
5
9
|
|
6
10
|
* Documentation: [quant-met.readthedocs.io](https://quant-met.readthedocs.io/en/latest/)
|
7
11
|
|
8
12
|
## Installation
|
9
13
|
|
10
14
|
The package can be installed via
|
15
|
+
```shell
|
16
|
+
pip install quant-met
|
17
|
+
```
|
11
18
|
|
12
19
|
## Usage
|
13
20
|
|
21
|
+
For usage examples see [documentation](https://quant-met.readthedocs.io/en/latest/examples.html).
|
22
|
+
|
14
23
|
## Contributing
|
15
24
|
|
16
|
-
|
25
|
+
This is a personal project, very geared to the work I did in my master's thesis.
|
26
|
+
If someone is using this and experiencing bugs or want the software extended, feel free to open an issue!
|
17
27
|
|
18
28
|
### Developing
|
19
29
|
|
@@ -30,12 +40,10 @@ Set up the development environment:
|
|
30
40
|
* run `make environment`
|
31
41
|
* now activate the conda environment `conda activate quant-met-dev`
|
32
42
|
|
33
|
-
Now you can create a separate branch to work on the project.
|
34
|
-
|
35
43
|
You can manually run tests using for example `tox -e py312` (for running against python 3.12).
|
36
|
-
After pushing your branch, all tests will also be run via
|
44
|
+
After pushing your branch, all tests will also be run via Github Actions.
|
37
45
|
|
38
46
|
Using `pre-commit`, automatic linting and formatting is done before every commit, which may cause the first commit to fail.
|
39
47
|
A second try should then succeed.
|
40
48
|
|
41
|
-
After you are done working on an issue and all tests are running successful, you can add a new piece of changelog via `scriv create` and make a
|
49
|
+
After you are done working on an issue and all tests are running successful, you can add a new piece of changelog via `scriv create` and make a pull request.
|
@@ -1,21 +1,18 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "quant-met"
|
3
|
-
version = "0.0.
|
4
|
-
description = ""
|
3
|
+
version = "0.0.2"
|
4
|
+
description = "Calculate superconductivity in flat-band systems."
|
5
5
|
authors = ["Tjark Sievers <tsievers@physnet.uni-hamburg.de>"]
|
6
6
|
readme = "README.md"
|
7
7
|
|
8
|
-
[tool.poetry.scripts]
|
9
|
-
quantmet = "quant_met.cli:cli"
|
10
|
-
|
11
8
|
[tool.poetry.dependencies]
|
12
9
|
python = "^3.10"
|
13
10
|
numpy = "^1.26.4"
|
14
11
|
scipy = "^1.13.0"
|
15
|
-
click = "^8.1.7"
|
16
12
|
matplotlib = "^3.8.4"
|
17
13
|
pandas = "^2.2.2"
|
18
14
|
sympy = "^1.12"
|
15
|
+
h5py = "^3.11.0"
|
19
16
|
|
20
17
|
[tool.poetry.group.dev.dependencies]
|
21
18
|
pre-commit = "^3.7.0"
|
@@ -29,6 +26,13 @@ sphinx-rtd-theme = "^2.0.0"
|
|
29
26
|
myst-parser = "^3.0.1"
|
30
27
|
nbsphinx = "^0.9.4"
|
31
28
|
sphinx-gallery = "^0.16.0"
|
29
|
+
hypothesis = {extras = ["numpy"], version = "^6.103.0"}
|
30
|
+
pytest = "^8.2.1"
|
31
|
+
pytest-cov = "^5.0.0"
|
32
|
+
mypy = "^1.10.0"
|
33
|
+
flake8 = "^7.0.0"
|
34
|
+
flake8-docstrings = "^1.7.0"
|
35
|
+
pytest-regressions = "^2.5.0"
|
32
36
|
|
33
37
|
[build-system]
|
34
38
|
requires = ["poetry-core"]
|
@@ -0,0 +1,14 @@
|
|
1
|
+
from ._base_hamiltonian import BaseHamiltonian
|
2
|
+
from ._eg_x import EGXHamiltonian
|
3
|
+
from ._free_energy import free_energy, free_energy_uniform_pairing
|
4
|
+
from ._graphene import GrapheneHamiltonian
|
5
|
+
from ._superfluid_weight import calculate_superfluid_weight
|
6
|
+
|
7
|
+
__all__ = [
|
8
|
+
"BaseHamiltonian",
|
9
|
+
"GrapheneHamiltonian",
|
10
|
+
"EGXHamiltonian",
|
11
|
+
"calculate_superfluid_weight",
|
12
|
+
"free_energy",
|
13
|
+
"free_energy_uniform_pairing",
|
14
|
+
]
|
@@ -0,0 +1,172 @@
|
|
1
|
+
import pathlib
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
|
4
|
+
import h5py
|
5
|
+
import numpy as np
|
6
|
+
import numpy.typing as npt
|
7
|
+
import pandas as pd
|
8
|
+
|
9
|
+
|
10
|
+
class BaseHamiltonian(ABC):
|
11
|
+
"""Base class for Hamiltonians."""
|
12
|
+
|
13
|
+
@property
|
14
|
+
@abstractmethod
|
15
|
+
def number_of_bands(self) -> int:
|
16
|
+
raise NotImplementedError
|
17
|
+
|
18
|
+
@property
|
19
|
+
@abstractmethod
|
20
|
+
def coloumb_orbital_basis(self) -> npt.NDArray[np.float64]:
|
21
|
+
raise NotImplementedError
|
22
|
+
|
23
|
+
@property
|
24
|
+
def delta_orbital_basis(self) -> npt.NDArray[np.float64]:
|
25
|
+
raise NotImplementedError
|
26
|
+
|
27
|
+
@delta_orbital_basis.setter
|
28
|
+
@abstractmethod
|
29
|
+
def delta_orbital_basis(self, new_delta: npt.NDArray[np.float64]) -> None:
|
30
|
+
raise NotImplementedError
|
31
|
+
|
32
|
+
@abstractmethod
|
33
|
+
def _hamiltonian_one_point(
|
34
|
+
self, k_point: npt.NDArray[np.float64]
|
35
|
+
) -> npt.NDArray[np.complex64]:
|
36
|
+
raise NotImplementedError
|
37
|
+
|
38
|
+
@abstractmethod
|
39
|
+
def _hamiltonian_derivative_one_point(
|
40
|
+
self, k_point: npt.NDArray[np.float64], directions: str
|
41
|
+
) -> npt.NDArray[np.complex64]:
|
42
|
+
raise NotImplementedError
|
43
|
+
|
44
|
+
def _bdg_hamiltonian_one_point(
|
45
|
+
self, k_point: npt.NDArray[np.float64]
|
46
|
+
) -> npt.NDArray[np.complex64]:
|
47
|
+
delta_matrix: npt.NDArray[np.complex64] = np.zeros(
|
48
|
+
shape=(self.number_of_bands, self.number_of_bands), dtype=np.complex64
|
49
|
+
)
|
50
|
+
np.fill_diagonal(delta_matrix, self.delta_orbital_basis)
|
51
|
+
|
52
|
+
h = np.block(
|
53
|
+
[
|
54
|
+
[self.hamiltonian(k_point), delta_matrix],
|
55
|
+
[np.conjugate(delta_matrix), -np.conjugate(self.hamiltonian(-k_point))],
|
56
|
+
]
|
57
|
+
)
|
58
|
+
return h
|
59
|
+
|
60
|
+
def save(self, filename: pathlib.Path) -> None:
|
61
|
+
with h5py.File(f"{filename}", "a") as f:
|
62
|
+
f.create_dataset("delta", data=self.delta_orbital_basis)
|
63
|
+
for key, value in vars(self).items():
|
64
|
+
if not key.startswith("_"):
|
65
|
+
f.attrs[key] = value
|
66
|
+
|
67
|
+
@classmethod
|
68
|
+
def from_file(cls, filename: pathlib.Path) -> "BaseHamiltonian":
|
69
|
+
config_dict = {}
|
70
|
+
with h5py.File(f"{filename}", "r") as f:
|
71
|
+
config_dict["delta"] = f["delta"][()]
|
72
|
+
for key, value in f.attrs.items():
|
73
|
+
config_dict[key] = value
|
74
|
+
|
75
|
+
return cls(**config_dict)
|
76
|
+
|
77
|
+
def bdg_hamiltonian(self, k: npt.NDArray[np.float64]) -> npt.NDArray[np.complex64]:
|
78
|
+
if np.isnan(k).any() or np.isinf(k).any():
|
79
|
+
raise ValueError("k is NaN or Infinity")
|
80
|
+
if k.ndim == 1:
|
81
|
+
h = self._bdg_hamiltonian_one_point(k)
|
82
|
+
else:
|
83
|
+
h = np.array([self._bdg_hamiltonian_one_point(k) for k in k])
|
84
|
+
return h
|
85
|
+
|
86
|
+
def hamiltonian(self, k: npt.NDArray[np.float64]) -> npt.NDArray[np.complex64]:
|
87
|
+
if np.isnan(k).any() or np.isinf(k).any():
|
88
|
+
raise ValueError("k is NaN or Infinity")
|
89
|
+
if k.ndim == 1:
|
90
|
+
h = self._hamiltonian_one_point(k)
|
91
|
+
else:
|
92
|
+
h = np.array([self._hamiltonian_one_point(k) for k in k])
|
93
|
+
return h
|
94
|
+
|
95
|
+
def hamiltonian_derivative(
|
96
|
+
self, k: npt.NDArray[np.float64], direction: str
|
97
|
+
) -> npt.NDArray[np.complex64]:
|
98
|
+
if np.isnan(k).any() or np.isinf(k).any():
|
99
|
+
raise ValueError("k is NaN or Infinity")
|
100
|
+
if k.ndim == 1:
|
101
|
+
h = self._hamiltonian_derivative_one_point(k, direction)
|
102
|
+
else:
|
103
|
+
h = np.array(
|
104
|
+
[self._hamiltonian_derivative_one_point(k, direction) for k in k]
|
105
|
+
)
|
106
|
+
return h
|
107
|
+
|
108
|
+
def diagonalize_nonint(
|
109
|
+
self, k: npt.NDArray[np.float64]
|
110
|
+
) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
|
111
|
+
k_point_matrix = self.hamiltonian(k)
|
112
|
+
|
113
|
+
if k.ndim == 1:
|
114
|
+
band_energies, bloch_wavefunctions = np.linalg.eigh(k_point_matrix)
|
115
|
+
else:
|
116
|
+
bloch_wavefunctions = np.zeros(
|
117
|
+
(len(k), self.number_of_bands, self.number_of_bands),
|
118
|
+
dtype=complex,
|
119
|
+
)
|
120
|
+
band_energies = np.zeros((len(k), self.number_of_bands))
|
121
|
+
|
122
|
+
for i, k in enumerate(k):
|
123
|
+
band_energies[i], bloch_wavefunctions[i] = np.linalg.eigh(
|
124
|
+
k_point_matrix[i]
|
125
|
+
)
|
126
|
+
|
127
|
+
return band_energies, bloch_wavefunctions
|
128
|
+
|
129
|
+
def diagonalize_bdg(
|
130
|
+
self, k: npt.NDArray[np.float64]
|
131
|
+
) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.complex64]]:
|
132
|
+
bdg_matrix = self.bdg_hamiltonian(k)
|
133
|
+
|
134
|
+
if k.ndim == 1:
|
135
|
+
bdg_energies, bdg_wavefunctions = np.linalg.eigh(bdg_matrix)
|
136
|
+
else:
|
137
|
+
bdg_wavefunctions = np.zeros(
|
138
|
+
(len(k), 2 * self.number_of_bands, 2 * self.number_of_bands),
|
139
|
+
dtype=np.complex64,
|
140
|
+
)
|
141
|
+
bdg_energies = np.zeros((len(k), 2 * self.number_of_bands))
|
142
|
+
|
143
|
+
for i, k in enumerate(k):
|
144
|
+
bdg_energies[i], bdg_wavefunctions[i] = np.linalg.eigh(bdg_matrix[i])
|
145
|
+
|
146
|
+
return bdg_energies, bdg_wavefunctions
|
147
|
+
|
148
|
+
def calculate_bandstructure(
|
149
|
+
self,
|
150
|
+
k: npt.NDArray[np.float64],
|
151
|
+
overlaps: tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]] | None = None,
|
152
|
+
) -> pd.DataFrame:
|
153
|
+
k_point_matrix = self.hamiltonian(k)
|
154
|
+
|
155
|
+
results = pd.DataFrame(
|
156
|
+
index=range(len(k)),
|
157
|
+
dtype=float,
|
158
|
+
)
|
159
|
+
|
160
|
+
for i, k in enumerate(k):
|
161
|
+
energies, eigenvectors = np.linalg.eigh(k_point_matrix[i])
|
162
|
+
|
163
|
+
for band_index in range(self.number_of_bands):
|
164
|
+
results.at[i, f"band_{band_index}"] = energies[band_index]
|
165
|
+
|
166
|
+
if overlaps is not None:
|
167
|
+
results.at[i, f"wx_{band_index}"] = (
|
168
|
+
np.abs(np.dot(eigenvectors[:, band_index], overlaps[0])) ** 2
|
169
|
+
- np.abs(np.dot(eigenvectors[:, band_index], overlaps[1])) ** 2
|
170
|
+
)
|
171
|
+
|
172
|
+
return results
|
@@ -0,0 +1,124 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import numpy.typing as npt
|
3
|
+
|
4
|
+
from ._base_hamiltonian import BaseHamiltonian
|
5
|
+
from ._utils import _check_valid_float
|
6
|
+
|
7
|
+
|
8
|
+
class EGXHamiltonian(BaseHamiltonian):
|
9
|
+
def __init__(
|
10
|
+
self,
|
11
|
+
t_gr: float,
|
12
|
+
t_x: float,
|
13
|
+
V: float,
|
14
|
+
a: float,
|
15
|
+
mu: float,
|
16
|
+
U_gr: float,
|
17
|
+
U_x: float,
|
18
|
+
delta: npt.NDArray[np.float64] | None = None,
|
19
|
+
):
|
20
|
+
self.t_gr = _check_valid_float(t_gr, "Hopping graphene")
|
21
|
+
self.t_x = _check_valid_float(t_x, "Hopping impurity")
|
22
|
+
self.V = _check_valid_float(V, "Hybridisation")
|
23
|
+
self.a = _check_valid_float(a, "Lattice constant")
|
24
|
+
self.mu = _check_valid_float(mu, "Chemical potential")
|
25
|
+
self.U_gr = _check_valid_float(U_gr, "Coloumb interaction graphene")
|
26
|
+
self.U_x = _check_valid_float(U_x, "Coloumb interaction impurity")
|
27
|
+
if delta is None:
|
28
|
+
self._delta_orbital_basis = np.zeros(3)
|
29
|
+
else:
|
30
|
+
self._delta_orbital_basis = delta
|
31
|
+
|
32
|
+
@property
|
33
|
+
def coloumb_orbital_basis(self) -> npt.NDArray[np.float64]:
|
34
|
+
return np.array([self.U_gr, self.U_gr, self.U_x])
|
35
|
+
|
36
|
+
@property
|
37
|
+
def delta_orbital_basis(self) -> npt.NDArray[np.float64]:
|
38
|
+
return self._delta_orbital_basis
|
39
|
+
|
40
|
+
@delta_orbital_basis.setter
|
41
|
+
def delta_orbital_basis(self, new_delta: npt.NDArray[np.float64]) -> None:
|
42
|
+
self._delta_orbital_basis = new_delta
|
43
|
+
|
44
|
+
@property
|
45
|
+
def number_of_bands(self) -> int:
|
46
|
+
return 3
|
47
|
+
|
48
|
+
def _hamiltonian_derivative_one_point(
|
49
|
+
self, k: npt.NDArray[np.float64], direction: str
|
50
|
+
) -> npt.NDArray[np.complex64]:
|
51
|
+
assert direction in ["x", "y"]
|
52
|
+
|
53
|
+
t_gr = self.t_gr
|
54
|
+
t_x = self.t_x
|
55
|
+
a = self.a
|
56
|
+
|
57
|
+
h = np.zeros((self.number_of_bands, self.number_of_bands), dtype=np.complex64)
|
58
|
+
|
59
|
+
if direction == "x":
|
60
|
+
h[0, 1] = (
|
61
|
+
t_gr
|
62
|
+
* a
|
63
|
+
* np.exp(-0.5j * a / np.sqrt(3) * k[1])
|
64
|
+
* np.sin(0.5 * a * k[0])
|
65
|
+
)
|
66
|
+
h[1, 0] = h[0, 1].conjugate()
|
67
|
+
h[2, 2] = (
|
68
|
+
2
|
69
|
+
* a
|
70
|
+
* t_x
|
71
|
+
* (
|
72
|
+
np.sin(a * k[0])
|
73
|
+
+ np.sin(0.5 * a * k[0]) * np.cos(0.5 * np.sqrt(3) * a * k[1])
|
74
|
+
)
|
75
|
+
)
|
76
|
+
else:
|
77
|
+
h[0, 1] = (
|
78
|
+
-t_gr
|
79
|
+
* 1j
|
80
|
+
* a
|
81
|
+
/ np.sqrt(3)
|
82
|
+
* (
|
83
|
+
np.exp(1j * a / np.sqrt(3) * k[1])
|
84
|
+
- np.exp(-0.5j * a / np.sqrt(3) * k[1]) * np.cos(0.5 * a * k[0])
|
85
|
+
)
|
86
|
+
)
|
87
|
+
h[1, 0] = h[0, 1].conjugate()
|
88
|
+
h[2, 2] = np.sqrt(3) * a * t_x * np.cos(0.5 * np.sqrt(3) * a * k[1])
|
89
|
+
|
90
|
+
return h
|
91
|
+
|
92
|
+
def _hamiltonian_one_point(
|
93
|
+
self, k: npt.NDArray[np.float64]
|
94
|
+
) -> npt.NDArray[np.complex64]:
|
95
|
+
t_gr = self.t_gr
|
96
|
+
t_x = self.t_x
|
97
|
+
a = self.a
|
98
|
+
# a_0 = a / np.sqrt(3)
|
99
|
+
V = self.V
|
100
|
+
mu = self.mu
|
101
|
+
|
102
|
+
h = np.zeros((self.number_of_bands, self.number_of_bands), dtype=np.complex64)
|
103
|
+
|
104
|
+
h[0, 1] = -t_gr * (
|
105
|
+
np.exp(1j * k[1] * a / np.sqrt(3))
|
106
|
+
+ 2 * np.exp(-0.5j * a / np.sqrt(3) * k[1]) * (np.cos(0.5 * a * k[0]))
|
107
|
+
)
|
108
|
+
|
109
|
+
h[1, 0] = h[0, 1].conjugate()
|
110
|
+
|
111
|
+
h[2, 0] = V
|
112
|
+
h[0, 2] = V
|
113
|
+
|
114
|
+
h[2, 2] = (
|
115
|
+
-2
|
116
|
+
* t_x
|
117
|
+
* (
|
118
|
+
np.cos(a * k[0])
|
119
|
+
+ 2 * np.cos(0.5 * a * k[0]) * np.cos(0.5 * np.sqrt(3) * a * k[1])
|
120
|
+
)
|
121
|
+
)
|
122
|
+
h -= mu * np.eye(3, dtype=np.complex64)
|
123
|
+
|
124
|
+
return h
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import numpy.typing as npt
|
3
|
+
|
4
|
+
from ._base_hamiltonian import BaseHamiltonian
|
5
|
+
|
6
|
+
|
7
|
+
def free_energy(
|
8
|
+
delta_vector: npt.NDArray[np.float64],
|
9
|
+
hamiltonian: BaseHamiltonian,
|
10
|
+
k_points: npt.NDArray[np.float64],
|
11
|
+
) -> float:
|
12
|
+
number_k_points = len(k_points)
|
13
|
+
hamiltonian.delta_orbital_basis = delta_vector
|
14
|
+
bdg_energies, _ = hamiltonian.diagonalize_bdg(k_points)
|
15
|
+
|
16
|
+
k_array = np.array(
|
17
|
+
[
|
18
|
+
np.sum(np.abs(bdg_energies[k_index][0 : hamiltonian.number_of_bands]))
|
19
|
+
for k_index in range(number_k_points)
|
20
|
+
]
|
21
|
+
)
|
22
|
+
|
23
|
+
integral: float = -np.sum(k_array, axis=-1) / number_k_points + np.sum(
|
24
|
+
np.power(np.abs(delta_vector), 2) / hamiltonian.coloumb_orbital_basis
|
25
|
+
)
|
26
|
+
|
27
|
+
return integral
|
28
|
+
|
29
|
+
|
30
|
+
def free_energy_uniform_pairing(
|
31
|
+
delta: float,
|
32
|
+
hamiltonian: BaseHamiltonian,
|
33
|
+
k_points: npt.NDArray[np.float64],
|
34
|
+
) -> float:
|
35
|
+
delta_vector = np.ones(hamiltonian.number_of_bands) * delta
|
36
|
+
|
37
|
+
return free_energy(
|
38
|
+
delta_vector=delta_vector, hamiltonian=hamiltonian, k_points=k_points
|
39
|
+
)
|
@@ -0,0 +1,93 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import numpy.typing as npt
|
3
|
+
|
4
|
+
from ._base_hamiltonian import BaseHamiltonian
|
5
|
+
from ._utils import _check_valid_float
|
6
|
+
|
7
|
+
|
8
|
+
class GrapheneHamiltonian(BaseHamiltonian):
|
9
|
+
def __init__(
|
10
|
+
self,
|
11
|
+
t_nn: float,
|
12
|
+
a: float,
|
13
|
+
mu: float,
|
14
|
+
coulomb_gr: float,
|
15
|
+
delta: npt.NDArray[np.float64] | None = None,
|
16
|
+
):
|
17
|
+
self.t_nn = _check_valid_float(t_nn, "Hopping")
|
18
|
+
if a <= 0:
|
19
|
+
raise ValueError("Lattice constant must be positive")
|
20
|
+
self.a = _check_valid_float(a, "Lattice constant")
|
21
|
+
self.mu = _check_valid_float(mu, "Chemical potential")
|
22
|
+
self.coulomb_gr = _check_valid_float(coulomb_gr, "Coloumb interaction")
|
23
|
+
if delta is None:
|
24
|
+
self._delta_orbital_basis = np.zeros(2)
|
25
|
+
else:
|
26
|
+
self._delta_orbital_basis = delta
|
27
|
+
|
28
|
+
@property
|
29
|
+
def coloumb_orbital_basis(self) -> npt.NDArray[np.float64]:
|
30
|
+
return np.array([self.coulomb_gr, self.coulomb_gr])
|
31
|
+
|
32
|
+
@property
|
33
|
+
def number_of_bands(self) -> int:
|
34
|
+
return 2
|
35
|
+
|
36
|
+
@property
|
37
|
+
def delta_orbital_basis(self) -> npt.NDArray[np.float64]:
|
38
|
+
return self._delta_orbital_basis
|
39
|
+
|
40
|
+
@delta_orbital_basis.setter
|
41
|
+
def delta_orbital_basis(self, new_delta: npt.NDArray[np.float64]) -> None:
|
42
|
+
self._delta_orbital_basis = new_delta
|
43
|
+
|
44
|
+
def _hamiltonian_derivative_one_point(
|
45
|
+
self, k: npt.NDArray[np.float64], direction: str
|
46
|
+
) -> npt.NDArray[np.complex64]:
|
47
|
+
assert direction in ["x", "y"]
|
48
|
+
|
49
|
+
t_nn = self.t_nn
|
50
|
+
a = self.a
|
51
|
+
|
52
|
+
h = np.zeros((self.number_of_bands, self.number_of_bands), dtype=np.complex64)
|
53
|
+
|
54
|
+
if direction == "x":
|
55
|
+
h[0, 1] = (
|
56
|
+
t_nn
|
57
|
+
* a
|
58
|
+
* np.exp(-0.5j * a / np.sqrt(3) * k[1])
|
59
|
+
* np.sin(0.5 * a * k[0])
|
60
|
+
)
|
61
|
+
h[1, 0] = h[0, 1].conjugate()
|
62
|
+
else:
|
63
|
+
h[0, 1] = (
|
64
|
+
-t_nn
|
65
|
+
* 1j
|
66
|
+
* a
|
67
|
+
/ np.sqrt(3)
|
68
|
+
* (
|
69
|
+
np.exp(1j * a / np.sqrt(3) * k[1])
|
70
|
+
- np.exp(-0.5j * a / np.sqrt(3) * k[1]) * np.cos(0.5 * a * k[0])
|
71
|
+
)
|
72
|
+
)
|
73
|
+
h[1, 0] = h[0, 1].conjugate()
|
74
|
+
|
75
|
+
return h
|
76
|
+
|
77
|
+
def _hamiltonian_one_point(
|
78
|
+
self, k: npt.NDArray[np.float64]
|
79
|
+
) -> npt.NDArray[np.complex64]:
|
80
|
+
t_nn = self.t_nn
|
81
|
+
a = self.a
|
82
|
+
mu = self.mu
|
83
|
+
|
84
|
+
h = np.zeros((self.number_of_bands, self.number_of_bands), dtype=np.complex64)
|
85
|
+
|
86
|
+
h[0, 1] = -t_nn * (
|
87
|
+
np.exp(1j * k[1] * a / np.sqrt(3))
|
88
|
+
+ 2 * np.exp(-0.5j * a / np.sqrt(3) * k[1]) * (np.cos(0.5 * a * k[0]))
|
89
|
+
)
|
90
|
+
h[1, 0] = h[0, 1].conjugate()
|
91
|
+
h -= mu * np.eye(2)
|
92
|
+
|
93
|
+
return h
|
@@ -0,0 +1,130 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import numpy.typing as npt
|
3
|
+
|
4
|
+
from ._base_hamiltonian import BaseHamiltonian
|
5
|
+
|
6
|
+
|
7
|
+
def calculate_current_operator(
|
8
|
+
h: BaseHamiltonian, direction: str, k: npt.NDArray[np.float64]
|
9
|
+
) -> npt.NDArray[np.complex64]:
|
10
|
+
j = np.zeros(shape=(h.number_of_bands, h.number_of_bands), dtype=np.complex64)
|
11
|
+
|
12
|
+
_, bloch = h.diagonalize_nonint(k=k)
|
13
|
+
|
14
|
+
for m in range(h.number_of_bands):
|
15
|
+
for n in range(h.number_of_bands):
|
16
|
+
j[m, n] = (
|
17
|
+
np.conjugate(bloch[:, m])
|
18
|
+
@ h.hamiltonian_derivative(direction=direction, k=k)
|
19
|
+
@ bloch[:, n]
|
20
|
+
)
|
21
|
+
|
22
|
+
return j
|
23
|
+
|
24
|
+
|
25
|
+
def calculate_w_matrix(
|
26
|
+
h: BaseHamiltonian, k: npt.NDArray[np.float64]
|
27
|
+
) -> tuple[npt.NDArray[np.complex64], npt.NDArray[np.complex64]]:
|
28
|
+
_, bloch = h.diagonalize_nonint(k=k)
|
29
|
+
_, bdg_functions = h.diagonalize_bdg(k=k)
|
30
|
+
|
31
|
+
w_plus = np.zeros((2 * h.number_of_bands, h.number_of_bands), dtype=np.complex64)
|
32
|
+
for i in range(2 * h.number_of_bands):
|
33
|
+
for m in range(h.number_of_bands):
|
34
|
+
w_plus[i, m] = (
|
35
|
+
np.tensordot(bloch[:, m], np.array([1, 0]), axes=0).reshape(-1)
|
36
|
+
@ bdg_functions[:, i]
|
37
|
+
)
|
38
|
+
|
39
|
+
w_minus = np.zeros((2 * h.number_of_bands, h.number_of_bands), dtype=np.complex64)
|
40
|
+
for i in range(2 * h.number_of_bands):
|
41
|
+
for m in range(h.number_of_bands):
|
42
|
+
w_minus[i, m] = (
|
43
|
+
np.tensordot(
|
44
|
+
np.conjugate(bloch[:, m]), np.array([0, 1]), axes=0
|
45
|
+
).reshape(-1)
|
46
|
+
@ bdg_functions[:, i]
|
47
|
+
)
|
48
|
+
|
49
|
+
return w_plus, w_minus
|
50
|
+
|
51
|
+
|
52
|
+
def calculate_c_factor(
|
53
|
+
h: BaseHamiltonian, k: npt.NDArray[np.float64]
|
54
|
+
) -> npt.NDArray[np.complex64]:
|
55
|
+
bdg_energies, _ = h.diagonalize_bdg(k)
|
56
|
+
w_plus, w_minus = calculate_w_matrix(h, k)
|
57
|
+
C_mnpq = np.zeros(
|
58
|
+
shape=(
|
59
|
+
h.number_of_bands,
|
60
|
+
h.number_of_bands,
|
61
|
+
h.number_of_bands,
|
62
|
+
h.number_of_bands,
|
63
|
+
),
|
64
|
+
dtype=np.complex64,
|
65
|
+
)
|
66
|
+
|
67
|
+
for m in range(h.number_of_bands):
|
68
|
+
for n in range(h.number_of_bands):
|
69
|
+
for p in range(h.number_of_bands):
|
70
|
+
for q in range(h.number_of_bands):
|
71
|
+
C_tmp: float = 0
|
72
|
+
for i in range(2 * h.number_of_bands):
|
73
|
+
for j in range(2 * h.number_of_bands):
|
74
|
+
if bdg_energies[i] != bdg_energies[j]:
|
75
|
+
C_tmp += (
|
76
|
+
fermi_dirac(bdg_energies[i])
|
77
|
+
- fermi_dirac(bdg_energies[j])
|
78
|
+
) / (bdg_energies[j] - bdg_energies[i])
|
79
|
+
else:
|
80
|
+
C_tmp -= fermi_dirac_derivative()
|
81
|
+
|
82
|
+
C_tmp *= (
|
83
|
+
np.conjugate(w_minus[i, m])
|
84
|
+
* w_plus[j, n]
|
85
|
+
* np.conjugate(w_minus[j, p])
|
86
|
+
* w_minus[i, q]
|
87
|
+
)
|
88
|
+
|
89
|
+
C_mnpq[m, n, p, q] = 2 * C_tmp
|
90
|
+
|
91
|
+
return C_mnpq
|
92
|
+
|
93
|
+
|
94
|
+
def fermi_dirac_derivative() -> float:
|
95
|
+
return 0
|
96
|
+
|
97
|
+
|
98
|
+
def fermi_dirac(energy: np.float64) -> np.float64:
|
99
|
+
if energy > 0:
|
100
|
+
return np.float64(0)
|
101
|
+
else:
|
102
|
+
return np.float64(1)
|
103
|
+
|
104
|
+
|
105
|
+
def calculate_superfluid_weight(
|
106
|
+
h: BaseHamiltonian,
|
107
|
+
k_grid: npt.NDArray[np.float64],
|
108
|
+
direction_1: str,
|
109
|
+
direction_2: str,
|
110
|
+
) -> tuple[float, float]:
|
111
|
+
# number_k_points = len(k_grid)
|
112
|
+
|
113
|
+
s_weight_conv = 0
|
114
|
+
s_weight_geom = 0
|
115
|
+
|
116
|
+
for k in k_grid:
|
117
|
+
C_mnpq = calculate_c_factor(h, k)
|
118
|
+
j_up = calculate_current_operator(h, direction_1, k)
|
119
|
+
j_down = calculate_current_operator(h, direction_2, -k)
|
120
|
+
for m in range(h.number_of_bands):
|
121
|
+
for n in range(h.number_of_bands):
|
122
|
+
for p in range(h.number_of_bands):
|
123
|
+
for q in range(h.number_of_bands):
|
124
|
+
s_weight = C_mnpq[m, n, p, q] * j_up[m, n] * j_down[q, p]
|
125
|
+
if m == n and p == q:
|
126
|
+
s_weight_conv += s_weight
|
127
|
+
else:
|
128
|
+
s_weight_geom += s_weight
|
129
|
+
|
130
|
+
return s_weight_conv, s_weight_geom
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import numpy as np
|
2
|
+
|
3
|
+
|
4
|
+
def _check_valid_float(float_in: float, parameter_name: str) -> float:
|
5
|
+
if np.isinf(float_in):
|
6
|
+
raise ValueError(f"{parameter_name} must not be Infinity")
|
7
|
+
elif np.isnan(float_in):
|
8
|
+
raise ValueError(f"{parameter_name} must not be NaN")
|
9
|
+
else:
|
10
|
+
return float_in
|
@@ -1,94 +1,56 @@
|
|
1
|
+
from typing import Any, List, Tuple
|
2
|
+
|
3
|
+
import matplotlib.axes
|
4
|
+
import matplotlib.colors
|
5
|
+
import matplotlib.figure
|
1
6
|
import matplotlib.pyplot as plt
|
2
7
|
import numpy as np
|
3
8
|
import numpy.typing as npt
|
4
9
|
from matplotlib.collections import LineCollection
|
5
|
-
|
6
|
-
|
7
|
-
def plot_into_bz(
|
8
|
-
bz_corners, k_points, fig: plt.Figure | None = None, ax: plt.Axes | None = None
|
9
|
-
):
|
10
|
-
if fig is None:
|
11
|
-
fig, ax = plt.subplots()
|
12
|
-
|
13
|
-
ax.scatter(*zip(*k_points))
|
14
|
-
ax.scatter(*zip(*bz_corners), alpha=0.8)
|
15
|
-
|
16
|
-
ax.set_aspect("equal", adjustable="box")
|
17
|
-
|
18
|
-
return fig
|
10
|
+
from numpy import dtype, generic, ndarray
|
19
11
|
|
20
12
|
|
21
13
|
def scatter_into_bz(
|
22
|
-
bz_corners,
|
23
|
-
k_points,
|
24
|
-
data,
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
14
|
+
bz_corners: List[npt.NDArray[np.float64]],
|
15
|
+
k_points: npt.NDArray[np.float64],
|
16
|
+
data: npt.NDArray[np.float64] | None = None,
|
17
|
+
data_label: str | None = None,
|
18
|
+
fig_in: matplotlib.figure.Figure | None = None,
|
19
|
+
ax_in: matplotlib.axes.Axes | None = None,
|
20
|
+
) -> matplotlib.figure.Figure:
|
21
|
+
if fig_in is None or ax_in is None:
|
29
22
|
fig, ax = plt.subplots()
|
23
|
+
else:
|
24
|
+
fig, ax = fig_in, ax_in
|
30
25
|
|
31
|
-
|
32
|
-
|
33
|
-
|
26
|
+
if data is not None:
|
27
|
+
scatter = ax.scatter(*zip(*k_points), c=data, cmap="viridis")
|
28
|
+
fig.colorbar(scatter, ax=ax, fraction=0.046, pad=0.04, label=data_label)
|
29
|
+
else:
|
30
|
+
ax.scatter(*zip(*k_points))
|
34
31
|
|
32
|
+
ax.scatter(*zip(*bz_corners), alpha=0.8)
|
35
33
|
ax.set_aspect("equal", adjustable="box")
|
34
|
+
ax.set_xlabel(r"$k_x\ [1/a_0]$")
|
35
|
+
ax.set_ylabel(r"$k_y\ [1/a_0]$")
|
36
36
|
|
37
37
|
return fig
|
38
38
|
|
39
39
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
ax.axhline(y=0, alpha=0.7, linestyle="--", color="black")
|
53
|
-
|
54
|
-
for index, (band, delta) in enumerate(zip(non_interacting_bands, deltas)):
|
55
|
-
ax.plot(
|
56
|
-
k_point_list,
|
57
|
-
np.sqrt(band**2 + np.abs(delta) ** 2),
|
58
|
-
label=f"band {index}, +",
|
59
|
-
)
|
60
|
-
ax.plot(
|
61
|
-
k_point_list,
|
62
|
-
-np.sqrt(band**2 + np.abs(delta) ** 2),
|
63
|
-
label=f"band {index}, -",
|
64
|
-
)
|
65
|
-
ax.set_box_aspect(1)
|
66
|
-
|
67
|
-
ax.set_xticks(ticks, labels)
|
68
|
-
ax.set_yticks(range(-5, 6))
|
69
|
-
ax.set_facecolor("lightgray")
|
70
|
-
ax.grid(visible=True)
|
71
|
-
# ax.set_ylim([-5, 5])
|
72
|
-
ax.tick_params(
|
73
|
-
axis="both", direction="in", bottom=True, top=True, left=True, right=True
|
74
|
-
)
|
75
|
-
|
76
|
-
ax.legend()
|
77
|
-
|
78
|
-
return fig, ax
|
79
|
-
|
80
|
-
|
81
|
-
def plot_nonint_bandstructure(
|
82
|
-
bands,
|
83
|
-
k_point_list,
|
84
|
-
ticks,
|
85
|
-
labels,
|
86
|
-
overlaps: npt.NDArray | None = None,
|
87
|
-
fig: plt.Figure | None = None,
|
88
|
-
ax: plt.Axes | None = None,
|
89
|
-
):
|
90
|
-
if fig is None:
|
40
|
+
def plot_bandstructure(
|
41
|
+
bands: npt.NDArray[np.float64],
|
42
|
+
k_point_list: npt.NDArray[np.float64],
|
43
|
+
ticks: List[float],
|
44
|
+
labels: List[str],
|
45
|
+
overlaps: npt.NDArray[np.float64] | None = None,
|
46
|
+
overlap_labels: List[str] | None = None,
|
47
|
+
fig_in: matplotlib.figure.Figure | None = None,
|
48
|
+
ax_in: matplotlib.axes.Axes | None = None,
|
49
|
+
) -> matplotlib.figure.Figure:
|
50
|
+
if fig_in is None or ax_in is None:
|
91
51
|
fig, ax = plt.subplots()
|
52
|
+
else:
|
53
|
+
fig, ax = fig_in, ax_in
|
92
54
|
|
93
55
|
ax.axhline(y=0, alpha=0.7, linestyle="--", color="black")
|
94
56
|
|
@@ -96,13 +58,12 @@ def plot_nonint_bandstructure(
|
|
96
58
|
for band in bands:
|
97
59
|
ax.plot(k_point_list, band)
|
98
60
|
else:
|
99
|
-
line =
|
100
|
-
|
61
|
+
line = LineCollection(segments=[np.array([(0, 0)])])
|
101
62
|
for band, wx in zip(bands, overlaps):
|
102
63
|
points = np.array([k_point_list, band]).T.reshape(-1, 1, 2)
|
103
64
|
segments = np.concatenate([points[:-1], points[1:]], axis=1)
|
104
65
|
|
105
|
-
norm =
|
66
|
+
norm = matplotlib.colors.Normalize(-1, 1)
|
106
67
|
lc = LineCollection(segments, cmap="seismic", norm=norm)
|
107
68
|
lc.set_array(wx)
|
108
69
|
lc.set_linewidth(2)
|
@@ -110,14 +71,17 @@ def plot_nonint_bandstructure(
|
|
110
71
|
|
111
72
|
colorbar = fig.colorbar(line, fraction=0.046, pad=0.04, ax=ax)
|
112
73
|
color_ticks = [-1, 1]
|
113
|
-
colorbar.set_ticks(ticks=color_ticks, labels=
|
74
|
+
colorbar.set_ticks(ticks=color_ticks, labels=overlap_labels)
|
114
75
|
|
76
|
+
ax.set_ylim(
|
77
|
+
top=np.max(bands) + 0.1 * np.max(bands),
|
78
|
+
bottom=np.min(bands) - 0.1 * np.abs(np.min(bands)),
|
79
|
+
)
|
115
80
|
ax.set_box_aspect(1)
|
116
81
|
ax.set_xticks(ticks, labels)
|
117
|
-
ax.
|
82
|
+
ax.set_ylabel(r"$E\ [t]$")
|
118
83
|
ax.set_facecolor("lightgray")
|
119
84
|
ax.grid(visible=True)
|
120
|
-
# ax.set_ylim([-5, 5])
|
121
85
|
ax.tick_params(
|
122
86
|
axis="both", direction="in", bottom=True, top=True, left=True, right=True
|
123
87
|
)
|
@@ -125,7 +89,12 @@ def plot_nonint_bandstructure(
|
|
125
89
|
return fig
|
126
90
|
|
127
91
|
|
128
|
-
def _generate_part_of_path(
|
92
|
+
def _generate_part_of_path(
|
93
|
+
p_0: npt.NDArray[np.float64],
|
94
|
+
p_1: npt.NDArray[np.float64],
|
95
|
+
n: int,
|
96
|
+
length_whole_path: int,
|
97
|
+
) -> npt.NDArray[np.float64]:
|
129
98
|
distance = np.linalg.norm(p_1 - p_0)
|
130
99
|
number_of_points = int(n * distance / length_whole_path) + 1
|
131
100
|
|
@@ -139,7 +108,14 @@ def _generate_part_of_path(p_0, p_1, n, length_whole_path):
|
|
139
108
|
return k_space_path
|
140
109
|
|
141
110
|
|
142
|
-
def generate_bz_path(
|
111
|
+
def generate_bz_path(
|
112
|
+
points: List[Tuple[npt.NDArray[np.float64], str]], number_of_points: int = 1000
|
113
|
+
) -> tuple[
|
114
|
+
ndarray[Any, dtype[generic | generic | Any]],
|
115
|
+
ndarray[Any, dtype[generic | generic | Any]],
|
116
|
+
list[int | Any],
|
117
|
+
list[str],
|
118
|
+
]:
|
143
119
|
n = number_of_points
|
144
120
|
|
145
121
|
cycle = [
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import numpy.typing as npt
|
3
|
+
|
4
|
+
|
5
|
+
def generate_uniform_grid(
|
6
|
+
ncols: int,
|
7
|
+
nrows: int,
|
8
|
+
corner_1: npt.NDArray[np.float64],
|
9
|
+
corner_2: npt.NDArray[np.float64],
|
10
|
+
origin: npt.NDArray[np.float64],
|
11
|
+
) -> npt.NDArray[np.float64]:
|
12
|
+
if ncols <= 1 or nrows <= 1:
|
13
|
+
raise ValueError("Number of columns and rows must be greater than 1.")
|
14
|
+
if np.linalg.norm(corner_1) == 0 or np.linalg.norm(corner_2) == 0:
|
15
|
+
raise ValueError("Vectors to the corners cannot be zero.")
|
16
|
+
|
17
|
+
grid: npt.NDArray[np.float64] = np.concatenate(
|
18
|
+
[
|
19
|
+
np.linspace(
|
20
|
+
origin[0] + i / (nrows - 1) * corner_2,
|
21
|
+
origin[1] + corner_1 + i / (nrows - 1) * corner_2,
|
22
|
+
num=ncols,
|
23
|
+
)
|
24
|
+
for i in range(nrows)
|
25
|
+
]
|
26
|
+
)
|
27
|
+
|
28
|
+
return grid
|
File without changes
|
@@ -1,65 +0,0 @@
|
|
1
|
-
import numpy as np
|
2
|
-
import numpy.typing as npt
|
3
|
-
from scipy import interpolate, optimize
|
4
|
-
|
5
|
-
from quant_met.bcs.gap_equation import gap_equation_real
|
6
|
-
from quant_met.configuration import DeltaVector
|
7
|
-
from quant_met.hamiltonians import BaseHamiltonian
|
8
|
-
|
9
|
-
|
10
|
-
def generate_k_space_grid(nx, nrows, corner_1, corner_2):
|
11
|
-
k_points = np.concatenate(
|
12
|
-
[
|
13
|
-
np.linspace(
|
14
|
-
i / (nrows - 1) * corner_2,
|
15
|
-
corner_1 + i / (nrows - 1) * corner_2,
|
16
|
-
num=nx,
|
17
|
-
)
|
18
|
-
for i in range(nrows)
|
19
|
-
]
|
20
|
-
)
|
21
|
-
|
22
|
-
return k_points
|
23
|
-
|
24
|
-
|
25
|
-
def solve_gap_equation(
|
26
|
-
hamiltonian: BaseHamiltonian, k_points: npt.NDArray, beta: float = 0
|
27
|
-
) -> DeltaVector:
|
28
|
-
energies, bloch_absolute = hamiltonian.generate_bloch(k_points=k_points)
|
29
|
-
|
30
|
-
delta_vector = DeltaVector(
|
31
|
-
k_points=k_points, initial=0.1, number_bands=hamiltonian.number_bands
|
32
|
-
)
|
33
|
-
try:
|
34
|
-
solution = optimize.fixed_point(
|
35
|
-
gap_equation_real,
|
36
|
-
delta_vector.as_1d_vector,
|
37
|
-
args=(hamiltonian.U, beta, bloch_absolute, energies, len(k_points)),
|
38
|
-
)
|
39
|
-
except RuntimeError:
|
40
|
-
print("Failed")
|
41
|
-
solution = DeltaVector(
|
42
|
-
k_points=k_points, initial=0.0, number_bands=hamiltonian.number_bands
|
43
|
-
).as_1d_vector
|
44
|
-
|
45
|
-
delta_vector.update_from_1d_vector(solution)
|
46
|
-
|
47
|
-
return delta_vector
|
48
|
-
|
49
|
-
|
50
|
-
def interpolate_gap(
|
51
|
-
delta_vector_on_grid: DeltaVector, bandpath: npt.NDArray
|
52
|
-
) -> DeltaVector:
|
53
|
-
delta_vector_interpolated = DeltaVector(
|
54
|
-
k_points=bandpath, number_bands=delta_vector_on_grid.number_bands
|
55
|
-
)
|
56
|
-
|
57
|
-
for band in range(delta_vector_interpolated.number_bands):
|
58
|
-
delta_vector_interpolated.data.loc[:, f"delta_{band}"] = interpolate.griddata(
|
59
|
-
delta_vector_on_grid.k_points,
|
60
|
-
delta_vector_on_grid.data.loc[:, f"delta_{band}"],
|
61
|
-
bandpath,
|
62
|
-
method="cubic",
|
63
|
-
)
|
64
|
-
|
65
|
-
return delta_vector_interpolated
|
@@ -1,43 +0,0 @@
|
|
1
|
-
import numpy as np
|
2
|
-
import numpy.typing as npt
|
3
|
-
|
4
|
-
|
5
|
-
def gap_equation_real(
|
6
|
-
delta_k: npt.NDArray,
|
7
|
-
U: npt.NDArray,
|
8
|
-
beta: float,
|
9
|
-
bloch_absolute: npt.NDArray,
|
10
|
-
energies: npt.NDArray,
|
11
|
-
number_k_points: int,
|
12
|
-
):
|
13
|
-
return_vector = np.zeros(len(delta_k))
|
14
|
-
|
15
|
-
number_bands = int(len(return_vector) / number_k_points)
|
16
|
-
|
17
|
-
for n in range(number_bands):
|
18
|
-
offset_n = int(len(delta_k) / number_bands * n)
|
19
|
-
for k_prime_index in range(0, number_k_points):
|
20
|
-
sum_tmp = 0
|
21
|
-
for alpha in range(number_bands):
|
22
|
-
for m in range(number_bands):
|
23
|
-
offset_m = int(len(delta_k) / number_bands * m)
|
24
|
-
for k_index in range(0, number_k_points):
|
25
|
-
sum_tmp += (
|
26
|
-
U[alpha]
|
27
|
-
* bloch_absolute[k_prime_index][alpha][n]
|
28
|
-
* bloch_absolute[k_index][alpha][m]
|
29
|
-
* delta_k[k_index + offset_m]
|
30
|
-
/ (
|
31
|
-
2
|
32
|
-
* np.sqrt(
|
33
|
-
(energies[k_index][m]) ** 2
|
34
|
-
+ np.abs(delta_k[k_index + offset_m]) ** 2
|
35
|
-
)
|
36
|
-
)
|
37
|
-
)
|
38
|
-
|
39
|
-
return_vector[k_prime_index + offset_n] = sum_tmp / (
|
40
|
-
2.5980762113533156 * number_k_points
|
41
|
-
)
|
42
|
-
|
43
|
-
return return_vector
|
@@ -1,53 +0,0 @@
|
|
1
|
-
import numpy as np
|
2
|
-
import numpy.typing as npt
|
3
|
-
import pandas as pd
|
4
|
-
|
5
|
-
|
6
|
-
class DeltaVector:
|
7
|
-
def __init__(
|
8
|
-
self,
|
9
|
-
number_bands: int,
|
10
|
-
hdf_file=None,
|
11
|
-
k_points: npt.NDArray | None = None,
|
12
|
-
initial: float | None = None,
|
13
|
-
):
|
14
|
-
self.number_bands = number_bands
|
15
|
-
if hdf_file is not None:
|
16
|
-
pass
|
17
|
-
# self.data = pd.DataFrame(pd.read_hdf(hdf_file, key="table"))
|
18
|
-
# self.k_points = np.column_stack(
|
19
|
-
# (np.array(self.data.loc[:, "kx"]), np.array(self.data.loc[:, "ky"]))
|
20
|
-
# )
|
21
|
-
else:
|
22
|
-
self.k_points = k_points
|
23
|
-
self.data = pd.DataFrame(
|
24
|
-
# columns=["kx", "ky", "delta_1", "delta_2", "delta_3"],
|
25
|
-
index=range(len(k_points)),
|
26
|
-
dtype=np.float64,
|
27
|
-
)
|
28
|
-
self.data.loc[:, "kx"] = self.k_points[:, 0]
|
29
|
-
self.data.loc[:, "ky"] = self.k_points[:, 1]
|
30
|
-
if initial is not None:
|
31
|
-
for i in range(number_bands):
|
32
|
-
self.data.loc[:, f"delta_{i}"] = initial
|
33
|
-
|
34
|
-
def __repr__(self):
|
35
|
-
return self.data.to_string(index=False)
|
36
|
-
|
37
|
-
def update_from_1d_vector(self, delta: npt.NDArray):
|
38
|
-
for n in range(self.number_bands):
|
39
|
-
offset = int(n * len(delta) / self.number_bands)
|
40
|
-
self.data.loc[:, f"delta_{n}"] = delta[offset : offset + len(self.k_points)]
|
41
|
-
|
42
|
-
def save(self, path):
|
43
|
-
pass
|
44
|
-
# self.data.to_hdf(path, key="table", format="table", data_columns=True)
|
45
|
-
|
46
|
-
@property
|
47
|
-
def as_1d_vector(self) -> npt.NDArray:
|
48
|
-
return np.concatenate(
|
49
|
-
[
|
50
|
-
np.array(self.data.loc[:, f"delta_{n}"].values)
|
51
|
-
for n in range(self.number_bands)
|
52
|
-
]
|
53
|
-
)
|
@@ -1,174 +0,0 @@
|
|
1
|
-
from abc import ABC, abstractmethod
|
2
|
-
|
3
|
-
import numpy as np
|
4
|
-
import numpy.typing as npt
|
5
|
-
import pandas as pd
|
6
|
-
|
7
|
-
|
8
|
-
class BaseHamiltonian(ABC):
|
9
|
-
@property
|
10
|
-
@abstractmethod
|
11
|
-
def number_bands(self) -> int:
|
12
|
-
raise NotImplementedError
|
13
|
-
|
14
|
-
@property
|
15
|
-
@abstractmethod
|
16
|
-
def U(self) -> list[float]:
|
17
|
-
raise NotImplementedError
|
18
|
-
|
19
|
-
@abstractmethod
|
20
|
-
def _k_space_matrix_one_point(self, k: npt.NDArray, h: npt.NDArray) -> npt.NDArray:
|
21
|
-
raise NotImplementedError
|
22
|
-
|
23
|
-
def k_space_matrix(self, k: npt.NDArray) -> npt.NDArray:
|
24
|
-
if k.ndim == 1:
|
25
|
-
h = np.zeros((1, self.number_bands, self.number_bands), dtype=complex)
|
26
|
-
h[0] = self._k_space_matrix_one_point(k, h[0])
|
27
|
-
else:
|
28
|
-
h = np.zeros(
|
29
|
-
(k.shape[0], self.number_bands, self.number_bands), dtype=complex
|
30
|
-
)
|
31
|
-
for k_index, k in enumerate(k):
|
32
|
-
h[k_index] = self._k_space_matrix_one_point(k, h[k_index])
|
33
|
-
return h
|
34
|
-
|
35
|
-
def calculate_bandstructure(self, k_point_list: npt.NDArray):
|
36
|
-
k_point_matrix = self.k_space_matrix(k_point_list)
|
37
|
-
|
38
|
-
results = pd.DataFrame(
|
39
|
-
index=range(len(k_point_list)),
|
40
|
-
dtype=float,
|
41
|
-
)
|
42
|
-
|
43
|
-
for i, k in enumerate(k_point_list):
|
44
|
-
energies, eigenvectors = np.linalg.eigh(k_point_matrix[i])
|
45
|
-
|
46
|
-
for band_index in range(self.number_bands):
|
47
|
-
results.at[i, f"band_{band_index}"] = energies[band_index]
|
48
|
-
|
49
|
-
return results
|
50
|
-
|
51
|
-
def generate_bloch(self, k_points: npt.NDArray):
|
52
|
-
k_point_matrix = self.k_space_matrix(k_points)
|
53
|
-
|
54
|
-
if k_points.ndim == 1:
|
55
|
-
energies, bloch = np.linalg.eigh(k_point_matrix[0])
|
56
|
-
else:
|
57
|
-
bloch = np.zeros(
|
58
|
-
(len(k_points), self.number_bands, self.number_bands), dtype=complex
|
59
|
-
)
|
60
|
-
energies = np.zeros((len(k_points), self.number_bands))
|
61
|
-
|
62
|
-
for i, k in enumerate(k_points):
|
63
|
-
energies[i], bloch[i] = np.linalg.eigh(k_point_matrix[i])
|
64
|
-
|
65
|
-
return energies, bloch
|
66
|
-
|
67
|
-
|
68
|
-
class GrapheneHamiltonian(BaseHamiltonian):
|
69
|
-
def __init__(self, t_nn: float, a: float, mu: float, U_gr: float):
|
70
|
-
self.t_nn = t_nn
|
71
|
-
self.a = a
|
72
|
-
self.mu = mu
|
73
|
-
self.U_gr = U_gr
|
74
|
-
|
75
|
-
@property
|
76
|
-
def U(self) -> list[float]:
|
77
|
-
return [self.U_gr, self.U_gr]
|
78
|
-
|
79
|
-
@property
|
80
|
-
def number_bands(self) -> int:
|
81
|
-
return 2
|
82
|
-
|
83
|
-
def _k_space_matrix_one_point(self, k: npt.NDArray, h: npt.NDArray) -> npt.NDArray:
|
84
|
-
t_nn = self.t_nn
|
85
|
-
a = self.a
|
86
|
-
mu = self.mu
|
87
|
-
|
88
|
-
h[0, 1] = t_nn * (
|
89
|
-
np.exp(1j * k[1] * a / np.sqrt(3))
|
90
|
-
+ 2 * np.exp(-0.5j * a / np.sqrt(3) * k[1]) * (np.cos(0.5 * a * k[0]))
|
91
|
-
)
|
92
|
-
|
93
|
-
h[1, 0] = h[0, 1].conjugate()
|
94
|
-
h = h - mu * np.eye(2)
|
95
|
-
return h
|
96
|
-
|
97
|
-
|
98
|
-
class EGXHamiltonian(BaseHamiltonian):
|
99
|
-
def __init__(
|
100
|
-
self,
|
101
|
-
t_gr: float,
|
102
|
-
t_x: float,
|
103
|
-
V: float,
|
104
|
-
a: float,
|
105
|
-
mu: float,
|
106
|
-
U_gr: float,
|
107
|
-
U_x: float,
|
108
|
-
):
|
109
|
-
self.t_gr = t_gr
|
110
|
-
self.t_x = t_x
|
111
|
-
self.V = V
|
112
|
-
self.a = a
|
113
|
-
self.mu = mu
|
114
|
-
self.U_gr = U_gr
|
115
|
-
self.U_x = U_x
|
116
|
-
|
117
|
-
@property
|
118
|
-
def U(self) -> list[float]:
|
119
|
-
return [self.U_gr, self.U_gr, self.U_x]
|
120
|
-
|
121
|
-
@property
|
122
|
-
def number_bands(self) -> int:
|
123
|
-
return 3
|
124
|
-
|
125
|
-
def _k_space_matrix_one_point(self, k: npt.NDArray, h: npt.NDArray) -> npt.NDArray:
|
126
|
-
t_gr = self.t_gr
|
127
|
-
t_x = self.t_x
|
128
|
-
a = self.a
|
129
|
-
a_0 = a / np.sqrt(3)
|
130
|
-
V = self.V
|
131
|
-
mu = self.mu
|
132
|
-
|
133
|
-
h[0, 1] = t_gr * (
|
134
|
-
np.exp(1j * k[1] * a / np.sqrt(3))
|
135
|
-
+ 2 * np.exp(-0.5j * a / np.sqrt(3) * k[1]) * (np.cos(0.5 * a * k[0]))
|
136
|
-
)
|
137
|
-
|
138
|
-
h[1, 0] = h[0, 1].conjugate()
|
139
|
-
|
140
|
-
h[2, 0] = V
|
141
|
-
h[0, 2] = V
|
142
|
-
|
143
|
-
h[2, 2] = (
|
144
|
-
-2
|
145
|
-
* t_x
|
146
|
-
* (
|
147
|
-
np.cos(a * k[0])
|
148
|
-
+ 2 * np.cos(0.5 * a * k[0]) * np.cos(0.5 * np.sqrt(3) * a * k[1])
|
149
|
-
)
|
150
|
-
)
|
151
|
-
h = h - mu * np.eye(3)
|
152
|
-
return h
|
153
|
-
|
154
|
-
def calculate_bandstructure(self, k_point_list: npt.NDArray):
|
155
|
-
k_point_matrix = self.k_space_matrix(k_point_list)
|
156
|
-
|
157
|
-
results = pd.DataFrame(
|
158
|
-
index=range(len(k_point_list)),
|
159
|
-
dtype=float,
|
160
|
-
)
|
161
|
-
|
162
|
-
for i, k in enumerate(k_point_list):
|
163
|
-
energies, eigenvectors = np.linalg.eigh(k_point_matrix[i])
|
164
|
-
|
165
|
-
for band_index in range(self.number_bands):
|
166
|
-
results.at[i, f"band_{band_index}"] = energies[band_index]
|
167
|
-
results.at[i, f"wx_{band_index}"] = (
|
168
|
-
np.abs(np.dot(eigenvectors[:, band_index], np.array([0, 0, 1])))
|
169
|
-
** 2
|
170
|
-
- np.abs(np.dot(eigenvectors[:, band_index], np.array([1, 0, 0])))
|
171
|
-
** 2
|
172
|
-
)
|
173
|
-
|
174
|
-
return results
|
File without changes
|