galore 0.10.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.
- galore/__init__.py +441 -0
- galore/cli/__init__.py +0 -0
- galore/cli/galore.py +336 -0
- galore/cli/galore_get_cs.py +62 -0
- galore/cli/galore_plot_cs.py +124 -0
- galore/cli/utils.py +12 -0
- galore/cross_sections.py +281 -0
- galore/data/cross_sections.json +636 -0
- galore/data/cross_sections_haxpes.json +635 -0
- galore/data/cross_sections_ups.json +634 -0
- galore/data/scofield_data.db +0 -0
- galore/formats.py +553 -0
- galore/plot.py +247 -0
- galore-0.10.0.dist-info/METADATA +272 -0
- galore-0.10.0.dist-info/RECORD +19 -0
- galore-0.10.0.dist-info/WHEEL +5 -0
- galore-0.10.0.dist-info/entry_points.txt +4 -0
- galore-0.10.0.dist-info/licenses/LICENSE +674 -0
- galore-0.10.0.dist-info/top_level.txt +1 -0
galore/__init__.py
ADDED
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
###############################################################################
|
|
2
|
+
# #
|
|
3
|
+
# GALORE: Gaussian and Lorentzian broadening for simulated spectra #
|
|
4
|
+
# #
|
|
5
|
+
# Developed by Scanlon Materials Theory Group at University College London #
|
|
6
|
+
# (c) 2017 #
|
|
7
|
+
# #
|
|
8
|
+
###############################################################################
|
|
9
|
+
# #
|
|
10
|
+
# This file is part of Galore. Galore is free software: you can redistribute #
|
|
11
|
+
# it and/or modify it under the terms of the GNU General Public License as #
|
|
12
|
+
# published by the Free Software Foundation, either version 3 of the License, #
|
|
13
|
+
# or (at your option) any later version. This program is distributed in the #
|
|
14
|
+
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the #
|
|
15
|
+
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. #
|
|
16
|
+
# See the GNU General Public License for more details. You should have #
|
|
17
|
+
# received a copy of the GNU General Public License along with this program. #
|
|
18
|
+
# If not, see <http://www.gnu.org/licenses/>. #
|
|
19
|
+
# #
|
|
20
|
+
###############################################################################
|
|
21
|
+
|
|
22
|
+
from __future__ import print_function
|
|
23
|
+
|
|
24
|
+
from collections import OrderedDict
|
|
25
|
+
from collections.abc import Sequence
|
|
26
|
+
import logging
|
|
27
|
+
from math import sqrt, log
|
|
28
|
+
import os.path
|
|
29
|
+
|
|
30
|
+
import numpy as np
|
|
31
|
+
from scipy.interpolate import interp1d
|
|
32
|
+
|
|
33
|
+
import galore.formats
|
|
34
|
+
from galore.cross_sections import get_cross_sections, cross_sections_info
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def auto_limits(data_1d, padding=0.05):
|
|
38
|
+
"""Return limiting values outside data range
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
data_1d (iterable): Data to obtain range from
|
|
42
|
+
padding (float): Scale factor for padding relative to data range
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
(2-tuple) (xmin, xmax)
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
xmin, xmax = min(data_1d), max(data_1d)
|
|
49
|
+
auto_xmax = xmax + padding * (xmax - xmin)
|
|
50
|
+
auto_xmin = xmin - padding * (xmax - xmin)
|
|
51
|
+
return auto_xmin, auto_xmax
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def process_1d_data(input=['vasprun.xml'],
|
|
55
|
+
gaussian=None, lorentzian=None,
|
|
56
|
+
sampling=1e-2,
|
|
57
|
+
xmin=None, xmax=None,
|
|
58
|
+
spikes=False,
|
|
59
|
+
**kwargs):
|
|
60
|
+
"""Read 1D data series from files, process for output
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
input (str or 1-list):
|
|
64
|
+
Input data file. Pass as either a string or a list containing one
|
|
65
|
+
string
|
|
66
|
+
**kwargs:
|
|
67
|
+
See main command reference
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
2-tuple (np.ndarray, np.ndarray):
|
|
71
|
+
Resampled x-values and corresponding broadened data as 1D numpy
|
|
72
|
+
arrays
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
if type(input) == str:
|
|
77
|
+
pass
|
|
78
|
+
elif len(input) > 1:
|
|
79
|
+
raise ValueError("Simple DOS only uses one input file, "
|
|
80
|
+
"not list: {0}".format(input))
|
|
81
|
+
else:
|
|
82
|
+
input = input[0]
|
|
83
|
+
|
|
84
|
+
if not os.path.exists(input):
|
|
85
|
+
raise Exception(
|
|
86
|
+
"Input file {0} does not exist!".format(input))
|
|
87
|
+
if galore.formats.is_xml(input):
|
|
88
|
+
xy_data = galore.formats.read_vasprun_totaldos(input)
|
|
89
|
+
elif galore.formats.is_gpw(input):
|
|
90
|
+
xy_data = galore.formats.read_gpaw_totaldos(input)
|
|
91
|
+
elif galore.formats.is_doscar(input):
|
|
92
|
+
xy_data = galore.formats.read_doscar(input)
|
|
93
|
+
elif galore.formats.is_vasp_raman(input):
|
|
94
|
+
xy_data = galore.formats.read_vasp_raman(input)
|
|
95
|
+
elif galore.formats.is_csv(input):
|
|
96
|
+
xy_data = galore.formats.read_csv(input)
|
|
97
|
+
else:
|
|
98
|
+
xy_data = galore.formats.read_txt(input)
|
|
99
|
+
|
|
100
|
+
# Add 5% to data range if not specified
|
|
101
|
+
auto_xmin, auto_xmax = auto_limits(xy_data[:, 0], padding=0.05)
|
|
102
|
+
if xmax is None:
|
|
103
|
+
xmax = auto_xmax
|
|
104
|
+
if xmin is None:
|
|
105
|
+
xmin = auto_xmin
|
|
106
|
+
|
|
107
|
+
d = sampling
|
|
108
|
+
x_values = np.arange(xmin, xmax, d)
|
|
109
|
+
data_1d = galore.xy_to_1d(xy_data, x_values, spikes=spikes)
|
|
110
|
+
|
|
111
|
+
broadened_data = data_1d.copy()
|
|
112
|
+
if lorentzian:
|
|
113
|
+
broadened_data = galore.broaden(
|
|
114
|
+
broadened_data, d=d, dist='lorentzian', width=lorentzian)
|
|
115
|
+
|
|
116
|
+
if gaussian:
|
|
117
|
+
broadened_data = galore.broaden(
|
|
118
|
+
broadened_data, d=d, dist='gaussian', width=gaussian)
|
|
119
|
+
|
|
120
|
+
return (x_values, broadened_data)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def process_pdos(input=['vasprun.xml'],
|
|
124
|
+
gaussian=None, lorentzian=None,
|
|
125
|
+
weighting=None, sampling=1e-2,
|
|
126
|
+
xmin=None, xmax=None, flipx=False, **kwargs):
|
|
127
|
+
"""Read PDOS from files, process for output
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
input (list or str):
|
|
131
|
+
Files for processing. Vasp output or space-separated files with
|
|
132
|
+
XXX_EL_YYY.EXT filename pattern where EL is the element label.
|
|
133
|
+
We recommend SYSTEM_EL_dos.dat. Alternatively, a
|
|
134
|
+
`pymatgen.electronic_structure.dos.CompleteDos` can be
|
|
135
|
+
provided. Spin channels indicated by an (up) or (down) suffix
|
|
136
|
+
in file header will be combined for each orbital type.
|
|
137
|
+
**kwargs:
|
|
138
|
+
See main command reference
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
dict:
|
|
142
|
+
Weighted and resampled orbital data in format::
|
|
143
|
+
|
|
144
|
+
{'el1': {'energy': values, 's': values, 'p': values ...},
|
|
145
|
+
'el2': {'energy': values, 's': values, ...}, ...}
|
|
146
|
+
|
|
147
|
+
"""
|
|
148
|
+
if isinstance(input, str) or not isinstance(input, Sequence):
|
|
149
|
+
input = [input]
|
|
150
|
+
|
|
151
|
+
# Read files into dict, check for consistency
|
|
152
|
+
energy_label = None
|
|
153
|
+
pdos_data = OrderedDict()
|
|
154
|
+
for pdos_file in input:
|
|
155
|
+
if (galore.formats.is_complete_dos(pdos_file)
|
|
156
|
+
or galore.formats.is_xml(pdos_file)):
|
|
157
|
+
pdos_data = galore.formats.read_vasprun_pdos(pdos_file)
|
|
158
|
+
kwargs['units'] = 'eV'
|
|
159
|
+
break
|
|
160
|
+
|
|
161
|
+
elif galore.formats.is_gpw(pdos_file):
|
|
162
|
+
pdos_data = galore.formats.read_gpaw_pdos(pdos_file)
|
|
163
|
+
kwargs['units'] = 'eV'
|
|
164
|
+
break
|
|
165
|
+
|
|
166
|
+
if not os.path.exists(pdos_file):
|
|
167
|
+
raise Exception("Input file {0} does not "
|
|
168
|
+
"exist!".format(input))
|
|
169
|
+
|
|
170
|
+
basename = os.path.basename(pdos_file)
|
|
171
|
+
try:
|
|
172
|
+
element = basename.split("_")[-2]
|
|
173
|
+
except IndexError:
|
|
174
|
+
raise Exception("Couldn't guess element name from filename. "
|
|
175
|
+
"Please format filename as XXX_EL_YYY.EXT"
|
|
176
|
+
"Where EL is the element label, and XXX, YYY "
|
|
177
|
+
"and EXT are labels of your choice. We recommend"
|
|
178
|
+
"SYSTEM_EL_dos.dat")
|
|
179
|
+
|
|
180
|
+
data = galore.formats.read_pdos_txt(pdos_file)
|
|
181
|
+
|
|
182
|
+
if energy_label is None:
|
|
183
|
+
energy_label = data.dtype.names[0]
|
|
184
|
+
else:
|
|
185
|
+
try:
|
|
186
|
+
assert data.dtype.names[0] == energy_label
|
|
187
|
+
except AssertionError as error:
|
|
188
|
+
error.args += ("Energy labels are not consistent "
|
|
189
|
+
"between input files",)
|
|
190
|
+
raise
|
|
191
|
+
|
|
192
|
+
orbital_labels = data.dtype.names[1:]
|
|
193
|
+
pdos_data[element] = OrderedDict([('energy', data[energy_label])])
|
|
194
|
+
pdos_data[element].update(OrderedDict((orbital, data[orbital])
|
|
195
|
+
for orbital in orbital_labels))
|
|
196
|
+
|
|
197
|
+
# Work out sampling details; 5% pad added to data if no limits specified
|
|
198
|
+
# In x-flip mode, the user specifies these as binding energies so values
|
|
199
|
+
# are reversed while treating DOS data.
|
|
200
|
+
d = sampling
|
|
201
|
+
limits = (auto_limits(data['energy'], padding=0.05)
|
|
202
|
+
for (element, data) in pdos_data.items())
|
|
203
|
+
xmins, xmaxes = zip(*limits)
|
|
204
|
+
|
|
205
|
+
if xmax is None:
|
|
206
|
+
xmax = max(xmaxes)
|
|
207
|
+
|
|
208
|
+
if xmin is None:
|
|
209
|
+
xmin = min(xmins)
|
|
210
|
+
|
|
211
|
+
if flipx:
|
|
212
|
+
xmin, xmax = -xmax, -xmin
|
|
213
|
+
|
|
214
|
+
x_values = np.arange(xmin, xmax, d)
|
|
215
|
+
|
|
216
|
+
# Resample data into new dictionary
|
|
217
|
+
pdos_plotting_data = OrderedDict()
|
|
218
|
+
for element, el_data in pdos_data.items():
|
|
219
|
+
pdos_plotting_data[element] = OrderedDict([('energy', x_values)])
|
|
220
|
+
for orbital, orb_data in el_data.items():
|
|
221
|
+
if orbital == 'energy':
|
|
222
|
+
continue
|
|
223
|
+
elif orb_data is None:
|
|
224
|
+
continue
|
|
225
|
+
|
|
226
|
+
xy_data = np.column_stack([el_data['energy'], orb_data])
|
|
227
|
+
|
|
228
|
+
pdos_resampled = galore.xy_to_1d(xy_data, x_values)
|
|
229
|
+
broadened_data = pdos_resampled.copy()
|
|
230
|
+
|
|
231
|
+
if lorentzian:
|
|
232
|
+
broadened_data = galore.broaden(broadened_data, d=d,
|
|
233
|
+
dist='lorentzian',
|
|
234
|
+
width=lorentzian)
|
|
235
|
+
|
|
236
|
+
if gaussian:
|
|
237
|
+
broadened_data = galore.broaden(broadened_data, d=d,
|
|
238
|
+
dist='gaussian',
|
|
239
|
+
width=gaussian)
|
|
240
|
+
|
|
241
|
+
pdos_plotting_data[element][orbital] = broadened_data
|
|
242
|
+
|
|
243
|
+
if weighting:
|
|
244
|
+
cross_sections = galore.get_cross_sections(weighting,
|
|
245
|
+
elements=pdos_data.keys())
|
|
246
|
+
pdos_plotting_data = galore.apply_orbital_weights(
|
|
247
|
+
pdos_plotting_data, cross_sections)
|
|
248
|
+
|
|
249
|
+
return pdos_plotting_data
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def xy_to_1d(xy, x_values, spikes=False):
|
|
253
|
+
"""Convert a set of x,y coordinates to 1D array
|
|
254
|
+
|
|
255
|
+
Data is resampled to a given sequence of regularly-spaced x-values. By
|
|
256
|
+
default linear interpolation is used between neighbouring points, which is
|
|
257
|
+
suitable for resampling distribution data.
|
|
258
|
+
|
|
259
|
+
Where the data consists of a set of discrete energy levels, it should be
|
|
260
|
+
resampled to a series of "spikes". y-values are placed on the nearest
|
|
261
|
+
x-value by subtracting d/2 and rounding up. d is determined by examining
|
|
262
|
+
the first two elements of x_values.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
xy: (ndarray) 2D numpy array of x, y values
|
|
266
|
+
x_values: (iterable) An evenly-spaced x-value mesh
|
|
267
|
+
Returns:
|
|
268
|
+
(np.array): re-sampled y values corresponding to x_values
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
x_values = np.array(x_values)
|
|
272
|
+
n_x_values = x_values.size
|
|
273
|
+
d = x_values[1] - x_values[0]
|
|
274
|
+
|
|
275
|
+
# Structured arrays are allowed, in which case first field is x,
|
|
276
|
+
# second is y. A bit of hackery is needed to slice these interchangeably.
|
|
277
|
+
if xy.dtype.names is None:
|
|
278
|
+
x_field, y_field = (Ellipsis, 0), (Ellipsis, 1)
|
|
279
|
+
else:
|
|
280
|
+
x_field, y_field = xy.dtype.names
|
|
281
|
+
|
|
282
|
+
if spikes:
|
|
283
|
+
spikes = np.zeros(n_x_values)
|
|
284
|
+
spike_locations = x_values.searchsorted(xy[x_field] - (0.5 * d))
|
|
285
|
+
|
|
286
|
+
for location, value in zip(spike_locations, xy[y_field]):
|
|
287
|
+
if location == 0 or location == n_x_values:
|
|
288
|
+
pass
|
|
289
|
+
else:
|
|
290
|
+
spikes[location] += value
|
|
291
|
+
|
|
292
|
+
return spikes
|
|
293
|
+
|
|
294
|
+
else:
|
|
295
|
+
y_func = interp1d(xy[x_field], xy[y_field],
|
|
296
|
+
assume_sorted=False,
|
|
297
|
+
bounds_error=False, fill_value=0)
|
|
298
|
+
return y_func(x_values)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def delta(f1, f2, w=1):
|
|
302
|
+
"""Compare two frequencies, return 1 if close"""
|
|
303
|
+
if abs(f1 - f2) <= 0.5 * w:
|
|
304
|
+
return 1
|
|
305
|
+
else:
|
|
306
|
+
return 0
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def lorentzian(f, f0=0, fwhm=1):
|
|
310
|
+
"""Lorentzian function with height 1 centered on f0.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
f (np.array): 1D array of x-values (e.g. frequencies)
|
|
314
|
+
f0 (float): Origin of function
|
|
315
|
+
fwhm (float): full-width half-maximum (FWHM);
|
|
316
|
+
i.e. the width of the function at half its maximum value.
|
|
317
|
+
|
|
318
|
+
"""
|
|
319
|
+
return 0.5 * fwhm / (np.pi * ((f - f0)**2 + (0.5 * fwhm)**2))
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def gaussian(f, f0=0, fwhm=1):
|
|
323
|
+
"""Gaussian function with height 1 centered on f0
|
|
324
|
+
|
|
325
|
+
f (np.array): 1D array of x-values (e.g. frequencies)
|
|
326
|
+
f0 (float): Origin of function
|
|
327
|
+
fwhm (float): full-width half-maximum (FWHM); i.e. the width of the
|
|
328
|
+
function at half its maximum value.
|
|
329
|
+
|
|
330
|
+
"""
|
|
331
|
+
c = fwhm / (2 * sqrt(2 * log(2)))
|
|
332
|
+
return np.exp(-np.power(f - f0, 2) / (2 * c**2))
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def broaden(data, dist='lorentz', width=2, pad=False, d=1):
|
|
336
|
+
"""Given a 1d data set, use convolution to apply a broadening function
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
data (np.array): 1D array of data points to broaden
|
|
340
|
+
dist (str): Type of distribution used for broadening. Currently only
|
|
341
|
+
"Lorentz" is supported.
|
|
342
|
+
width (float): Width parameter for broadening function. Determines the
|
|
343
|
+
full-width at half-maximum (FWHM) of the broadening function.
|
|
344
|
+
pad (float): Distance sampled on each side of broadening function.
|
|
345
|
+
d (float): x-axis distance associated with each sample in 1D data
|
|
346
|
+
|
|
347
|
+
"""
|
|
348
|
+
|
|
349
|
+
if not pad:
|
|
350
|
+
pad = width * 20
|
|
351
|
+
|
|
352
|
+
if dist.lower() in ('lorentz', 'lorentzian'):
|
|
353
|
+
fwhm = width
|
|
354
|
+
broadening = lorentzian(np.arange(-pad, pad, d), f0=0, fwhm=fwhm)
|
|
355
|
+
elif dist.lower() in ('gauss', 'gaussian'):
|
|
356
|
+
fwhm = width
|
|
357
|
+
broadening = gaussian(np.arange(-pad, pad, d), f0=0, fwhm=fwhm)
|
|
358
|
+
else:
|
|
359
|
+
raise Exception('Broadening distribution '
|
|
360
|
+
' "{0}" not known.'.format(dist))
|
|
361
|
+
|
|
362
|
+
pad_points = int(pad / d)
|
|
363
|
+
broadened_data = np.convolve(broadening, data)
|
|
364
|
+
broadened_data = broadened_data[pad_points:len(data) + pad_points]
|
|
365
|
+
|
|
366
|
+
return broadened_data
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def apply_orbital_weights(pdos_data, cross_sections):
|
|
370
|
+
"""Weight orbital intensities by cross-section for photoemission simulation
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
pdos_data (dict): DOS data in format::
|
|
374
|
+
|
|
375
|
+
{'el1': {'energy': values, 's': values, 'p': values ...},
|
|
376
|
+
'el2': {'energy': values, 's': values, ...}, ...}
|
|
377
|
+
|
|
378
|
+
where DOS values are 1D numpy arrays. Orbital labels must match
|
|
379
|
+
cross_sections data. It is recommended to use
|
|
380
|
+
collections.OrderedDict instead of regular dictionaries, to ensure
|
|
381
|
+
consistent output.
|
|
382
|
+
|
|
383
|
+
In addition, the fields "citation", "link" and "energy" are
|
|
384
|
+
recognised and logged as "INFO".
|
|
385
|
+
|
|
386
|
+
cross_sections (dict): Weightings in format::
|
|
387
|
+
|
|
388
|
+
{'el1': {'1s': x1, '2s': x2, '2p': x3 ...},
|
|
389
|
+
'el2': {'3s': y1, '3p': y2 ...}, ...}
|
|
390
|
+
|
|
391
|
+
The labels should correspond to the headers in the input data. It
|
|
392
|
+
is fine not so specify the level (e.g. use 's', 'p', etc.) as is
|
|
393
|
+
done in the sample data; however, this means that all levels are
|
|
394
|
+
being treated equally and hence probably the core levels will be
|
|
395
|
+
weighted incorrectly. It is possible to set the cross-section of
|
|
396
|
+
undesired orbitals (e.g. projection onto d-orbital for early
|
|
397
|
+
elements) to None; in this case the orbital will be dropped from
|
|
398
|
+
the returned data set.
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
weighted_pdos_data (dict): Weighted data in same format as input
|
|
402
|
+
"""
|
|
403
|
+
|
|
404
|
+
if type(cross_sections) != dict:
|
|
405
|
+
raise TypeError('Cross-section data should be a dictionary. Try using '
|
|
406
|
+
'galore.get_cross_sections for a suitable data set.')
|
|
407
|
+
|
|
408
|
+
logging.info("Applying cross-section weighting values to PDOS:")
|
|
409
|
+
|
|
410
|
+
cross_sections_info(cross_sections, logging=logging)
|
|
411
|
+
|
|
412
|
+
logging.info(" Orbital cross-section weights per electron:")
|
|
413
|
+
|
|
414
|
+
weighted_pdos_data = OrderedDict()
|
|
415
|
+
for el, orbitals in pdos_data.items():
|
|
416
|
+
if 'warning' in cross_sections[el]:
|
|
417
|
+
logging.warning(" {0}: {1}".format(
|
|
418
|
+
el, cross_sections[el]['warning']))
|
|
419
|
+
weighted_orbitals = OrderedDict()
|
|
420
|
+
for orbital, data in orbitals.items():
|
|
421
|
+
if orbital == 'energy':
|
|
422
|
+
weighted_orbitals.update({'energy': data})
|
|
423
|
+
else:
|
|
424
|
+
try:
|
|
425
|
+
cs = cross_sections[el][orbital]
|
|
426
|
+
except KeyError:
|
|
427
|
+
logging.warning("Could not find cross-section data for " +
|
|
428
|
+
"element {0}, ".format(el) +
|
|
429
|
+
"orbital {0}. ".format(orbital) +
|
|
430
|
+
"Skipping this orbital.")
|
|
431
|
+
cs = None
|
|
432
|
+
|
|
433
|
+
if cs is None:
|
|
434
|
+
pass
|
|
435
|
+
else:
|
|
436
|
+
logging.info(" {0} {1}: {2:.3e}".format(el, orbital, cs))
|
|
437
|
+
weighted_orbitals.update({orbital: data * cs})
|
|
438
|
+
|
|
439
|
+
weighted_pdos_data.update({el: weighted_orbitals})
|
|
440
|
+
|
|
441
|
+
return weighted_pdos_data
|
galore/cli/__init__.py
ADDED
|
File without changes
|