From 6364de866f8a115c35c9a88c09beb0dcb304719b Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Sun, 25 Aug 2024 17:47:39 +0100 Subject: [PATCH 1/3] Remove `numexpr` from required dependencies --- .github/workflows/tests.yml | 11 +++++- doc/user_guide/install.rst | 28 +++++++++++++++ exspy/_utils.py | 36 +++++++++++++++++++ exspy/components/eels_double_power_law.py | 4 ++- exspy/components/pes_see.py | 4 ++- exspy/components/volume_plasmon_drude.py | 4 ++- .../tests/components/test_double_power_law.py | 6 ++-- pyproject.toml | 7 ++-- 8 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 exspy/_utils.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 88e719c7e..73ce8d502 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,13 +15,22 @@ jobs: os: [ubuntu, windows, macos] PYTHON_VERSION: ['3.9', '3.11'] LABEL: [''] + PIP_SELECTOR: ['[tests, speed]'] include: - os: ubuntu PYTHON_VERSION: '3.8' + PIP_SELECTOR: '[tests, speed]' - os: ubuntu PYTHON_VERSION: '3.10' + PIP_SELECTOR: '[tests, speed]' - os: ubuntu PYTHON_VERSION: '3.12' + PIP_SELECTOR: '[tests, speed]' + # test minimum requirement + - os: ubuntu + PYTHON_VERSION: '3.9' + LABEL: '-minimum' + PIP_SELECTOR: '[tests]' steps: - uses: actions/checkout@v4 @@ -52,7 +61,7 @@ jobs: - name: Install run: | - pip install -e .[tests] + pip install -e '.${{ matrix.PIP_SELECTOR }}' - name: Pip list run: | diff --git a/doc/user_guide/install.rst b/doc/user_guide/install.rst index dfe4dc6a0..bfc04f874 100644 --- a/doc/user_guide/install.rst +++ b/doc/user_guide/install.rst @@ -95,7 +95,35 @@ and install it using (requires ``pip``): Required dependencies will be installed automatically. +Optional dependencies +--------------------- +Optional dependencies can be installed using the +`extras `_. +To install all optional dependencies: + +.. code-block:: bash + + pip install exspy[all] + +The list of *extras*: + ++------------------+-----------------------------+------------------------------------------------------------+ +| Extra | Dependencies | Usage | ++==================+=============================+============================================================+ +| ``speed`` | ``numexpr`` | To speed up fitting with components supporting ``numexpr`` | ++------------------+-----------------------------+------------------------------------------------------------+ +| ``gui-jupyter`` | ``hyperspy_gui_ipywidgets`` | To use the ``ipywidgets`` user interface | ++------------------+-----------------------------+------------------------------------------------------------+ +| ``gui-traitsui`` | ``hyperspy_gui_traitsui`` | To use the ``qt`` user interface | ++------------------+-----------------------------+------------------------------------------------------------+ + +And for development, the following *extras* are available (see ``pyproject.toml`` for more information): + +- tests +- doc +- dev + Updating the package ==================== diff --git a/exspy/_utils.py b/exspy/_utils.py new file mode 100644 index 000000000..ed6b6367a --- /dev/null +++ b/exspy/_utils.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Copyright 2007-2024 The eXSpy developers +# +# This file is part of eXSpy. +# +# eXSpy is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# eXSpy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with eXSpy. If not, see . + +import importlib +import logging + +_logger = logging.getLogger(__name__) + + +def parse_component_module(module): + """Check if numexpr is installed, if not fall back to numpy""" + if module == "numexpr": + numexpr_spec = importlib.util.find_spec("numexpr") + if numexpr_spec is None: + module = "numpy" + _logger.warning( + "Numexpr is not installed, falling back to numpy, " + "which is slower to calculate model." + ) + + return module diff --git a/exspy/components/eels_double_power_law.py b/exspy/components/eels_double_power_law.py index 7db5f49c2..477eb774f 100644 --- a/exspy/components/eels_double_power_law.py +++ b/exspy/components/eels_double_power_law.py @@ -22,6 +22,8 @@ from hyperspy.docstrings.parameters import FUNCTION_ND_DOCSTRING import hyperspy.api as hs +from exspy._utils import parse_component_module + class DoublePowerLaw(hs.model.components1D.Expression): r"""Double power law component for EELS spectra. @@ -87,7 +89,7 @@ def __init__( left_cutoff=left_cutoff, position="origin", autodoc=False, - module=module, + module=parse_component_module(module), compute_gradients=compute_gradients, linear_parameter_list=["A"], check_parameter_linearity=False, diff --git a/exspy/components/pes_see.py b/exspy/components/pes_see.py index b7bf6b2a6..924c21b3b 100644 --- a/exspy/components/pes_see.py +++ b/exspy/components/pes_see.py @@ -22,6 +22,8 @@ import hyperspy.api as hs +from exspy._utils import parse_component_module + _logger = logging.getLogger(__name__) @@ -77,7 +79,7 @@ def __init__( Phi=Phi, B=B, position="Phi", - module=module, + module=parse_component_module(module), autodoc=False, compute_gradients=compute_gradients, linear_parameter_list=["A"], diff --git a/exspy/components/volume_plasmon_drude.py b/exspy/components/volume_plasmon_drude.py index d8665d367..8bc2fd08e 100644 --- a/exspy/components/volume_plasmon_drude.py +++ b/exspy/components/volume_plasmon_drude.py @@ -20,6 +20,8 @@ import hyperspy.api as hs +from exspy._utils import parse_component_module + class VolumePlasmonDrude(hs.model.components1D.Expression): r""" @@ -72,7 +74,7 @@ def __init__( plasmon_energy=plasmon_energy, fwhm=fwhm, position="plasmon_energy", - module=module, + module=parse_component_module(module), autodoc=False, compute_gradients=compute_gradients, linear_parameter_list=["intensity"], diff --git a/exspy/tests/components/test_double_power_law.py b/exspy/tests/components/test_double_power_law.py index 7b5b8ef71..9e7dcb6e7 100644 --- a/exspy/tests/components/test_double_power_law.py +++ b/exspy/tests/components/test_double_power_law.py @@ -31,8 +31,10 @@ def test_function(): g.origin.value = 1 g.shift.value = 2 g.ratio.value = 2 - assert np.isinf(g.function(1)) - assert np.isinf(g.function(3)) + if g._module == "numexpr": + # numpy raises a ZeroDivisionError + assert np.isinf(g.function(1)) + assert np.isinf(g.function(3)) assert g.function(-1) == 0 assert g.function(0) == 0 assert g.function(2) == 9 diff --git a/pyproject.toml b/pyproject.toml index c21cdbc64..2c4ac933b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,6 @@ dependencies = [ "dask[array]", "hyperspy>=2.0rc0", "matplotlib", - "numexpr", "numpy", "pint", "pooch", @@ -74,6 +73,7 @@ file = "LICENSE" # unpin when sphinxcontrib-towncrier support more recent version to towncrier "towncrier<24", ] +"speed" = ["numexpr"] "tests" = [ "pytest >= 5.0", "pytest-mpl", @@ -81,10 +81,11 @@ file = "LICENSE" "pytest-xdist", "setuptools-scm", ] -"dev" = ["black"] +"dev" = ["ruff"] "all" = [ "exspy[gui-jupyter]", - "exspy[gui-traitsui]" + "exspy[gui-traitsui]", + "exspy[speed]" ] [project.urls] From aa1e5c4a3211bb035b67644aa73fb778c977b49b Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Sun, 25 Aug 2024 17:48:37 +0100 Subject: [PATCH 2/3] Fix heading for fine structure analysis in user guide --- doc/user_guide/eels.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/user_guide/eels.rst b/doc/user_guide/eels.rst index 19a2611ad..7e4a346cb 100644 --- a/doc/user_guide/eels.rst +++ b/doc/user_guide/eels.rst @@ -394,16 +394,17 @@ edge functionalities: .. _eels.fine_structure: -Fine structure analysis -^^^^^^^^^^^^^^^^^^^^^^^ +Fine structure analysis using a spline function +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The fine structure of an EELS ionization edge manifests as distinct features within the first few tens of eVs energy. It is due to variations in the electron's energy loss probability caused by the interactions with the material's electronic structure. It offers insights into the material's electronic properties, bonding, and local environments. Therefore, we cannot model them from first-principles because i) the material is usually unknown -ii) HyperSpy only supports Hydrogenic and Hartree-Slater EELS core-loss models. Instead, the -:py:class:`~.components.EELSCLEdge` component includes features +ii) HyperSpy supports Hydrogenic, Hartree-Slater EELS, DFT and Dirac core-loss models, which don't +include modeling of the fine structure - see :ref:`eels.GOS` and :class:`~.components.EELSCLEdge` +for more information. Instead, the :py:class:`~.components.EELSCLEdge` component includes features for EELS fine structure modelling and analysis using functions to mimic the fine structure features. The most basic consists in modelling the fine structure of ionization edges using a spline function. You can activate this feature @@ -483,8 +484,8 @@ structure width. It is possible to suspend this feature by calling To resume it use :py:meth:`~.models.EELSModel.suspend_auto_fine_structure_width` -Fine structure analysis -""""""""""""""""""""""" +Fine structure analysis using Gaussian functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Fine structure analysis consists on measuring different features of the fine structure (e.g., peak position, area, ...). Often, these features can be related to the ionized atom environment. From 77752578fc134915726bc4213eb9f28323274791 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Sun, 25 Aug 2024 18:55:03 +0100 Subject: [PATCH 3/3] Add changelog entry --- upcoming_changes/80.enhancements.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 upcoming_changes/80.enhancements.rst diff --git a/upcoming_changes/80.enhancements.rst b/upcoming_changes/80.enhancements.rst new file mode 100644 index 000000000..57ce98067 --- /dev/null +++ b/upcoming_changes/80.enhancements.rst @@ -0,0 +1 @@ +Make ``numexpr`` an optional dependency to allow installation in pyodide. \ No newline at end of file