diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 4efeddb..dcce07b 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -26,9 +26,15 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} - - name: Install package + - name: Install uv run: | curl -LsSf https://astral.sh/uv/install.sh | sh + - name: Install nomad + if: "${{ matrix.python_version != '3.8'}" + run: | + uv pip install nomad-lab[infrastructure]@git+https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-FAIR.git + - name: Install package + run: | uv pip install ".[dev]" - name: Test with pytest run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8f97e07..907ca72 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.7.0 + rev: v0.8.0 hooks: # Run the linter. - id: ruff diff --git a/MANIFEST.in b/MANIFEST.in index a9131e3..129a474 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ prune * exclude * recursive-include src/pynxtools_mpes *.py -include pyproject.toml README.md dev-requirements.txt \ No newline at end of file +include pyproject.toml README.md dev-requirements.txt +graft src/pynxtools_mpes/nomad/examples \ No newline at end of file diff --git a/docs/tutorial/standalone.md b/docs/tutorial/standalone.md index 81a3447..cffa431 100644 --- a/docs/tutorial/standalone.md +++ b/docs/tutorial/standalone.md @@ -33,7 +33,7 @@ $ \ ### Examples -You can find exhaustive examples how to use `pynxtools-mpes` for your ARPES research data pipeline [here](https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-remote-tools-hub/-/tree/develop/docker/mpes). +You can find exhaustive examples how to use `pynxtools-mpes` for your ARPES research data pipeline in [`src/pynxtools-mpes/nomad/examples`](../../src/pynxtools_mpes/nomad/examples/). These are designed for working with [`NOMAD`](https://nomad-lab.eu/) and its [`NOMAD Remote Tools Hub (NORTH)`](https://nomad-lab.eu/prod/v1/gui/analyze/north). There are also small example files for using the `pynxtools` dataconverter with the `mpes` reader and the `NXmpes` application definition in [`tests/data`](https://github.com/FAIRmat-NFDI/pynxtools.mpes/tree/main/tests/data). diff --git a/pyproject.toml b/pyproject.toml index c3b61c1..5adb358 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,9 @@ docs = [ [project.entry-points."pynxtools.reader"] mpes = "pynxtools_mpes.reader:MPESReader" +[project.entry-points.'nomad.plugin'] +mpes_example = "pynxtools_mpes.nomad.entrypoints:mpes_example" + [tool.setuptools.packages.find] where = [ "src", diff --git a/src/pynxtools_mpes/nomad/entrypoints.py b/src/pynxtools_mpes/nomad/entrypoints.py new file mode 100644 index 0000000..ba7e59e --- /dev/null +++ b/src/pynxtools_mpes/nomad/entrypoints.py @@ -0,0 +1,41 @@ +# +# Copyright The NOMAD Authors. +# +# This file is part of NOMAD. See https://nomad-lab.eu for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""Entry points for mpes examples.""" + +try: + from nomad.config.models.plugins import ExampleUploadEntryPoint +except ImportError as exc: + raise ImportError( + "Could not import nomad package. Please install the package 'nomad-lab'." + ) from exc + +mpes_example = ExampleUploadEntryPoint( + title="Multidimensional photoemission spectroscopy (MPES)", + category="FAIRmat examples", + description=""" + This example presents the capabilities of the NOMAD platform to store and standardize multidimensional photoemission spectroscopy (MPES) experimental data. It contains four major examples: + + - Taking a pre-binned file, here stored in a h5 file, and converting it into the standardized MPES NeXus format. + There exists a [NeXus application definition for MPES](https://manual.nexusformat.org/classes/contributed_definitions/NXmpes.html#nxmpes) which details the internal structure of such a file. + - Binning of raw data (see [here](https://www.nature.com/articles/s41597-020-00769-8) for additional resources) into a h5 file and consecutively generating a NeXus file from it. + - An analysis example using data in the NeXus format and employing the [pyARPES](https://github.com/chstan/arpes) analysis tool to reproduce the main findings of [this paper](https://arxiv.org/pdf/2107.07158.pdf). + - Importing angle-resolved data stored in NXmpes_arpes, and convert these into momentum space coordinates using tools in pyARPES. + """, + plugin_package="pynxtools_mpes", + resources=["nomad/examples/*"], +) diff --git a/src/pynxtools_mpes/nomad/examples/E1 Convert to NeXus.ipynb b/src/pynxtools_mpes/nomad/examples/E1 Convert to NeXus.ipynb new file mode 100644 index 0000000..921df2e --- /dev/null +++ b/src/pynxtools_mpes/nomad/examples/E1 Convert to NeXus.ipynb @@ -0,0 +1,161 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Converting Multidimensional Photoemission Spectroscopy (MPES) data into the NeXus format" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This example shows how to convert x-array based h5 measurement files as generated by the [preprocessing output](https://www.nature.com/articles/s41597-020-00769-8) of the software used at the Fritz-Haber Institute into the [MPES NeXus format](https://manual.nexusformat.org/classes/contributed_definitions/NXmpes.html#nxmpes).\n", + "For an example on how to generate such a h5 measurement file please refer to the [postprocessing example](./E2%20ARPES%20postprocessing.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Download the data\n", + "Since the provided datafile is comparably large (~200MB) it is not directly provided with the example.\n", + "You can [download](https://zenodo.org/record/7573825/files/MoTe2.h5?download=1) it from zenodo. Place the file in the directory of this notebook afterwards. Under Linux, macOS and in a NORTH container you can directly use the cell below to download the file with curl." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "skip-execution" + ] + }, + "outputs": [], + "source": [ + "! curl -o MoTe2.h5 \"https://zenodo.org/records/7573825/files/MoTe2.h5?download=1\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Convert the file to NeXus\n", + "To convert the available files to the NeXus format we use the convert function readily supplied by pynxtools.\n", + "It uses the downloaded measurement file, a json config file and optionally an electronic lab notebook (ELN) yaml file that is used in the [binning of WSe2 example](./E2%20Binning%20of%20WSe2.ipynb).\n", + "The json config file maps specific metadata from the h5 measurement file to the nxs file, i.e. a pressure reading which automatically gets collected during measurement.\n", + "The ELN is a file which supplies additional metadata which is written into the NeXus file.\n", + "This is data which is not collected automatically, such as the person conducting the experiment.\n", + "It can be written manually or generated, e.g. by the NOMAD ELN functionality.\n", + "\n", + "The convert command may also be executed in the command line with the command `dataconverter`:\n", + "```\n", + "dataconverter \\\n", + " --reader mpes \\\n", + " --nxdl NXmpes \\\n", + " --output MoTe2.mpes.nxs \\\n", + " MoTe2.h5 \\\n", + " config_file.json\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from pynxtools.dataconverter.convert import convert" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The input parameters are defined as follows:\n", + "\n", + "**reader**: The specific reader which gets called inside pynxtools. This is supplied in the pynxtools python code. If you create a specific reader for your measurement file it gets selecetd here. If you use the binning procedure from FHI to generate a xarray h5 file you should use the reader called `mpes`.\n", + "\n", + "**nxdl**: The specific nxdl file to be used. For MPES this should always be `NXmpes` or one of its subdefinitions of the form `NXmpes_`.\n", + " \n", + "**output**: The output filename of the NeXus file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "convert(input_file=[\"MoTe2.h5\", \"config_file.json\"],\n", + " reader='mpes',\n", + " nxdl='NXmpes',\n", + " output='MoTe2.mpes.nxs')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## View the data with H5Web\n", + "H5Web is a tool for visualizing any data in the h5 data format. Since the NeXus format builds opon h5 it can be used to view this data as well. We just import the package and call H5Web with the output filename from the convert command above. For an analysis on NeXus data files please refer to [analysis example](./E3%20pyARPES%20analysis.ipynb).\n", + "\n", + "You can also view this data with the H5Viewer or other tools from your local filesystem." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from jupyterlab_h5web import H5Web" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "H5Web('MoTe2.mpes.nxs')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.12" + }, + "vscode": { + "interpreter": { + "hash": "982c6a11128e6710069266786b6cc2519a098e817123fe996210f6bb9a67eb40" + } + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/src/pynxtools_mpes/nomad/examples/E2 ARPES postprocessing.ipynb b/src/pynxtools_mpes/nomad/examples/E2 ARPES postprocessing.ipynb new file mode 100644 index 0000000..5382ef7 --- /dev/null +++ b/src/pynxtools_mpes/nomad/examples/E2 ARPES postprocessing.ipynb @@ -0,0 +1,874 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8ad4167a-e4e7-498d-909a-c04da9f177ed", + "metadata": { + "tags": [] + }, + "source": [ + "# Postprocessing Multidimensional Photoemission Spectroscopy (MPES) data and converting it into the NeXus format\n", + "\n", + "In this example, we pull data from a time-resolved ARPES on WSe2 measurement from Zenodo, and load it into the sed package using functions of the mpes package. Then, we run a conversion pipeline on it, containing steps for visualizing the channels, correcting image distortions, calibrating the momentum space, correcting for energy distortions and calibrating the energy axis. Finally, the data are binned in calibrated axes and stored in the standardised [MPES NExus format](https://fairmat-nfdi.github.io/nexus_definitions/classes/contributed_definitions/NXmpes.html#nxmpes).\n", + "For performance reasons, best store the data on a locally attached storage (no network drive). This can also be achieved transparently using the included MirrorUtil class.\n", + "\n", + "This example works on a rather large dataset (~6GB) and hence it takes some time and resources to execute. If you just want to learn how to convert already processed data in an xarray based h5 file into the NeXus format you may have a look at the simpler [Convert to NeXus example](./E1%20Convert%20to%20NeXus.ipynb), which is runs quicker and has lower requirements.\n", + "\n", + "Further information on the postprocessing pipeline can be found in the [documentation of sed](https://opencompes.github.io/sed/) or in [R.P. Xian et al., Sci Data 7, 442 (2020)](https://www.nature.com/articles/s41597-020-00769-8)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb045e17-fa89-4c11-9d51-7f06e80d96d5", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import os\n", + "\n", + "import sed\n", + "\n", + "%matplotlib widget" + ] + }, + { + "cell_type": "markdown", + "id": "5d8f092b-df92-4abb-b00c-444f0351efd3", + "metadata": {}, + "source": [ + "## Download RAW data (trARPES data of WSe2)\n", + "\n", + "Here, we just set the main file folder for holding the measurement data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e14a45c3-b8d0-4aa0-8469-f2c641fdb236", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "FDIR = f'{os.getcwd()}/data/Scan049_1'\n", + "ECAL = f'{os.getcwd()}/data/energycal_2019_01_08'" + ] + }, + { + "cell_type": "markdown", + "id": "fbbd67d7-2a2b-42ec-ba9e-28c1e64d6dce", + "metadata": {}, + "source": [ + "Since the provided measurement files are rather large (~6GB), they are not directly provided with the example.\n", + "You can [download](https://zenodo.org/record/6369728/files/WSe2.zip) it from zenodo. This may take some time. Place the file in the directory of this notebook afterwards. Under Linux, macOS and in a NORTH container you can directly use the cell below to download the file with curl." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "334632d0-84d1-46fe-a4c0-3966feb1e69a", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "! curl -o WSe2.zip \"https://zenodo.org/records/6369728/files/WSe2.zip\"" + ] + }, + { + "cell_type": "markdown", + "id": "c12a6c18-a6c6-417f-b016-1c7e11fe9116", + "metadata": {}, + "source": [ + "Now we extract the measurement files." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f79c9e03-7f8a-4917-97f9-bf94fc36270e", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "! unzip WSe2.zip -d data" + ] + }, + { + "cell_type": "markdown", + "id": "42a6afaa-17dd-4637-ba75-a28c4ead1adf", + "metadata": {}, + "source": [ + "## Load Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "906abf04-6232-4b72-90f1-3843d51b5108", + "metadata": { + "jupyter": { + "source_hidden": true + }, + "tags": [] + }, + "outputs": [], + "source": [ + "metadata = {}\n", + "# manual Meta data. These should ideally come from an Electronic Lab Notebook.\n", + "#General\n", + "metadata['experiment_summary'] = 'WSe2 XUV NIR pump probe data.'\n", + "metadata['entry_title'] = 'Valence Band Dynamics - 800 nm linear s-polarized pump, 0.6 mJ/cm2 absorbed fluence'\n", + "metadata['experiment_title'] = 'Valence band dynamics of 2H-WSe2'\n", + "\n", + "#User\n", + "# Fill general parameters of NXuser\n", + "# TODO: discuss how to deal with multiple users?\n", + "metadata['user0'] = {}\n", + "metadata['user0']['name'] = 'Julian Maklar'\n", + "metadata['user0']['role'] = 'Principal Investigator'\n", + "metadata['user0']['affiliation'] = 'Fritz Haber Institute of the Max Planck Society'\n", + "metadata['user0']['address'] = 'Faradayweg 4-6, 14195 Berlin'\n", + "metadata['user0']['email'] = 'maklar@fhi-berlin.mpg.de'\n", + "\n", + "#NXinstrument\n", + "metadata['instrument'] = {}\n", + "metadata['instrument']['energy_resolution'] = 140.\n", + "#analyzer\n", + "metadata['instrument']['analyzer']={}\n", + "metadata['instrument']['analyzer']['slow_axes'] = \"delay\" # the scanned axes\n", + "metadata['instrument']['analyzer']['spatial_resolution'] = 10.\n", + "metadata['instrument']['analyzer']['energy_resolution'] = 110.\n", + "metadata['instrument']['analyzer']['momentum_resolution'] = 0.08\n", + "metadata['instrument']['analyzer']['working_distance'] = 4.\n", + "metadata['instrument']['analyzer']['lens_mode'] = \"6kV_kmodem4.0_30VTOF.sav\"\n", + "\n", + "#probe beam\n", + "metadata['instrument']['beam']={}\n", + "metadata['instrument']['beam']['probe']={}\n", + "metadata['instrument']['beam']['probe']['incident_energy'] = 21.7\n", + "metadata['instrument']['beam']['probe']['incident_energy_spread'] = 0.11\n", + "metadata['instrument']['beam']['probe']['pulse_duration'] = 20.\n", + "metadata['instrument']['beam']['probe']['frequency'] = 500.\n", + "metadata['instrument']['beam']['probe']['incident_polarization'] = [1, 1, 0, 0] # p pol Stokes vector\n", + "metadata['instrument']['beam']['probe']['extent'] = [80., 80.]\n", + "#pump beam\n", + "metadata['instrument']['beam']['pump']={}\n", + "metadata['instrument']['beam']['pump']['incident_energy'] = 1.55\n", + "metadata['instrument']['beam']['pump']['incident_energy_spread'] = 0.08\n", + "metadata['instrument']['beam']['pump']['pulse_duration'] = 35.\n", + "metadata['instrument']['beam']['pump']['frequency'] = 500.\n", + "metadata['instrument']['beam']['pump']['incident_polarization'] = [1, -1, 0, 0] # s pol Stokes vector\n", + "metadata['instrument']['beam']['pump']['incident_wavelength'] = 800.\n", + "metadata['instrument']['beam']['pump']['average_power'] = 300.\n", + "metadata['instrument']['beam']['pump']['pulse_energy'] = metadata['instrument']['beam']['pump']['average_power']/metadata['instrument']['beam']['pump']['frequency']#µJ\n", + "metadata['instrument']['beam']['pump']['extent'] = [230., 265.]\n", + "metadata['instrument']['beam']['pump']['fluence'] = 0.15\n", + "\n", + "#sample\n", + "metadata['sample']={}\n", + "metadata['sample']['preparation_date'] = '2019-01-13T10:00:00+00:00'\n", + "metadata['sample']['preparation_description'] = 'Cleaved'\n", + "metadata['sample']['sample_history'] = 'Cleaved'\n", + "metadata['sample']['chemical_formula'] = 'WSe2'\n", + "metadata['sample']['description'] = 'Sample'\n", + "metadata['sample']['name'] = 'WSe2 Single Crystal'\n", + "\n", + "metadata['file'] = {}\n", + "metadata['file'][\"trARPES:Carving:TEMP_RBV\"] = 300.\n", + "metadata['file'][\"trARPES:XGS600:PressureAC:P_RD\"] = 5.e-11\n", + "metadata['file'][\"KTOF:Lens:Extr:I\"] = -0.12877\n", + "metadata['file'][\"KTOF:Lens:UDLD:V\"] = 399.99905\n", + "metadata['file'][\"KTOF:Lens:Sample:V\"] = 17.19976\n", + "metadata['file'][\"KTOF:Apertures:m1.RBV\"] = 3.729931\n", + "metadata['file'][\"KTOF:Apertures:m2.RBV\"] = -5.200078\n", + "metadata['file'][\"KTOF:Apertures:m3.RBV\"] = -11.000425\n", + "\n", + "# Sample motor positions\n", + "metadata['file']['trARPES:Carving:TRX.RBV'] = 7.1900000000000004\n", + "metadata['file']['trARPES:Carving:TRY.RBV'] = -6.1700200225439552\n", + "metadata['file']['trARPES:Carving:TRZ.RBV'] = 33.4501953125\n", + "metadata['file']['trARPES:Carving:THT.RBV'] = 423.30500940561586\n", + "metadata['file']['trARPES:Carving:PHI.RBV'] = 0.99931647456264949\n", + "metadata['file']['trARPES:Carving:OMG.RBV'] = 11.002500171914066" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77c9e4a2-e6fd-4ab0-bd55-417cc3995e13", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "base_path = '.'\n", + "data_path = f'{base_path}/data'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f1f82054", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# The Scan directory\n", + "fdir = data_path + '/Scan049_1'\n", + "# create sed processor using the config file:\n", + "sp = sed.SedProcessor(\n", + " folder=fdir,\n", + " config=f\"{base_path}/sed_config/mpes_example_config.yaml\",\n", + " metadata=metadata,\n", + " collect_metadata=True,\n", + " verbose=True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50d0a3b3", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Apply jittering to X, Y, t, ADC columns.\n", + "# Columns are defined in the config, or can be provided as list.\n", + "sp.add_jitter()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0a0d336b", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Plot of the count rate through the scan\n", + "rate, secs = sp.loader.get_count_rate(range(100))\n", + "plt.plot(secs, rate)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dfb42777", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# The time elapsed in the scan\n", + "sp.loader.get_elapsed_time()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb074f29", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Inspect data in dataframe Columns:\n", + "# axes = ['X', 'Y', 't', 'ADC']\n", + "# bins = [100, 100, 100, 100]\n", + "# ranges = [(0, 1800), (0, 1800), (130000, 140000), (0, 9000)]\n", + "# sp.view_event_histogram(dfpid=1, axes=axes, bins=bins, ranges=ranges)\n", + "sp.view_event_histogram(dfpid=2)" + ] + }, + { + "cell_type": "markdown", + "id": "70aa4343", + "metadata": {}, + "source": [ + "## Distortion correction and Momentum Calibration workflow\n", + "### Distortion correction\n", + "#### 1. step: \n", + "Bin and load part of the dataframe in detector coordinates, and choose energy plane where high-symmetry points can well be identified. Either use the interactive tool, or pre-select the range:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76bf8aad", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "#sp.bin_and_load_momentum_calibration(df_partitions=20, plane=170)\n", + "sp.bin_and_load_momentum_calibration(df_partitions=100, plane=33, width=10, apply=True)" + ] + }, + { + "cell_type": "markdown", + "id": "fee3ca76", + "metadata": {}, + "source": [ + "#### 2. Step:\n", + "Next, we select a number of features corresponding to the rotational symmetry of the material, plus the center. These can either be auto-detected (for well-isolated points), or provided as a list (these can be read-off the graph in the cell above).\n", + "These are then symmetrized according to the rotational symmetry, and a spline-warping correction for the x/y coordinates is calculated, which corrects for any geometric distortions from the perfect n-fold rotational symmetry." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fd9666c5", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "#features = np.array([[203.2, 341.96], [299.16, 345.32], [350.25, 243.70], [304.38, 149.88], [199.52, 152.48], [154.28, 242.27], [248.29, 248.62]])\n", + "#sp.define_features(features=features, rotation_symmetry=6, include_center=True, apply=True)\n", + "# Manual selection: Use a GUI tool to select peaks:\n", + "#sp.define_features(rotation_symmetry=6, include_center=True)\n", + "#sp.generate_splinewarp(rotation_symmetry=6, include_center=True, fwhm=10, sigma=12, sigma_radius=4)\n", + "# Autodetect: Uses the DAOStarFinder routine to locate maxima.\n", + "# Parameters are:\n", + "# fwhm: Full-width at half maximum of peaks.\n", + "# sigma: Number of standard deviations above the mean value of the image peaks must have.\n", + "# sigma_radius: number of standard deviations around a peak that peaks are fitted\n", + "sp.define_features(rotation_symmetry=6, auto_detect=True, include_center=True, fwhm=10, sigma=12, sigma_radius=4, apply=True)" + ] + }, + { + "cell_type": "markdown", + "id": "f7519ff8", + "metadata": {}, + "source": [ + "#### 3. Step: \n", + "Generate nonlinear correction using splinewarp algorithm. If no landmarks have been defined in previous step, default parameters from the config are used" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d27cd7a4", + "metadata": {}, + "outputs": [], + "source": [ + "# Option whether a central point shall be fixed in the determiantion fo the correction\n", + "sp.generate_splinewarp(include_center=True)" + ] + }, + { + "cell_type": "markdown", + "id": "4211ac21", + "metadata": {}, + "source": [ + "#### Optional (Step 3a): \n", + "Save distortion correction parameters to configuration file in current data folder: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f32988f", + "metadata": {}, + "outputs": [], + "source": [ + "# Save generated distortion correction parameters for later reuse\n", + "sp.save_splinewarp()" + ] + }, + { + "cell_type": "markdown", + "id": "b5e69ffa", + "metadata": {}, + "source": [ + "#### 4. Step:\n", + "To adjust scaling, position and orientation of the corrected momentum space image, you can apply further affine transformations to the distortion correction field. Here, first a postential scaling is applied, next a translation, and finally a rotation around the center of the image (defined via the config). One can either use an interactive tool, or provide the adjusted values and apply them directly." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "62abfa41", + "metadata": {}, + "outputs": [], + "source": [ + "#sp.pose_adjustment(xtrans=14, ytrans=18, angle=2)\n", + "sp.pose_adjustment(xtrans=8, ytrans=7, angle=-4, apply=True)" + ] + }, + { + "cell_type": "markdown", + "id": "a78a68e9", + "metadata": {}, + "source": [ + "#### 5. Step:\n", + "Finally, the momentum correction is applied to the dataframe, and corresponding meta data are stored" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "845f002d", + "metadata": {}, + "outputs": [], + "source": [ + "sp.apply_momentum_correction()" + ] + }, + { + "cell_type": "markdown", + "id": "d9810488", + "metadata": {}, + "source": [ + "### Momentum calibration workflow\n", + "#### 1. Step:\n", + "First, the momentum scaling needs to be calibtrated. Either, one can provide the coordinates of one point outside the center, and provide its distane to the Brillouin zone center (which is assumed to be located in the center of the image), one can specify two points on the image and their distance (where the 2nd point marks the BZ center),or one can provide absolute k-coordinates of two distinct momentum points.\n", + "\n", + "If no points are provided, an interactive tool is created. Here, left mouse click selectes the off-center point (brillouin_zone_cetnered=True) or toggle-selects the off-center and center point." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a358f07d", + "metadata": {}, + "outputs": [], + "source": [ + "k_distance = 2/np.sqrt(3)*np.pi/3.28 # k-distance of the K-point in a hexagonal Brilloiun zone\n", + "#sp.calibrate_momentum_axes(k_distance = k_distance)\n", + "point_a = [308, 345]\n", + "sp.calibrate_momentum_axes(point_a=point_a, k_distance = k_distance, apply=True)\n", + "#point_b = [247, 249]\n", + "#sp.calibrate_momentum_axes(point_a=point_a, point_b = point_b, k_coord_a = [.5, 1.1], k_coord_b = [1.3, 0], equiscale=False" + ] + }, + { + "cell_type": "markdown", + "id": "1a3697b1", + "metadata": {}, + "source": [ + "##### Optional (Step 1a): \n", + "Save momentum calibration parameters to configuration file in current data folder: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "86bedfa7", + "metadata": {}, + "outputs": [], + "source": [ + "# Save generated momentum calibration parameters for later reuse\n", + "sp.save_momentum_calibration()" + ] + }, + { + "cell_type": "markdown", + "id": "c2f8a513", + "metadata": {}, + "source": [ + "#### 2. Step:\n", + "Now, the distortion correction and momentum calibration needs to be applied to the dataframe." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9ae5066", + "metadata": {}, + "outputs": [], + "source": [ + "sp.apply_momentum_calibration()" + ] + }, + { + "cell_type": "markdown", + "id": "0bce2388", + "metadata": {}, + "source": [ + "## Energy Correction (optional)\n", + "The purpose of the energy correction is to correct for any momentum-dependent distortion of the energy axis, e.g. from geometric effects in the flight tube, or from space charge" + ] + }, + { + "cell_type": "markdown", + "id": "5289de59", + "metadata": {}, + "source": [ + "#### 1st step:\n", + "Here, one can select the functional form to be used, and adjust its parameters. The binned data used for the momentum calibration is plotted around the Fermi energy (defined by tof_fermi), and the correction function is plotted ontop. Possible correction functions are: \"sperical\" (parameter: diameter), \"Lorentzian\" (parameter: gamma), \"Gaussian\" (parameter: sigma), and \"Lorentzian_asymmetric\" (parameters: gamma, amplitude2, gamma2).\n", + "\n", + "One can either use an interactive alignment tool, or provide parameters directly." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72c6c8f9", + "metadata": {}, + "outputs": [], + "source": [ + "#sp.adjust_energy_correction(amplitude=2.5, center=(730, 730), gamma=920, tof_fermi = 66200)\n", + "sp.adjust_energy_correction(amplitude=2.5, center=(730, 730), gamma=920, tof_fermi = 66200, apply=True)" + ] + }, + { + "cell_type": "markdown", + "id": "e43fbf33", + "metadata": {}, + "source": [ + "##### Optional (Step 1a): \n", + "Save energy correction parameters to configuration file in current data folder: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7699e690", + "metadata": {}, + "outputs": [], + "source": [ + "# Save generated energy correction parameters for later reuse\n", + "sp.save_energy_correction()" + ] + }, + { + "cell_type": "markdown", + "id": "41a6a3e6", + "metadata": {}, + "source": [ + "#### 2. Step\n", + "After adjustment, the energy correction is directly applied to the TOF axis." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb1e2bee", + "metadata": {}, + "outputs": [], + "source": [ + "sp.apply_energy_correction()" + ] + }, + { + "cell_type": "markdown", + "id": "8b571b4c", + "metadata": {}, + "source": [ + "## 3. Energy calibration\n", + "For calibrating the energy axis, a set of data taken at different bias voltages around the value where the measurement was taken is required." + ] + }, + { + "cell_type": "markdown", + "id": "6bc28642", + "metadata": {}, + "source": [ + "#### 1. Step:\n", + "In a first step, the data are loaded, binned along the TOF dimension, and normalized. The used bias voltages can be either provided, or read from attributes in the source files if present." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f44a586", + "metadata": {}, + "outputs": [], + "source": [ + "# Load energy calibration EDCs\n", + "energycalfolder = data_path + \"/energycal_2019_01_08/\"\n", + "scans = np.arange(1,12)\n", + "voltages = np.arange(12,23,1)\n", + "files = [energycalfolder + r'Scan' + str(num).zfill(3) + '_' + str(num+11) + '.h5' for num in scans]\n", + "sp.load_bias_series(data_files=files, normalize=True, biases=voltages, ranges=[(64000, 75000)])" + ] + }, + { + "cell_type": "markdown", + "id": "314a79c8", + "metadata": {}, + "source": [ + "#### 2. Step:\n", + "Next, the same peak or feature needs to be selected in each curve. For this, one needs to define \"ranges\" for each curve, within which the peak of interest is located. One can either provide these ranges manually, or provide one range for a \"reference\" curve, and infer the ranges for the other curves using a dynamic time warping algorithm." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2f843244", + "metadata": {}, + "outputs": [], + "source": [ + "# Option 1 = specify the ranges containing a common feature (e.g an equivalent peak) for all bias scans\n", + "# rg = [(129031.03103103103, 129621.62162162163), (129541.54154154155, 130142.14214214214), (130062.06206206206, 130662.66266266267), (130612.61261261262, 131213.21321321322), (131203.20320320321, 131803.8038038038), (131793.7937937938, 132384.38438438438), (132434.43443443443, 133045.04504504506), (133105.10510510512, 133715.71571571572), (133805.8058058058, 134436.43643643643), (134546.54654654654, 135197.1971971972)]\n", + "# sp.find_bias_peaks(ranges=rg, infer_others=False)\n", + "# Option 2 = specify the range for one curve and infer the others\n", + "# This will open an interactive tool to select the correct ranges for the curves.\n", + "# IMPORTANT: Don't choose the range too narrow about a peak, and choose a refid\n", + "# somewhere in the middle or towards larger biases!\n", + "rg = (66100, 67000)\n", + "sp.find_bias_peaks(ranges=rg, ref_id=5, infer_others=True, apply=True)" + ] + }, + { + "cell_type": "markdown", + "id": "b2638818", + "metadata": {}, + "source": [ + "#### 3. Step:\n", + "Next, the detected peak positions and bias voltages are used to determine the calibration function. This can be either done by fitting the functional form d^2/(t-t0)^2 via lmfit (\"lmfit\"), or using a polynomial approxiamtion (\"lstsq\" or \"lsqr\"). Here, one can also define a reference id, and a reference energy. Those define the absolute energy position of the feature used for calibration in the \"reference\" trace, at the bias voltage where the final measurement has been performed. The energy scale can be either \"kientic\" (decreasing energy with increasing TOF), or \"binding\" (increasing energy with increasing TOF).\n", + "\n", + "After calculating the calibration, all traces corrected with the calibration are plotted ontop of each other, the calibration function together with the extracted features is plotted." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22e15f5d", + "metadata": {}, + "outputs": [], + "source": [ + "# use the refid of the bias that the measurement was taken at\n", + "# Eref can be used to set the absolute energy (kinetic energy, E-EF) of the feature used for energy calibration (if known)\n", + "refid=4\n", + "Eref=-0.5\n", + "# the lmfit method uses a fit of (d/(t-t0))**2 to determine the energy calibration\n", + "sp.calibrate_energy_axis(ref_energy=Eref, ref_id=refid, energy_scale=\"kinetic\", method=\"lmfit\")" + ] + }, + { + "cell_type": "markdown", + "id": "df63c6c7", + "metadata": {}, + "source": [ + "##### Optional (Step 3a): \n", + "Save energy calibration parameters to configuration file in current data folder: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b870293c", + "metadata": {}, + "outputs": [], + "source": [ + "# Save generated energy calibration parameters for later reuse\n", + "sp.save_energy_calibration()" + ] + }, + { + "cell_type": "markdown", + "id": "563709c7", + "metadata": {}, + "source": [ + "#### 4. Step:\n", + "Finally, the the energy axis is added to the dataframe." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c470ffd9", + "metadata": {}, + "outputs": [], + "source": [ + "sp.append_energy_axis()" + ] + }, + { + "cell_type": "markdown", + "id": "b2d8cdf9", + "metadata": {}, + "source": [ + "## 4. Delay calibration:\n", + "The delay axis is calculated from the ADC input column based on the provided delay range. ALternatively, the delay scan range can also be extracted from attributes inside a source file, if present." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0943d349", + "metadata": {}, + "outputs": [], + "source": [ + "#from pathlib import Path\n", + "#datafile = \"file.h5\"\n", + "#print(datafile)\n", + "#sp.calibrate_delay_axis(datafile=datafile)\n", + "delay_range = (-500, 1500)\n", + "sp.calibrate_delay_axis(delay_range=delay_range, preview=True)" + ] + }, + { + "cell_type": "markdown", + "id": "d9d0b018", + "metadata": {}, + "source": [ + "## 5. Visualization of calibrated histograms\n", + "With all calibrated axes present in the dataframe, we can visualize the corresponding histograms, and determine the respective binning ranges" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c330da64", + "metadata": {}, + "outputs": [], + "source": [ + "axes = ['kx', 'ky', 'energy', 'delay']\n", + "ranges = [[-3, 3], [-3, 3], [-6, 2], [-600, 1600]]\n", + "sp.view_event_histogram(dfpid=1, axes=axes, ranges=ranges)" + ] + }, + { + "cell_type": "markdown", + "id": "6902fd56-1456-4da6-83a4-0f3f6b831eb6", + "metadata": {}, + "source": [ + "## Define the binning ranges and compute calibrated data volume" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a7601cd7-cd51-40a9-8fc7-8b7d32ff15d0", + "metadata": {}, + "outputs": [], + "source": [ + "axes = ['kx', 'ky', 'energy', 'delay']\n", + "bins = [100, 100, 200, 50]\n", + "ranges = [[-2, 2], [-2, 2], [-4, 2], [-600, 1600]]\n", + "res = sp.compute(bins=bins, axes=axes, ranges=ranges, normalize_to_acquisition_time=\"delay\")" + ] + }, + { + "cell_type": "markdown", + "id": "523794dc", + "metadata": {}, + "source": [ + "## Some visualization:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "99d7d136-b677-4c16-bc8f-31ba8216579c", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axs = plt.subplots(4, 1, figsize=(6, 18), constrained_layout=True)\n", + "res.loc[{'energy':slice(-.1, 0)}].sum(axis=(2,3)).T.plot(ax=axs[0])\n", + "res.loc[{'kx':slice(-.8, -.5)}].sum(axis=(0,3)).T.plot(ax=axs[1])\n", + "res.loc[{'ky':slice(-.2, .2)}].sum(axis=(1,3)).T.plot(ax=axs[2])\n", + "res.loc[{'kx':slice(-.8, -.5), 'energy':slice(.5, 2)}].sum(axis=(0,1)).plot(ax=axs[3])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "596a3217", + "metadata": {}, + "outputs": [], + "source": [ + "# save to NXmpes NeXus (including standardized metadata)\n", + "sp.save(\"WSe2.nxs\")" + ] + }, + { + "cell_type": "markdown", + "id": "185bb6c9-cfea-4987-8ac1-8d2884dd11f3", + "metadata": {}, + "source": [ + "## View the data with H5Web\n", + "H5Web is a tool for visualizing any data in the h5 data format. Since the NeXus format builds opon h5 it can be used to view this data as well. We just import the package and call H5Web with the output filename from the convert command above. For an analysis on NeXus data files please refer to [analysis example](./E3%20pyARPES%20analysis.ipynb).\n", + "\n", + "You can also view this data with the H5Viewer or other tools from your local filesystem." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35934a0f-b7bd-4fd4-bcda-054cc2f034f3", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from jupyterlab_h5web import H5Web" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ccc16e59-4c61-4530-87b7-786e60c990b4", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "H5Web('WSe2.nxs')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e0940aee-f887-4b60-b70d-23f231bede20", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "interpreter": { + "hash": "728003ee06929e5fa5ff815d1b96bf487266025e4b7440930c6bf4536d02d243" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": { + "undefined": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "VBoxModel", + "state": { + "_view_name": "ErrorWidgetView", + "error": {}, + "msg": "Model class 'VBoxModel' from module '@jupyter-widgets/controls' is loaded but can not be instantiated" + } + } + }, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/pynxtools_mpes/nomad/examples/E3 pyARPES analysis.ipynb b/src/pynxtools_mpes/nomad/examples/E3 pyARPES analysis.ipynb new file mode 100644 index 0000000..8ef1889 --- /dev/null +++ b/src/pynxtools_mpes/nomad/examples/E3 pyARPES analysis.ipynb @@ -0,0 +1,252 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "17a6a4f9-8c8a-49f3-b267-ee38d68d4a50", + "metadata": {}, + "source": [ + "# Analysing Multidimensional Photoemission Spectroscopy (MPES) data with pyARPES\n", + "This example shows how to analyse data in the NeXus format with the [pyARPES](https://github.com/chstan/arpes) python package. You'll find details on how to generate such NeXus files in the [convert](./E1%20Convert%20to%20NeXus.ipynb) or [postprocessing](./E2%20ARPES%20postprocessing.ipynb) example." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a8fb79b7-7379-4309-9298-dc16fba67c70", + "metadata": {}, + "outputs": [], + "source": [ + "from arpes.plotting.qt_tool import qt_tool\n", + "import xarray as xr\n", + "import numpy as np\n", + "import h5py as h5" + ] + }, + { + "cell_type": "markdown", + "id": "a98808fd-8d13-4cd9-98a0-ab55c8c40f15", + "metadata": {}, + "source": [ + "## Download the data\n", + "First we set the file directory and create it if not already present." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e9658e8f-21e3-445e-b04f-c8657f707fb6", + "metadata": {}, + "outputs": [], + "source": [ + "FDIR = \"TiTe2\"\n", + "! mkdir -p '{FDIR}'" + ] + }, + { + "cell_type": "markdown", + "id": "14597c45-b958-434c-b0ed-f31ad68378bc", + "metadata": {}, + "source": [ + "Since the provided datafile is comparably large (~200MB) it is not directly provided with the example.\n", + "You can download the data for [zero](https://zenodo.org/record/5541490/files/TiTe2_0deg.nxs?download=1) and [60 degree](https://zenodo.org/record/5541490/files/TiTe2_60deg.nxs?download=1) from zenodo. Place the files in the FDIR directory specified in the cell above. Under Linux, macOS and in a NORTH container you can directly use the cell below to download the files with curl." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58315f02-ae42-4318-abdc-e102e1753290", + "metadata": {}, + "outputs": [], + "source": [ + "! curl -o \"{FDIR}/TiTe2_0deg.nxs\" \"https://zenodo.org/records/5541490/files/TiTe2_0deg.nxs?download=1\"\n", + "! curl -o \"{FDIR}/TiTe2_60deg.nxs\" \"https://zenodo.org/records/5541490/files/TiTe2_60deg.nxs?download=1\"" + ] + }, + { + "cell_type": "markdown", + "id": "ea6ea584-fe25-4af1-ae72-6ae7d7f34830", + "metadata": {}, + "source": [ + "## Load data into a xarray\n", + "Extract the NeXus file into an xArray. In the future, mode advanced loading APIs will be developed, transferring all useful metadata." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "3b259695-1f73-4bd1-a1bc-4b5abd0432a6", + "metadata": {}, + "outputs": [], + "source": [ + "h5_TiTe2_0deg = h5.File(f'{FDIR}/TiTe2_0deg.nxs', 'r' )\n", + "meas_TiTe2_0deg = xr.DataArray(\n", + " h5_TiTe2_0deg['entry/data/Photoemission intensity'][:],\n", + " coords={'BE': h5_TiTe2_0deg['entry/data/calculated_Energy'][:],\n", + " 'kx': h5_TiTe2_0deg['entry/data/calculated_kx'][:],\n", + " 'ky': h5_TiTe2_0deg['entry/data/calculated_ky'][:]},\n", + ")\n", + "\n", + "h5_TiTe2_60deg = h5.File(f'{FDIR}/TiTe2_60deg.nxs', 'r' )\n", + "meas_TiTe2_60deg = xr.DataArray(\n", + " h5_TiTe2_60deg['entry/data/Photoemission intensity'][:],\n", + " coords={'BE': h5_TiTe2_60deg['entry/data/calculated_Energy'][:],\n", + " 'kx': h5_TiTe2_60deg['entry/data/calculated_kx'][:],\n", + " 'ky': h5_TiTe2_60deg['entry/data/calculated_ky'][:]},\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "1d7de2d8-025f-4ead-9342-3c40f530b7fe", + "metadata": {}, + "source": [ + "## Visualize 0º data\n", + "You can use the \"Axes\" tab to transpose the volume to obtain the optimal panel arrangement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a9436bce-4f5d-4f68-9f8b-c0f5ee1ef3d3", + "metadata": {}, + "outputs": [], + "source": [ + "qt_tool(meas_TiTe2_0deg)" + ] + }, + { + "cell_type": "markdown", + "id": "88310069-fb9a-48c4-a490-072118176937", + "metadata": {}, + "source": [ + "## Visualize 60º data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a01b408e-1caf-400c-a72f-0b21d7085916", + "metadata": {}, + "outputs": [], + "source": [ + "qt_tool(meas_TiTe2_60deg)" + ] + }, + { + "cell_type": "markdown", + "id": "7769f64f-1efd-4bd6-a905-a7cfcd9aea20", + "metadata": {}, + "source": [ + "## View the difference between the angles\n", + "Noticed the asymmetry in the electron pockts at the Fermi surface? Let's make it shine!\n", + "We calculate the difference between the two angles and visualize it.\n", + "Unfortunately, there are only linear colorscales available in pyARPES.\n", + "The resulting data resembles the main observation in this [paper](https://arxiv.org/pdf/2107.07158.pdf)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9aa56079-be66-4575-91c9-7bc77213893f", + "metadata": {}, + "outputs": [], + "source": [ + "qt_tool(meas_TiTe2_0deg - meas_TiTe2_60deg)" + ] + }, + { + "cell_type": "markdown", + "id": "6310460a-c58f-49bc-8d64-cfcf87f2fc51", + "metadata": {}, + "source": [ + "## 4D visualization" + ] + }, + { + "cell_type": "markdown", + "id": "c2e62727-32c9-432e-9d25-9d35b4347811", + "metadata": {}, + "source": [ + "We can also test 4D data. The procedure is the same: [download](https://zenodo.org/record/4632481/files/201905_MoTe2.nxs?download=1) the data, convert it to xarray and show it with `qt_tool`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4bf60894-22d3-4034-a808-311f285278bc", + "metadata": {}, + "outputs": [], + "source": [ + "! curl -o \"{FDIR}/MoTe2_dyn.nxs\" \"https://zenodo.org/records/4632481/files/201905_MoTe2.nxs?download=1\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "dda4bc9a-9729-4bd3-9456-a174427e7a3c", + "metadata": {}, + "outputs": [], + "source": [ + "h5_MoTe2_dyn = h5.File(f'{FDIR}/MoTe2_dyn.nxs', 'r')\n", + "meas_MoTe2_dyn = xr.DataArray(\n", + " h5_MoTe2_dyn['entry/data/Photoemission intensity'][:],\n", + " coords={'tpp': np.squeeze(h5_MoTe2_dyn['entry/data/calculated_Tpp'][:]),\n", + " 'BE': np.squeeze(h5_MoTe2_dyn['entry/data/calculated_Energy'][:]),\n", + " 'kx': np.squeeze(h5_MoTe2_dyn['entry/data/calculated_kx'][:]),\n", + " 'ky': np.squeeze(h5_MoTe2_dyn['entry/data/calculated_ky'][:])},\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "e44915a3-e13f-4bb9-a6d5-3af34c113d09", + "metadata": {}, + "source": [ + "At the Fermi energy, you will see an electron pocket appearing close to time zero and disappearing immediately after wards. This is the signature of a dynamical Lifshitz transition, a change in the topology of the Fermi surface. To learn more, read https://www.science.org/doi/10.1126/sciadv.abd9275'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb0b2144-7ae8-401e-a351-6416098c408a", + "metadata": {}, + "outputs": [], + "source": [ + "qt_tool(meas_MoTe2_dyn)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.12" + }, + "vscode": { + "interpreter": { + "hash": "c2ecc4d45d4efcd07af778d75fd26bf86d0642a6646ea5c57f06d5857684e419" + } + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/pynxtools_mpes/nomad/examples/E4 Convert to k-space.ipynb b/src/pynxtools_mpes/nomad/examples/E4 Convert to k-space.ipynb new file mode 100644 index 0000000..7c607c0 --- /dev/null +++ b/src/pynxtools_mpes/nomad/examples/E4 Convert to k-space.ipynb @@ -0,0 +1,278 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Convert measurement angles to k-space\n", + "\n", + "In this example, we will use the NeXus format to automatically detect and read the angles from an ARPES measurement and do a k-space conversion with pyArpes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "import numpy as np\n", + "\n", + "from arpes.io import load_data\n", + "from arpes.endstations.plugin.nexus import NeXusEndstation\n", + "from arpes.utilities.conversion import convert_to_kspace\n", + "\n", + "\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Download data\n", + "\n", + "First we need to automatically download the data from [Nomad](https://nomad-lab.eu/prod/v1/api/v1/entries/xV9yMspIyXMfia6nNw3NIbnlZ91H/raw/Scan1496.nxs)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "! curl -o Scan1496.nxs https://nomad-lab.eu/prod/v1/api/v1/entries/xV9yMspIyXMfia6nNw3NIbnlZ91H/raw/Scan1496.nxs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Scan1496 = load_data(\"Scan1496.nxs\", location=NeXusEndstation)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Apply rotational offset from experimental geometry" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "Scan1496.S.apply_offsets({\n", + " \"chi\": 19/180*np.pi,\n", + "})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Subtract photon energy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "Scan1496_shifted = Scan1496\n", + "Scan1496_shifted['eV'] = Scan1496_shifted['eV'] - Scan1496.attrs[\"hv\"].magnitude" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We read all metadata from the nexus file into xarray attributes for easy programmatic access." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Scan1496_shifted.attrs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can easily readout interesting values including their units, e.g., the incident photon energy of the beam:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "Scan1496_shifted.attrs['instrument/beam_probe/incident_energy']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generate Fermi surface" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "fsmap = Scan1496_shifted.S.generic_fermi_surface(0)\n", + "fsmap.S.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Convert into k-space" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "fsmap_converted = convert_to_kspace(\n", + " fsmap,\n", + " kx=np.linspace(-.6, .6, 400),\n", + " ky=np.linspace(-1.2, 1.2, 400),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "fsmap_converted.T.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "Scan1496_converted = convert_to_kspace(\n", + " Scan1496,\n", + " kx=np.linspace(-.6, .6, 400),\n", + " ky=np.linspace(-1.2, 1.2, 400),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Scan1496_converted.S.generic_fermi_surface(0).T.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "Scan1496.loc[{\"eV\":slice(1, 1.2)}].sum(dim=\"eV\").S.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "Scan1496_converted.loc[{\"eV\":slice(1, 1.2)}].sum(dim=\"eV\").S.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.12" + }, + "vscode": { + "interpreter": { + "hash": "a164666994e9db75450cd7016dd7e51d42ea6e7c1e5e8017af1f8068ca906367" + } + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/src/pynxtools_mpes/nomad/examples/MoTe.archive.json b/src/pynxtools_mpes/nomad/examples/MoTe.archive.json new file mode 100644 index 0000000..21bf872 --- /dev/null +++ b/src/pynxtools_mpes/nomad/examples/MoTe.archive.json @@ -0,0 +1,13 @@ +{ + "data": { + "m_def": "pynxtools.nomad.dataconverter.NexusDataConverter", + "input_files": [ + "MoTe2.h5", + "config_file.json" + ], + "reader": "mpes", + "nxdl": "NXmpes", + "output": "MoTe2.mpes.nxs" + }, + "m_ref_archives": {} +} \ No newline at end of file diff --git a/src/pynxtools_mpes/nomad/examples/README.md b/src/pynxtools_mpes/nomad/examples/README.md new file mode 100644 index 0000000..6545349 --- /dev/null +++ b/src/pynxtools_mpes/nomad/examples/README.md @@ -0,0 +1,45 @@ +# Introduction + +This example presents the capabilities of the NOMAD platform to store and standardize multi photoemission spectroscopy (MPES) experimental data. It contains four major examples: + +- Taking a pre-binned file, here stored in a h5 file, and converting it into the standardized MPES NeXus format. There exists a [NeXus application definition for MPES](https://manual.nexusformat.org/classes/contributed_definitions/NXmpes.html#nxmpes) which details the internal structure of such a file. +- Binning of raw data (see [here](https://www.nature.com/articles/s41597-020-00769-8) for additional resources) into a h5 file and consecutively generating a NeXus file from it. +- An analysis example using data in the NeXus format and employing the [pyARPES](https://github.com/chstan/arpes) analysis tool to reproduce the main findings of [this paper](https://arxiv.org/pdf/2107.07158.pdf). +- Importing angle-resolved data stored in NXmpes_arpes, and convert these into momentum space coordinates using tools in pyARPES. + +# Viewing uploaded data + +Below, you find an overview of your uploaded data. +Click on the `> /` button to get a list of your data or select **FILES** from the top menu of this upload. +You may add your own files to the upload or experiment with the pre-existing electronic lab book example. +The ELN follows the general structure of NOMAD ELN templates and you may refer to the [documentation](https://nomad-lab.eu/prod/v1/staging/docs/archive.html) or a [YouTube tutorial](https://youtu.be/o5ETHmGmnaI) (~1h) +for further information. +When the ELN is saved a NeXus file will be generated from the provided example data. +You may also view your supplied or generated NeXus files here with the H5Web viewer. +To do so open the **FILES** tab and just select a `.nxs` file. + +# Analysing the data + +The examples work through the use of NOMAD remote tools hub (NORTH) containers, i.e. besides using and dealing with the uploaded MPES data, an analysis container can be started. If you want to execute the examples locally, please refer to the `INSTALL.md` file in this directory. +Please note that the binning from raw files is rather memory intense and you should have at least 40 GB of available +memory to be able to execute this particular example. +If your local container suddenly stops and shows an error 137 it is due to memory limitations. + +In the container you'll find three example notebooks containing examples mentioned above. + +To start an analysis, note your upload id (which you find on top of this explanation) and select **ANALYZE** from the top menu, then **NOMAD Remote Tools Hub**. +In the appearing list you'll find the `mpes` Example, click on it and click **LAUNCH**. +After a few moments a new tab will open which displays a jupyter environment providing the required analysis tools. +To find the examples navigate to uploads inside the jupyter hub browser and select the folder with your noted upload id. +There you'll find the example `ipynb` notebooks. +Double-clicking one of the notebooks will open the example in the jupyter main window. +From here you find detailed instructions inside each of the notebooks. + +# Where to go from here? + +If you're interested in using this pipeline and NOMAD in general you'll find support at [FAIRmat](https://www.fairmat-nfdi.eu/fairmat/consortium). + +If you have questions regarding the experiments in the examples please refer to the [Dynamics of Correlated Materials](https://pc.fhi-berlin.mpg.de/docm/) or [Structural & Electronic Surface Dynamics](https://pc.fhi-berlin.mpg.de/sesd/) working group at the Fritz-Haber Institute Berlin. + +For general questions regarding the MPES pipeline and if you're interested in building one for your +own research workflow you may contact [Florian Dobener](https://www.fairmat-nfdi.eu/fairmat/fairmat_/fairmatteam) from the FAIRmat consortium. diff --git a/src/pynxtools_mpes/nomad/examples/WSe2.archive.json b/src/pynxtools_mpes/nomad/examples/WSe2.archive.json new file mode 100644 index 0000000..fd20421 --- /dev/null +++ b/src/pynxtools_mpes/nomad/examples/WSe2.archive.json @@ -0,0 +1,84 @@ +{ + "data": { + "m_def": "../upload/raw/mpes.scheme.archive.yml#/definitions/section_definitions/0", + "output": "WSe2_eln.yaml", + "title": "Valence Band Dynamics - 800 nm linear s-polarized pump, 0.6 mJ/cm2 absorbed fluence", + "User": { + "name": "Julian Maklar", + "role": "Principal Investigator", + "affiliation": "Fritz Haber Institute of the Max Planck Society", + "address": "Faradayweg 4-6, 14915 Berlin", + "email": "maklar@fhi-berlin.mpg.de" + }, + "Instrument": { + "energy_resolution": 2.2430472876e-20, + "momentum_resolution": 800000000, + "temporal_resolution": 3.5e-14, + "Manipulator": { + "sample_temperature": 26.85 + }, + "Analyzer": { + "slow_axes": "delay", + "spatial_resolution": 0.00001, + "energy_resolution": 1.7623942974e-17, + "momentum_resolution": 800000000 + }, + "Source": { + "Probe": { + "frequency": 500000, + "photon_energy": 3.47672329578e-18 + }, + "Pump": { + "frequency": 500000, + "photon_energy": 2.4833737827e-19 + } + }, + "Beam": { + "Probe": { + "incident_energy": 3.4767232957799996e-18, + "incident_energy_spread": 1.7623942974e-20, + "pulse_duration": 2.0000000000000003e-14, + "incident_polarization": [ + 1, + 1, + 0, + 0 + ], + "extent": [ + 0.00007999999999999999, + 0.00007999999999999999 + ] + }, + "Pump": { + "incident_energy": 2.4833737826999997e-19, + "incident_energy_spread": 1.2817413071999999e-20, + "pulse_duration": 3.5e-14, + "incident_polarization": [ + 1, + -1, + 0, + 0 + ], + "extent": [ + 0.00022999999999999998, + 0.000265 + ], + "incident_wavelength": 8e-7, + "average_power": 0.3, + "pulse_energy": 6e-7, + "fluence": 1.5 + } + } + }, + "Sample": { + "preparation_date": "2019-01-13T09:00:00+00:00", + "sample_history": "Cleaved", + "chemical_formula": "WSe2", + "description": "Sample", + "name": "WSe2 Single Crystal", + "temperature": 26.85, + "gas_pressure": 5e-14 + } + }, + "m_ref_archives": {} +} \ No newline at end of file diff --git a/src/pynxtools_mpes/nomad/examples/WSe2_eln.yaml b/src/pynxtools_mpes/nomad/examples/WSe2_eln.yaml new file mode 100644 index 0000000..2c2d2d7 --- /dev/null +++ b/src/pynxtools_mpes/nomad/examples/WSe2_eln.yaml @@ -0,0 +1,113 @@ +Instrument: + Analyzer: + energy_resolution: + unit: eV + value: 110.00000000000001 + momentum_resolution: + unit: 1 / Å + value: 0.08 + slow_axes: delay + spatial_resolution: + unit: µm + value: 10.0 + Beam: + Probe: + extent: + unit: µm + value: + - 80.0 + - 80.0 + incident_energy: + unit: eV + value: 21.7 + incident_energy_spread: + unit: eV + value: 0.11 + incident_polarization: + - 1.0 + - 1.0 + - 0.0 + - 0.0 + pulse_duration: + unit: fs + value: 20.0 + Pump: + average_power: + unit: mW + value: 300.0 + extent: + unit: µm + value: + - 229.99999999999997 + - 265.0 + fluence: + unit: mJ / cm ** 2 + value: 0.15000000000000002 + incident_energy: + unit: eV + value: 1.5499999999999998 + incident_energy_spread: + unit: eV + value: 0.08 + incident_polarization: + - 1.0 + - -1.0 + - 0.0 + - 0.0 + incident_wavelength: + unit: nm + value: 799.9999999999999 + pulse_duration: + unit: fs + value: 35.0 + pulse_energy: + unit: µJ + value: 0.6 + Manipulator: + sample_temperature: + unit: K + value: 300.0 + Source: + Probe: + frequency: + unit: kHz + value: 500.0 + photon_energy: + unit: eV + value: 21.700000000000003 + Pump: + frequency: + unit: kHz + value: 500.0 + photon_energy: + unit: eV + value: 1.5500000000000003 + energy_resolution: + unit: meV + value: 140.0 + momentum_resolution: + unit: 1 / Å + value: 0.08 + temporal_resolution: + unit: fs + value: 35.0 +Sample: + chemical_formula: WSe2 + description: Sample + gas_pressure: + unit: mbar + value: 5.000000000000001e-11 + name: WSe2 Single Crystal + preparation_date: '2019-01-13T09:00:00+00:00' + sample_history: Cleaved + temperature: + unit: K + value: 300.0 +User: + address: Faradayweg 4-6, 14915 Berlin + affiliation: Fritz Haber Institute of the Max Planck Society + email: maklar@fhi-berlin.mpg.de + name: Julian Maklar + role: Principal Investigator +title: Valence Band Dynamics - 800 nm linear s-polarized pump, 0.6 mJ/cm2 absorbed + fluence diff --git a/src/pynxtools_mpes/nomad/examples/config_file.json b/src/pynxtools_mpes/nomad/examples/config_file.json new file mode 100644 index 0000000..226c89d --- /dev/null +++ b/src/pynxtools_mpes/nomad/examples/config_file.json @@ -0,0 +1,357 @@ +{ + "/@default": "entry", + "/ENTRY/@default": "data", + "/ENTRY/title": "['@eln:/ENTRY/title', '@attrs:metadata/entry_title']", + "/ENTRY/start_time": "@attrs:metadata/timing/acquisition_start", + "/ENTRY/experiment_institution": "Fritz Haber Institute - Max Planck Society", + "/ENTRY/experiment_facility": "Time Resolved ARPES", + "/ENTRY/experiment_laboratory": "Clean Room 4", + "/ENTRY/entry_identifier": { + "identifier":"@attrs:metadata/entry_identifier" + }, + "/ENTRY/end_time": "@attrs:metadata/timing/acquisition_stop", + "/ENTRY/duration": "@attrs:metadata/timing/acquisition_duration", + "/ENTRY/duration/@units": "s", + "/ENTRY/collection_time": "@attrs:metadata/timing/collection_time", + "/ENTRY/collection_time/@units": "s", + "/ENTRY/USER[user]": { + "name": "!['@eln:/ENTRY/User/name', '@attrs:metadata/user0/name']", + "role": "['@eln:/ENTRY/User/role', '@attrs:metadata/user0/role']", + "affiliation": "!['@eln:/ENTRY/User/affiliation', '@attrs:metadata/user0/affiliation']", + "address": "['@eln:/ENTRY/User/address', '@attrs:metadata/user0/address']", + "email": "['@eln:/ENTRY/User/email', '@attrs:metadata/user0/email']" + }, + "/ENTRY/INSTRUMENT[instrument]": { + "name": "Time-of-flight momentum microscope equipped delay line detector, at the endstation of the high rep-rate HHG source at FHI", + "name/@short_name": "TR-ARPES @ FHI", + "energy_resolution": { + "resolution": "!['@eln:/ENTRY/Instrument/Analyzer/energy_resolution', '@attrs:metadata/instrument/energy_resolution', '140']", + "resolution/@units": "meV", + "physical_quantity": "energy", + "type": "estimated" + }, + "RESOLUTION[temporal_resolution]": { + "resolution": 35.0, + "resolution/@units": "fs", + "physical_quantity": "time", + "type": "estimated" + }, + "RESOLUTION[momentum_resolution]": "@link:/entry/instrument/electronanalyser/momentum_resolution", + "pressure_gauge": { + "name": "sample_chamber_pressure", + "measurement": "pressure", + "value": "!['@eln:/ENTRY/Sample/gas_pressure', '@attrs:metadata/file/trARPES:XGS600:PressureAC:P_RD']", + "value/@units": "mbar" + }, + "ELECTRONANALYSER[electronanalyser]": { + "description": "SPECS Metis 1000 Momentum Microscope", + "device_information": { + "vendor": "SPECS GmbH", + "model": "Metis 1000 Momentum Microscope" + }, + "fast_axes": ["kx", "ky", "E"], + "slow_axes": "@attrs:metadata/instrument/analyzer/slow_axes", + "energy_resolution": { + "resolution": "!['@eln:/ENTRY/Instrument/Analyzer/energy_resolution', '@attrs:metadata/instrument/analyzer/energy_resolution']", + "resolution/@units": "meV", + "physical_quantity": "energy", + "type": "estimated" + }, + "momentum_resolution": { + "resolution": "!['@eln:/ENTRY/Instrument/Analyzer/momentum_resolution', '@attrs:metadata/instrument/analyzer/momentum_resolution']", + "resolution/@units": "1/angstrom", + "physical_quantity": "momentum", + "type": "estimated" + }, + "spatial_resolution": { + "resolution": "!['@eln:/ENTRY/Instrument/Analyzer/spatial_resolution', '@attrs:metadata/instrument/analyzer/spatial_resolution']", + "resolution/@units": "µm", + "physical_quantity": "length", + "type": "estimated" + }, + "depends_on": "/entry/instrument/electronanalyser/transformations/trans_z", + "TRANSFORMATIONS[transformations]": { + "AXISNAME[trans_z]": 4.0, + "AXISNAME[trans_z]/@depends_on": "rot_y", + "AXISNAME[trans_z]/@transformation_type": "translation", + "AXISNAME[trans_z]/@units": "mm", + "AXISNAME[trans_z]/@vector": [0, 0, 1], + "AXISNAME[rot_y]": -115.0, + "AXISNAME[rot_y]/@depends_on": ".", + "AXISNAME[rot_y]/@transformation_type": "rotation", + "AXISNAME[rot_y]/@units": "degrees", + "AXISNAME[rot_y]/@vector": [0, 1, 0] + }, + "COLLECTIONCOLUMN[collectioncolumn]": { + "projection": "@attrs:metadata/instrument/analyzer/projection", + "scheme": "['@attrs:metadata/instrument/analyzer/scheme', 'momentum dispersive']", + "lens_mode": "@attrs:metadata/instrument/analyzer/lens_mode", + "extractor_voltage": "@attrs:metadata/file/KTOF:Lens:Extr:V", + "extractor_voltage/@units": "V", + "extractor_current": "@attrs:metadata/file/KTOF:Lens:Extr:I", + "extractor_current/@units": "µA", + "working_distance": 4.0, + "working_distance/@units": "mm", + "LENS_EM[lens_*{A,B,C,D,E,F,G,H,I,UCA,UFA,Foc}]": { + "name": "*", + "voltage": "@attrs:metadata/file/KTOF:Lens:*:V", + "voltage/@units": "V" + }, + "field_aperture": { + "shape": "@attrs:metadata/instrument/analyzer/fa_shape", + "size": "@attrs:metadata/instrument/analyzer/fa_size", + "size/@units": "µm", + "POSITIONER[fa_m1]": { + "value": "@attrs:metadata/file/KTOF:Apertures:m1.RBV", + "value/@units": "mm" + }, + "POSITIONER[fa_m2]": { + "value": "@attrs:metadata/file/KTOF:Apertures:m2.RBV", + "value/@units": "mm" + } + }, + "contrast_aperture": { + "shape": "@attrs:metadata/instrument/analyzer/ca_shape", + "size": "@attrs:metadata/instrument/analyzer/ca_size", + "size/@units": "µm", + "POSITIONER[ca_m3]": { + "value": "@attrs:metadata/file/KTOF:Apertures:m3.RBV", + "value/@units": "mm" + } + } + }, + "ENERGYDISPERSION[energydispersion]": { + "pass_energy": "@attrs:metadata/file/KTOF:Lens:TOF:V", + "pass_energy/@units": "eV", + "scheme": "tof", + "tof_distance": 0.9, + "tof_distance/@units": "m" + }, + "DETECTOR[detector]": { + "amplifier_type": "MCP", + "detector_type": "DLD", + "sensor_pixels": [1800, 1800], + "sensor_pixels/@units": "", + "amplifier_bias": "@attrs:metadata/file/KTOF:Lens:MCPfront:V", + "amplifier_bias/@units": "V", + "amplifier_voltage": "@attrs:metadata/file/KTOF:Lens:MCPback:V", + "amplifier_voltage/@units": "V", + "detector_voltage": "@attrs:metadata/file/KTOF:Lens:UDLD:V", + "detector_voltage/@units": "V" + } + }, + "sourceTYPE[source_probe]": { + "name": "HHG @ TR-ARPES @ FHI", + "probe": "photon", + "type": "HHG laser", + "mode": "Single Bunch", + "frequency": "['@eln:/ENTRY/Instrument/Source/Probe/frequency', '@attrs:metadata/instrument/beam/probe/frequency']", + "frequency/@units": "kHz", + "associated_beam": "/entry/instrument/beam_probe" + }, + "beamTYPE[beam_probe]": { + "distance": 0.0, + "distance/@units": "mm", + "incident_energy": "!['@eln:/ENTRY/Instrument/Beam/Probe/incident_energy', '@attrs:metadata/instrument/beam/probe/incident_energy']", + "incident_energy/@units": "eV", + "incident_energy_spread": "['@eln:/ENTRY/Instrument/Beam/Probe/incident_energy_spread', '@attrs:metadata/instrument/beam/probe/incident_energy_spread']", + "incident_energy_spread/@units": "eV", + "pulse_duration": "['@eln:/ENTRY/Instrument/Beam/Probe/pulse_duration', '@attrs:metadata/instrument/beam/probe/pulse_duration']", + "pulse_duration/@units": "fs", + "incident_polarization": "['@eln:/ENTRY/Instrument/Beam/Probe/incident_polarization', '@attrs:metadata/instrument/beam/probe/incident_polarization']", + "incident_polarization/@units": "V^2/mm^2", + "extent": "['@eln:/ENTRY/Instrument/Beam/Probe/extent', '@attrs:metadata/instrument/beam/probe/extent']", + "extent/@units": "µm", + "associated_source": "/entry/instrument/source_probe" + }, + "sourceTYPE[source_pump]": { + "name": "OPCPA @ TR-ARPES @ FHI", + "probe": "visible light", + "type": "Optical Laser", + "mode": "Single Bunch", + "frequency": "['@eln:/ENTRY/Instrument/Source/Pump/frequency', '@attrs:metadata/instrument/beam/pump/frequency']", + "frequency/@units": "kHz", + "associated_beam": "/entry/instrument/beam_pump" + }, + "beamTYPE[beam_pump]": { + "distance": 0.0, + "distance/@units": "mm", + "incident_energy": "!['@eln:/ENTRY/Instrument/Beam/Pump/incident_energy', '@attrs:metadata/instrument/beam/pump/incident_energy']", + "incident_energy/@units": "eV", + "incident_energy_spread": "['@eln:/ENTRY/Instrument/Beam/Pump/incident_energy_spread', '@attrs:metadata/instrument/beam/pump/incident_energy_spread']", + "incident_energy_spread/@units": "eV", + "incident_wavelength": "['@eln:/ENTRY/Instrument/Beam/Pump/incident_wavelength', '@attrs:metadata/instrument/beam/pump/incident_wavelength']", + "incident_wavelength/@units": "nm", + "pulse_duration": "['@eln:/ENTRY/Instrument/Beam/Pump/pulse_duration', '@attrs:metadata/instrument/beam/pump/pulse_duration']", + "pulse_duration/@units": "fs", + "incident_polarization": "['@eln:/ENTRY/Instrument/Beam/Pump/incident_polarization', '@attrs:metadata/instrument/beam/pump/incident_polarization']", + "incident_polarization/@units": "V^2/mm^2", + "pulse_energy": "['@eln:/ENTRY/Instrument/Beam/Pump/pulse_energy', '@attrs:metadata/instrument/beam/pump/pulse_energy']", + "pulse_energy/@units": "µJ", + "average_power": "['@eln:/ENTRY/Instrument/Beam/Pump/average_power', '@attrs:metadata/instrument/beam/pump/average_power']", + "average_power/@units": "mW", + "extent": "['@eln:/ENTRY/Instrument/Beam/Pump/extent', '@attrs:metadata/instrument/beam/pump/extent']", + "extent/@units": "µm", + "fluence": "['@eln:/ENTRY/Instrument/Beam/Pump/fluence', '@attrs:metadata/instrument/beam/pump/fluence']", + "fluence/@units": "mJ/cm^2", + "associated_source": "/entry/instrument/source_pump" + }, + "MANIPULATOR[manipulator]": { + "temperature_sensor": { + "name": "sample_temperature", + "measurement": "temperature", + "value": "!['@eln:/ENTRY/Instrument/Manipulator/sample_temperature', '@attrs:metadata/file/trARPES:Carving:TEMP_RBV']", + "value/@units": "K" + }, + "sample_bias_voltmeter": { + "name": "sample_bias", + "measurement": "voltage", + "value": "!@attrs:metadata/file/KTOF:Lens:Sample:V", + "value/@units": "V" + }, + "depends_on": "/entry/instrument/manipulator/transformations/trans_z", + "TRANSFORMATIONS[transformations]": { + "AXISNAME[trans_z]": -0.32, + "AXISNAME[trans_z]/@depends_on": "rot_z", + "AXISNAME[trans_z]/@transformation_type": "translation", + "AXISNAME[trans_z]/@units": "m", + "AXISNAME[trans_z]/@vector": [0, 0, 1], + "AXISNAME[rot_z]/@depends_on": "rot_x", + "AXISNAME[rot_z]": -25.0, + "AXISNAME[rot_z]/@transformation_type": "rotation", + "AXISNAME[rot_z]/@units": "degrees", + "AXISNAME[rot_z]/@vector": [0, 0, 1], + "AXISNAME[rot_x]/@depends_on": ".", + "AXISNAME[rot_x]": -90.0, + "AXISNAME[rot_x]/@transformation_type": "rotation", + "AXISNAME[rot_x]/@units": "degrees", + "AXISNAME[rot_x]/@vector": [1, 0, 0] + } + } + }, + "/ENTRY/SAMPLE[sample]": { + "preparation_date": "['@eln:/ENTRY/Sample/preparation_date', '@attrs:metadata/sample/preparation_date']", + "history/notes/description": "['@eln:/ENTRY/Sample/sample_history', '@attrs:metadata/sample/sample_history']", + "history/notes/type": "text/plain", + "description": "['@eln:/ENTRY/Sample/description', '@attrs:metadata/sample/chemical_formula']", + "name": "['@eln:/ENTRY/Sample/name', '@attrs:metadata/sample/name']", + "situation": "vacuum", + "SUBSTANCE[substance]/molecular_formula_hill": "['@eln:/ENTRY/Sample/chemical_formula', '@attrs:metadata/sample/chemical_formula']", + "temperature_env": { + "temperature_sensor": "@link:/entry/instrument/manipulator/temperature_sensor" + }, + "gas_pressure_env": { + "pressure_gauge": "@link:/entry/instrument/pressure_gauge" + }, + "bias_env": { + "voltmeter": "@link:/entry/instrument/manipulator/sample_bias_voltmeter" + }, + "depends_on": "/entry/sample/transformations/corrected_phi", + "TRANSFORMATIONS[transformations]": { + "AXISNAME[corrected_phi]/@depends_on": "rot_omg", + "AXISNAME[corrected_phi]": 90.0, + "AXISNAME[corrected_phi]/@units": "degrees", + "AXISNAME[corrected_phi]/@transformation_type": "rotation", + "AXISNAME[corrected_phi]/@vector": [0, 1, 0], + "AXISNAME[rot_omg]/@depends_on": "rot_phi", + "AXISNAME[rot_omg]": "@attrs:metadata/file/trARPES:Carving:OMG.RBV", + "AXISNAME[rot_omg]/@units": "degrees", + "AXISNAME[rot_omg]/@transformation_type": "rotation", + "AXISNAME[rot_omg]/@vector": [1, 0, 0], + "AXISNAME[rot_phi]/@depends_on": "rot_tht", + "AXISNAME[rot_phi]": "@attrs:metadata/file/trARPES:Carving:PHI.RBV", + "AXISNAME[rot_phi]/@units": "degrees", + "AXISNAME[rot_phi]/@transformation_type": "rotation", + "AXISNAME[rot_phi]/@vector": [0, 1, 0], + "AXISNAME[rot_tht]/@depends_on": "trans_z", + "AXISNAME[rot_tht]": "@attrs:metadata/file/trARPES:Carving:THT.RBV", + "AXISNAME[rot_tht]/@units": "degrees", + "AXISNAME[rot_tht]/@transformation_type": "rotation", + "AXISNAME[rot_tht]/@vector": [0, 0, 1], + "AXISNAME[trans_z]/@depends_on": "trans_y", + "AXISNAME[trans_z]": "@attrs:metadata/file/trARPES:Carving:TRZ.RBV", + "AXISNAME[trans_z]/@units": "mm", + "AXISNAME[trans_z]/@transformation_type": "translation", + "AXISNAME[trans_z]/@vector": [0, 0, 1], + "AXISNAME[trans_y]/@depends_on": "trans_x", + "AXISNAME[trans_y]": "@attrs:metadata/file/trARPES:Carving:TRY.RBV", + "AXISNAME[trans_y]/@units": "mm", + "AXISNAME[trans_y]/@transformation_type": "translation", + "AXISNAME[trans_y]/@vector": [0, 1, 0], + "AXISNAME[trans_x]/@depends_on": "/entry/instrument/manipulator/transformations/trans_z", + "AXISNAME[trans_x]": "@attrs:metadata/file/trARPES:Carving:TRX.RBV", + "AXISNAME[trans_x]/@units": "mm", + "AXISNAME[trans_x]/@transformation_type": "translation", + "AXISNAME[trans_x]/@vector": [1, 0, 0] + } + }, + "/ENTRY/PROCESS_MPES[process]/DISTORTION[distortion]": { + "symmetry": "!['@attrs:metadata/momentum_correction/correction/rotation_symmetry', '@attrs:metadata/momentum_correction/rotsym']", + "symmetry/@units": "", + "original_centre": "['@attrs:metadata/momentum_correction/correction/center_point', '@attrs:metadata/momentum_correction/pcent']", + "original_centre/@units": "", + "original_points": "['@attrs:metadata/momentum_correction/correction/outer_points', '@attrs:metadata/momentum_correction/pouter']", + "original_points/@units": "", + "cdeform_field": "['@attrs:metadata/momentum_correction/correction/cdeform_field', '@attrs:metadata/momentum_correction/cdeform_field']", + "cdeform_field/@units": "", + "rdeform_field": "['@attrs:metadata/momentum_correction/correction/rdeform_field', '@attrs:metadata/momentum_correction/rdeform_field']", + "rdeform_field/@units": "" + }, + "/ENTRY/PROCESS_MPES[process]/REGISTRATION[registration]": { + "depends_on": "/entry/process/registration/transformations/rot_z", + "TRANSFORMATIONS[transformations]": { + "AXISNAME[trans_x]": "['@attrs:metadata/momentum_correction/registration/trans_x/value', '@attrs:metadata/momentum_correction/adjust_params/xtrans']", + "AXISNAME[trans_x]/@transformation_type": "translation", + "AXISNAME[trans_x]/@units": "pixels", + "AXISNAME[trans_x]/@vector": "['@attrs:metadata/momentum_correction/registration/trans_x/vector', '@attrs:metadata/momentum_correction/adjust_params/x_vector']", + "AXISNAME[trans_x]/@depends_on": "['@attrs:metadata/momentum_correction/registration/trans_x/depends_on', '.']", + "AXISNAME[trans_y]": "['@attrs:metadata/momentum_correction/registration/trans_y/value', '@attrs:metadata/momentum_correction/adjust_params/ytrans']", + "AXISNAME[trans_y]/@units": "pixels", + "AXISNAME[trans_y]/@transformation_type": "translation", + "AXISNAME[trans_y]/@vector": "['@attrs:metadata/momentum_correction/registration/trans_y/vector', '@attrs:metadata/momentum_correction/adjust_params/y_vector']", + "AXISNAME[trans_y]/@depends_on": "['@attrs:metadata/momentum_correction/registration/trans_y/depends_on', 'trans_x']", + "AXISNAME[rot_z]": "['@attrs:metadata/momentum_correction/registration/rot_z/value', '@attrs:metadata/momentum_correction/adjust_params/angle']", + "AXISNAME[rot_z]/@units": "degrees", + "AXISNAME[rot_z]/@transformation_type": "rotation", + "AXISNAME[rot_z]/@offset": "['@attrs:metadata/momentum_correction/registration/rot_z/offset', '@attrs:metadata/momentum_correction/adjust_params/offset']", + "AXISNAME[rot_z]/@vector": "['@attrs:metadata/momentum_correction/registration/rot_z/vector', '@attrs:metadata/momentum_correction/adjust_params/rotation_vector']", + "AXISNAME[rot_z]/@depends_on": "['@attrs:metadata/momentum_correction/registration/rot_z/depends_on', 'trans_y']" + } + }, + "/ENTRY/PROCESS_MPES[process]/energy_calibration":{ + "coefficients": "['@attrs:metadata/energy_calibration/calibration/coefficients', '@attrs:metadata/energy_correction/calibration/coefficients']", + "coefficients/@units": "", + "fit_function": "['@attrs:metadata/energy_calibration/calibration/fit_function', '@attrs:metadata/energy_correction/calibration/fit_function']", + "original_axis": "['@attrs:metadata/energy_calibration/tof', '@attrs:metadata/energy_correction/tof']", + "original_axis/@units": "", + "calibrated_axis": "['@attrs:metadata/energy_calibration/calibration/axis', '@attrs:metadata/energy_correction/calibration/axis']", + "calibrated_axis/@units": "eV", + "physical_quantity": "energy" + }, + "/ENTRY/PROCESS_MPES[process]/CALIBRATION[kx_calibration]": { + "scaling": "['@attrs:metadata/momentum_calibration/calibration/kx_scale', '@attrs:metadata/momentum_correction/calibration/scale_kx']", + "scaling/@units": "", + "offset": "['@attrs:metadata/momentum_calibration/calibration/x_center', '@attrs:metadata/momentum_correction/offset_kx']", + "offset/@units": "", + "calibrated_axis": "['@attrs:metadata/momentum_calibration/calibration/kx_axis', '@attrs:metadata/momentum_correction/calibration/axis_kx']", + "calibrated_axis/@units": "1/angstrom", + "physical_quantity": "momentum" + }, + "/ENTRY/PROCESS_MPES[process]/CALIBRATION[ky_calibration]": { + "scaling": "['@attrs:metadata/momentum_calibration/calibration/ky_scale', '@attrs:metadata/momentum_correction/calibration/scale_ky']", + "offset": "['@attrs:metadata/momentum_calibration/calibration/y_center', '@attrs:metadata/momentum_correction/offset_ky']", + "calibrated_axis": "['@attrs:metadata/momentum_calibration/calibration/ky_axis', '@attrs:metadata/momentum_correction/calibration/axis_ky']", + "calibrated_axis/@units": "1/angstrom", + "physical_quantity": "momentum" + }, + "/ENTRY/data": { + "@axes": "@data:dims", + "@*_indices": "@data:*.index", + "@signal": "data", + "data": "@data:data", + "data/@units": "counts", + "*": "@data:*.data", + "*/@units": "@data:*.unit", + "energy/@type": "['@attrs:metadata/energy_calibration/calibration/energy_scale', 'kinetic']" + } +} diff --git a/src/pynxtools_mpes/nomad/examples/downloads.archive.yaml b/src/pynxtools_mpes/nomad/examples/downloads.archive.yaml new file mode 100644 index 0000000..e65757e --- /dev/null +++ b/src/pynxtools_mpes/nomad/examples/downloads.archive.yaml @@ -0,0 +1,15 @@ +data: + m_def: nomad.datamodel.metainfo.downloads.Downloads + mainfiles: + - TiTe2/TiTe2_0deg.nxs + - TiTe2/TiTe2_60deg.nxs + - mpes.archive.json + downloads: + - url: https://zenodo.org/records/7573825/files/MoTe2.h5?download=1 + output: MoTe2.h5 + - url: https://zenodo.org/records/5541490/files/TiTe2_0deg.nxs?download=1 + output: TiTe2/TiTe2_0deg.nxs + - url: https://zenodo.org/records/5541490/files/TiTe2_60deg.nxs?download=1 + output: TiTe2/TiTe2_60deg.nxs + - url: https://nomad-lab.eu/prod/v1/api/v1/entries/xV9yMspIyXMfia6nNw3NIbnlZ91H/raw/Scan1496.nxs + output: Scan1496.nxs \ No newline at end of file diff --git a/src/pynxtools_mpes/nomad/examples/eln_data.yaml b/src/pynxtools_mpes/nomad/examples/eln_data.yaml new file mode 100644 index 0000000..1d5772d --- /dev/null +++ b/src/pynxtools_mpes/nomad/examples/eln_data.yaml @@ -0,0 +1,112 @@ +Instrument: + Analyzer: + energy_resolution: + unit: eV + value: 110.00000000000001 + momentum_resolution: + unit: 1 / Å + value: 0.08 + slow_axes: delay + spatial_resolution: + unit: µm + value: 10.0 + Beam: + Probe: + extent: + unit: µm + value: + - 80.0 + - 80.0 + incident_energy: + unit: eV + value: 21.7 + incident_energy_spread: + unit: eV + value: 0.11 + incident_polarization: + - 1.0 + - 1.0 + - 0.0 + - 0.0 + pulse_duration: + unit: fs + value: 20.0 + Pump: + average_power: + unit: mW + value: 300.0 + extent: + unit: µm + value: + - 229.99999999999997 + - 265.0 + fluence: + unit: mJ / cm ** 2 + value: 0.15000000000000002 + incident_energy: + unit: eV + value: 1.5499999999999998 + incident_energy_spread: + unit: eV + value: 0.08 + incident_polarization: + - 1.0 + - -1.0 + - 0.0 + - 0.0 + incident_wavelength: + unit: nm + value: 799.9999999999999 + pulse_duration: + unit: fs + value: 35.0 + pulse_energy: + unit: µJ + value: 0.6 + Manipulator: + sample_temperature: + unit: K + value: 300.0 + Source: + Probe: + frequency: + unit: kHz + value: 500.0 + photon_energy: + unit: eV + value: 21.700000000000003 + Pump: + frequency: + unit: kHz + value: 500.0 + photon_energy: + unit: eV + value: 1.5500000000000003 + energy_resolution: + unit: meV + value: 140.0 + momentum_resolution: + unit: 1 / Å + value: 0.08 + temporal_resolution: + unit: fs + value: 35.0 +Sample: + chemical_formula: WSe2 + description: Sample + gas_pressure: + unit: mbar + value: 5.000000000000001e-11 + name: WSe2 Single Crystal + preparation_date: '2019-01-13T09:00:00+00:00' + sample_history: Cleaved + temperature: + unit: K + value: 300.0 +User: + address: Faradayweg 4-6, 14915 Berlin + affiliation: Fritz Haber Institute of the Max Planck Society + email: maklar@fhi-berlin.mpg.de + name: Julian Maklar + role: Principal Investigator +title: Valence Band Dynamics - 800 nm linear s-polarized pump, 0.6 mJ/cm2 absorbed fluence diff --git a/src/pynxtools_mpes/nomad/examples/mpes.scheme.archive.yml b/src/pynxtools_mpes/nomad/examples/mpes.scheme.archive.yml new file mode 100644 index 0000000..e0ee3c4 --- /dev/null +++ b/src/pynxtools_mpes/nomad/examples/mpes.scheme.archive.yml @@ -0,0 +1,312 @@ +definitions: + name: "MPES ELN Example" + sections: + MPES: + base_sections: + - "pynxtools.nomad.dataconverter.ElnYamlConverter" + - "nomad.datamodel.data.EntryData" + m_annotations: + template: + reader: mpes + nxdl: NXmpes + eln: + hide: [] + quantities: + title: + type: str + m_annotations: + eln: + component: StringEditQuantity + sub_sections: + User: + section: + m_annotations: + eln: + overview: true + quantities: + name: + type: str + m_annotations: + eln: + component: StringEditQuantity + role: + type: str + m_annotations: + eln: + component: StringEditQuantity + affiliation: + type: str + m_annotations: + eln: + component: StringEditQuantity + address: + type: str + m_annotations: + eln: + component: StringEditQuantity + email: + type: str + m_annotations: + eln: + component: StringEditQuantity + + Instrument: + section: + quantities: + energy_resolution: + type: np.float64 + unit: joule + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "meV" + momentum_resolution: + type: np.float64 + unit: 1/meter + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "1/angstrom" + temporal_resolution: + type: np.float64 + unit: second + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "fs" + sub_sections: + Manipulator: + section: + quantities: + sample_temperature: + type: np.float64 + unit: celsius + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "kelvin" + Analyzer: + section: + quantities: + slow_axes: + type: str + m_annotations: + eln: + component: StringEditQuantity + spatial_resolution: + type: np.float64 + unit: meter + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "micrometer" + energy_resolution: + type: np.float64 + unit: joule + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "eV" + momentum_resolution: + type: np.float64 + unit: 1/meter + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "1/angstrom" + Source: + section: + sub_sections: + Probe: + section: + quantities: + frequency: + type: np.float64 + unit: hertz + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "kilohertz" + photon_energy: + type: np.float64 + unit: joule + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "eV" + Pump: + section: + quantities: + frequency: + type: np.float64 + unit: hertz + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "kilohertz" + photon_energy: + type: np.float64 + unit: joule + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "eV" + Beam: + section: + sub_sections: + Probe: + section: + quantities: + incident_energy: + type: np.float64 + unit: joule + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0 + defaultDisplayUnit: "eV" + incident_energy_spread: + type: np.float64 + unit: joule + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0 + defaultDisplayUnit: "eV" + pulse_duration: + type: np.float64 + unit: second + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0 + defaultDisplayUnit: "fs" + incident_polarization: + type: np.float64 + shape: ["4"] + description: Incident Stokes Vector + m_annotations: + eln: + component: NumberEditQuantity + extent: + type: np.float64 + shape: ["2"] + unit: meter + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "micrometer" + Pump: + section: + quantities: + incident_energy: + type: np.float64 + unit: joule + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0 + defaultDisplayUnit: "eV" + incident_energy_spread: + type: np.float64 + unit: joule + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0 + defaultDisplayUnit: "eV" + pulse_duration: + type: np.float64 + unit: second + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0 + defaultDisplayUnit: "fs" + incident_polarization: + type: np.float64 + shape: ["4"] + m_annotations: + eln: + component: NumberEditQuantity + extent: + type: np.float64 + shape: ["2"] + unit: meter + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "micrometer" + incident_wavelength: + type: np.float64 + unit: meter + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "nanometer" + average_power: + type: np.float64 + unit: watt + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "milliwatt" + pulse_energy: + type: np.float64 + unit: joule + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "microjoule" + fluence: + type: np.float64 + unit: joule / meter ** 2 + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "millijoule / centimeter**2" + Sample: + section: + m_annotations: + eln: + overview: true + quantities: + preparation_date: + type: Datetime + m_annotations: + eln: + component: DateTimeEditQuantity + sample_history: + type: str + m_annotations: + eln: + component: StringEditQuantity + chemical_formula: + type: str + m_annotations: + eln: + component: StringEditQuantity + description: + type: str + m_annotations: + eln: + component: StringEditQuantity + name: + type: str + m_annotations: + eln: + component: StringEditQuantity + temperature: + type: np.float64 + unit: celsius + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "kelvin" + gas_pressure: + type: np.float64 + unit: bar + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: "mbar" diff --git a/src/pynxtools_mpes/nomad/examples/sed_config/mpes_example_config.yaml b/src/pynxtools_mpes/nomad/examples/sed_config/mpes_example_config.yaml new file mode 100644 index 0000000..90a6624 --- /dev/null +++ b/src/pynxtools_mpes/nomad/examples/sed_config/mpes_example_config.yaml @@ -0,0 +1,314 @@ +core: + # Set verbosity of sed + verbose: True + # The loader to use. The mpes loader allows for loading hdf5 files from the METIS momentum microscope. + loader: mpes + # Option to use the copy tool to mirror data to a local storage location before processing. + use_copy_tool: False + # path to the root of the source data directory + copy_tool_source: "/path/to/data/" + # path to the root or the local data storage + copy_tool_dest: "/path/to/localDataStore/" + # optional keyworkds for the copy tool: + copy_tool_kwds: + # number of parallel copy jobs + ntasks: 20 + # group id to set for copied files and folders + gid: 1001 + +dataframe: + # hdf5 group names to read from the h5 files (for mpes reader) + hdf5_groupnames: ["Stream_0", "Stream_1", "Stream_2", "Stream_4"] + # aliases to assign to the dataframe columns for the corresponding hdf5 streams + hdf5_aliases: + Stream_0: "X" + Stream_1: "Y" + Stream_2: "t" + Stream_4: "ADC" + # dataframe column name for the time stamp column + time_stamp_alias: "timeStamps" + # hdf5 group name containing eventIDs occuring at every millisecond (used to calculate timestamps) + ms_markers_group: "msMarkers" + # hdf5 attribute containing the timestamp of the first event in a file + first_event_time_stamp_key: "FirstEventTimeStamp" + # Time stepping in seconds of the succesive events in the timed dataframe + timed_dataframe_unit_time: 0.001 + # list of columns to apply jitter to + jitter_cols: ["X", "Y", "t", "ADC"] + # dataframe column containing x coordinates + x_column: "X" + # dataframe column containing y coordinates + y_column: "Y" + # dataframe column containing time-of-flight data + tof_column: "t" + # dataframe column containing analog-to-digital data + adc_column: "ADC" + # dataframe column containing corrected x coordinates + corrected_x_column: "Xm" + # dataframe column containing corrected y coordinates + corrected_y_column: "Ym" + # dataframe column containing corrected time-of-flight data + corrected_tof_column: "tm" + # dataframe column containing kx coordinates + kx_column: "kx" + # dataframe column containing ky coordinates + ky_column: "ky" + # dataframe column containing energy data + energy_column: "energy" + # dataframe column containing delay data + delay_column: "delay" + # time length of a base time-of-flight bin in ns + tof_binwidth: 4.125e-12 + # Binning factor of the tof_column-data compared to tof_binwidth (2^(tof_binning-1)) + tof_binning: 2 + # binning factor used for the adc coordinate (2^(adc_binning-1)) + adc_binning: 3 + # Default units for dataframe entries + units: + X: 'step' + Y: 'step' + t: 'step' + tof_voltage: 'V' + extractor_voltage: 'V' + extractor_current: 'A' + cryo_temperature: 'K' + sample_temperature: 'K' + dld_time: 'ns' + delay: 'ps' + timeStamp: 's' + energy: 'eV' + E: 'eV' + kx: '1/A' + ky: '1/A' + +energy: + # Number of bins to use for energy calibration traces + bins: 1000 + # Bin ranges to use for energy calibration curves (for tof_binning=0) + ranges: [128000, 138000] + # hdf5 path to attribute storing bias information for a given file + bias_key: "@KTOF:Lens:Sample:V" + # Option to normalize energy calibration traces + normalize: True + # Pixel range for smoothing + normalize_span: 7 + # Spline order for smoothing + normalize_order: 1 + # Radius parameter for fastdtw algorithm to find path correspondence + fastdtw_radius: 2 + # Window around a peak to make sure that no other peaks are present + peak_window: 7 + # Mehtod to use for energy calibration + calibration_method: "lmfit" + # Energy scale to use for energy calibration + energy_scale: "kinetic" + # Approximate position of the high-energy-cutoff in tof_column bins, + # used for displaying a graph to choose the energy correction function parameters. + tof_fermi: 132250 + # TOF range to visualize for the correction tool around tof_fermi + tof_width: [-600, 1000] + # x-intergration range for the correction tool around the center pixel + x_width: [-20, 20] + # y-intergration range for the correction tool around the center pixel + y_width: [-20, 20] + # High intensity cutoff for the visulaization tool + color_clip: 300 + correction: + # Correction type + correction_type: "Lorentzian" + # Correction amplitude + amplitude: 2.5 + # center coordinates for the correction (in detector coordinates) + center: [730.0, 730.0] + # gamma value for the Lorentzian correction (same for x and y) + gamma: 920.0 + # sigma value for the gaussian correction (same for x and y) + sigma: 700.0 + # diameter value for the radial correction (same for x and y) + diameter: 3000.0 + # Default energy calibration + calibration: + # time-of-flight distance (in m) + d: 1.058206295066418 + # time offset (in ns) + t0: 7.684410678887588e-07 + # energy offset (in eV) + E0: -30.440035779171833 + # energy scale of calibration + energy_scale: "kinetic" + +momentum: + # binning axes to use for momentum correction/calibration. + # Axes names starting with "@" refer to keys in the "dataframe" section + axes: ["@x_column", "@y_column", "@tof_column"] + # Bin numbers used for the respective axes + bins: [512, 512, 300] + # bin ranges to use (in unbinned detector coordinates) + ranges: [[-256, 1792], [-256, 1792], [132000, 136000]] + # The x/y pixel ranges of the detector + detector_ranges: [[0, 2048], [0, 2048]] + # The center pixel of the detector in the binned x/y coordinates + center_pixel: [256, 256] + # Sigma parameter for feature selection (intensity above background) + sigma: 5 + # FWHM parameter for feature selection (width of features to extract) + fwhm: 8 + # Sigma_radius parameter for feature selection (variation of radius size) + sigma_radius: 1 + # default momentum calibration + calibration: + # x momentum scaleing factor + kx_scale: 0.010729535670610963 + # y momentum scaleing factor + ky_scale: 0.010729535670610963 + # x BZ center pixel + x_center: 256.0 + # y BZ center pixel + y_center: 256.0 + # x start value of the calibration dataset + rstart: -256. + # y start value of the calibration dataset + cstart: -256. + # x direction pixel stepping of the calibration dataset + rstep: 4.0 + # y direction pixel stepping of the calibration dataset + cstep: 4.0 + correction: + # default feature points used for calculating the distortion correction. + feature_points: [[203.2, 341.96], [299.16, 345.32], [350.25, 243.70], [304.38, 149.88], [199.52, 152.48], [154.28, 242.27], [248.29, 248.62]] + # rotational symmetry of the structure used for correction. Should be an even number + rotation_symmetry: 6 + # Option whether the center of the structure is included in the feature points. + include_center: True + # Option whether the center should be included in the correction algorithm + use_center: True + +delay: + # value ranges of the analog-to-digital converter axes used for encoding the delay stage position + # (in unbinned coordinates) + adc_range: [1900, 25600] + # hdf5 attribute containing the starting point of the delay stage + p1_key: "@trARPES:DelayStage:p1" + # hdf5 attribute containing the end point of the delay stage + p2_key: "@trARPES:DelayStage:p2" + # hdf5 attribute containing the t0 value of the delay stage + t0_key: "@trARPES:DelayStage:t0" + +binning: + # Histogram computation mode to use. + hist_mode: "numba" + # Mode for hostogram recombination to use + mode: "fast" + # Whether to display a progress bar + pbar: True + # Number of parallel binning threads to use + num_cores: 20 + # Number of multithreading threads per worker thread + threads_per_worker: 4 + # API for numpy multithreading + threadpool_API: "blas" + +histogram: + # number of bins used for histogram visualization + bins: [80, 80, 80, 80] + # default axes to use for histgram visualization. + # Axes names starting with "@" refer to keys in the "dataframe" section + axes: ["@x_column", "@y_column", "@tof_column", "@adc_column"] + # default ranges to use for histogram visualization (in unbinned detector coordinates) + ranges: [[0, 1800], [0, 1800], [128000, 138000], [0, 32000]] + +metadata: + # URL of the epics archiver request engine + archiver_url: "http://aa0.fhi-berlin.mpg.de:17668/retrieval/data/getData.json?pv=" + # EPICS channels to collect from EPICS archiver + epics_pvs: ["KTOF:Lens:Extr:I", "trARPES:Carving:TEMP_RBV", "trARPES:XGS600:PressureAC:P_RD", "KTOF:Lens:UDLD:V", "KTOF:Lens:Sample:V", "KTOF:Apertures:m1.RBV", "KTOF:Apertures:m2.RBV", "KTOF:Apertures:m3.RBV", "trARPES:Carving:TRX.RBV", "trARPES:Carving:TRY.RBV", "trARPES:Carving:TRZ.RBV", "trARPES:Carving:THT.RBV", "trARPES:Carving:PHI.RBV", "trARPES:Carving:OMG.RBV"] + # hdf5 attribute containing the field aperture "in" motor position + fa_in_channel: 'KTOF:Apertures:m1.RBV' + # hdf5 attribute containing the field aperture "hor" motor position + fa_hor_channel: 'KTOF:Apertures:m2.RBV' + # hdf5 attribute containing the contrast aperture "in" motor position + ca_in_channel: 'KTOF:Apertures:m3.RBV' + # dictionary containing contrast and field aperture motor positions and sizes + aperture_config: + "2018-01-23T19:35:15": + fa_size: + '750': [[-3.0, -1.4], [-5.4, -4.6]] + grid: [[-3.0, -1.4], [0.15, 1.75]] + '1500': [[-3.0, -1.4], [6.25, 7.75]] + '200': [[3.3, 4.4], [-5.4, -4.6]] + '500': [[3.3, 4.4], [0.15, 1.75]] + '1000': [[3.3, 4.4], [6.25, 7.75]] + '20': [[9.6, 10.1], [-5.4, -4.6]] + '50': [[9.6, 10.1], [0.15, 1.75]] + '100': [[9.6, 10.1], [6.25, 7.75]] + open: [[-15, -9.0], [-15, -8.9]] + ca_size: + '50': [8.0, 8.4] + '200': [-0.5, -0.9] + '100': [3.4, 3.8] + grid: [-5.3, -5.9] + open: [-12.0, -8] + "2020-01-23T19:35:15": + fa_size: + '750': [[-6.2, -4.8], [5.0, 6.0]] + grid: [[-6.2, -4.8], [-0.7, -0.3]] + '500': [[-6.2, -4.8], [-7.0, -6.0]] + '200': [[0.5, 0.9], [-0.7, -0.3]] + '100': [[0.5, 0.9], [-7.0, -6.0]] + '300': [[0.5, 0.9], [5.0, 6.0]] + '10': [[6.5, 6.9], [-7.0, -6.0]] + '20': [[6.5, 6.9], [-0.7, -0.3]] + '50': [[6.5, 6.9], [5.0, 6.0]] + open: [[-15, -8.5], [-15, -8.9]] + ca_size: + '50': [9.0, 11.0] + '300': [-0.1, 0.1] + '200': [0.7, 1.5] + '100': [5.1, 5.9] + grid: [-5.5, -5.2] + open: [-15, -8.5] + # dictionary containing lens mode configurations + lens_mode_config: + "6kV_kmodem4.0_20VTOF_v3.sav": + Extr: 6000.0 + UCA: 1200 + UFA: 600.0 + Z1: 2452.9 + Z2: 1489.9 + A: 420.07 + B: 2494.8 + C: 489.2 + D: 228.05 + E: 113.82 + F: 54.232 + G: 20.0 + H: 25.5 + I: 36.0 + TOF: 20.0 + MCPfront: 20.0 + "6kV_kmodem4.0_30VTOF_453ns_focus.sav": + Extr: 6000.0 + UCA: 1200 + UFA: 600.0 + Z1: 2452.9 + Z2: 1489.9 + A: 403.07 + B: 2500 + C: 422.25 + D: 208.88 + E: 199.49 + F: 68.735 + G: 30.0 + H: 30.0 + I: 44.5 + TOF: 30.0 + MCPfront: 30.0 + +nexus: + # pynxtools reader to use for saving NXmpes files + reader: "mpes" + # NeXus application definition to use for saving + definition: "NXmpes" + # List conatining additional input files to be handed to the pynxtools converter tool, + # e.g. containing a configuration file, and additional metadata. + input_files: ["config_file.json"] diff --git a/tests/test_nomad_examples.py b/tests/test_nomad_examples.py new file mode 100644 index 0000000..22604ca --- /dev/null +++ b/tests/test_nomad_examples.py @@ -0,0 +1,74 @@ +# +# Copyright The NOMAD Authors. +# +# This file is part of NOMAD. See https://nomad-lab.eu for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""Test for NOMAD examples in MPES reader plugin.""" + +import os +import pytest + +try: + import nomad +except ImportError: + pytest.skip( + "Skipping NOMAD example tests because nomad is not installed", + allow_module_level=True, + ) + +from pynxtools.testing.nomad_example import ( + get_file_parameter, + parse_nomad_examples, + example_upload_entry_point_valid, +) + +from pynxtools_mpes.nomad.entrypoints import mpes_example + + +EXAMPLE_PATH = os.path.join( + os.path.dirname(__file__), + "..", + "src", + "pynxtools_mpes", + "nomad", + "examples", +) + + +@pytest.mark.parametrize( + "mainfile", + get_file_parameter(EXAMPLE_PATH), +) +def test_parse_nomad_examples(mainfile): + """Test if NOMAD examples work.""" + archive_dict = parse_nomad_examples(mainfile) + + +@pytest.mark.parametrize( + ("entrypoint", "example_path"), + [ + pytest.param( + mpes_example, + EXAMPLE_PATH, + id="mpes_example", + ), + ], +) +def test_example_upload_entry_point_valid(entrypoint, example_path): + """Test if NOMAD ExampleUploadEntryPoint works.""" + example_upload_entry_point_valid( + entrypoint=entrypoint, + example_path=example_path, + ) diff --git a/tests/test_reader.py b/tests/test_reader.py index e54d589..d730c4b 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -1,5 +1,22 @@ +# +# Copyright The NOMAD Authors. +# +# This file is part of NOMAD. See https://nomad-lab.eu for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# """ -Basic example based test for the stm reader +Basic example based test for the MPES reader """ from pathlib import Path