xarpes 0.3.3__py3-none-any.whl → 0.4.0__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.
- xarpes/__init__.py +1 -1
- xarpes/functions.py +102 -48
- xarpes/plotting.py +31 -1
- xarpes/spectral.py +474 -65
- {xarpes-0.3.3.dist-info → xarpes-0.4.0.dist-info}/METADATA +16 -8
- xarpes-0.4.0.dist-info/RECORD +11 -0
- {xarpes-0.3.3.dist-info → xarpes-0.4.0.dist-info}/WHEEL +1 -1
- xarpes-0.3.3.dist-info/RECORD +0 -11
- {xarpes-0.3.3.dist-info → xarpes-0.4.0.dist-info}/entry_points.txt +0 -0
- {xarpes-0.3.3.dist-info → xarpes-0.4.0.dist-info/licenses}/LICENSE +0 -0
xarpes/__init__.py
CHANGED
xarpes/functions.py
CHANGED
|
@@ -254,65 +254,119 @@ def download_examples():
|
|
|
254
254
|
import shutil
|
|
255
255
|
import io
|
|
256
256
|
import jupytext
|
|
257
|
+
import tempfile
|
|
258
|
+
import re
|
|
257
259
|
|
|
258
|
-
# Main xARPES repo (examples
|
|
259
|
-
repo_url =
|
|
260
|
-
output_dir =
|
|
260
|
+
# Main xARPES repo (examples live under /examples there)
|
|
261
|
+
repo_url = "https://github.com/xARPES/xARPES"
|
|
262
|
+
output_dir = "." # Directory from which the function is called
|
|
261
263
|
|
|
262
264
|
# Target 'examples' directory in the user's current location
|
|
263
|
-
final_examples_path = os.path.join(output_dir,
|
|
265
|
+
final_examples_path = os.path.join(output_dir, "examples")
|
|
264
266
|
if os.path.exists(final_examples_path):
|
|
265
267
|
print("Warning: 'examples' folder already exists. "
|
|
266
268
|
"No download will be performed.")
|
|
267
269
|
return 1 # Exit the function if 'examples' directory exists
|
|
268
270
|
|
|
269
|
-
#
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
#
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
print(f"'examples' subdirectory moved to {final_examples_path}")
|
|
293
|
-
|
|
294
|
-
# Convert all .Rmd files in the examples directory to .ipynb
|
|
295
|
-
# and delete the .Rmd files
|
|
296
|
-
for dirpath, dirnames, filenames in os.walk(final_examples_path):
|
|
297
|
-
for filename in filenames:
|
|
298
|
-
if filename.endswith('.Rmd'):
|
|
299
|
-
full_path = os.path.join(dirpath, filename)
|
|
300
|
-
jupytext.write(
|
|
301
|
-
jupytext.read(full_path),
|
|
302
|
-
full_path.replace('.Rmd', '.ipynb')
|
|
303
|
-
)
|
|
304
|
-
os.remove(full_path) # Deletes .Rmd file afterwards
|
|
305
|
-
print(f'Converted and deleted {full_path}')
|
|
306
|
-
|
|
307
|
-
# Remove the rest of the extracted content
|
|
308
|
-
shutil.rmtree(main_folder_path)
|
|
309
|
-
print(f'Cleaned up temporary files in {main_folder_path}')
|
|
310
|
-
return 0
|
|
271
|
+
# --- Determine version from xarpes.__init__.__version__ -----------------
|
|
272
|
+
try:
|
|
273
|
+
# Import inside the function, avoiding circular imports at import time
|
|
274
|
+
import xarpes as _xarpes
|
|
275
|
+
raw_version = getattr(_xarpes, "__version__", None)
|
|
276
|
+
except Exception as exc:
|
|
277
|
+
print(f"Warning: could not import xarpes to determine version: {exc}")
|
|
278
|
+
raw_version = None
|
|
279
|
+
|
|
280
|
+
tag_version = None
|
|
281
|
+
if raw_version is not None:
|
|
282
|
+
raw_version = str(raw_version)
|
|
283
|
+
# Strip dev/local suffixes so that '0.3.3.dev1' or '0.3.3+0.gHASH'
|
|
284
|
+
# maps to the tag 'v0.3.3'. If you use plain '0.3.3' already, this is
|
|
285
|
+
# a no-op.
|
|
286
|
+
m = re.match(r"(\d+\.\d+\.\d+)", raw_version)
|
|
287
|
+
if m:
|
|
288
|
+
tag_version = m.group(1)
|
|
289
|
+
else:
|
|
290
|
+
tag_version = raw_version
|
|
291
|
+
|
|
292
|
+
print(f"Determined xARPES version from __init__: {raw_version} "
|
|
293
|
+
f"(using tag version '{tag_version}').")
|
|
311
294
|
else:
|
|
312
|
-
print(
|
|
313
|
-
|
|
295
|
+
print("Warning: xarpes.__version__ is not defined; will skip "
|
|
296
|
+
"tag-based download and try the main branch only.")
|
|
297
|
+
|
|
298
|
+
# --- Build refs and use for–else to try them in order -------------------
|
|
299
|
+
repo_parts = repo_url.replace("https://github.com/", "").rstrip("/")
|
|
300
|
+
|
|
301
|
+
refs_to_try = []
|
|
302
|
+
if tag_version is not None:
|
|
303
|
+
refs_to_try.append(f"tags/v{tag_version}") # version-matched examples
|
|
304
|
+
refs_to_try.append("heads/main") # fallback: latest examples
|
|
305
|
+
|
|
306
|
+
response = None
|
|
307
|
+
for ref in refs_to_try:
|
|
308
|
+
zip_url = f"https://github.com/{repo_parts}/archive/refs/{ref}.zip"
|
|
309
|
+
print(f"Attempting to download examples from '{ref}':\n {zip_url}")
|
|
310
|
+
response = requests.get(zip_url)
|
|
311
|
+
|
|
312
|
+
if response.status_code == 200:
|
|
313
|
+
if ref.startswith("tags/"):
|
|
314
|
+
print(f"Successfully downloaded examples from tagged release "
|
|
315
|
+
f"'v{tag_version}'.")
|
|
316
|
+
else:
|
|
317
|
+
print("Tagged release not available; using latest examples "
|
|
318
|
+
"from the 'main' branch instead.")
|
|
319
|
+
break
|
|
320
|
+
else:
|
|
321
|
+
print("Failed to download from this ref. HTTP status code: "
|
|
322
|
+
f"{response.status_code}")
|
|
323
|
+
else:
|
|
324
|
+
# for–else: only executed if we never hit 'break'
|
|
325
|
+
print("Error: could not download examples from any ref "
|
|
326
|
+
f"(tried: {', '.join(refs_to_try)}).")
|
|
314
327
|
return 1
|
|
315
328
|
|
|
329
|
+
# At this point, 'response' holds a successful download
|
|
330
|
+
zip_file_bytes = io.BytesIO(response.content)
|
|
331
|
+
|
|
332
|
+
# --- Extract into a temporary directory to avoid polluting CWD ----------
|
|
333
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
334
|
+
with zipfile.ZipFile(zip_file_bytes, "r") as zip_ref:
|
|
335
|
+
zip_ref.extractall(tmpdir)
|
|
336
|
+
# First member gives us the top-level directory in the archive,
|
|
337
|
+
# typically something like 'xARPES-0.3.3/' or 'xARPES-main/'.
|
|
338
|
+
first_member = zip_ref.namelist()[0]
|
|
339
|
+
|
|
340
|
+
top_level_dir = first_member.split("/")[0]
|
|
341
|
+
main_folder_path = os.path.join(tmpdir, top_level_dir)
|
|
342
|
+
examples_path = os.path.join(main_folder_path, "examples")
|
|
343
|
+
|
|
344
|
+
if not os.path.exists(examples_path):
|
|
345
|
+
print("Error: downloaded archive does not contain an 'examples' "
|
|
346
|
+
"directory.")
|
|
347
|
+
return 1
|
|
348
|
+
|
|
349
|
+
# Move the 'examples' directory to the target location in the CWD
|
|
350
|
+
shutil.move(examples_path, final_examples_path)
|
|
351
|
+
print(f"'examples' subdirectory moved to {final_examples_path}")
|
|
352
|
+
|
|
353
|
+
# Convert all .Rmd files in the examples directory to .ipynb
|
|
354
|
+
# and delete the .Rmd files
|
|
355
|
+
for dirpath, dirnames, filenames in os.walk(final_examples_path):
|
|
356
|
+
for filename in filenames:
|
|
357
|
+
if filename.endswith(".Rmd"):
|
|
358
|
+
full_path = os.path.join(dirpath, filename)
|
|
359
|
+
jupytext.write(
|
|
360
|
+
jupytext.read(full_path),
|
|
361
|
+
full_path.replace(".Rmd", ".ipynb")
|
|
362
|
+
)
|
|
363
|
+
os.remove(full_path) # Deletes .Rmd file afterwards
|
|
364
|
+
print(f"Converted and deleted {full_path}")
|
|
365
|
+
|
|
366
|
+
# Temporary directory is cleaned up automatically
|
|
367
|
+
print("Cleaned up temporary files.")
|
|
368
|
+
return 0
|
|
369
|
+
|
|
316
370
|
|
|
317
371
|
def set_script_dir():
|
|
318
372
|
r"""This function sets the directory such that the xARPES code can be
|
xarpes/plotting.py
CHANGED
|
@@ -12,14 +12,19 @@
|
|
|
12
12
|
"""Functions related to plotting."""
|
|
13
13
|
|
|
14
14
|
from functools import wraps
|
|
15
|
+
from IPython import get_ipython
|
|
15
16
|
import matplotlib.pyplot as plt
|
|
16
17
|
import matplotlib as mpl
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
|
|
20
|
+
def plot_settings(name='default', register_pre_run=True):
|
|
21
|
+
"""Configure default plotting style for xARPES."""
|
|
22
|
+
|
|
19
23
|
mpl.rc('xtick', labelsize=10, direction='in')
|
|
20
24
|
mpl.rc('ytick', labelsize=10, direction='in')
|
|
21
25
|
plt.rcParams['legend.frameon'] = False
|
|
22
26
|
lw = dict(default=2.0, large=4.0)[name]
|
|
27
|
+
|
|
23
28
|
mpl.rcParams.update({
|
|
24
29
|
'lines.linewidth': lw,
|
|
25
30
|
'lines.markersize': 3,
|
|
@@ -30,6 +35,31 @@ def plot_settings(name='default'):
|
|
|
30
35
|
'axes.ymargin': 0.15,
|
|
31
36
|
})
|
|
32
37
|
|
|
38
|
+
if register_pre_run:
|
|
39
|
+
_maybe_register_pre_run_close_all()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _maybe_register_pre_run_close_all():
|
|
43
|
+
"""Register a pre_run_cell hook once, and only inside Jupyter."""
|
|
44
|
+
|
|
45
|
+
# Create the function attribute on first call
|
|
46
|
+
if not hasattr(_maybe_register_pre_run_close_all, "_registered"):
|
|
47
|
+
_maybe_register_pre_run_close_all._registered = False
|
|
48
|
+
|
|
49
|
+
if _maybe_register_pre_run_close_all._registered:
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
ip = get_ipython()
|
|
53
|
+
if ip is None or ip.__class__.__name__ != "ZMQInteractiveShell":
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
def _close_all(_info):
|
|
57
|
+
plt.close('all')
|
|
58
|
+
|
|
59
|
+
ip.events.register('pre_run_cell', _close_all)
|
|
60
|
+
_maybe_register_pre_run_close_all._registered = True
|
|
61
|
+
|
|
62
|
+
|
|
33
63
|
def get_ax_fig_plt(ax=None, **kwargs):
|
|
34
64
|
r"""Helper function used in plot functions supporting an optional `Axes`
|
|
35
65
|
argument.
|
xarpes/spectral.py
CHANGED
|
@@ -16,7 +16,7 @@ from igor2 import binarywave
|
|
|
16
16
|
from .plotting import get_ax_fig_plt, add_fig_kwargs
|
|
17
17
|
from .functions import fit_leastsq, extend_function
|
|
18
18
|
from .distributions import FermiDirac, Linear
|
|
19
|
-
from .constants import uncr, pref, dtor, kilo
|
|
19
|
+
from .constants import uncr, pref, dtor, kilo, stdv
|
|
20
20
|
|
|
21
21
|
class BandMap:
|
|
22
22
|
r"""
|
|
@@ -346,27 +346,41 @@ class BandMap:
|
|
|
346
346
|
return mdcs, angle_range_out, self.angle_resolution, \
|
|
347
347
|
enel_range_out, self.hnuminphi
|
|
348
348
|
|
|
349
|
-
|
|
350
349
|
@add_fig_kwargs
|
|
351
350
|
def plot(self, abscissa='momentum', ordinate='electron_energy',
|
|
352
|
-
|
|
351
|
+
self_energies=None, ax=None, markersize=None,
|
|
352
|
+
plot_dispersions='none', **kwargs):
|
|
353
353
|
r"""
|
|
354
|
-
Plot the band map. Optionally
|
|
354
|
+
Plot the band map. Optionally overlay a collection of self-energies,
|
|
355
355
|
e.g. a CreateSelfEnergies instance or any iterable of self-energy
|
|
356
|
-
objects.
|
|
356
|
+
objects. Self-energies are *not* stored internally; they are used
|
|
357
|
+
only for this plotting call.
|
|
358
|
+
|
|
359
|
+
When self-energies are present and ``abscissa='momentum'``, their
|
|
360
|
+
MDC maxima are overlaid with 95 % confidence intervals.
|
|
361
|
+
|
|
362
|
+
The `plot_dispersions` argument controls bare-band plotting:
|
|
363
|
+
|
|
364
|
+
- "full" : use the full momentum range of the map (default)
|
|
365
|
+
- "none" : do not plot bare dispersions
|
|
366
|
+
- "kink" : for each self-energy, use the min/max of its own
|
|
367
|
+
momentum range (typically its MDC maxima), with
|
|
368
|
+
`len(self.angles)` points.
|
|
369
|
+
- "domain" : for SpectralQuadratic, use only the left or right
|
|
370
|
+
domain relative to `center_wavevector`, based on the self-energy
|
|
371
|
+
attribute `side` ("left" / "right"); for other cases this behaves
|
|
372
|
+
as "full".
|
|
357
373
|
"""
|
|
374
|
+
import warnings
|
|
375
|
+
|
|
376
|
+
plot_disp_mode = plot_dispersions
|
|
377
|
+
valid_disp_modes = ('full', 'none', 'kink', 'domain')
|
|
378
|
+
if plot_disp_mode not in valid_disp_modes:
|
|
379
|
+
raise ValueError(
|
|
380
|
+
f"Invalid plot_dispersions '{plot_disp_mode}'. "
|
|
381
|
+
f"Valid options: {valid_disp_modes}."
|
|
382
|
+
)
|
|
358
383
|
|
|
359
|
-
# Optionally store self-energies on the instance
|
|
360
|
-
if self_energies is not None:
|
|
361
|
-
# You can wrap here if you like, e.g.:
|
|
362
|
-
# from .containers import CreateSelfEnergies
|
|
363
|
-
# if not isinstance(self_energies, CreateSelfEnergies):
|
|
364
|
-
# self_energies = CreateSelfEnergies(self_energies)
|
|
365
|
-
self._self_energies = self_energies
|
|
366
|
-
elif not hasattr(self, "_self_energies"):
|
|
367
|
-
self._self_energies = None
|
|
368
|
-
|
|
369
|
-
# Validate options early
|
|
370
384
|
valid_abscissa = ('angle', 'momentum')
|
|
371
385
|
valid_ordinate = ('kinetic_energy', 'electron_energy')
|
|
372
386
|
|
|
@@ -381,57 +395,247 @@ class BandMap:
|
|
|
381
395
|
f"Valid options: {valid_ordinate}"
|
|
382
396
|
)
|
|
383
397
|
|
|
398
|
+
if self_energies is not None:
|
|
399
|
+
|
|
400
|
+
# MDC maxima are defined in momentum space, not angle space
|
|
401
|
+
if abscissa == 'angle':
|
|
402
|
+
raise ValueError(
|
|
403
|
+
"MDC maxima cannot be plotted against angles; they are "
|
|
404
|
+
"defined in momentum space. Use abscissa='momentum' "
|
|
405
|
+
"when passing self-energies."
|
|
406
|
+
)
|
|
407
|
+
|
|
384
408
|
ax, fig, plt = get_ax_fig_plt(ax=ax)
|
|
385
409
|
|
|
386
410
|
Angl, Ekin = np.meshgrid(self.angles, self.ekin)
|
|
387
|
-
mesh = None # sentinel to detect missing branch
|
|
388
411
|
|
|
389
412
|
if abscissa == 'angle':
|
|
390
413
|
ax.set_xlabel('Angle ($\\degree$)')
|
|
391
414
|
if ordinate == 'kinetic_energy':
|
|
392
415
|
mesh = ax.pcolormesh(
|
|
393
|
-
Angl, Ekin, self.intensities,
|
|
394
|
-
|
|
395
|
-
|
|
416
|
+
Angl, Ekin, self.intensities,
|
|
417
|
+
shading='auto',
|
|
418
|
+
cmap=plt.get_cmap('bone').reversed())
|
|
396
419
|
ax.set_ylabel('$E_{\\mathrm{kin}}$ (eV)')
|
|
397
420
|
elif ordinate == 'electron_energy':
|
|
398
421
|
Enel = Ekin - self.hnuminphi
|
|
399
422
|
mesh = ax.pcolormesh(
|
|
400
|
-
Angl, Enel, self.intensities,
|
|
401
|
-
|
|
402
|
-
|
|
423
|
+
Angl, Enel, self.intensities,
|
|
424
|
+
shading='auto',
|
|
425
|
+
cmap=plt.get_cmap('bone').reversed())
|
|
403
426
|
ax.set_ylabel('$E-\\mu$ (eV)')
|
|
404
427
|
|
|
405
428
|
elif abscissa == 'momentum':
|
|
406
|
-
Mome = np.sqrt(Ekin / pref) * np.sin(Angl * dtor)
|
|
407
429
|
ax.set_xlabel(r'$k_{//}$ ($\mathrm{\AA}^{-1}$)')
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
430
|
+
|
|
431
|
+
with warnings.catch_warnings(record=True) as wlist:
|
|
432
|
+
warnings.filterwarnings(
|
|
433
|
+
"always",
|
|
434
|
+
message=(
|
|
435
|
+
"The input coordinates to pcolormesh are "
|
|
436
|
+
"interpreted as cell centers, but are not "
|
|
437
|
+
"monotonically increasing or decreasing."
|
|
438
|
+
),
|
|
439
|
+
category=UserWarning,
|
|
412
440
|
)
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
441
|
+
|
|
442
|
+
Mome = np.sqrt(Ekin / pref) * np.sin(Angl * dtor)
|
|
443
|
+
mome_min = np.min(Mome)
|
|
444
|
+
mome_max = np.max(Mome)
|
|
445
|
+
full_disp_momenta = np.linspace(
|
|
446
|
+
mome_min, mome_max, len(self.angles)
|
|
419
447
|
)
|
|
420
|
-
ax.set_ylabel('$E-\\mu$ (eV)')
|
|
421
448
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
449
|
+
if ordinate == 'kinetic_energy':
|
|
450
|
+
mesh = ax.pcolormesh(
|
|
451
|
+
Mome, Ekin, self.intensities,
|
|
452
|
+
shading='auto',
|
|
453
|
+
cmap=plt.get_cmap('bone').reversed())
|
|
454
|
+
ax.set_ylabel('$E_{\\mathrm{kin}}$ (eV)')
|
|
455
|
+
elif ordinate == 'electron_energy':
|
|
456
|
+
Enel = Ekin - self.hnuminphi
|
|
457
|
+
mesh = ax.pcolormesh(
|
|
458
|
+
Mome, Enel, self.intensities,
|
|
459
|
+
shading='auto',
|
|
460
|
+
cmap=plt.get_cmap('bone').reversed())
|
|
461
|
+
ax.set_ylabel('$E-\\mu$ (eV)')
|
|
462
|
+
|
|
463
|
+
y_lims = ax.get_ylim()
|
|
464
|
+
|
|
465
|
+
if any("cell centers" in str(w.message) for w in wlist):
|
|
466
|
+
warnings.warn(
|
|
467
|
+
"Conversion from angle to momenta causes warping of the "
|
|
468
|
+
"cell centers. \n Cell edges of the mesh plot may look "
|
|
469
|
+
"irregular.",
|
|
470
|
+
UserWarning,
|
|
471
|
+
stacklevel=2,
|
|
472
|
+
)
|
|
430
473
|
|
|
431
|
-
|
|
432
|
-
|
|
474
|
+
if abscissa == 'momentum' and self_energies is not None:
|
|
475
|
+
for self_energy in self_energies:
|
|
476
|
+
|
|
477
|
+
mdc_maxima = getattr(self_energy, "mdc_maxima", None)
|
|
478
|
+
|
|
479
|
+
# If this self-energy doesn't contain maxima, don't plot
|
|
480
|
+
if mdc_maxima is None:
|
|
481
|
+
continue
|
|
482
|
+
|
|
483
|
+
# Reserve a colour from the axes cycle for this self-energy,
|
|
484
|
+
# and use it consistently for MDC maxima and dispersion.
|
|
485
|
+
line_color = ax._get_lines.get_next_color()
|
|
486
|
+
|
|
487
|
+
peak_sigma = getattr(
|
|
488
|
+
self_energy, "peak_positions_sigma", None
|
|
489
|
+
)
|
|
490
|
+
xerr = stdv * peak_sigma if peak_sigma is not None else None
|
|
491
|
+
|
|
492
|
+
if ordinate == 'kinetic_energy':
|
|
493
|
+
y_vals = self_energy.ekin_range
|
|
494
|
+
else:
|
|
495
|
+
y_vals = self_energy.enel_range
|
|
496
|
+
|
|
497
|
+
x_vals = mdc_maxima
|
|
498
|
+
label = getattr(self_energy, "label", None)
|
|
499
|
+
|
|
500
|
+
# First plot the MDC maxima, using the reserved colour
|
|
501
|
+
if xerr is not None:
|
|
502
|
+
ax.errorbar(
|
|
503
|
+
x_vals, y_vals, xerr=xerr, fmt='o',
|
|
504
|
+
linestyle='', label=label,
|
|
505
|
+
markersize=markersize,
|
|
506
|
+
color=line_color, ecolor=line_color,
|
|
507
|
+
)
|
|
508
|
+
else:
|
|
509
|
+
ax.plot(
|
|
510
|
+
x_vals, y_vals, linestyle='',
|
|
511
|
+
marker='o', label=label,
|
|
512
|
+
markersize=markersize,
|
|
513
|
+
color=line_color,
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
# Bare-band dispersion for SpectralLinear / SpectralQuadratic
|
|
517
|
+
spec_class = getattr(
|
|
518
|
+
self_energy, "_class",
|
|
519
|
+
self_energy.__class__.__name__,
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
if (plot_disp_mode != 'none'
|
|
523
|
+
and spec_class in ("SpectralLinear",
|
|
524
|
+
"SpectralQuadratic")):
|
|
525
|
+
|
|
526
|
+
# Determine momentum grid for the dispersion
|
|
527
|
+
if plot_disp_mode == 'kink':
|
|
528
|
+
x_arr = np.asarray(x_vals)
|
|
529
|
+
mask = np.isfinite(x_arr)
|
|
530
|
+
if not np.any(mask):
|
|
531
|
+
# No valid k-points to define a range
|
|
532
|
+
continue
|
|
533
|
+
k_min = np.min(x_arr[mask])
|
|
534
|
+
k_max = np.max(x_arr[mask])
|
|
535
|
+
disp_momenta = np.linspace(
|
|
536
|
+
k_min, k_max, len(self.angles)
|
|
537
|
+
)
|
|
538
|
+
elif (plot_disp_mode == 'domain'
|
|
539
|
+
and spec_class == "SpectralQuadratic"):
|
|
540
|
+
side = getattr(self_energy, "side", None)
|
|
541
|
+
if side == 'left':
|
|
542
|
+
disp_momenta = np.linspace(
|
|
543
|
+
mome_min, self_energy.center_wavevector,
|
|
544
|
+
len(self.angles)
|
|
545
|
+
)
|
|
546
|
+
elif side == 'right':
|
|
547
|
+
disp_momenta = np.linspace(
|
|
548
|
+
self_energy.center_wavevector, mome_max,
|
|
549
|
+
len(self.angles)
|
|
550
|
+
)
|
|
551
|
+
else:
|
|
552
|
+
# Fallback: no valid side, use full range
|
|
553
|
+
disp_momenta = full_disp_momenta
|
|
554
|
+
else:
|
|
555
|
+
# 'full' or 'domain' for SpectralLinear
|
|
556
|
+
disp_momenta = full_disp_momenta
|
|
557
|
+
|
|
558
|
+
# --- Robust parameter checks before computing base_disp ---
|
|
559
|
+
if spec_class == "SpectralLinear":
|
|
560
|
+
fermi_vel = getattr(
|
|
561
|
+
self_energy, "fermi_velocity", None
|
|
562
|
+
)
|
|
563
|
+
fermi_k = getattr(
|
|
564
|
+
self_energy, "fermi_wavevector", None
|
|
565
|
+
)
|
|
566
|
+
if fermi_vel is None or fermi_k is None:
|
|
567
|
+
missing = []
|
|
568
|
+
if fermi_vel is None:
|
|
569
|
+
missing.append("fermi_velocity")
|
|
570
|
+
if fermi_k is None:
|
|
571
|
+
missing.append("fermi_wavevector")
|
|
572
|
+
raise TypeError(
|
|
573
|
+
"Cannot plot bare dispersion for "
|
|
574
|
+
"SpectralLinear: "
|
|
575
|
+
f"{', '.join(missing)} is None."
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
base_disp = (
|
|
579
|
+
fermi_vel * (disp_momenta - fermi_k)
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
else: # SpectralQuadratic
|
|
583
|
+
bare_mass = getattr(
|
|
584
|
+
self_energy, "bare_mass", None
|
|
585
|
+
)
|
|
586
|
+
center_k = getattr(
|
|
587
|
+
self_energy, "center_wavevector", None
|
|
588
|
+
)
|
|
589
|
+
fermi_k = getattr(
|
|
590
|
+
self_energy, "fermi_wavevector", None
|
|
591
|
+
)
|
|
433
592
|
|
|
593
|
+
missing = []
|
|
594
|
+
if bare_mass is None:
|
|
595
|
+
missing.append("bare_mass")
|
|
596
|
+
if center_k is None:
|
|
597
|
+
missing.append("center_wavevector")
|
|
598
|
+
if fermi_k is None:
|
|
599
|
+
missing.append("fermi_wavevector")
|
|
600
|
+
|
|
601
|
+
if missing:
|
|
602
|
+
raise TypeError(
|
|
603
|
+
"Cannot plot bare dispersion for "
|
|
604
|
+
"SpectralQuadratic: "
|
|
605
|
+
f"{', '.join(missing)} is None."
|
|
606
|
+
)
|
|
607
|
+
|
|
608
|
+
dk = disp_momenta - center_k
|
|
609
|
+
base_disp = (
|
|
610
|
+
pref * (dk ** 2 - fermi_k ** 2) / bare_mass
|
|
611
|
+
)
|
|
612
|
+
# --- end parameter checks and base_disp construction ---
|
|
613
|
+
|
|
614
|
+
if ordinate == 'electron_energy':
|
|
615
|
+
disp_vals = base_disp
|
|
616
|
+
else: # kinetic energy
|
|
617
|
+
disp_vals = base_disp + self.hnuminphi
|
|
618
|
+
|
|
619
|
+
band_label = getattr(self_energy, "label", None)
|
|
620
|
+
if band_label is not None:
|
|
621
|
+
band_label = f"{band_label} (bare)"
|
|
622
|
+
|
|
623
|
+
ax.plot(
|
|
624
|
+
disp_momenta, disp_vals,
|
|
625
|
+
label=band_label,
|
|
626
|
+
linestyle='--',
|
|
627
|
+
color=line_color,
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
handles, labels = ax.get_legend_handles_labels()
|
|
631
|
+
if any(labels):
|
|
632
|
+
ax.legend()
|
|
434
633
|
|
|
634
|
+
ax.set_ylim(y_lims)
|
|
635
|
+
|
|
636
|
+
plt.colorbar(mesh, ax=ax, label='counts (-)')
|
|
637
|
+
return fig
|
|
638
|
+
|
|
435
639
|
@add_fig_kwargs
|
|
436
640
|
def fit_fermi_edge(self, hnuminphi_guess, background_guess=0.0,
|
|
437
641
|
integrated_weight_guess=1.0, angle_min=-np.inf,
|
|
@@ -1233,14 +1437,25 @@ class MDCs:
|
|
|
1233
1437
|
label = getattr(dist, 'label', str(dist))
|
|
1234
1438
|
individual_labels.append(label)
|
|
1235
1439
|
|
|
1236
|
-
# ---- collect parameters for this distribution
|
|
1440
|
+
# ---- collect parameters for this distribution
|
|
1237
1441
|
# (Aggregated over slices)
|
|
1238
1442
|
cls = getattr(dist, 'class_name', None)
|
|
1239
1443
|
wanted = param_spec.get(cls, ())
|
|
1240
1444
|
|
|
1241
1445
|
# ensure dicts exist
|
|
1242
1446
|
label_bucket = aggregated_properties.setdefault(label, {})
|
|
1243
|
-
class_bucket = label_bucket.setdefault(
|
|
1447
|
+
class_bucket = label_bucket.setdefault(
|
|
1448
|
+
cls, {'label': label, '_class': cls}
|
|
1449
|
+
)
|
|
1450
|
+
|
|
1451
|
+
# store center_wavevector (scalar) for SpectralQuadratic
|
|
1452
|
+
if (
|
|
1453
|
+
cls == 'SpectralQuadratic'
|
|
1454
|
+
and hasattr(dist, 'center_wavevector')
|
|
1455
|
+
):
|
|
1456
|
+
class_bucket.setdefault(
|
|
1457
|
+
'center_wavevector', dist.center_wavevector
|
|
1458
|
+
)
|
|
1244
1459
|
|
|
1245
1460
|
# ensure keys for both values and sigmas
|
|
1246
1461
|
for pname in wanted:
|
|
@@ -1560,8 +1775,8 @@ class MDCs:
|
|
|
1560
1775
|
return final_result
|
|
1561
1776
|
|
|
1562
1777
|
|
|
1563
|
-
def expose_parameters(self, select_label, fermi_wavevector=None,
|
|
1564
|
-
|
|
1778
|
+
def expose_parameters(self, select_label, fermi_wavevector=None,
|
|
1779
|
+
fermi_velocity=None, bare_mass=None, side=None):
|
|
1565
1780
|
r"""
|
|
1566
1781
|
Select and return fitted parameters for a given component label, plus a
|
|
1567
1782
|
flat export dictionary containing values **and** 1σ uncertainties.
|
|
@@ -1575,7 +1790,8 @@ class MDCs:
|
|
|
1575
1790
|
fermi_velocity : float, optional
|
|
1576
1791
|
Optional Fermi velocity to include.
|
|
1577
1792
|
bare_mass : float, optional
|
|
1578
|
-
Optional bare mass to include (used for SpectralQuadratic
|
|
1793
|
+
Optional bare mass to include (used for SpectralQuadratic
|
|
1794
|
+
dispersions).
|
|
1579
1795
|
side : {'left','right'}, optional
|
|
1580
1796
|
Optional side selector for SpectralQuadratic dispersions.
|
|
1581
1797
|
|
|
@@ -1588,30 +1804,41 @@ class MDCs:
|
|
|
1588
1804
|
label : str
|
|
1589
1805
|
Label of the selected distribution.
|
|
1590
1806
|
selected_properties : dict or list of dict
|
|
1591
|
-
Nested dictionary (or list thereof) containing <param> and
|
|
1592
|
-
<param>_sigma arrays.
|
|
1807
|
+
Nested dictionary (or list thereof) containing <param> and
|
|
1808
|
+
<param>_sigma arrays. For SpectralQuadratic components, a
|
|
1809
|
+
scalar `center_wavevector` is also present.
|
|
1593
1810
|
exported_parameters : dict
|
|
1594
|
-
Flat dictionary of parameters and their uncertainties, plus
|
|
1595
|
-
Fermi quantities and `side`.
|
|
1811
|
+
Flat dictionary of parameters and their uncertainties, plus
|
|
1812
|
+
optional Fermi quantities and `side`. For SpectralQuadratic
|
|
1813
|
+
components, `center_wavevector` is included and taken directly
|
|
1814
|
+
from the fitted distribution.
|
|
1596
1815
|
"""
|
|
1597
1816
|
|
|
1598
1817
|
if self._ekin_range is None:
|
|
1599
|
-
raise AttributeError(
|
|
1818
|
+
raise AttributeError(
|
|
1819
|
+
"ekin_range not yet set. Run `.fit_selection()` first."
|
|
1820
|
+
)
|
|
1600
1821
|
|
|
1601
1822
|
store = getattr(self, "_individual_properties", None)
|
|
1602
1823
|
if not store or select_label not in store:
|
|
1603
|
-
all_labels = (sorted(store.keys())
|
|
1824
|
+
all_labels = (sorted(store.keys())
|
|
1825
|
+
if isinstance(store, dict) else [])
|
|
1604
1826
|
raise ValueError(
|
|
1605
|
-
f"Label '{select_label}' not found in available labels:
|
|
1827
|
+
f"Label '{select_label}' not found in available labels: "
|
|
1828
|
+
f"{all_labels}"
|
|
1606
1829
|
)
|
|
1607
1830
|
|
|
1608
|
-
# Convert lists → numpy arrays within the selected label’s classes
|
|
1831
|
+
# Convert lists → numpy arrays within the selected label’s classes.
|
|
1832
|
+
# Keep scalar center_wavevector as a scalar.
|
|
1609
1833
|
per_class_dicts = []
|
|
1610
1834
|
for cls, bucket in store[select_label].items():
|
|
1611
1835
|
dct = {}
|
|
1612
1836
|
for k, v in bucket.items():
|
|
1613
1837
|
if k in ("label", "_class"):
|
|
1614
1838
|
dct[k] = v
|
|
1839
|
+
elif k == "center_wavevector":
|
|
1840
|
+
# keep scalar as-is, do not wrap in np.asarray
|
|
1841
|
+
dct[k] = v
|
|
1615
1842
|
else:
|
|
1616
1843
|
dct[k] = np.asarray(v)
|
|
1617
1844
|
per_class_dicts.append(dct)
|
|
@@ -1628,20 +1855,23 @@ class MDCs:
|
|
|
1628
1855
|
"side": side,
|
|
1629
1856
|
}
|
|
1630
1857
|
|
|
1631
|
-
# Collect parameters without prefixing by class
|
|
1858
|
+
# Collect parameters without prefixing by class. This will also include
|
|
1859
|
+
# center_wavevector from the fitted SpectralQuadratic class, and since
|
|
1860
|
+
# there is no function argument with that name, it cannot be overridden.
|
|
1632
1861
|
if isinstance(selected_properties, dict):
|
|
1633
1862
|
for key, val in selected_properties.items():
|
|
1634
1863
|
if key not in ("label", "_class"):
|
|
1635
1864
|
exported_parameters[key] = val
|
|
1636
1865
|
else:
|
|
1637
|
-
# If multiple classes, merge sequentially
|
|
1866
|
+
# If multiple classes, merge sequentially
|
|
1867
|
+
# (last overwrites same-name keys).
|
|
1638
1868
|
for cls_bucket in selected_properties:
|
|
1639
1869
|
for key, val in cls_bucket.items():
|
|
1640
1870
|
if key not in ("label", "_class"):
|
|
1641
1871
|
exported_parameters[key] = val
|
|
1642
1872
|
|
|
1643
|
-
return self._ekin_range, self.hnuminphi, select_label,
|
|
1644
|
-
|
|
1873
|
+
return (self._ekin_range, self.hnuminphi, select_label,
|
|
1874
|
+
selected_properties, exported_parameters)
|
|
1645
1875
|
|
|
1646
1876
|
|
|
1647
1877
|
class SelfEnergy:
|
|
@@ -1697,6 +1927,7 @@ class SelfEnergy:
|
|
|
1697
1927
|
self._peak_sigma = self._properties.get("peak_sigma")
|
|
1698
1928
|
self._broadening = self._properties.get("broadening")
|
|
1699
1929
|
self._broadening_sigma = self._properties.get("broadening_sigma")
|
|
1930
|
+
self._center_wavevector = self._properties.get("center_wavevector")
|
|
1700
1931
|
|
|
1701
1932
|
# lazy caches
|
|
1702
1933
|
self._peak_positions = None
|
|
@@ -1763,6 +1994,7 @@ class SelfEnergy:
|
|
|
1763
1994
|
self._peak_positions = None
|
|
1764
1995
|
self._real = None
|
|
1765
1996
|
self._real_sigma = None
|
|
1997
|
+
self._mdc_maxima = None
|
|
1766
1998
|
|
|
1767
1999
|
@property
|
|
1768
2000
|
def fermi_wavevector(self):
|
|
@@ -1838,6 +2070,7 @@ class SelfEnergy:
|
|
|
1838
2070
|
# invalidate dependent cache
|
|
1839
2071
|
self._peak_positions = None
|
|
1840
2072
|
self._real = None
|
|
2073
|
+
self._mdc_maxima = None
|
|
1841
2074
|
|
|
1842
2075
|
@property
|
|
1843
2076
|
def peak_sigma(self):
|
|
@@ -1870,6 +2103,11 @@ class SelfEnergy:
|
|
|
1870
2103
|
self._properties["broadening_sigma"] = x
|
|
1871
2104
|
self._imag_sigma = None
|
|
1872
2105
|
|
|
2106
|
+
@property
|
|
2107
|
+
def center_wavevector(self):
|
|
2108
|
+
"""Read-only center wavevector (SpectralQuadratic, if present)."""
|
|
2109
|
+
return self._center_wavevector
|
|
2110
|
+
|
|
1873
2111
|
# ---------------- derived outputs ----------------
|
|
1874
2112
|
@property
|
|
1875
2113
|
def peak_positions(self):
|
|
@@ -1936,7 +2174,7 @@ class SelfEnergy:
|
|
|
1936
2174
|
"(SpectralLinear): set `fermi_velocity` first.")
|
|
1937
2175
|
self._imag_sigma = np.abs(self._fermi_velocity) * \
|
|
1938
2176
|
np.sqrt(self._ekin_range / pref) * self._broadening_sigma
|
|
1939
|
-
else:
|
|
2177
|
+
else:
|
|
1940
2178
|
if self._bare_mass is None:
|
|
1941
2179
|
raise AttributeError("Cannot compute `imag_sigma` "
|
|
1942
2180
|
"(SpectralQuadratic): set `bare_mass` first.")
|
|
@@ -1987,6 +2225,178 @@ class SelfEnergy:
|
|
|
1987
2225
|
* np.abs(self.peak_positions / self._bare_mass)
|
|
1988
2226
|
return self._real_sigma
|
|
1989
2227
|
|
|
2228
|
+
@property
|
|
2229
|
+
def mdc_maxima(self):
|
|
2230
|
+
"""
|
|
2231
|
+
MDC maxima (lazy).
|
|
2232
|
+
|
|
2233
|
+
SpectralLinear:
|
|
2234
|
+
identical to peak_positions
|
|
2235
|
+
|
|
2236
|
+
SpectralQuadratic:
|
|
2237
|
+
peak_positions + center_wavevector
|
|
2238
|
+
"""
|
|
2239
|
+
if getattr(self, "_mdc_maxima", None) is None:
|
|
2240
|
+
if self.peak_positions is None:
|
|
2241
|
+
return None
|
|
2242
|
+
|
|
2243
|
+
if self._class == "SpectralLinear":
|
|
2244
|
+
self._mdc_maxima = self.peak_positions
|
|
2245
|
+
elif self._class == "SpectralQuadratic":
|
|
2246
|
+
self._mdc_maxima = (
|
|
2247
|
+
self.peak_positions + self._center_wavevector
|
|
2248
|
+
)
|
|
2249
|
+
|
|
2250
|
+
return self._mdc_maxima
|
|
2251
|
+
|
|
2252
|
+
def _se_legend_labels(self):
|
|
2253
|
+
"""Return (real_label, imag_label) for legend with safe subscripts."""
|
|
2254
|
+
se_label = getattr(self, "label", None)
|
|
2255
|
+
|
|
2256
|
+
if se_label is None:
|
|
2257
|
+
real_label = r"$\Sigma'(E)$"
|
|
2258
|
+
imag_label = r"$-\Sigma''(E)$"
|
|
2259
|
+
return real_label, imag_label
|
|
2260
|
+
|
|
2261
|
+
safe_label = str(se_label).replace("_", r"\_")
|
|
2262
|
+
|
|
2263
|
+
# If the label is empty after conversion, fall back
|
|
2264
|
+
if safe_label == "":
|
|
2265
|
+
real_label = r"$\Sigma'(E)$"
|
|
2266
|
+
imag_label = r"$-\Sigma''(E)$"
|
|
2267
|
+
return real_label, imag_label
|
|
2268
|
+
|
|
2269
|
+
real_label = rf"$\Sigma_{{\mathrm{{{safe_label}}}}}'(E)$"
|
|
2270
|
+
imag_label = rf"$-\Sigma_{{\mathrm{{{safe_label}}}}}''(E)$"
|
|
2271
|
+
|
|
2272
|
+
return real_label, imag_label
|
|
2273
|
+
|
|
2274
|
+
@add_fig_kwargs
|
|
2275
|
+
def plot_real(self, ax=None, **kwargs):
|
|
2276
|
+
r"""Plot the real part Σ' of the self-energy as a function of E-μ.
|
|
2277
|
+
|
|
2278
|
+
Parameters
|
|
2279
|
+
----------
|
|
2280
|
+
ax : Matplotlib-Axes or None
|
|
2281
|
+
Axis to plot on. Created if not provided by the user.
|
|
2282
|
+
**kwargs :
|
|
2283
|
+
Additional keyword arguments passed to ``ax.errorbar``.
|
|
2284
|
+
|
|
2285
|
+
Returns
|
|
2286
|
+
-------
|
|
2287
|
+
fig : Matplotlib-Figure
|
|
2288
|
+
Figure containing the Σ'(E) plot.
|
|
2289
|
+
"""
|
|
2290
|
+
|
|
2291
|
+
ax, fig, plt = get_ax_fig_plt(ax=ax)
|
|
2292
|
+
|
|
2293
|
+
x = self.enel_range
|
|
2294
|
+
y = self.real
|
|
2295
|
+
y_sigma = self.real_sigma
|
|
2296
|
+
|
|
2297
|
+
real_label, _ = self._se_legend_labels()
|
|
2298
|
+
kwargs.setdefault("label", real_label)
|
|
2299
|
+
|
|
2300
|
+
if y_sigma is not None:
|
|
2301
|
+
if np.isnan(y_sigma).any():
|
|
2302
|
+
print(
|
|
2303
|
+
"Warning: some Σ'(E) uncertainty values are missing. "
|
|
2304
|
+
"Error bars omitted at those energies."
|
|
2305
|
+
)
|
|
2306
|
+
kwargs.setdefault("yerr", stdv * y_sigma)
|
|
2307
|
+
|
|
2308
|
+
ax.errorbar(x, y, **kwargs)
|
|
2309
|
+
ax.set_xlabel(r"$E-\mu$ (eV)")
|
|
2310
|
+
ax.set_ylabel(r"$\Sigma'(E)$ (eV)")
|
|
2311
|
+
ax.legend()
|
|
2312
|
+
|
|
2313
|
+
return fig
|
|
2314
|
+
|
|
2315
|
+
@add_fig_kwargs
|
|
2316
|
+
def plot_imag(self, ax=None, **kwargs):
|
|
2317
|
+
r"""Plot the imaginary part -Σ'' of the self-energy vs. E-μ.
|
|
2318
|
+
|
|
2319
|
+
Parameters
|
|
2320
|
+
----------
|
|
2321
|
+
ax : Matplotlib-Axes or None
|
|
2322
|
+
Axis to plot on. Created if not provided by the user.
|
|
2323
|
+
**kwargs :
|
|
2324
|
+
Additional keyword arguments passed to ``ax.errorbar``.
|
|
2325
|
+
|
|
2326
|
+
Returns
|
|
2327
|
+
-------
|
|
2328
|
+
fig : Matplotlib-Figure
|
|
2329
|
+
Figure containing the -Σ''(E) plot.
|
|
2330
|
+
"""
|
|
2331
|
+
|
|
2332
|
+
ax, fig, plt = get_ax_fig_plt(ax=ax)
|
|
2333
|
+
|
|
2334
|
+
x = self.enel_range
|
|
2335
|
+
y = self.imag
|
|
2336
|
+
y_sigma = self.imag_sigma
|
|
2337
|
+
|
|
2338
|
+
_, imag_label = self._se_legend_labels()
|
|
2339
|
+
kwargs.setdefault("label", imag_label)
|
|
2340
|
+
|
|
2341
|
+
if y_sigma is not None:
|
|
2342
|
+
if np.isnan(y_sigma).any():
|
|
2343
|
+
print(
|
|
2344
|
+
"Warning: some -Σ''(E) uncertainty values are missing. "
|
|
2345
|
+
"Error bars omitted at those energies."
|
|
2346
|
+
)
|
|
2347
|
+
kwargs.setdefault("yerr", stdv * y_sigma)
|
|
2348
|
+
|
|
2349
|
+
ax.errorbar(x, y, **kwargs)
|
|
2350
|
+
ax.set_xlabel(r"$E-\mu$ (eV)")
|
|
2351
|
+
ax.set_ylabel(r"$-\Sigma''(E)$ (eV)")
|
|
2352
|
+
ax.legend()
|
|
2353
|
+
|
|
2354
|
+
return fig
|
|
2355
|
+
|
|
2356
|
+
@add_fig_kwargs
|
|
2357
|
+
def plot_both(self, ax=None, **kwargs):
|
|
2358
|
+
r"""Plot Σ'(E) and -Σ''(E) vs. E-μ on the same axis."""
|
|
2359
|
+
|
|
2360
|
+
ax, fig, plt = get_ax_fig_plt(ax=ax)
|
|
2361
|
+
|
|
2362
|
+
x = self.enel_range
|
|
2363
|
+
real = self.real
|
|
2364
|
+
imag = self.imag
|
|
2365
|
+
real_sigma = self.real_sigma
|
|
2366
|
+
imag_sigma = self.imag_sigma
|
|
2367
|
+
|
|
2368
|
+
real_label, imag_label = self._se_legend_labels()
|
|
2369
|
+
|
|
2370
|
+
# --- plot Σ'
|
|
2371
|
+
kw_real = dict(kwargs)
|
|
2372
|
+
if real_sigma is not None:
|
|
2373
|
+
if np.isnan(real_sigma).any():
|
|
2374
|
+
print(
|
|
2375
|
+
"Warning: some Σ'(E) uncertainty values are missing. "
|
|
2376
|
+
"Error bars omitted at those energies."
|
|
2377
|
+
)
|
|
2378
|
+
kw_real.setdefault("yerr", stdv * real_sigma)
|
|
2379
|
+
kw_real.setdefault("label", real_label)
|
|
2380
|
+
ax.errorbar(x, real, **kw_real)
|
|
2381
|
+
|
|
2382
|
+
# --- plot -Σ''
|
|
2383
|
+
kw_imag = dict(kwargs)
|
|
2384
|
+
if imag_sigma is not None:
|
|
2385
|
+
if np.isnan(imag_sigma).any():
|
|
2386
|
+
print(
|
|
2387
|
+
"Warning: some -Σ''(E) uncertainty values are missing. "
|
|
2388
|
+
"Error bars omitted at those energies."
|
|
2389
|
+
)
|
|
2390
|
+
kw_imag.setdefault("yerr", stdv * imag_sigma)
|
|
2391
|
+
kw_imag.setdefault("label", imag_label)
|
|
2392
|
+
ax.errorbar(x, imag, **kw_imag)
|
|
2393
|
+
|
|
2394
|
+
ax.set_xlabel(r"$E-\mu$ (eV)")
|
|
2395
|
+
ax.set_ylabel(r"$\Sigma'(E),\ -\Sigma''(E)$ (eV)")
|
|
2396
|
+
ax.legend()
|
|
2397
|
+
|
|
2398
|
+
return fig
|
|
2399
|
+
|
|
1990
2400
|
|
|
1991
2401
|
class CreateSelfEnergies:
|
|
1992
2402
|
r"""
|
|
@@ -2063,5 +2473,4 @@ class CreateSelfEnergies:
|
|
|
2063
2473
|
r"""
|
|
2064
2474
|
Return a {label: self_energy} dictionary for convenient access.
|
|
2065
2475
|
"""
|
|
2066
|
-
return {se.label: se for se in self.self_energies}
|
|
2067
|
-
|
|
2476
|
+
return {se.label: se for se in self.self_energies}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: xarpes
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Extraction from angle resolved photoemission spectra
|
|
5
5
|
Author: xARPES Developers
|
|
6
6
|
Requires-Python: >=3.7.0
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
License-File: LICENSE
|
|
10
11
|
Requires-Dist: igor2>=0.5.8
|
|
11
12
|
Requires-Dist: jupyterlab
|
|
12
13
|
Requires-Dist: jupytext
|
|
@@ -15,7 +16,8 @@ Requires-Dist: numpy
|
|
|
15
16
|
Requires-Dist: scipy
|
|
16
17
|
Requires-Dist: lmfit
|
|
17
18
|
Requires-Dist: pyqt5
|
|
18
|
-
Requires-Dist: ipympl
|
|
19
|
+
Requires-Dist: ipympl>=0.9.3
|
|
20
|
+
Requires-Dist: ipywidgets>=8.1.5
|
|
19
21
|
Requires-Dist: ipykernel<6.32.0
|
|
20
22
|
Project-URL: Documentation, https://xarpes.github.io
|
|
21
23
|
|
|
@@ -31,7 +33,7 @@ This project is currently undergoing **beta testing**. Some of the functionaliti
|
|
|
31
33
|
|
|
32
34
|
# Contributing
|
|
33
35
|
|
|
34
|
-
Contributions to the code are most welcome. xARPES is intended to co-develop alongside the increasing complexity of experimental ARPES data sets. Contributions can be made by forking the code and creating a pull request. Importing of file formats from different beamlines is particularly encouraged.
|
|
36
|
+
Contributions to the code are most welcome. xARPES is intended to co-develop alongside the increasing complexity of experimental ARPES data sets. Contributions can be made by forking the code and creating a pull request. Importing of file formats from different beamlines is particularly encouraged. Files useful for developers can be found in `/dev_tools`, such as the `Rmd2py.py` script for the development of examples.
|
|
35
37
|
|
|
36
38
|
# Installation
|
|
37
39
|
|
|
@@ -40,6 +42,8 @@ xARPES installation can be divided into graphical package manager instructions,
|
|
|
40
42
|
- via conda-forge, out-of-the-box or editable installation, sourcing the [conda-forge package](https://anaconda.org/conda-forge/xarpes).
|
|
41
43
|
- via Pip, out-of-the-box or editable installation, sourcing the [PyPI package](https://pypi.org/project/xarpes).
|
|
42
44
|
|
|
45
|
+
We strongly recommend installing xARPES in a (conda/pip) virtual environment, and to activate the environment each time before activating xARPES.
|
|
46
|
+
|
|
43
47
|
## Graphical package manager installation
|
|
44
48
|
|
|
45
49
|
Most IDEs and scientific Python distributions include a GUI-based package manager.
|
|
@@ -82,11 +86,13 @@ Example for Linux:
|
|
|
82
86
|
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
|
|
83
87
|
bash Miniconda3-latest-Linux-x86_64.sh
|
|
84
88
|
|
|
85
|
-
Create and activate
|
|
89
|
+
Answer `y` to questions. Create and activate a new environment:
|
|
86
90
|
|
|
87
|
-
conda create -n <my_env> -c
|
|
91
|
+
conda create -n <my_env> -c conda-forge
|
|
88
92
|
conda activate <my_env>
|
|
89
93
|
|
|
94
|
+
Where `<my_env>` must be replaced by your desired name. Package compatibility ssues may arise if conda installs from different channels. This can be prevented by appending `--strict-channel-priority` to the creation command.
|
|
95
|
+
|
|
90
96
|
### Installing xARPES
|
|
91
97
|
|
|
92
98
|
#### Option A — Out-of-the-box installation (from conda-forge)
|
|
@@ -116,8 +122,8 @@ Install venv if necessary:
|
|
|
116
122
|
|
|
117
123
|
Create and activate a virtual environment:
|
|
118
124
|
|
|
119
|
-
python3 -m venv <
|
|
120
|
-
source <
|
|
125
|
+
python3 -m venv <my_env>
|
|
126
|
+
source <my_env>/bin/activate
|
|
121
127
|
|
|
122
128
|
Upgrade pip:
|
|
123
129
|
|
|
@@ -146,6 +152,8 @@ After installation of xARPES, the `examples/` folder can be downloaded to the cu
|
|
|
146
152
|
|
|
147
153
|
python -c "import xarpes; xarpes.download_examples()"
|
|
148
154
|
|
|
155
|
+
This attempts to download the examples from the version corresponding encountered in `__init__.py`. If no corresponding tagged version can be downloaded, the code attempts to download the latest examples instead.
|
|
156
|
+
|
|
149
157
|
# Execution
|
|
150
158
|
|
|
151
159
|
It is recommended to use JupyterLab to analyse data. JupyterLab is launched using:
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
xarpes/__init__.py,sha256=c_dJwE9MtE67k6ms-2ljYZxPwTHEHDUDtOiI_g6s9QE,125
|
|
2
|
+
xarpes/constants.py,sha256=vQxxFeCdGIxMpdh5XGbeRbn7-HF1d5snWkR09d8spGc,587
|
|
3
|
+
xarpes/distributions.py,sha256=svzhvf994_5gndJA1M04SW4MVfHEVwiAumbhO5Jj22s,23434
|
|
4
|
+
xarpes/functions.py,sha256=4s2atkWyPUb1ipJApRDVMsow_47kt25wvSwLtfqD2fs,14261
|
|
5
|
+
xarpes/plotting.py,sha256=3nwq-6q3i3hUG_tMv6y-62v2FceB2LuayDeE_fSdUr0,6499
|
|
6
|
+
xarpes/spectral.py,sha256=2AmqiyAk3xYJpUj1zPwbdA1Q62mGAVvXJAWmyiGHb6s,93908
|
|
7
|
+
xarpes-0.4.0.dist-info/entry_points.txt,sha256=917UR-cqFTMMI_vMqIbk7boYSuFX_zHwQlXKcj9vlCE,79
|
|
8
|
+
xarpes-0.4.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
9
|
+
xarpes-0.4.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
10
|
+
xarpes-0.4.0.dist-info/METADATA,sha256=cBUTzdc-CF8QCIag1q7kgSISydC2o1Mo4ZlAHMe87n0,6526
|
|
11
|
+
xarpes-0.4.0.dist-info/RECORD,,
|
xarpes-0.3.3.dist-info/RECORD
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
xarpes/__init__.py,sha256=fx6uw8FrJ2VesH-h-V0mQiUciW7oka3RL8jNgO0mtbs,125
|
|
2
|
-
xarpes/constants.py,sha256=vQxxFeCdGIxMpdh5XGbeRbn7-HF1d5snWkR09d8spGc,587
|
|
3
|
-
xarpes/distributions.py,sha256=svzhvf994_5gndJA1M04SW4MVfHEVwiAumbhO5Jj22s,23434
|
|
4
|
-
xarpes/functions.py,sha256=gE76z-Y9UI1KNUUtADyLziLU1UJ43E1CHLDi_khj0bc,12007
|
|
5
|
-
xarpes/plotting.py,sha256=W-5WaKjBtg8PIxTypqja2R29mgWkQ844lgRWci0nhn0,5679
|
|
6
|
-
xarpes/spectral.py,sha256=ze6rPKrOg5jMoTL3Pv2MNdDGu5wOMzJJ2OF_iyrslRc,78580
|
|
7
|
-
xarpes-0.3.3.dist-info/entry_points.txt,sha256=917UR-cqFTMMI_vMqIbk7boYSuFX_zHwQlXKcj9vlCE,79
|
|
8
|
-
xarpes-0.3.3.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
9
|
-
xarpes-0.3.3.dist-info/WHEEL,sha256=jPMR_Dzkc4X4icQtmz81lnNY_kAsfog7ry7qoRvYLXw,81
|
|
10
|
-
xarpes-0.3.3.dist-info/METADATA,sha256=sK0m3UVDAhHi5a5K29zmrz8eTBZLCvYqPkWLAXnnk7s,5741
|
|
11
|
-
xarpes-0.3.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|