exafs.py 8.26 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
payno's avatar
payno committed
37
from est.core.process.process import Process
38
from est.core.process.process import _NexusDatasetDef
payno's avatar
payno committed
39
from est.core.types import XASObject, Spectrum
payno's avatar
payno committed
40
41
42
43

_logger = logging.getLogger(__name__)


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

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

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

88
89
90
91
92
93
    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
94

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

110
111
112
    if callbacks:
        for callback in callbacks:
            callback()
113
114
115

    res_spectrum = Spectrum.from_dict(ddict=ddict)

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


130
def pymca_exafs(xas_obj):
payno's avatar
payno committed
131
132
    """

133
134
135
136
    :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
137
    """
payno's avatar
payno committed
138
139
    exafs_obj = PyMca_exafs(inputs={"xas_obj": xas_obj})
    return exafs_obj.run()
140
141


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


146
class PyMca_exafs(
147
148
149
150
151
    Process,
    name="exafs",
    input_names=["xas_obj"],
    output_names=["xas_obj"],
    optional_input_names=["exafs"],
152
):
153
154
    """Process spectra for exafs and get information about the processing
    advancement"""
payno's avatar
payno committed
155

156
    def set_properties(self, properties):
payno's avatar
payno committed
157
158
        if "_pymcaSettings" in properties:
            self.setConfiguration(properties["_pymcaSettings"])
159

160
161
    def run(self):
        xas_obj = self.inputs.xas_obj
162
        if xas_obj is None:
163
            raise ValueError("xas_obj should be provided")
164
        _xas_obj = self.getXasObject(xas_obj=xas_obj)
165
166
167
        if self.inputs.exafs:
            self.setConfiguration(self.inputs.exafs)
            _xas_obj.configuration["EXAFS"] = self.inputs.exafs
168

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

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

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

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

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

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

226
227
        return PyMca5.version()

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

232
    __call__ = run
233
234
235
236
237


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

239
240
241
242
243
244
245
    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)
246
    res_xas_object._create_saving_pt()
247
248
249
250

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

    # 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)