exafs.py 6.58 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
# 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.
#
# ###########################################################################*/
25
"""wrapper to pymca `exafs` process"""
payno's avatar
payno committed
26
27
28

__authors__ = ["H. Payno"]
__license__ = "MIT"
29
__date__ = "04/10/2021"
payno's avatar
payno committed
30

31
import functools
payno's avatar
payno committed
32
import logging
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
from est.core.process.process import Process
39
from est.core.process.process import _NexusDatasetDef
payno's avatar
payno committed
40
from est.core.types import XASObject, Spectrum
payno's avatar
payno committed
41
42
43
44

_logger = logging.getLogger(__name__)


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

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

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

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

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

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

    res_spectrum = Spectrum.from_dict(ddict=ddict)

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


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

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


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

153
    def set_properties(self, properties):
payno's avatar
payno committed
154
155
        if "_pymcaSettings" in properties:
            self.setConfiguration(properties["_pymcaSettings"])
156

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

166
        self.progress = 0.0
167
        self._pool_process(xas_obj=_xas_obj)
168
        self.progress = 100.0
169
        self._advancement.endProcess()
170
171
172
173
174
175
176
        self.register_process(
            _xas_obj,
            data_keys=(
                _NexusDatasetDef("EXAFSKValues"),
                _NexusDatasetDef("EXAFSSignal"),
            ),
        )
177
        self.outputs.xas_obj = _xas_obj.to_dict()
178
        return _xas_obj
179

180
    def _pool_process(self, xas_obj):
181
        assert isinstance(xas_obj, XASObject)
182
183
184
185
186
187
188
189
190
191
192
        n_s = len(xas_obj.spectra.data.flat)
        for i_s, spectrum in enumerate(xas_obj.spectra.data.flat):
            assert "NormalizedBackground" in spectrum
            process_spectr_exafs(
                spectrum=spectrum,
                configuration=xas_obj.configuration,
                callbacks=self.callbacks,
                overwrite=True,
            )
            assert "EXAFSKValues" in spectrum
            self.progress = i_s / n_s * 100.0
193

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

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

200
201
        return PyMca5.version()

202
203
    @staticmethod
    def program_name():
payno's avatar
payno committed
204
        return "pymca_exafs"
205

206
    __call__ = run