Skip to content

Commit

Permalink
support Dirac GOS
Browse files Browse the repository at this point in the history
  • Loading branch information
zezhong-zhang committed Jul 16, 2024
1 parent e06bf68 commit 89b3e81
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 5 deletions.
1 change: 1 addition & 0 deletions doc/user_guide/eels.rst
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ The model for the GOS can be specified with the ``GOS`` argument
By default, a freely usable tabulated dataset, in `gosh <https://gitlab.com/gguzzina/gosh>`__
format, is downloaded from Zenodo:
`doi:10.5281/zenodo.7645765 <https://zenodo.org/record/7645765>`_.
As alternative, one can use the `Dirac GOS <https://arxiv.org/abs/2405.10151>`__ to include the relativistic effects, which is downloaded from Zenodo: `doi:10.5281/zenodo.12751756 <https://zenodo.org/record/12751756>`_ and also follows the `gosh` format.

Custom GOS saved in the `gosh <https://gitlab.com/gguzzina/gosh>`__ format can be used,
the following example download a previous version (1.0) of the GOS file from Zenodo
Expand Down
9 changes: 9 additions & 0 deletions examples/model_fitting/EELS_curve_fitting.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# %%
"""
EELS curve fitting
==================
Expand All @@ -21,3 +22,11 @@
m.enable_fine_structure()
m.multifit(kind="smart")
m.plot()

# one can also use the Dirac GOS by specifying the GOS parameter
m = s.create_model(low_loss=ll, GOS="Dirac")
m.enable_fine_structure()
m.multifit(kind="smart")
m.plot()

# %%
9 changes: 7 additions & 2 deletions exspy/components/eels_cl_edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

from hyperspy.component import Component
from exspy.misc.eels.gosh_gos import GoshGOS, _GOSH_DOI
from exspy.misc.eels.dirac_gos import DiracGOS
from exspy.misc.eels.hartree_slater_gos import HartreeSlaterGOS
from exspy.misc.eels.hydrogenic_gos import HydrogenicGOS
from exspy.misc.eels.effective_angle import effective_angle
Expand Down Expand Up @@ -92,7 +93,7 @@ class EELSCLEdge(Component):
Usually a string, for example, ``'Ti_L3'`` for the GOS of the titanium L3
subshell. If a dictionary is passed, it is assumed that Hartree Slater
GOS was exported using `GOS.as_dictionary`, and will be reconstructed.
GOS : ``'gosh'``, ``'hydrogenic'``, ``'Hartree-Slater'`` or str
GOS : ``'gosh'``,``'Dirac'``, ``'hydrogenic'``, ``'Hartree-Slater'`` or str
The GOS to use. Default is ``'gosh'``. If str, it must the path to gosh
GOS file.
gos_file_path : str, None
Expand Down Expand Up @@ -169,13 +170,15 @@ def __init__(self, element_subshell, GOS="gosh", gos_file_path=None):

if GOS == "gosh":
self.GOS = GoshGOS(element_subshell, gos_file_path=gos_file_path)
elif GOS == "Dirac":
self.GOS = DiracGOS(element_subshell,gos_file_path=gos_file_path)

Check warning on line 174 in exspy/components/eels_cl_edge.py

View check run for this annotation

Codecov / codecov/patch

exspy/components/eels_cl_edge.py#L174

Added line #L174 was not covered by tests
elif GOS == "Hartree-Slater": # pragma: no cover
self.GOS = HartreeSlaterGOS(element_subshell)
elif GOS == "hydrogenic":
self.GOS = HydrogenicGOS(element_subshell)
else:
raise ValueError(
"GOS must be one of 'gosh', 'hydrogenic' or 'Hartree-Slater'."
"GOS must be one of 'gosh', 'Dirac','hydrogenic' or 'Hartree-Slater'."
)
self.onset_energy.value = self.GOS.onset_energy
self.onset_energy.free = False
Expand All @@ -189,6 +192,8 @@ def __init__(self, element_subshell, GOS="gosh", gos_file_path=None):
self._whitelist["GOS"] = ("init", GOS)
if GOS == "gosh":
self._whitelist["element_subshell"] = ("init", self.GOS.as_dictionary(True))
elif GOS == "Dirac":
self._whitelist["element_subshell"] = ("init", self.GOS.as_dictionary(True))

Check warning on line 196 in exspy/components/eels_cl_edge.py

View check run for this annotation

Codecov / codecov/patch

exspy/components/eels_cl_edge.py#L196

Added line #L196 was not covered by tests
elif GOS == "Hartree-Slater": # pragma: no cover
self._whitelist["element_subshell"] = ("init", self.GOS.as_dictionary(True))
elif GOS == "hydrogenic":
Expand Down
4 changes: 2 additions & 2 deletions exspy/docstrings/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@

from exspy.misc.eels.gosh_gos import _GOSH_DOI

GOS_PARAMETER = """GOS : 'hydrogenic', 'gosh', 'Hartree-Slater'.
GOS_PARAMETER = """GOS : 'hydrogenic', 'gosh', 'Dirac', 'Hartree-Slater'.
The GOS to use. Default is ``'gosh'``.
gos_file_path : str, None
Only with GOS='gosh'. Specify the file path of the gosh file
Only with GOS='gosh' or 'Dirac'. Specify the file path of the gosh file
to use. If None, use the file from doi:{}""".format(_GOSH_DOI)

