kdotpy 1.0.0rc3__py3-none-any.whl

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.
Files changed (124) hide show
  1. kdotpy/__init__.py +45 -0
  2. kdotpy/__main__.py +43 -0
  3. kdotpy/bandalign/__init__.py +42 -0
  4. kdotpy/bandalign/adiabatic.py +238 -0
  5. kdotpy/bandalign/bandalign.py +310 -0
  6. kdotpy/bandalign/base.py +845 -0
  7. kdotpy/bandalign/csvi.py +255 -0
  8. kdotpy/bandtools.py +666 -0
  9. kdotpy/batchtools.py +429 -0
  10. kdotpy/berry.py +446 -0
  11. kdotpy/bhz.py +734 -0
  12. kdotpy/bhzprint.py +873 -0
  13. kdotpy/cmdargs/__init__.py +47 -0
  14. kdotpy/cmdargs/cmdargs.py +2184 -0
  15. kdotpy/cmdargs/range.py +206 -0
  16. kdotpy/cmdargs/tools.py +395 -0
  17. kdotpy/cnp.py +418 -0
  18. kdotpy/config.py +787 -0
  19. kdotpy/density/__init__.py +56 -0
  20. kdotpy/density/base.py +715 -0
  21. kdotpy/density/broadening.py +873 -0
  22. kdotpy/density/density.py +392 -0
  23. kdotpy/density/densitydata.py +822 -0
  24. kdotpy/density/densityscale.py +187 -0
  25. kdotpy/density/densityz.py +373 -0
  26. kdotpy/density/elements.py +463 -0
  27. kdotpy/density/intobs.py +159 -0
  28. kdotpy/diagonalization/__init__.py +41 -0
  29. kdotpy/diagonalization/diagdata.py +2870 -0
  30. kdotpy/diagonalization/diagonalization.py +1150 -0
  31. kdotpy/diagonalization/diagsolver.py +783 -0
  32. kdotpy/diagonalization/feastsolver.py +193 -0
  33. kdotpy/diagonalization/lldiagonalization.py +235 -0
  34. kdotpy/diagonalization/stitch.py +144 -0
  35. kdotpy/doc.py +104 -0
  36. kdotpy/docs/helpfile.txt +2321 -0
  37. kdotpy/erange.py +166 -0
  38. kdotpy/errorhandling.py +78 -0
  39. kdotpy/etransform.py +210 -0
  40. kdotpy/extrema.py +1600 -0
  41. kdotpy/hamiltonian/__init__.py +55 -0
  42. kdotpy/hamiltonian/blocks.py +2035 -0
  43. kdotpy/hamiltonian/full.py +222 -0
  44. kdotpy/hamiltonian/hamiltonian.py +806 -0
  45. kdotpy/hamiltonian/parity.py +200 -0
  46. kdotpy/hamiltonian/tools.py +103 -0
  47. kdotpy/hamiltonian/transform.py +659 -0
  48. kdotpy/hamiltonian/transformable.py +133 -0
  49. kdotpy/hdf5o.py +102 -0
  50. kdotpy/integrate.py +135 -0
  51. kdotpy/intervaltools.py +118 -0
  52. kdotpy/kdotpy-1d.py +268 -0
  53. kdotpy/kdotpy-2d.py +373 -0
  54. kdotpy/kdotpy-batch.py +85 -0
  55. kdotpy/kdotpy-bulk-ll.py +211 -0
  56. kdotpy/kdotpy-bulk.py +217 -0
  57. kdotpy/kdotpy-compare.py +324 -0
  58. kdotpy/kdotpy-config.py +127 -0
  59. kdotpy/kdotpy-ll.py +406 -0
  60. kdotpy/kdotpy-merge.py +279 -0
  61. kdotpy/kdotpy-test.py +410 -0
  62. kdotpy/latticetrans.py +340 -0
  63. kdotpy/layerstack.py +589 -0
  64. kdotpy/lltools.py +153 -0
  65. kdotpy/main.py +105 -0
  66. kdotpy/materials/__init__.py +43 -0
  67. kdotpy/materials/base.py +440 -0
  68. kdotpy/materials/default +176 -0
  69. kdotpy/materials/initial.py +90 -0
  70. kdotpy/materials/materials.py +1086 -0
  71. kdotpy/models.py +708 -0
  72. kdotpy/momentum.py +3148 -0
  73. kdotpy/observables.py +1615 -0
  74. kdotpy/parallel.py +586 -0
  75. kdotpy/physconst.py +70 -0
  76. kdotpy/physparams.py +724 -0
  77. kdotpy/phystext.py +319 -0
  78. kdotpy/ploto/__init__.py +63 -0
  79. kdotpy/ploto/auxil.py +588 -0
  80. kdotpy/ploto/colormaps.py +105 -0
  81. kdotpy/ploto/colortools.py +886 -0
  82. kdotpy/ploto/disp.py +934 -0
  83. kdotpy/ploto/dos.py +1256 -0
  84. kdotpy/ploto/kdotpy.mplstyle +14 -0
  85. kdotpy/ploto/tools.py +1185 -0
  86. kdotpy/ploto/toolslegend.py +478 -0
  87. kdotpy/ploto/toolstext.py +637 -0
  88. kdotpy/ploto/toolsticks.py +265 -0
  89. kdotpy/ploto/wf.py +1246 -0
  90. kdotpy/postprocess.py +1567 -0
  91. kdotpy/potential.py +1057 -0
  92. kdotpy/potentialbc.py +317 -0
  93. kdotpy/resumetools.py +95 -0
  94. kdotpy/selfcon.py +1676 -0
  95. kdotpy/spinmat.py +167 -0
  96. kdotpy/strain.py +176 -0
  97. kdotpy/symbolic.py +1506 -0
  98. kdotpy/symmetry.py +280 -0
  99. kdotpy/tableo/__init__.py +61 -0
  100. kdotpy/tableo/auxil.py +419 -0
  101. kdotpy/tableo/disp.py +645 -0
  102. kdotpy/tableo/dos.py +345 -0
  103. kdotpy/tableo/postwrite.py +252 -0
  104. kdotpy/tableo/read.py +218 -0
  105. kdotpy/tableo/simple.py +146 -0
  106. kdotpy/tableo/tools.py +347 -0
  107. kdotpy/tableo/wf.py +514 -0
  108. kdotpy/tableo/write.py +322 -0
  109. kdotpy/tasks.py +303 -0
  110. kdotpy/testselfcon.py +145 -0
  111. kdotpy/transitions.py +977 -0
  112. kdotpy/types.py +410 -0
  113. kdotpy/version.py +61 -0
  114. kdotpy/wf.py +271 -0
  115. kdotpy/xmlio/__init__.py +45 -0
  116. kdotpy/xmlio/read.py +710 -0
  117. kdotpy/xmlio/tools.py +253 -0
  118. kdotpy/xmlio/write.py +837 -0
  119. kdotpy-1.0.0rc3.dist-info/LICENSE +675 -0
  120. kdotpy-1.0.0rc3.dist-info/LICENSE.additional +22 -0
  121. kdotpy-1.0.0rc3.dist-info/METADATA +199 -0
  122. kdotpy-1.0.0rc3.dist-info/RECORD +124 -0
  123. kdotpy-1.0.0rc3.dist-info/WHEEL +4 -0
  124. kdotpy-1.0.0rc3.dist-info/entry_points.txt +3 -0
