Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DOC: add xps to howto #438

Merged
merged 20 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ build/
export/
.do-not-setup-on-localhost


# Sphinx documentation
docs/html
screenshots/
Binary file added docs/source/_static/images/xps_etfa_dft.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/source/_static/images/xps_etfa_exp.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/source/_static/images/xps_step_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/source/_static/images/xps_step_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/source/_static/images/xps_step_4_output.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/source/_static/images/xps_step_4_xps_tab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/source/howto/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ How-to guides
import_structure
upgrade_uninstall
xas
xps
161 changes: 161 additions & 0 deletions docs/source/howto/xps.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
============================
How to calculate XPS spectra
============================

Overview
========
This tutorial will guide you through the process of setting up and running an XPS calculation for Phenylacetylene molecule.


Steps
=====

To start, go ahead and :doc:`launch </installation/launch>` the app, then follow the steps below.


Step 1 Select a structure
--------------------------------
For this tutorial task, please use the `From Examples` tab, and select the Phenylacetylene molecule structure.

Click the `Confirm` button to proceed.

.. figure:: /_static/images/xps_step_1.png
:align: center


Step 2 Configure workflow
--------------------------------

In the **Basic Settings** tab, set the following parameters:

- In the **Structure optimization** section, select ``Structure as is``.
- Set **Electronic Type** to ``Insulator``
- In the **properties** section, select ``X-ray photoelectron spectroscopy (XPS)``


Then go to the **Advanced settings** tab, navigate to `Accuracy and precision`, tick the `Override` box on the right-hand-side and in the dropdown box under `Exchange-correlation functional` select `PBE`.

.. image:: ../_static/images/XAS_Plugin-Set_Adv_Options-Alt-Annotated-Cropped.png
:align: center


.. note::
At present, core-hole pseudopotentials for Si and O are only available for the PBE functional.

Then go to the **XPS setting** tab and, in the **Select core-level** section, select ``C_1s`` by ticking the appropriate box.

.. image:: ../_static/images/xps_step_2_setting_tab.png
:align: center


Click the **Confirm** button to proceed.


Step 3 Choose computational resources
---------------------------------------
We need to use a `pw` code on the high-performance computer to run XPS calculation for this system.
Please read the relevant :doc:`How-To </howto/setup_computer_code>` section to setup code on a remote machine.

.. image:: ../_static/images/xps_step_3.png
:align: center


Then, click the **Submit** button.



Step 4 Check the status and results
-----------------------------------------
The job may take 5~10 minutes to finish if your jobs are running immediately without waiting in the queue.

While the calculation is running, you can monitor its status as shown in the :ref:`basic tutorial <basic_status>`.
When the job is finished, you can view result spectra in the `XPS` tab.

.. tip::

If the `XPS` tab is now shown when the jobs is finished.
Click the ``QeAppWorkChain<pk>`` item on top of the nodetree to refresh the step.

Here is the result of the XPS calculation.
You can click the **Binding energy** button to view the calculated binding energies.
You can change which element to view XPS spectra for using the dropdown box in the top left.

.. figure:: /_static/images/xps_step_4_xps_tab.png
:align: center

One can upload the experimental XPS spectra, and compare to the calculated XPS spectra.
There is a button on the bottom left of the XPS tab to upload the experimental data.
Here is an example of the comparison between the calculated and experimental XPS spectra [1] for the C_1s core level of Phenylacetylene.

.. figure:: /_static/images/xps_step_4_pa_exp.png
:align: center

The calculated spectra agrees well with the experimental data, underscoring the reliability of DFT calculations.


.. tip::

One can also read the exact binding energies from the the output of the calculation, by clicking the `outputs` tab on the node tree of the WorkChain, as shown below.

.. figure:: /_static/images/xps_step_4_output.png
:align: center


The DFT calculated binding energies do not include spin-orbit splitting of the core level state.
We can include the spin-orbit splitting using its experimental value.
Take `f` orbit as a example, we need subtracting :math:`3/7` of the experimental spin-orbit splitting or adding :math:`4/7` of the DFT calculated value, to get the position of the :math:`4f_{7/2}` and :math:`4f_{5/2}` peaks, respectively. Here is a table of the spin-orbit splitting for different orbitals.

+----------------+-------------------+-------------------+
| Orbit | Substracting | Adding |
+================+===================+===================+
| 1s | 0 | 0 |
+----------------+-------------------+-------------------+
| 2p | :math:`1/3` | :math:`2/3` |
+----------------+-------------------+-------------------+
| 3d | :math:`2/5` | :math:`3/5` |
+----------------+-------------------+-------------------+
| 4f | :math:`3/7` | :math:`4/7` |
+----------------+-------------------+-------------------+



Congratulations, you have finished this tutorial!


Another example
====================
ETFA is commonly used as example for XPS measurements and calculations due to the extreme chemical shifts of its four different carbon atoms. [2]