EELSMODEL_PARAMETERS = """ll : None or EELSSpectrum
Expand Down
3 changes: 2 additions & 1 deletion exspy/misc/eels/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from exspy.misc.eels.hydrogenic_gos import HydrogenicGOS
from exspy.misc.eels.gosh_gos import GoshGOS
from exspy.misc.eels.dirac_gos import DiracGOS
from exspy.misc.eels.hartree_slater_gos import HartreeSlaterGOS

__all__ = ["HydrogenicGOS", "GoshGOS", "HartreeSlaterGOS"]
__all__ = ["HydrogenicGOS", "GoshGOS", "DiracGOS", "HartreeSlaterGOS"]
137 changes: 137 additions & 0 deletions exspy/misc/eels/dirac_gos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# -*- 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 <https://www.gnu.org/licenses/#GPL>.

import logging

import h5py
import numpy as np
import pooch
from scipy import constants

from hyperspy.defaults_parser import preferences
from exspy.misc.eels.base_gos import TabulatedGOS


_logger = logging.getLogger(__name__)

R = constants.value("Rydberg constant times hc in eV")
a0 = constants.value("Bohr radius")

_Dirac_GOSH_DOI = "10.5281/zenodo.12752410"
_Dirac_GOSH_URL = f"doi:{_Dirac_GOSH_DOI}/Dirac_GOS.gosh"
_Dirac_GOSH_KNOWN_HASH = "md5:02fb22ab55e39e51eb03c08dbf699545"


class DiracGOS(TabulatedGOS):
"""Read Generalized Oscillator Strength from a Dirac GOSH database.
Parameters
----------
element_subshell : {str, dict}
Usually a string, for example, 'Ti_L3' for the GOS of the titanium L3
subshell. If a dictionary is passed, it is assumed that a GOSH GOS was
exported using `GOS.as_dictionary`, and will be reconstructed.
Methods
-------
readgosarray()
Read the GOS files of the element subshell from the location
defined in Preferences.
get_qaxis_and_gos(ienergy, qmin, qmax)
given the energy axis index and qmin and qmax values returns
the qaxis and gos between qmin and qmax using linear
interpolation to include qmin and qmax in the range.
Attributes
----------
energy_axis : array
The tabulated energy axis
qaxis : array
The tabulated qaxis
energy_onset: float
The energy onset for the given element subshell as obtained
from iternal tables.
"""

_name = "gosh"
_whitelist = {
"gos_array": None,
"rel_energy_axis": None,
"qaxis": None,
"element": None,
"subshell": None,
"subshell_factor": None,
}

def __init__(self, element_subshell, gos_file_path=None):
"""
Parameters
----------
element_subshell : str
For example, 'Ti_L3' for the GOS of the titanium L3 subshell
gos_file_path : str
The path of the gosh file to use.
"""

if gos_file_path is None:
gos_file_path = pooch.retrieve(
url=_Dirac_GOSH_URL,
known_hash=_Dirac_GOSH_KNOWN_HASH,
progressbar=preferences.General.show_progressbar,
)
self.gos_file_path = gos_file_path
super().__init__(element_subshell=element_subshell)

def read_gos_data(self):
_logger.info(
"GOSH precomputed GOS\n"
f"\tElement: {self.element} "
f"\tSubshell: {self.subshell}"
f"\tOnset Energy = {self.onset_energy}"
)
element = self.element
subshell = self.subshell

error_message = (
"The GOSH Parametrized GOS database does not "
f"contain a valid entry the {subshell} subshell "
f"of {element}. Please select a different database."
)

with h5py.File(self.gos_file_path, "r") as h:
conventions = h["metadata/edges_info"]
if subshell not in conventions:
raise ValueError(error_message)
table = conventions[subshell].attrs["table"]
self.subshell_factor = conventions[subshell].attrs["occupancy_ratio"]
stem = f"/{element}/{table}"
if stem not in h:
raise ValueError(error_message)
gos_group = h[stem]
gos = gos_group["data"][:]
q = gos_group["q"][:]
free_energies = gos_group["free_energies"][:]
doi = h["/metadata/data_ref"].attrs["data_doi"]

gos = np.squeeze(gos.T)
self.doi = doi
self.gos_array = gos
self.qaxis = q
self.rel_energy_axis = free_energies - min(free_energies)
self.energy_axis = self.rel_energy_axis + self.onset_energy
1 change: 1 addition & 0 deletions exspy/models/eelsmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def __init__(
Parameters
----------
GOS : Generalized Oscillator Strength, availiable option in ['hydrogenic', 'gosh', 'Dirac', 'Hartree-Slater'], default is 'gosh'.
spectrum : a EELSSpectrum instance
%s
Expand Down
1 change: 1 addition & 0 deletions exspy/signals/eels.py
Original file line number Diff line number Diff line change
Expand Up @@ -1635,6 +1635,7 @@ def create_model(
Parameters
----------
%s
GOS: Generalized Oscillator Strength, availiable option in ['hydrogenic', 'gosh', 'Dirac', 'Hartree-Slater'], default is 'gosh'
Returns
-------
Expand Down
22 changes: 22 additions & 0 deletions exspy/tests/misc/test_gos.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

from exspy._defaults_parser import preferences
from exspy.misc.eels.gosh_gos import GoshGOS
from exspy.misc.eels.dirac_gos import DiracGOS
from exspy.misc.eels.hartree_slater_gos import HartreeSlaterGOS
from exspy.misc.eels import HydrogenicGOS
from exspy.misc.elements import elements
Expand Down Expand Up @@ -80,3 +81,24 @@ def test_binding_energy_database():
# These elements are not in the database
if element not in ["Bk", "Cf", "Cm", "metadata"]:
assert "Binding_energies" in elements[element]["Atomic_properties"].keys()

def test_dirac_gosh_not_in_conventions():
gos = DiracGOS("Ti_L2")
gos.subshell = "L234"
with pytest.raises(ValueError):
gos.read_gos_data()


def test_dirac_gosh_not_in_file():
# Use version 1.0 which doesn't have the Ac element
with pytest.raises(ValueError):
_ = DiracGOS("Ac_L3", gos_file_path=GOSH10)


def test_dirac_binding_energy_database():
gos = DiracGOS("Ti_L3")
gosh15 = h5py.File(gos.gos_file_path)
for element in gosh15.keys():
# These elements are not in the database
if element not in ["Bk", "Cf", "Cm", "metadata"]:
assert "Binding_energies" in elements[element]["Atomic_properties"].keys()

0 comments on commit 89b3e81

Please sign in to comment.