kdotpy/__init__.py ADDED
@@ -0,0 +1,45 @@
1
+ # kdotpy - k·p theory on a lattice for simulating semiconductor band structures
2
+ # Copyright (C) 2024 The kdotpy collaboration <kdotpy@uni-wuerzburg.de>
3
+ #
4
+ # SPDX-License-Identifier: GPL-3.0-only
5
+ #
6
+ # This file is part of kdotpy.
7
+ #
8
+ # kdotpy is free software: you can redistribute it and/or modify it under the
9
+ # terms of the GNU General Public License as published by the Free Software
10
+ # Foundation, version 3.
11
+ #
12
+ # kdotpy is distributed in the hope that it will be useful, but WITHOUT ANY
13
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
+ # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along with
17
+ # kdotpy. If not, see <https://www.gnu.org/licenses/>.
18
+ #
19
+ # Under Section 7 of GPL version 3 we require you to fulfill the following
20
+ # additional terms:
21
+ #
22
+ # - We require the preservation of the full copyright notice and the license
23
+ # in all original files.
24
+ #
25
+ # - We prohibit misrepresentation of the origin of the original files. To
26
+ # obtain the original files, please visit the Git repository at
27
+ # <https://git.physik.uni-wuerzburg.de/kdotpy/kdotpy>
28
+ #
29
+ # - As part of a scientific environment, we believe it is reasonable to
30
+ # expect that you follow the rules of good scientific practice when using
31
+ # kdotpy. In particular, we expect that you credit the original authors if
32
+ # you benefit from this program, by citing our work, following the
33
+ # citation instructions in the file CITATION.md bundled with kdotpy.
34
+ #
35
+ # - If you make substantial changes to kdotpy, we strongly encourage that
36
+ # you contribute to the original project by joining our team. If you use
37
+ # or publish a modified version of this program, you are required to mark
38
+ # your material in a reasonable way as different from the original
39
+ # version.
40
+
41
+ from .main import main
42
+ from .version import get_version
43
+
44
+ __version__ = get_version()
45
+
kdotpy/__main__.py ADDED
@@ -0,0 +1,43 @@
1
+ # kdotpy - k·p theory on a lattice for simulating semiconductor band structures
2
+ # Copyright (C) 2024 The kdotpy collaboration <kdotpy@uni-wuerzburg.de>
3
+ #
4
+ # SPDX-License-Identifier: GPL-3.0-only
5
+ #
6
+ # This file is part of kdotpy.
7
+ #
8
+ # kdotpy is free software: you can redistribute it and/or modify it under the
9
+ # terms of the GNU General Public License as published by the Free Software
10
+ # Foundation, version 3.
11
+ #
12
+ # kdotpy is distributed in the hope that it will be useful, but WITHOUT ANY
13
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
+ # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along with
17
+ # kdotpy. If not, see <https://www.gnu.org/licenses/>.
18
+ #
19
+ # Under Section 7 of GPL version 3 we require you to fulfill the following
20
+ # additional terms:
21
+ #
22
+ # - We require the preservation of the full copyright notice and the license
23
+ # in all original files.
24
+ #
25
+ # - We prohibit misrepresentation of the origin of the original files. To
26
+ # obtain the original files, please visit the Git repository at
27
+ # <https://git.physik.uni-wuerzburg.de/kdotpy/kdotpy>
28
+ #
29
+ # - As part of a scientific environment, we believe it is reasonable to
30
+ # expect that you follow the rules of good scientific practice when using
31
+ # kdotpy. In particular, we expect that you credit the original authors if
32
+ # you benefit from this program, by citing our work, following the
33
+ # citation instructions in the file CITATION.md bundled with kdotpy.
34
+ #
35
+ # - If you make substantial changes to kdotpy, we strongly encourage that
36
+ # you contribute to the original project by joining our team. If you use
37
+ # or publish a modified version of this program, you are required to mark
38
+ # your material in a reasonable way as different from the original
39
+ # version.
40
+
41
+ from . import main
42
+
43
+ main()
@@ -0,0 +1,42 @@
1
+ # kdotpy - k·p theory on a lattice for simulating semiconductor band structures
2
+ # Copyright (C) 2024 The kdotpy collaboration <kdotpy@uni-wuerzburg.de>
3
+ #
4
+ # SPDX-License-Identifier: GPL-3.0-only
5
+ #
6
+ # This file is part of kdotpy.
7
+ #
8
+ # kdotpy is free software: you can redistribute it and/or modify it under the
9
+ # terms of the GNU General Public License as published by the Free Software
10
+ # Foundation, version 3.
11
+ #
12
+ # kdotpy is distributed in the hope that it will be useful, but WITHOUT ANY
13
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
+ # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along with
17
+ # kdotpy. If not, see <https://www.gnu.org/licenses/>.
18
+ #
19
+ # Under Section 7 of GPL version 3 we require you to fulfill the following
20
+ # additional terms:
21
+ #
22
+ # - We require the preservation of the full copyright notice and the license
23
+ # in all original files.
24
+ #
25
+ # - We prohibit misrepresentation of the origin of the original files. To
26
+ # obtain the original files, please visit the Git repository at
27
+ # <https://git.physik.uni-wuerzburg.de/kdotpy/kdotpy>
28
+ #
29
+ # - As part of a scientific environment, we believe it is reasonable to
30
+ # expect that you follow the rules of good scientific practice when using
31
+ # kdotpy. In particular, we expect that you credit the original authors if
32
+ # you benefit from this program, by citing our work, following the
33
+ # citation instructions in the file CITATION.md bundled with kdotpy.
34
+ #
35
+ # - If you make substantial changes to kdotpy, we strongly encourage that
36
+ # you contribute to the original project by joining our team. If you use
37
+ # or publish a modified version of this program, you are required to mark
38
+ # your material in a reasonable way as different from the original
39
+ # version.
40
+
41
+ from .bandalign import bandindices
42
+ from .adiabatic import bandindices_adiabatic, bandindices_adiabatic_ll
@@ -0,0 +1,238 @@
1
+ # kdotpy - k·p theory on a lattice for simulating semiconductor band structures
2
+ # Copyright (C) 2024 The kdotpy collaboration <kdotpy@uni-wuerzburg.de>
3
+ #
4
+ # SPDX-License-Identifier: GPL-3.0-only
5
+ #
6
+ # This file is part of kdotpy.
7
+ #
8
+ # kdotpy is free software: you can redistribute it and/or modify it under the
9
+ # terms of the GNU General Public License as published by the Free Software
10
+ # Foundation, version 3.
11
+ #
12
+ # kdotpy is distributed in the hope that it will be useful, but WITHOUT ANY
13
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
+ # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along with
17
+ # kdotpy. If not, see <https://www.gnu.org/licenses/>.
18
+ #
19
+ # Under Section 7 of GPL version 3 we require you to fulfill the following
20
+ # additional terms:
21
+ #
22
+ # - We require the preservation of the full copyright notice and the license
23
+ # in all original files.
24
+ #
25
+ # - We prohibit misrepresentation of the origin of the original files. To
26
+ # obtain the original files, please visit the Git repository at
27
+ # <https://git.physik.uni-wuerzburg.de/kdotpy/kdotpy>
28
+ #
29
+ # - As part of a scientific environment, we believe it is reasonable to
30
+ # expect that you follow the rules of good scientific practice when using
31
+ # kdotpy. In particular, we expect that you credit the original authors if
32
+ # you benefit from this program, by citing our work, following the
33
+ # citation instructions in the file CITATION.md bundled with kdotpy.
34
+ #
35
+ # - If you make substantial changes to kdotpy, we strongly encourage that
36
+ # you contribute to the original project by joining our team. If you use
37
+ # or publish a modified version of this program, you are required to mark
38
+ # your material in a reasonable way as different from the original
39
+ # version.
40
+
41
+ import numpy as np
42
+ import sys
43
+
44
+ from ..config import get_config_bool
45
+ from ..cnp import estimate_charge_neutrality_point
46
+ from ..diagonalization import DiagData
47
+ from ..diagonalization import diagonalization as diag
48
+ from ..diagonalization import lldiagonalization as lldiag
49
+ from ..momentum import Vector, VectorGrid
50
+ from ..parallel import parallel_apply
51
+
52
+ from .bandalign import bandindices
53
+
54
+ ### ADIABATIC BAND INDEX INITIALIZATION
55
+ def diag_hz_pot(alpha, params, pot = None, **modelopts):
56
+ """Thin wrapper around diag.hz_k0"""
57
+ alpha_val = alpha.value[0] if isinstance(alpha, Vector) else alpha
58
+ if pot is None:
59
+ raise TypeError("Argument pot must be an array")
60
+ ddp = diag.hz_k0(
61
+ params, pot = alpha_val * pot, bandtype_warning_level = 0, **modelopts)
62
+ ddp.paramval = alpha if isinstance(alpha, Vector) else Vector(alpha)
63
+ return ddp
64
+
65
+ def diag_hz_pot_ll(alpha, ll_mode, ll_max, h_sym, params, pot = None, **modelopts):
66
+ """Thin wrapper around lldiag.hll_k0"""
67
+ alpha_val = alpha.value[0] if isinstance(alpha, Vector) else alpha
68
+ if pot is None:
69
+ raise TypeError("Argument pot must be an array")
70
+ ddp = lldiag.hll_k0(
71
+ ll_mode, ll_max, h_sym, params, pot = alpha_val * pot,
72
+ bandtype_warning_level = 0, modelopts = modelopts)
73
+ ddp.paramval = alpha if isinstance(alpha, Vector) else Vector(alpha)
74
+ return ddp
75
+
76
+ def fmt_eival(x):
77
+ """String formatting function for bandindices_automatic_debug()"""
78
+ return "" if x is None else ("%.3f" % x)
79
+
80
+ def bandindices_adiabatic_debug(filename, diagdata, alphas, b_max=6):
81
+ """Write bandindices_adiabatic(_ll) debug output to a file.
82
+
83
+ Arguments:
84
+ filename String. The filename of the output file
85
+ diagdata DiagData instance. Generated by bandindices_adiabatic(_ll).
86
+ alphas Numpy array. Values between 0 and 1.
87
+ b_max Positive integer. Only include band indices from -b_max to b_max.
88
+ """
89
+ b_idx = [b for b in range(-b_max, b_max + 1) if b != 0]
90
+ with open(filename, "w") as f:
91
+ f.write("alpha," + ",".join("%i" % b for b in b_idx) + "\n")
92
+ for j, alpha in enumerate(alphas):
93
+ f.write("%g," % alpha)
94
+ f.write(",".join([fmt_eival(diagdata[j].get_eival((b,))) for b in b_idx]) + "\n")
95
+
96
+ def bandindices_adiabatic_verbose(diagdata, alphas):
97
+ """Verbose output for bandindices_adiabatic(_ll) to stdout"""
98
+ for j, alpha in enumerate(alphas):
99
+ e_m1 = diagdata[j].get_eival((-1,))
100
+ e_p1 = diagdata[j].get_eival((1,))
101
+ c_m1 = diagdata[j].get_char((-1,))
102
+ c_p1 = diagdata[j].get_char((1,))
103
+ print("alpha = %g: CNP between %s (%.2f meV) and %s (%.2f meV)" % (alpha, c_m1, e_m1, c_p1, e_p1))
104
+
105
+ def bandindices_adiabatic(
106
+ params, steps = 10, pot = None, num_cpus = 1, modelopts = None,
107
+ bandalign_opts = None):
108
+ """Do band alignment over slowly increasing potential, starting at zero potential
109
+
110
+ Since an electrostatic potential can affect the band characters, these may
111
+ no longer be suitable to estimate the charge neutrality point (CNP).
112
+ Instead, estimate the CNP for zero potential and then increase it 'slowly'
113
+ to full strength, i.e., diagonalize the Hamiltonian H0 + alpha V, where H0
114
+ is the Hamiltonian without potential and V is the potential. The coefficient
115
+ alpha is increased from 0 to 1. Then do band alignment over the sequence of
116
+ DiagDataPoint instances. The result is the DiagDataPoint for alpha = 1 with
117
+ the band indices set.
118
+
119
+ Arguments:
120
+ params PhysParams instance.
121
+ steps Integer. The number of intermediate steps. The step size for
122
+ alpha is 1 / steps.
123
+ pot Array or None. The potential.
124
+ num_cpus Integer. Number of processes/threads for parallelization.
125
+ modelopts A dict instance. The options for diagonalization.
126
+ bandalign_opts A dict instance. The options for band alignment.
127
+
128
+ Returns:
129
+ ddp DiagDataPoint for H = H0 + V, i.e., alpha = 1. The band indices
130
+ (ddp.bindex) are set.
131
+ """
132
+ if modelopts is None:
133
+ modelopts = {}
134
+ if bandalign_opts is None:
135
+ bandalign_opts = {}
136
+ modelopts1 = modelopts.copy() # Make a copy so that we can change it safely
137
+ if 'obs' in modelopts1:
138
+ del modelopts1['obs']
139
+ if 'pot' in modelopts1:
140
+ del modelopts1['pot']
141
+
142
+ alphas = np.linspace(0, 1, steps+1)
143
+ grid = VectorGrid('x', alphas, astype = 'x', prefix = 'a')
144
+ diagdata = DiagData(parallel_apply(
145
+ diag_hz_pot, grid, (params, pot), f_kwds = modelopts1,
146
+ num_processes = num_cpus, propagate_interrupt = True,
147
+ description = "Calculating bands (k=0)"),
148
+ grid = grid)
149
+
150
+ # If bandalign_opts contains e0 (the location of the charge neutrality
151
+ # point), use it. Otherwise, get it from estimate_charge_neutrality_point().
152
+ if 'e0' in bandalign_opts:
153
+ ba_data = bandindices(diagdata, input_data=diagdata[0], params=params, **bandalign_opts)
154
+ else:
155
+ e0 = estimate_charge_neutrality_point(params, data=diagdata[0])
156
+ ba_data = bandindices(diagdata, e0=e0, input_data=diagdata[0], params=params, **bandalign_opts)
157
+
158
+ if get_config_bool('bandindices_adiabatic_debug'): # debug output (to a file)
159
+ debug_file = "bandindices-adiabatic.csv"
160
+ bandindices_adiabatic_debug(debug_file, diagdata, alphas)
161
+ if 'verbose' in sys.argv: # verbose output (to stdout)
162
+ bandindices_adiabatic_verbose(diagdata, alphas)
163
+
164
+ # The value of paramval should be reset to 0, as otherwise paramval = 1
165
+ # can be misinterpreted as B = 1.
166
+ diagdata[-1].paramval = Vector(0)
167
+ return diagdata[-1]
168
+
169
+ def bandindices_adiabatic_ll(
170
+ ll_mode, ll_max, h_sym, params, steps=10, pot=None, num_cpus=1,
171
+ modelopts=None, bandalign_opts=None):
172
+ """Do band alignment over slowly increasing potential, starting at zero potential, LL version
173
+
174
+ This function is for the most part identical to bandindices_adiabatic() and
175
+ should/could be merged into it in the future.
176
+
177
+ Since an electrostatic potential can affect the band characters, these may
178
+ no longer be suitable to estimate the charge neutrality point (CNP).
179
+ Instead, estimate the CNP for zero potential and then increase it 'slowly'
180
+ to full strength, i.e., diagonalize the Hamiltonian H0 + alpha V, where H0
181
+ is the Hamiltonian without potential and V is the potential. The coefficient
182
+ alpha is increased from 0 to 1. Then do band alignment over the sequence of
183
+ DiagDataPoint instances. The result is the DiagDataPoint for alpha = 1 with
184
+ the band indices set.
185
+
186
+ Arguments:
187
+ ll_mode String. The Landau level mode, 'legacy', 'sym', or 'full'.
188
+ ll_max Integer. The highest Landau level index to consider.
189
+ h_sym SymbolicHamiltonian instance.
190
+ params PhysParams instance.
191
+ steps Integer. The number of intermediate steps. The step size for
192
+ alpha is 1 / steps.
193
+ pot Array or None. The potential.
194
+ num_cpus Integer. Number of processes/threads for parallelization.
195
+ modelopts A dict instance. The options for diagonalization.
196
+ bandalign_opts A dict instance. The options for band alignment.
197
+
198
+ Returns:
199
+ ddp DiagDataPoint for H = H0 + V, i.e., alpha = 1. The band indices
200
+ (ddp.bindex) are set.
201
+ """
202
+ if modelopts is None:
203
+ modelopts = {}
204
+ if bandalign_opts is None:
205
+ bandalign_opts = {}
206
+ modelopts1 = modelopts.copy() # Make a copy so that we can change it safely
207
+ if 'obs' in modelopts1:
208
+ del modelopts1['obs']
209
+ if 'pot' in modelopts1:
210
+ del modelopts1['pot']
211
+
212
+ alphas = np.linspace(0, 1, steps + 1)
213
+ grid = VectorGrid('x', alphas, astype='x', prefix='a')
214
+ diagdata = DiagData(parallel_apply(
215
+ diag_hz_pot_ll, grid, (ll_mode, ll_max, h_sym, params, pot), f_kwds=modelopts1, # mind different diag-function
216
+ num_processes=num_cpus, propagate_interrupt=True,
217
+ description="Calculating bands (k=0)"),
218
+ grid=grid)
219
+
220
+ # If bandalign_opts contains e0 (the location of the charge neutrality
221
+ # point), use it. Otherwise, get it from estimate_charge_neutrality_point().
222
+ if 'e0' in bandalign_opts:
223
+ ba_data = bandindices(diagdata, input_data=diagdata[0], params=params, **bandalign_opts)
224
+ else:
225
+ e0 = estimate_charge_neutrality_point(params, data=diagdata[0])
226
+ ba_data = bandindices(diagdata, e0=e0, input_data=diagdata[0], params=params, **bandalign_opts)
227
+
228
+ if get_config_bool('bandindices_adiabatic_debug'): # debug output (to a file)
229
+ debug_file = "bandindices-adiabatic.csv"
230
+ bandindices_adiabatic_debug(debug_file, diagdata, alphas)
231
+ if 'verbose' in sys.argv: # verbose output (to stdout)
232
+ bandindices_adiabatic_verbose(diagdata, alphas)
233
+
234
+ # For b-dependence, paramval should be reset to 0, as otherwise data cannot
235
+ # be interpreted as B = 0.
236
+ diagdata[-1].paramval = Vector(0)
237
+ return diagdata[-1]
238
+
@@ -0,0 +1,310 @@
1
+ # kdotpy - k·p theory on a lattice for simulating semiconductor band structures
2
+ # Copyright (C) 2024 The kdotpy collaboration <kdotpy@uni-wuerzburg.de>
3
+ #
4
+ # SPDX-License-Identifier: GPL-3.0-only
5
+ #
6
+ # This file is part of kdotpy.
7
+ #
8
+ # kdotpy is free software: you can redistribute it and/or modify it under the
9
+ # terms of the GNU General Public License as published by the Free Software
10
+ # Foundation, version 3.
11
+ #
12
+ # kdotpy is distributed in the hope that it will be useful, but WITHOUT ANY
13
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
+ # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along with
17
+ # kdotpy. If not, see <https://www.gnu.org/licenses/>.
18
+ #
19
+ # Under Section 7 of GPL version 3 we require you to fulfill the following
20
+ # additional terms:
21
+ #
22
+ # - We require the preservation of the full copyright notice and the license
23
+ # in all original files.
24
+ #
25
+ # - We prohibit misrepresentation of the origin of the original files. To
26
+ # obtain the original files, please visit the Git repository at
27
+ # <https://git.physik.uni-wuerzburg.de/kdotpy/kdotpy>
28
+ #
29
+ # - As part of a scientific environment, we believe it is reasonable to
30
+ # expect that you follow the rules of good scientific practice when using
31
+ # kdotpy. In particular, we expect that you credit the original authors if
32
+ # you benefit from this program, by citing our work, following the
33
+ # citation instructions in the file CITATION.md bundled with kdotpy.
34
+ #
35
+ # - If you make substantial changes to kdotpy, we strongly encourage that
36
+ # you contribute to the original project by joining our team. If you use
37
+ # or publish a modified version of this program, you are required to mark
38
+ # your material in a reasonable way as different from the original
39
+ # version.
40
+
41
+ import numpy as np
42
+ import sys
43
+
44
+ from ..cnp import estimate_charge_neutrality_point
45
+ from ..types import DiagDataPoint
46
+
47
+ from .base import BandAlignData, EnergyOutOfRangeError
48
+ from .base import bandalign, bandalign2d, bandalign_bulk
49
+ from .base import diagdatapoint_to_bandalignpoint, eival_e0_to_bandaligndata
50
+ from .csvi import bandindices_from_file
51
+
52
+
53
+ def bandindices_ddp_input(ddp, gridvar=None, e0=None, e0_relax=False):
54
+ """Extract a BandAlignData with a single BandAlignPoint from a DiagDataPoint instance"""
55
+ if ddp.bindex is not None:
56
+ p0 = diagdatapoint_to_bandalignpoint(ddp, gridvar=gridvar)
57
+ ba_data = None if p0 is None else BandAlignData([p0])
58
+ else: # Handles both cases, e0 = value and e0 = None
59
+ x0 = ddp.k if gridvar in ['k', ''] else ddp.paramval
60
+ ba_data = eival_e0_to_bandaligndata(ddp.eival, e0, x0=x0, e0_relax=e0_relax)
61
+ # Catch EnergyOutOfRangeError in bandindices_worker().
62
+ return ba_data
63
+
64
+
65
+ ### BAND INDICES INTERFACE ###
66
+ def bandindices_worker(
67
+ data, input_data = None, e0 = None, k0 = None, params = None, g0 = None,
68
+ component = None, e0_relax = False, auto_cnp = True):
69
+ """Get band indices (worker function)
70
+ This function prepares data for band alignment, and performs the appropriate
71
+ extrapolations in order to try and fill in the band indices for all points
72
+ in the input data.
73
+
74
+ Arguments:
75
+ data DiagData instance. Result from diagonalization functions, which
76
+ may be for a multi-dimensional grid.
77
+ input_data BandAlignData instance, DiagDataPoint instance or None. If set,
78
+ start with the band indices already defined there. If None,
79
+ infer band indices from e0.
80
+ e0 Float or None. Energy where to 'pin' the band indices to. By
81
+ default for g0 = 0), this is the energy neutral gap, between
82
+ bands -1 and 1. If both e0 and input_data are not None, then e0
83
+ is prioritized.
84
+ k0 Vector instance or None. Where to apply the energy e0 and start
85
+ the band alignment. If None, use the zero point of data.
86
+ params PhysParams instance.
87
+ g0 Integer. Gap index at energy e0. The band indices below and
88
+ above the gap are set to -1 and 1 if g0 == 0, g0 - 1 and g0 if
89
+ g0 < 0, and g0 and g0 + 1 if g0 > 0. Only affects the result
90
+ when input_data is None and e0 is given.
91
+ component String or None. Vector component that is used as 'variable'.
92
+ For example, 'kx'.
93
+ e0_relax True or False. If False (default), require that both the
94
+ energies above and below the gap must be defined. If True,
95
+ allow one to be undefined. Applies only to 'pinning' to e0.
96
+ auto_cnp True or False. If True, try to determine the CNP if e0 has not
97
+ been specified. If False, do not attempt to calculate the CNP.
98
+
99
+ Returns:
100
+ ba_data BandAlignData instance. Contains the band indices for as many data
101
+ points as possible. On failure, return None.
102
+ """
103
+ if len(data) == 0:
104
+ sys.stderr.write("Warning (bandindices_worker): No data.\n")
105
+ return None
106
+
107
+ # Bulk mode, except bulk LL mode. If the data is from bulk LL mode, e0 is
108
+ # given explicitly, so we use e0 as a way to detect this mode.
109
+ if params is not None and params.nz == 1 and e0 is None:
110
+ return bandalign_bulk(data, params=params)
111
+
112
+ if k0 is None and isinstance(input_data, DiagDataPoint):
113
+ data_k0, k0 = data.find(input_data.k, input_data.paramval, return_index=True)
114
+ if data_k0 is None:
115
+ sys.stderr.write("ERROR (bandindices_worker): Argument input_data is a DiagDataPoint outside the DiagData instance.\n")
116
+ input_data = None
117
+ elif k0 is None:
118
+ data_k0, k0 = data.get_zero_point(return_index = True)
119
+ elif isinstance(k0, (float, np.floating, tuple)):
120
+ data_k0, k0 = data.find(k0, return_index = True)
121
+ elif isinstance(k0, (int, np.integer)):
122
+ if k0 < 0 or k0 >= len(data):
123
+ raise IndexError("Invalid k index.")
124
+ data_k0 = data[k0]
125
+ else:
126
+ raise TypeError("Invalid type for k0.")
127
+ if data_k0 is None:
128
+ sys.stderr.write("Warning (bandindices_worker): Anchor point for alignment could not be determined. Using alternative base point.\n")
129
+ data_k0, k0 = data.get_base_point(return_index = True)
130
+ auto_cnp = False
131
+
132
+ ## Determine whether data has a single LL index
133
+ llindices = data.get_all_llindex()
134
+ llindex = None
135
+ if llindices is None or len(llindices) == 0:
136
+ pass
137
+ elif len(llindices) == 1:
138
+ llindex = llindices[0]
139
+ else:
140
+ sys.stderr.write("Warning (bandindices_worker): Data point at zero is a mixture of different LL indices.\n")
141
+ ef_gap_message = (llindex is None) or (llindex == 1)
142
+
143
+ ## If e0 is not defined, try to get it from estimate_charge_neutrality_point()
144
+ if e0 is None and params is not None and auto_cnp:
145
+ cnp_data = input_data if isinstance(input_data, DiagDataPoint) else data_k0
146
+ e0 = estimate_charge_neutrality_point(params, data=cnp_data, print_gap_message=ef_gap_message)
147
+ g0 = 0 # The result from estimate_charge_neutrality_point() always refers to the CNP
148
+
149
+ ## Prepare: From input_data, define the initial BandAlignData (ba_data)
150
+ if input_data is not None and not isinstance(input_data, (BandAlignData, DiagDataPoint)):
151
+ raise TypeError("Argument input_data must be a BandAlignData instance, DiagDataPoint instance, or None")
152
+ if isinstance(input_data, DiagDataPoint) and e0 is None:
153
+ try:
154
+ ba_data = bandindices_ddp_input(input_data, gridvar=data.gridvar, e0_relax=e0_relax)
155
+ except EnergyOutOfRangeError:
156
+ sys.stderr.write("ERROR (bandindices_worker): Band alignment failed, because zero energy is out of range.\n")
157
+ ba_data = None
158
+ elif isinstance(input_data, BandAlignData): # BandAlignData: use input itself
159
+ ba_data = input_data
160
+ else:
161
+ x0 = data_k0.k if data.gridvar in ['k', ''] else data_k0.paramval
162
+ try:
163
+ # Note: e0 may be a numerical value or None
164
+ ba_data = eival_e0_to_bandaligndata(data_k0.eival, e0, g0=g0, x0=x0, e0_relax=e0_relax)
165
+ except EnergyOutOfRangeError:
166
+ sys.stderr.write("ERROR (bandindices_worker): Band alignment failed, because zero energy is out of range.\n")
167
+ return None
168
+
169
+ if ba_data is None:
170
+ sys.stderr.write("Warning (bandindices_worker): Failed.\n")
171
+ return None
172
+
173
+ ## For low LLs (llindex = -2, -1, 0), redefine band indices based on e0
174
+ ## Otherwise the band indices may misalign because of non-matching eigenvalues
175
+ if llindex in [-2, -1, 0] and len(ba_data) == 1:
176
+ x0 = data_k0.k if data.gridvar in ['k', ''] else data_k0.paramval
177
+ p0 = ba_data.get(x0)
178
+ if p0 is not None:
179
+ try:
180
+ p1 = p0.match_gap(data_k0.eival)
181
+ except ValueError:
182
+ # With a single precision solver, the default accuracy can be
183
+ # too strict and no match occurs. Retry with lower accuracy.
184
+ p1 = p0.match_gap(data_k0.eival, accuracy=1e-3)
185
+ ba_data = BandAlignData([p1])
186
+ else:
187
+ sys.stderr.write("Warning (bandindices_worker): Cannot not match energy eigenvalues for LL %i. Beware that densities may be incorrect.\n")
188
+
189
+ ## Align bands and set data
190
+ if not isinstance(data.shape, tuple):
191
+ raise TypeError("Attribute data.shape must be a tuple")
192
+ dim = len(data.shape)
193
+ if dim == 1: # 1D grid (linear)
194
+ if component is None and data.grid is not None:
195
+ component = data.grid.var[0]
196
+
197
+ ba_data = bandalign(data, ba_data=ba_data, component=component)
198
+ return ba_data
199
+ elif dim == 2: # 2D grid; common code for polar and cartesian arrangements
200
+ components = [None, None] if data.grid is None else data.grid.var
201
+
202
+ ba_data = bandalign2d(data, ba_data=ba_data, components=components)
203
+
204
+ return ba_data
205
+ elif dim == 3: # 3D grid; we shouldn't end up here in bulk mode
206
+ raise RuntimeError("For 3-dim data, the dedicated bulk mode must be used")
207
+ else:
208
+ raise ValueError("Data must be of 1, 2, or 3 dimensions")
209
+
210
+ def bandindices_retry(data, params = None, e0 = None, **kwds):
211
+ """Retry band index calculation.
212
+
213
+ If the band index calculation fails, retry at slightly different energies.
214
+
215
+ Arguments:
216
+ data DiagData instance. Result from diagonalization functions, which
217
+ may be for a multi-dimensional grid.
218
+ params PhysParams instance.
219
+ e0 Float or None. Energy where to 'pin' the band indices to. By
220
+ default for g0 = 0), this is the energy neutral gap, between
221
+ bands -1 and 1. If both e0 and input_data are not None, then e0
222
+ is prioritized.
223
+ **kwds Keyword arguments passed to bandindices_worker(), i.e., band
224
+ alignment options.
225
+
226
+ Returns:
227
+ b_idx BandAlignData instance. Contains the band indices for as many data
228
+ points as possible. On failure, return None.
229
+ e1 Float or None. If successful, the energy where the band index
230
+ calculation succeeded. On failure, return None.
231
+ """
232
+ if e0 is None:
233
+ b_idx = bandindices_worker(data, params = params, e0 = None, **kwds)
234
+ return b_idx
235
+ n = 0
236
+ for e1 in e0 + np.array([0.0, 1.0, -1.0, 0.5, -0.5, 2.0, -2.0]):
237
+ b_idx = bandindices_worker(data, params = params, e0 = e1, **kwds)
238
+ n += 1
239
+ if b_idx is not None:
240
+ if n != 1:
241
+ sys.stderr.write("Warning (bandindices_retry): Band indices were found successfully at target energy %s.\n" % e1)
242
+ return b_idx
243
+ sys.stderr.write("Warning (bandindices_retry): Band indices were not found after %i attempts.\n" % n)
244
+ return None
245
+
246
+ def bandindices(data, e0 = None, g0 = None, from_file = None, retry = False, do_apply = True, **kwds):
247
+ """Do band alignment. Wrapper function for several other functions defined in bandalign.py.
248
+
249
+ data DiagData instance.
250
+ e0 Float or None. Energy where to 'pin' the band indices to. By
251
+ default for g0 = 0), this is the energy neutral gap, between
252
+ bands -1 and 1. This value takes precedence above input_data,
253
+ if that is also defined in **kwds.
254
+ g0 Integer. Gap index at energy e0. The band indices below and
255
+ above the gap are set to -1 and 1 if g0 == 0, g0 - 1 and g0 if
256
+ g0 < 0, and g0 and g0 + 1 if g0 > 0.
257
+ from_file String or None. Filename for extracting band index information.
258
+ If None, do not use a file.
259
+ retry True or False. If True, try to do the alignment for slightly
260
+ different energies. This used to be the default for
261
+ kdotpy-merge.py, but is probably not needed any more.
262
+ do_apply True or False. If True, fill in the band indices into the
263
+ DiagData instance data. If False, only return the result.
264
+ **kwds Keyword arguments passed on to bandindices_worker().
265
+
266
+ Returns:
267
+ ba_data BandAlignData instance, dict of BandAlignData instances, or
268
+ None. The value None indicates failure of band alignment. For
269
+ LL mode, return a dict, otherwise a single BandAlignData
270
+ instance.
271
+ """
272
+ # Check whether the data has LL indices
273
+ ll_idx = data.get_all_llindex()
274
+ ll = ll_idx is not None
275
+
276
+ if from_file is not None:
277
+ ba_data = bandindices_from_file(from_file)
278
+ if ll: # split by LL index
279
+ for lln in ba_data:
280
+ data_lln = data.select_llindex(lln)
281
+ ba_data[lln] = bandindices_worker(data_lln, e0 = None, e0_relax = (lln < 1), input_data = ba_data[lln])
282
+ else:
283
+ ba_data = bandindices_worker(data, e0 = None, input_data = ba_data)
284
+ else:
285
+ bandindices_fn = bandindices_retry if retry else bandindices_worker
286
+ if ll: # split by LL index
287
+ ba_data = {}
288
+ for lln in ll_idx:
289
+ data_lln = data.select_llindex(lln)
290
+ ba_data[lln] = bandindices_fn(data_lln, e0 = e0, g0 = g0, e0_relax = (lln < 1), **kwds)
291
+ else:
292
+ ba_data = bandindices_fn(data, e0 = e0, g0 = g0, **kwds)
293
+
294
+ if do_apply:
295
+ data.reset_bindex()
296
+ if ll and isinstance(ba_data, dict):
297
+ for lln in ba_data:
298
+ if isinstance(ba_data[lln], BandAlignData):
299
+ ba_data[lln].apply_to(data, llindex = lln)
300
+ elif ba_data[lln] is not None:
301
+ raise TypeError("Value should be a BandAlignData instance or None")
302
+ # Possible fallback: data.set_bindex(ba_data[lln], llindex = lln)
303
+ elif isinstance(ba_data, BandAlignData):
304
+ ba_data.apply_to(data)
305
+ elif ba_data is not None:
306
+ raise TypeError("Value should be a BandAlignData instance or None")
307
+ # Possible fallback: data.set_bindex(ba_data)
308
+
309
+ return ba_data
310
+