.. tip::

One can select the ETFA molecule from the `From Example` tab, and follow the same steps as above to run the XPS calculation for this molecule.

Here is the result of the XPS calculation for the ETFA molecule.

.. figure:: /_static/images/xps_etfa_dft.png
:align: center

Here is the chemical shift from experiment. [2]

.. figure:: /_static/images/xps_etfa_exp.jpg
:align: center


The calculated relative shifts align well with the trends observed in experimental data, underscoring the reliability of DFT calculations.
Although there are minor discrepancies in the absolute shift values, this is a recognized limitation stemming from the approximations in the exchange-correlation functional within DFT frameworks. [3]

Questions
=========

If you have any questions, please, do not hesitate to ask on the AiiDA discourse forum: https://aiida.discourse.group/.



References
==========

[1] V. Carravetta, *et al.*, *Chem. Phys.* 264, 175 (2001) https://doi.org/10.1016/S0301-0104(00)00396-7

[2] O. Travnikova, *et al.*, , *Relat. Phenom.* 185, 191 (2012) https://doi.org/10.1016/j.elspec.2012.05.009

[3] B.P. Klein, *et al.*, , *J. Phys. Condens. Matter* 33, 154005 (2021) https://doi.org/10.1088/1361-648X/abdf00
2 changes: 2 additions & 0 deletions src/aiidalab_qe/app/structure/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
("Gold (fcc)", file_path / "examples" / "Au.cif"),
("Cobalt (hcp)", file_path / "examples" / "Co.cif"),
("Lithium carbonate", file_path / "examples" / "Li2CO3.cif"),
("Phenylacetylene molecule", file_path / "examples" / "Phenylacetylene.xyz"),
("ETFA molecule", file_path / "examples" / "ETFA.xyz"),
]

OptimadeQueryWidget.title = "OPTIMADE" # monkeypatch
Expand Down
16 changes: 16 additions & 0 deletions src/aiidalab_qe/app/structure/examples/ETFA.xyz
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
14
Lattice="12.30241974 0.0 0.0 0.0 14.640175719999998 0.0 0.0 0.0 15.46505195" Properties=species:S:1:pos:R:3 pbc="T T T"
C 6.21899370 8.39832269 5.55679337
C 6.34421988 8.42035817 7.11190719
C 6.34626493 7.04152238 9.04380147
C 6.24209921 5.56976459 9.37839976
F 6.36233512 9.64017572 5.05341708
F 7.17124959 7.60031062 5.00000000
F 5.00000000 7.92279567 5.17742669
O 6.50098824 9.43606057 7.75095904
O 6.24663056 7.16890953 7.58195838
H 7.30241974 7.48055705 9.35298092
H 5.53775445 7.63550949 9.48679926
H 5.28506039 5.15498764 9.04352071
H 7.05371060 5.00000000 8.91281931
H 6.31087194 5.44012134 10.46505195
16 changes: 16 additions & 0 deletions src/aiidalab_qe/app/structure/examples/Phenylacetylene.xyz
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
14
Lattice="17.572400000000002 0.0 0.0 0.0 14.3161 0.0 0.0 0.0 10.0002" Properties=species:S:1:pos:R:3 pbc="T T T"
C 8.87580000 7.15810000 5.00010000
C 8.17830000 8.36610000 5.00010000
C 8.17830000 5.95000000 5.00010000
C 6.78350000 8.36630000 5.00010000
C 6.78340000 5.95020000 5.00010000
C 6.08610000 7.15830000 5.00010000
C 10.30480000 7.15810000 5.00010000
C 11.50750000 7.15840000 5.00010000
H 8.70750000 9.31610000 5.00000000
H 8.70750000 5.00000000 5.00010000
H 6.24030000 9.30680000 5.00010000
H 6.24010000 5.00980000 5.00010000
H 5.00000000 7.15830000 5.00020000
H 12.57240000 7.15850000 5.00020000
108 changes: 62 additions & 46 deletions src/aiidalab_qe/plugins/xps/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class Result(ResultPanel):

def __init__(self, node=None, **kwargs):
super().__init__(node=node, **kwargs)
self.experimental_data = None # Placeholder for experimental data

def _update_view(self):
import plotly.graph_objects as go
Expand All @@ -101,17 +102,17 @@ def _update_view(self):
value="chemical_shift",
)
gamma = ipw.FloatSlider(
value=0.3,
min=0.1,
max=1,
value=0.1,
min=0.01,
max=0.5,
description="Lorentzian profile ($\gamma$)",
disabled=False,
style={"description_width": "initial"},
)
sigma = ipw.FloatSlider(
value=0.3,
min=0.1,
max=1,
value=0.1,
min=0.01,
max=0.5,
description="Gaussian profile ($\sigma$)",
disabled=False,
style={"description_width": "initial"},
Expand All @@ -122,6 +123,21 @@ def _update_view(self):
disabled=False,
style={"description_width": "initial"},
)
# Create a description label
upload_description = ipw.HTML(
value="<b>Upload Experimental Data (csv format):</b>",
placeholder="",
description="",
)

