exafs.py 8.33 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
import multiprocessing

import numpy
from PyMca5.PyMcaPhysics.xas.XASClass import XASClass
from PyMca5.PyMcaPhysics.xas.XASClass import e2k, k2e
37
38
from est.core.process.process import _input_desc
from est.core.process.process import _output_desc
payno's avatar
payno committed
39
from est.core.process.process import Process
40
from est.core.process.process import _NexusDatasetDef
payno's avatar
payno committed
41
from est.core.types import XASObject, Spectrum
payno's avatar
payno committed
42
43
44
45

_logger = logging.getLogger(__name__)


payno's avatar
payno committed
46
47
48
49
50
51
52
53
def process_spectr_exafs(
    spectrum,
    configuration,
    overwrite=True,
    callbacks=None,
    output=None,
    output_dict=None,
):
54
55
    """

56
57
58
59
60
61
62
    :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.
63
64
    :param output: list to store the result, needed for pool processing
    :type: multiprocessing.manager.list
65
66
67
    :param output_dict: key is input spectrum, value is index in the output
                        list.
    :type: dict
68
    :return: processed spectrum
69
70
    :rtype: tuple (configuration, spectrum)
    """
payno's avatar
payno committed
71
    _logger.debug("start exafs on spectrum (%s, %s)" % (spectrum.x, spectrum.y))
72
    if spectrum.energy is None or spectrum.mu is None:
payno's avatar
payno committed
73
74
75
        _logger.error(
            "Energy and or Mu is/are not specified, unable to " "compute exafs"
        )
76
        return None, None
77
    pymca_xas = XASClass()
payno's avatar
payno committed
78
    pymca_xas.setSpectrum(energy=spectrum.energy, mu=spectrum.mu)
79
80
    if configuration is not None:
        pymca_xas.setConfiguration(configuration)
payno's avatar
payno committed
81
    assert "NormalizedBackground" in spectrum
82

payno's avatar
payno committed
83
84
85
86
87
    if (
        "NormalizedBackground" not in spectrum
        or spectrum["NormalizedBackground"] is None
    ):
        _logger.warning("spectrum has not been normalized, will not process exafs")
88
89
        return None, None

90
91
92
93
94
95
    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
96

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
    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

112
113
114
    if callbacks:
        for callback in callbacks:
            callback()
115
116
117

    res_spectrum = Spectrum.from_dict(ddict=ddict)

118
    def get_output(original_spec, res_spec):
119
        if overwrite:
120
121
            original_spec.update(res_spec)
            return original_spec
122
123
124
125
126
127
128
129
        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)
130
131


132
def pymca_exafs(xas_obj):
payno's avatar
payno committed
133
134
    """

135
136
137
138
    :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
139
    """
140
141
    exafs_obj = PyMca_exafs()
    return exafs_obj.process(xas_obj=xas_obj)
142
143


144
_USE_MULTIPROCESSING_POOL = False
payno's avatar
payno committed
145
# note: we cannot use multiprocessing pool with pypushflow for now.
146
147


148
class PyMca_exafs(Process):
149
150
    """Process spectra for exafs and get information about the processing
    advancement"""
payno's avatar
payno committed
151

152
    INPUT_NAMES = ["xas_obj"]
153

154
    OUTPUT_NAMES = ["xas_obj"]
155

156
157
    def __init__(self, varinfo=None, **inputs):
        Process.__init__(self, name="exafs", varinfo=varinfo, **inputs)
158

159
    def set_properties(self, properties):
payno's avatar
payno committed
160
161
        if "_pymcaSettings" in properties:
            self.setConfiguration(properties["_pymcaSettings"])
162

163
164
165
    def process(self, xas_obj=None):
        if xas_obj is None:
            xas_obj = self.input.xas_obj.value
166
        _xas_obj = self.getXasObject(xas_obj=xas_obj)
167
        if self._settings:
payno's avatar
payno committed
168
            _xas_obj.configuration["EXAFS"] = self._settings
169

170
        self._advancement.reset(max_=_xas_obj.n_spectrum)
171
        self._advancement.startProcess()
172
        self._pool_process(xas_obj=_xas_obj)
173
        self._advancement.endProcess()
174
175
176
177
178
179
180
        self.register_process(
            _xas_obj,
            data_keys=(
                _NexusDatasetDef("EXAFSKValues"),
                _NexusDatasetDef("EXAFSSignal"),
            ),
        )
181
        self.output.xas_obj.value = _xas_obj.to_dict()
182
        return _xas_obj
183

184
    def _pool_process(self, xas_obj):
185
186
        assert isinstance(xas_obj, XASObject)
        if not _USE_MULTIPROCESSING_POOL:
payno's avatar
payno committed
187
            for spectrum in xas_obj.spectra.data.flat:
payno's avatar
payno committed
188
189
190
191
192
193
194
195
                assert "NormalizedBackground" in spectrum
                process_spectr_exafs(
                    spectrum=spectrum,
                    configuration=xas_obj.configuration,
                    callbacks=self.callbacks,
                    overwrite=True,
                )
                assert "EXAFSKValues" in spectrum
196
197
        else:
            from multiprocessing import Manager
payno's avatar
payno committed
198

199
200
201
202
203
204
205
206
            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:
payno's avatar
payno committed
207
208
209
210
211
212
213
214
                partial_ = functools.partial(
                    process_spectr_exafs,
                    configuration=xas_obj.configuration,
                    callbacks=self.callbacks,
                    overwrite=False,
                    output=res_list,
                    output_dict=output_dict,
                )
215
216
217
218
219
                p.map(partial_, xas_obj.spectra)

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

221
222
223
224
225
    def definition(self):
        return "exafs calculation"

    def program_version(self):
        import PyMca5
payno's avatar
payno committed
226

227
228
        return PyMca5.version()

229
230
    @staticmethod
    def program_name():
payno's avatar
payno committed
231
        return "pymca_exafs"
232

233
    __call__ = process
234
235
236
237
238


if __name__ == "__main__":
    import sys
    import yaml
payno's avatar
payno committed
239

240
241
242
243
244
245
246
    xas_object_yaml_file = sys.argv[1]
    _logger.debug("Load xas object from {}".format(xas_object_yaml_file))
    with open(xas_object_yaml_file, "r") as file:
        ddict = yaml.load(file)["input_data"]
        xas_object = XASObject.from_dict(ddict)
    print("******* do exafs ********")
    res_xas_object = pymca_exafs(xas_obj=xas_object)
247
    res_xas_object._create_saving_pt()
248
249
250
251

    # dump resulting object
    with open(xas_object_yaml_file, "w") as file:
        yaml.dump({"input_data": res_xas_object.to_dict()}, file)
252
253
254
255

    # dump resulting object into output_normalization file
    with open("output_exafs.yaml", "w") as file:
        yaml.dump({"input_data": res_xas_object.to_dict()}, file)