exafs.py 6.73 KB
Newer Older
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
from xas.core.types import XASObject, Spectrum
31
from .process import Process
32
from PyMca5.PyMcaPhysics.xas.XASClass import XASClass
33
from PyMca5.PyMcaPhysics.xas.XASClass import e2k, k2e
34 35
import multiprocessing
import functools
36
import numpy
37 38 39 40 41
import logging

_logger = logging.getLogger(__name__)


42 43
def process_spectr_exafs(spectrum, configuration, overwrite=True, callback=None,
                         output=None, output_dict=None):
44 45 46 47 48
    """

    :param :class:`.Spectrum` spectrum: spectrum to process
    :param dict configuration: configuration of the pymca normalization
    :param bool overwrite: False if we want to return a new Spectrum instance
49 50 51 52 53 54
    :param function pointer callback: callback to execute.
    :param output: list to store the result, needed for pool processing
    :type: multiprocessing.manager.list
    :param dict output_dict: key: input spectrum, value: index in the output
                                  list.
    :return: processed spectrum
55 56
    :rtype: tuple (configuration, spectrum)
    """
57 58 59 60
    if spectrum.energy is None or spectrum.mu is None:
        _logger.error('Energy and or Mu is/are not specified, unable to '
                      'compute exafs')
        return None, None
61 62 63
    pymca_xas = XASClass()
    pymca_xas.setSpectrum(energy=spectrum.energy,
                          mu=spectrum.mu)
64 65 66
    if configuration is not None:
        pymca_xas.setConfiguration(configuration)
    assert 'NormalizedBackground' in spectrum
67

68 69
    if 'NormalizedBackground' not in spectrum:
        _logger.warning('spectrum has not been normalized, will not process exafs')
70 71
        return None, None

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
    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)
    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

    if callback:
        callback()

    res_spectrum = Spectrum.from_dict(ddict=ddict)

    def get_output(orignal_spec, res_spec):
        if overwrite:
            orignal_spec.update(res_spec)
            return orignal_spec
        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)
110 111


112
def pymca_exafs(xas_obj):
113 114
    """

115 116 117 118
    :param xas_obj: object containing the configuration and spectra to process
    :type: Union[XASObject, dict]
    :return: spectra dict
    :rtype: XASObject
119
    """
120 121
    exafs_obj = PyMca_exafs()
    return exafs_obj.process(xas_obj=xas_obj)
122 123


124 125 126 127
_USE_MULTIPROCESSING_POOL = False
# note: we cannot use multiprocessing pool with push workflow for now.


128
class PyMca_exafs(Process):
129 130
    """Process spectra for exafs and get information about the processing
    advancement"""
131
    def __init__(self):
132
        Process.__init__(self, name='exafs')
133 134 135 136 137 138 139
        self._settings = None

    def setProperties(self, properties):
        if '_pymcaSettings' in properties:
            self._settings = properties['_pymcaSettings']

    def process(self, xas_obj):
140
        _xas_obj = self.getXasObject(xas_obj=xas_obj)
141 142 143
        if self._settings:
            _xas_obj.configuration['EXAFS'] = self._settings

144
        self._advancement.reset(max_=_xas_obj.n_spectrum)
145
        self._advancement.startProcess()
146
        self._pool_process(xas_obj=_xas_obj)
147 148
        self._advancement.endProcess()
        return _xas_obj
149

150
    def _pool_process(self, xas_obj):
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
        assert isinstance(xas_obj, XASObject)
        if not _USE_MULTIPROCESSING_POOL:
            for spectrum in xas_obj.spectra:
                assert 'NormalizedBackground' in spectrum
                process_spectr_exafs(spectrum=spectrum,
                                     configuration=xas_obj.configuration,
                                     callback=self._advancement.increaseAdvancement,
                                     overwrite=True)
                assert 'EXAFSKValues' in spectrum
        else:
            from multiprocessing import Manager
            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:
                partial_ = functools.partial(process_spectr_exafs,
                                             configuration=xas_obj.configuration,
                                             callback=self._advancement.increaseAdvancement,
                                             overwrite=False,
                                             output=res_list,
                                             output_dict=output_dict)
                p.map(partial_, xas_obj.spectra)

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

182
    __call__ = process