ft.py 7.25 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
31
import logging
32
import multiprocessing
33

34
import numpy
35
36
from PyMca5.PyMcaPhysics.xas.XASClass import XASClass

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

_logger = logging.getLogger(__name__)


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

53
54
55
56
57
58
59
    :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.
60
61
    :param output: list to store the result, needed for pool processing
    :type: multiprocessing.manager.list
62
63
64
    :param output_dict: key is input spectrum, value is index in the output
                        list.
    :type: dict
65
    :return: processed spectrum
66
67
    :rtype: tuple (configuration, spectrum)
    """
payno's avatar
payno committed
68
69
70
    _logger.debug(
        "start fourier transform on spectrum (%s, %s)" % (spectrum.x, spectrum.y)
    )
71
72
    pymca_xas = XASClass()
    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
78
79

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

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

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

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

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

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

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

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

120
    if overwrite:
121
122
        spectrum_ = spectrum
    else:
123
124
125
126
127
128
129
130
131
        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_
132
133


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

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


147
148
149
150
_USE_MULTIPROCESSING_POOL = False
# note: we cannot use multiprocessing pool with push workflow for now.


151
class PyMca_ft(Process):
152
    def __init__(self):
payno's avatar
payno committed
153
        Process.__init__(self, name="ft")
154

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

    def process(self, xas_obj):
        """
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.getXasObject(xas_obj=xas_obj)
168
        if self._settings:
payno's avatar
payno committed
169
            _xas_obj.configuration["FT"] = self._settings
170

171
        self._advancement.reset(max_=_xas_obj.n_spectrum)
172
        self._advancement.startProcess()
173
        self._pool_process(xas_obj=_xas_obj)
174
        self._advancement.endProcess()
payno's avatar
payno committed
175
176
177
        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
178
179
180
        self.register_process(
            _xas_obj, data_keys=("ft.radius", "ft.intensity", "ft.imaginary")
        )
181
        return _xas_obj
182

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

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

            # then update local spectrum
            for spectrum, res in zip(xas_obj.spectra, res_list):
                spectrum.update(res)
217
218
219
220
221
222

    def definition(self):
        return "fourier transform"

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

224
225
226
        return PyMca5.version()

    def program_name(self):
payno's avatar
payno committed
227
        return "pymca_ft"
228

229
    __call__ = process