ncrystal-python 3.9.81__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.
- NCrystal/__init__.py +85 -0
- NCrystal/__main__.py +98 -0
- NCrystal/_chooks.py +854 -0
- NCrystal/_cli_cif2ncmat.py +269 -0
- NCrystal/_cli_endf2ncmat.py +503 -0
- NCrystal/_cli_hfg2ncmat.py +144 -0
- NCrystal/_cli_mcstasunion.py +74 -0
- NCrystal/_cli_ncmat2cpp.py +31 -0
- NCrystal/_cli_ncmat2hkl.py +180 -0
- NCrystal/_cli_nctool.py +1018 -0
- NCrystal/_cli_vdos2ncmat.py +463 -0
- NCrystal/_cli_verifyatompos.py +257 -0
- NCrystal/_cliimpl.py +307 -0
- NCrystal/_cliwrap_config.py +36 -0
- NCrystal/_common.py +499 -0
- NCrystal/_coreimpl.py +114 -0
- NCrystal/_hfgdata.py +546 -0
- NCrystal/_hklobjects.py +136 -0
- NCrystal/_is_std.py +0 -0
- NCrystal/_locatelib.py +210 -0
- NCrystal/_miscimpl.py +354 -0
- NCrystal/_mmc.py +757 -0
- NCrystal/_msg.py +60 -0
- NCrystal/_ncmat2cpp_impl.py +445 -0
- NCrystal/_ncmatimpl.py +2131 -0
- NCrystal/_numpy.py +76 -0
- NCrystal/_testimpl.py +579 -0
- NCrystal/api.py +56 -0
- NCrystal/atomdata.py +177 -0
- NCrystal/cfgstr.py +77 -0
- NCrystal/cifutils.py +1795 -0
- NCrystal/cli.py +96 -0
- NCrystal/constants.py +134 -0
- NCrystal/core.py +1910 -0
- NCrystal/datasrc.py +226 -0
- NCrystal/exceptions.py +66 -0
- NCrystal/hfg2ncmat.py +270 -0
- NCrystal/mcstasutils.py +438 -0
- NCrystal/misc.py +317 -0
- NCrystal/mmc.py +35 -0
- NCrystal/ncmat.py +778 -0
- NCrystal/ncmat2cpp.py +80 -0
- NCrystal/obsolete.py +67 -0
- NCrystal/plot.py +484 -0
- NCrystal/plugins.py +49 -0
- NCrystal/test.py +76 -0
- NCrystal/vdos.py +1034 -0
- ncrystal_python-3.9.81.dist-info/LICENSE +206 -0
- ncrystal_python-3.9.81.dist-info/METADATA +515 -0
- ncrystal_python-3.9.81.dist-info/RECORD +53 -0
- ncrystal_python-3.9.81.dist-info/WHEEL +5 -0
- ncrystal_python-3.9.81.dist-info/entry_points.txt +10 -0
- ncrystal_python-3.9.81.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
|
|
2
|
+
################################################################################
|
|
3
|
+
## ##
|
|
4
|
+
## This file is part of NCrystal (see https://mctools.github.io/ncrystal/) ##
|
|
5
|
+
## ##
|
|
6
|
+
## Copyright 2015-2024 NCrystal developers ##
|
|
7
|
+
## ##
|
|
8
|
+
## Licensed under the Apache License, Version 2.0 (the "License"); ##
|
|
9
|
+
## you may not use this file except in compliance with the License. ##
|
|
10
|
+
## You may obtain a copy of the License at ##
|
|
11
|
+
## ##
|
|
12
|
+
## http://www.apache.org/licenses/LICENSE-2.0 ##
|
|
13
|
+
## ##
|
|
14
|
+
## Unless required by applicable law or agreed to in writing, software ##
|
|
15
|
+
## distributed under the License is distributed on an "AS IS" BASIS, ##
|
|
16
|
+
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ##
|
|
17
|
+
## See the License for the specific language governing permissions and ##
|
|
18
|
+
## limitations under the License. ##
|
|
19
|
+
## ##
|
|
20
|
+
################################################################################
|
|
21
|
+
|
|
22
|
+
from ._cliimpl import ( create_ArgumentParser,
|
|
23
|
+
cli_entry_point,
|
|
24
|
+
print )
|
|
25
|
+
from ._common import write_text
|
|
26
|
+
from .constants import ( constant_planck, constant_boltzmann )
|
|
27
|
+
from .vdos import vdos_units_2_eV
|
|
28
|
+
import pathlib
|
|
29
|
+
units_2_fact = vdos_units_2_eV
|
|
30
|
+
units_opts = ', '.join(sorted(units_2_fact.keys()))
|
|
31
|
+
|
|
32
|
+
_cache_np=[None]
|
|
33
|
+
def get_numpy():
|
|
34
|
+
if _cache_np[0] is None:
|
|
35
|
+
from ._numpy import _ensure_numpy, _np
|
|
36
|
+
_ensure_numpy()
|
|
37
|
+
_cache_np[0] = _np
|
|
38
|
+
return _cache_np[0]
|
|
39
|
+
|
|
40
|
+
def parseArgs( progname, arglist, return_parser=False ):
|
|
41
|
+
import textwrap
|
|
42
|
+
helpw = 60
|
|
43
|
+
descrw = helpw + 22
|
|
44
|
+
|
|
45
|
+
descr="""WARNING: This script is somewhat outdated. Many users will instead
|
|
46
|
+
wish to use the PhononDOSAnalyser helper class from the Python API in the
|
|
47
|
+
NCrystal.vdos python module. For more information, see the jupyter notebook
|
|
48
|
+
about "Adding phonon information" on:
|
|
49
|
+
https://github.com/mctools/ncrystal-notebooks
|
|
50
|
+
|
|
51
|
+
Script which can read vdos curves from either .ncmat files or simple two-column
|
|
52
|
+
ascii text files (the two columns being energy grid and density) and help users
|
|
53
|
+
prepare output suitable for inclusion in .ncmat files. When the input is not an
|
|
54
|
+
.ncmat file, the user must specify the energy grid units by adding a line in the file
|
|
55
|
+
like for instance "#unit:THz" or "#unit:meV".
|
|
56
|
+
|
|
57
|
+
In case of NCMAT files with more than one VDOS, just post-fix the filename with
|
|
58
|
+
the element to investigate, separated by '@@', e.g. "Al2O3.ncmat@@O". In case of
|
|
59
|
+
.txt files with more than 2 columns, select the column representing the VDOS
|
|
60
|
+
density in the same manner, e.g. "Al2O3.txt@2" (the first column is always the
|
|
61
|
+
energy or frequency).
|
|
62
|
+
|
|
63
|
+
Thus it is possible to plot the curve, compare against vdos curves from other
|
|
64
|
+
.ncmat files, apply low-E truncation (cutoff), regularise binning, and overlay
|
|
65
|
+
with an ideal Debye spectrum for a given Debye energy or Debye
|
|
66
|
+
temperature. Finally, when running without --plot, it will output the resulting
|
|
67
|
+
spectrum into format which is ready for inclusion in .ncmat files.
|
|
68
|
+
|
|
69
|
+
"""
|
|
70
|
+
descr = '\n\n'.join(textwrap.fill(e,descrw) for e in descr.split('\n\n'))
|
|
71
|
+
|
|
72
|
+
from argparse import RawTextHelpFormatter
|
|
73
|
+
parser = create_ArgumentParser( prog = progname,
|
|
74
|
+
description=descr,
|
|
75
|
+
formatter_class=RawTextHelpFormatter )
|
|
76
|
+
def wrap(t):
|
|
77
|
+
return textwrap.fill(' '.join(t.split()),width=helpw)
|
|
78
|
+
|
|
79
|
+
parser.add_argument("FILE",help=wrap("Either .ncmat file with VDOS or a"
|
|
80
|
+
" text file with two colums of data:"
|
|
81
|
+
" egrid and density."))
|
|
82
|
+
parser.add_argument('--plot','-p', action='store_true',help='Plot extracted spectrum')
|
|
83
|
+
parser.add_argument("--cutoff",'-c',nargs='+',default=None,type=float,
|
|
84
|
+
help=wrap("""Emin cutoff points in eV (more than one can
|
|
85
|
+
be provided for simultaneous inspection with --plot)."""))
|
|
86
|
+
parser.add_argument("--unit",'-u',default='meV',type=str,
|
|
87
|
+
help=wrap('Visualisation unit (ignored unless'
|
|
88
|
+
' --plot is supplied). Defaults to meV. '
|
|
89
|
+
f'Possible options are {units_opts}.'))
|
|
90
|
+
parser.add_argument("--ref",'-r',nargs='+',
|
|
91
|
+
action='append',type=str,
|
|
92
|
+
help=wrap("""Optionally provide list of .ncmat files
|
|
93
|
+
with vdos data, to superimpose on plots."""))
|
|
94
|
+
parser.add_argument("--forceregular",'-f',type=int,nargs='?',default=0,
|
|
95
|
+
help=wrap("""Optionally provide this argument to
|
|
96
|
+
reparameterise with that amount of linearly spaced
|
|
97
|
+
points in [emin,emax+epsilon], where epsilon is chosen
|
|
98
|
+
so the grid can be extended to 0 with a whole number of
|
|
99
|
+
bins. This format will be directly used by NCrystal
|
|
100
|
+
without on-the-fly reparameterisation upon loading."""))
|
|
101
|
+
parser.add_argument("--debye",'-d',nargs='?',default='',type=str,
|
|
102
|
+
help=wrap("""Set to debye temperature (unit K) or egrid
|
|
103
|
+
point (units like meV, eV, THz, ...) in order to plot
|
|
104
|
+
Debye spectrum with that parameter on top."""))
|
|
105
|
+
parser.add_argument('--g1',default=0.0,type=float,metavar='TEMP',
|
|
106
|
+
help=wrap("""Use with --plot to show Sjolander's G1
|
|
107
|
+
function at the indicated temperature value (in kelvin)
|
|
108
|
+
instead of the DOS directly. This is the Symmetric G1
|
|
109
|
+
fct without a detailed balance factor, and it will be
|
|
110
|
+
plotted assuming gamma0=1.0."""))
|
|
111
|
+
parser.add_argument('--stdout',action='store_true',help=wrap("""Produce no
|
|
112
|
+
output file but print vdos_egrid and vdos_density lines
|
|
113
|
+
to stdout (for scripting)"""))
|
|
114
|
+
|
|
115
|
+
#We could expand the env var name to account for any namespace:
|
|
116
|
+
# from ._common import expand_envname
|
|
117
|
+
# dpienvvar = expand_envname('DPI')
|
|
118
|
+
#But that will break the unit tests, so we just use:
|
|
119
|
+
dpienvvar = 'NCRYSTAL_DPI'
|
|
120
|
+
|
|
121
|
+
dpi_default=200
|
|
122
|
+
parser.add_argument('--dpi', default=-1,type=int,
|
|
123
|
+
help=wrap(f"""Change plot resolution. Set to 0 to leave
|
|
124
|
+
matplotlib defaults alone. (default value is
|
|
125
|
+
{dpi_default}, or whatever the {dpienvvar} env var is
|
|
126
|
+
set to)."""))
|
|
127
|
+
|
|
128
|
+
if return_parser:
|
|
129
|
+
return parser
|
|
130
|
+
args = parser.parse_args(arglist)
|
|
131
|
+
|
|
132
|
+
if args.forceregular is None:
|
|
133
|
+
parser.error('Missing argument (number of points) to --forceregular.')
|
|
134
|
+
|
|
135
|
+
if args.dpi==-1:
|
|
136
|
+
from ._common import ncgetenv_int_nonneg
|
|
137
|
+
args.dpi = ncgetenv_int_nonneg('DPI',dpi_default)
|
|
138
|
+
if args.dpi > 3000:
|
|
139
|
+
parser.error('Too high DPI value requested.')
|
|
140
|
+
|
|
141
|
+
if args.ref:
|
|
142
|
+
args.ref = [item for sublist in args.ref for item in sublist]
|
|
143
|
+
if args.ref and not args.plot:
|
|
144
|
+
parser.error('Option --ref requires --plot')
|
|
145
|
+
if args.unit and args.unit not in units_2_fact:
|
|
146
|
+
parser.error(f'Unknown unit {args.unit}. Valid options are {units_opts}')
|
|
147
|
+
if args.debye and not args.plot:
|
|
148
|
+
parser.error('Option --debye requires --plot')
|
|
149
|
+
if args.stdout and args.plot:
|
|
150
|
+
parser.error('Option --stdout can not be used with --plot')
|
|
151
|
+
if args.cutoff and len(args.cutoff)>1 and not args.plot:
|
|
152
|
+
parser.error('Option --cutoff can only have one argument when not using --plot')
|
|
153
|
+
if args.cutoff and len(args.cutoff)>1 and args.forceregular:
|
|
154
|
+
parser.error('Option --cutoff can only have one argument when using --forceregular')
|
|
155
|
+
|
|
156
|
+
if args.debye:
|
|
157
|
+
if args.debye.endswith('K'):
|
|
158
|
+
args.debye = float(args.debye[0:-1])*constant_boltzmann
|
|
159
|
+
else:
|
|
160
|
+
#find (longest, so "meV" does not trigger "eV") fitting unit:
|
|
161
|
+
ll=[ (len(u),u) for u in units_2_fact.keys() if args.debye.endswith(u) ]
|
|
162
|
+
ll.sort()
|
|
163
|
+
if not ll:
|
|
164
|
+
parser.error("Option --debye requires unit (see --help)")
|
|
165
|
+
unit = ll[-1][1]
|
|
166
|
+
args.debye = units_2_fact[unit] * float(args.debye[0:-len(unit)])
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
return args
|
|
170
|
+
|
|
171
|
+
def create_argparser_for_sphinx( progname ):
|
|
172
|
+
return parseArgs(progname,[],return_parser=True)
|
|
173
|
+
|
|
174
|
+
def decodeFileName(filename):
|
|
175
|
+
if '@@' in filename:
|
|
176
|
+
path,select = filename.split('@@',1)
|
|
177
|
+
else:
|
|
178
|
+
path,select = filename,None
|
|
179
|
+
import os
|
|
180
|
+
bn=os.path.basename(path)
|
|
181
|
+
return dict(path=path,select=select,basename=bn,
|
|
182
|
+
title=bn if not select else '%s in %s'%((select if not select.isdigit(
|
|
183
|
+
) else f'column #{select}'),bn))
|
|
184
|
+
|
|
185
|
+
def getVDOSFromFile(fn):
|
|
186
|
+
fnd = decodeFileName(fn)
|
|
187
|
+
if fnd['basename'].endswith('.ncmat'):
|
|
188
|
+
return getVDOSFromNCMAT(fn)
|
|
189
|
+
return getVDOSFromTXT(fn)
|
|
190
|
+
|
|
191
|
+
def getVDOSFromTXT(fn):
|
|
192
|
+
fn = decodeFileName(fn)
|
|
193
|
+
select=None
|
|
194
|
+
if fn['select']:
|
|
195
|
+
assert fn['select'].isdigit(),"selection must be column number"
|
|
196
|
+
select = int(fn['select'])
|
|
197
|
+
#figure out unit:
|
|
198
|
+
unit=None
|
|
199
|
+
with open(fn['path']) as fh:
|
|
200
|
+
for ll in fh:
|
|
201
|
+
if ll.startswith('#') and 'unit' in ll:
|
|
202
|
+
_=ll.split('#',1)[1].split(':',1)
|
|
203
|
+
if not len(_)==2:
|
|
204
|
+
continue
|
|
205
|
+
unit=_[1].strip()
|
|
206
|
+
if unit not in units_2_fact.keys():
|
|
207
|
+
raise RuntimeError(f'Unknown unit "{unit}" specified in {fn["path"]}. Valid choices are: {units_opts}')
|
|
208
|
+
break
|
|
209
|
+
if not unit:
|
|
210
|
+
raise RuntimeError(f'Missing energy/frequency unit in {fn["path"]}. Please put initial line with content like "#unit:THz". Valid units are: {units_opts}')
|
|
211
|
+
usecols=None
|
|
212
|
+
if select is not None:
|
|
213
|
+
assert select>0
|
|
214
|
+
usecols=(0,select)
|
|
215
|
+
_ = get_numpy().genfromtxt(fn['path'],dtype=[('egrid','f8'),('density','f8')],usecols=usecols)
|
|
216
|
+
egrid=_['egrid'].copy()
|
|
217
|
+
density=_['density'].copy()
|
|
218
|
+
density /= density.max()
|
|
219
|
+
return egrid.copy()*units_2_fact[unit],density.copy()
|
|
220
|
+
|
|
221
|
+
def getVDOSFromNCMAT(fn):
|
|
222
|
+
fnd = decodeFileName(fn)
|
|
223
|
+
from . import core as NC
|
|
224
|
+
info = NC.createInfo(fnd['path'])
|
|
225
|
+
select = fnd['select']
|
|
226
|
+
di_vdos = [di for di in info.dyninfos if isinstance(di,NC.Info.DI_VDOS)]
|
|
227
|
+
if select is not None:
|
|
228
|
+
ll=[]
|
|
229
|
+
for di in di_vdos:
|
|
230
|
+
dl=di.atomData.displayLabel()
|
|
231
|
+
if dl!=select:
|
|
232
|
+
print(f"NB: Ignoring (due to selection) VDOS for element {dl} in file {fn}.")
|
|
233
|
+
else:
|
|
234
|
+
ll+=[di]
|
|
235
|
+
if not ll:
|
|
236
|
+
raise RuntimeError(f'ERROR: Could not find VDOS in file {fn} for selected element: {select}')
|
|
237
|
+
if not len(ll)==1:
|
|
238
|
+
raise RuntimeError(f'ERROR: Multiple VDOS entries in file {fn} for selected element: {select} (which is rather odd!)')
|
|
239
|
+
di_vdos = ll
|
|
240
|
+
if len(di_vdos)>1:
|
|
241
|
+
s=' '.join(di.atomData.displayLabel() for di in di_vdos)
|
|
242
|
+
raise RuntimeError(f"ERROR: Multiple VDOS entries found in file {fn}. Please select one of them (by putting \"@@<element>\" after the file-name): {s}")
|
|
243
|
+
elif not di_vdos:
|
|
244
|
+
raise RuntimeError(f"ERROR: No vdos found in file {fn}")
|
|
245
|
+
eg,ds = di_vdos[0].vdosOrigEgrid(),di_vdos[0].vdosOrigDensity()
|
|
246
|
+
ds /= ds.max()
|
|
247
|
+
if len(eg)==2:
|
|
248
|
+
get_numpy()
|
|
249
|
+
from ._numpy import _np_linspace
|
|
250
|
+
eg = _np_linspace(eg[0],eg[1],len(ds))
|
|
251
|
+
return eg.copy(),ds.copy()
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
@cli_entry_point
|
|
257
|
+
def main( progname, arglist ):
|
|
258
|
+
args = parseArgs( progname, arglist )
|
|
259
|
+
from ._numpy import _ensure_numpy, _np
|
|
260
|
+
_ensure_numpy()
|
|
261
|
+
|
|
262
|
+
file_decoded = decodeFileName(args.FILE)
|
|
263
|
+
args_file_basename = file_decoded['basename']
|
|
264
|
+
egrid,density = getVDOSFromFile(args.FILE)
|
|
265
|
+
|
|
266
|
+
np = _np
|
|
267
|
+
|
|
268
|
+
assert len(egrid) == len(density)
|
|
269
|
+
print (f"Loaded VDOS with {len(density)} grid points from {args_file_basename}")
|
|
270
|
+
|
|
271
|
+
np = get_numpy()
|
|
272
|
+
def numpy_is_sorted(a):
|
|
273
|
+
return np.all(a[:-1] <= a[1:])
|
|
274
|
+
def numpy_is_strongly_sorted(a):
|
|
275
|
+
return np.all(a[:-1] < a[1:])
|
|
276
|
+
|
|
277
|
+
if not numpy_is_strongly_sorted(egrid):
|
|
278
|
+
for i in range(len(egrid)-1):
|
|
279
|
+
if not egrid[i] < egrid[i+1]:
|
|
280
|
+
print("Problems detected in egrid points with values ",egrid[i],"and",egrid[i+1])
|
|
281
|
+
raise RuntimeError('ERROR: egrid values (first column) of input file are not in sorted'
|
|
282
|
+
+' (ascending) order, or there are identical elements.')
|
|
283
|
+
|
|
284
|
+
cutoffs=[]
|
|
285
|
+
if args.cutoff:
|
|
286
|
+
for c in args.cutoff:
|
|
287
|
+
if c >= egrid[-1]:
|
|
288
|
+
raise RuntimeError(f'ERROR: Cutoff value {c} is higher than highest point in egrid')
|
|
289
|
+
i=np.searchsorted(egrid,c)
|
|
290
|
+
assert i==0 or egrid[i-1]<c
|
|
291
|
+
assert egrid[i]>=c
|
|
292
|
+
cutoffs+=[ (i, egrid[i] ) ]
|
|
293
|
+
print(f" => Mapping cutoff value of {c} to grid point at {cutoffs[-1][1]}")
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
if args.forceregular or (not args.plot):
|
|
297
|
+
if applyCutoff(egrid,density,cutoffs)[0][0]<=1e-5:
|
|
298
|
+
raise RuntimeError(f"""
|
|
299
|
+
ERROR: The first value in the loaded egrid is {egrid[0]} which is less than 1e-5eV.
|
|
300
|
+
This is not allowed when using --forceregular or when not using --plot.
|
|
301
|
+
Please use the --cutoff parameter to remove lowest part of input spectrum (perhaps
|
|
302
|
+
after investigating the cutoff value with --plot).
|
|
303
|
+
""")
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
if args.forceregular:
|
|
307
|
+
regularised_egrid,regularised_density = regularise(*applyCutoff(egrid,density,cutoffs),args.forceregular)
|
|
308
|
+
|
|
309
|
+
if args.plot:
|
|
310
|
+
vis_unit=args.unit
|
|
311
|
+
vis_unit_fact = 1.0 / units_2_fact[vis_unit]
|
|
312
|
+
|
|
313
|
+
def plt_plot(egrid,dos,*aargs,**kwargs):
|
|
314
|
+
if args.g1 and args.g1 > 0.0:
|
|
315
|
+
#G1 = f(E)/(E*2*gamma0*sinh(E/2kT)) [symmetric G1 that is]
|
|
316
|
+
#u = E/2kT
|
|
317
|
+
#asymmetric means another factor of exp(-u).
|
|
318
|
+
#And exp(-u)/2sinh(u) = exp(-u) / (exp(u)-exp(-u) ) = 1 / ( exp(2u)-1)
|
|
319
|
+
#And exp(+u)/2sinh(u) = exp(+u) / (exp(u)-exp(-u) ) = 1 / ( 1-exp(-2u) )
|
|
320
|
+
#
|
|
321
|
+
#So with gamma0=0 we get:
|
|
322
|
+
egrid_eV = egrid.copy() / vis_unit_fact
|
|
323
|
+
T = args.g1
|
|
324
|
+
#gamma0 = 1.0
|
|
325
|
+
two_u = egrid_eV / ( constant_boltzmann * T )
|
|
326
|
+
#g1asym_neg = dos / ( egrid_eV * -np.expm1(-two_u) )
|
|
327
|
+
#g1asym_pos = dos / ( egrid_eV * np.expm1(two_u) )
|
|
328
|
+
g1sym = dos / (egrid_eV*np.sinh(0.5*two_u))
|
|
329
|
+
plt.plot(egrid,g1sym,*aargs,**kwargs)
|
|
330
|
+
else:
|
|
331
|
+
plt.plot(egrid,dos,*aargs,**kwargs)
|
|
332
|
+
|
|
333
|
+
import matplotlib as mpl
|
|
334
|
+
mpl.rcParams['figure.dpi']=args.dpi
|
|
335
|
+
#ability to quit plot windows with Q:
|
|
336
|
+
if 'keymap.quit' in mpl.rcParams and 'q' not in mpl.rcParams['keymap.quit']:
|
|
337
|
+
mpl.rcParams['keymap.quit'] = tuple(list(mpl.rcParams['keymap.quit'])+['q','Q'])
|
|
338
|
+
import matplotlib.pyplot as plt
|
|
339
|
+
plt.xlabel(vis_unit)
|
|
340
|
+
plt_plot(egrid*vis_unit_fact,density,'o-',label=file_decoded['title'])
|
|
341
|
+
if args.forceregular:
|
|
342
|
+
plt_plot(regularised_egrid*vis_unit_fact,regularised_density,'x-',label='regularised')
|
|
343
|
+
from ._numpy import _np_linspace
|
|
344
|
+
for c_idx, c_val in cutoffs:
|
|
345
|
+
d=density[c_idx]
|
|
346
|
+
# f(x)=k*x^2, f(c_val)=d<=> k*c_val^2 = d <=> k = d/c_val^2
|
|
347
|
+
x=_np_linspace(0.0,c_val,3000)
|
|
348
|
+
plt_plot(x*vis_unit_fact,(d/c_val**2)*(x**2),label=f'with cutoff {c_val} eV')
|
|
349
|
+
if args.debye:
|
|
350
|
+
x=_np_linspace(0.0,max(egrid.max(),args.debye),1000)
|
|
351
|
+
y = np.where( x<=args.debye, x**2 * ( density.max() / args.debye**2 ), 0.0 )
|
|
352
|
+
plt_plot(x*vis_unit_fact,y,
|
|
353
|
+
label=f'Debye spectrum (E_Debye={1000*args.debye:.5}meV, T_Debye={args.debye/constant_boltzmann:.5}K)')
|
|
354
|
+
for r in (args.ref or []):
|
|
355
|
+
eg,ds = getVDOSFromFile(r)
|
|
356
|
+
plt_plot(eg*vis_unit_fact,ds,label=decodeFileName(r)['title'])
|
|
357
|
+
plt.legend()
|
|
358
|
+
plt.title(file_decoded['title'])
|
|
359
|
+
plt.grid(ls=':')
|
|
360
|
+
plt.show()
|
|
361
|
+
return
|
|
362
|
+
|
|
363
|
+
if args.forceregular:
|
|
364
|
+
egrid, density = regularised_egrid,regularised_density
|
|
365
|
+
else:
|
|
366
|
+
egrid, density = applyCutoff(egrid,density,cutoffs)
|
|
367
|
+
|
|
368
|
+
#Check if egrid is linspace:
|
|
369
|
+
binwidth = (egrid[-1]-egrid[0])/(len(egrid)-1)
|
|
370
|
+
is_linspace=True
|
|
371
|
+
if not args.forceregular:
|
|
372
|
+
for i in range(len(egrid)-1):
|
|
373
|
+
bw=egrid[i+1]-egrid[i]
|
|
374
|
+
if abs(binwidth-bw)>0.01*binwidth:
|
|
375
|
+
is_linspace=False
|
|
376
|
+
break
|
|
377
|
+
if is_linspace:
|
|
378
|
+
print('NB: Detected linearly spaced input egrid')
|
|
379
|
+
|
|
380
|
+
#normalise so unity at highest point (gives smaller file sizes):
|
|
381
|
+
density /= density.max()
|
|
382
|
+
|
|
383
|
+
#remove excess trailing zeros
|
|
384
|
+
while len(density)>10 and density[-2]==0.0 and density[-1]==0.0:
|
|
385
|
+
density = density[0:-1]
|
|
386
|
+
egrid = egrid[0:-1]
|
|
387
|
+
|
|
388
|
+
from .ncmat import formatVectorForNCMAT
|
|
389
|
+
|
|
390
|
+
egrid_cnt =''
|
|
391
|
+
if is_linspace:
|
|
392
|
+
egrid_cnt += f' vdos_egrid {egrid[0]:.14} {egrid[-1]:.14}'
|
|
393
|
+
else:
|
|
394
|
+
egrid_cnt += formatVectorForNCMAT('vdos_egrid',egrid)
|
|
395
|
+
egrid_cnt += '\n'
|
|
396
|
+
egrid_cnt += formatVectorForNCMAT('vdos_density',density)
|
|
397
|
+
|
|
398
|
+
if args.stdout:
|
|
399
|
+
print("<<<GENERATED-CONTENT-BEGIN>>>")
|
|
400
|
+
print(egrid_cnt,end='')
|
|
401
|
+
else:
|
|
402
|
+
outfn=pathlib.Path('converted_output.ncmat')
|
|
403
|
+
content = f"""NCMAT v5
|
|
404
|
+
#Autogenerated file from {args_file_basename}.
|
|
405
|
+
@DENSITY
|
|
406
|
+
1.0 g_per_cm3 #FIX{'ME'}!! Please replace with proper value, or remove and optionally provide crystal structure!
|
|
407
|
+
@DYNINFO
|
|
408
|
+
element <UNKNOWN-PLEASE-EDIT>
|
|
409
|
+
fraction 1
|
|
410
|
+
type vdos\n"""
|
|
411
|
+
content += egrid_cnt
|
|
412
|
+
content += '\n'
|
|
413
|
+
write_text( outfn, content )
|
|
414
|
+
print(f"Wrote {outfn}")
|
|
415
|
+
|
|
416
|
+
def applyCutoff(egrid,density,cutoffs):
|
|
417
|
+
if cutoffs:
|
|
418
|
+
assert len(cutoffs)==1
|
|
419
|
+
c_idx,c_val = cutoffs[0]
|
|
420
|
+
return egrid[c_idx:], density[c_idx:]
|
|
421
|
+
return egrid,density
|
|
422
|
+
|
|
423
|
+
def regularise(egrid,density,n):
|
|
424
|
+
|
|
425
|
+
#first step back from any zeroes at the upper end:
|
|
426
|
+
i=1
|
|
427
|
+
while i < len(density) and density[-i]==0.0:
|
|
428
|
+
i += 1
|
|
429
|
+
safepeel = i-2
|
|
430
|
+
if safepeel>=1:
|
|
431
|
+
print (f"Ignoring {safepeel} last points while regularising since last {safepeel+1} points are 0.")
|
|
432
|
+
egrid,density = egrid[0:-(safepeel)],density[0:-(safepeel)]
|
|
433
|
+
emin,emax=egrid[0],egrid[-1]
|
|
434
|
+
print('old range',emin,emax)
|
|
435
|
+
THZ = constant_planck*1e12
|
|
436
|
+
print('old range [THZ]',emin/THZ,emax/THZ)
|
|
437
|
+
|
|
438
|
+
np = get_numpy()
|
|
439
|
+
for k in range(1,1000000000):
|
|
440
|
+
#k is number of bins below emin, an integral number by definition in a regularised grid.
|
|
441
|
+
binwidth = emin/k
|
|
442
|
+
nbins=int(np.floor((emax-emin)/binwidth))+1
|
|
443
|
+
eps = (emin+nbins*binwidth)-emax
|
|
444
|
+
assert eps>=0.0
|
|
445
|
+
if nbins+1 >= n:
|
|
446
|
+
break
|
|
447
|
+
n=nbins+1
|
|
448
|
+
binwidth = emin/k
|
|
449
|
+
new_emax = emin + (n-1) * binwidth
|
|
450
|
+
if abs( (new_emax-binwidth) - emax ) < 1e-3*binwidth:
|
|
451
|
+
nbins -= 1
|
|
452
|
+
n -= 1
|
|
453
|
+
new_emax -= binwidth
|
|
454
|
+
print (f" ==> Choosing regular grid with n={n} pts from emin={emin} to emax={new_emax} ({new_emax-emax} beyond old emax)")
|
|
455
|
+
assert new_emax >= emax-binwidth*1.001e-3
|
|
456
|
+
from ._numpy import _np_linspace
|
|
457
|
+
new_egrid = _np_linspace(emin,new_emax,n)
|
|
458
|
+
test=new_egrid[0] / ( (new_egrid[-1]-new_egrid[0])/(len(new_egrid)-1) )
|
|
459
|
+
assert abs(round(test)-test)<1e-6,f'{test}'
|
|
460
|
+
np = get_numpy()
|
|
461
|
+
new_density = np.interp(new_egrid,egrid,density, left=0.0, right=0.0)
|
|
462
|
+
print('last density values in new grid:',new_density[-5:])
|
|
463
|
+
return new_egrid,new_density
|