Commit c3d20bc9 authored by payno's avatar payno
Browse files

[core][plots] handle plots units and latex definition

* _NexusDatasetDef: can now have some units defined by latex: `units_latex`
* _NexusSpectrumDef: can now have a `title` and a `title_latex` defined
parent fa40c0c3
Pipeline #40858 passed with stages
in 6 minutes and 42 seconds
......@@ -159,7 +159,9 @@ class Larch_pre_edge(Process):
self.register_process(
_xas_obj,
data_keys=(
_NexusDatasetDef("flat", "{}(E)".format(MU_CHAR)),
_NexusDatasetDef(
"flat", units="{}(E)".format(MU_CHAR), units_latex="\mu(E)"
),
_NexusDatasetDef("dmude"),
_NexusDatasetDef("edge_step_poly"),
_NexusDatasetDef("norm"),
......@@ -167,9 +169,13 @@ class Larch_pre_edge(Process):
_NexusDatasetDef("post_edge"),
_NexusDatasetDef("pre_edge_details"),
_NexusDatasetDef("e0", "eV"),
_NexusDatasetDef("Mu", "{}(E)".format(MU_CHAR)),
_NexusDatasetDef(
"Mu", units="{}(E)".format(MU_CHAR), units_latex="\mu(E)"
),
_NexusDatasetDef("energy", "eV"),
_NexusDatasetDef("mu_ref", "{}(E)".format(MU_CHAR)),
_NexusDatasetDef(
"mu_ref", units="{}(E)".format(MU_CHAR), units_latex="\mu(E)"
),
_NexusDatasetDef("I0"),
_NexusDatasetDef("I1"),
_NexusDatasetDef("I2"),
......@@ -180,6 +186,8 @@ class Larch_pre_edge(Process):
axes=("energy",),
auxiliary_signals=None,
silx_style={"signal_scale_type": "linear"},
title="Mu",
title_latex="\mu",
),
_NexusSpectrumDef(
signal="Mu",
......@@ -195,12 +203,16 @@ class Larch_pre_edge(Process):
axes=("energy",),
auxiliary_signals=("mu_ref",),
silx_style={"signal_scale_type": "linear"},
title="Mu vs Mu ref",
title_latex="\mu \quad vs \quad \mu_{ref}",
),
_NexusSpectrumDef(
signal="flat",
axes=("energy",),
auxiliary_signals=None,
silx_style={"signal_scale_type": "linear"},
title="Mu flat",
title_latex="\mu_{flat}",
),
),
)
......
......@@ -191,16 +191,44 @@ class Larch_xftf(Process):
self.register_process(
_xas_obj,
data_keys=(
_NexusDatasetDef("chir_im", "{}^(-3)".format(ANGSTROM_CHAR)),
_NexusDatasetDef(
"chir_im",
units="{}^(-3)".format(ANGSTROM_CHAR),
units_latex="\mathring{A}^{-3}",
),
_NexusDatasetDef("chir_re"),
_NexusDatasetDef("chir_mag", "{}^(-3)".format(ANGSTROM_CHAR)),
_NexusDatasetDef("masked_chir_mag", "{}".format(ANGSTROM_CHAR)),
_NexusDatasetDef("r", "{}".format(ANGSTROM_CHAR)),
_NexusDatasetDef("masked_r", "{}".format(ANGSTROM_CHAR)),
_NexusDatasetDef("k", "{}^(-1)".format(ANGSTROM_CHAR)),
_NexusDatasetDef("masked_k", "{}^(-1)".format(ANGSTROM_CHAR)),
_NexusDatasetDef(
"masked_chi_weighted_k", "{}^(-2)".format(ANGSTROM_CHAR)
"chir_mag",
units="{}^(-3)".format(ANGSTROM_CHAR),
units_latex="\mathring{A}^{-3}",
),
_NexusDatasetDef(
"masked_chir_mag",
units="{}".format(ANGSTROM_CHAR),
units_latex="\mathring{A}",
),
_NexusDatasetDef(
"r", units="{}".format(ANGSTROM_CHAR), units_latex="\mathring{A}"
),
_NexusDatasetDef(
"masked_r",
units="{}".format(ANGSTROM_CHAR),
units_latex="\mathring{A}",
),
_NexusDatasetDef(
"k",
units="{}^(-1)".format(ANGSTROM_CHAR),
units_latex="\mathring{A}^{-1}",
),
_NexusDatasetDef(
"masked_k",
units="{}^(-1)".format(ANGSTROM_CHAR),
units_latex="\mathring{A}^{-1}",
),
_NexusDatasetDef(
"masked_chi_weighted_k",
units="{}^(-2)".format(ANGSTROM_CHAR),
units_latex="\mathring{A}^{-2}",
),
),
plots=(
......@@ -209,12 +237,15 @@ class Larch_xftf(Process):
axes=("masked_k",),
auxiliary_signals=None,
silx_style={"signal_scale_type": "linear"},
title="chi(k)*k^k_weight",
title_latex="\chi(k).k^{k\_weight}",
),
_NexusSpectrumDef(
signal="masked_chir_mag",
axes=("masked_r",),
auxiliary_signals=None,
silx_style={"signal_scale_type": "linear"},
title="chir_mag",
),
),
)
......
......@@ -195,8 +195,8 @@ class NoiseProcess(Process):
_xas_obj,
data_keys=(
_NexusDatasetDef("norm_noise_savgol"),
_NexusDatasetDef("noise_savgol", "raw data noise"),
_NexusDatasetDef("noise_savgol_energy", "eV"),
_NexusDatasetDef("noise_savgol", units="raw data noise"),
_NexusDatasetDef("noise_savgol_energy", units="eV"),
_NexusDatasetDef("edge_step"),
),
plots=(
......@@ -205,6 +205,7 @@ class NoiseProcess(Process):
axes=("noise_savgol_energy",),
auxiliary_signals=None,
silx_style={"signal_scale_type": "log"},
title="noise",
),
),
)
......
......@@ -47,15 +47,26 @@ _output_desc = namedtuple("_output_desc", ["name", "type", "doc"])
class _NexusSpectrumDef:
"""Util function to define a Nexus plot"""
def __init__(self, signal, axes, auxiliary_signals, silx_style=None):
def __init__(
self,
signal,
axes,
auxiliary_signals,
silx_style=None,
title=None,
title_latex=None,
):
self.__signal = None
self.__axes = None
self.__auxiliary_signals = None
self.__title = None
self.__title_latex = None
self.__silx_style = silx_style
self.signal = signal
self.axes = axes
self.auxiliary_signals = auxiliary_signals
self.title = title
@property
def signal(self):
......@@ -97,18 +108,42 @@ class _NexusSpectrumDef:
else:
return self.__silx_style
@property
def title(self):
return self.__title
@title.setter
def title(self, title):
if not isinstance(title, (str, type(None))):
raise TypeError("`title` should be an instance of str or None")
else:
self.__title = title
@property
def title_latex(self):
return self.__title_latex
@title.setter
def title_latex(self, title):
if not isinstance(title, (str, type(None))):
raise TypeError("`title` should be an instance of str or None")
else:
self.__title_latex = title
class _NexusDatasetDef:
"""Util function to define a Nexus plot"""
def __init__(self, name: str, units=None):
def __init__(self, name: str, units=None, units_latex=None):
self.__name = None
self.__units = None
self.__units_latex = None
assert isinstance(name, str)
self.name = name
self.units = units
self.units_latex = units_latex
@property
def name(self):
......@@ -132,11 +167,24 @@ class _NexusDatasetDef:
else:
self.__units = units
@property
def units_latex(self):
return self.__units_latex
@units_latex.setter
def units_latex(self, units):
if not isinstance(units, (str, type(None))):
raise TypeError("units should be an instance of str")
else:
self.__units_latex = units
@property
def attrs(self):
attrs = {}
if self.units is not None:
attrs.update({"units": self.units})
if self.units_latex is not None:
attrs.update({"units_latex": self.units_latex})
return attrs
def __str__(self):
......
......@@ -382,7 +382,7 @@ def write_xas_proc(
try:
nx_process[key_path] = value
except TypeError as e:
_logger.error(
_logger.warning(
"Unable to write at {} reason is {}"
"".format(str(key_path), str(e))
)
......@@ -427,12 +427,14 @@ def write_xas_proc(
plot_group[name] = h5py.SoftLink(dataset_to_link.name)
elif dataset_to_link.ndim == 2:
plot_group[name] = dataset_to_link[:, 0]
if "units" in dataset_to_link.attrs:
plot_group[name].attrs["units"] = dataset_to_link.attrs["units"]
for key in ("units", "units_latex"):
if key in dataset_to_link.attrs:
plot_group[name].attrs[key] = dataset_to_link.attrs[key]
elif dataset_to_link.ndim == 3:
plot_group[name] = dataset_to_link[:, 0, 0]
if "units" in dataset_to_link.attrs:
plot_group[name].attrs["units"] = dataset_to_link.attrs["units"]
for key in ("units", "units_latex"):
if key in dataset_to_link.attrs:
plot_group[name].attrs[key] = dataset_to_link.attrs[key]
else:
raise ValueError(
"Unable to handle dataset {}".format(dataset_to_link.name)
......@@ -467,6 +469,13 @@ def write_xas_proc(
link_dataset(dataset_to_link=aux_sig_dataset, name=aux_sig)
plot_group.attrs["auxiliary_signals"] = plot.auxiliary_signals
# handle title(s)
if plot.title is not None:
plot_group.attrs["title"] = plot.title
if plot.title_latex is not None:
plot_group.attrs["title_latex"] = plot.title_latex
# handle silx style
if plot.silx_style is not None:
import json
......
# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2018 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/12/2019"
import logging
import unittest
_logger = logging.getLogger(__name__)
def suite():
test_suite = unittest.TestSuite()
from .test_write import suite as test_write_suite
test_suite.addTest(test_write_suite())
return test_suite
# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2017-2019 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/26/2019"
import unittest
import urllib
import tempfile
import os
import shutil
from est.core.types import Spectrum, XASObject
from silx.io.utils import h5py_read_dataset
import h5py
try:
from est.io.utils.larch import read_ascii
from est.core.process.larch.pre_edge import Larch_pre_edge
except ImportError:
has_larch = False
else:
has_larch = True
@unittest.skipIf(has_larch is False, "xraylarch not installed")
class TestWriteProcess(unittest.TestCase):
"""
Insure saving processes works
"""
def setUp(self):
self.outputdir = tempfile.mkdtemp()
xmu_url = "https://raw.githubusercontent.com/xraypy/xraylarch/master/examples/xafs/cu_rt01.xmu"
self.data_file = os.path.join(self.outputdir, "cu_rt01.xmu")
with urllib.request.urlopen(xmu_url) as response, open(
self.data_file, "wb"
) as out_file:
data = response.read() # a `bytes` object
out_file.write(data)
assert os.path.exists(self.data_file)
energy, mu = read_ascii(self.data_file)
self.spectrum = Spectrum(energy=energy, mu=mu)
self.xas_obj = XASObject(spectra=(self.spectrum,), energy=self.spectrum.energy)
self.output_file = os.path.join(self.outputdir, "output.h5")
def tearDown(self):
shutil.rmtree(self.outputdir)
def testProcess(self):
self.assertTrue(self.spectrum.pre_edge is None)
self.assertTrue(self.spectrum.e0 is None)
process = Larch_pre_edge()
process.process(self.xas_obj)
self.assertTrue(self.spectrum.pre_edge is not None)
self.assertTrue(self.spectrum.e0 is not None)
# check process
self.xas_obj.dump(self.output_file)
self.assertTrue(os.path.exists(self.output_file))
with h5py.File(self.output_file, "r") as h5f:
scan = h5f["scan1"]
pre_edge_process = scan["xas_process_1"]
# check general informqtion
self.assertTrue("class_instance" in pre_edge_process)
self.assertTrue("date" in pre_edge_process)
self.assertTrue("processing_order" in pre_edge_process)
self.assertTrue("program" in pre_edge_process)
self.assertTrue("version" in pre_edge_process)
self.assertEqual(
h5py_read_dataset(pre_edge_process["program"]), "larch_pre_edge"
)
# check results
self.assertTrue("results" in pre_edge_process)
results_grp = pre_edge_process["results"]
self.assertTrue("Mu" in results_grp)
mu_grp = results_grp["Mu"]
self.assertTrue("units" in mu_grp.attrs)
self.assertTrue("units_latex" in mu_grp.attrs)
# check plots
self.assertTrue("plots" in pre_edge_process)
plot_0_grp = pre_edge_process["plots"]["plot_0"]
self.assertTrue("Mu" in plot_0_grp)
self.assertTrue("energy" in plot_0_grp)
self.assertTrue("units" in plot_0_grp["Mu"].attrs)
self.assertTrue("units_latex" in plot_0_grp["Mu"].attrs)
self.assertTrue("title" in plot_0_grp.attrs)
self.assertTrue("title_latex" in plot_0_grp.attrs)
def suite():
test_suite = unittest.TestSuite()
for ui in (TestWriteProcess,):
test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(ui))
return test_suite
if __name__ == "__main__":
unittest.main(defaultTest="suite")
......@@ -41,11 +41,13 @@ logger = logging.getLogger(__name__)
def suite():
from ..core import test as test_core
from ..io import test as test_io
from ..app import test as test_app
test_suite = unittest.TestSuite()
# test sx first cause qui tests load ipython module
test_suite.addTest(test_core.suite())
test_suite.addTest(test_io.suite())
test_suite.addTest(test_app.suite())
return test_suite
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment