diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 13f1e7e..737bbe7 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -33,6 +33,10 @@ jobs: run: | curl -LsSf https://astral.sh/uv/install.sh | sh uv pip install coverage coveralls + - 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]" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 81a6893..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.5.5 + rev: v0.8.0 hooks: # Run the linter. - id: ruff diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..e72b403 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,5 @@ +prune * +exclude * +recursive-include src/pynxtools_apm *.py +include pyproject.toml README.md dev-requirements.txt +graft src/pynxtools_apm/nomad/examples \ No newline at end of file diff --git a/dev-requirements.txt b/dev-requirements.txt index 5c5eae9..8f6aae8 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,11 +1,13 @@ # This file was autogenerated by uv via the following command: -# uv pip compile pyproject.toml --extra=dev --extra=docs -o dev-requirements.txt -anyio==4.4.0 +# uv pip compile --universal -p 3.11 --extra=dev --extra=docs --output-file=dev-requirements.txt pyproject.toml +anyio==4.6.2.post1 # via # httpx # jupyter-server anytree==2.12.1 # via pynxtools +appnope==0.1.4 ; platform_system == 'Darwin' + # via ipykernel argon2-cffi==23.1.0 # via jupyter-server argon2-cffi-bindings==21.2.0 @@ -34,7 +36,7 @@ bleach==6.1.0 # via nbconvert blosc2==2.7.1 # via tables -build==1.2.1 +build==1.2.2.post1 # via pip-tools certifi==2024.8.30 # via @@ -42,10 +44,12 @@ certifi==2024.8.30 # httpx # requests cffi==1.17.1 - # via argon2-cffi-bindings + # via + # argon2-cffi-bindings + # pyzmq cfgv==3.4.0 # via pre-commit -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via requests click==8.1.7 # via @@ -57,34 +61,40 @@ click==8.1.7 click-default-group==1.2.4 # via pynxtools colorama==0.4.6 - # via mkdocs-material + # via + # build + # click + # ipython + # mkdocs + # mkdocs-material + # pytest comm==0.2.2 # via # ipykernel # ipywidgets contourpy==1.3.0 # via matplotlib -coverage==7.6.1 +coverage==7.6.4 # via pytest-cov cycler==0.12.1 # via matplotlib -debugpy==1.8.5 +debugpy==1.8.7 # via ipykernel decorator==5.1.1 # via ipython defusedxml==0.7.1 # via nbconvert -distlib==0.3.8 +distlib==0.3.9 # via virtualenv executing==2.1.0 # via stack-data fastjsonschema==2.20.0 # via nbformat -filelock==3.15.4 +filelock==3.16.1 # via virtualenv flatdict==4.0.1 # via ifes-apt-tc-data-modeling -fonttools==4.53.1 +fonttools==4.54.1 # via matplotlib fqdn==1.5.1 # via jsonschema @@ -92,21 +102,25 @@ ghp-import==2.1.0 # via mkdocs h11==0.14.0 # via httpcore -h5grove==2.2.0 +h5grove==2.3.0 # via jupyterlab-h5web -h5py==3.11.0 +h5py==3.12.1 # via # h5grove # ifes-apt-tc-data-modeling # jupyterlab-h5web # pynxtools -httpcore==1.0.5 +hjson==3.1.0 + # via + # mkdocs-macros-plugin + # super-collections +httpcore==1.0.6 # via httpx httpx==0.27.2 # via jupyterlab -identify==2.6.0 +identify==2.6.1 # via pre-commit -idna==3.8 +idna==3.10 # via # anyio # httpx @@ -114,7 +128,7 @@ idna==3.8 # requests ifes-apt-tc-data-modeling==0.2.2 # via pynxtools-apm (pyproject.toml) -importlib-metadata==8.4.0 +importlib-metadata==8.5.0 # via pynxtools iniconfig==2.0.0 # via pytest @@ -123,7 +137,7 @@ ipykernel==6.29.5 # jupyter # jupyter-console # jupyterlab -ipython==8.27.0 +ipython==8.28.0 # via # ipykernel # ipywidgets @@ -152,13 +166,13 @@ jsonschema==4.23.0 # jupyter-events # jupyterlab-server # nbformat -jsonschema-specifications==2023.12.1 +jsonschema-specifications==2024.10.1 # via jsonschema jupyter==1.1.1 # via # pynxtools-apm (pyproject.toml) # ifes-apt-tc-data-modeling -jupyter-client==8.6.2 +jupyter-client==8.6.3 # via # ipykernel # jupyter-console @@ -196,7 +210,7 @@ jupyterlab==4.2.5 # ifes-apt-tc-data-modeling # jupyter # notebook -jupyterlab-h5web==12.2.0 +jupyterlab-h5web==12.3.0 # via # pynxtools-apm (pyproject.toml) # ifes-apt-tc-data-modeling @@ -221,7 +235,7 @@ markdown==3.7 # pymdown-extensions markdown-include==0.8.1 # via pynxtools-apm (pyproject.toml) -markupsafe==2.1.5 +markupsafe==3.0.2 # via # jinja2 # mkdocs @@ -250,9 +264,9 @@ mkdocs-click==0.8.1 # via pynxtools-apm (pyproject.toml) mkdocs-get-deps==0.2.0 # via mkdocs -mkdocs-macros-plugin==1.0.5 +mkdocs-macros-plugin==1.3.6 # via pynxtools-apm (pyproject.toml) -mkdocs-material==9.5.34 +mkdocs-material==9.5.42 # via pynxtools-apm (pyproject.toml) mkdocs-material-extensions==1.3.1 # via @@ -260,9 +274,9 @@ mkdocs-material-extensions==1.3.1 # mkdocs-material mpmath==1.3.0 # via sympy -msgpack==1.0.8 +msgpack==1.1.0 # via blosc2 -mypy==1.11.2 +mypy==1.12.1 # via pynxtools-apm (pyproject.toml) mypy-extensions==1.0.0 # via mypy @@ -277,11 +291,11 @@ nbformat==5.10.4 # jupyter-server # nbclient # nbconvert -ndindex==1.8 +ndindex==1.9.2 # via blosc2 nest-asyncio==1.6.0 # via ipykernel -networkx==3.3 +networkx==3.4.2 # via radioactivedecay nodeenv==1.9.1 # via pre-commit @@ -313,7 +327,7 @@ numpy==1.26.4 # tables # tifffile # xarray -orjson==3.10.7 +orjson==3.10.9 # via h5grove overrides==7.7.0 # via jupyter-server @@ -326,6 +340,7 @@ packaging==24.1 # jupyterlab-server # matplotlib # mkdocs + # mkdocs-macros-plugin # nbconvert # pint # pytest @@ -333,7 +348,7 @@ packaging==24.1 # xarray paginate==0.5.7 # via mkdocs-material -pandas==2.2.2 +pandas==2.2.3 # via # ifes-apt-tc-data-modeling # pynxtools @@ -344,10 +359,12 @@ pandocfilters==1.5.1 parso==0.8.4 # via jedi pathspec==0.12.1 - # via mkdocs -pexpect==4.9.0 + # via + # mkdocs + # mkdocs-macros-plugin +pexpect==4.9.0 ; sys_platform != 'emscripten' and sys_platform != 'win32' # via ipython -pillow==10.4.0 +pillow==11.0.0 # via matplotlib pint==0.17 # via pynxtools-apm (pyproject.toml) @@ -355,24 +372,24 @@ pip==24.2 # via pip-tools pip-tools==7.4.1 # via pynxtools-apm (pyproject.toml) -platformdirs==4.2.2 +platformdirs==4.3.6 # via # jupyter-core # mkdocs-get-deps # virtualenv pluggy==1.5.0 # via pytest -pre-commit==3.8.0 +pre-commit==4.0.1 # via pynxtools-apm (pyproject.toml) -prometheus-client==0.20.0 +prometheus-client==0.21.0 # via jupyter-server -prompt-toolkit==3.0.47 +prompt-toolkit==3.0.48 # via # ipython # jupyter-console -psutil==6.0.0 +psutil==6.1.0 # via ipykernel -ptyprocess==0.7.0 +ptyprocess==0.7.0 ; os_name != 'nt' or (sys_platform != 'emscripten' and sys_platform != 'win32') # via # pexpect # terminado @@ -390,17 +407,17 @@ pygments==2.18.0 # jupyter-console # mkdocs-material # nbconvert -pymdown-extensions==10.9 +pymdown-extensions==10.11.2 # via mkdocs-material -pynxtools==0.7.0 +pynxtools==0.9.0 # via pynxtools-apm (pyproject.toml) -pyparsing==3.1.4 +pyparsing==3.2.0 # via matplotlib -pyproject-hooks==1.1.0 +pyproject-hooks==1.2.0 # via # build # pip-tools -pytest==8.3.2 +pytest==8.3.3 # via # pynxtools-apm (pyproject.toml) # pytest-cov @@ -419,8 +436,15 @@ python-dateutil==2.9.0.post0 # pandas python-json-logger==2.0.7 # via jupyter-events -pytz==2024.1 +pytz==2024.2 # via pandas +pywin32==308 ; platform_python_implementation != 'PyPy' and sys_platform == 'win32' + # via jupyter-core +pywinpty==2.0.14 ; os_name == 'nt' + # via + # jupyter-server + # jupyter-server-terminals + # terminado pyyaml==6.0.2 # via # jupyter-events @@ -446,7 +470,7 @@ referencing==0.35.1 # jsonschema # jsonschema-specifications # jupyter-events -regex==2024.7.24 +regex==2024.9.11 # via mkdocs-material requests==2.32.3 # via @@ -464,7 +488,7 @@ rpds-py==0.20.0 # via # jsonschema # referencing -ruff==0.5.5 +ruff==0.8.0 # via pynxtools-apm (pyproject.toml) scipy==1.14.1 # via @@ -472,7 +496,7 @@ scipy==1.14.1 # radioactivedecay send2trash==1.8.3 # via jupyter-server -setuptools==74.1.2 +setuptools==75.2.0 # via # jupyterlab # pip-tools @@ -494,20 +518,24 @@ stack-data==0.6.3 # via ipython structlog==24.4.0 # via pynxtools-apm (pyproject.toml) -sympy==1.13.2 +super-collections==0.5.3 + # via mkdocs-macros-plugin +sympy==1.13.3 # via radioactivedecay tables==3.10.1 # via ifes-apt-tc-data-modeling -termcolor==2.4.0 +termcolor==2.5.0 # via mkdocs-macros-plugin terminado==0.18.1 # via # jupyter-server # jupyter-server-terminals -tifffile==2024.8.30 +tifffile==2024.9.20 # via h5grove tinycss2==1.3.0 # via nbconvert +tomli==2.0.2 ; python_full_version == '3.11' + # via coverage tornado==6.4.1 # via # ipykernel @@ -532,32 +560,33 @@ traitlets==5.14.3 # nbclient # nbconvert # nbformat -types-python-dateutil==2.9.0.20240906 +types-python-dateutil==2.9.0.20241003 # via arrow -types-pytz==2024.1.0.20240417 +types-pytz==2024.2.0.20241003 # via pynxtools-apm (pyproject.toml) -types-pyyaml==6.0.12.20240808 +types-pyyaml==6.0.12.20240917 # via pynxtools-apm (pyproject.toml) -types-requests==2.32.0.20240905 +types-requests==2.32.0.20241016 # via pynxtools-apm (pyproject.toml) typing-extensions==4.12.2 # via # h5grove + # ipython # mypy # tables -tzdata==2024.1 +tzdata==2024.2 # via pandas uri-template==1.3.0 # via jsonschema -urllib3==2.2.2 +urllib3==2.2.3 # via # requests # types-requests -uv==0.4.6 +uv==0.4.25 # via pynxtools-apm (pyproject.toml) -virtualenv==20.26.3 +virtualenv==20.27.0 # via pre-commit -watchdog==5.0.2 +watchdog==5.0.3 # via mkdocs wcwidth==0.2.13 # via prompt-toolkit @@ -573,9 +602,9 @@ wheel==0.44.0 # via pip-tools widgetsnbextension==4.0.13 # via ipywidgets -xarray==2024.7.0 +xarray==2024.9.0 # via pynxtools -xmltodict==0.13.0 +xmltodict==0.14.2 # via ifes-apt-tc-data-modeling -zipp==3.20.1 +zipp==3.20.2 # via importlib-metadata diff --git a/pyproject.toml b/pyproject.toml index 2b57674..7e64109 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ ] dependencies = [ "pint==0.17", - "pynxtools>=0.7.0", + "pynxtools>=0.9.0", "ifes_apt_tc_data_modeling>=0.2.2", "numpy<=1.26.4", ] @@ -35,7 +35,7 @@ dependencies = [ [project.optional-dependencies] dev = [ "mypy", - "ruff==0.5.5", + "ruff>=0.6.0", "pytest", "pytest-cov", "pytest-timeout", @@ -63,6 +63,9 @@ docs = [ [project.entry-points."pynxtools.reader"] apm = "pynxtools_apm.reader:APMReader" +[project.entry-points.'nomad.plugin'] +apm_example = "pynxtools_apm.nomad.entrypoints:apm_example" + [tool.setuptools.packages.find] where = [ "src", diff --git a/src/pynxtools_apm/concepts/mapping_functors_pint.py b/src/pynxtools_apm/concepts/mapping_functors_pint.py index 73e0119..d53853c 100644 --- a/src/pynxtools_apm/concepts/mapping_functors_pint.py +++ b/src/pynxtools_apm/concepts/mapping_functors_pint.py @@ -487,20 +487,18 @@ def add_specific_metadata_pint( # https://numpy.org/doc/stable/reference/arrays.dtypes.html for prefix_src in prfx_src: - for functor_key in cfg: + for functor_key, functor in cfg.items(): if functor_key in ["prefix_trg", "prefix_src"]: continue if functor_key == "use": use_functor(cfg["use"], mdata, prefix_trg, ids, template) if functor_key == "map": - map_functor( - cfg[functor_key], mdata, prefix_src, prefix_trg, ids, template - ) + map_functor(functor, mdata, prefix_src, prefix_trg, ids, template) if functor_key.startswith("map_to_"): dtype_key = functor_key.replace("map_to_", "") if dtype_key in MAP_TO_DTYPES: map_functor( - cfg[functor_key], + functor, mdata, prefix_src, prefix_trg, diff --git a/src/pynxtools_apm/nomad/entrypoints.py b/src/pynxtools_apm/nomad/entrypoints.py new file mode 100644 index 0000000..ed23d41 --- /dev/null +++ b/src/pynxtools_apm/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 XPS 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 + +apm_example = ExampleUploadEntryPoint( + title="Atom Probe Microscopy", + category="FAIRmat examples", + description=""" + This example presents the capabilities of the NOMAD platform to store and standardize atom probe data. + It shows the generation of a NeXus file according to the + [NXapm](https://fairmat-nfdi.github.io/nexus_definitions/classes/contributed_definitions/NXapm.html#nxapm) + application definition and a successive analysis of an example data set. + The example contains a small atom probe dataset from an experiment with a LEAP instrument to get you started + and keep the size of your NOMAD installation small. Once started, we recommend changing the respective + input file in the NOMAD Oasis ELN to run the example with your own datasets. + """, + plugin_package="pynxtools_apm", + resources=["nomad/examples/*"], +) diff --git a/src/pynxtools_apm/nomad/examples/README.md b/src/pynxtools_apm/nomad/examples/README.md new file mode 100644 index 0000000..5cb2573 --- /dev/null +++ b/src/pynxtools_apm/nomad/examples/README.md @@ -0,0 +1,55 @@ +# Tutorial for parsing data from atom probe microscopy + +## Scope +This example combines a set of configured components of NOMAD to collect +metadata specific for atom probe and combine these with reconstructed +and ranged atom probe datasets available in different domain-specific +file formats. The example shows how functionalities of a NOMAD electronic +lab notebook (ELN) template instance that is customized for atom probe, +the `pynxtools-apm` plugin of the `pynxtools` NeXus parser, the `NXapm` +NeXus data schema, and several NOMAD core functionalities work together +to create atom-probe-domain-specific NOMAD entries with respective +default plots for `h5web`. + +## Getting started +1. Click the respective button to execute the example. +2. Wait for NOMAD to instantiate the example for you. +3. Check that you are in the `Uploads` section (menu bar). +4. Select the generated upload. +5. Explore its content by clicking on the arrow keys (to the right side of a file). +6. Modify the input file in the NOMAD ELN to match your dataset. +7. Click `save` to trigger a reprocessing of your upload. +8. Explore its content via e.g. the preview or via the `Entries` + (select from the menu bar). + +## Which input is supported by this example? +Please consult the [reference section of the pynxtools-apm plugin](https://fairmat-nfdi.github.io/pynxtools-apm/) to learn which file formats the plugin currently supports. + +## Which data artefacts should I expect to find? +This example upload contains the following entries: +- A schema in NOMAD's *archive.yaml* format: *nxapm.schema.archive.yaml* +- A schema instance file as used and generated by NOMAD *apm.archive.json*. + For educational purposes this file is filled with values but it is the + user's responsibility to check that these metadata match with their dataset. +- Another schema instance file *eln_data.yaml*. This file contains a plain + view of all entered/modified quantities including their units. This file is + updated with clicking the `save` button. This file should not be edited + manually. Instead, edit the schema instance using the NOMAD GUI. +- Another schema instance file *apm.oasis.specific.yaml*. This file contains + metadata that for specific local NOMAD OASIS installations should become + overwritten to reduce the complexity of ELN templates. + +## Which data schema is used? +The ELN template is configured such that its terminology matches with the [NXapm](https://fairmat-nfdi.github.io/nexus_definitions/classes/contributed_definitions/NXapm.html#nxapm) NeXus application definition. + +## Acknowledgements +[This example comes with real world datasets contributed by the international atom probe community.](https://zenodo.org/records/7986279) + +## Where to raise questions, comments, and suggestions? +You are very welcome to approach us with all sorts of questions and feature requests. +For atom-probe-domain-specific requests [Markus Kühbach](https://www.fairmat-nfdi.eu/fairmat/about-fairmat/team-fairmat) +is the respective contact person within FAIRmat. Feel free and [create a GitHub issue](https://github.com/FAIRmat-NFDI/pynxtools-apm) to point us to specific questions, experiences, or problems. + +## Where to go from now? +Please consult the documentation of the domain-specific examples for atom probe in the NOMAD documentation +to learn how you can process your data further using NOMAD and the Nomad Remote Tools Hub (NORTH). diff --git a/src/pynxtools_apm/nomad/examples/apm.archive.json b/src/pynxtools_apm/nomad/examples/apm.archive.json new file mode 100644 index 0000000..95c9c44 --- /dev/null +++ b/src/pynxtools_apm/nomad/examples/apm.archive.json @@ -0,0 +1 @@ +{"data":{"m_def":"../upload/raw/nxapm.schema.archive.yaml#/definitions/section_definitions/0","reader":"apm","nxdl":"NXapm","input_files":["eln_data.yaml","Si.apt","Si.RRNG","apm.oasis.specific.yaml"],"output":"apm.nxs","entry":{"experiment_description":"
Normal
\nBold
\nItalics
","start_time":"2023-06-12T13:57:00+00:00","end_time":"2023-06-12T13:57:00+00:00","run_number":"2121","operation_mode":"apt"},"workflow":{"raw_dat_file":"","hit_dat_file":"","recon_cfg_file":""},"sample":{"alias":"Si","method":"experiment","composition":["Si","Cr 2 +- 0.5","C 50 ppm +- 12"],"grain_diameter":200,"grain_diameter_error":50,"heat_treatment_temperature":600,"heat_treatment_temperature_error":20,"heat_treatment_quenching_rate":150,"heat_treatment_quenching_rate_error":10},"specimen":{"alias":"Si","method":"experiment","preparation_date":"2023-06-12T13:59:00+00:00","is_polycrystalline":false,"is_amorphous":false,"description":"this is a NOMAD example","initial_radius":20,"shank_angle":5},"user":[{"name":"MarkusK"},{"name":"Jesse Smith"}],"instrument":{"status":"success","instrument_name":"LEAP","location":"Denton","nominal_flight_path":1.2,"fabrication_vendor":"Cameca","fabrication_model":"LEAP3000","fabrication_identifier":"12","reflectron_status":"used","base_temperature":20,"chamber_pressure":2e-10,"target_detection_rate":0.8,"pulser":{"pulse_mode":"laser","pulse_frequency":250,"pulse_fraction":0.8,"laser_source":[{"name":"laser1","wavelength":254,"power":12,"pulse_energy":20},{"name":"laser2","wavelength":355,"power":24,"pulse_energy":40}]},"local_electrode_name":"L1"},"reconstruction":{"program":"IVAS","program_version":"3.6.8","protocol_name":"bas","parameter":"kf = 1.8, icf = 3.3","crystallographic_calibration":"n/a","field_of_view":20},"ranging":{"program":"IVAS","program_version":"3.6.8"}}} \ No newline at end of file diff --git a/src/pynxtools_apm/nomad/examples/apm.oasis.specific.yaml b/src/pynxtools_apm/nomad/examples/apm.oasis.specific.yaml new file mode 100644 index 0000000..ef2b686 --- /dev/null +++ b/src/pynxtools_apm/nomad/examples/apm.oasis.specific.yaml @@ -0,0 +1,17 @@ +citation: +- doi: https://doi.org/repo + description: DOI to the respective entry in a data repository. +- doi: https://doi.org/paper + description: DOI to the respective publication. +coordinate_system_set: +- alias: Following the idea of McStas that the z-axis points along the direction of an ion leaving the apex along the longest direction of the specimen. + type: cartesian + handedness: right_handed + x_direction: Direction 1 that is perpendicular to the z_direction for a right_handed cartesian + x_alias: x-axis + y_direction: Direction 2 that is perpendicular to the xaxis_direction and the z_direction for a right_handed cartesian + y_alias: y-axis + z_direction: Direction of an ion travelling hypothetically exactly along the assumed axis that is parallel to the longest direction of the specimen. + z_alias: z-axis + origin: E.g. a characteristic point e.g. initial apex or center of the base of the specimen or something else. + diff --git a/src/pynxtools_apm/nomad/examples/downloads.archive.yaml b/src/pynxtools_apm/nomad/examples/downloads.archive.yaml new file mode 100644 index 0000000..6cb291c --- /dev/null +++ b/src/pynxtools_apm/nomad/examples/downloads.archive.yaml @@ -0,0 +1,8 @@ +data: + m_def: nomad.datamodel.metainfo.downloads.Downloads + mainfiles: + - apm.archive.json + downloads: + - url: https://zenodo.org/records/7986279/files/usa_denton_smith_apav_si.zip?download=1 + output: usa_denton_smith_apav_si.zip + extract: true diff --git a/src/pynxtools_apm/nomad/examples/eln_data.yaml b/src/pynxtools_apm/nomad/examples/eln_data.yaml new file mode 100644 index 0000000..3ae7457 --- /dev/null +++ b/src/pynxtools_apm/nomad/examples/eln_data.yaml @@ -0,0 +1,132 @@ +instrument: + fabrication_identifier: '12' + fabrication_model: LEAP3000 + fabrication_vendor: Cameca + instrument_name: LEAP + local_electrode_name: L1 + location: Denton + pulser: + laser_source: + - name: laser1 + power: + unit: nW + value: 24. + pulse_energy: + unit: pJ + value: 24. + wavelength: + unit: nm + value: 355. + - name: laser2 + power: + unit: nW + value: 12. + pulse_energy: + unit: pJ + value: 12. + wavelength: + unit: nm + value: 254. + pulse_fraction: 0.8 + pulse_frequency: + unit: kHz + value: 250. + pulse_mode: laser_and_voltage + reflectron_status: used + evaporation_control: detection_rate + target_detection_rate: 0.01 + chamber_pressure: + unit: torr + value: 2.0e-10 + base_temperature: + unit: K + value: 20.0 + nominal_flight_path: + unit: m + value: 1.2 + status: success +entry: + experiment_description: 'Normal
+ +Bold
+ +Italics
' + start_time: '2023-06-11T11:20:00+00:00' + end_time: '2023-06-11T11:20:00+00:00' + run_number: '2121' + operation_mode: apt +workflow: + raw_dat_file: str.str + hit_dat_file: hits.hits + recon_cfg_file: root.root + # recon_res_file: recon.apt + # range_dat_file: recon.rrng +ranging: + program: IVAS + program_version: '3.6.8' +reconstruction: + program: IVAS + program_version: '3.6.8' + crystallographic_calibration: n/a + parameter: kf = 1.8, icf = 3.3 + protocol_name: bas + field_of_view: + unit: nm + value: 20. +sample: + composition: + - Mo + - Al 12 +- 3 + - B 50 ppm +- 12 + - C 3.6 + method: experiment + alias: Si + identifier: + service: undefined + identifier: 'Si in sample' + is_persistent: false + grain_diameter: + unit: µm + value: 200. + grain_diameter_error: + unit: µm + value: 50. + heat_treatment_temperature: + unit: K + value: 600. + heat_treatment_temperature_error: + unit: K + value: 20. + heat_treatment_quenching_rate: + unit: K / s + value: 150. + heat_treatment_quenching_rate_error: + unit: K / s + value: 10. + description: 'nothing
' +specimen: + method: experiment + alias: usa_denton_smith_si + identifier: + service: undefined + identifier: 'Si in specimen' + is_persistent: false + description: 'normal
+ +bold
+ +italics
' + is_polycrystalline: true + is_amorphous: false + preparation_date: '2023-06-11T12:51:00+00:00' + initial_radius: + unit: nm + value: 12. + shank_angle: + unit: ° + value: 5. +user: +- name: MarkusK + orcid: '0000' +- name: AlexR +- {} diff --git a/src/pynxtools_apm/nomad/examples/nxapm.schema.archive.yaml b/src/pynxtools_apm/nomad/examples/nxapm.schema.archive.yaml new file mode 100644 index 0000000..2fb8290 --- /dev/null +++ b/src/pynxtools_apm/nomad/examples/nxapm.schema.archive.yaml @@ -0,0 +1,695 @@ +# https://fairmat-nfdi.github.io/nexus_definitions/classes/contributed_definitions/NXapm.html#nxapm +definitions: + name: 'apm' + sections: + AtomProbe: + base_sections: + - 'pynxtools.nomad.dataconverter.NexusDataConverter' + - 'nomad.datamodel.data.EntryData' + m_annotations: + template: + reader: apm + nxdl: NXapm.nxdl + # Listing quantities in the hide component will not show them in the ELN. + # This would be useful to make the default values set in `template` fixed. + # Leave the hide key even if you want to pass an empty list like in this example. + eln: + hide: [] + sub_sections: + entry: + section: + description: | + Generic details about the experiment. + m_annotations: + eln: + overview: true + quantities: + experiment_description: + type: str + description: | + Free-text description about the experiment. + m_annotations: + eln: + component: RichTextEditQuantity + start_time: + type: Datetime + description: | + ISO 8601 time code with local time zone offset to UTC information + included when the atom probe session started. + m_annotations: + eln: + component: DateTimeEditQuantity + end_time: + type: Datetime + description: | + ISO 8601 time code with local time zone offset to UTC information + included when the atom probe session ended. + m_annotations: + eln: + component: DateTimeEditQuantity + run_number: + type: str + description: | + The identifier whereby the experiment is referred + to in the control software. + m_annotations: + eln: + component: StringEditQuantity + operation_mode: + type: + type_kind: Enum + type_data: + - apt + - fim + - apt_fim + - other + description: | + What type of atom probe microscope experiment is performed. + APT experiments use no imaging gas while FIM does. + m_annotations: + eln: + component: RadioEnumEditQuantity + + workflow: + section: + description: | + Details about data artifacts that have been collected during the measurement to document the sequence of post-processing + steps applied to measured atom probe experiments. + m_annotations: + eln: + overview: true + quantities: + raw_dat_file: + type: str + description: | + Place to drag-and-drop the RRAW/STR file (for LEAP systems) + or an equivalent file with raw detector timing and hit data. + m_annotations: + eln: + component: FileEditQuantity + hit_dat_file: + type: str + description: | + Place to drag-and-drop the RHIT/HITS file (for LEAP systems) + or an equivalent file with the parameterization of the hit finding + algorithm and other calibration and correction algorithms + applied on th raw data. + m_annotations: + eln: + component: FileEditQuantity + recon_cfg_file: + type: str + description: | + Place to drag-and-drop the ROOT file (for LEAP systems) + or an equivalent file with the parameterization of the ROI + selection and the reconstruction algorithm. + m_annotations: + eln: + component: FileEditQuantity + + sample: + section: + description: | + Description of the sample from which the specimen was prepared or + site-specifically cut out using e.g. a focused-ion beam instrument. + m_annotations: + eln: + quantities: + method: + type: + type_kind: Enum + type_data: + - experiment + - simulation + description: | + A qualifier whether the sample is a real one + or a virtual one (in a computer simulation). + m_annotations: + eln: + component: RadioEnumEditQuantity + alias: + type: str + description: | + Given name/alias for the sample. + m_annotations: + eln: + component: StringEditQuantity + composition: + type: str + shape: ['*'] + description: | + Chemical composition of the sample. The composition from e.g. + a composition table can be added as individual strings. + One string for each element with statements separated via a + single space. The string is expected to have the following format: + Symbol value unit +- stdev + + An example: B 1. +- 0.2, means + composition of boron 1. at.-% +- 0.2 at.%. + If a string contains only a symbol this is interpreted + that the symbol specifies the matrix or remainder element + for the composition table. + + If unit is omitted or named % this is interpreted as at.-%. + Unit can be at% or wt% but all strings have to use either atom + or weight percent but no mixtures. + No unit for stdev should be repeated as it has to be the + same unit as is used for the composition value. + m_annotations: + eln: + component: StringEditQuantity + grain_diameter: + type: np.float64 + unit: micrometer + description: | + Qualitative information about the grain size, here specifically + described as the equivalent spherical diameter of an assumed + average grain size that is representative for the crystal ensemble. + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0.0 + defaultDisplayUnit: micrometer + grain_diameter_error: + type: np.float64 + unit: micrometer + description: | + Magnitude of the standard deviation of the grain_diameter. + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0.0 + defaultDisplayUnit: micrometer + heat_treatment_temperature: + type: np.float64 + unit: kelvin + description: | + The temperature of the last heat treatment step before quenching. + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0.0 + defaultDisplayUnit: kelvin + heat_treatment_temperature_error: + type: np.float64 + unit: kelvin + description: | + Magnitude of the standard deviation of the heat_treatment_temperature. + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0.0 + defaultDisplayUnit: kelvin + heat_treatment_quenching_rate: + type: np.float64 + unit: kelvin/second + description: | + Rate of the last quenching step. + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0.0 + defaultDisplayUnit: kelvin/second + heat_treatment_quenching_rate_error: + type: np.float64 + unit: K/s + description: | + Magnitude of the standard deviation of the heat_treatment_quenching_rate. + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0.0 + defaultDisplayUnit: K/s + description: + type: str + description: | + Discouraged free text field which only exists to learn which + additional metadata users would like to enter to support + making improvements to this atom probe example + in the future. + m_annotations: + eln: + component: RichTextEditQuantity + + specimen: + section: + description: | + Details about the specimen and its immediate environment. + m_annotations: + eln: + quantities: + method: + type: + type_kind: Enum + type_data: + - experiment + - simulation + description: | + A qualifier whether the specimen is a real one or a virtual one. + m_annotations: + eln: + component: RadioEnumEditQuantity + alias: + type: str + description: | + Given name as an alias of the specimen. + m_annotations: + eln: + component: StringEditQuantity + preparation_date: + type: Datetime + description: | + ISO 8601 time code with local time zone offset to UTC when the + measured specimen surface was prepared last time. + m_annotations: + eln: + component: DateTimeEditQuantity + is_polycrystalline: + type: bool + description: | + Is the specimen polycrystalline or not? + m_annotations: + eln: + component: BoolEditQuantity + is_amorphous: + type: bool + description: | + Is the specimen amorphous or not? + m_annotations: + eln: + component: BoolEditQuantity + description: + type: str + description: | + Discouraged free text field which only exists to learn which + additional metadata users would like to enter to support + making improvements to this atom probe example + in the future. + m_annotations: + eln: + component: RichTextEditQuantity + initial_radius: + type: np.float64 + unit: nanometer + description: | + Ideally measured or best elaborated guess of + the initial radius of the specimen. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: nanometer + minValue: 1.0 + maxValue: 1000.0 + shank_angle: + type: np.float64 + unit: degree + descriptions: | + Ideally measured or best elaborated guess of the shank angle. + This is a measure of the specimen taper. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: degree + minValue: 0.0 + maxValue: 90.0 + + user: + repeats: true + section: + description: | + Contact information and eventually details of at least one + person who was involved in taking the microscope session. + m_annotations: + eln: + quantities: + name: + type: str + description: | + Given (first) name and surname. + m_annotations: + eln: + component: StringEditQuantity + email: + type: str + description: | + Email address of the user at the point in time + when the experiment was performed. + m_annotations: + eln: + component: StringEditQuantity + affiliation: + type: str + description: | + Name of the affiliation of the user at the point in time + when the experiment was performed. + m_annotations: + eln: + component: StringEditQuantity + address: + type: str + description: | + Postal address of the affiliation. + m_annotations: + eln: + component: StringEditQuantity + orcid: + type: str + description: | + ORCID of that person. + m_annotations: + eln: + component: StringEditQuantity + telephone_number: + type: str + description: | + (Business) (tele)phone number of the user at the point in time + when the experiment was performed. + m_annotations: + eln: + component: StringEditQuantity + role: + type: str + description: | + Which role does the user have in the place and at the point in time + when the experiment was performed? + Technician operating the microscope. Student, postdoc, + principle investigator, or guest are common examples. + m_annotations: + eln: + component: StringEditQuantity + + instrument: + section: + description: | + The instrument and the lab in which it stands. + m_annotations: + eln: + quantities: + status: + type: + type_kind: Enum + type_data: + - success + - failure + - unknown + description: | + A statement whether the measurement was + successful or failed prematurely. + m_annotations: + eln: + component: RadioEnumEditQuantity + instrument_name: + type: str + description: | + Given name of the atom probe at the hosting institution. + m_annotations: + eln: + component: StringEditQuantity + location: + type: str + description: | + Location of the lab or place where the instrument is installed. + Using GEOREF is preferred. + m_annotations: + eln: + component: StringEditQuantity + nominal_flight_path: + type: np.float64 + unit: meter + description: | + The space inside the atom probe which ions pass nominally + when they leave the specimen and travel to the detector. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: meter + minValue: 0.0 + maxValue: 10.0 + fabrication_vendor: + type: str + description: | + Company name of the manufacturer. + m_annotations: + eln: + component: StringEditQuantity + fabrication_model: + type: str + description: | + Version or model of the component named by the manufacturer. + m_annotations: + eln: + component: StringEditQuantity + fabrication_identifier: + type: str + description: | + Ideally, (globally) unique persistent identifier, i.e. a serial + number or hash identifier of the component. + m_annotations: + eln: + component: StringEditQuantity + reflectron_status: + type: + type_kind: Enum + type_data: + - used + - present + - none + description: | + Is a reflectron installed and was it used? + m_annotations: + eln: + component: RadioEnumEditQuantity + local_electrode_name: + type: str + description: | + Identifier of the local_electrode in the control software database. + m_annotations: + eln: + component: StringEditQuantity + base_temperature: + type: np.float64 + unit: kelvin + description: | + Average temperature (at the specimen base) during the measurement. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: kelvin + minValue: 0.0 + maxValue: 300.0 + chamber_pressure: + type: np.float64 + unit: torr + description: | + Average pressure in the analysis chamber during the measurement. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: torr + minValue: 0.0 + maxValue: 1500.12 + target_detection_rate: + type: np.float64 + descriptions: | + Target detection rate in ions/per pulse. + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0.0 + maxValue: 1000.0 + evaporation_control: + type: + type_kind: Enum + type_data: + - detection_rate + description: | + Control software setting (for LEAP systems) how + field-evaporation is controlled. + m_annotations: + eln: + component: RadioEnumEditQuantity + sub_sections: + pulser: + section: + description: | + Details about the pulsing device and method + m_annotations: + eln: + quantities: + pulse_mode: + type: + type_kind: Enum + type_data: + - laser + - voltage + - laser_and_voltage + description: | + Which pulsing mode was used? + m_annotations: + eln: + component: RadioEnumEditQuantity + pulse_frequency: + type: np.float64 + unit: kilohertz + description: | + Frequency with which the pulser fire(s). + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: kilohertz + minValue: 0.0 + maxValue: 10000.0 + pulse_fraction: + type: np.float64 + description: | + Fraction of the pulse_voltage that is applied in addition to the standing_voltage at peak voltage of a pulse. If a standing voltage + is applied, this gives nominal pulse fraction (as a function + of standing voltage). Otherwise, this field should not be present. + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0.0 + maxValue: 1.0 + # LEAP 6000 instrument has up to two lasers + sub_sections: + laser_source: + repeats: True + section: + description: | + Details about each laser pulsing unit. LEAP6000-type + instruments can use up to two lasers. + m_annotations: + eln: + quantities: + name: + type: str + description: | + Given name/alias. + m_annotations: + eln: + component: StringEditQuantity + wavelength: + type: np.float64 + unit: nanometer + description: | + Nominal wavelength of the laser radiation. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: nanometer + minValue: 0.0 + power: + type: np.float64 + unit: nanowatt + description: | + Nominal power of the laser source while + illuminating the specimen. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: nanowatt + minValue: 0.0 + pulse_energy: + type: np.float64 + unit: picojoule + description: | + Average energy of the laser at peak of each pulse. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: picojoule + minValue: 0.0 + + reconstruction: + section: + description: | + Details about the reconstruction + m_annotations: + eln: + quantities: + program: + type: str + description: | + Name of the program used to generate the reconstruction. + m_annotations: + eln: + component: StringEditQuantity + program_version: + type: str + description: | + Version plus build number, commit hash, or description + of the program to support reproducibility. + m_annotations: + eln: + component: StringEditQuantity + protocol_name: + type: + type_kind: Enum + type_data: + - bas + - geiser + - gault + - cameca + - other + description: | + Qualitative statement about which reconstruction protocol was used. + m_annotations: + eln: + component: RadioEnumEditQuantity + parameter: + type: str + description: | + Different reconstruction protocols exist. Although these approaches + are qualitatively similar, each protocol uses different parameters + (and interprets these differently). The source code to IVAS/APSuite + is not open. For now users should store reconstruction parameter + they deem relevant in this free text field. + m_annotations: + eln: + component: StringEditQuantity + crystallographic_calibration: + type: str + description: | + Different strategies for crystallographic calibration of the reconstruction + are possible. Therefore, we collect first such feedback before + parameterizing this further. If no crystallographic calibration was + performed, the field should be filled with the n/a, + meaning not applied. + m_annotations: + eln: + component: StringEditQuantity + field_of_view: + type: np.float64 + unit: nanometer + description: | + The nominal diameter of the specimen ROI which is measured in the + experiment. The physical specimen cannot be measured completely + because ions may launch but hit in locations other than the detector. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: nanometer + minValue: 0.0 + + ranging: + section: + description: | + Details about the ranging definitions. + m_annotations: + eln: + quantities: + program: + type: str + description: | + Name of the program whereby ranging definitions were defined. + m_annotations: + eln: + component: StringEditQuantity + program_version: + type: str + description: | + Version plus build number, commit hash, or description of the + program to support reproducibility. + m_annotations: + eln: + component: StringEditQuantity diff --git a/tests/test_nomad_examples.py b/tests/test_nomad_examples.py new file mode 100644 index 0000000..9ca42c4 --- /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 APM 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_apm.nomad.entrypoints import apm_example + + +EXAMPLE_PATH = os.path.join( + os.path.dirname(__file__), + "..", + "src", + "pynxtools_apm", + "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( + apm_example, + EXAMPLE_PATH, + id="apm_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, + )