diff --git a/.gitmodules b/.gitmodules index ecb757aca34d30922c52de87b60f9a1bdc9252ea..326af5de69b54df4156b64b2bab28dc440357b70 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,9 +8,6 @@ path = dependencies/parsers/eelsdb url = https://github.com/nomad-coe/nomad-parser-eelsdb.git branch = master -[submodule "dependencies/parsers/example"] - path = dependencies/parsers/example - url = https://github.com/nomad-coe/nomad-parser-example.git [submodule "dependencies/nomad-remote-tools-hub"] path = dependencies/nomad-remote-tools-hub url = https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-remote-tools-hub.git diff --git a/Dockerfile b/Dockerfile index 9d710cd88e894d2131b2e8a5f2855f697f1e1ba2..ff4502bcfcdfc0379fad40692ca5d6616d065eb5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -67,8 +67,10 @@ RUN rm -rf /pymolfile # Copy files and install nomad@FAIRDI WORKDIR /install COPY . /install -RUN python setup.py compile +# RUN python setup.py compile +RUN pip install -r requirements-all.txt RUN pip install .[all] +RUN ./dependencies.sh RUN ./generate_gui_artifacts.sh RUN ./generate_docs_artifacts.sh RUN mkdocs build && mv site docs/build @@ -95,6 +97,7 @@ COPY --from=build /install/gui/src/toolkitMetadata.json /app/src/toolkitMetadata COPY --from=build /install/gui/src/exampleUploads.json /app/src/exampleUploads.json COPY --from=build /install/gui/src/unitsData.js /app/src/unitsData.js COPY --from=build /install/gui/src/northTools.json /app/src/northTools.json +ENV NODE_OPTIONS "--max_old_space_size=3072" RUN yarn run build # Third, create a slim final image diff --git a/MANIFEST.in b/MANIFEST.in index fe15e110b79a7b3e7e4c51e35750ebe64b8f8dde..54a26a510fe9f713fd8be2d3248aaa333f2fb4fe 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,15 +1 @@ -recursive-include dependencies/optimade-python-tools *.txt *.g *.py *.ini *.json -recursive-include nomad *.json *.j2 *.md *.yaml -include nomad/units/*.txt -include README.md -include LICENSE.txt -include requirements.txt -include auto_complete_install.sh -include setup.json -recursive-include nomad/app/static/gui/ *.css *.ico *.html *.json *.js *.map *.txt *.svg *.png -recursive-include docs/build/ *.css *.ico *.html *.json *.js *.map *.txt *.svg *.png recursive-include dependencies/parsers/ metadata.yaml -recursive-include dependencies/parsers/nexus/nexusparser/definitions/base_classes/ *.xml -recursive-include dependencies/parsers/nexus/nexusparser/definitions/applications/ *.xml -recursive-include dependencies/parsers/nexus/nexusparser/definitions/contributed_definitions/ *.xml -include dependencies/parsers/nexus/nexusparser/definitions/ *.xsd diff --git a/dependencies.sh b/dependencies.sh index f1a49b26cded6b99340c918690ae5f5b320e207a..7bbaf5e13ad1f545a4887bb7277dfb447e1bb85d 100755 --- a/dependencies.sh +++ b/dependencies.sh @@ -5,8 +5,7 @@ set -e git config -f .gitmodules --get-regexp '^submodule\..*\.path$' | while read path_key path do - (echo "$path" | grep -vEq '^dependencies/(nexus_definitions|optimade-python-tools|matid)$') \ - && [ -f $path/requirements.txt ] && pip install -r $path/requirements.txt - [ -f $path/setup.py ] && pip install --ignore-requires-python $1 $path + (echo "$path" | grep -vEq '^dependencies/(nexus_definitions|optimade-python-tools|matid)$') + [ -f $path/setup.py ] && pip install --no-deps $1 $path echo $path done diff --git a/dependencies/nomad-remote-tools-hub b/dependencies/nomad-remote-tools-hub index df6aa87b2dc54dc1626e653a0b18f255c6f17561..59e7d1d39c5da0c77501e8046b9f840cef5bb1d4 160000 --- a/dependencies/nomad-remote-tools-hub +++ b/dependencies/nomad-remote-tools-hub @@ -1 +1 @@ -Subproject commit df6aa87b2dc54dc1626e653a0b18f255c6f17561 +Subproject commit 59e7d1d39c5da0c77501e8046b9f840cef5bb1d4 diff --git a/dependencies/parsers/electronic b/dependencies/parsers/electronic index 5c077f48adcda8e6ec2f5cfb2220adc04100c3e6..9c1c9a5adaa4a50fee11292751ba29bb2ef3db2f 160000 --- a/dependencies/parsers/electronic +++ b/dependencies/parsers/electronic @@ -1 +1 @@ -Subproject commit 5c077f48adcda8e6ec2f5cfb2220adc04100c3e6 +Subproject commit 9c1c9a5adaa4a50fee11292751ba29bb2ef3db2f diff --git a/dependencies/parsers/example b/dependencies/parsers/example deleted file mode 160000 index 8d8b58027e9ab4e61077b94d19e74bc0645419a3..0000000000000000000000000000000000000000 --- a/dependencies/parsers/example +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8d8b58027e9ab4e61077b94d19e74bc0645419a3 diff --git a/dependencies/parsers/nexus b/dependencies/parsers/nexus index 4a01429f1b8a3e23e93af85de03f63bd024c0bb0..08ecd31872a09ef01738a9c9eae7875c05b4eb2a 160000 --- a/dependencies/parsers/nexus +++ b/dependencies/parsers/nexus @@ -1 +1 @@ -Subproject commit 4a01429f1b8a3e23e93af85de03f63bd024c0bb0 +Subproject commit 08ecd31872a09ef01738a9c9eae7875c05b4eb2a diff --git a/examples/data/apm/README.md b/examples/data/apm/README.md new file mode 100644 index 0000000000000000000000000000000000000000..fb7c80f4591a562d3736f03733b17d5c46e0abb4 --- /dev/null +++ b/examples/data/apm/README.md @@ -0,0 +1,110 @@ +# An Electronic Lab Notebook (ELN) Example for Atom Probe Microscopy (APM) + +## Introduction + +This example shows how the NOMAD ELN functionalities can be used to collect +required metadata for groups, fields, and attributes for creating a dataset +which is compliant with the NeXus NXapm application definition. + +The NeXus NXapm data model is documented here [NXapm](https://fairmat-experimental.github.io/nexus-fairmat-proposal) + +Specifically, this example works together with vendor/community file formats +(POS, ePOS, APT, and RNG or RRNG, respectively) which contain the tomographic +reconstruction and associated ranging data to interpret ion species from +mass-to-charge-state ratio values. This ELN can be used to collect metadata which +are currently not stored in vendor or community files including details about the +specimen, the instrument, users, and post-processing steps. + +This example upload contains the following entries: +- A schema in NOMAD's *archive.yaml* format: *nxapm.schema.archive.yaml* +- A schema instance file used by NOMAD *nxapm.archive.json* which is filled for educational purpose with values for the example. +- The primary consumer of this json file is NOMAD and its internal data management system. +- Another schema instance file used by the nomad-parser-nexus *eln_data.yaml*. This file contains all entered +quantities from the ELN GUI (after the save button was stored). +The example is also filled for educational purpose with values matching those in nxapm.archive.json. +This file stores metadata with value and units. The file is updated each time the save button in the ELN GUI is clicked. +The file is used by the [NOMAD-PARSER-NEXUS](https://github.com/nomad-coe/nomad-parser-nexus) which creates +NeXus files, like those for atom probe matching the NXapm application definition. + +This example is configured to take an example dataset and call the nomad-parser-nexus dataconverter +which creates a NeXus file that is compliant with NXapm. Once completed, this file is available +in the upload section/staging area. This makes also these files explorable with H5Web visualization +through the files menu. + +This example comes with a measured dataset which is meant for testing and exploration of the ELN and +NORTH container functionalities. The scientific dataset is a tomographic reconstruction of an +ODS steel specimen which was measured and characterized by [J. Wang and coworkers](https://doi.org/10.1017/S1431927618015386) +- R31_06365-v02.pos (the reconstruction) +- R31_06365-v02.rrng (the range file) + +## Creating NeXus files + +When you modify the content inside the ELN and click the save button, the data from the ELN will +be parsed and combined with the POS and RRNG file to create the NXS file. You can replace +these files with your own and accordingly use the ELN to enter metadata for your own datasets. +Upon saving, a NeXus/HDF5 file will be created for your specific dataset. + +The drag-and-drop functionality of the upload section can be used to pass your reconstruction +and range file onto the respective file upload fields of the ELN. After clicking the save button, +the newly entered metadata and files will be processed on-the-fly and a NeXus file, +compliant with NXapm will be generated. + +Currently, the implementation supports reconstructions in POS, ePOS and APT file format +(the one generated by APSuite from AMETEK/Cameca). Range files are accepted in +RNG or RRNG format. + +## Where to go from now + +With an example in your upload **you should go to the Analytics tab in the** NOMAD OASIS menu bar +and **start up the apmtools container**. The container connects your upload with a set of +containerized software tools for post-processing and exploring atom probe data in substantial more detail. + +These tools contain currently the +- [Leoben APT_analyzer](https://github.com/areichm/APT_analyzer) by Alexander Reichmann et al. (Lorenz Romaner's team at Montanuniversität Leoben) +- The [paraprobe-toolbox](https://gitlab.com/paraprobe/paraprobe-toolbox) by Markus Kühbach et al. (developed mainly at his time with the Max-Planck-Institut für Eisenforschung GmbH) + +**Once running, the apmtools container offers a jupyter lab server in which the tools are installed.** +**Further examples are available in the container. To access these you can start with the Cheatsheet.ipynb** +inside the **/home/atom_probe_tools directory.** + +Once you have explored the example datasets you should also explore and realize that the +containers are mounted with your uploads section. The respective directory in the apmtools container +is the **/config** directory. The key benefit of this container design is that you can use a simple +command in a jupyter notebook cell after your analyses + +``` +! mv *.png /config +``` + +This will move for example all generated figures (replace the file extension with a specific +name or file type) from the working directory in the container into your upload section. +H5Web can be used to display the content of HDF5 files, such as results and config files +of the paraprobe-toolbox. Please keep in mind that the config files for the tools of the +paraprobe-toolbox are already fully described with NeXus. You can find the respective schema descriptions here +[NXapm_paraprobe](https://fairmat-experimental.github.io/nexus-fairmat-proposal) + +The result files of these tools however are not yet fully described with NeXus, so no default plots will +be available here but you can still conveniently explore all the data as they are HDF5 files +which H5Web can present to you. + +## Summary + +The example is meant as a starting point. You can download the schema file and extend it to collect +further metadata (e. g. for adding optional quantities defined in NXapm) based on what is relevant for +your laboratory and use case. Also you can explore the implementation of the example to customize it for +your own needs. We would be happy if you could support us with improving this example and the associated +schemes by leaving us comments via the nexus-fairmat-proposal pages or by contacting us via +the various channels. We are also very happy to guide you on how to customize these functionalities +for your own laboratory and needs. + +Consult our [documentation on the NOMAD Archive and Metainfo](https://nomad-lab.eu/prod/v1/docs/archive.html) +to learn more about schemes. + +## Questions, comments, suggestions? + +For general questions regarding the APM tools and if you're interested in building one for your +own research workflow or your colleagues and group, you are very welcome +[Markus Kühbach](https://www.fair-di.eu/fairmat/fairmat_/fairmatteam) from the FAIRmat consortium. + +## Known bugs + diff --git a/examples/data/apm/apm.archive.json b/examples/data/apm/apm.archive.json new file mode 100644 index 0000000000000000000000000000000000000000..3da107f05b435695796b098e938ddf7a4d80b091 --- /dev/null +++ b/examples/data/apm/apm.archive.json @@ -0,0 +1 @@ +{"data":{"m_def":"../uploads/TCackfAXQeyapTYgFob_Rw/raw/nxapm.schema.archive.yaml#/definitions/section_definitions/0","reader":"apm","nxdl":"NXapm.nxdl","input_files":["eln_data.yaml","R31_06365-v02.pos","R31_06365-v02.rrng"],"entry":{"attr_version":"nexus-fairmat-proposal successor of 50433d9039b3f33299bab338998acb5335cd8951","definition":"NXapm","experiment_identifier":"R31-06365-v02","experiment_description":"some details for nomad, ODS steel precipitates for testing a developmental clustering algorithm called OPTICS.","start_time":"2022-09-20T20:00:00+00:00","end_time":"2022-09-22T20:00:00+00:00","program":"IVAS","program__attr_version":"3.6.4","run_number":"6365","operation_mode":"apt"},"user":[{"name":"Jing Wang"},{"name":"Daniel Schreiber"}],"specimen":{"name":"ODS-Specimen 1","sample_history":"undocumented","preparation_date":"2022-09-12T20:01:00+00:00","short_title":"ODS","atom_types":["Fe","Cr","Y","O"],"description":"ODS steel, i.e. material with Y2O3 dispersoids"},"atom_probe":{"instrument_name":"LEAP 3000","flight_path_length":0.9,"fabrication_vendor":"AMETEK/Camcca","fabrication_model":"LEAP3000","fabrication_identifier":"n/a","fabrication_capabilities":"n/a","reflectron_applied":true,"local_electrode_name":"electrode 1","ion_detector_type":"mcp_dld","ion_detector_name":"none","ion_detector_model":"cameca","ion_detector_serial_number":"n/a","stage_lab_base_temperature":30,"analysis_chamber_pressure":1e-10,"specimen_monitoring_initial_radius":30,"specimen_monitoring_shank_angle":5,"specimen_monitoring_detection_rate":0.6,"control_software_program":"IVAS","control_software_program__attr_version":"3.6.4","pulser":{"pulse_mode":"laser","pulse_frequency":250,"pulse_fraction":0.1,"laser_gun_name":"laser","laser_gun_wavelength":4.8e-7,"laser_gun_power":2e-8,"laser_gun_pulse_energy":1.2e-11}},"reconstruction":{"program":"IVAS","program__attr_version":"3.6.4","protocol_name":"cameca","parameter":"kf = 1.8, ICF = 1.02, Vat = 60 at/nm^3","crystallographic_calibration":"n/a"},"ranging":{"program":"IVAS","program__attr_version":"3.6.4"},"output":"output.nxs"},"m_ref_archives":{}} \ No newline at end of file diff --git a/examples/data/apm/eln_data.yaml b/examples/data/apm/eln_data.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5eea73472718d094ecc038098fe6a1fcd9d26cac --- /dev/null +++ b/examples/data/apm/eln_data.yaml @@ -0,0 +1,81 @@ +atom_probe: + analysis_chamber_pressure: + unit: torr + value: 1.0e-10 + control_software_program: IVAS + control_software_program__attr_version: 3.6.4 + fabrication_capabilities: n/a + fabrication_identifier: n/a + fabrication_model: LEAP3000 + fabrication_vendor: AMETEK/Camcca + flight_path_length: + unit: m + value: 0.9 + instrument_name: LEAP 3000 + ion_detector_model: cameca + ion_detector_name: none + ion_detector_serial_number: n/a + ion_detector_type: mcp_dld + local_electrode_name: electrode 1 + pulser: + laser_gun_name: laser + laser_gun_power: + unit: W + value: 2.0e-08 + laser_gun_pulse_energy: + unit: J + value: 1.2e-11 + laser_gun_wavelength: + unit: m + value: 4.8e-07 + pulse_fraction: 0.1 + pulse_frequency: + unit: kHz + value: 250 + pulse_mode: laser + reflectron_applied: true + specimen_monitoring_detection_rate: 0.6 + specimen_monitoring_initial_radius: + unit: nm + value: 30 + specimen_monitoring_shank_angle: + unit: ° + value: 5 + stage_lab_base_temperature: + unit: K + value: 30 +entry: + attr_version: nexus-fairmat-proposal successor of 50433d9039b3f33299bab338998acb5335cd8951 + definition: NXapm + end_time: '2022-09-22T20:00:00+00:00' + experiment_description: some details for nomad, ODS steel precipitates for testing + a developmental clustering algorithm called OPTICS. + experiment_identifier: R31-06365-v02 + operation_mode: apt + program: IVAS + program__attr_version: 3.6.4 + run_number: '6365' + start_time: '2022-09-20T20:00:00+00:00' +ranging: + program: IVAS + program__attr_version: 3.6.4 +reconstruction: + crystallographic_calibration: n/a + parameter: kf = 1.8, ICF = 1.02, Vat = 60 at/nm^3 + program: IVAS + program__attr_version: 3.6.4 + protocol_name: cameca +specimen: + atom_types: + - Fe + - Cr + - Y + - O + description: ODS steel, i.e. material with Y2O3 dispersoids + name: ODS-Specimen 1 + preparation_date: '2022-09-12T20:01:00+00:00' + sample_history: undocumented + short_title: ODS +user: +- name: Jing Wang +- name: Daniel Schreiber diff --git a/examples/data/apm/nxapm.schema.archive.yaml b/examples/data/apm/nxapm.schema.archive.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a7e8407e9b611b3059393618b11e83349935904a --- /dev/null +++ b/examples/data/apm/nxapm.schema.archive.yaml @@ -0,0 +1,661 @@ +# group, field, and attribute names match to NXapm, for further details +# what each field should contain consult the respective docstring of the +# quantity in NXapm +definitions: + name: 'apm' + # 'ELN/application definition schema for atom probe microscopy (APM) experiments.' + sections: # section definitions what were back in the old days msection base classes + # Operator: + # Specimen: + AtomProbeMicroscopy: # the actual schema + # nomad.datamodel.metainfo.eln.NexusParser + base_sections: + - 'nomad.datamodel.metainfo.eln.NexusParser' + - 'nomad.datamodel.data.EntryData' + # base_section: nomad.datamodel.data.EntryData + m_annotations: + # Here you can set your default values for the reader and nxdl. + 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: ['nxdl', 'reader'] + hide: [] + sub_sections: + entry: + section: + description: | + Generic details about an experiment. + m_annotations: + eln: + overview: true + quantities: + attr_version: + type: + type_kind: Enum + type_data: + - 'nexus-fairmat-proposal successor of 50433d9039b3f33299bab338998acb5335cd8951' + description: Hashvalue of the NeXus application definition file + m_annotations: + eln: + component: RadioEnumEditQuantity + definition: + type: + type_kind: Enum + type_data: + - NXapm + description: NeXus NXDL schema to which this file conforms + m_annotations: + eln: + component: RadioEnumEditQuantity + experiment_identifier: + type: str + description: GUID of the experiment + m_annotations: + eln: + component: StringEditQuantity + experiment_description: + type: str + description: Free text details about the experiment + m_annotations: + eln: + component: StringEditQuantity + start_time: + type: Datetime + description: ISO 8601 time code with local time zone offset to UTC when the experiment started. + m_annotations: + eln: + component: DateTimeEditQuantity + end_time: + type: Datetime + description: ISO 8601 time code with local time zone offset to UTC when the experiment ended. + m_annotations: + eln: + component: DateTimeEditQuantity + program: + type: str + description: Name of the program used to create this file. + m_annotations: + eln: + component: StringEditQuantity + program__attr_version: + type: str + description: Version plus build number, commit hash, or description of the program to support reproducibility. + m_annotations: + eln: + component: StringEditQuantity + run_number: + type: str + description: Identifier in the instrument control software given for this experiment. + m_annotations: + eln: + component: StringEditQuantity + # experiment_documentation(NXnote): + # thumbnail(NXnote): + # attr_type: + 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 + # inputfile_reconstruction: + # type: str + # description: | + # Place to drag-and-drop the file containing the result of the measurement. + # This result has to be the tomographic reconstruction. + # Accepted file formats are POS, ePOS, and APT (from APSuite). + # m_annotations: + # eln: + # component: FileEditQuantity + # inputfile_range_file: + # type: str + # description: | + # Place to drag-and-drop a file which contains the result of a ranging of + # the mass-to-charge-state ratio values to assigned ion labels. + # Accepted file formats are RNG, and RRNG. + # m_annotations: + # eln: + # component: FileEditQuantity + user: + repeats: true + section: + description: | + Contact information and eventually details of at least one person + involved in the taking of 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: Globally unique identifier of the user as offered by services like OrcID or ResearcherID. + m_annotations: + eln: + component: StringEditQuantity + orcid_platform: + type: str + description: Name of the OrcID or ResearcherID where the account under orcid is registered. + 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, guest are common examples. + m_annotations: + eln: + component: StringEditQuantity + social_media_name: + type: str + description: Account name that is associated with the user in social media platforms. + m_annotations: + eln: + component: StringEditQuantity + social_media_platform: + type: str + description: Name of the social media platform where the account under social_media_name is registered. + m_annotations: + eln: + component: StringEditQuantity + specimen: + section: + description: | + Details about the specimen and its immediate environment. + m_annotations: + eln: + quantities: + name: + type: str + description: | + GUID which distinguishes the specimen from all others and especially + the predecessor/origin from where the specimen was cut. + In cases where the specimen was e.g. site-specifically cut from + samples or in cases of an instrument session during which multiple + specimens are loaded, the name has to be descriptive enough to + resolve which specimen on e.g. the microtip array was taken. + This field must not be used for an alias of the specimen. + Instead, use short_title. + m_annotations: + eln: + component: StringEditQuantity + sample_history: + type: str + description: | + Reference to the location of or a GUID providing as many details + as possible of the material, its microstructure, and its + thermo-chemo-mechanical processing/preparation history. + m_annotations: + eln: + component: StringEditQuantity + preparation_date: + type: Datetime + description: | + ISO 8601 time code with local time zone offset to UTC information when + the measured specimen surface was actively prepared. + m_annotations: + eln: + component: DateTimeEditQuantity + short_title: + type: str + description: Possibility to give an abbreviation of the specimen name field. + m_annotations: + eln: + component: StringEditQuantity + # atom_types should be a list of strings + atom_types: + type: str + shape: ['*'] + description: | + Use Hill's system for listing elements of the periodic table which + are inside or attached to the surface of the specimen and thus + relevant from a scientific point of view. + m_annotations: + eln: + component: StringEditQuantity + description: + type: str + description: | + Discouraged free text field to be used in the case when properly + designed records for the sample_history are not available. + m_annotations: + eln: + component: StringEditQuantity + # composition_element_symbol: + # type: str + # shape: ['*'] + # description: | + # Chemical symbol. + # m_annotations: + # eln: + # component: StringEditQuantity + # composition_mass_fraction: + # type: np.float64 + # shape: ['*'] + # description: | + # Composition but this can be atomic or mass fraction. + # Best is you specify which you want. Under the hood oasis uses pint + # /nomad/nomad/units is the place where you can predefine exotic + # constants and units for a local oasis instance + # m_annotations: + # eln: + # component: NumberEditQuantity + # minValue: 0. + # maxValue: 1. + # composition_mass_fraction_error: + # type: np.float64 + # shape: ['*'] + # description: | + # Composition but this can be atomic or mass fraction. + # Also here best to be specific. If people write at.-% but mean wt.-% you + # cannot guard yourself against this + # m_annotations: + # eln: + # component: NumberEditQuantity + # minValue: 0. + # maxValue: 1. + atom_probe: + section: + description: | + The instrument and the lab in which it stands. + m_annotations: + eln: + quantities: + 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. + # (NXfabrication): + flight_path_length: + type: np.float64 + unit: meter + description: | + The space inside the atom probe that ions pass through nominally + when they leave the specimen and travel to the detector. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: meter + minValue: 0.0 + maxValue: 10.0 + field_of_view(NX_FLOAT): + type: np.float64 + unit: meter + description: | + The nominal diameter of the specimen ROI which is measured in the + experiment. Physically, the specimen cannot be measured completely + because ions may launch but not become detected or hit elsewhere. + fabrication_vendor: + type: str + description: Name of the manufacturer/company, i.e. AMETEK/Cameca. + m_annotations: + eln: + component: StringEditQuantity + fabrication_model: + type: str + description: Model name of the instrument + m_annotations: + eln: + component: StringEditQuantity + fabrication_identifier: + type: str + description: Serial number/identifier of the instrument as issued by the manufacturer + m_annotations: + eln: + component: StringEditQuantity + fabrication_capabilities: + type: str + description: Capabilities of the instrument + m_annotations: + eln: + component: StringEditQuantity + # analysis_chamber(NXchamber): + # load_lock_chamber(NXchamber): + # buffer_chamber(NXchamber): + # getter_pump(NXpump): + # roughening_pump(NXpump): + # turbomolecular_pump(NXpump): + reflectron_applied: + type: bool + description: Is a reflectron installed and was it used? + m_annotations: + eln: + component: BoolEditQuantity + local_electrode_name: + type: str + description: Identifier of the local_electrode in the control software database. + m_annotations: + eln: + component: StringEditQuantity + # local_electrode__(NXaperture_em): + # ion_detector(NXdetector): + ion_detector_type: + type: + type_kind: Enum + type_data: + - mcp_dld + - phosphor_ccd + - other + description: | + Type of the ToF-taking detector system + Examples are mcp_dld, phosphor_ccd, or other + m_annotations: + eln: + component: RadioEnumEditQuantity + ion_detector_name: + type: str + description: Given name or alias of the detector + m_annotations: + eln: + component: StringEditQuantity + ion_detector_model: + type: str + description: Given brand or model name by the manufacturer. + m_annotations: + eln: + component: StringEditQuantity + ion_detector_serial_number: + type: str + description: Given hardware name/serial number or hash identifier issued by the manufacturer. + m_annotations: + eln: + component: StringEditQuantity + # fabrication_vendor: + # signal_amplitude(NX_FLOAT): + stage_lab_base_temperature: + type: np.float64 + unit: kelvin + description: | + Average temperature at the specimen base, i.e. + base temperature, during the measurement. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: kelvin + minValue: 0.0 + maxValue: 273.15 + analysis_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 + specimen_monitoring_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 + specimen_monitoring_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 + specimen_monitoring_detection_rate: + type: np.float64 + descriptions: | + Average detection rate over the course of the experiment. + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0.0 + maxValue: 1.0 + control_software_program: + type: str + description: | + Name of the control software of the microscope + used during acquisition (e.g. IVAS/APSuite). + m_annotations: + eln: + component: StringEditQuantity + control_software_program__attr_version: + type: str + description: Version plus build number, commit hash, or description of the program to support reproducibility. + m_annotations: + eln: + component: StringEditQuantity + 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 + - high_voltage + - laser_and_high_voltage + description: | + Which pulsing mode was used? + m_annotations: + eln: + component: RadioEnumEditQuantity + pulse_frequency: + type: np.float64 + unit: kilohertz + description: Pulse frequency + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: kilohertz + minValue: 0.0 + maxValue: 10000.0 + pulse_fraction: + type: np.float64 + description: Pulse fraction + m_annotations: + eln: + component: NumberEditQuantity + minValue: 0.0 + maxValue: 1.0 + laser_gun_name: + type: str + description: Given name/alias. + m_annotations: + eln: + component: StringEditQuantity + laser_gun_wavelength: + type: np.float64 + unit: meter + description: Nominal wavelength of the laser radiation. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: nanometer + minValue: 0.0 + laser_gun_power: + type: np.float64 + unit: watt + description: | + Nominal power of the laser source while + illuminating the specimen. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: nanowatt + minValue: 0.0 + laser_gun_pulse_energy: + type: np.float64 + unit: joule + description: Average energy of the laser at peak of each pulse. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: picojoule + minValue: 0.0 + # control_software: + # section: + # description: Which control software was used e.g. IVAS/APSuite + # m_annotations: + # eln: + # quantities: + # program: + # type: str + # description: | + # Name of the program used to control the instrument during the measurement. + # Examples are IVAS or APSuite for Cameca/AMETEK local electrode atom probes. + # m_annotations: + # eln: + # component: StringEditQuantity + # program__attr_version: + # type: str + # description: | + # Version plus build number, commit hash, or description + # of the program to support reproducibility. + # m_annotations: + # eln: + # component: StringEditQuantity + # (NXstage_lab): + # (NXdata): + # (NXcoordinate_system_set): + # (NXmonitor): + reconstruction: + section: + description: Details about the reconstruction + m_annotations: + eln: + quantities: + program: + type: str + description: | + Name of the program used to perform the reconstruction with. + Examples are IVAS, APSuite, or names of open-source tools. + m_annotations: + eln: + component: StringEditQuantity + program__attr_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. + Bas maps currently to bas_modified. + Cameca maps currently to apsuite. + Add more details in the parameter text field, + e.g. reconstruction parameter. + 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 + in a collection, i.e. here with a free-text field. + m_annotations: + eln: + component: StringEditQuantity + crystallographic_calibration: + type: str + description: | + Different strategies for crystallographic calibration of the + reconstruction are possible. The field is required and details + should be specified in free-text at least. If the not crystallographic + calibration was performed the field should be filled with the n/a, + meaning not applied. + m_annotations: + eln: + component: StringEditQuantity + ranging: + section: + description: Details about the ranging definitions. + m_annotations: + eln: + quantities: + program: + type: str + description: | + Name of the program used to perform the ranging with. + Examples are IVAS, APSuite, or names of open-source tools. + m_annotations: + eln: + component: StringEditQuantity + program__attr_version: + type: str + description: Version plus build number, commit hash, or description of the program to support reproducibility. + m_annotations: + eln: + component: StringEditQuantity + # number_of_iontypes + # maximum_number_of_atoms_per_molecular_ion + # mass_to_charge_distribution + # background_quantification + # peak_search_and_deconvolution + # peak_identification taken over from program and program__attr_version diff --git a/examples/data/em_spctrscpy/README.md b/examples/data/em_spctrscpy/README.md new file mode 100644 index 0000000000000000000000000000000000000000..699b1f5a07e730e9ac0a54183e88874848692b90 --- /dev/null +++ b/examples/data/em_spctrscpy/README.md @@ -0,0 +1,92 @@ +# An Electronic Lab Notebook (ELN) Example for Electron Microscopy (EM) + +## Introduction + +This example shows how the NOMAD ELN functionalities can be used to collect +required metadata for groups, fields, and attributes for creating a dataset +which is compliant with the NeXus NXem application definition. + +Specifically, this example shows how I/O functionalities of hyperspy can be used +to load data of spectroscopy experiments from three exemplar file formats, namely +Bruker BCF, Velox EMD, and DigitalMicrograph DM3 into NOMAD OASIS. The implementation +shows how instances of NeXus base classes like NXspectrum_set_em_xray, +NXspectrum_set_em_eels, and NXimage_set_em_adf can be created and registered inside +instances of NXevent_data_em. The example shows how all these base classes can be +composed and stored inside a NeXus/HDF5 file. + +This makes the example relevant for researchers who work within the fields of +scanning electron microscopy (SEM) EDS/EDX, transmission electron microscopy +(EDS/STEM), and electron energy loss spectroscopy (EELS). + +The NeXus NXem data model is documented here [NXem](https://fairmat-experimental.github.io/nexus-fairmat-proposal) + +This example also shows how the NOMAD OASIS ELN functionalities can be used +to supplement a NeXus file with metadata which are currently or usually not stored +in vendor or community files including details about the specimen, the instrument, +users, and post-processing steps. + +This example upload contains the following entries: +- A schema in NOMAD's *archive.yaml* format: *nxem.schema.archive.yaml* +- A schema instance file used by NOMAD *nxem.archive.json* which is filled for educational purpose with values for the example. +- The primary consumer of this json file is NOMAD and its internal data management system. +- Another schema instance file used by the nomad-parser-nexus *eln_data.yaml*. This file contains all entered +quantities from the ELN GUI (after the save button was stored). The example is also filled for educational purposes +with values matching those in nxem.archive.json. +Files are updated each time the save button in the ELN GUI is clicked. +The eln_data.yaml file is used by the [NOMAD-PARSER-NEXUS](https://github.com/nomad-coe/nomad-parser-nexus). + +This example is configured to take an example dataset and call the nomad-parser-nexus dataconverter +which creates a NeXus file compliant with NXem. Once completed, this file is available in the +upload section/staging area. This makes also these files explorable with H5Web visualization +through the files menu. + +This example comes with a measured datasets which are meant for testing and exploring. +The datasets include two examples kindly shared by Adrien Teurtrie and Cécile Hebert at EPFL +(Bruker BCF, Velox EMD), and a Digital Micrograph DM3 file with EELS data measured and kindly +shared by Hannah Nerl and Christoph Koch. + +## Creating NeXus files + +When you modify the ELN and click the save button, the data from the ELN will be +parsed and combined with the content from the vendor file to create the NXS file. +You can replace these files with your own and accordingly use the ELN to enter your +own metadata. Upon saving, a NeXus/HDF5 file in NXapm format will be created for your specific dataset. + +With this functionality, you can use this example as a template to translate your own +datasets into NeXus. The drag-and-drop functionality of the upload section can be +used to pass your vendor file onto the respective file upload fields of the ELN. +After clicking the save button, the newly entered metadata and files will be processed +on-the-fly and a new NeXus file, compliant with NXem will be generated. + +## Where to go from now + +With an example in your upload **you can explore** the content in H5Web. +Furthermore, you can work with the data by starting for instance +a generic jupyterlab container via the **Analytics tab in the** NOMAD OASIS +menu bar. This container is a part of the Nomad Remote Tools Hub (NORTH) service. + +**Once running, the container offers a jupyter-lab server and notebook.** + +## Summary + +The example is meant as a starting point. You can download the schema file and extend the +schema to collect further metadata (e. g. for adding optional quantities defined in NXem) based +on what is relevant for your laboratory and use case. Also you can explore the implementation +of the example to customize it for your own needs. We would be happy if you could support us +with improving this example and the associated schemes by leaving us comments via the +nexus-fairmat-proposal pages or by contacting us via the various channels. +We are also very happy to guide you on how to customize these functionalities +for your own laboratory and needs. + +Consult our [documentation on the NOMAD Archive and Metainfo](https://nomad-lab.eu/prod/v1/docs/archive.html) +to learn more about schemes. + +## Questions, comments, suggestions? + +For general questions regarding the EM tools and if you're interested in building one for your +own research workflow or your colleagues and group you are very welcome to contact +[Markus Kühbach](https://www.fair-di.eu/fairmat/fairmat_/fairmatteam) from the FAIRmat consortium. + + +## Known bugs + diff --git a/examples/data/em_spctrscpy/eln_data.yaml b/examples/data/em_spctrscpy/eln_data.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e9683551f27a80fb5b69258890b4f77ba7e2887b --- /dev/null +++ b/examples/data/em_spctrscpy/eln_data.yaml @@ -0,0 +1,61 @@ +em_lab: + detector: + - type: EDS detector + ebeam_column: + aberration_correction: + applied: true + aperture_em: + - name: C1 + value: 4 + electron_gun: + emitter_type: cold_cathode_field_emitter + voltage: + unit: V + value: 200000 + fabrication: + capabilities: '---' + identifier: talos + model: TALOS + vendor: FEI + instrument_name: TALOS + location: fEPFL + optical_system_em: + beam_current: + unit: A + value: 1.2e-11 + beam_current_description: estimated + magnification: 610000 + semi_convergence_angle: + unit: rad + value: 0.2 + stage_lab: + description: double tilt + name: nothing +entry: + attr_version: nexus-fairmat-proposal successor of 50433d9039b3f33299bab338998acb5335cd8951 + definition: NXem + end_time: '2022-09-21T19:16:00+00:00' + experiment_description: 1613_si + experiment_identifier: 1613_si + program: Velox + program__attr_version: v6.8 + start_time: '2022-09-20T19:16:00+00:00' +sample: + atom_types: + - Al + - Nd + - O + description: test + method: experiment + name: 1613_Si + preparation_date: '2022-09-15T19:15:00+00:00' + sample_history: unknown + short_title: '1613' + thickness: + unit: m + value: 2.0e-08 +user: +- name: MarkusK + orcid: '0000' +- email: '----' + name: MarkusS diff --git a/examples/data/em_spctrscpy/em.archive.json b/examples/data/em_spctrscpy/em.archive.json new file mode 100644 index 0000000000000000000000000000000000000000..8d1a50ee0cadea7d403fbf02bbfdd3ef81dec6db --- /dev/null +++ b/examples/data/em_spctrscpy/em.archive.json @@ -0,0 +1 @@ +{"data":{"m_def":"../uploads/DoBifrB0S66bsCcZXT-v6Q/raw/nxem.schema.archive.yaml#/definitions/section_definitions/0","reader":"em_spctrscpy","nxdl":"NXem.nxdl","input_files":["eln_data.yaml","EELS_map_2_ROI_1_location_4.dm3"],"output":"output.nxs","entry":{"attr_version":"nexus-fairmat-proposal successor of 50433d9039b3f33299bab338998acb5335cd8951","definition":"NXem","experiment_identifier":"1613_si","experiment_description":"1613_si","start_time":"2022-09-20T19:16:00+00:00","end_time":"2022-09-21T19:16:00+00:00","program":"Velox","program__attr_version":"v6.8"},"user":[{"name":"MarkusK","orcid":"0000"},{"name":"MarkusS","email":"----"}],"sample":{"method":"experiment","name":"1613_Si","sample_history":"unknown","preparation_date":"2022-09-15T19:15:00+00:00","short_title":"1613","atom_types":["Al","Nd","O"],"description":"test","thickness":2e-8},"em_lab":{"instrument_name":"TALOS","location":"fEPFL","fabrication":{"vendor":"FEI","model":"TALOS","identifier":"talos","capabilities":"---"},"ebeam_column":{"electron_gun":{"voltage":200000,"emitter_type":"cold_cathode_field_emitter"},"aperture_em":[{"name":"C1","value":4}],"aberration_correction":{"applied":true}},"optical_system_em":{"magnification":610000,"semi_convergence_angle":0.2,"beam_current":1.2e-11,"beam_current_description":"estimated"},"detector":[{"type":"EDS detector"}],"stage_lab":{"name":"nothing","description":"double tilt"}}},"m_ref_archives":{}} \ No newline at end of file diff --git a/examples/data/em_spctrscpy/nxem.schema.archive.yaml b/examples/data/em_spctrscpy/nxem.schema.archive.yaml new file mode 100644 index 0000000000000000000000000000000000000000..73d2933fadcd381e599bc607c5c7f2fa038c6157 --- /dev/null +++ b/examples/data/em_spctrscpy/nxem.schema.archive.yaml @@ -0,0 +1,508 @@ +# group, field, and attribute names match to NXem, for further details +# what each field should contain consult the respective docstring of the +# quantity in NXem +definitions: + name: 'em' + # 'ELN/application definition schema for electron microscopy (EM) experiments.' + sections: # section definitions what were back in the old days msection base classes + # Operator: + # Specimen: + ElectronMicroscopy: # the actual schema + # nomad.datamodel.metainfo.eln.NexusParser + base_sections: + - 'nomad.datamodel.metainfo.eln.NexusParser' + - 'nomad.datamodel.data.EntryData' + # base_section: nomad.datamodel.data.EntryData + m_annotations: + # Here you can set your default values for the reader and nxdl. + template: + reader: em_spctrscpy + nxdl: NXem.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: ['nxdl', 'reader'] + hide: [] + sub_sections: + entry: + section: + description: | + Generic details about an experiment. + m_annotations: + eln: + overview: true + quantities: + attr_version: + type: + type_kind: Enum + type_data: + - 'nexus-fairmat-proposal successor of 50433d9039b3f33299bab338998acb5335cd8951' + description: Hashvalue of the NeXus application definition file + m_annotations: + eln: + component: RadioEnumEditQuantity + definition: + type: + type_kind: Enum + type_data: + - NXem + description: NeXus NXDL schema to which this file conforms + m_annotations: + eln: + component: RadioEnumEditQuantity + experiment_identifier: + type: str + description: GUID of the experiment + m_annotations: + eln: + component: StringEditQuantity + experiment_description: + type: str + description: Free text details about the experiment + m_annotations: + eln: + component: StringEditQuantity + start_time: + type: Datetime + description: | + ISO 8601 time code with local time zone offset to UTC + when the microscope session started. + m_annotations: + eln: + component: DateTimeEditQuantity + end_time: + type: Datetime + description: | + ISO 8601 time code with local time zone offset to UTC + when the microscope session ended. + m_annotations: + eln: + component: DateTimeEditQuantity + program: + type: str + description: Name of the program used to create this file. + m_annotations: + eln: + component: StringEditQuantity + program__attr_version: + type: str + description: Version plus build number, commit hash, or description of the program to support reproducibility. + m_annotations: + eln: + component: StringEditQuantity + # experiment_documentation(NXnote): + # thumbnail(NXnote): + # attr_type: + # inputfile: + # type: str + # description: not used + # m_annotations: + # eln: + # component: FileEditQuantity + user: + repeats: true + section: + description: | + Contact information and eventually details of at least one person + involved in the taking of 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: Globally unique identifier of the user as offered by services like OrcID or ResearcherID. + m_annotations: + eln: + component: StringEditQuantity + orcid_platform: + type: str + description: Name of the OrcID or ResearcherID where the account under orcid is registered. + 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, guest are common examples. + m_annotations: + eln: + component: StringEditQuantity + social_media_name: + type: str + description: Account name that is associated with the user in social media platforms. + m_annotations: + eln: + component: StringEditQuantity + social_media_platform: + type: str + description: Name of the social media platform where the account under social_media_name is registered. + m_annotations: + eln: + component: StringEditQuantity + sample: + section: + description: | + Details about the sample and its immediate environment. + m_annotations: + eln: + quantities: + method: + type: + type_kind: Enum + type_data: + - experiment + - simulation + m_annotations: + eln: + component: RadioEnumEditQuantity + name: + type: str + description: | + GUID which distinguishes the specimen from all others and especially + the predecessor/origin from where the specimen was cut. + In cases where the specimen was e.g. site-specifically cut from + samples or in cases of an instrument session during which multiple + specimens are loaded, the name has to be descriptive enough to + resolve which specimen was taken. This field must not be used for an + alias of the specimen. Instead, use short_title. + + In cases where multiple specimens have been loaded into the microscope + the name has to identify the specific one, whose results are stored + by this NXentry, because a single NXentry should be used only for + the characterization of a single specimen. + Details about the specimen preparation should be stored in the + sample history. + m_annotations: + eln: + component: StringEditQuantity + sample_history: + type: str + description: | + Reference to the location of or a GUID providing as many details + as possible of the material, its microstructure, and its + thermo-chemo-mechanical processing/preparation history. + m_annotations: + eln: + component: StringEditQuantity + preparation_date: + type: Datetime + description: | + ISO 8601 time code with local time zone offset to UTC information when + the measured specimen surface was last actively prepared before + loading the sample into the microscope. + m_annotations: + eln: + component: DateTimeEditQuantity + short_title: + type: str + description: Possibility to give an abbreviation or alias of the specimen name field. + m_annotations: + eln: + component: StringEditQuantity + # atom_types should be a list of strings + atom_types: + type: str + shape: ['*'] + description: | + Use Hill's system for listing elements of the periodic table which + are inside or attached to the surface of the specimen and thus + relevant from a scientific point of view. + m_annotations: + eln: + component: StringEditQuantity + description: + type: str + description: | + Discouraged free text field to be used in the case when properly + designed records for the sample_history are not available. + m_annotations: + eln: + component: StringEditQuantity + thickness: + type: np.float64 + unit: meter + description: | + (Measured) sample thickness. The information is recorded to qualify + if the beam used was likely able to shine through the specimen. + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: nanometer + minValue: 0.0 + # density: + # type: np.float64 + # unit: g/cm^3 # how to do? + # description: | + # (Measured) density of the specimen. For multi-layered specimens + # this field should only be used to describe the density of the excited volume. + # For scanning electron microscopy the usage of this field is discouraged + # and instead an instance of an NXinteraction_volume_em for individual + # NXevent_data_em instances can provide a much better description of + # the relevant details why one would otherwise ask to store the + # density of the specimen. + # m_annotations: + # eln: + # component: NumberEditQuantity + # defaultDisplayUnit: g/cm^3 + # minValue: 0.0 + em_lab: + section: + description: | + Metadata of the microscope and the lab in which it stands. + m_annotations: + eln: + quantities: + 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 + sub_sections: + fabrication: + section: + description: Details about the microscope fabrication. + m_annotations: + eln: + quantities: + # sub_sections: + vendor: + type: str + description: Company name of the manufacturer. + m_annotations: + eln: + component: StringEditQuantity + model: + type: str + description: Version or model of the component named by the manufacturer. + m_annotations: + eln: + component: StringEditQuantity + 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 + capabilities: + type: str + description: Free-text list with eventually multiple terms of functionalities which the component offers. + m_annotations: + eln: + component: StringEditQuantity + ebeam_column: + section: + description: Components to form a controlled electron beam + m_annotations: + eln: + # quantities: + sub_sections: + electron_gun: + section: + description: The source which creates the electron beam + m_annotations: + eln: + quantities: + name: + type: str + description: Given name/alias + m_annotations: + eln: + component: StringEditQuantity + voltage: + type: np.float64 + description: | + Voltage relevant to compute the energy of the + electrons immediately after they left the gun. + unit: volt + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: kilovolt + minValue: 0.0 + maxValue: 1.1e6 + emitter_type: + type: + type_kind: Enum + type_data: + - filament + - schottky + - cold_cathode_field_emitter + - other + description: | + Emitter type used to create the beam. + m_annotations: + eln: + component: RadioEnumEditQuantity + # emitter_material: + # type: str + # description: Material of which the emitter is build, e.g. the filament material. + # m_annotations: + # eln: + # component: StringEditQuantity + aperture_em: + repeats: true + section: + description: Used apertures. + m_annotations: + eln: + quantities: + name: + type: str + description: Given name/alias of the aperture. + m_annotations: + eln: + component: StringEditQuantity + value: + type: np.float64 + description: Relevant value from the control software. + m_annotations: + eln: + component: NumberEditQuantity + # NXlens_em + aberration_correction: + section: + description: Aberration corrector details. + m_annotations: + eln: + quantities: + applied: + type: bool + description: Was the corrector used? + m_annotations: + eln: + component: BoolEditQuantity + # ibeam_column: + # ebeam_deflector: + # ibeam_deflector: + # stage_lab: + optical_system_em: + section: + description: Qualifying the electron optical system + m_annotations: + eln: + quantities: + camera_length: + type: np.float64 + unit: meter + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: m + minValue: 0.0 + magnification: + type: np.float64 + m_annotations: + eln: + component: NumberEditQuantity + minValue: 1.0 + defocus: + type: np.float64 + unit: meter + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: nm + semi_convergence_angle: + type: np.float64 + unit: radian + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: radian + # field_of_view: + # type: np.float64 + # unit: meter + # m_annotations: + # eln: + # component: NumberEditQuantity + # defaultDisplayUnit: nm + working_distance: + type: np.float64 + unit: meter + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: mm + beam_current: + type: np.float64 + unit: ampere + m_annotations: + eln: + component: NumberEditQuantity + defaultDisplayUnit: picoampere + minValue: 0.0 + beam_current_description: + type: str + m_annotations: + eln: + component: StringEditQuantity + detector: + repeats: true + section: + description: | + Description of the type of detector. + m_annotations: + eln: + quantities: + type: + type: str + description: Free text option to write further details about the detector. + m_annotations: + eln: + component: StringEditQuantity + # (NXpump) + stage_lab: + section: + description: A stage lab which can hold, align, orient, and prepare a specimen. + m_annotations: + eln: + quantities: + name: + type: str + description: Given name/alias for the stage. + m_annotations: + eln: + component: StringEditQuantity + description: + type: str + description: Ideally, a (globally) unique persistent identifier, link, or text to a resource which gives further details. + m_annotations: + eln: + component: StringEditQuantity diff --git a/examples/data/generate_example_uploads.sh b/examples/data/generate_example_uploads.sh index 9b981c4539f6c8d766cf6164e18a6a1e5351afc3..3ac2fee7d5989a1471c8b321275843ee0cde1774 100755 --- a/examples/data/generate_example_uploads.sh +++ b/examples/data/generate_example_uploads.sh @@ -1,11 +1,50 @@ #!/bin/sh + +cd ../../ +BASE_PATH=$(pwd) +REMOTE_TOOLS_PATH=$BASE_PATH/dependencies/nomad-remote-tools-hub/docker/ + +cd $BASE_PATH/examples/data rm -rf uploads/*.zip curl -L https://www.dropbox.com/s/8zd7aqe91lza2r4/theory-example-upload.zip?dl=1 -o uploads/theory.zip # TODO this does not work on the mpcdf servers (no route to host) !? # curl https://datashare.mpcdf.mpg.de/s/xeBEsWGyrRq9XH4/download -o uploads/theory.zip -cd eln + +cd $BASE_PATH/examples/data/eln zip -r ../uploads/eln.zip * -cd .. -cd tabular +cd $BASE_PATH/examples/data + +cd $BASE_PATH/examples/data/tabular zip -r ../uploads/tabular.zip * -cd .. \ No newline at end of file +cd $BASE_PATH/examples/data + +cd $BASE_PATH/examples/data/apm +curl -L https://zenodo.org/record/6808516/files/R31_06365-v02.tar.gz?download=1 -o R31_06365-v02.tar.gz +tar -xvf R31_06365-v02.tar.gz +rm R31_06365-v02.tar.gz +zip -r ../uploads/apm.zip * +cd $BASE_PATH/examples/data + +cd $REMOTE_TOOLS_PATH/mpes/example +curl -L https://zenodo.org/record/7035754/files/MoTe2.h5?download=1 -o MoTe2.h5 +mkdir -p TiTe2 +cd TiTe2 +curl -L https://zenodo.org/record/5541490/files/TiTe2_0deg.nxs?download=1 -o TiTe2_0deg.nxs +curl -L https://zenodo.org/record/5541490/files/TiTe2_60deg.nxs?download=1 -o TiTe2_60deg.nxs +cd .. +zip -r $BASE_PATH/examples/data/uploads/mpes.zip * + +cd $REMOTE_TOOLS_PATH/ellips/example +zip -r $BASE_PATH/examples/data/uploads/ellips.zip * +cd $BASE_PATH/examples/data + +cd $BASE_PATH/examples/data/em_spctrscpy +curl -L https://www.zenodo.org/record/7050774/files/em-spctrscpy-sprint9-example.zip?download=1 -o em-spctrscpy-sprint9-example.zip +unzip em-spctrscpy-sprint9-example.zip +rm em-spctrscpy-sprint9-example.zip +zip -r ../uploads/em_spctrscpy.zip * +cd $BASE_PATH/examples/data + +cd $BASE_PATH/examples/data/iv_temp +zip -r $BASE_PATH/examples/data/uploads/iv_temp.zip * +cd $BASE_PATH/examples/data diff --git a/examples/data/iv_temp/IV_temp.archive.json b/examples/data/iv_temp/IV_temp.archive.json new file mode 100644 index 0000000000000000000000000000000000000000..9dc6e845e519fb16d5fdc26ebfd536fe26c71e08 --- /dev/null +++ b/examples/data/iv_temp/IV_temp.archive.json @@ -0,0 +1,13 @@ +{ + "data": { + "m_def": "../upload/raw/IV_temp.schema.archive.yaml#/definitions/section_definitions/0", + "reader": "json_map", + "nxdl": "NXiv_temp.nxdl", + "input_files": [ + "IV_temp.mapping.json", + "IV_temp.pickle" + ], + "output": "IV_temp.nxs" + }, + "m_ref_archives": {} +} \ No newline at end of file diff --git a/examples/data/iv_temp/IV_temp.ipynb b/examples/data/iv_temp/IV_temp.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..59d53d8c6ca509e60ce61ce420c31c9c93db0176 --- /dev/null +++ b/examples/data/iv_temp/IV_temp.ipynb @@ -0,0 +1,178 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b3a07ad5", + "metadata": {}, + "source": [ + "## Install missing packages" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "65a32250-cfcb-4179-bfaa-1a9e3a08fc16", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting lmfit\n", + " Downloading lmfit-1.0.3.tar.gz (292 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m292.5/292.5 KB\u001b[0m \u001b[31m3.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25ldone\n", + "\u001b[?25hCollecting asteval>=0.9.22\n", + " Downloading asteval-0.9.27-py3-none-any.whl (17 kB)\n", + "Requirement already satisfied: numpy>=1.18 in /opt/conda/lib/python3.9/site-packages (from lmfit) (1.21.5)\n", + "Requirement already satisfied: scipy>=1.4 in /opt/conda/lib/python3.9/site-packages (from lmfit) (1.8.0)\n", + "Collecting uncertainties>=3.0.1\n", + " Downloading uncertainties-3.1.7-py2.py3-none-any.whl (98 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m98.4/98.4 KB\u001b[0m \u001b[31m11.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting future\n", + " Downloading future-0.18.2.tar.gz (829 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m829.2/829.2 KB\u001b[0m \u001b[31m5.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25ldone\n", + "\u001b[?25hBuilding wheels for collected packages: lmfit, future\n", + " Building wheel for lmfit (setup.py) ... \u001b[?25ldone\n", + "\u001b[?25h Created wheel for lmfit: filename=lmfit-1.0.3-py3-none-any.whl size=84413 sha256=b9a9a8f2e589b50b5dc95f5f379c7f08cafc9d6e303a478f25be88e9cd2a5562\n", + " Stored in directory: /home/jovyan/.cache/pip/wheels/76/f4/32/c336957bfd694c7746f4df19b74e08d918ada688fe1349cca2\n", + " Building wheel for future (setup.py) ... \u001b[?25ldone\n", + "\u001b[?25h Created wheel for future: filename=future-0.18.2-py3-none-any.whl size=491070 sha256=319fae78dac9c677828bfca92d897fe1f74448d40f8758e739c3dc9331030a6c\n", + " Stored in directory: /home/jovyan/.cache/pip/wheels/2f/a0/d3/4030d9f80e6b3be787f19fc911b8e7aa462986a40ab1e4bb94\n", + "Successfully built lmfit future\n", + "Installing collected packages: future, asteval, uncertainties, lmfit\n", + "Successfully installed asteval-0.9.27 future-0.18.2 lmfit-1.0.3 uncertainties-3.1.7\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "pip install lmfit" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "146542b7", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import h5py\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import lmfit" + ] + }, + { + "cell_type": "markdown", + "id": "c5bc22d2", + "metadata": {}, + "source": [ + "## Read Nexus file" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "c2032ddb-397a-4f9c-a27c-be63cd94cf16", + "metadata": {}, + "outputs": [], + "source": [ + "file = h5py.File(os.getcwd() + \"/IV_temp.nxs\")\n", + "temperatures = file['/entry/data/temperature']\n", + "voltages = file['/entry/data/voltage']\n", + "currents = file['/entry/data/current']\n", + "voltages = np.array(voltages).flatten()" + ] + }, + { + "cell_type": "markdown", + "id": "2660d39d", + "metadata": {}, + "source": [ + "## Run a simple fitting and calculate the resistance" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "9c3e4f07-0aeb-483c-a571-c3361f11003c", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "stdColors = plt.rcParams['axes.prop_cycle'].by_key()['color']\n", + "fitmodel = lmfit.models.LinearModel()\n", + "\n", + "resistances = []\n", + "plt.figure()\n", + "for i,temperature in enumerate(temperatures):\n", + " plt.plot(voltages, currents[i], 'x', label=f'T = {temperature} K', color=stdColors[i])\n", + " result = fitmodel.fit(currents[i], x=voltages)\n", + " plt.plot(voltages, result.best_fit, color=stdColors[i])\n", + " \n", + " resistances.append(result.params['slope'].value)\n", + " \n", + "plt.legend()\n", + "\n", + "plt.figure()\n", + "plt.plot(np.array(temperatures), resistances, 'x')\n", + "plt.xlabel('temperature (K)')\n", + "plt.ylabel('resistance ($\\Omega$)')\n", + "plt.tight_layout()" + ] + } + ], + "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.9.10" + }, + "vscode": { + "interpreter": { + "hash": "87f5da4a2ff473bca99f340815244c4c037408ffc7defb627265e8f3f8a2a06e" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/data/iv_temp/IV_temp.mapping.json b/examples/data/iv_temp/IV_temp.mapping.json new file mode 100644 index 0000000000000000000000000000000000000000..4cc60cf857a872e5b33661f7fa95aa116f1b85ed --- /dev/null +++ b/examples/data/iv_temp/IV_temp.mapping.json @@ -0,0 +1,45 @@ +{ + "/@default": "entry", + "/ENTRY[entry]/@default": "data", + "/ENTRY[entry]/DATA[data]/current_295C": {"link": "/entry/instrument/environment/current_sensor/value", "shape": "0:21"}, + "/ENTRY[entry]/DATA[data]/current_300C": {"link": "/entry/instrument/environment/current_sensor/value", "shape": "21:42"}, + "/ENTRY[entry]/DATA[data]/current_305C": {"link": "/entry/instrument/environment/current_sensor/value", "shape": "42:63"}, + "/ENTRY[entry]/DATA[data]/current_310C": {"link": "/entry/instrument/environment/current_sensor/value", "shape": "63:84"}, + "/ENTRY[entry]/DATA[data]/voltage": {"link": "/entry/instrument/environment/voltage_controller/value"}, + "/ENTRY[entry]/DATA[data]/current": "/currents_stack", + "/ENTRY[entry]/DATA[data]/temperature": {"link": "/entry/instrument/environment/temperature_controller/value"}, + "/ENTRY[entry]/DATA[data]/@axes": ["voltage"], + "/ENTRY[entry]/DATA[data]/@signal": "current_295C", + "/ENTRY[entry]/DATA[data]/@auxiliary_signals": ["current_300C", "current_305C", "current_310C"], + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/voltage_controller/calibration_time": "/metadata_start/time", + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/voltage_controller/run_control": 0, + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/voltage_controller/value": "/voltages", + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/voltage_controller/value/@units": "V", + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/temperature_controller/calibration_time": "/metadata_start/time", + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/temperature_controller/run_control": 0, + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/temperature_controller/value": "/temperatures", + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/temperature_controller/value/@units": "°C", + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/current_sensor/calibration_time": "/metadata_start/time", + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/current_sensor/run_control": 0, + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/current_sensor/value": "/currents", + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/current_sensor/value/@units": "A", + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/independent_controllers": ["voltage_controller", "temperature_control"], + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/measurement_sensors": ["current_sensor"], + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/NXpid[heating_pid]/description": "The heating current is controlled by a virtual PID device (internally in EPICS). The input value for the PID is the measured temperature of the sample. The output of the PID is the heating current.", + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/NXpid[heating_pid]/pv_sensor/value_log/value": {"link": "/entry/instrument/environment/current_sensor/value"}, + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/NXpid[heating_pid]/setpoint": {"link": "/entry/instrument/environment/temperature_controller/value"}, + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/NXpid[heating_pid]/K_p_value": "/metadata_start/device_config/PID_controller/PID_controller_pid_kp/value", + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/NXpid[heating_pid]/K_i_value": "/metadata_start/device_config/PID_controller/PID_controller_pid_ki/value", + "/ENTRY[entry]/INSTRUMENT[instrument]/ENVIRONMENT[environment]/NXpid[heating_pid]/K_d_value": "/metadata_start/device_config/PID_controller/PID_controller_pid_kd/value", + "/ENTRY[entry]/PROCESS[process]/program": "Bluesky", + "/ENTRY[entry]/PROCESS[process]/program/@version": "/metadata_start/versions/bluesky", + "/ENTRY[entry]/SAMPLE[sample]/name": "/metadata_start/sample/Name", + "/ENTRY[entry]/USER[user]/email": "/metadata_start/user/E-Mail", + "/ENTRY[entry]/USER[user]/name": "/metadata_start/user/Name", + "/ENTRY[entry]/definition": "NXiv_temp", + "/ENTRY[entry]/definition/@version": "1", + "/ENTRY[entry]/experiment_identifier": "/metadata_start/uid", + "/ENTRY[entry]/experiment_description": "A simple IV temperature experiment.", + "/ENTRY[entry]/start_time": "/metadata_start/time" + +} diff --git a/examples/data/iv_temp/IV_temp.nxs b/examples/data/iv_temp/IV_temp.nxs new file mode 100644 index 0000000000000000000000000000000000000000..b3d050ca4d5156a7ee7b66aa1fb1a820a61699d9 Binary files /dev/null and b/examples/data/iv_temp/IV_temp.nxs differ diff --git a/examples/data/iv_temp/IV_temp.pickle b/examples/data/iv_temp/IV_temp.pickle new file mode 100644 index 0000000000000000000000000000000000000000..ddbe22dd8eff16da0f9e9425397f19a87230044a Binary files /dev/null and b/examples/data/iv_temp/IV_temp.pickle differ diff --git a/examples/data/iv_temp/IV_temp.schema.archive.yaml b/examples/data/iv_temp/IV_temp.schema.archive.yaml new file mode 100644 index 0000000000000000000000000000000000000000..afe7a9699232f77d4f6a6202679b1d4bc6a8620f --- /dev/null +++ b/examples/data/iv_temp/IV_temp.schema.archive.yaml @@ -0,0 +1,18 @@ +definitions: + name: "IV Temp ELN Example" + sections: + IV_TEMP: + base_sections: + - "nomad.datamodel.metainfo.eln.NexusParser" + - "nomad.datamodel.data.EntryData" + m_annotations: + template: + reader: json_map + nxdl: IV_temp.nxdl + input_files": [ + "IV_temp.mapping.json", + "IV_temp.pickle" + ] + output: IV_temp.nxs + eln: + hide: [] diff --git a/examples/data/iv_temp/README.md b/examples/data/iv_temp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5c37f05e76b0325ca5ed941290a74d72536800bb --- /dev/null +++ b/examples/data/iv_temp/README.md @@ -0,0 +1,30 @@ + + +# Introduction + +This is an example of a PID controlled sensor sweep scan. The temperature is set using a PID controller. Then for the set temperature a voltage sweep is performed. For each of the voltages, the current is measured. This is repeated for a given list of temperatures. + +This specific data was capured using a Bluesky controlled system. This was then saved into a binary file using Pickle. This example shows how such a dataset could be converted using the JSONMapReader of the NexusParser. +The data is mapped on to a Nexus application definition for IV Temperature measurements, [NXiv_temp](https://fairmat-experimental.github.io/nexus-fairmat-proposal/50433d9039b3f33299bab338998acb5335cd8951/classes/contributed_definitions/NXiv_temp.html#nxiv-temp). + +# 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. + +# Using a Jupyter Notebook + +This example comes with a very simple Jupyter Notebook that shows how one could easily get access to a Python environment with access to all your data in one place. +To give this a go, click the **FILES** tab and select `iv_temp.ipynb`. Feel free to modify this or just create a new one to try! + +# 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 any questions about this example you may contact [Sherjeel Shabih](https://www.fairmat-nfdi.eu/fairmat/fairmat_/fairmatteam) from the FAIRmat consortium. diff --git a/examples/data/uploads/example_uploads.yml b/examples/data/uploads/example_uploads.yml index 442df6d2e47039193d572fd85bc9ed1fa7a9e3f3..72b321e4b19865c8de431da1bff9fc93a9db637e 100644 --- a/examples/data/uploads/example_uploads.yml +++ b/examples/data/uploads/example_uploads.yml @@ -22,3 +22,38 @@ tables: file in combination with a custom schema. The schema describes what the columns in the excel file mean and NOMAD can parse everything accordingly to produce a **FAIR** dataset. +ellips: + path: examples/data/uploads/ellips.zip + title: Ellipsometry + description: | + This example presents the capabilities of the NOMAD platform to store and standardize ellipsometry data. + It shows the generation of a NeXus file according to the [NXellipsometry](https://manual.nexusformat.org/classes/contributed_definitions/NXellipsometry.html#nxellipsometry) + application definition and a successive analysis of a SiO2 on Si Psi/Delta measurement. +mpes: + path: examples/data/uploads/mpes.zip + title: Mpes + description: | + This example presents the capabilities of the NOMAD platform to store and standardize multi photoemission spectroscopy (MPES) experimental data. It contains three 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). +apm: + path: examples/data/uploads/apm.zip + title: Electronic Lab Notebook for Atom Probe Microscopy (APM) + description: | + This is an example for atom probe microscopy. + The example contains a custom NOMAD *schema* to create an **Electronic + Lab Notebook (ELN)** with which users can enter metadata that are usually + not stored in vendor or community file formats. The example serves two + purposes. On the one hand it shows how custom NOMAD *schema* can be + created for a research community, here atom probe. On the other hand it + shows how all required data in a NeXus NXapm file can be added to supplement + content from vendor and community files. +iv_temp: + path: examples/data/uploads/iv_temp.zip + title: Sensor Scan - IV Temperature Curve + description: | + This example shows users how to take data from a Python framework and map it out to a Nexus application definition for IV Temperature measurements, [NXiv_temp](https://fairmat-experimental.github.io/nexus-fairmat-proposal/50433d9039b3f33299bab338998acb5335cd8951/classes/contributed_definitions/NXiv_temp.html#nxiv-temp). + We use the Nexus ELN features of Nomad to generate a Nexus file. \ No newline at end of file diff --git a/generate_sdist.sh b/generate_sdist.sh new file mode 100755 index 0000000000000000000000000000000000000000..a2c7cfa724cc362514bc997930d982e9d7b60a41 --- /dev/null +++ b/generate_sdist.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +./gitinfo.sh + +./generate_docs_artifacts.sh +mkdocs build +mkdir -p nomad/app/static/docs +cp -r site/* nomad/app/static/docs/ + + +./generate_gui_artifacts.sh +cd gui +yarn +yarn build +cd .. +mkdir -p nomad/app/static/gui +cp -r gui/build/* nomad/app/static/gui +rm nomad/app/static/gui/env.js + +rm -rf nomad_lab.egg-info dist build +python setup.py sdist \ No newline at end of file diff --git a/gui/src/components/archive/FilePreview.js b/gui/src/components/archive/FilePreview.js index 73c5c99f7f30d7f0c33f9f1eb281d0008ef99eab..abb633eb7728fb5f9337a7cb5be2776323989854 100644 --- a/gui/src/components/archive/FilePreview.js +++ b/gui/src/components/archive/FilePreview.js @@ -100,7 +100,7 @@ const viewerPDF = { } const viewerHDF5 = { name: 'hdf5', - fileExtensions: ['hdf5', 'hd5', 'nxs'], + fileExtensions: ['hdf5', 'hd5', 'nxs', 'h5', 'nexus', 'nx'], maxSizeAutoPreview: 10e6, width: 'fit-content', render: ({uploadId, path}) => diff --git a/gui/src/components/entry/OverviewView.js b/gui/src/components/entry/OverviewView.js index 286f298d53fea8ee4cd5940028da72a92175d366..72a07dc1b42cac985d727d2438a305909e7db0a5 100644 --- a/gui/src/components/entry/OverviewView.js +++ b/gui/src/components/entry/OverviewView.js @@ -173,7 +173,7 @@ const OverviewView = React.memo((props) => { return - + @@ -213,7 +213,7 @@ const OverviewView = React.memo((props) => { - + {editable && ( diff --git a/gui/tests/data/search/inputlist.json b/gui/tests/data/search/inputlist.json index 0dfc9371f20ab45dd39ec8dd4654172cbd0728b0..1097ff1934fb01e4eaec3945237a8fbc31c083ca 100644 --- a/gui/tests/data/search/inputlist.json +++ b/gui/tests/data/search/inputlist.json @@ -51,7 +51,7 @@ "page_size": 0, "order_by": "entry_id", "order": "asc", - "total": 3 + "total": 15 }, "required": { "include": [ diff --git a/gui/yarn.lock b/gui/yarn.lock index a386a8023992bcceec35eeac10e110cfeca6bc19..6953c68cb0b49341302cc77aef1e53cb437d0d02 100644 --- a/gui/yarn.lock +++ b/gui/yarn.lock @@ -4717,6 +4717,11 @@ clsx@^1.0.2, clsx@^1.0.4, clsx@^1.1.1: resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== +clsx@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -13348,6 +13353,17 @@ react-grid-layout@^1.3.4: react-draggable "^4.0.0" react-resizable "^3.0.4" +react-grid-layout@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/react-grid-layout/-/react-grid-layout-1.3.4.tgz#4fa819be24a1ba9268aa11b82d63afc4762a32ff" + integrity sha512-sB3rNhorW77HUdOjB4JkelZTdJGQKuXLl3gNg+BI8gJkTScspL1myfZzW/EM0dLEn+1eH+xW+wNqk0oIM9o7cw== + dependencies: + clsx "^1.1.1" + lodash.isequal "^4.0.0" + prop-types "^15.8.1" + react-draggable "^4.0.0" + react-resizable "^3.0.4" + react-highlight@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/react-highlight/-/react-highlight-0.14.0.tgz#5aefa5518baa580f96b68d48129d7a5d2dc0c9ef" diff --git a/nomad/app/main.py b/nomad/app/main.py index 04b2a10675f98d934153dc819d7ca817adc93b1f..5c940fc865f52e93f19a9a6033ed06301267816e 100644 --- a/nomad/app/main.py +++ b/nomad/app/main.py @@ -65,7 +65,7 @@ app.mount(f'{app_base}/optimade', optimade_app) app.mount(f'{app_base}/h5grove', h5grove_app) dist_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../dist')) -docs_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../docs/build')) +docs_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), 'static/docs')) gui_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), 'static/gui')) if not os.path.exists(gui_folder): os.makedirs(gui_folder) diff --git a/nomad/datamodel/metainfo/eln/__init__.py b/nomad/datamodel/metainfo/eln/__init__.py index 5c0acc80b1a7570d08ae738461b92d2919fb0c54..b44df23d103e53c03832d5d5f556d64571c8a8ad 100644 --- a/nomad/datamodel/metainfo/eln/__init__.py +++ b/nomad/datamodel/metainfo/eln/__init__.py @@ -16,14 +16,21 @@ # limitations under the License. # +import re import numpy as np +import yaml + from nomad import utils from nomad.units import ureg from nomad.datamodel.data import EntryData, ArchiveSection, user_reference, author_reference from nomad.metainfo.metainfo import SectionProxy from nomad.datamodel.results import ELN, Results, Material, BandGap -from nomad.metainfo import Package, Quantity, Datetime, Reference, Section from nomad.datamodel.metainfo.eln.perovskite_solar_cell_database import addSolarCell +from nomad.metainfo import Package, Quantity, Datetime, Reference, Section, MEnum + +from nexusparser.tools.dataconverter.convert import get_names_of_all_readers, convert +from nexusparser.tools.nexus import get_app_defs_names + m_package = Package(name='material_library') @@ -924,4 +931,62 @@ class SolarCellEQE(ArchiveSection): archive.results.properties.available_properties = props +class NexusParser(ArchiveSection): + + reader = Quantity( + type=MEnum(get_names_of_all_readers()), + description='The reader needed to run the Nexus converter.', + a_eln=dict(component='AutocompleteEditQuantity')) + + nxdl = Quantity( + type=MEnum(get_app_defs_names()), + description='The nxdl needed for running the Nexus converter.', + a_eln=dict(component='AutocompleteEditQuantity')) + + input_files = Quantity( + type=str, + shape=['*'], + description='Input files needed to run the nexus converter.', + a_eln=dict(component='FileEditQuantity'), + a_browser=dict(adaptor='RawFileAdaptor')) + + output = Quantity( + type=str, + description='Output Nexus filename to save all the data. Default: output.nxs', + a_eln=dict(component='StringEditQuantity'), + a_browser=dict(adaptor='RawFileAdaptor'), + default="output.nxs") + + def normalize(self, archive, logger): + super(NexusParser, self).normalize(archive, logger) + + raw_dir = archive.m_context.upload_files._raw_dir + + def transform(quantity_def, section, value, path): + if quantity_def.unit: + return dict(value=value, unit=str(format(quantity_def.unit, '~'))) + return value + + def exclude(quantity_def, section): + return quantity_def.name in ("reader", "input_files", "output", "nxdl") + + eln_dict = archive.m_to_dict(transform=transform, exclude=exclude) + del eln_dict["data"]["m_def"] + with archive.m_context.raw_file("eln_data.yaml", 'w') as eln_file: + yaml.dump(eln_dict["data"], eln_file, allow_unicode=True) + + if archive.data.input_files is None: + archive.data.input_files = [] + + converter_params = {"reader": archive.data.reader, + "nxdl": archive.data.nxdl, + "input_file": [str(raw_dir.join_file(file)) + for file in archive.data.input_files], + "output": str(raw_dir.join_file(archive.data.output))} + convert(**converter_params) + + if logger is None: + logger = utils.get_logger(__name__) + + m_package.__init_metainfo__() diff --git a/nomad/jupyterhub_config.py b/nomad/jupyterhub_config.py index c414f6f8b06e50a3debd37c4c0514028c29b4f9a..f04b92f548635d7697ddac720080284ec3aaf18b 100644 --- a/nomad/jupyterhub_config.py +++ b/nomad/jupyterhub_config.py @@ -252,9 +252,26 @@ def create_configure_from_tool_json(tool_json): spawner.cmd = tools_json['cmd'] for key, value in spawner.volumes.items(): - if value.startswith('/prefix') and 'mount_path' in tool_json: + if not isinstance(value, dict) \ + and value.startswith('/prefix') \ + and 'mount_path' in tool_json: spawner.volumes[key] = value.replace('/prefix', tool_json['mount_path']) + mounts = tool_json.get('mounts', {}) + for key, value in mounts.items(): + if isinstance(value, dict) \ + and value['bind'].startswith('/prefix') \ + and 'mount_path' in tool_json: + spawner.volumes[key] = { + 'bind': value['bind'].replace('/prefix', tool_json['mount_path']), + 'mode': value['mode'] + } + + hosts = tool_json.get('extra_hosts', {}) + spawner.extra_host_config.update({ + "extra_hosts": hosts + }) + return configure diff --git a/pyproject.toml b/pyproject.toml index 829d4b6f37c9bca3e36ac409b394f15df4f95082..3945c583ced2841a644f399b9b1d734f1827269b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,153 @@ [build-system] -requires = ["pip", "setuptools", "wheel", "fastentrypoints", "numpy", "pyyaml"] \ No newline at end of file +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = 'nomad-lab' +version = '1.1.2' +description = 'The NOvel MAterials Discovery (NOMAD) Python package' +readme = "README.md" +authors = [ + { name = "NOMAD Laboratory" }, + { email = 'markus.scheidgen@physik.hu-berlin.de' }, +] +license = { file = "LICENSE" } +requires-python = ">=3.6" +dependencies = [ + 'nptyping==1.4.4', + 'cachetools==4.2.4', + 'docstring-parser==0.12', + 'Pint==0.17', + 'orjson==3.6.0', + 'click>=7.1.2', + 'requests==2.27.1', + 'pytz==2021.1', + 'aniso8601==7.0.0', + 'ase==3.19.0', + 'python-keycloak==0.26.1', + 'elasticsearch-dsl==7.4.0', + 'pydantic==1.9.1', + 'jmespath==0.10.0', + 'h5grove==1.0.0', + 'httpx==0.22.0', + 'memoization==0.4.0', + 'lxml~=4.6', +] + +[project.urls] +homepage = "https://nomad-lab.eu/" +documentation = "https://nomad-lab.eu/prod/v1/docs" +repository = 'https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-FAIR' + +[project.optional-dependencies] +parsing = [ + 'netCDF4==1.5.4', + 'h5py==3.6.0', + 'hjson==3.0.2', + 'scipy==1.7.1', + 'scikit-learn==0.24.2', + 'matid==0.6.1', + 'python-magic==0.4.24', + 'panedr==0.2', + 'parmed==3.0.0', + 'mdtraj==1.9.6', + 'mdanalysis', + 'xarray~=0.19', + 'phonopy==2.11.0', + 'pymatgen==2022.0.17', + 'asr==0.4.1', + 'bitarray==2.3.5', + 'xrdtools==0.1.1', + 'openpyxl==3.0.9', +] +infrastructure = [ + 'optimade[mongo]==0.18.0', + 'structlog==20.1.0', + 'elasticsearch==7.17.1', + 'msgpack', + 'celery[redis]==4.4.7', + 'mongoengine', + 'pymongo==3.12.1', + # 'Werkzeug', + 'itsdangerous==2.0.1', + 'passlib==1.7.4', + 'python-logstash==0.4.6', + 'gitpython==3.1.24', + 'm2r==0.2.1', + 'zipstream-new==1.1.5', + 'bagit==1.8.1', + 'bcrypt==3.2.0', + 'filelock==3.3.1', + 'pyjwt[crypto]==1.7.1', + 'cryptography', + 'jsonschema[format]==4.1.2', + 'runstats==2.0.0', + 'pyyaml==6.0', + 'tabulate==0.8.9', + 'bs4==0.0.1', + 'html5lib==1.1', + 'zipfile37==0.1.3', + 'basicauth==0.4.1', + 'inflection==0.5.1', + 'unidecode==1.3.2', + 'python-json-logger==2.0.2', + 'recommonmark==0.7.1', + 'jinja2==3.0.3', + 'rdflib==5.0.0', + 'fastapi==0.65.3', + 'uvicorn[standard]', + 'python-multipart==0.0.5', + 'jupyterhub==1.4.2', + 'dockerspawner==12.1.0', + 'oauthenticator==14.2.0', + 'validators==0.18.2', + 'aiofiles==0.8.0', + 'joblib==1.1.0', +] +dev = [ + 'markupsafe', + 'setuptools==57.5.0', + 'gitpython==3.1.24', + 'mypy==0.730', + 'typed-ast==1.4.2', + 'astroid==2.5.1', + 'pylint==2.3.1', + 'pylint_plugin_utils==0.5', + 'pylint_mongoengine==0.3.3', + 'pycodestyle==2.8.0', + 'pytest==3.10.0', + 'pytest-timeout==1.4.2', + 'pytest-cov==2.7.1', + 'rope==0.21.0', + 'names==0.3.0', + 'essential_generators==1.0', + 'twine==3.4.2', + 'python-gitlab==2.10.1', + 'devtools==0.8.0', + 'mkdocs==1.2.3', + 'mkdocs-material==8.1.1', + 'mkdocs-material-extensions==1.0.3', + 'mkdocs-macros-plugin==0.6.3', +] + +[project.scripts] +nomad = "nomad.cli:run_cli" + +[tool.setuptools.packages.find] +where = [ + ".", + "dependencies/matid", + "dependencies/nomad-dos-fingerprints", + "dependencies/parsers/atomistic", + "dependencies/parsers/database", + "dependencies/parsers/eelsdb", + "dependencies/parsers/electronic", + "dependencies/parsers/nexus", + "dependencies/parsers/workflow", +] +exclude = ["tests*"] +namespaces = false + +[tool.setuptools.package-data] +nomad = ["**/*.json", "**/*.j2", "**/*.md", "**/*.txt", "app/static/**/*"] +nexusparser = ["definitions/**/*.xml", "definitions/**/*.xsd"] diff --git a/requirements-all.txt b/requirements-all.txt new file mode 100644 index 0000000000000000000000000000000000000000..fa212a7c39bc4e67d1b22ddd88109b80191ee1a5 --- /dev/null +++ b/requirements-all.txt @@ -0,0 +1,303 @@ +# +# This file is autogenerated by pip-compile with python 3.7 +# To update, run: +# +# pip-compile --annotation-style=line --extra=dev --extra=infrastructure --extra=parsing --output-file=requirements-all.txt dependencies/matid/setup.py dependencies/nomad-dos-fingerprints/setup.py dependencies/parsers/atomistic/requirements.txt dependencies/parsers/database/requirements.txt dependencies/parsers/eelsdb/requirements.txt dependencies/parsers/electronic/requirements.txt dependencies/parsers/nexus/requirements.txt pyproject.toml +# +aiofiles==0.8.0 # via nomad-lab (pyproject.toml) +alabaster==0.7.12 # via sphinx +alembic==1.8.1 # via jupyterhub +amqp==2.6.1 # via kombu +aniso8601==7.0.0 # via nomad-lab (pyproject.toml) +anyio==3.6.1 # via httpcore, watchfiles +arrow==1.2.3 # via isoduration +asciitree==0.3.3 # via zarr +ase==3.19.0 # via -r dependencies/parsers/nexus/requirements.txt, asr, matid, matid (dependencies/matid/setup.py), nomad-lab (pyproject.toml) +asr==0.4.1 # via nomad-lab (pyproject.toml) +astroid==2.5.1 # via -r dependencies/parsers/nexus/requirements.txt, nomad-lab (pyproject.toml), pylint +asttokens==2.0.8 # via devtools +astunparse==1.6.3 # via mdtraj +async-generator==1.10 # via jupyterhub +async-timeout==4.0.2 # via redis +atomicwrites==1.4.1 # via pytest +attrs==22.1.0 # via jsonschema, pytest +babel==2.10.3 # via sphinx +backcall==0.2.0 # via ipython +bagit==1.8.1 # via nomad-lab (pyproject.toml) +basicauth==0.4.1 # via nomad-lab (pyproject.toml) +bcrypt==3.2.0 # via nomad-lab (pyproject.toml) +beautifulsoup4==4.11.1 # via bs4 +billiard==3.6.4.0 # via celery +biopython==1.79 # via mdanalysis +bitarray==2.3.5 # via nomad-dos-fingerprints (dependencies/nomad-dos-fingerprints/setup.py), nomad-lab (pyproject.toml) +bleach==5.0.1 # via readme-renderer +bs4==0.0.1 # via nomad-lab (pyproject.toml) +cached-property==1.5.2 # via fqdn, h5py +cachetools==4.2.4 # via nomad-lab (pyproject.toml) +celery[redis]==4.4.7 # via nomad-lab (pyproject.toml) +certifi==2022.9.14 # via elasticsearch, httpcore, httpx, requests +certipy==0.1.3 # via jupyterhub +cffi==1.15.1 # via bcrypt, cryptography +cftime==1.6.2 # via netcdf4 +charset-normalizer==2.0.12 # via httpx, requests +chronic==0.3.4 # via matid +click==8.1.3 # via -r dependencies/parsers/nexus/requirements.txt, asr, flask, mkdocs, nomad-lab (pyproject.toml), uvicorn +cloudpickle==2.2.0 # via dask +colorama==0.4.5 # via twine +commonmark==0.9.1 # via recommonmark +coverage==6.4.4 # via pytest-cov +cryptography==38.0.1 # via nomad-lab (pyproject.toml), pyjwt, pyopenssl, secretstorage +cycler==0.11.0 # via matplotlib +dask[array]==2022.2.0 # via -r dependencies/parsers/nexus/requirements.txt, hyperspy +debugpy==1.6.3 # via ipykernel +decorator==5.1.1 # via ipyparallel, ipython, validators +deprecated==1.2.13 # via redis +devtools==0.8.0 # via nomad-lab (pyproject.toml) +dill==0.3.5.1 # via hyperspy +dnspython==2.2.1 # via email-validator +docker==6.0.0 # via dockerspawner +dockerspawner==12.1.0 # via nomad-lab (pyproject.toml) +docstring-parser==0.12 # via nomad-lab (pyproject.toml) +docutils==0.19 # via m2r, readme-renderer, recommonmark, sphinx +ecdsa==0.18.0 # via python-jose +elasticsearch==7.17.1 # via elasticsearch-dsl, nomad-lab (pyproject.toml) +elasticsearch-dsl==7.4.0 # via nomad-lab (pyproject.toml) +email-validator==1.3.0 # via optimade +entrypoints==0.4 # via ipyparallel, jupyter-client, jupyterhub, numcodecs +escapism==1.0.1 # via dockerspawner +essential-generators==1.0 # via nomad-lab (pyproject.toml) +et-xmlfile==1.1.0 # via openpyxl +executing==0.10.0 # via devtools +fastapi==0.65.3 # via nomad-lab (pyproject.toml), optimade +fasteners==0.18 # via zarr +filelock==3.3.1 # via nomad-lab (pyproject.toml) +flask==2.2.2 # via asr +flatdict==4.0.1 # via -r dependencies/parsers/nexus/requirements.txt +fonttools==4.37.3 # via matplotlib +fqdn==1.5.1 # via jsonschema +fsspec==2022.8.2 # via dask, hyperspy +future==0.18.2 # via matid, uncertainties +ghp-import==2.1.0 # via mkdocs +gitdb==4.0.9 # via gitpython +gitpython==3.1.24 # via nomad-lab (pyproject.toml) +greenlet==1.1.3 # via sqlalchemy +griddataformats==0.7.0 # via mdanalysis +gsd==2.6.0 # via mdanalysis +h11==0.12.0 # via httpcore, uvicorn +h5grove==1.0.0 # via nomad-lab (pyproject.toml) +h5py==3.6.0 # via -r dependencies/parsers/nexus/requirements.txt, h5grove, hyperspy, nomad-lab (pyproject.toml), phonopy +hjson==3.0.2 # via nomad-lab (pyproject.toml) +html5lib==1.1 # via nomad-lab (pyproject.toml) +httpcore==0.14.7 # via httpx +httptools==0.5.0 # via uvicorn +httpx==0.22.0 # via nomad-lab (pyproject.toml) +hyperspy==1.7.2 # via -r dependencies/parsers/nexus/requirements.txt +idna==3.4 # via anyio, email-validator, jsonschema, requests, rfc3986 +imageio==2.22.0 # via hyperspy, scikit-image +imagesize==1.4.1 # via sphinx +importlib-metadata==4.12.0 # via alembic, click, flask, hyperspy, jsonschema, keyring, kombu, mako, markdown, mkdocs, numba, pint, pluggy, prettytable, redis, sphinx, sqlalchemy, twine, xarray +importlib-resources==5.9.0 # via alembic +inflection==0.5.1 # via nomad-lab (pyproject.toml) +ipykernel==6.15.3 # via ipyparallel +ipyparallel==8.4.1 # via hyperspy +ipython==7.34.0 # via hyperspy, ipykernel, ipyparallel +isodate==0.6.1 # via rdflib +isoduration==20.11.0 # via jsonschema +isort==4.3.21 # via pylint +itsdangerous==2.0.1 # via flask, nomad-lab (pyproject.toml) +jaraco-classes==3.2.2 # via keyring +jedi==0.18.1 # via ipython +jeepney==0.8.0 # via keyring, secretstorage +jinja2==3.0.3 # via flask, hyperspy, jupyterhub, mkdocs, mkdocs-macros-plugin, mkdocs-material, nomad-lab (pyproject.toml), sphinx +jmespath==0.10.0 # via nomad-lab (pyproject.toml) +joblib==1.1.0 # via mdanalysis, nomad-lab (pyproject.toml), scikit-learn +jsonpointer==2.3 # via jsonschema +jsonschema[format]==4.1.2 # via jupyter-telemetry, nomad-lab (pyproject.toml) +jupyter-client==7.3.5 # via ipykernel, ipyparallel +jupyter-core==4.11.1 # via jupyter-client +jupyter-telemetry==0.1.0 # via jupyterhub +jupyterhub==1.4.2 # via dockerspawner, nomad-lab (pyproject.toml), oauthenticator +keyring==23.9.3 # via twine +kiwisolver==1.4.4 # via matplotlib +kombu==4.6.11 # via celery +lark-parser==0.12.0 # via optimade +lazy-object-proxy==1.7.1 # via astroid +llvmlite==0.39.1 # via numba +locket==1.0.0 # via partd +lxml==4.7.1 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, nomad-lab (pyproject.toml), xrdtools +m2r==0.2.1 # via nomad-lab (pyproject.toml) +mako==1.2.2 # via alembic +markdown==3.4.1 # via mkdocs, mkdocs-material, pymdown-extensions +markupsafe==2.1.1 # via jinja2, mako, nomad-lab (pyproject.toml), werkzeug +matid==0.6.1 # via -r dependencies/parsers/electronic/requirements.txt, nomad-lab (pyproject.toml) +matplotlib==3.5.3 # via ase, asr, hyperspy, mdanalysis, phonopy, pymatgen +matplotlib-inline==0.1.6 # via ipykernel, ipython +mccabe==0.6.1 # via pylint +mdanalysis==2.1.0 # via -r dependencies/parsers/atomistic/requirements.txt, nomad-lab (pyproject.toml) +mdtraj==1.9.6 # via nomad-lab (pyproject.toml) +memoization==0.4.0 # via nomad-lab (pyproject.toml) +mergedeep==1.3.4 # via mkdocs +mistune==2.0.4 # via m2r +mkdocs==1.2.3 # via mkdocs-macros-plugin, mkdocs-material, nomad-lab (pyproject.toml) +mkdocs-macros-plugin==0.6.3 # via nomad-lab (pyproject.toml) +mkdocs-material==8.1.1 # via nomad-lab (pyproject.toml) +mkdocs-material-extensions==1.0.3 # via mkdocs-material, nomad-lab (pyproject.toml) +mmtf-python==1.1.3 # via mdanalysis +mongoengine==0.24.2 # via nomad-lab (pyproject.toml) +mongomock==4.1.2 # via optimade +monty==2022.9.9 # via pymatgen +more-itertools==8.14.0 # via jaraco-classes, pytest +mpmath==1.2.1 # via sympy +mrcfile==1.4.2 # via griddataformats +msgpack==1.0.4 # via mmtf-python, nomad-lab (pyproject.toml) +mypy==0.730 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/database/requirements.txt, -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, nomad-lab (pyproject.toml) +mypy-extensions==0.4.3 # via mypy +names==0.3.0 # via nomad-lab (pyproject.toml) +natsort==8.2.0 # via hyperspy +nest-asyncio==1.5.5 # via ipykernel, jupyter-client +netcdf4==1.5.4 # via -r dependencies/parsers/electronic/requirements.txt, nomad-lab (pyproject.toml) +networkx==2.6.3 # via matid, matid (dependencies/matid/setup.py), mdanalysis, pymatgen, scikit-image +nptyping==1.4.4 # via nomad-lab (pyproject.toml) +numba==0.56.2 # via hyperspy, sparse +numcodecs==0.10.2 # via zarr +numexpr==2.8.3 # via hyperspy +numpy==1.21.2 # via -r dependencies/parsers/nexus/requirements.txt, ase, biopython, cftime, dask, griddataformats, gsd, h5grove, h5py, hyperspy, imageio, matid, matid (dependencies/matid/setup.py), matplotlib, mdanalysis, mdtraj, mrcfile, netcdf4, nomad-dos-fingerprints (dependencies/nomad-dos-fingerprints/setup.py), nptyping, numba, numcodecs, numexpr, pandas, phonopy, pymatgen, pywavelets, scikit-image, scikit-learn, scipy, sparse, spglib, tifffile, xarray, xrdtools, zarr +oauthenticator==14.2.0 # via nomad-lab (pyproject.toml) +oauthlib==3.2.1 # via jupyterhub +openpyxl==3.0.9 # via nomad-lab (pyproject.toml) +optimade[mongo]==0.18.0 # via nomad-lab (pyproject.toml) +orjson==3.6.0 # via h5grove, nomad-lab (pyproject.toml) +packaging==21.3 # via dask, docker, hyperspy, ipykernel, matplotlib, mdanalysis, mkdocs, mongomock, numexpr, pint, redis, scikit-image, sphinx +palettable==3.3.0 # via pymatgen +pamela==1.0.0 # via jupyterhub +pandas==1.3.5 # via -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/nexus/requirements.txt, panedr, pymatgen, xarray +panedr==0.2 # via -r dependencies/parsers/atomistic/requirements.txt, nomad-lab (pyproject.toml) +parmed==3.0.0 # via nomad-lab (pyproject.toml) +parso==0.8.3 # via jedi +partd==1.3.0 # via dask +passlib==1.7.4 # via nomad-lab (pyproject.toml) +pexpect==4.8.0 # via ipython +phonopy==2.11.0 # via asr, nomad-lab (pyproject.toml) +pickleshare==0.7.5 # via ipython +pillow==9.2.0 # via imageio, matplotlib, scikit-image +pint==0.17 # via hyperspy, nomad-lab (pyproject.toml) +pkginfo==1.8.3 # via twine +plotly==5.10.0 # via asr, pymatgen +pluggy==1.0.0 # via pytest +prettytable==3.4.1 # via hyperspy +prometheus-client==0.14.1 # via jupyterhub +prompt-toolkit==3.0.31 # via ipython +psutil==5.9.2 # via ipykernel, ipyparallel +ptyprocess==0.7.0 # via pexpect +py==1.11.0 # via pytest +pyasn1==0.4.8 # via python-jose, rsa +pycodestyle==2.8.0 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/database/requirements.txt, -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, nomad-lab (pyproject.toml) +pycparser==2.21 # via cffi +pydantic==1.9.1 # via fastapi, nomad-lab (pyproject.toml), optimade +pygments==2.13.0 # via ipython, mkdocs-material, readme-renderer, sphinx +pyjwt[crypto]==1.7.1 # via nomad-lab (pyproject.toml) +pylint==2.3.1 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/database/requirements.txt, -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, nomad-lab (pyproject.toml), pylint-mongoengine, pylint-plugin-utils +pylint-mongoengine==0.3.3 # via nomad-lab (pyproject.toml) +pylint-plugin-utils==0.5 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/database/requirements.txt, -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, nomad-lab (pyproject.toml), pylint-mongoengine +pymatgen==2022.0.17 # via asr, nomad-lab (pyproject.toml) +pymdown-extensions==9.5 # via mkdocs-material +pymongo==3.12.1 # via mongoengine, nomad-lab (pyproject.toml), optimade +pyopenssl==22.0.0 # via certipy +pyparsing==3.0.9 # via matplotlib, mdtraj, packaging, rdflib +pyrsistent==0.18.1 # via jsonschema +pytest==3.10.0 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/database/requirements.txt, -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, nomad-lab (pyproject.toml), pytest-cov, pytest-timeout +pytest-cov==2.7.1 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/database/requirements.txt, -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, nomad-lab (pyproject.toml) +pytest-timeout==1.4.2 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/database/requirements.txt, -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, nomad-lab (pyproject.toml) +python-dateutil==2.8.2 # via arrow, elasticsearch-dsl, ghp-import, hyperspy, ipyparallel, jupyter-client, jupyterhub, matplotlib, mkdocs-macros-plugin, pandas +python-dotenv==0.21.0 # via uvicorn +python-gitlab==2.10.1 # via nomad-lab (pyproject.toml) +python-jose==3.3.0 # via python-keycloak +python-json-logger==2.0.2 # via jupyter-telemetry, nomad-lab (pyproject.toml) +python-keycloak==0.26.1 # via nomad-lab (pyproject.toml) +python-logstash==0.4.6 # via nomad-lab (pyproject.toml) +python-magic==0.4.24 # via nomad-lab (pyproject.toml) +python-multipart==0.0.5 # via nomad-lab (pyproject.toml) +pytz==2021.1 # via babel, celery, nomad-lab (pyproject.toml), pandas +pywavelets==1.3.0 # via scikit-image +pyyaml==6.0 # via -r dependencies/parsers/nexus/requirements.txt, dask, hyperspy, mkdocs, mkdocs-macros-plugin, nomad-lab (pyproject.toml), phonopy, pyyaml-env-tag, uvicorn +pyyaml-env-tag==0.1 # via mkdocs +pyzmq==24.0.1 # via ipykernel, ipyparallel, jupyter-client +rdflib==5.0.0 # via nomad-lab (pyproject.toml) +readme-renderer==37.1 # via twine +recommonmark==0.7.1 # via nomad-lab (pyproject.toml) +redis==4.3.4 # via celery +requests==2.27.1 # via docker, hyperspy, jupyterhub, nomad-lab (pyproject.toml), optimade, pymatgen, python-gitlab, python-keycloak, requests-toolbelt, sphinx, twine +requests-toolbelt==0.9.1 # via python-gitlab, twine +rfc3339-validator==0.1.4 # via jsonschema +rfc3986[idna2008]==1.5.0 # via httpx, twine +rfc3987==1.3.8 # via jsonschema +rope==0.21.0 # via nomad-lab (pyproject.toml) +rsa==4.9 # via python-jose +ruamel-yaml==0.17.21 # via jupyter-telemetry, pymatgen +ruamel-yaml-clib==0.2.6 # via ruamel-yaml +runstats==2.0.0 # via nomad-lab (pyproject.toml) +scikit-image==0.19.3 # via hyperspy +scikit-learn==0.24.2 # via matid (dependencies/matid/setup.py), nomad-lab (pyproject.toml), sklearn +scipy==1.7.1 # via -r dependencies/parsers/atomistic/requirements.txt, ase, griddataformats, hyperspy, matid, matid (dependencies/matid/setup.py), mdanalysis, mdtraj, nomad-lab (pyproject.toml), pymatgen, scikit-image, scikit-learn, sparse +secretstorage==3.3.3 # via keyring +sentinels==1.0.0 # via mongomock +six==1.16.0 # via asttokens, astunparse, basicauth, bcrypt, bleach, ecdsa, elasticsearch-dsl, griddataformats, html5lib, isodate, pytest, python-dateutil, python-multipart, rdflib, rfc3339-validator, structlog, validators +sklearn==0.0 # via matid +smmap==5.0.0 # via gitdb +sniffio==1.3.0 # via anyio, httpcore, httpx +snowballstemmer==2.2.0 # via sphinx +soupsieve==2.3.2.post1 # via beautifulsoup4 +sparse==0.13.0 # via hyperspy +spglib==2.0.1 # via asr, matid, matid (dependencies/matid/setup.py), phonopy, pymatgen +sphinx==5.1.1 # via recommonmark +sphinxcontrib-applehelp==1.0.2 # via sphinx +sphinxcontrib-devhelp==1.0.2 # via sphinx +sphinxcontrib-htmlhelp==2.0.0 # via sphinx +sphinxcontrib-jsmath==1.0.1 # via sphinx +sphinxcontrib-qthelp==1.0.3 # via sphinx +sphinxcontrib-serializinghtml==1.1.5 # via sphinx +sqlalchemy==1.4.41 # via alembic, jupyterhub +starlette==0.14.2 # via fastapi +structlog==20.1.0 # via nomad-lab (pyproject.toml) +sympy==1.10.1 # via hyperspy, pymatgen +tabulate==0.8.9 # via nomad-lab (pyproject.toml), pymatgen +tenacity==8.1.0 # via plotly +termcolor==2.0.1 # via mkdocs-macros-plugin +threadpoolctl==3.1.0 # via mdanalysis, scikit-learn +tifffile==2021.11.2 # via h5grove, hyperspy, scikit-image +toolz==0.12.0 # via dask, hyperspy, partd +tornado==6.2 # via ipykernel, ipyparallel, jupyter-client, jupyterhub +tqdm==4.64.1 # via hyperspy, ipyparallel, mdanalysis, twine +traitlets==5.4.0 # via ipykernel, ipyparallel, ipython, jupyter-client, jupyter-telemetry, jupyterhub, matplotlib-inline +traits==6.4.1 # via hyperspy +twine==3.4.2 # via nomad-lab (pyproject.toml) +typed-ast==1.4.2 # via -r dependencies/parsers/nexus/requirements.txt, astroid, mypy, nomad-lab (pyproject.toml) +typing-extensions==4.3.0 # via anyio, arrow, async-timeout, gitpython, importlib-metadata, kiwisolver, mypy, numcodecs, optimade, pydantic, pymatgen, redis, uvicorn, xarray +typish==1.9.3 # via nptyping +uncertainties==3.1.7 # via pymatgen +unidecode==1.3.2 # via nomad-lab (pyproject.toml) +uri-template==1.2.0 # via jsonschema +urllib3==1.26.12 # via docker, elasticsearch, requests +uvicorn[standard]==0.18.3 # via nomad-lab (pyproject.toml) +uvloop==0.17.0 # via uvicorn +validators==0.18.2 # via nomad-lab (pyproject.toml) +vine==1.3.0 # via amqp, celery +watchdog==2.1.9 # via mkdocs +watchfiles==0.17.0 # via uvicorn +wcwidth==0.2.5 # via prettytable, prompt-toolkit +webcolors==1.12 # via jsonschema +webencodings==0.5.1 # via bleach, html5lib +websocket-client==1.4.1 # via docker +websockets==10.3 # via uvicorn +werkzeug==2.2.2 # via flask +wheel==0.37.1 # via astunparse +wrapt==1.12.1 # via astroid, deprecated +xarray==0.20.2 # via -r dependencies/parsers/nexus/requirements.txt, nomad-lab (pyproject.toml) +xrdtools==0.1.1 # via nomad-lab (pyproject.toml) +zarr==2.12.0 # via hyperspy +zipfile37==0.1.3 # via nomad-lab (pyproject.toml) +zipp==3.8.1 # via importlib-metadata, importlib-resources +zipstream-new==1.1.5 # via nomad-lab (pyproject.toml) + +# The following packages are considered to be unsafe in a requirements file: +# setuptools diff --git a/requirements.txt b/requirements.txt index dcd43781a3bcf25580c425053ed0dd6d665be612..d1789f845401719aeb8126cd2863138cdbee8310 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,237 @@ -# install -pip -wheel -pyyaml==6.0 -numpy==1.21.2 -pandas==1.3.5 -fastentrypoints==0.12 -cython>=0.19 -future==0.18.2 +# +# This file is autogenerated by pip-compile with python 3.7 +# To update, run: +# +# pip-compile --annotation-style=line --extra=infrastructure --extra=parsing --output-file=requirements.txt dependencies/matid/setup.py dependencies/nomad-dos-fingerprints/setup.py dependencies/parsers/atomistic/requirements.txt dependencies/parsers/database/requirements.txt dependencies/parsers/eelsdb/requirements.txt dependencies/parsers/electronic/requirements.txt dependencies/parsers/nexus/requirements.txt dependencies/parsers/workflow/requirements.txt pyproject.toml +# +aiofiles==0.8.0 # via nomad-lab (pyproject.toml) +alabaster==0.7.12 # via sphinx +alembic==1.8.1 # via jupyterhub +amqp==2.6.1 # via kombu +aniso8601==7.0.0 # via nomad-lab (pyproject.toml) +anyio==3.6.1 # via httpcore, watchfiles +arrow==1.2.3 # via isoduration +ase==3.19.0 # via -r dependencies/parsers/nexus/requirements.txt, asr, matid, matid (dependencies/matid/setup.py), nomad-lab (pyproject.toml) +asr==0.4.1 # via -r dependencies/parsers/workflow/requirements.txt, nomad-lab (pyproject.toml) +astroid==2.12.10 # via pylint +astunparse==1.6.3 # via mdtraj +async-generator==1.10 # via jupyterhub +async-timeout==4.0.2 # via redis +atomicwrites==1.4.1 # via pytest +attrs==22.1.0 # via jsonschema, pytest +babel==2.10.3 # via sphinx +bagit==1.8.1 # via nomad-lab (pyproject.toml) +basicauth==0.4.1 # via nomad-lab (pyproject.toml) +bcrypt==3.2.0 # via nomad-lab (pyproject.toml) +beautifulsoup4==4.11.1 # via bs4 +billiard==3.6.4.0 # via celery +biopython==1.79 # via mdanalysis +bitarray==2.3.5 # via nomad-dos-fingerprints (dependencies/nomad-dos-fingerprints/setup.py), nomad-lab (pyproject.toml) +bs4==0.0.1 # via nomad-lab (pyproject.toml) +cached-property==1.5.2 # via fqdn, h5py +cachetools==4.2.4 # via nomad-lab (pyproject.toml) +celery[redis]==4.4.7 # via nomad-lab (pyproject.toml) +certifi==2022.9.14 # via elasticsearch, httpcore, httpx, requests +certipy==0.1.3 # via jupyterhub +cffi==1.15.1 # via bcrypt, cryptography +cftime==1.6.2 # via netcdf4 +charset-normalizer==2.0.12 # via httpx, requests +chronic==0.3.4 # via matid +click==8.1.3 # via -r dependencies/parsers/nexus/requirements.txt, asr, flask, nomad-lab (pyproject.toml), uvicorn +commonmark==0.9.1 # via recommonmark +coverage==6.4.4 # via pytest-cov +cryptography==38.0.1 # via nomad-lab (pyproject.toml), pyjwt, pyopenssl +cycler==0.11.0 # via matplotlib +decorator==5.1.1 # via validators +defusedxml==0.7.1 # via odfpy +deprecated==1.2.13 # via redis +dnspython==2.2.1 # via email-validator +docker==6.0.0 # via dockerspawner +dockerspawner==12.1.0 # via nomad-lab (pyproject.toml) +docstring-parser==0.12 # via nomad-lab (pyproject.toml) +docutils==0.19 # via m2r, recommonmark, sphinx +ecdsa==0.18.0 # via python-jose +elasticsearch==7.17.1 # via elasticsearch-dsl, nomad-lab (pyproject.toml) +elasticsearch-dsl==7.4.0 # via nomad-lab (pyproject.toml) +email-validator==1.3.0 # via optimade +entrypoints==0.4 # via jupyterhub +escapism==1.0.1 # via dockerspawner +et-xmlfile==1.1.0 # via openpyxl +fastapi==0.65.3 # via nomad-lab (pyproject.toml), optimade +filelock==3.3.1 # via nomad-lab (pyproject.toml) +flask==2.2.2 # via asr +fonttools==4.37.3 # via matplotlib +fqdn==1.5.1 # via jsonschema +future==0.18.2 # via matid, uncertainties +gitdb==4.0.9 # via gitpython +gitpython==3.1.24 # via nomad-lab (pyproject.toml) +greenlet==1.1.3 # via sqlalchemy +griddataformats==0.7.0 # via mdanalysis +gsd==2.6.0 # via mdanalysis +h11==0.12.0 # via httpcore, uvicorn +h5grove==1.0.0 # via nomad-lab (pyproject.toml) +h5py==3.6.0 # via -r dependencies/parsers/nexus/requirements.txt, -r dependencies/parsers/workflow/requirements.txt, h5grove, nomad-lab (pyproject.toml), phonopy, pyscf +hjson==3.0.2 # via nomad-lab (pyproject.toml) +html5lib==1.1 # via nomad-lab (pyproject.toml) +httpcore==0.14.7 # via httpx +httptools==0.5.0 # via uvicorn +httpx==0.22.0 # via nomad-lab (pyproject.toml) +idna==3.4 # via anyio, email-validator, jsonschema, requests, rfc3986 +imagesize==1.4.1 # via sphinx +importlib-metadata==4.12.0 # via alembic, click, flask, jsonschema, kombu, mako, pint, pluggy, redis, sphinx, sqlalchemy, xarray +importlib-resources==5.9.0 # via alembic +inflection==0.5.1 # via nomad-lab (pyproject.toml) +isodate==0.6.1 # via rdflib +isoduration==20.11.0 # via jsonschema +isort==4.3.21 # via pylint +itsdangerous==2.0.1 # via flask, nomad-lab (pyproject.toml) +jinja2==3.0.3 # via flask, jupyterhub, nomad-lab (pyproject.toml), sphinx +jmespath==0.10.0 # via nomad-lab (pyproject.toml) +joblib==1.1.0 # via mdanalysis, nomad-lab (pyproject.toml), scikit-learn +jsonpointer==2.3 # via jsonschema +jsonschema[format]==4.1.2 # via jupyter-telemetry, nomad-lab (pyproject.toml) +jupyter-telemetry==0.1.0 # via jupyterhub +jupyterhub==1.4.2 # via dockerspawner, nomad-lab (pyproject.toml), oauthenticator +kiwisolver==1.4.4 # via matplotlib +kombu==4.6.11 # via celery +lark-parser==0.12.0 # via optimade +lazy-object-proxy==1.7.1 # via astroid +lxml==4.7.1 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, -r dependencies/parsers/nexus/requirements.txt, -r dependencies/parsers/workflow/requirements.txt, nomad-lab (pyproject.toml), xrdtools +m2r==0.2.1 # via nomad-lab (pyproject.toml) +mako==1.2.2 # via alembic +markupsafe==2.1.1 # via jinja2, mako, werkzeug +matid==0.6.1 # via -r dependencies/parsers/electronic/requirements.txt, nomad-lab (pyproject.toml) +matplotlib==3.5.3 # via ase, asr, mdanalysis, phonopy, pymatgen +mccabe==0.6.1 # via pylint +mdanalysis==2.1.0 # via -r dependencies/parsers/atomistic/requirements.txt, nomad-lab (pyproject.toml) +mdtraj==1.9.6 # via nomad-lab (pyproject.toml) +memoization==0.4.0 # via nomad-lab (pyproject.toml) +mistune==2.0.4 # via m2r +mmtf-python==1.1.3 # via mdanalysis +mongoengine==0.24.2 # via nomad-lab (pyproject.toml) +mongomock==4.1.2 # via optimade +monty==2022.9.9 # via pymatgen +more-itertools==8.14.0 # via pytest +mpmath==1.2.1 # via sympy +mrcfile==1.4.2 # via griddataformats +msgpack==1.0.4 # via mmtf-python, nomad-lab (pyproject.toml) +mypy==0.730 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/database/requirements.txt, -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, -r dependencies/parsers/nexus/requirements.txt, -r dependencies/parsers/workflow/requirements.txt +mypy-extensions==0.4.3 # via mypy +netcdf4==1.5.4 # via -r dependencies/parsers/electronic/requirements.txt, nomad-lab (pyproject.toml) +networkx==2.6.3 # via matid, matid (dependencies/matid/setup.py), mdanalysis, pymatgen +nptyping==1.4.4 # via nomad-lab (pyproject.toml) +numpy==1.21.2 # via -r dependencies/parsers/nexus/requirements.txt, ase, biopython, cftime, griddataformats, gsd, h5grove, h5py, matid, matid (dependencies/matid/setup.py), matplotlib, mdanalysis, mdtraj, mrcfile, netcdf4, nomad-dos-fingerprints (dependencies/nomad-dos-fingerprints/setup.py), nptyping, pandas, phonopy, pymatgen, pyscf, scikit-learn, scipy, spglib, tifffile, xarray, xrdtools +oauthenticator==14.2.0 # via nomad-lab (pyproject.toml) +oauthlib==3.2.1 # via jupyterhub +odfpy==1.4.1 # via -r dependencies/parsers/nexus/requirements.txt +openpyxl==3.0.9 # via nomad-lab (pyproject.toml) +optimade[mongo]==0.18.0 # via nomad-lab (pyproject.toml) +orjson==3.6.0 # via h5grove, nomad-lab (pyproject.toml) +packaging==21.3 # via docker, matplotlib, mdanalysis, mongomock, pint, redis, sphinx +palettable==3.3.0 # via pymatgen +pamela==1.0.0 # via jupyterhub +pandas==1.3.5 # via -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/nexus/requirements.txt, panedr, pymatgen, xarray +panedr==0.2 # via -r dependencies/parsers/atomistic/requirements.txt, nomad-lab (pyproject.toml) +parmed==3.0.0 # via nomad-lab (pyproject.toml) +passlib==1.7.4 # via nomad-lab (pyproject.toml) +phonopy==2.11.0 # via -r dependencies/parsers/workflow/requirements.txt, asr, nomad-lab (pyproject.toml) +pillow==9.2.0 # via matplotlib +pint==0.17 # via nomad-lab (pyproject.toml) +plotly==5.10.0 # via asr, pymatgen +pluggy==1.0.0 # via pytest +prometheus-client==0.14.1 # via jupyterhub +py==1.11.0 # via pytest +pyaml==21.10.1 # via -r dependencies/parsers/nexus/requirements.txt +pyasn1==0.4.8 # via python-jose, rsa +pycodestyle==2.8.0 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/database/requirements.txt, -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, -r dependencies/parsers/nexus/requirements.txt, -r dependencies/parsers/workflow/requirements.txt +pycparser==2.21 # via cffi +pydantic==1.9.1 # via fastapi, nomad-lab (pyproject.toml), optimade +pygments==2.13.0 # via sphinx +pyjwt[crypto]==1.7.1 # via nomad-lab (pyproject.toml) +pylint==2.3.1 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/database/requirements.txt, -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, -r dependencies/parsers/nexus/requirements.txt, -r dependencies/parsers/workflow/requirements.txt, pylint-plugin-utils +pylint-plugin-utils==0.5 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/database/requirements.txt, -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, -r dependencies/parsers/nexus/requirements.txt, -r dependencies/parsers/workflow/requirements.txt +pymatgen==2022.0.17 # via asr, nomad-lab (pyproject.toml) +pymongo==3.12.1 # via mongoengine, nomad-lab (pyproject.toml), optimade +pyopenssl==22.0.0 # via certipy +pyparsing==3.0.9 # via matplotlib, mdtraj, packaging, rdflib +pyrsistent==0.18.1 # via jsonschema +pyscf==2.0.1 ; sys_platform == "darwin" # via -r dependencies/parsers/electronic/requirements.txt +pytest==3.10.0 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/database/requirements.txt, -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, -r dependencies/parsers/nexus/requirements.txt, -r dependencies/parsers/workflow/requirements.txt, pytest-cov, pytest-timeout +pytest-cov==2.7.1 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/database/requirements.txt, -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, -r dependencies/parsers/nexus/requirements.txt, -r dependencies/parsers/workflow/requirements.txt +pytest-timeout==1.4.2 # via -r dependencies/parsers/atomistic/requirements.txt, -r dependencies/parsers/database/requirements.txt, -r dependencies/parsers/eelsdb/requirements.txt, -r dependencies/parsers/electronic/requirements.txt, -r dependencies/parsers/nexus/requirements.txt, -r dependencies/parsers/workflow/requirements.txt +python-dateutil==2.8.2 # via arrow, elasticsearch-dsl, jupyterhub, matplotlib, pandas +python-dotenv==0.21.0 # via uvicorn +python-jose==3.3.0 # via python-keycloak +python-json-logger==2.0.2 # via -r dependencies/parsers/nexus/requirements.txt, jupyter-telemetry, nomad-lab (pyproject.toml) +python-keycloak==0.26.1 # via nomad-lab (pyproject.toml) +python-logstash==0.4.6 # via nomad-lab (pyproject.toml) +python-magic==0.4.24 # via nomad-lab (pyproject.toml) +python-multipart==0.0.5 # via nomad-lab (pyproject.toml) +pytz==2021.1 # via babel, celery, nomad-lab (pyproject.toml), pandas +pyyaml==6.0 # via nomad-lab (pyproject.toml), phonopy, pyaml, uvicorn +rdflib==5.0.0 # via nomad-lab (pyproject.toml) +recommonmark==0.7.1 # via nomad-lab (pyproject.toml) +redis==4.3.4 # via celery +requests==2.27.1 # via docker, jupyterhub, nomad-lab (pyproject.toml), optimade, pymatgen, python-keycloak, sphinx +rfc3339-validator==0.1.4 # via jsonschema +rfc3986[idna2008]==1.5.0 # via httpx +rfc3987==1.3.8 # via jsonschema +rsa==4.9 # via python-jose +ruamel-yaml==0.17.21 # via jupyter-telemetry, pymatgen +ruamel-yaml-clib==0.2.6 # via ruamel-yaml +runstats==2.0.0 # via nomad-lab (pyproject.toml) +scikit-learn==0.24.2 # via matid (dependencies/matid/setup.py), nomad-lab (pyproject.toml), sklearn +scipy==1.7.1 # via -r dependencies/parsers/atomistic/requirements.txt, ase, griddataformats, matid, matid (dependencies/matid/setup.py), mdanalysis, mdtraj, nomad-lab (pyproject.toml), pymatgen, pyscf, scikit-learn +sentinels==1.0.0 # via mongomock +six==1.16.0 # via astunparse, basicauth, bcrypt, ecdsa, elasticsearch-dsl, griddataformats, html5lib, isodate, pytest, python-dateutil, python-multipart, rdflib, rfc3339-validator, structlog, validators +sklearn==0.0 # via matid +smmap==5.0.0 # via gitdb +sniffio==1.3.0 # via anyio, httpcore, httpx +snowballstemmer==2.2.0 # via sphinx +soupsieve==2.3.2.post1 # via beautifulsoup4 +spglib==2.0.1 # via asr, matid, matid (dependencies/matid/setup.py), phonopy, pymatgen +sphinx==5.1.1 # via recommonmark +sphinxcontrib-applehelp==1.0.2 # via sphinx +sphinxcontrib-devhelp==1.0.2 # via sphinx +sphinxcontrib-htmlhelp==2.0.0 # via sphinx +sphinxcontrib-jsmath==1.0.1 # via sphinx +sphinxcontrib-qthelp==1.0.3 # via sphinx +sphinxcontrib-serializinghtml==1.1.5 # via sphinx +sqlalchemy==1.4.41 # via alembic, jupyterhub +starlette==0.14.2 # via fastapi +structlog==20.1.0 # via nomad-lab (pyproject.toml) +sympy==1.10.1 # via pymatgen +tabulate==0.8.9 # via nomad-lab (pyproject.toml), pymatgen +tenacity==8.1.0 # via plotly +threadpoolctl==3.1.0 # via mdanalysis, scikit-learn +tifffile==2021.11.2 # via h5grove +tornado==6.2 # via jupyterhub +tqdm==4.64.1 # via mdanalysis +traitlets==5.4.0 # via jupyter-telemetry, jupyterhub +typed-ast==1.4.3 # via astroid, mypy +typing-extensions==4.3.0 # via anyio, arrow, astroid, async-timeout, gitpython, importlib-metadata, kiwisolver, mypy, optimade, pydantic, pymatgen, redis, uvicorn, xarray +typish==1.9.3 # via nptyping +uncertainties==3.1.7 # via pymatgen +unidecode==1.3.2 # via nomad-lab (pyproject.toml) +uri-template==1.2.0 # via jsonschema +urllib3==1.26.12 # via docker, elasticsearch, requests +uvicorn[standard]==0.18.3 # via nomad-lab (pyproject.toml) +uvloop==0.17.0 # via uvicorn +validators==0.18.2 # via nomad-lab (pyproject.toml) +vine==1.3.0 # via amqp, celery +watchfiles==0.17.0 # via uvicorn +webcolors==1.12 # via jsonschema +webencodings==0.5.1 # via html5lib +websocket-client==1.4.1 # via docker +websockets==10.3 # via uvicorn +werkzeug==2.2.2 # via flask +wheel==0.37.1 # via astunparse +wrapt==1.14.1 # via astroid, deprecated +xarray==0.20.2 # via -r dependencies/parsers/nexus/requirements.txt, -r dependencies/parsers/workflow/requirements.txt, nomad-lab (pyproject.toml) +xrdtools==0.1.1 # via nomad-lab (pyproject.toml) +zipfile37==0.1.3 # via nomad-lab (pyproject.toml) +zipp==3.8.1 # via importlib-metadata, importlib-resources +zipstream-new==1.1.5 # via nomad-lab (pyproject.toml) # nomad nptyping==1.4.4 @@ -119,4 +344,4 @@ markupsafe==2.0.1 mkdocs==1.2.3 mkdocs-material==8.1.1 mkdocs-material-extensions==1.0.3 -mkdocs-macros-plugin==0.6.3 \ No newline at end of file +mkdocs-macros-plugin==0.6.3 diff --git a/setup.py b/setup.py index cffd58698c611b8674cc7d3b1051703f9814c3f0..606849326a4002007fd42060b51e69a19c18675c 100644 --- a/setup.py +++ b/setup.py @@ -1,292 +1,3 @@ -# -# 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. -# +from setuptools import setup -from setuptools import setup, find_packages -from subprocess import call -from setuptools.command.install import install as setup_install -import runpy -import os -import sys -import json -import re -import fastentrypoints # pylint: disable=unused-import - -''' -This setup.py works differently for creating a distribution than installing for -development. The idea is that for distributions, we compile the requirements, packages, and -data files from all dependencies and install everything under one package called 'nomad-lab'. -For development we install each dependency as its own project. This is mainly due to -pip's limitation of not being able to install from multiple source folders in develop -mode. - -The compilation process can by run with ``python setup.py compile``. It will create a -file with setup kwargs calles ``./setup.json``. If this file exists setup will be called -with compiled args. If this file does not exist, setup will be called with regular args. - -This setup makes use of ``extras_require``. The extras are parsing, infrastructure, dev, -and all. Where all is the union of the other ones. The extras are parsed from the -requirements.txt where specific comments are used to assign an extra to requirements. -''' - - -ignore_extra_requires = ['optimade'] -''' Dependencies where the extra_requires should not be added ''' - - -def parse_requirements(): - ''' - Parses the requirements.txt file to extras install and extra requirements. - Sections headed with # [extra] are assigned to the 'extra' extra. - - Returns: - Tuple with install and extra requires passible to :func:`setuptools.setup`. - ''' - with open('requirements.txt', 'rt') as f: - lines = f.readlines() - - extras_require = {} - requires = [] - all_requires = [] - current = None - for line in lines: - line = line.strip() - - if line == '': - continue - - match = re.match(r'^#\s*\[([a-zA-Z0-9_]+)\]$', line) - if match: - extra = match.group(1) - current = list() - extras_require[extra] = current - elif line.startswith('#'): - continue - else: - line = line.split('#')[0].strip() - if current is None: - requires.append(line) - else: - current.append(line) - all_requires.append(line) - - extras_require['all'] = all_requires - - return requires, extras_require - - -class install(setup_install): - def __post_install(self, dir): - if os.name == 'posix': - call(['./auto_complete_install.sh']) - - def run(self): - setup_install.run(self) - self.execute(self.__post_install, (self.install_lib, ), msg='installing autocompletion') - - -def compile_dependency_setup_kwargs(paths, **kwargs): - import setuptools - import distutils.core as distutils_core - - # collect all kwargs from all setup.pys - results = {} - current = {} - - def patched_setup(*args, **kwargs): - assert len(args) == 0 - assert current['name'] not in results, 'current is %s' % current['name'] - - results[current['name']] = { - 'meta': dict(**current), - 'kwargs': kwargs - } - - if len(kwargs) > 0: - current['name'] = kwargs.get('name', 'nomad') - current['directory'] = './' - current['setup.py'] = './setup.py' - patched_setup(**kwargs) - - setuptoolss = [setuptools, distutils_core] - orig_setups = [] - for st in setuptoolss: - orig_setups.append((st, getattr(st, 'setup'))) - setattr(st, 'setup', patched_setup) - - for path in paths: - for root, _, files in os.walk(path): - for file in files: - if os.path.basename(file) == 'setup.py': - setup_path = os.path.join(root, file) - current['name'] = os.path.basename(os.path.dirname(setup_path)) - current['directory'] = os.path.dirname(setup_path) - current['setup.py'] = setup_path - cwd = os.getcwd() - os.chdir(os.path.dirname(setup_path)) - try: - runpy.run_path(file, run_name='__main__') - except Exception: - import traceback - traceback.print_exc() - print('Could not run %s' % setup_path) - sys.exit(1) - finally: - os.chdir(cwd) - - for st, setup in orig_setups: - setattr(st, 'setup', setup) - - # combine the kwargs - all_packages = [] - all_package_dir = {} - all_package_data = {} - all_install_requires = {} - all_names = set() - for _, setup_data in results.items(): - meta = setup_data['meta'] - local_kwargs = setup_data['kwargs'] - - if 'name' in local_kwargs: - all_names.add(local_kwargs['name']) - - name = local_kwargs.get('name', meta['name']) - - # 1. packages, package_dir - package_dir = local_kwargs.get('package_dir', {'': './'}) - packages = local_kwargs.get('packages', []) - - assert len(package_dir) == 1 - assert '' in package_dir - - for package in packages: - root = package.split('.')[0] - if root not in package_dir and root not in all_package_dir: - all_package_dir[root] = os.path.normpath( - os.path.join(meta['directory'], package_dir[''], root)) - all_packages.append(package) - - # 2. package_data - all_package_data.update(**local_kwargs.get('package_data', {})) - - # 3. requires - local_install_requires = set() - if name not in ignore_extra_requires: - for extra_require in local_kwargs.get('extras_require', {}).values(): - for require in extra_require: - local_install_requires.add(require) - - for require in local_kwargs.get('install_requires', []): - local_install_requires.add(require) - all_install_requires[name] = local_install_requires - - # automatically add parser deps - for _, setup_data in results.items(): - if 'parsers' in setup_data['meta']['setup.py']: - parsing = kwargs.setdefault('extras_require', {}).setdefault('parsing', []) - all = kwargs.setdefault('extras_require', {}).setdefault('all', []) - for require in setup_data['kwargs'].get('install_requires', []): - if require not in parsing: - parsing.append(require) - all.append(require) - - def replace_own_packages(requires): - ''' replaces nomad dependencies with their requirements ''' - for other in all_names: - if other in requires: - requires.remove(other) - requires.extend(all_install_requires[other]) - - # remove dups - sorted_requires = sorted(requires) - sorted_normalized = [re.match(r'^([a-zA-Z0-9_\.\-]*).*$', r).group(1) for r in sorted_requires] - to_remove = [] - for i, require in enumerate(sorted_normalized): - if i + 1 < len(sorted_requires) and require == sorted_normalized[i + 1]: - to_remove.append(i) - for i in to_remove: - requires.remove(sorted_requires[i]) - - # run twice because dependencies can be dependencies of dependencies - for _ in range(0, 2): - for extra_require in kwargs.get('extras_require', {}).values(): - replace_own_packages(extra_require) - replace_own_packages(kwargs.get('install_requires', [])) - - kwargs.update(**{ - 'package_dir': all_package_dir, - 'packages': all_packages, - 'package_data': all_package_data - }) - - return kwargs - - -def setup_kwargs(): - from nomad import config - - install_requires, extras_require = parse_requirements() - with open("README.md", "r") as fh: - long_description = fh.read() - - return dict( - name='nomad-lab', - author='NOMAD Laboratory', - author_email='markus.scheidgen@physik.hu-berlin.de', - url='https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-FAIR', - version=config.meta.version, - license='APACHE 2.0', - description='The NOvel MAterials Discovery (NOMAD) Python package', - long_description=long_description, - long_description_content_type="text/markdown", - package_dir={'': './'}, - packages=['nomad.%s' % pkg for pkg in find_packages('./nomad')] + ['nomad'], - setup_requires=['pip', 'setuptools', 'wheel', 'fastentrypoints', 'numpy', 'pyyaml'], - install_requires=install_requires, - extras_require=extras_require, - include_package_data=True, - python_requires='>=3.6', - entry_points={ - 'console_scripts': [ - 'nomad = nomad.cli:run_cli' - ] - } - ) - - -if __name__ == '__main__': - if len(sys.argv) == 2 and sys.argv[1] == 'compile': - kwargs = compile_dependency_setup_kwargs(['dependencies'], **setup_kwargs()) - kwargs['package_data']['optimade.grammar'] = ['*.lark'] - with open('setup.json', 'wt') as f: - json.dump(kwargs, f, indent=2) - sys.exit(0) - - if len(sys.argv) == 2 and sys.argv[1] == 'dry': - import pprint - pprint.pprint( - compile_dependency_setup_kwargs(['dependencies'], **setup_kwargs())) - sys.exit(0) - - if os.path.exists('setup.json'): - with open('setup.json', 'rt') as f: - kwargs = json.load(f) - - else: - kwargs = setup_kwargs() - - setup(cmdclass={'install': install}, **kwargs) +setup() diff --git a/setup.sh b/setup.sh index cfc1a0e82ede990cf0599649b6d90357ef2dbc5d..ba7eb51669983f56637672b4b43af081134e1d79 100755 --- a/setup.sh +++ b/setup.sh @@ -7,8 +7,11 @@ pip install --upgrade pip git submodule sync --recursive sleep 5 +# Install all reqs +pip install -r requirements-all.txt + # Install nomad -pip install -e .[all] +pip install --no-deps -e . # Install sub-modules git submodule update --init --recursive --jobs=4