xarpes 0.1.0__py3-none-any.whl → 0.2.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.
@@ -0,0 +1,133 @@
1
+ # Copyright (C) 2024 xARPES Developers
2
+ # This program is free software under the terms of the GNU GPLv3 license.
3
+
4
+ """Separate functions mostly used in conjunction with various classes."""
5
+
6
+ def download_examples():
7
+ """Downloads the examples folder from the xARPES code only if it does not
8
+ already exist. Prints executed steps and a final cleanup/failure message.
9
+
10
+ Returns
11
+ -------
12
+ 0, 1 : int
13
+ Returns 0 if the execution succeeds, 1 if it fails.
14
+ """
15
+ import requests
16
+ import zipfile
17
+ import os
18
+ import shutil
19
+ import io
20
+
21
+ repo_url = 'https://github.com/xARPES/xARPES_examples'
22
+ output_dir = '.' # Directory from which the function is called
23
+
24
+ # Check if 'examples' directory already exists
25
+ final_examples_path = os.path.join(output_dir, 'examples')
26
+ if os.path.exists(final_examples_path):
27
+ print("Warning: 'examples' folder already exists. No download will be performed.")
28
+ return 1 # Exit the function if 'examples' directory exists
29
+
30
+ # Proceed with download if 'examples' directory does not exist
31
+ repo_parts = repo_url.replace("https://github.com/", "").rstrip('/')
32
+ zip_url = f"https://github.com/{repo_parts}/archive/refs/heads/main.zip"
33
+
34
+ # Make the HTTP request to download the zip file
35
+ print(f"Downloading {zip_url}")
36
+ response = requests.get(zip_url)
37
+ if response.status_code == 200:
38
+ zip_file_bytes = io.BytesIO(response.content)
39
+
40
+ with zipfile.ZipFile(zip_file_bytes, 'r') as zip_ref:
41
+ zip_ref.extractall(output_dir)
42
+
43
+ # Path to the extracted main folder
44
+ main_folder_path = os.path.join(output_dir, repo_parts.split('/')[-1] + '-main')
45
+ examples_path = os.path.join(main_folder_path, 'examples')
46
+
47
+ # Move the 'examples' directory to the target location if it was extracted
48
+ if os.path.exists(examples_path):
49
+ shutil.move(examples_path, final_examples_path)
50
+ print(f"'examples' subdirectory moved to {final_examples_path}")
51
+ else:
52
+ print("'examples' subdirectory not found in the repository.")
53
+
54
+ # Remove the rest of the extracted content
55
+ shutil.rmtree(main_folder_path)
56
+ print(f"Cleaned up temporary files in {main_folder_path}")
57
+ return 0
58
+ else:
59
+ print(f"Failed to download the repository. Status code: {response.status_code}")
60
+ return 1
61
+
62
+
63
+ def error_function(p, xdata, ydata, function, extra_args):
64
+ r"""The error function used inside the fit_leastsq function.
65
+
66
+ Parameters
67
+ ----------
68
+ p : ndarray
69
+ Array of parameters during the optimization
70
+ xdata : ndarray
71
+ Array of abscissa values the function is evaluated on
72
+ ydata : ndarray
73
+ Outcomes on ordinate the evaluated function is compared to
74
+ function : function
75
+ Function or class with call method to be evaluated
76
+ extra_args :
77
+ Arguments provided to function that should not be optimized
78
+
79
+ Returns
80
+ -------
81
+ residual :
82
+ Residual between evaluated function and ydata
83
+ """
84
+ residual = function(xdata, *p, extra_args) - ydata
85
+ return residual
86
+
87
+
88
+ def fit_leastsq(p0, xdata, ydata, function, extra_args):
89
+ r"""Wrapper arround scipy.optimize.leastsq.
90
+
91
+ Parameters
92
+ ----------
93
+ p0 : ndarray
94
+ Initial guess for parameters to be optimized
95
+ xdata : ndarray
96
+ Array of abscissa values the function is evaluated on
97
+ ydata : ndarray
98
+ Outcomes on ordinate the evaluated function is compared to
99
+ function : function
100
+ Function or class with call method to be evaluated
101
+ extra_args :
102
+ Arguments provided to function that should not be optimized
103
+
104
+ Returns
105
+ -------
106
+ pfit_leastsq : ndarray
107
+ Array containing the optimized parameters
108
+ perr_leastsq : ndarray
109
+ Covariance matrix of the optimized parameters
110
+ """
111
+ import numpy as np
112
+ from scipy.optimize import leastsq
113
+ pfit, pcov, infodict, errmsg, success = leastsq(
114
+ error_function, p0, args=(xdata, ydata, function, extra_args),
115
+ full_output=1)
116
+
117
+ if (len(ydata) > len(p0)) and pcov is not None:
118
+ s_sq = (error_function(pfit, xdata, ydata, function,
119
+ extra_args) ** 2).sum() / (len(ydata) - len(p0))
120
+ pcov = pcov * s_sq
121
+ else:
122
+ pcov = np.inf
123
+
124
+ error = []
125
+ for i in range(len(pfit)):
126
+ try:
127
+ error.append(np.absolute(pcov[i][i]) ** 0.5)
128
+ except:
129
+ error.append(0.00)
130
+ pfit_leastsq = pfit
131
+ perr_leastsq = np.array(error)
132
+
133
+ return pfit_leastsq, perr_leastsq
@@ -0,0 +1,157 @@
1
+ # Copyright (C) 2024 xARPES Developers
2
+ # This program is free software under the terms of the GNU GPLv3 license.
3
+
4
+ # get_ax_fig_plt and add_fig_kwargs originate from pymatgen/util/plotting.py.
5
+ # Copyright (C) 2011-2024 Shyue Ping Ong and the pymatgen Development Team
6
+ # pymatgen is released under the MIT License.
7
+
8
+ # See also abipy/tools/plotting.py.
9
+ # Copyright (C) 2021 Matteo Giantomassi and the AbiPy Group
10
+ # AbiPy is free software under the terms of the GNU GPLv2 license.
11
+
12
+ """Functions related to plotting."""
13
+
14
+ from functools import wraps
15
+ import matplotlib.pyplot as plt
16
+ import matplotlib as mpl
17
+
18
+ def plot_settings(name='default'):
19
+ mpl.rc('xtick', labelsize=10, direction='in')
20
+ mpl.rc('ytick', labelsize=10, direction='in')
21
+ lw = dict(default=2.0, large=4.0)[name]
22
+ mpl.rcParams['lines.linewidth'] = lw
23
+ mpl.rcParams['lines.markersize'] = 3
24
+ mpl.rcParams['xtick.major.size'] = 4
25
+ mpl.rcParams['xtick.minor.size'] = 2
26
+ mpl.rcParams['xtick.major.width'] = 0.8
27
+ mpl.rcParams.update({'font.size': 16})
28
+
29
+ def get_ax_fig_plt(ax=None, **kwargs):
30
+ r"""Helper function used in plot functions supporting an optional `Axes`
31
+ argument.
32
+
33
+ If `ax` is `None`, we build the `matplotlib` figure and create the `Axes`.
34
+ Else we return the current active figure.
35
+
36
+ Parameters
37
+ ----------
38
+ ax : object
39
+ `Axes` object. Defaults to `None`.
40
+ **kwargs
41
+ Keyword arguments are passed to `plt.figure` if `ax` is not `None`.
42
+
43
+ Returns
44
+ -------
45
+ ax : object
46
+ `Axes` object.
47
+ figure : object
48
+ `matplotlib` figure.
49
+ plt : object
50
+ `matplotlib.pyplot` module.
51
+ """
52
+ if ax is None:
53
+ fig = plt.figure(**kwargs)
54
+ ax = fig.gca()
55
+ else:
56
+ fig = plt.gcf()
57
+
58
+ return ax, fig, plt
59
+
60
+ def add_fig_kwargs(func):
61
+ """Decorator that adds keyword arguments for functions returning matplotlib
62
+ figures.
63
+
64
+ The function should return either a matplotlib figure or None to signal
65
+ some sort of error/unexpected event.
66
+ """
67
+ @wraps(func)
68
+ def wrapper(*args, **kwargs):
69
+ # pop the kwds used by the decorator.
70
+ title = kwargs.pop('title', None)
71
+ size_kwargs = kwargs.pop('size_kwargs', None)
72
+ show = kwargs.pop('show', True)
73
+ savefig = kwargs.pop('savefig', None)
74
+ tight_layout = kwargs.pop('tight_layout', False)
75
+ ax_grid = kwargs.pop('ax_grid', None)
76
+ ax_annotate = kwargs.pop('ax_annotate', None)
77
+ fig_close = kwargs.pop('fig_close', False)
78
+
79
+ # Call func and return immediately if None is returned.
80
+ fig = func(*args, **kwargs)
81
+ if fig is None:
82
+ return fig
83
+
84
+ # Operate on matplotlib figure.
85
+ if title is not None:
86
+ fig.suptitle(title)
87
+
88
+ if size_kwargs is not None:
89
+ fig.set_size_inches(size_kwargs.pop('w'), size_kwargs.pop('h'),
90
+ **size_kwargs)
91
+
92
+ if ax_grid is not None:
93
+ for ax in fig.axes:
94
+ ax.grid(bool(ax_grid))
95
+
96
+ if ax_annotate:
97
+ tags = ascii_letters
98
+ if len(fig.axes) > len(tags):
99
+ tags = (1 + len(ascii_letters) // len(fig.axes)) * ascii_letters
100
+ for ax, tag in zip(fig.axes, tags):
101
+ ax.annotate(f'({tag})', xy=(0.05, 0.95),
102
+ xycoords='axes fraction')
103
+
104
+ if tight_layout:
105
+ try:
106
+ fig.tight_layout()
107
+ except Exception as exc:
108
+ # For some unknown reason, this problem shows up only on travis.
109
+ # https://stackoverflow.com/questions/22708888/valueerror-when-using-matplotlib-tight-layout
110
+ print('Ignoring Exception raised by fig.tight_layout\n',
111
+ str(exc))
112
+
113
+ if savefig:
114
+ fig.savefig(savefig)
115
+
116
+ if show:
117
+ plt.show()
118
+ if fig_close:
119
+ plt.close(fig=fig)
120
+
121
+ return fig
122
+
123
+ # Add docstring to the decorated method.
124
+ doc_str = """\n\n
125
+
126
+ notes
127
+ -----
128
+
129
+ Keyword arguments controlling the display of the figure:
130
+
131
+ ================ ====================================================
132
+ kwargs Meaning
133
+ ================ ====================================================
134
+ title Title of the plot (Default: None).
135
+ show True to show the figure (default: True).
136
+ savefig "abc.png" or "abc.eps" to save the figure to a file.
137
+ size_kwargs Dictionary with options passed to fig.set_size_inches
138
+ e.g. size_kwargs=dict(w=3, h=4)
139
+ tight_layout True to call fig.tight_layout (default: False)
140
+ ax_grid True (False) to add (remove) grid from all axes in
141
+ fig.
142
+ Default: None i.e. fig is left unchanged.
143
+ ax_annotate Add labels to subplots e.g. (a), (b).
144
+ Default: False
145
+ fig_close Close figure. Default: False.
146
+ ================ ====================================================
147
+
148
+ """
149
+
150
+ if wrapper.__doc__ is not None:
151
+ # Add s at the end of the docstring.
152
+ wrapper.__doc__ += f'\n{doc_str}'
153
+ else:
154
+ # Use s
155
+ wrapper.__doc__ = doc_str
156
+
157
+ return wrapper
xarpes/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = '0.1.0'
1
+ __version__ = '0.2.0'
2
2
 
3
3
  from . import plotting
4
4
 
xarpes/band_map.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # Copyright (C) 2024 xARPES Developers
2
- # This program is free software under the terms of the GNU GPLv2 license.
2
+ # This program is free software under the terms of the GNU GPLv3 license.
3
3
 
4
4
  # get_ax_fig_plt and add_fig_kwargs originate from pymatgen/util/plotting.py.
5
5
  # Copyright (C) 2011-2024 Shyue Ping Ong and the pymatgen Development Team
@@ -28,43 +28,72 @@ class band_map():
28
28
  1D array of kinetic energy values for the ordinate [eV]
29
29
  energy_resolution : float
30
30
  Energy resolution of the detector [eV]
31
- temperature : float
31
+ temperature : float, None
32
32
  Temperature of the sample [K]
33
- hnuminphi : float
33
+ hnuminphi : float, None
34
34
  Kinetic energy minus the work function [eV]
35
+ hnuminphi_std : float, None
36
+ Standard deviation of kinetic energy minus work function [eV]
35
37
  """
36
38
  def __init__(self, intensities, angles, ekin, energy_resolution=None,
37
- temperature=None, hnuminphi=None):
38
-
39
+ temperature=None, hnuminphi=None, hnuminphi_std=None):
39
40
  self.intensities = intensities
40
41
  self.angles = angles
41
42
  self.ekin = ekin
42
43
  self.energy_resolution = energy_resolution
43
44
  self.temperature = temperature
44
45
  self.hnuminphi = hnuminphi
46
+ self.hnuminphi_std = hnuminphi_std
45
47
 
46
48
  @property
47
49
  def hnuminphi(self):
48
- r"""Returns the photon energy minus the work function in eV.
50
+ r"""Returns the photon energy minus the work function in eV if it has
51
+ been set, either during instantiation, with the setter, or by fitting
52
+ the Fermi-Dirac distribution to the integrated weight.
49
53
 
50
54
  Returns
51
55
  -------
52
- hnuminphi : float
56
+ hnuminphi : float, None
53
57
  Kinetic energy minus the work function [eV]
54
58
  """
55
59
  return self._hnuminphi
56
60
 
57
61
  @hnuminphi.setter
58
62
  def hnuminphi(self, hnuminphi):
59
- r"""Manually sets the photon energy minus the work function in eV.
63
+ r"""Manually sets the photon energy minus the work function in eV if it
64
+ has been set; otherwise returns None.
60
65
 
61
66
  Parameters
62
67
  ----------
63
- hnuminphi : float
68
+ hnuminphi : float, None
64
69
  Kinetic energy minus the work function [eV]
65
70
  """
66
71
  self._hnuminphi = hnuminphi
67
72
 
73
+ @property
74
+ def hnuminphi_std(self):
75
+ r"""Returns standard deviation of the photon energy minus the work
76
+ function in eV.
77
+
78
+ Returns
79
+ -------
80
+ hnuminphi_std : float
81
+ Standard deviation of energy minus the work function [eV]
82
+ """
83
+ return self._hnuminphi_std
84
+
85
+ @hnuminphi_std.setter
86
+ def hnuminphi_std(self, hnuminphi_std):
87
+ r"""Manually sets the standard deviation of photon energy minus the
88
+ work function in eV.
89
+
90
+ Parameters
91
+ ----------
92
+ hnuminphi_std : float
93
+ Standard deviation of energy minus the work function [eV]
94
+ """
95
+ self._hnuminphi_std = hnuminphi_std
96
+
68
97
  def shift_angles(self, shift):
69
98
  r"""
70
99
  Shifts the angles by the specified amount in degrees. Used to shift
@@ -84,6 +113,9 @@ class band_map():
84
113
  ekin_max=np.infty, ax=None, **kwargs):
