diff --git a/nomad/datamodel/metainfo/__init__.py b/nomad/datamodel/metainfo/__init__.py index fc2a91d583e66578c3029d85d054d6c597ea0eeb..71b4badd91dd18ae3f173678bfbc1078f7a48862 100644 --- a/nomad/datamodel/metainfo/__init__.py +++ b/nomad/datamodel/metainfo/__init__.py @@ -17,3 +17,6 @@ # from .simulation import m_env from .material_library import m_package + +from .apm import m_package +from .em_nion import m_package diff --git a/nomad/datamodel/metainfo/apm/__init__.py b/nomad/datamodel/metainfo/apm/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e67becad427fde7e33671f41850ba22be00667dc --- /dev/null +++ b/nomad/datamodel/metainfo/apm/__init__.py @@ -0,0 +1,529 @@ +# +# 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 matplotlib.afm import CharMetrics +import numpy as np +import os + +from nomad.units import ureg +from nomad.metainfo import ( + MSection, Package, Quantity, SubSection, MEnum, Datetime, Section) +from nomad.datamodel.data import EntryData + + +m_package = Package(name='atom_probe_microscopy') + + +class Reflectron(MSection): + """Details about a reflectron device at the instrument.""" + + has_reflectron = Quantity( + type=bool, + description='''Does the instrument have a reflectron?''', + a_eln=dict(component='BoolEditQuantity')) + + # watch out for auto-translated keywords which become py reserved keywords + # nexus_string prefixing, like with nx_<<quantity_name>> comes to rescue + name = Quantity( + type=str, + description='''Name of the reflectron.''', + a_eln=dict(component='StringEditQuantity')) + # keyword "name" must not be used except as keyword nomad + + model = Quantity( + type=str, + description='''Reflectron model.''', + a_eln=dict(component='StringEditQuantity')) + + serial_number = Quantity( + type=str, + description='''Reflectron serial number.''', + a_eln=dict(component='StringEditQuantity')) + + manufacturer_name = Quantity( + type=str, + description='''Reflectron manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + +class Detector(MSection): + """Details about the detector.""" + + description = Quantity( + type=str, + description=''' + An (ideally globally persistent) unique identifier, link, or text + which gives further details.''', + a_eln=dict(component='StringEditQuantity')) + + detector_type = Quantity( + type=str, + description=''' + Description of the detector type. Specify if the detector is not + the usual type of delay-line detectors.''', + a_eln=dict(component='StringEditQuantity')) + + +class Pulser(MSection): + """Details about the pulser (laser/high-voltage).""" + + name = Quantity( + type=str, + description='''Given name.''', + a_eln=dict(component='StringEditQuantity')) + + model = Quantity( + type=str, + description='''Given brand or model name by the manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + serial_number = Quantity( + type=str, + description=''' + Given hardware name/serial number or hash identifier issued by + the manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + manufacturer_name = Quantity( + type=str, + description='''Given name of the manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + pulse_mode = Quantity( + type=MEnum([ + 'laser', + 'high_voltage', + 'laser_and_high_voltage']), + shape=[], + description='''Which type of pulsing mode.''', + a_eln=dict(component='RadioEnumEditQuantity')) + + pulse_fraction = Quantity( + type=np.dtype(np.float64), + description='''Average pulse fraction.''', + a_eln=dict(component='NumberEditQuantity')) + + _pulse_fraction_units = Quantity( + type=str, + description='''SI unit used for pulse fraction.''', + a_eln=dict(component='StringEditQuantity')) + + +class Stage(MSection): + """Details about the stage.""" + + design = Quantity( + type=str, + description='''Principal design of the stage.''', + a_eln=dict(component='StringEditQuantity')) + + name = Quantity( + type=str, + description='''Name of the stage''', + a_eln=dict(component='StringEditQuantity')) + + model = Quantity( + type=str, + description='''Type of the stage''', + a_eln=dict(component='StringEditQuantity')) + + serial_number = Quantity( + type=str, + description=''' + Serial number of the stage.''', + a_eln=dict(component='StringEditQuantity')) + + manufacturer_name = Quantity( + type=str, + description='''Given name of the stage manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + +class InstrumentControl(MSection): + """Details relevant for the control of the instrument.""" + + base_temperature = Quantity( + type=np.dtype(np.float64), + unit='kelvin', + description=''' + Average temperature at the specimen base, i.e. base temperature, + during the measurement.''', + a_eln=dict(component='NumberEditQuantity')) + + _base_temperature_units = Quantity( + type=str, + description=''' + Unit used for the average temperature at the specimen base.''', + a_eln=dict(component='StringEditQuantity')) + + pressure = Quantity( + type=np.dtype(np.float64), + unit='pascal', + description='''Average pressure in the analysis chamber.''', + a_eln=dict(component='NumberEditQuantity')) + + _pressure_units = Quantity( + type=str, + description='''Average pressure in the analysis chamber.''', + a_eln=dict(component='StringEditQuantity')) + + program = Quantity( + type=str, + description=''' + Given name of the program that was used to control the experiment.''', + a_eln=dict(component='StringEditQuantity')) + + _program_version = Quantity( + type=str, + description=''' + Ideally program version plus build number, or commit hash or + description of ever persistent resources where the source code of the + program and build instructions can be found so that the program can + be configured ideally in such a manner that the result of this + computational process is recreatable + in the same deterministic manner.''', + a_eln=dict(component='StringEditQuantity')) + + +class Instrument(MSection): + """Details about the atom probe microscope, components, and software.""" + + name = Quantity( + type=str, + description=''' + Given name of the microscope, e.g Oxcart, One atom at a time.''', + a_eln=dict(component='StringEditQuantity')) + + location = Quantity( + type=str, + description=''' + Geographic coordinates of the lab or the place where the instrument + is installed using GEOREF geocodes ideally.''', + a_eln=dict(component='StringEditQuantity')) + + instrument_manufacturer = Quantity( + type=str, + description=''' + Manufacturer of the entire instrument (e.g. AMETEK/Cameca) to enable + e.g. queries in materials database systems for instrument + manufacturers. Usually more technical details are needed though to + specify the instrument, these should be written into instrument_model + and instrument_capabilities.''', + a_eln=dict(component='StringEditQuantity')) + + instrument_model = Quantity( + type=str, + description=''' + Manufacturer brand/model to enable e.g. queries about + microscope models (e.g. LEAP3000XS).''', + a_eln=dict(component='StringEditQuantity')) + + instrument_identifier = Quantity( + type=str, + description=''' + Hardware name/serial number or hash identifier given by the + manufacturer to identify the instrument.''', + a_eln=dict(component='StringEditQuantity')) + + instrument_capability = Quantity( + type=str, + description='''Free-text list possibly multiple terms of + functionalities which the instrument provides.''', + a_eln=dict(component='StringEditQuantity')) + + flight_path_length = Quantity( + type=np.dtype(np.float64), + unit='meter', + description=''' + The space inside the atom probe that ions pass through when they + leave the specimen and travel to the detector.''', + a_eln=dict(component='NumberEditQuantity')) + + _flight_path_length_units = Quantity( + type=str, + description=''' + The space inside the atom probe that ions pass through when they + leave the specimen and travel to the detector.''', + a_eln=dict(component='StringEditQuantity')) + + reflectron = SubSection(section_def=Reflectron) + detector = SubSection(section_def=Detector) + pulser = SubSection(section_def=Pulser) + control = SubSection(section_def=InstrumentControl) + + +class Operator(MSection): + """Contact information of at least the user of the instrument. + + Or the investigator who performed this experiment. + Adding multiple users if relevant is recommended. + """ + + m_def = Section(a_eln=dict()) + + name = Quantity( + type=str, + description=''' + Given (first) name and surname of the user.''', + a_eln=dict(component='StringEditQuantity')) + + affiliation = Quantity( + type=str, + description=''' + Name of the affiliation of the user at the point in time + when the experiment was performed.''', + a_eln=dict(component='StringEditQuantity')) + + address = Quantity( + type=str, + description='''Postal address of the affiliation.''', + a_eln=dict(component='StringEditQuantity')) + + email = Quantity( + type=str, + description=''' + Email address of the user at the point in time + when the experiment was performed. + Giving the most permanently used email is recommended.''', + a_eln=dict(component='StringEditQuantity')) + + orcid = Quantity( + type=str, + description=''' + Globally unique identifier of the user as offers + by services like ORCID or Researcher ID.''', + a_eln=dict(component='StringEditQuantity')) + + telephone_number = Quantity( + type=str, + description=''' + (Business) (tele)phone number of the user at the point + in time when the experiment was performed.''', + a_eln=dict(component='StringEditQuantity')) + + role = Quantity( + type=str, + description=''' + Which role does the user have in the place, and at the point in time + when, the experiment was performed (e.g. technician operating the + microscope, student, postdoc, principal investigator, guest ...)''', + a_eln=dict(component='StringEditQuantity')) + + +class Sample(MSection): + """Properties of the specimen, its history.""" + + name = Quantity( + type=str, + description=''' + Descriptive name or identifier with which to distinguish the specimen + from all others and especially the parts from where it 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. The user is + advised to store the details how specimens were cut/prepared from + samples in the sample history.''', + a_eln=dict(component='StringEditQuantity')) + + sample_history = Quantity( + type=str, + description=''' + Ideally, a reference to the location of or a (globally persistent) + unique identifier of e.g. another file which should document ideally + as many details as possible of the material, its microstructure, and + its thermo-chemo-mechanical processing/preparation history. + In the case that such a detailed history of the sample/specimen is not + available, use this field as a free-text description to specify a sub- + set of the entire sample history, i.e. what you would consider being + the key steps and relevant information about the specimen, its + material, microstructure, thermo-chemo-mechanical processing state, + and details of the preparation.''', + a_eln=dict(component='StringEditQuantity')) + + preparation_date = Quantity( + type=Datetime, + description=''' + ISO8601 date and time with local time zone offset to UTC included + when the specimen was prepared. Ideally report the end of the + preparation, i.e. the last known time the measured specimen surface + was actively prepared. Knowing when the specimen was exposed to e.g. + specific atmosphere is especially required for environmentally + sensitive material such as hydrogen-charged specimens or experiments + including tracers with a short half-time. The user is advised to + include these temporal details in the sample_history.''', + a_eln=dict(component='DateTimeEditQuantity')) + + atom_types = Quantity( + type=str, + 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 + considered relevant from a scientific point. The purpose of the field + is to offer materials database systems an opportunity to parse the + relevant elements without having to interpret these + from the sample history.''', + a_eln=dict(component='StringEditQuantity')) + + +class AtomProbeMicroscopy(EntryData): + """Application definition describing atom probe experiments. + + The here specified ELN is a proof-of-concept whose scope is to provide + a mechanism to inject additional pieces of information to create a NeXus + file that matches a: + - particular version of the NXapm.nxdl.xml appdef + - https://github.com/FAIRmat-Experimental/nexus_definitions/commit/ + - 1d46b94dc36a42225ad247cc763277a47dcd30d4 + - working with experiments as a pair of APT/RNG or APT/RRNG files, + - specifically with these file formats being interpretable as with the + - following so-called apm dataconverter (see commit below). + The ELN returns a json file that is to be used to inject the + here collected pieces of information in a + - specifically-versioned dataconverter apm + - https://github.com/nomad-coe/nomad-parser-nexus/commit/ + - 47bc4f9724b8e980bd41d0fca3a663c0b79a7fb1 + """ + + start_time = Quantity( + type=Datetime, + description=''' + ISO 8601 formatted time code with local time zone offset to UTC + information included when the experiment started. + If the application demands that time codes in this section of the + application definition should only be used for specifying when the + experiment was performed - and the exact duration is not relevant + - this start time field should be used. Often though it is useful to + specify a time interval with specifying both start_time and end_time + to allow for more detailed bookkeeping and interpretation of the + experiment. The user should be aware that even with having both time + instances specified it may not be advisable to infer how long the + experiment took or for how long data were acquired. More detailed + timing data over the course of the experiment have to be collected.''', + a_eln=dict(component='DateTimeEditQuantity')) + + end_time = Quantity( + type=Datetime, + description=''' + ISO 8601-formatted time code with local time zone offset to UTC + included when the experiment ended.''', + a_eln=dict(component='DateTimeEditQuantity')) + + run_number = Quantity( + type=str, + description=''' + Not the specimen name or the experiment identifier but the identifier + through which the experiment is referred to in the control software. + For LEAP instruments it is recommended to use the IVAS/AP Suite run + number. For other instruments, such as the one from Stuttgart or Oxcart + from Erlangen, or the instruments in Rouen, use the identifier that is + closest in meaning to the LEAP run number. As a destructive microscopy + method, a run can be performed only once. It is possible, however, + to interrupt a run and restart, still all using the same specimen. + In this case, each evaporation run needs to be distinguished with + different run numbers. This is how most atom probe groups handle + it across the globe.''', + a_eln=dict(component='StringEditQuantity')) + + operation_mode = Quantity( + type=MEnum([ + 'leap', + 'apt', + 'fim', + 'apt_and_fim']), + shape=[], + description=''' + What type of atom probe microscope experiment is performed. + This field can be used e.g. by materials database systems to + qualitatively filter experiments.''', + a_eln=dict(component='RadioEnumEditQuantity')) + + sample = SubSection(section_def=Sample) + operator = SubSection(section_def=Operator, repeats=True) + instrument = SubSection(section_def=Instrument) + + reconstruction_file = Quantity( + type=str, + description=''' + Vendor/community-specific file (APT, POS, ePOS are supported) + which contains reconstructed ion positions and other data.''', + a_eln=dict(component='FileEditQuantity'), + a_browser=dict(adaptor='RawFileAdaptor')) + + ranging_file = Quantity( + type=str, + description=''' + Vendor/community-specific file (RNG, RRNG are supported) + which contains ranging definitions.''', + a_eln=dict(component='FileEditQuantity'), + a_browser=dict(adaptor='RawFileAdaptor')) + + # maybe a database dump file from IVAS/APSuite + + def normalize(self, archive, logger): + """Normalize data.""" + # store content of the above ELN entries into a dictionary + # data = self.m_to_dict() + + # map paths in this dictionary on paths as expected by apm reader + # for instance by storing flattened data key value pairs in a json + # file + + # remains to be implemented + +# runner = CliRunner() +# converter_args = ['--input-file', self.reconstruction_file, +# '--input-file', self.ranging_file, +# '--input-file', +# os.path.join(filepath, 'eln.apm.json'), +# '--reader', 'apm', +# '--nxdl', 'NXapm', +# '--output', os.path.join(filepath, \ +# 'apm.test.nxs')] +# runner.invoke(convert, converter_args) + + pass + +# ============================================================================= +# import yaml +# from nomad.datamodel.metainfo.ellipsometry.dict_flattener \ +# import dict_flatten, dict_key_cut +# from nexusparser.tools.dataconverter.convert import convert +# from click.testing import CliRunner +# +# data = self.m_to_dict() +# data.update(data.pop('data_file')) +# dict_flatten(data) +# dict_key_cut(data) +# +# kl = list(data.keys()) +# for k in kl: +# if '/_' in k: +# data[k.replace('/_', r"/\@")] = data.pop(k) +# dict_key_cut(data, '/', 1, '@') +# data['definition'] = 'NXellipsometry' +# data[r"definition/\@version"] = '0.0.1' +# data[r"definition/\@url"] = 'https://' +# +# with archive.m_context.raw_file('eln.apm.json', 'w') as outfile: +# yaml.dump(data, outfile, default_flow_style=False) +# filepath = os.path.dirname(outfile.name) +# ============================================================================= + + +m_package.__init_metainfo__() diff --git a/nomad/datamodel/metainfo/em_nion/__init__.py b/nomad/datamodel/metainfo/em_nion/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9ba4fd24a00f1d5b087c56b59c2e0787e97e3d50 --- /dev/null +++ b/nomad/datamodel/metainfo/em_nion/__init__.py @@ -0,0 +1,599 @@ +# +# 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 matplotlib.afm import CharMetrics +import numpy as np +import os + +from nomad.units import ureg +from nomad.metainfo import ( + MSection, Package, Quantity, SubSection, MEnum, Datetime, Section) +from nomad.datamodel.data import EntryData + + +m_package = Package(name='electron_microscopy_nion') + + +class ElectronGun(MSection): + """Details the electron gun.""" + + emitter_type = Quantity( + type=str, + description=''' + Emitter type used to create the beam.''', + a_eln=dict(component='StringEditQuantity')) + + description = Quantity( + type=str, + description=''' + Ideally a reference to (another) file (ideally formatted using also an + an application definition) via a link, name, or a (globally persistent) + unique identifier to give further details about the electron gun.''', + a_eln=dict(component='StringEditQuantity')) + + name = Quantity( + type=str, + description='''Given name.''', + a_eln=dict(component='StringEditQuantity')) + + model = Quantity( + type=str, + description='''Given brand or model name by the manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + serial_number = Quantity( + type=str, + description=''' + Given hardware name/serial number or hash identifier issued by the + manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + +class Aperture(MSection): + """Details about an aperture.""" + + name = Quantity( + type=str, + description='''Given name.''', + a_eln=dict(component='StringEditQuantity')) + + model = Quantity( + type=str, + description='''Given brand or model name by the manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + serial_number = Quantity( + type=str, + description=''' + Given hardware name/serial number or hash identifier issued by the + manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + manufacturer_name = Quantity( + type=str, + description='''Given name of the manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + value = Quantity( + type=np.dtype(np.float64), + description=''' + Relevant value from the control software as this is not always just + the diameter of (not even in the case) of a circular aperture but a + rather a value from a list of predefined ones in the control software. + Which actual settings are behind these should be Details which choice + was made should be explained under description.''', + a_eln=dict(component='NumberEditQuantity')) + + _value_units = Quantity( + type=str, + description=''' + Relevant value from the control software as this is not always just + the diameter of (not even in the case) of a circular aperture but + a rather a value from a list of predefined ones in the control + software. Which actual settings are behind these should be detailed and + which choices were made should be explained under description.''', + a_eln=dict(component='StringEditQuantity')) + + description = Quantity( + type=str, + description=''' + An (ideally globally persistent) unique identifier, link, or text + which gives further details.''', + a_eln=dict(component='StringEditQuantity')) + + +class Lens(MSection): + """Details about a lens.""" + + name = Quantity( + type=str, + description='''Given name.''', + a_eln=dict(component='StringEditQuantity')) + + model = Quantity( + type=str, + description='''Given brand or model name by the manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + serial_number = Quantity( + type=str, description='''Given hardware name/serial number or + hash identifier issued by the manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + manufacturer_name = Quantity( + type=str, description='''Given name of the manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + +class Detector(MSection): + """Details about the detector.""" + + name = Quantity( + type=str, + description='''Given name.''', + a_eln=dict(component='StringEditQuantity')) + + model = Quantity( + type=str, + description='''Given brand or model name by the manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + serial_number = Quantity( + type=str, description=''' + Given hardware name/serial number or hash identifier issued + by the manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + manufacturer_name = Quantity( + type=str, + description='''Given name of the manufacturer.''', + a_eln=dict(component='StringEditQuantity')) + + sensor_material = Quantity( + type=str, + description=''' ''', + a_eln=dict(component='StringEditQuantity')) + + bit_depth_readout = Quantity( + type=np.dtype(np.int64), + description=''' ''', + a_eln=dict(component='NumberEditQuantity')) + + _bit_depth_readout_units = Quantity( + type=str, + description=''' ''', + a_eln=dict(component='StringEditQuantity')) + + number_of_cycles = Quantity( + type=np.dtype(np.int64), + description=''' ''', + a_eln=dict(component='NumberEditQuantity')) + + _number_of_cycles_units = Quantity( + type=str, + description=''' ''', + a_eln=dict(component='StringEditQuantity')) + + x_pixel_size = Quantity( + type=np.dtype(np.float64), + unit='meter', + description='''Physical distance represented by + the edge of the pixel along the x direction.''', + a_eln=dict(component='NumberEditQuantity')) + + _x_pixel_size_units = Quantity( + type=str, + description=''' ''', + a_eln=dict(component='StringEditQuantity')) + + y_pixel_size = Quantity( + type=np.dtype(np.float64), + unit='meter', + description='''Physical distance represented by + the edge of the pixel along the y direction.''', + a_eln=dict(component='NumberEditQuantity')) + + _y_pixel_size_units = Quantity( + type=str, + description=''' ''', + a_eln=dict(component='StringEditQuantity')) + + flatfield_applied = Quantity( + type=bool, + description=''' ''', + a_eln=dict(component='BoolEditQuantity')) + + flatfield = Quantity( + type=np.dtype(np.float64), + description=''' ''', + a_eln=dict(component='NumberEditQuantity')) + + _flatfield_units = Quantity( + type=str, + description=''' ''', + a_eln=dict(component='StringEditQuantity')) + + description = Quantity( + type=str, + description=''' + Free text option to write further details about the detector.''', + a_eln=dict(component='StringEditQuantity')) + + +class InstrumentControl(MSection): + """Details relevant for the control of the instrument.""" + + camera_length = Quantity( + type=np.dtype(np.float64), + unit='meter', + description='''Exact definition as understood by HU colleagues + remains to be communicated.''', + a_eln=dict(component='NumberEditQuantity')) + + _camera_length_units = Quantity( + type=str, + description='''Exact definition as understood by HU colleagues + remains to be communicated.''', + a_eln=dict(component='StringEditQuantity')) + + magnification = Quantity( + type=np.dtype(np.float64), + description=''' + Exact definition as understood by HU colleagues + remains to be communicated.''', + a_eln=dict(component='NumberEditQuantity')) + + _magnification_units = Quantity( + type=str, + description=''' + Exact definition as understood by HU colleagues + remains to be communicated.''', + a_eln=dict(component='StringEditQuantity')) + + +class Stage(MSection): + """Details about the stage.""" + + design = Quantity( + type=str, + description=''' ''', + a_eln=dict(component='StringEditQuantity')) + + name = Quantity( + type=str, + description=''' ''', + a_eln=dict(component='StringEditQuantity')) + + model = Quantity( + type=str, + description=''' ''', a_eln=dict(component='StringEditQuantity')) + + serial_number = Quantity( + type=str, + description=''' ''', + a_eln=dict(component='StringEditQuantity')) + + manufacturer_name = Quantity( + type=str, + description=''' ''', + a_eln=dict(component='StringEditQuantity')) + + description = Quantity( + type=str, + description=''' ''', + a_eln=dict(component='StringEditQuantity')) + + +class Instrument(MSection): + """Details about the electron microscope, its components, and software.""" + + name = Quantity( + type=str, + description=''' + Given name of the microscope, e.g NionHermes.''', + a_eln=dict(component='StringEditQuantity')) + + location = Quantity( + type=str, + description=''' + Geographic coordinates of the lab or the place where the instrument + is installed using GEOREF geocodes ideally.''', + a_eln=dict(component='StringEditQuantity')) + + instrument_manufacturer = Quantity( + type=str, + description=''' + Manufacturer of the entire instrument (e.g. Nion, FEI, Zeiss, + Thermofisher) to enable e.g. queries in materials + database systems for instrument manufacturers. + Usually more technical details are needed though to specify the + instrument, these should be written into instrument_model and + instrument_capabilities.''', + a_eln=dict(component='StringEditQuantity')) + + instrument_model = Quantity( + type=str, + description=''' + Manufacturer brand/model to enable e.g. queries about + microscope models.''', + a_eln=dict(component='StringEditQuantity')) + + instrument_identifier = Quantity( + type=str, + description=''' + Hardware name/serial number or hash identifier given by the + manufacturer to identify the instrument.''', + a_eln=dict(component='StringEditQuantity')) + + instrument_capability = Quantity( + type=str, + description='''Free-text list possibly multiple terms of + functionalities which the instrument provides.''', + a_eln=dict(component='StringEditQuantity')) + + electron_gun = SubSection(section_def=ElectronGun) + aperture = SubSection(section_def=Aperture, repeats=True) + lens = SubSection(section_def=Lens, repeats=True) + stage = SubSection(section_def=Stage) + detector = SubSection(section_def=Detector) + control = SubSection(section_def=InstrumentControl) + + +class Operator(MSection): + """Contact information of at least the user of the instrument. + + Or the investigator who performed this experiment. + Adding multiple users if relevant is recommended. + """ + + m_def = Section(a_eln=dict()) + + name = Quantity( + type=str, + description=''' + Given (first) name and surname of the user.''', + a_eln=dict(component='StringEditQuantity')) + + affiliation = Quantity( + type=str, + description=''' + Name of the affiliation of the user at the point in time + when the experiment was performed.''', + a_eln=dict(component='StringEditQuantity')) + + address = Quantity( + type=str, + description='''Postal address of the affiliation.''', + a_eln=dict(component='StringEditQuantity')) + + email = Quantity( + type=str, + description=''' + Email address of the user at the point in time + when the experiment was performed. + Giving the most permanently used email is recommended.''', + a_eln=dict(component='StringEditQuantity')) + + orcid = Quantity( + type=str, + description=''' + Globally unique identifier of the user as offered + by services like ORCID or ResearcherID.''', + a_eln=dict(component='StringEditQuantity')) + + telephone_number = Quantity( + type=str, + description=''' + (Business) (tele)phone number of the user at the point + in time when the experiment was performed.''', + a_eln=dict(component='StringEditQuantity')) + + role = Quantity( + type=str, + description=''' + Which role does the user have in the place, and at the point in time + when, the experiment was performed (e.g. technician operating the + microscope, student, postdoc, principal investigator, guest ...)''', + a_eln=dict(component='StringEditQuantity')) + + +class Sample(MSection): + """Properties of the specimen, its history.""" + + name = Quantity( + type=str, + description=''' + Descriptive name or identifier with which to distinguish the specimen + from all others and especially the parts from where it 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. The user is + advised to store the details how specimens were cut/prepared from + samples in the sample history.''', + a_eln=dict(component='StringEditQuantity')) + + sample_history = Quantity( + type=str, + description=''' + Ideally, a reference to the location of or a (globally persistent) + unique identifier of e.g. another file which should document ideally + as many details as possible of the material, its microstructure, and + its thermo-chemo-mechanical processing/preparation history. + In the case that such a detailed history of the sample/specimen is not + available, use this field as a free-text description to specify a sub- + set of the entire sample history, i.e. what you would consider being + the key steps and relevant information about the specimen, its + material, microstructure, thermo-chemo-mechanical processing state, + and details of the preparation.''', + a_eln=dict(component='StringEditQuantity')) + + preparation_date = Quantity( + type=Datetime, + description=''' + ISO8601 date and time with local time zone offset to UTC included + when the specimen was prepared. Ideally report the end of the + preparation, i.e. the last known time the measured specimen surface + was actively prepared. Knowing when the specimen was exposed to e.g. + specific atmosphere is especially required for environmentally + sensitive material such as hydrogen-charged specimens or experiments + including tracers with a short half-time. The user is advised to + include these temporal details in the sample_history.''', + a_eln=dict(component='DateTimeEditQuantity')) + + atom_types = Quantity( + type=str, + 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 + considered relevant from a scientific point. The purpose of the field + is to offer materials database systems an opportunity to parse the + relevant elements without having to interpret these + from the sample history.''', + a_eln=dict(component='StringEditQuantity')) + + thickness = Quantity( + type=np.dtype(np.float64), + unit='meter', + description='''Sample thickness''', + a_eln=dict(component='NumberEditQuantity')) + + _thickness_units = Quantity( + type=str, + description='''Sample thickness''', + a_eln=dict(component='StringEditQuantity')) + + +class ElectronMicroscopyNion(EntryData): + """Application definition describing electron microscope experiments. + + Applies when working with a Nion microscope using NXem_nion.nxdl.xml. + The here specified ELN is a proof-of-concept whose scope is to provide + a mechanism to inject additional pieces of information to create a NeXus + file that matches a: + - particular version of the NXem_nion.nxdl.xml appdef + - https://github.com/FAIRmat-Experimental/nexus_definitions/commit/ + - 1d46b94dc36a42225ad247cc763277a47dcd30d4 + - working with experiments as a pair of Numpy NPY binary and Nion + - metadata JSON file. Specifically with these file formats being + - interpretable with the following, so-called em_nion dataconverter + - (see commit below). + The ELN returns a json file that is to be used to inject the + here collected pieces of information in a + - specifically-versioned dataconverter em_nion + - https://github.com/nomad-coe/nomad-parser-nexus/commit/ + 47bc4f9724b8e980bd41d0fca3a663c0b79a7fb1 + """ + + start_time = Quantity( + type=Datetime, + description=''' + ISO 8601 formatted time code with local time zone offset to UTC + information included when the experiment started. If the application + demands that time codes in this section of the application definition + should only be used for specifying when the experiment was performed - + and the exact duration is not relevant - this start time field + should be used. Often though it is useful to specify a time interval + with specifying both start_time and end_time to allow for more + detailed bookkeeping and interpretation of the experiment. The user + should be aware that even with having both time instances specified + it may not be advisable to infer how long the experiment took or + for how long data were acquired. More detailed timing data over + the course of the experiment have to be collected.''', + a_eln=dict(component='DateTimeEditQuantity')) + + end_time = Quantity( + type=Datetime, + description=''' + ISO 8601-formatted time code with local time zone offset to UTC + included when the experiment ended.''', + a_eln=dict(component='DateTimeEditQuantity')) + + sample = SubSection(section_def=Sample) + operator = SubSection(section_def=Operator, repeats=True) + instrument = SubSection(section_def=Instrument) + + nionswift_data_npy_file = Quantity( + type=str, + description=''' + Numpy data file as exported from NionSwift''', + a_eln=dict(component='FileEditQuantity'), + a_browser=dict(adaptor='RawFileAdaptor')) + + nionswift_metadata_json_file = Quantity( + type=str, + description=''' + Json file with metadata as exported from NionSwift''', + a_eln=dict(component='FileEditQuantity'), + a_browser=dict(adaptor='RawFileAdaptor')) + + # maybe a database dump file from e.g. NionSwift or microscope control + # software in general + + def normalize(self, archive, logger): + """Normalize data.""" + # store content of the above ELN entries into a dictionary + # data = self.m_to_dict() + + # map paths in this dictionary on paths as expected by apm reader + # for instance by storing flattened data key value pairs in a json + # file + + # remains to be implemented + +# runner = CliRunner() +# converter_args = ['--input-file', self.nionswift_data_npy_file, +# '--input-file', self.nionswift_metadata_json_file, +# '--input-file', +# os.path.join(filepath, 'eln.em_nion.json'), +# '--reader', 'em_nion', +# '--nxdl', 'NXem_nion', +# '--output', os.path.join(filepath, \ +# 'em_nion.test.nxs')] +# runner.invoke(convert, converter_args) + + pass + +# ============================================================================= +# import yaml +# from nomad.datamodel.metainfo.ellipsometry.dict_flattener \ +# import dict_flatten, dict_key_cut +# from nexusparser.tools.dataconverter.convert import convert +# from click.testing import CliRunner +# +# data = self.m_to_dict() +# data.update(data.pop('data_file')) +# dict_flatten(data) +# dict_key_cut(data) +# +# kl = list(data.keys()) +# for k in kl: +# if '/_' in k: +# data[k.replace('/_', r"/\@")] = data.pop(k) +# dict_key_cut(data, '/', 1, '@') +# data['definition'] = 'NXellipsometry' +# data[r"definition/\@version"] = '0.0.1' +# data[r"definition/\@url"] = 'https://' +# +# with archive.m_context.raw_file('eln.apm.json', 'w') as outfile: +# yaml.dump(data, outfile, default_flow_style=False) +# filepath = os.path.dirname(outfile.name) +# ============================================================================= + + +m_package.__init_metainfo__()