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
NCrystal/ncmat2cpp.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
"""Module providing utilities for embedding text data in C++ code"""
|
|
23
|
+
|
|
24
|
+
def ncmat2cpp( *input_files_or_text_data,
|
|
25
|
+
quiet = False,
|
|
26
|
+
cppfunctionname='registerData',
|
|
27
|
+
compact=False,
|
|
28
|
+
width=140,
|
|
29
|
+
validate=False,
|
|
30
|
+
extra_includes=None,
|
|
31
|
+
outfile = None,
|
|
32
|
+
regfctname=None ):
|
|
33
|
+
"""Function which can be used to embed the content of .ncmat files (or
|
|
34
|
+
actually any ASCII/UTF8 excoded text files) directly into a C++ library. It
|
|
35
|
+
does so by reading the files and creating C++ code which keeps the contents
|
|
36
|
+
of the files in static UTF8-encoded strings, and registers those strings
|
|
37
|
+
with the NCrystal C++ API, using the original filename as key. Naturally,
|
|
38
|
+
for this to work the resulting C++ code should be stored in a file, and that
|
|
39
|
+
file must be compiled along with the rest of the users C++ code, and the
|
|
40
|
+
enclosing function must be invoked.
|
|
41
|
+
|
|
42
|
+
Note that despite the name of this function, it can actually be used to
|
|
43
|
+
process any text string.
|
|
44
|
+
|
|
45
|
+
If outfile is not None, the contents will be stored in that file. In any
|
|
46
|
+
case, the C++ code will be returned as a string.
|
|
47
|
+
|
|
48
|
+
if regfctname is None, the C++ function used for registering the in-memory
|
|
49
|
+
file data will be assumed to be
|
|
50
|
+
"NCrystal::registerInMemoryStaticFileData(const std::string&,const char*)".
|
|
51
|
+
|
|
52
|
+
For a meaning of the the other parameters, see 'ncrystal_ncmat2cpp --help'.
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
#NOTE: The above doc-string should be kept in sync with argparse help text
|
|
56
|
+
#in _ncmat2cpp_impl.py.
|
|
57
|
+
|
|
58
|
+
#NOTE: ncrystal_ncmat2cpp is a special command-line script, since the
|
|
59
|
+
#NCrystal CMake code needs to be able to invoke _cli_ncmat2cpp.py directly
|
|
60
|
+
#as a standalone script, in a mode where no other of the NCrystal python
|
|
61
|
+
#modules are imported. Therefore, all of the actual code implementing the
|
|
62
|
+
#ncmat2cpp functionality needs to reside in _cli_ncmat2cpp.py, rather than
|
|
63
|
+
#here in ncmat2cpp.py. This is the opposite of how most other command-line
|
|
64
|
+
#scripts are implemented, with the bulk of the implementation residing in
|
|
65
|
+
#the Python API, and the command-line script being a wrapper around it.
|
|
66
|
+
|
|
67
|
+
if not input_files_or_text_data:
|
|
68
|
+
from .exceptions import NCBadInput
|
|
69
|
+
raise NCBadInput('No files or text data provided')
|
|
70
|
+
|
|
71
|
+
from ._ncmat2cpp_impl import files2cppcode
|
|
72
|
+
return files2cppcode( infiles = input_files_or_text_data,
|
|
73
|
+
quiet = quiet,
|
|
74
|
+
outfile = outfile,
|
|
75
|
+
cppfunctionname = cppfunctionname,
|
|
76
|
+
compact = compact,
|
|
77
|
+
width = width,
|
|
78
|
+
validate = validate,
|
|
79
|
+
extra_includes = extra_includes,
|
|
80
|
+
regfctname = regfctname )
|
NCrystal/obsolete.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
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
|
+
"""
|
|
23
|
+
|
|
24
|
+
Obsolete functions
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from ._common import ncgetenv_bool as _ncgetenv_bool
|
|
29
|
+
|
|
30
|
+
def decodecfg_packfact(cfgstr):
|
|
31
|
+
"""OBSOLETE FUNCTION (always returns 1.0 now)."""
|
|
32
|
+
from . import _common as nc_common
|
|
33
|
+
nc_common.warn('The decodecfg_packfact function is obsolete and now always'
|
|
34
|
+
' returns 1.0. It will be removed in a future release.')
|
|
35
|
+
return 1.0
|
|
36
|
+
|
|
37
|
+
def getFileContents(name):
|
|
38
|
+
"""OBSOLETE FUNCTION: Use createTextData(..).rawData instead."""
|
|
39
|
+
from . import _common as nc_common
|
|
40
|
+
nc_common.warn('The getFileContents(name) function is obsolete. Please use the'
|
|
41
|
+
' createTextData function instead (specifically calling '
|
|
42
|
+
'createTextData(name).rawData will produce the same results getFileContents(name).')
|
|
43
|
+
from .core import createTextData
|
|
44
|
+
return createTextData(name).rawData
|
|
45
|
+
|
|
46
|
+
def clearInfoCaches():
|
|
47
|
+
"""Deprecated. Does the same as clearCaches()"""
|
|
48
|
+
from . import _common as nc_common
|
|
49
|
+
nc_common.warn('The clearInfoCaches function is deprecated. Please'
|
|
50
|
+
' call the clearCaches() function instead.')
|
|
51
|
+
from .core import clearCaches
|
|
52
|
+
clearCaches()
|
|
53
|
+
|
|
54
|
+
def disableCaching():
|
|
55
|
+
"""Obsolete function. Instead call clearCaches() as needed."""
|
|
56
|
+
raise RuntimeError('The disableCaching() function has been obsoleted and no longer works. Users can'
|
|
57
|
+
+' instead call the clearCaches() function if really needed to clear the caches.')
|
|
58
|
+
|
|
59
|
+
def enableCaching():
|
|
60
|
+
"""Obsolete function. Instead call clearCaches() as needed."""
|
|
61
|
+
raise RuntimeError('The enableCaching function has been removed. Users can'
|
|
62
|
+
+' instead call the clearCaches function if really needed to clear the caches.')
|
|
63
|
+
|
|
64
|
+
if not _ncgetenv_bool('NOPYOBSOLETE'):
|
|
65
|
+
_ = globals()
|
|
66
|
+
for _k in [_k for _k in _.keys() if ( hasattr(_k,'startswith') and not _k.startswith('_') )]:
|
|
67
|
+
del _[_k]
|
NCrystal/plot.py
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
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
|
+
"""
|
|
23
|
+
|
|
24
|
+
Utility functions related to plotting.
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def plot_xsect( material, *, mode='wl', do_show = True, do_newfig = True,
|
|
29
|
+
npts=5000, xmin = None, xmax = None, ymin=None, ymax=None,
|
|
30
|
+
logx = None, logy=None,
|
|
31
|
+
scatter_breakdown = True, xsmode='peratom',
|
|
32
|
+
show_absorption = True, show_scattering = True,
|
|
33
|
+
do_legend = True, do_grid = True, color = None,
|
|
34
|
+
labelfct = None, only_total = False, extra_cfg=None,
|
|
35
|
+
title = None ):
|
|
36
|
+
"""Quick plot (with matplotlib) showing the cross sections produced by
|
|
37
|
+
current material. The mode parameter can be either 'wl' or 'ekin', and
|
|
38
|
+
logx/logy can be set to None or a boolean to control whether the
|
|
39
|
+
corresponding axis should be logarithmic (None indicates False when
|
|
40
|
+
mode=='wl' and True when mode=='ekin'). Set scatter_breakdown to False
|
|
41
|
+
to show the total scattering contribution rather than a breakdown into
|
|
42
|
+
components (only affects materials that are not preloaded).
|
|
43
|
+
|
|
44
|
+
The extra_cfg parameter can be used to append cfg parameters
|
|
45
|
+
(e.g. "comp=bragg,incoh_elas;temp=50K").
|
|
46
|
+
|
|
47
|
+
Use the plot_xsects (note the trailing "s") function instead to compare
|
|
48
|
+
cross sections from multiple materials.
|
|
49
|
+
|
|
50
|
+
"""
|
|
51
|
+
from . import misc as nc_misc
|
|
52
|
+
matsrc = nc_misc.MaterialSource( material, cfg_params = extra_cfg )
|
|
53
|
+
assert mode in ('wl','ekin')
|
|
54
|
+
assert logx is None or logx in (True,False)
|
|
55
|
+
assert xsmode in ('macroscopic','peratom')
|
|
56
|
+
loadedmat = matsrc.load()
|
|
57
|
+
if not loadedmat.scatter and not loadedmat.absorption:
|
|
58
|
+
from .exceptions import NCBadInput
|
|
59
|
+
raise NCBadInput('Can not produce plots for material source which contains no physics processes')
|
|
60
|
+
do_absn = show_absorption and bool(loadedmat.absorption)
|
|
61
|
+
do_scat = show_scattering and bool(loadedmat.scatter)
|
|
62
|
+
labelfct = labelfct or ( lambda x : x )
|
|
63
|
+
plt = _import_matplotlib_plt()
|
|
64
|
+
if do_newfig:
|
|
65
|
+
plt.figure()
|
|
66
|
+
from ._numpy import _np_linspace, _np_geomspace, _ensure_numpy
|
|
67
|
+
_ensure_numpy()
|
|
68
|
+
from .constants import wl2ekin
|
|
69
|
+
from ._common import _palette_Few as _palette
|
|
70
|
+
wlmax = _estimate_longest_interesting_wavelength( loadedmat.info )
|
|
71
|
+
if mode=='wl':
|
|
72
|
+
xmin = 0.0 if xmin is None else xmin
|
|
73
|
+
xmax = wlmax if xmax is None else xmax
|
|
74
|
+
logxydefault = False
|
|
75
|
+
else:
|
|
76
|
+
xmin = ( wl2ekin(wlmax)*1e-3 ) if ( xmin is None ) else ( xmin or 1e-7 )
|
|
77
|
+
xmax = xmax or xmin*1e5
|
|
78
|
+
logxydefault = True
|
|
79
|
+
assert xmax > xmin
|
|
80
|
+
logx = logxydefault if logx is None else bool(logx)
|
|
81
|
+
logy = logxydefault if logy is None else bool(logy)
|
|
82
|
+
x = (_np_geomspace if logx else _np_linspace)( xmin, xmax, npts )
|
|
83
|
+
xsectargs = dict(wl=x) if mode=='wl' else dict(ekin=x)
|
|
84
|
+
if mode=='macroscopic' and not loadedmat.info:
|
|
85
|
+
from .exceptions import NCBadInput
|
|
86
|
+
raise NCBadInput('Can not produce macroscopic cross section'
|
|
87
|
+
' plots for material source which contains'
|
|
88
|
+
' no NCrystal.Info object (and therefore no material density).')
|
|
89
|
+
xsectfactor = ( loadedmat.info.factor_macroscopic_xs ) if ( xsmode=='macroscopic' and loadedmat.info ) else 1.0
|
|
90
|
+
do_scatter_breakdown = do_scat and scatter_breakdown and not matsrc.is_preloaded and not loadedmat.scatter.isNull()
|
|
91
|
+
xs_s = xsectfactor * loadedmat.scatter.xsect(**xsectargs) if do_scat else None
|
|
92
|
+
xs_a = xsectfactor * loadedmat.absorption.xsect(**xsectargs) if do_absn else None
|
|
93
|
+
xs_tot = xs_s + xs_a if ( do_scat and do_absn ) else ( xs_s if do_scat else xs_a )
|
|
94
|
+
nullxs = not ( xs_tot.max() > 0.0 )
|
|
95
|
+
if nullxs and logy:
|
|
96
|
+
from ._common import warn
|
|
97
|
+
warn('Could not set log scale since curves are 0.0 everywhere')
|
|
98
|
+
logy=False
|
|
99
|
+
|
|
100
|
+
plotcalls = []
|
|
101
|
+
def add_plot(x,y,*a,**kw):
|
|
102
|
+
if y is not None:
|
|
103
|
+
#if logy:
|
|
104
|
+
# nonzeroy = ( y > 0.0 )
|
|
105
|
+
# x,y = x[nonzeroy],y[nonzeroy]
|
|
106
|
+
plotcalls.append( (x,y,a,kw) )
|
|
107
|
+
if do_scatter_breakdown:
|
|
108
|
+
lblmap = { 'coh_elas':'Coherent elastic',
|
|
109
|
+
'incoh_elas':'Incoherent elastic',
|
|
110
|
+
'inelas':'Inelastic',
|
|
111
|
+
'sans':'SANS'}
|
|
112
|
+
colmap = { 'coh_elas':'blue',
|
|
113
|
+
'incoh_elas':'yellow',
|
|
114
|
+
'inelas':'green',
|
|
115
|
+
'sans':'pink'}#nb unused: brown, gray (but brown looks similar to yellow)
|
|
116
|
+
|
|
117
|
+
assert set(lblmap.keys())==set(nc_misc.standard_comp_types),"update lblmap"
|
|
118
|
+
assert set(colmap.keys())==set(nc_misc.standard_comp_types),"update colmap"
|
|
119
|
+
for comp in nc_misc.detect_scattering_components( matsrc ):
|
|
120
|
+
_xs_comp = xsectfactor * matsrc.load(f'comp={comp}',doInfo=False,doAbsorption=False).scatter.xsect(**xsectargs)
|
|
121
|
+
add_plot(x,_xs_comp,label=labelfct(lblmap[comp]),
|
|
122
|
+
color = color or _palette[colmap[comp]] )
|
|
123
|
+
else:
|
|
124
|
+
add_plot(x,xs_s,label=labelfct('Scattering'),
|
|
125
|
+
color = color or _palette['blue'])
|
|
126
|
+
add_plot(x,xs_a,label=labelfct('Absorption'),color = color or _palette['purple'])
|
|
127
|
+
|
|
128
|
+
if only_total:
|
|
129
|
+
plotcalls=[]
|
|
130
|
+
if only_total or len(plotcalls) > 1:
|
|
131
|
+
totlabel = 'Total' if ( do_absn and do_scat ) else ( 'Total scattering' if do_scat else 'Total absorption' )
|
|
132
|
+
add_plot(x,xs_tot,label=labelfct(totlabel),
|
|
133
|
+
color=color or _palette['red'])
|
|
134
|
+
for x,y,a,kw in plotcalls:
|
|
135
|
+
plt.plot(x,y,*a,**kw)
|
|
136
|
+
|
|
137
|
+
plt.xlabel('Neutron wavelength (angstrom)' if mode=='wl' else 'Neutron energy (eV)')
|
|
138
|
+
plt.ylabel('Macroscopic cross section (1/cm)' if xsmode=='macroscopic' else 'Cross section per atom (barn)')
|
|
139
|
+
|
|
140
|
+
auto_ymin = ( None if logy else 0.0)
|
|
141
|
+
auto_ymax = ( 1.0 if nullxs else None )
|
|
142
|
+
ymin = ( auto_ymin if ymin is None else ymin )
|
|
143
|
+
ymax = ( auto_ymax if ymax is None else ymax )
|
|
144
|
+
if ymin is not None or ymax is not None:
|
|
145
|
+
if ymin is not None and ymax is not None and not ymax>ymin:
|
|
146
|
+
from ._common import warn
|
|
147
|
+
def _fmt(x):
|
|
148
|
+
return ( 'auto' if x is None else x )
|
|
149
|
+
warn('ymin/ymax parameters would lead to a plot range of'
|
|
150
|
+
f' [{ymin},{ymax}]. Reverting to [{_fmt(auto_ymin)},{_fmt(auto_ymax)}].')
|
|
151
|
+
ymin,ymax = auto_ymin,auto_ymax
|
|
152
|
+
plt.ylim(ymin,ymax)
|
|
153
|
+
plt.xlim(xmin,xmax)
|
|
154
|
+
if title is not False:
|
|
155
|
+
plt.title(title or matsrc.plotlabel)
|
|
156
|
+
_plt_final(do_grid,do_legend,do_show,logy=logy,logx=logx,plt=plt)
|
|
157
|
+
|
|
158
|
+
def plot_xsects( *materials, **plotkwargs ):
|
|
159
|
+
"""Compares cross sections of multiple materials. Accepts similar keywords
|
|
160
|
+
as the plot_xsect function."""
|
|
161
|
+
from .misc import MaterialSource
|
|
162
|
+
mats = [ MaterialSource(m) for m in materials ]
|
|
163
|
+
do={}
|
|
164
|
+
for e in ['do_newfig','do_legend','do_grid','do_show','logy','logx']:
|
|
165
|
+
do[e] = plotkwargs.get(e,None if e in ('logy','logx') else True)
|
|
166
|
+
plotkwargs[e] = False
|
|
167
|
+
title = plotkwargs.get('title')
|
|
168
|
+
plotkwargs['title'] = False
|
|
169
|
+
plotkwargs['scatter_breakdown']=False
|
|
170
|
+
plotkwargs['only_total'] = True
|
|
171
|
+
plt = _import_matplotlib_plt()
|
|
172
|
+
if do['do_newfig']:
|
|
173
|
+
plt.figure()
|
|
174
|
+
col_ordered = _get_col_ordered()
|
|
175
|
+
for i,m in enumerate(mats):
|
|
176
|
+
plotkwargs['color'] = col_ordered[i%len(col_ordered)]
|
|
177
|
+
plotkwargs['labelfct'] = lambda _ : m.plotlabel
|
|
178
|
+
if i+1 == len(mats):
|
|
179
|
+
#the last:
|
|
180
|
+
plotkwargs['logy'] = do['logy']
|
|
181
|
+
plotkwargs['logx'] = do['logx']
|
|
182
|
+
plot_xsect(m,**plotkwargs)
|
|
183
|
+
if title:
|
|
184
|
+
plt.title(title)
|
|
185
|
+
_plt_final(do['do_grid'],do['do_legend'],do['do_show'],plt=plt)
|
|
186
|
+
|
|
187
|
+
def plot_vdos( *vdos, unit='meV',
|
|
188
|
+
show_orig_data = False,
|
|
189
|
+
show_normalised = True,
|
|
190
|
+
do_show = True, do_newfig = True,
|
|
191
|
+
npts_parabola=5000, logy=False,
|
|
192
|
+
do_legend = True, do_grid = True, color = None,
|
|
193
|
+
labelfct = None ):
|
|
194
|
+
"""
|
|
195
|
+
Quick plot (with matplotlib) showing the requested VDOS curve(s).
|
|
196
|
+
"""
|
|
197
|
+
from . import misc as nc_misc
|
|
198
|
+
from . import vdos as nc_vdos
|
|
199
|
+
#from ._common import _palette_Few as _palette
|
|
200
|
+
from ._numpy import _np_linspace,_ensure_numpy, _np
|
|
201
|
+
unit_name, unit_value = nc_vdos._parsevdosunit( unit )
|
|
202
|
+
|
|
203
|
+
#from ._numpy import _np_linspace, _np_geomspace, _np, _ensure_numpy
|
|
204
|
+
#_ensure_numpy()
|
|
205
|
+
#from .constants import wl2ekin
|
|
206
|
+
|
|
207
|
+
vdoslist = [ ( nc_misc.AnyVDOS(v),False) for v in vdos ]
|
|
208
|
+
if show_orig_data:
|
|
209
|
+
ll=[]
|
|
210
|
+
for v,_ in vdoslist:
|
|
211
|
+
ll.append( (v, False ) )
|
|
212
|
+
if v.has_orig:
|
|
213
|
+
ll.append( (v, True ) )
|
|
214
|
+
vdoslist = ll
|
|
215
|
+
|
|
216
|
+
plt = _import_matplotlib_plt()
|
|
217
|
+
if do_newfig:
|
|
218
|
+
plt.figure()
|
|
219
|
+
|
|
220
|
+
col_ordered = _get_col_ordered()
|
|
221
|
+
|
|
222
|
+
for ivdos,(vdos,use_orig) in enumerate(vdoslist):
|
|
223
|
+
lbl = vdos.label or 'VDOS#%i'%(ivdos+1)
|
|
224
|
+
if use_orig:
|
|
225
|
+
lbl += ' (original)'
|
|
226
|
+
if labelfct:
|
|
227
|
+
lbl = labelfct( lbl )
|
|
228
|
+
x = vdos.egrid( orig = use_orig ) / unit_value
|
|
229
|
+
y = vdos.dos( orig = use_orig, norm = show_normalised ) * unit_value
|
|
230
|
+
color = col_ordered[ivdos%len(col_ordered)]
|
|
231
|
+
if x[0] > 0.0 and npts_parabola:
|
|
232
|
+
_ensure_numpy()
|
|
233
|
+
xp = _np_linspace( 0.0, x[0], npts_parabola )
|
|
234
|
+
plt.plot(xp, y[0] *(xp/x[0])**2, color = color, ls = ':' )
|
|
235
|
+
if y[-1] > 0.0:
|
|
236
|
+
_ensure_numpy()
|
|
237
|
+
x = _np.append( x, [ x[-1] ] )
|
|
238
|
+
y = _np.append( y, [ 0.0 ] )
|
|
239
|
+
plt.plot(x,y,color = color, label = lbl, marker='.' )
|
|
240
|
+
|
|
241
|
+
plt.xlabel(f'Frequency ({unit_name})')
|
|
242
|
+
plt.ylabel('VDOS (arbitrary units)')
|
|
243
|
+
_plt_final(do_grid,do_legend,do_show,logy=logy,plt=plt)
|
|
244
|
+
|
|
245
|
+
def plot_knl( kernel, do_newfig = True, do_show = True, do_grid = True, logz=False, phasespace_curves = None,
|
|
246
|
+
clim=None, xlim = None, ylim = None):
|
|
247
|
+
"""Quick plot (with matplotlib) showing the requested scattering kernel in
|
|
248
|
+
in S(alpha,beta) format). The kernel is assume to be a dictionary with keys
|
|
249
|
+
'alpha', 'beta', and 'sab' associated with values being numpy arrays
|
|
250
|
+
containing the relevant info. Optionally, keys 'egrid' and 'suggestedEmax'
|
|
251
|
+
can be present, and will be used to estimate Emax - the highest intended
|
|
252
|
+
neutron energy for which the table is to be used. Emax is only used to plot
|
|
253
|
+
the kinematic bound of Emax.
|
|
254
|
+
"""
|
|
255
|
+
#estimate emax:
|
|
256
|
+
_egrid = kernel.get('egrid')
|
|
257
|
+
_suggestedEmax = kernel.get('suggestedEmax')
|
|
258
|
+
emax = None
|
|
259
|
+
if _suggestedEmax and _suggestedEmax>0.0:
|
|
260
|
+
emax = _suggestedEmax
|
|
261
|
+
if emax is None and _egrid is not None:
|
|
262
|
+
import numbers
|
|
263
|
+
if isinstance(_egrid,numbers.Real):
|
|
264
|
+
emax = float(_egrid)
|
|
265
|
+
elif len(_egrid)==1:
|
|
266
|
+
emax = _egrid[0]
|
|
267
|
+
elif len(_egrid)==3:
|
|
268
|
+
emax = _egrid[1]
|
|
269
|
+
elif len(_egrid)>3:
|
|
270
|
+
emax = _egrid[-1]
|
|
271
|
+
|
|
272
|
+
alpha = kernel['alpha']
|
|
273
|
+
beta = kernel['beta']
|
|
274
|
+
sab = kernel['sab']
|
|
275
|
+
assert len(alpha)>=5
|
|
276
|
+
assert len(beta)>=5
|
|
277
|
+
assert len(alpha)*len(beta) == len(sab)
|
|
278
|
+
|
|
279
|
+
from ._numpy import _ensure_numpy, _np, _np_linspace
|
|
280
|
+
_ensure_numpy()
|
|
281
|
+
plt = _import_matplotlib_plt()
|
|
282
|
+
if do_newfig:
|
|
283
|
+
plt.figure()
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def plotedges(x):
|
|
287
|
+
"""given array [a,b,c,...,x,y,z] returns [a,(a+b)2,(b+c)/2,...,(y+z)/2,z]"""
|
|
288
|
+
return _np.concatenate([[x[0]],0.5*(x[:-1]+x[1::1]),[x[-1]]])
|
|
289
|
+
na,nb = len(alpha),len(beta)
|
|
290
|
+
pa = plotedges(alpha)
|
|
291
|
+
pb = plotedges(beta)
|
|
292
|
+
x,y=_np.meshgrid(pa,pb)
|
|
293
|
+
if logz:
|
|
294
|
+
sab = _np.log(sab)
|
|
295
|
+
sab=sab.reshape((nb,na))
|
|
296
|
+
|
|
297
|
+
cmap = 'jet'#fallback for some special options
|
|
298
|
+
#sab_max = sab.max()
|
|
299
|
+
quadmesh = plt.pcolormesh( x,y,sab, clim=clim,cmap=cmap )
|
|
300
|
+
|
|
301
|
+
def _real_mpl_object( o ):
|
|
302
|
+
#in case we are running under NCRYSTAL_FAKEPYPLOT=log, we are dealing
|
|
303
|
+
#with a wrapped object - which we can not pass directly to matplotlib
|
|
304
|
+
#classes.
|
|
305
|
+
return o.the_real_inspected_object if hasattr(o,'the_real_inspected_object') else o
|
|
306
|
+
|
|
307
|
+
plt.colorbar(_real_mpl_object(quadmesh))
|
|
308
|
+
if clim:
|
|
309
|
+
quadmesh.set_clim(clim)
|
|
310
|
+
|
|
311
|
+
plt.xlim(*(xlim or (0.0,alpha[-1])))
|
|
312
|
+
plt.ylim(*(ylim or (beta[0],beta[-1])))
|
|
313
|
+
|
|
314
|
+
def alpha_limits(E_div_kT,beta):
|
|
315
|
+
c = E_div_kT
|
|
316
|
+
cb = c + beta
|
|
317
|
+
assert cb>=0.0,f'c+beta={cb}, beta={beta}, c={c}'
|
|
318
|
+
a = cb + c
|
|
319
|
+
b = 2*_np.sqrt( c * cb )
|
|
320
|
+
assert not _np.isinf(a).any()
|
|
321
|
+
assert not _np.isinf(b).any()
|
|
322
|
+
return max(0.0,a - b), a + b
|
|
323
|
+
alpha_minus = _np.vectorize(lambda *a : alpha_limits(*a)[0])
|
|
324
|
+
alpha_plus = _np.vectorize(lambda *a : alpha_limits(*a)[1])
|
|
325
|
+
def kin_curve( E_div_kT ):
|
|
326
|
+
b0, b1 = b0,b1=max(beta.min(),-E_div_kT),beta.max()
|
|
327
|
+
#Smoother curve by putting more points around beta=0, less points
|
|
328
|
+
#further out (actually, we could improve this alg when
|
|
329
|
+
#ekin_div_kT>>1):
|
|
330
|
+
if b0<1.0 and b1>1.0:
|
|
331
|
+
bvals=_np.append(_np_linspace(b0,1.0,2500), _np_linspace(1.0+1e-14,b1,2500))
|
|
332
|
+
else:
|
|
333
|
+
bvals=_np_linspace(b0,b1,5000)
|
|
334
|
+
color='yellow'
|
|
335
|
+
plt.plot(alpha_minus(E_div_kT,bvals),bvals,label=f'E/kT={E_div_kT}',color=color,lw=2)
|
|
336
|
+
plt.plot(alpha_plus(E_div_kT,bvals).clip(0,alpha.max()),bvals,color=color,lw=2)
|
|
337
|
+
|
|
338
|
+
from .constants import constant_boltzmann
|
|
339
|
+
kT = kernel['temperature']*constant_boltzmann
|
|
340
|
+
|
|
341
|
+
for ekin in (phasespace_curves or []):
|
|
342
|
+
kin_curve( ekin / kT )
|
|
343
|
+
|
|
344
|
+
plt.xlabel('alpha')
|
|
345
|
+
plt.ylabel('beta')
|
|
346
|
+
_plt_final(do_grid=do_grid,do_legend=False,do_show=do_show,plt=plt)
|
|
347
|
+
|
|
348
|
+
def plot_vdos_Gn( Gn, unit = 'meV', logy = False,
|
|
349
|
+
do_newfig = True, do_legend = True,
|
|
350
|
+
do_grid = True, do_show = True ):
|
|
351
|
+
"""Plot Sjolander Gn functions. Each Gn must be specified as a sequence like
|
|
352
|
+
(egrid,Gnvals,n[,label]), where either len(egrid)==len(Gnvals) or
|
|
353
|
+
egrid=(emin,emax). The Gn argument can either be a single Gn function, or a
|
|
354
|
+
sequence of them.
|
|
355
|
+
"""
|
|
356
|
+
def decode_Gn( x ):
|
|
357
|
+
if len(x) not in (3,4):
|
|
358
|
+
return None
|
|
359
|
+
if len(x[1]) <= 4:
|
|
360
|
+
return None
|
|
361
|
+
if len(x) == 3:
|
|
362
|
+
egrid, gnvals, n = x
|
|
363
|
+
label = None
|
|
364
|
+
elif len(x) == 4:
|
|
365
|
+
egrid, gnvals, n, label = x
|
|
366
|
+
else:
|
|
367
|
+
return None
|
|
368
|
+
from ._numpy import _ensure_numpy,_np,_np_linspace
|
|
369
|
+
_ensure_numpy()
|
|
370
|
+
if len(egrid)==2 and len(gnvals)>2:
|
|
371
|
+
egrid = _np_linspace(egrid[0],egrid[1],len(gnvals))
|
|
372
|
+
return dict( egrid = _np.asarray(egrid,dtype=float),
|
|
373
|
+
gnvals = _np.asarray(gnvals,dtype=float),
|
|
374
|
+
n = n,
|
|
375
|
+
label = f'{label} (G{n})' if label else f'G{n}' )
|
|
376
|
+
_ = decode_Gn( Gn )
|
|
377
|
+
if _ is not None:
|
|
378
|
+
gnlist = [ _ ]
|
|
379
|
+
else:
|
|
380
|
+
gnlist = [ decode_Gn(e) for e in Gn ]
|
|
381
|
+
if not gnlist or any( e is None for e in gnlist ):
|
|
382
|
+
from .exceptions import NCBadInput
|
|
383
|
+
raise NCBadInput('Invalid specification of Gn functions.')
|
|
384
|
+
|
|
385
|
+
from . import vdos as nc_vdos
|
|
386
|
+
#from ._common import _palette_Few as _palette
|
|
387
|
+
unit_name, unit_value = nc_vdos._parsevdosunit( unit )
|
|
388
|
+
|
|
389
|
+
plt = _import_matplotlib_plt()
|
|
390
|
+
if do_newfig:
|
|
391
|
+
plt.figure()
|
|
392
|
+
|
|
393
|
+
for gn in gnlist:
|
|
394
|
+
plt.plot( gn['egrid']/unit_value, gn['gnvals'], 'o', label = gn['label'] )
|
|
395
|
+
|
|
396
|
+
plt.xlabel(f'Energy ({unit_name})')
|
|
397
|
+
_plt_final(do_grid,do_legend,do_show,logy=logy,plt=plt)
|
|
398
|
+
|
|
399
|
+
def _get_col_ordered():
|
|
400
|
+
from ._common import _palette_Few
|
|
401
|
+
return [_palette_Few.get(k,k) for k in
|
|
402
|
+
('blue','orange','green','red','brown',
|
|
403
|
+
'purple','yellow','pink','gray','black')]
|
|
404
|
+
|
|
405
|
+
def _plt_final(do_grid,
|
|
406
|
+
do_legend,
|
|
407
|
+
do_show, *,
|
|
408
|
+
logx=False,
|
|
409
|
+
logy=False,
|
|
410
|
+
plt=None,
|
|
411
|
+
extra_legend_kwargs = None ):
|
|
412
|
+
plt = plt or _import_matplotlib_plt()
|
|
413
|
+
if logx:
|
|
414
|
+
plt.semilogx()
|
|
415
|
+
if logy:
|
|
416
|
+
plt.semilogy()
|
|
417
|
+
if do_legend:
|
|
418
|
+
leg=plt.legend(**(extra_legend_kwargs or {}))
|
|
419
|
+
if do_legend=='draggable':
|
|
420
|
+
leg.set_draggable(True)
|
|
421
|
+
if do_grid:
|
|
422
|
+
plt.grid()
|
|
423
|
+
if do_show:
|
|
424
|
+
plt.show()
|
|
425
|
+
|
|
426
|
+
_fakepyplot_mode_cache = [None]
|
|
427
|
+
def _fakepyplot_mode():
|
|
428
|
+
if _fakepyplot_mode_cache[0] is None:
|
|
429
|
+
from ._common import ncgetenv
|
|
430
|
+
_ = ncgetenv('FAKEPYPLOT','')
|
|
431
|
+
if not _ or _=='0':
|
|
432
|
+
res = False
|
|
433
|
+
else:
|
|
434
|
+
res = 'log_and_plot' if ( _ == 'log' ) else 'log_and_block'
|
|
435
|
+
_fakepyplot_mode_cache[0] = res
|
|
436
|
+
return _fakepyplot_mode_cache[0]
|
|
437
|
+
|
|
438
|
+
_theplt=[None]
|
|
439
|
+
def _import_matplotlib_plt():
|
|
440
|
+
if not _theplt[0]:
|
|
441
|
+
mode = _fakepyplot_mode()
|
|
442
|
+
if mode:
|
|
443
|
+
from ._testimpl import _create_pyplot_inspector
|
|
444
|
+
_theplt[0] = _create_pyplot_inspector( pass_calls_to_real_plt = ( mode == 'log_and_plot' ) )
|
|
445
|
+
else:
|
|
446
|
+
#first matplotlib alone, for a perhaps more pedagogical ImportError.
|
|
447
|
+
import matplotlib # noqa F401
|
|
448
|
+
import matplotlib.pyplot as plt
|
|
449
|
+
_theplt[0]=plt
|
|
450
|
+
return _theplt[0]
|
|
451
|
+
|
|
452
|
+
_thepdfpages=[None]
|
|
453
|
+
def _import_matplotlib_pdfpages():
|
|
454
|
+
if not _thepdfpages[0]:
|
|
455
|
+
def _raw_import_pdf_pages():
|
|
456
|
+
try:
|
|
457
|
+
from matplotlib.backends.backend_pdf import PdfPages
|
|
458
|
+
except ImportError:
|
|
459
|
+
raise ImportError("ERROR: Your installation of matplotlib does not have the required support for PDF output.")
|
|
460
|
+
return PdfPages
|
|
461
|
+
mode = _fakepyplot_mode()
|
|
462
|
+
if mode!='log_and_block':
|
|
463
|
+
import matplotlib
|
|
464
|
+
matplotlib.use('agg')
|
|
465
|
+
if mode:
|
|
466
|
+
from ._testimpl import _create_pdfpages_inspector
|
|
467
|
+
_thepdfpages[0] = _create_pdfpages_inspector( _raw_import_pdf_pages() if mode != 'log_and_block' else None )
|
|
468
|
+
else:
|
|
469
|
+
_thepdfpages[0] = _raw_import_pdf_pages()
|
|
470
|
+
return _thepdfpages[0]
|
|
471
|
+
|
|
472
|
+
def _find_highest_bragg_edge( info ):
|
|
473
|
+
if info.isSinglePhase():
|
|
474
|
+
return info.braggthreshold
|
|
475
|
+
ll = [_find_highest_bragg_edge(p) for frac,p in info.phases]
|
|
476
|
+
ll = [e for e in ll if e]
|
|
477
|
+
return max(ll) if ll else None
|
|
478
|
+
|
|
479
|
+
def _estimate_longest_interesting_wavelength( info ):
|
|
480
|
+
#longest wavelength of interest in material, for the purpose of plotting. In
|
|
481
|
+
#case of bragg edges, this will be the longest bragg edge found in the
|
|
482
|
+
#material.
|
|
483
|
+
_ = _find_highest_bragg_edge( info ) if info else None
|
|
484
|
+
return _*1.2 if _ else 15.0
|
NCrystal/plugins.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
"""
|
|
23
|
+
|
|
24
|
+
Utilities related to plugins and dynamic factories.
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def hasFactory(name):
|
|
29
|
+
"""Check if a factory of a given name exists"""
|
|
30
|
+
from ._chooks import _get_raw_cfcts,_str2cstr
|
|
31
|
+
return bool(_get_raw_cfcts()['ncrystal_has_factory'](_str2cstr(name)))
|
|
32
|
+
|
|
33
|
+
def browsePlugins(dump=False):
|
|
34
|
+
|
|
35
|
+
"""Return list of plugins [(pluginname,filename,plugintype),...].
|
|
36
|
+
|
|
37
|
+
If the dump flag is set to True, the list will not be returned. Instead it
|
|
38
|
+
will be printed to stdout.
|
|
39
|
+
"""
|
|
40
|
+
from ._chooks import _get_raw_cfcts
|
|
41
|
+
ll=_get_raw_cfcts()['ncrystal_get_pluginlist']()
|
|
42
|
+
if not dump:
|
|
43
|
+
return ll
|
|
44
|
+
from ._common import print
|
|
45
|
+
print('NCrystal has %i plugins loaded.'%len(ll))
|
|
46
|
+
for i in range(len(ll)):
|
|
47
|
+
pluginname, filename, plugintype = ll[i]
|
|
48
|
+
print('==> %s (%s%s)'%(pluginname,plugintype,
|
|
49
|
+
' from %s'%filename if filename else ''))
|