85
114
  r"""
86
115
  Fits the Fermi edge of the band map and plots the result.
116
+ Also sets hnuminphi, the kinetic energy minus the work function in eV.
117
+ The fitting includes an energy convolution with an abscissa range
118
+ expanded by 5 times the energy resolution standard deviation.
87
119
 
88
120
  Parameters
89
121
  ----------
@@ -104,13 +136,17 @@ class band_map():
104
136
  ax : Matplotlib-Axes / NoneType
105
137
  Axis for plotting the Fermi edge on. Created if not provided by
106
138
  the user.
139
+
140
+ Other parameters
141
+ ----------------
107
142
  **kwargs : dict, optional
108
143
  Additional arguments passed on to add_fig_kwargs. See the keyword
109
144
  table below.
110
145
 
111
146
  Returns
112
147
  -------
113
- Matplotlib-Figure
148
+ fig : Matplotlib-Figure
149
+ Figure containing the Fermi edge fit
114
150
  """
115
151
  from xarpes.functions import fit_leastsq
116
152
 
@@ -148,6 +184,7 @@ class band_map():
148
184
  name='Fitted result')
149
185
 
150
186
  self.hnuminphi = popt[0]
187
+ self.hnuminphi_std = np.sqrt(np.diag(pcov))[0][0]
151
188
 
152
189
  ax.set_xlabel(r'$E_{\mathrm{kin}}$ (-)')
153
190
  ax.set_ylabel('Counts (-)')
@@ -165,4 +202,4 @@ class band_map():
165
202
 
166
203
  ax.legend()
167
204
 
168
- return fig
205
+ return fig