xarpes 0.3.3__py3-none-any.whl → 0.3.4__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 +2 -2
- xarpes/functions.py +102 -48
- xarpes/spectral.py +171 -63
- {xarpes-0.3.3.dist-info → xarpes-0.3.4.dist-info}/METADATA +15 -7
- xarpes-0.3.4.dist-info/RECORD +11 -0
- {xarpes-0.3.3.dist-info → xarpes-0.3.4.dist-info}/WHEEL +1 -1
- xarpes-0.3.3.dist-info/RECORD +0 -11
- {xarpes-0.3.3.dist-info → xarpes-0.3.4.dist-info}/entry_points.txt +0 -0
- {xarpes-0.3.3.dist-info → xarpes-0.3.4.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/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"""
|
|
@@ -349,24 +349,17 @@ class BandMap:
|
|
|
349
349
|
|
|
350
350
|
@add_fig_kwargs
|
|
351
351
|
def plot(self, abscissa='momentum', ordinate='electron_energy',
|
|
352
|
-
|
|
352
|
+
self_energies=None, ax=None, markersize=None, **kwargs):
|
|
353
353
|
r"""
|
|
354
354
|
Plot the band map. Optionally attach a collection of self-energies,
|
|
355
355
|
e.g. a CreateSelfEnergies instance or any iterable of self-energy
|
|
356
356
|
objects. They are stored on `self` for later overlay plotting.
|
|
357
|
-
"""
|
|
358
357
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
|
358
|
+
When self-energies are present and ``abscissa='momentum'``, their
|
|
359
|
+
MDC maxima are overlaid with 95 % confidence intervals.
|
|
360
|
+
"""
|
|
361
|
+
import warnings
|
|
368
362
|
|
|
369
|
-
# Validate options early
|
|
370
363
|
valid_abscissa = ('angle', 'momentum')
|
|
371
364
|
valid_ordinate = ('kinetic_energy', 'electron_energy')
|
|
372
365
|
|
|
@@ -381,52 +374,109 @@ class BandMap:
|
|
|
381
374
|
f"Valid options: {valid_ordinate}"
|
|
382
375
|
)
|
|
383
376
|
|
|
377
|
+
# Optionally store self-energies on the instance
|
|
378
|
+
if self_energies is not None:
|
|
379
|
+
|
|
380
|
+
# MDC maxima are defined in momentum space, not angle space
|
|
381
|
+
if abscissa == 'angle' and isinstance(
|
|
382
|
+
self_energies, (list, tuple, CreateSelfEnergies)
|
|
383
|
+
):
|
|
384
|
+
raise ValueError( "MDC maxima cannot be plotted against "
|
|
385
|
+
"angles; they are defined in momentum space. Use " \
|
|
386
|
+
"abscissa='momentum' when passing a list of self-energies.")
|
|
387
|
+
|
|
388
|
+
if not isinstance(self_energies, CreateSelfEnergies):
|
|
389
|
+
self_energies = CreateSelfEnergies(self_energies)
|
|
390
|
+
|
|
391
|
+
self._self_energies = self_energies
|
|
392
|
+
elif not hasattr(self, "_self_energies"):
|
|
393
|
+
self._self_energies = None
|
|
394
|
+
|
|
384
395
|
ax, fig, plt = get_ax_fig_plt(ax=ax)
|
|
396
|
+
# Below, **kwargs is
|
|
385
397
|
|
|
386
398
|
Angl, Ekin = np.meshgrid(self.angles, self.ekin)
|
|
387
|
-
|
|
388
|
-
|
|
399
|
+
|
|
389
400
|
if abscissa == 'angle':
|
|
390
401
|
ax.set_xlabel('Angle ($\\degree$)')
|
|
391
402
|
if ordinate == 'kinetic_energy':
|
|
392
|
-
mesh = ax.pcolormesh(
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
)
|
|
403
|
+
mesh = ax.pcolormesh(Angl, Ekin, self.intensities,
|
|
404
|
+
shading='auto', cmap=plt.get_cmap('bone').reversed(),
|
|
405
|
+
**kwargs)
|
|
396
406
|
ax.set_ylabel('$E_{\\mathrm{kin}}$ (eV)')
|
|
397
407
|
elif ordinate == 'electron_energy':
|
|
398
408
|
Enel = Ekin - self.hnuminphi
|
|
399
|
-
mesh = ax.pcolormesh(
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
)
|
|
409
|
+
mesh = ax.pcolormesh(Angl, Enel, self.intensities,
|
|
410
|
+
shading='auto', cmap=plt.get_cmap('bone').reversed(),
|
|
411
|
+
**kwargs)
|
|
403
412
|
ax.set_ylabel('$E-\\mu$ (eV)')
|
|
404
413
|
|
|
405
414
|
elif abscissa == 'momentum':
|
|
406
|
-
Mome = np.sqrt(Ekin / pref) * np.sin(Angl * dtor)
|
|
407
415
|
ax.set_xlabel(r'$k_{//}$ ($\mathrm{\AA}^{-1}$)')
|
|
408
|
-
if ordinate == 'kinetic_energy':
|
|
409
|
-
mesh = ax.pcolormesh(
|
|
410
|
-
Mome, Ekin, self.intensities, shading='auto',
|
|
411
|
-
cmap=plt.get_cmap('bone').reversed(), **kwargs
|
|
412
|
-
)
|
|
413
|
-
ax.set_ylabel('$E_{\\mathrm{kin}}$ (eV)')
|
|
414
|
-
elif ordinate == 'electron_energy':
|
|
415
|
-
Enel = Ekin - self.hnuminphi
|
|
416
|
-
mesh = ax.pcolormesh(
|
|
417
|
-
Mome, Enel, self.intensities, shading='auto',
|
|
418
|
-
cmap=plt.get_cmap('bone').reversed(), **kwargs
|
|
419
|
-
)
|
|
420
|
-
ax.set_ylabel('$E-\\mu$ (eV)')
|
|
421
416
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
417
|
+
with warnings.catch_warnings(record=True) as wlist:
|
|
418
|
+
warnings.filterwarnings("always",
|
|
419
|
+
message=("The input coordinates to pcolormesh are "
|
|
420
|
+
"interpreted as cell centers, but are not "
|
|
421
|
+
"monotonically increasing or decreasing."),
|
|
422
|
+
category=UserWarning)
|
|
423
|
+
|
|
424
|
+
Mome = np.sqrt(Ekin / pref) * np.sin(Angl * dtor)
|
|
425
|
+
|
|
426
|
+
if ordinate == 'kinetic_energy':
|
|
427
|
+
mesh = ax.pcolormesh(Mome, Ekin, self.intensities,
|
|
428
|
+
shading='auto', cmap=plt.get_cmap('bone').reversed(),
|
|
429
|
+
**kwargs)
|
|
430
|
+
ax.set_ylabel('$E_{\\mathrm{kin}}$ (eV)')
|
|
431
|
+
|
|
432
|
+
elif ordinate == 'electron_energy':
|
|
433
|
+
Enel = Ekin - self.hnuminphi
|
|
434
|
+
mesh = ax.pcolormesh(Mome, Enel, self.intensities,
|
|
435
|
+
shading='auto', cmap=plt.get_cmap('bone').reversed(),
|
|
436
|
+
**kwargs)
|
|
437
|
+
ax.set_ylabel('$E-\\mu$ (eV)')
|
|
438
|
+
|
|
439
|
+
if any("cell centers" in str(w.message) for w in wlist):
|
|
440
|
+
warnings.warn("Conversion from angle to momenta causes warping "
|
|
441
|
+
"of the cell centers. \n Cell edges of the "
|
|
442
|
+
"mesh plot may look irregular.", UserWarning,
|
|
443
|
+
stacklevel=2)
|
|
444
|
+
|
|
445
|
+
if abscissa == 'momentum' and self._self_energies is not None:
|
|
446
|
+
for self_energy in self._self_energies:
|
|
447
|
+
mdc_maxima = getattr(self_energy, "mdc_maxima", None)
|
|
448
|
+
|
|
449
|
+
# If this self-energy doesn't contain maxima, don't plot them
|
|
450
|
+
if mdc_maxima is None:
|
|
451
|
+
continue
|
|
452
|
+
|
|
453
|
+
peak_sigma = getattr(self_energy, "peak_positions_sigma",
|
|
454
|
+
None)
|
|
455
|
+
xerr = stdv * peak_sigma if peak_sigma is not None else None
|
|
456
|
+
|
|
457
|
+
if ordinate == 'kinetic_energy':
|
|
458
|
+
y_vals = self_energy.ekin_range
|
|
459
|
+
else: # electron energy
|
|
460
|
+
y_vals = self_energy.enel_range
|
|
461
|
+
|
|
462
|
+
x_vals = mdc_maxima
|
|
463
|
+
label = getattr(self_energy, "label", None)
|
|
464
|
+
|
|
465
|
+
if xerr is not None:
|
|
466
|
+
ax.errorbar(x_vals, y_vals, xerr=xerr, fmt='o',
|
|
467
|
+
linestyle='', label=label,
|
|
468
|
+
markersize=markersize)
|
|
469
|
+
else:
|
|
470
|
+
ax.plot(x_vals, y_vals, linestyle='', marker='o',
|
|
471
|
+
label=label, markersize=markersize)
|
|
472
|
+
|
|
473
|
+
handles, labels = ax.get_legend_handles_labels()
|
|
474
|
+
if any(labels):
|
|
475
|
+
ax.legend()
|
|
476
|
+
|
|
477
|
+
handles, labels = ax.get_legend_handles_labels()
|
|
478
|
+
if any(labels):
|
|
479
|
+
ax.legend()
|
|
430
480
|
|
|
431
481
|
plt.colorbar(mesh, ax=ax, label='counts (-)')
|
|
432
482
|
return fig
|
|
@@ -1233,14 +1283,25 @@ class MDCs:
|
|
|
1233
1283
|
label = getattr(dist, 'label', str(dist))
|
|
1234
1284
|
individual_labels.append(label)
|
|
1235
1285
|
|
|
1236
|
-
# ---- collect parameters for this distribution
|
|
1286
|
+
# ---- collect parameters for this distribution
|
|
1237
1287
|
# (Aggregated over slices)
|
|
1238
1288
|
cls = getattr(dist, 'class_name', None)
|
|
1239
1289
|
wanted = param_spec.get(cls, ())
|
|
1240
1290
|
|
|
1241
1291
|
# ensure dicts exist
|
|
1242
1292
|
label_bucket = aggregated_properties.setdefault(label, {})
|
|
1243
|
-
class_bucket = label_bucket.setdefault(
|
|
1293
|
+
class_bucket = label_bucket.setdefault(
|
|
1294
|
+
cls, {'label': label, '_class': cls}
|
|
1295
|
+
)
|
|
1296
|
+
|
|
1297
|
+
# store center_wavevector (scalar) for SpectralQuadratic
|
|
1298
|
+
if (
|
|
1299
|
+
cls == 'SpectralQuadratic'
|
|
1300
|
+
and hasattr(dist, 'center_wavevector')
|
|
1301
|
+
):
|
|
1302
|
+
class_bucket.setdefault(
|
|
1303
|
+
'center_wavevector', dist.center_wavevector
|
|
1304
|
+
)
|
|
1244
1305
|
|
|
1245
1306
|
# ensure keys for both values and sigmas
|
|
1246
1307
|
for pname in wanted:
|
|
@@ -1560,8 +1621,8 @@ class MDCs:
|
|
|
1560
1621
|
return final_result
|
|
1561
1622
|
|
|
1562
1623
|
|
|
1563
|
-
def expose_parameters(self, select_label, fermi_wavevector=None,
|
|
1564
|
-
|
|
1624
|
+
def expose_parameters(self, select_label, fermi_wavevector=None,
|
|
1625
|
+
fermi_velocity=None, bare_mass=None, side=None):
|
|
1565
1626
|
r"""
|
|
1566
1627
|
Select and return fitted parameters for a given component label, plus a
|
|
1567
1628
|
flat export dictionary containing values **and** 1σ uncertainties.
|
|
@@ -1575,7 +1636,8 @@ class MDCs:
|
|
|
1575
1636
|
fermi_velocity : float, optional
|
|
1576
1637
|
Optional Fermi velocity to include.
|
|
1577
1638
|
bare_mass : float, optional
|
|
1578
|
-
Optional bare mass to include (used for SpectralQuadratic
|
|
1639
|
+
Optional bare mass to include (used for SpectralQuadratic
|
|
1640
|
+
dispersions).
|
|
1579
1641
|
side : {'left','right'}, optional
|
|
1580
1642
|
Optional side selector for SpectralQuadratic dispersions.
|
|
1581
1643
|
|
|
@@ -1588,30 +1650,41 @@ class MDCs:
|
|
|
1588
1650
|
label : str
|
|
1589
1651
|
Label of the selected distribution.
|
|
1590
1652
|
selected_properties : dict or list of dict
|
|
1591
|
-
Nested dictionary (or list thereof) containing <param> and
|
|
1592
|
-
<param>_sigma arrays.
|
|
1653
|
+
Nested dictionary (or list thereof) containing <param> and
|
|
1654
|
+
<param>_sigma arrays. For SpectralQuadratic components, a
|
|
1655
|
+
scalar `center_wavevector` is also present.
|
|
1593
1656
|
exported_parameters : dict
|
|
1594
|
-
Flat dictionary of parameters and their uncertainties, plus
|
|
1595
|
-
Fermi quantities and `side`.
|
|
1657
|
+
Flat dictionary of parameters and their uncertainties, plus
|
|
1658
|
+
optional Fermi quantities and `side`. For SpectralQuadratic
|
|
1659
|
+
components, `center_wavevector` is included and taken directly
|
|
1660
|
+
from the fitted distribution.
|
|
1596
1661
|
"""
|
|
1597
1662
|
|
|
1598
1663
|
if self._ekin_range is None:
|
|
1599
|
-
raise AttributeError(
|
|
1664
|
+
raise AttributeError(
|
|
1665
|
+
"ekin_range not yet set. Run `.fit_selection()` first."
|
|
1666
|
+
)
|
|
1600
1667
|
|
|
1601
1668
|
store = getattr(self, "_individual_properties", None)
|
|
1602
1669
|
if not store or select_label not in store:
|
|
1603
|
-
all_labels = (sorted(store.keys())
|
|
1670
|
+
all_labels = (sorted(store.keys())
|
|
1671
|
+
if isinstance(store, dict) else [])
|
|
1604
1672
|
raise ValueError(
|
|
1605
|
-
f"Label '{select_label}' not found in available labels:
|
|
1673
|
+
f"Label '{select_label}' not found in available labels: "
|
|
1674
|
+
f"{all_labels}"
|
|
1606
1675
|
)
|
|
1607
1676
|
|
|
1608
|
-
# Convert lists → numpy arrays within the selected label’s classes
|
|
1677
|
+
# Convert lists → numpy arrays within the selected label’s classes.
|
|
1678
|
+
# Keep scalar center_wavevector as a scalar.
|
|
1609
1679
|
per_class_dicts = []
|
|
1610
1680
|
for cls, bucket in store[select_label].items():
|
|
1611
1681
|
dct = {}
|
|
1612
1682
|
for k, v in bucket.items():
|
|
1613
1683
|
if k in ("label", "_class"):
|
|
1614
1684
|
dct[k] = v
|
|
1685
|
+
elif k == "center_wavevector":
|
|
1686
|
+
# keep scalar as-is, do not wrap in np.asarray
|
|
1687
|
+
dct[k] = v
|
|
1615
1688
|
else:
|
|
1616
1689
|
dct[k] = np.asarray(v)
|
|
1617
1690
|
per_class_dicts.append(dct)
|
|
@@ -1628,20 +1701,23 @@ class MDCs:
|
|
|
1628
1701
|
"side": side,
|
|
1629
1702
|
}
|
|
1630
1703
|
|
|
1631
|
-
# Collect parameters without prefixing by class
|
|
1704
|
+
# Collect parameters without prefixing by class. This will also include
|
|
1705
|
+
# center_wavevector from the fitted SpectralQuadratic class, and since
|
|
1706
|
+
# there is no function argument with that name, it cannot be overridden.
|
|
1632
1707
|
if isinstance(selected_properties, dict):
|
|
1633
1708
|
for key, val in selected_properties.items():
|
|
1634
1709
|
if key not in ("label", "_class"):
|
|
1635
1710
|
exported_parameters[key] = val
|
|
1636
1711
|
else:
|
|
1637
|
-
# If multiple classes, merge sequentially
|
|
1712
|
+
# If multiple classes, merge sequentially
|
|
1713
|
+
# (last overwrites same-name keys).
|
|
1638
1714
|
for cls_bucket in selected_properties:
|
|
1639
1715
|
for key, val in cls_bucket.items():
|
|
1640
1716
|
if key not in ("label", "_class"):
|
|
1641
1717
|
exported_parameters[key] = val
|
|
1642
1718
|
|
|
1643
|
-
return self._ekin_range, self.hnuminphi, select_label,
|
|
1644
|
-
|
|
1719
|
+
return (self._ekin_range, self.hnuminphi, select_label,
|
|
1720
|
+
selected_properties, exported_parameters)
|
|
1645
1721
|
|
|
1646
1722
|
|
|
1647
1723
|
class SelfEnergy:
|
|
@@ -1697,6 +1773,7 @@ class SelfEnergy:
|
|
|
1697
1773
|
self._peak_sigma = self._properties.get("peak_sigma")
|
|
1698
1774
|
self._broadening = self._properties.get("broadening")
|
|
1699
1775
|
self._broadening_sigma = self._properties.get("broadening_sigma")
|
|
1776
|
+
self._center_wavevector = self._properties.get("center_wavevector")
|
|
1700
1777
|
|
|
1701
1778
|
# lazy caches
|
|
1702
1779
|
self._peak_positions = None
|
|
@@ -1763,6 +1840,7 @@ class SelfEnergy:
|
|
|
1763
1840
|
self._peak_positions = None
|
|
1764
1841
|
self._real = None
|
|
1765
1842
|
self._real_sigma = None
|
|
1843
|
+
self._mdc_maxima = None
|
|
1766
1844
|
|
|
1767
1845
|
@property
|
|
1768
1846
|
def fermi_wavevector(self):
|
|
@@ -1838,6 +1916,7 @@ class SelfEnergy:
|
|
|
1838
1916
|
# invalidate dependent cache
|
|
1839
1917
|
self._peak_positions = None
|
|
1840
1918
|
self._real = None
|
|
1919
|
+
self._mdc_maxima = None
|
|
1841
1920
|
|
|
1842
1921
|
@property
|
|
1843
1922
|
def peak_sigma(self):
|
|
@@ -1870,6 +1949,11 @@ class SelfEnergy:
|
|
|
1870
1949
|
self._properties["broadening_sigma"] = x
|
|
1871
1950
|
self._imag_sigma = None
|
|
1872
1951
|
|
|
1952
|
+
@property
|
|
1953
|
+
def center_wavevector(self):
|
|
1954
|
+
"""Read-only center wavevector (SpectralQuadratic, if present)."""
|
|
1955
|
+
return self._center_wavevector
|
|
1956
|
+
|
|
1873
1957
|
# ---------------- derived outputs ----------------
|
|
1874
1958
|
@property
|
|
1875
1959
|
def peak_positions(self):
|
|
@@ -1936,7 +2020,7 @@ class SelfEnergy:
|
|
|
1936
2020
|
"(SpectralLinear): set `fermi_velocity` first.")
|
|
1937
2021
|
self._imag_sigma = np.abs(self._fermi_velocity) * \
|
|
1938
2022
|
np.sqrt(self._ekin_range / pref) * self._broadening_sigma
|
|
1939
|
-
else:
|
|
2023
|
+
else:
|
|
1940
2024
|
if self._bare_mass is None:
|
|
1941
2025
|
raise AttributeError("Cannot compute `imag_sigma` "
|
|
1942
2026
|
"(SpectralQuadratic): set `bare_mass` first.")
|
|
@@ -1987,6 +2071,30 @@ class SelfEnergy:
|
|
|
1987
2071
|
* np.abs(self.peak_positions / self._bare_mass)
|
|
1988
2072
|
return self._real_sigma
|
|
1989
2073
|
|
|
2074
|
+
@property
|
|
2075
|
+
def mdc_maxima(self):
|
|
2076
|
+
"""
|
|
2077
|
+
MDC maxima (lazy).
|
|
2078
|
+
|
|
2079
|
+
SpectralLinear:
|
|
2080
|
+
identical to peak_positions
|
|
2081
|
+
|
|
2082
|
+
SpectralQuadratic:
|
|
2083
|
+
peak_positions + center_wavevector
|
|
2084
|
+
"""
|
|
2085
|
+
if getattr(self, "_mdc_maxima", None) is None:
|
|
2086
|
+
if self.peak_positions is None:
|
|
2087
|
+
return None
|
|
2088
|
+
|
|
2089
|
+
if self._class == "SpectralLinear":
|
|
2090
|
+
self._mdc_maxima = self.peak_positions
|
|
2091
|
+
elif self._class == "SpectralQuadratic":
|
|
2092
|
+
self._mdc_maxima = (
|
|
2093
|
+
self.peak_positions + self._center_wavevector
|
|
2094
|
+
)
|
|
2095
|
+
|
|
2096
|
+
return self._mdc_maxima
|
|
2097
|
+
|
|
1990
2098
|
|
|
1991
2099
|
class CreateSelfEnergies:
|
|
1992
2100
|
r"""
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: xarpes
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
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
91
|
conda create -n <my_env> -c defaults -c conda-forge
|
|
88
92
|
conda activate <my_env>
|
|
89
93
|
|
|
94
|
+
Where `<my_env>` must be replaced by your desired name.
|
|
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=WAZKbz4gEW1H7Yb24GVBAryDrMsQiAn0UNmeJwEM_no,124
|
|
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=W-5WaKjBtg8PIxTypqja2R29mgWkQ844lgRWci0nhn0,5679
|
|
6
|
+
xarpes/spectral.py,sha256=GT_1GUMy9Md4X8W7UM0l8ir_WRaPAaUbgvS5Ep0uSY8,82863
|
|
7
|
+
xarpes-0.3.4.dist-info/entry_points.txt,sha256=917UR-cqFTMMI_vMqIbk7boYSuFX_zHwQlXKcj9vlCE,79
|
|
8
|
+
xarpes-0.3.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
9
|
+
xarpes-0.3.4.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
10
|
+
xarpes-0.3.4.dist-info/METADATA,sha256=FLClCjMjfV2D_MYH3o9o3PlFG2KsDJLZLyNUzwD2FG4,6369
|
|
11
|
+
xarpes-0.3.4.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
|