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

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

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

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

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

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

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

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

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

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


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

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


149
_USE_MULTIPROCESSING_POOL = False
payno's avatar
payno committed
150
# note: we cannot use multiprocessing pool with pypushflow for now.
151
152


153
class PyMca_ft(Process):
154

155
    _INPUT_NAMES = set(["xas_obj"])
156

157
    _OUTPUT_NAMES = set(["xas_obj"])
158

159
160
    def __init__(self, varinfo=None, **inputs):
        Process.__init__(self, name="ft", varinfo=varinfo, **inputs)
161

162
    def set_properties(self, properties):
payno's avatar
payno committed
163
164
        if "_pymcaSettings" in properties:
            self._settings = properties["_pymcaSettings"]
165

166
    def run(self, xas_obj=None):
167
        """
payno's avatar
payno committed
168

169
170
171
        :param xas_obj: object containing the configuration and spectra to process
        :type: Union[XASObject, dict]
        :return: spectra dict
172
173
        :rtype: dict
        """
174
        if xas_obj is None:
175
            xas_obj = self.inputs.xas_obj
176
        _xas_obj = self.getXasObject(xas_obj=xas_obj)
177
        if self._settings:
payno's avatar
payno committed
178
            _xas_obj.configuration["FT"] = self._settings
179

180
        self._advancement.reset(max_=_xas_obj.n_spectrum)
181
        self._advancement.startProcess()
182
        self._pool_process(xas_obj=_xas_obj)
183
        self._advancement.endProcess()
payno's avatar
payno committed
184
185
186
        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
187
        self.register_process(
188
189
190
191
192
193
            _xas_obj,
            data_keys=(
                _NexusDatasetDef("ft.radius"),
                _NexusDatasetDef("ft.intensity"),
                _NexusDatasetDef("ft.imaginary"),
            ),
payno's avatar
payno committed
194
        )
195
        self.outputs.xas_obj = _xas_obj.to_dict()
196
        return _xas_obj
197

198
    def _pool_process(self, xas_obj):
199
200
201
        assert isinstance(xas_obj, XASObject)
        if not _USE_MULTIPROCESSING_POOL:
            for spectrum in xas_obj.spectra:
payno's avatar
payno committed
202
203
204
205
206
207
                process_spectr_ft(
                    spectrum=spectrum,
                    configuration=xas_obj.configuration,
                    callbacks=self.callbacks,
                    overwrite=True,
                )
208
209
        else:
            from multiprocessing import Manager
payno's avatar
payno committed
210

211
212
213
214
215
216
217
218
            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
219
220
221
222
223
224
225
226
                partial_ = functools.partial(
                    process_spectr_ft,
                    configuration=xas_obj.configuration,
                    callbacks=self.callbacks,
                    overwrite=False,
                    output=res_list,
                    output_dict=output_dict,
                )
227
228
229
230
231
                p.map(partial_, xas_obj.spectra)

            # then update local spectrum
            for spectrum, res in zip(xas_obj.spectra, res_list):
                spectrum.update(res)
232
233
234
235
236
237

    def definition(self):
        return "fourier transform"

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

239
240
        return PyMca5.version()

241
242
    @staticmethod
    def program_name():
payno's avatar
payno committed
243
        return "pymca_ft"
244

245
    __call__ = run
246
247
248
249
250


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

252
253
254
255
256
257
258
    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 ft ********")
    res_xas_object = pymca_ft(xas_obj=xas_object)
259
    res_xas_object._create_saving_pt()
260
261
262
263

    # dump resulting object
    with open(xas_object_yaml_file, "w") as file:
        yaml.dump({"input_data": res_xas_object.to_dict()}, file)
264
265
266
267

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