# Create the upload button
upload_btn = ipw.FileUpload(
description="Choose File",
multiple=False,
)
upload_container = ipw.VBox([upload_description, upload_btn])
upload_btn.observe(self._handle_upload, names="value")

paras = ipw.HBox(
children=[
gamma,
Expand All @@ -145,21 +161,23 @@ def _update_view(self):
layout=ipw.Layout(width="20%"),
)
# init figure
g = go.FigureWidget(
self.g = go.FigureWidget(
layout=go.Layout(
title=dict(text="XPS"),
barmode="overlay",
)
)
g.layout.xaxis.title = "Chemical shift (eV)"
g.layout.xaxis.autorange = "reversed"
self.g.layout.xaxis.title = "Chemical shift (eV)"
self.g.layout.xaxis.autorange = "reversed"
#
spectra = xps_spectra_broadening(
chemical_shifts, equivalent_sites_data, gamma=gamma.value, sigma=sigma.value
)
# only plot the selected spectrum
for site, d in spectra[spectrum_select.value].items():
g.add_scatter(x=d[0], y=d[1], fill="tozeroy", name=site.replace("_", " "))
self.g.add_scatter(
x=d[0], y=d[1], fill="tozeroy", name=site.replace("_", " ")
)

def response(change):
data = []
Expand All @@ -183,29 +201,29 @@ def response(change):
}
)
fill_type = "tozeroy" if fill.value else None
with g.batch_update():
if len(g.data) == len(data):
with self.g.batch_update():
if len(self.g.data) == len(data):
for i in range(len(data)):
g.data[i].x = data[i]["x"]
g.data[i].y = data[i]["y"]
g.data[i].fill = fill_type
g.data[i].name = data[i]["site"].replace("_", " ")
self.g.data[i].x = data[i]["x"]
self.g.data[i].y = data[i]["y"]
self.g.data[i].fill = fill_type
self.g.data[i].name = data[i]["site"].replace("_", " ")

else:
g.data = []
self.g.data = []
for d in data:
g.add_scatter(
self.g.add_scatter(
x=d["x"], y=d["y"], fill=fill_type, name=d["site"]
)
g.layout.barmode = "overlay"
g.layout.xaxis.title = xaxis
self.g.layout.barmode = "overlay"
self.g.layout.xaxis.title = xaxis
self.plot_experimental_data()

spectra_type.observe(response, names="value")
spectrum_select.observe(response, names="value")
gamma.observe(response, names="value")
sigma.observe(response, names="value")
fill.observe(response, names="value")
correction_energies_table = self._get_corrections()
self.children = [
spectra_type,
ipw.HBox(
Expand All @@ -217,30 +235,28 @@ def response(change):
voigt_profile_help,
paras,
fill,
g,
correction_energies_table,
self.g,
upload_container,
]

def _get_corrections(self):
from aiida.orm.utils.serialize import deserialize_unsafe

ui_parameters = self.node.base.extras.get("ui_parameters", {})
ui_parameters = deserialize_unsafe(ui_parameters)
correction_energies = ui_parameters["xps"]["correction_energies"]
# create a table for the correction energies using ipywidgets
# These offsets mainly depend on chemical element and core level, and are determined by comparing the calculated core electron binding energy to the experimental one.
correction_energies_table = ipw.HTML(
"""<h4>Offset Energies (δ)</h4>
<div>When comparing the calculated binding energies to the experimental data, these should be corrected by the given offset listed below.</div>
<table><tr><th>Core-level</th><th>Value (eV)</th></tr>"""
)
for core_level, value in correction_energies.items():
element = core_level.split("_")[0]
if element not in self.spectrum_select_options:
continue
exp = -value["exp"]
sign = "" if exp < 0 else "+"
correction_energies_table.value += (
f"<tr><td>{core_level}</td><td>{sign}{exp}</td></tr>"
)
return correction_energies_table
def _handle_upload(self, change):
"""Process the uploaded experimental data file."""
import pandas as pd

uploaded_file = next(iter(change.new.values()))
content = uploaded_file["content"]
content_str = content.decode("utf-8")

from io import StringIO

df = pd.read_csv(StringIO(content_str), header=None)

self.experimental_data = df
self.plot_experimental_data()

def plot_experimental_data(self):
"""Plot the experimental data alongside the calculated data."""
if self.experimental_data is not None:
x = self.experimental_data[0]
y = self.experimental_data[1]
self.g.add_scatter(x=x, y=y, mode="lines", name="Experimental Data")
Loading
Loading