ft.py 6.81 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 `ft` process"""
payno's avatar
payno committed
26
27
28

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

31
import functools
32
import logging
33
import multiprocessing
34

35
import numpy
36
from PyMca5.PyMcaPhysics.xas.XASClass import XASClass
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_ft(
    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
70
71
    _logger.debug(
        "start fourier transform on spectrum (%s, %s)" % (spectrum.x, spectrum.y)
    )
72
73
    pymca_xas = XASClass()
    if spectrum.energy is None or spectrum.mu is None:
payno's avatar
payno committed
74
75
76
        _logger.error(
            "Energy and or Mu is/are not specified, unable to " "compute exafs"
        )
77
        return None, None
78
79
80

    if configuration is not None:
        pymca_xas.setConfiguration(configuration)
payno's avatar
payno committed
81
    pymca_xas.setSpectrum(energy=spectrum.energy, mu=spectrum.mu)
82

payno's avatar
payno committed
83
84
85
86
    if "EXAFSSignal" not in spectrum:
        _logger.warning(
            "exafs has not been processed yet, unable to process" "fourier transform"
        )
87
88
        return None, None

payno's avatar
payno committed
89
90
    if "EXAFSNormalized" not in spectrum:
        _logger.warning("ft window need to be defined first")
payno's avatar
payno committed
91
92
        return None, None

payno's avatar
payno committed
93
94
    cleanMu = spectrum["EXAFSSignal"]
    kValues = spectrum["EXAFSKValues"]
95
96
97
98
99
100

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

    set2 = dataSet.copy()
payno's avatar
payno committed
101
    set2[:, 1] = spectrum["EXAFSNormalized"]
102
103
104
105
106

    # remove points with k<2
    goodi = (set2[:, 0] >= spectrum["KMin"]) & (set2[:, 0] <= spectrum["KMax"])
    set2 = set2[goodi, :]

107
    if set2.size == 0:
payno's avatar
payno committed
108
        ft = {"FTImaginary": numpy.nan, "FTIntensity": numpy.nan, "FTRadius": numpy.nan}
109
    else:
payno's avatar
payno committed
110
111
112
        ft = pymca_xas.fourierTransform(
            set2[:, 0], set2[:, 1], kMin=spectrum["KMin"], kMax=spectrum["KMax"]
        )
113
        assert "FTIntensity" in ft
114
115
        assert "FTRadius" in ft
        assert ft["FTRadius"] is not None
116
        assert ft["FTIntensity"] is not None
117
118
119
    if callbacks:
        for callback in callbacks:
            callback()
120

121
    if overwrite:
122
123
        spectrum_ = spectrum
    else:
124
125
126
127
128
129
130
131
132
        spectrum_ = Spectrum()
        spectrum_.update(spectrum)
    assert spectrum_
    spectrum_.ft = ft

    if output is not None:
        assert output_dict is not None
        output[output_dict[spectrum]] = spectrum_
    return configuration, spectrum_
133
134


135
def pymca_ft(xas_obj):
payno's avatar
payno committed
136
137
    """

138
139
140
    :param xas_obj: object containing the configuration and spectra to process
    :type: Union[XASObject, dict]
    :return: spectra dict
141
    :rtype: dict
payno's avatar
payno committed
142
    """
143
    assert xas_obj is not None
payno's avatar
payno committed
144
145
    ft_obj = PyMca_ft(inputs={"xas_obj": xas_obj})
    return ft_obj.run()
146
147


148
149
150
151
152
153
154
class PyMca_ft(
    Process,
    name="ft",
    input_names=["xas_obj"],
    output_names=["xas_obj"],
    optional_input_names=["ft"],
):
155
    def set_properties(self, properties):
payno's avatar
payno committed
156
157
        if "_pymcaSettings" in properties:
            self._settings = properties["_pymcaSettings"]
158

159
    def run(self):
160
        """
payno's avatar
payno committed
161

162
163
164
        :param xas_obj: object containing the configuration and spectra to process
        :type: Union[XASObject, dict]
        :return: spectra dict
165
166
        :rtype: dict
        """
167
        xas_obj = self.inputs.xas_obj
168
        if xas_obj is None:
169
            raise ValueError("xas_obj should be provided")
170
        _xas_obj = self.getXasObject(xas_obj=xas_obj)
171
172
173
174

        if self.inputs.ft:
            self.setConfiguration(self.inputs.ft)
            _xas_obj.configuration["FT"] = self.inputs.ft
175

176
        self._advancement.reset(max_=_xas_obj.n_spectrum)
177
        self._advancement.startProcess()
178
        self._pool_process(xas_obj=_xas_obj)
179
        self._advancement.endProcess()
payno's avatar
payno committed
180
181
182
        assert hasattr(_xas_obj.spectra.data.flat[0], "ft")
        assert hasattr(_xas_obj.spectra.data.flat[0].ft, "intensity")
        assert hasattr(_xas_obj.spectra.data.flat[0].ft, "imaginary")
payno's avatar
payno committed
183
        self.register_process(
184
185
186
187
188
189
            _xas_obj,
            data_keys=(
                _NexusDatasetDef("ft.radius"),
                _NexusDatasetDef("ft.intensity"),
                _NexusDatasetDef("ft.imaginary"),
            ),
payno's avatar
payno committed
190
        )
191
        self.outputs.xas_obj = _xas_obj.to_dict()
192
        return _xas_obj
193

194
    def _pool_process(self, xas_obj):
195
        assert isinstance(xas_obj, XASObject)
196
197
198
199
200
201
202
203
204
        n_s = len(xas_obj.spectra.data.flat)
        for i_s, spectrum in enumerate(xas_obj.spectra):
            process_spectr_ft(
                spectrum=spectrum,
                configuration=xas_obj.configuration,
                callbacks=self.callbacks,
                overwrite=True,
            )
            self.progress = i_s / n_s * 100.0
205
206
207
208
209
210

    def definition(self):
        return "fourier transform"

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

212
213
        return PyMca5.version()

214
215
    @staticmethod
    def program_name():
payno's avatar
payno committed
216
        return "pymca_ft"
217

218
    __call__ = run