exafs.py 7.07 KB
Newer Older
payno's avatar
payno committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ###########################################################################*/

__authors__ = ["H. Payno"]
__license__ = "MIT"
__date__ = "06/11/2019"

30
import functools
payno's avatar
payno committed
31
import logging
32
33
34
35
36
37
import multiprocessing

import numpy
from PyMca5.PyMcaPhysics.xas.XASClass import XASClass
from PyMca5.PyMcaPhysics.xas.XASClass import e2k, k2e

payno's avatar
payno committed
38
39
from est.core.process.process import Process
from est.core.types import XASObject, Spectrum
payno's avatar
payno committed
40
41
42
43

_logger = logging.getLogger(__name__)


44
def process_spectr_exafs(spectrum, configuration, overwrite=True, callbacks=None,
45
                         output=None, output_dict=None):
46
47
    """

48
49
50
51
52
53
54
    :param spectrum: spectrum to process
    :type: :class:`.Spectrum`
    :param configuration: configuration of the pymca normalization
    :type: dict
    :param overwrite: False if we want to return a new Spectrum instance
    :type: bool
    :param callback: callback to execute.
55
56
    :param output: list to store the result, needed for pool processing
    :type: multiprocessing.manager.list
57
58
59
    :param output_dict: key is input spectrum, value is index in the output
                        list.
    :type: dict
60
    :return: processed spectrum
61
62
    :rtype: tuple (configuration, spectrum)
    """
63
64
65
66
    if spectrum.energy is None or spectrum.mu is None:
        _logger.error('Energy and or Mu is/are not specified, unable to '
                      'compute exafs')
        return None, None
67
68
69
    pymca_xas = XASClass()
    pymca_xas.setSpectrum(energy=spectrum.energy,
                          mu=spectrum.mu)
70
71
72
    if configuration is not None:
        pymca_xas.setConfiguration(configuration)
    assert 'NormalizedBackground' in spectrum
73

74
75
    if 'NormalizedBackground' not in spectrum:
        _logger.warning('spectrum has not been normalized, will not process exafs')
76
77
        return None, None

78
79
80
81
82
83
    e0 = pymca_xas.calculateE0()
    ddict = spectrum.to_dict()
    ddict["Energy"] = pymca_xas._energy
    ddict["Mu"] = pymca_xas._mu
    cleanMu = pymca_xas._mu - ddict["NormalizedBackground"]
    kValues = e2k(pymca_xas._energy - e0)
payno's avatar
payno committed
84

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
    ddict.update(pymca_xas.postEdge(kValues, cleanMu))

    dataSet = numpy.zeros((cleanMu.size, 2), numpy.float)
    dataSet[:, 0] = kValues
    dataSet[:, 1] = cleanMu

    # exafs normalization
    exafs = (cleanMu - ddict["PostEdgeB"]) / ddict["PostEdgeB"]
    ddict["EXAFSEnergy"] = k2e(kValues)
    ddict["EXAFSKValues"] = kValues
    ddict["EXAFSSignal"] = cleanMu
    if ddict["KWeight"]:
        exafs *= pow(kValues, ddict["KWeight"])
    ddict["EXAFSNormalized"] = exafs

100
101
102
    if callbacks:
        for callback in callbacks:
            callback()
103
104
105

    res_spectrum = Spectrum.from_dict(ddict=ddict)

106
    def get_output(original_spec, res_spec):
107
        if overwrite:
108
109
            original_spec.update(res_spec)
            return original_spec
110
111
112
113
114
115
116
117
        else:
            return res_spec

    if output is not None:
        assert output_dict is not None
        output[output_dict[spectrum]] = get_output(spectrum, res_spectrum)

    return configuration, get_output(spectrum, res_spectrum)
118
119


120
def pymca_exafs(xas_obj):
payno's avatar
payno committed
121
122
    """

123
124
125
126
    :param xas_obj: object containing the configuration and spectra to process
    :type: Union[XASObject, dict]
    :return: spectra dict
    :rtype: XASObject
payno's avatar
payno committed
127
    """
128
129
    exafs_obj = PyMca_exafs()
    return exafs_obj.process(xas_obj=xas_obj)
130
131


132
133
134
135
_USE_MULTIPROCESSING_POOL = False
# note: we cannot use multiprocessing pool with push workflow for now.


136
class PyMca_exafs(Process):
137
138
    """Process spectra for exafs and get information about the processing
    advancement"""
139
    def __init__(self):
140
        Process.__init__(self, name='exafs')
141
142
143

    def setProperties(self, properties):
        if '_pymcaSettings' in properties:
144
            self.setConfiguration(properties['_pymcaSettings'])
145
146

    def process(self, xas_obj):
147
        _xas_obj = self.getXasObject(xas_obj=xas_obj)
148
149
150
        if self._settings:
            _xas_obj.configuration['EXAFS'] = self._settings

151
        self._advancement.reset(max_=_xas_obj.n_spectrum)
152
        self._advancement.startProcess()
153
        self._pool_process(xas_obj=_xas_obj)
154
        self._advancement.endProcess()
155
        self.register_process(_xas_obj, data_keys=("EXAFSKValues",
156
                                                   "EXAFSSignal"))
157
        return _xas_obj
158

159
    def _pool_process(self, xas_obj):
160
161
162
163
164
165
        assert isinstance(xas_obj, XASObject)
        if not _USE_MULTIPROCESSING_POOL:
            for spectrum in xas_obj.spectra:
                assert 'NormalizedBackground' in spectrum
                process_spectr_exafs(spectrum=spectrum,
                                     configuration=xas_obj.configuration,
166
                                     callbacks=self.callbacks,
167
168
169
170
171
172
173
174
175
176
177
178
179
180
                                     overwrite=True)
                assert 'EXAFSKValues' in spectrum
        else:
            from multiprocessing import Manager
            manager = Manager()
            output_dict = {}
            res_list = manager.list()
            for i_spect, spect in enumerate(xas_obj.spectra):
                res_list.append(None)
                output_dict[spect] = i_spect

            with multiprocessing.Pool(5) as p:
                partial_ = functools.partial(process_spectr_exafs,
                                             configuration=xas_obj.configuration,
181
                                             callbacks=self.callbacks,
182
183
184
185
186
187
188
189
                                             overwrite=False,
                                             output=res_list,
                                             output_dict=output_dict)
                p.map(partial_, xas_obj.spectra)

            # then update local spectrum
            for spectrum, res in zip(xas_obj.spectra, res_list):
                spectrum.update(res)
190

191
192
193
194
195
196
197
198
    def definition(self):
        return "exafs calculation"

    def program_version(self):
        import PyMca5
        return PyMca5.version()

    def program_name(self):
199
        return 'pymca_exafs'
200

201
    __call__ = process