diff --git a/README.md b/README.md index 068e1774..376c867d 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,25 @@ material processing. pip install nomad-material-processing ``` +### Setting up your OASIS +Read the [NOMAD plugin documentation](https://nomad-lab.eu/prod/v1/staging/docs/plugins/plugins.html#add-a-plugin-to-your-nomad) for all details on how to deploy the plugin on your NOMAD instance. + +You don't need to modify the ```nomad.yaml``` configuration file of your NOMAD instance, beacuse the package is pip installed and all the available modules (entry points) are loaded. +To include, instead, only some of the entry points, you need to specify them in the ```include``` section of the ```nomad.yaml```. In the following lines, a list of all the available entry points: + +```yaml +plugins: + include: + - "nomad_material_processing:schema" + - "nomad_material_processing.solution:schema" + - "nomad_material_processing.vapor_deposition.cvd:schema" + - "nomad_material_processing.vapor_deposition.pvd:schema" + - "nomad_material_processing.vapor_deposition.pvd:mbe_schema" + - "nomad_material_processing.vapor_deposition.pvd:pld_schema" + - "nomad_material_processing.vapor_deposition.pvd:sputtering_schema" + - "nomad_material_processing.vapor_deposition.pvd:thermal_schema" + ``` + ### Development This code is currently under development and for installing and contributing you should clone the repository: ```sh diff --git a/nomad.yaml b/nomad.yaml deleted file mode 100644 index 1d169acf..00000000 --- a/nomad.yaml +++ /dev/null @@ -1,10 +0,0 @@ -normalize: - normalizers: - include: - - MetainfoNormalizer -plugins: - include: - - schemas/example - options: - schemas/example: - python_package: nomad_material_processing diff --git a/pyproject.toml b/pyproject.toml index 0ce5ca45..953a4812 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,35 +6,31 @@ requires = [ build-backend = "setuptools.build_meta" [project] +classifiers = [ + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "License :: OSI Approved :: MIT License", +] name = "nomad-material-processing" dynamic = ["version"] description = "A plugin for NOMAD containing base sections for material processing." readme = "README.md" authors = [ + { name = "Sarthak Kapoor", email = 'sarthak.kapoor@physik.hu-berlin.de' }, { name = "Hampus Näsström", email = 'hampus.naesstroem@physik.hu-berlin.de' }, { name = "Andrea Albino", email = 'andrea.albino@physik.hu-berlin.de' }, { name = "Sebastian Brückner", email = 'sebastian.brueckner@ikz-berlin.de' }, ] -requires-python = ">=3.9" -classifiers = [ - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", - "License :: OSI Approved :: Apache Software License", -] +license = { file = "LICENSE" } dependencies = [ - "nomad-lab>=1.3.3.dev86", -] -[project.optional-dependencies] -dev = [ - "pytest", - "ruff", - "structlog>=22.3.0", + "nomad-lab>=1.3.4dev", ] -[project.license] -file = "LICENSE" - [project.urls] "Homepage" = "https://github.com/FAIRmat-NFDI/nomad-material-processing" "Bug Tracker" = "https://github.com/FAIRmat-NFDI/nomad-material-processing/issues" @@ -42,10 +38,40 @@ file = "LICENSE" [tool.uv] index-url = "https://gitlab.mpcdf.mpg.de/api/v4/projects/2187/packages/pypi/simple" + +[project.optional-dependencies] +dev = [ + "pytest", + "ruff", + "structlog", +] + [tool.ruff] include = ["src/*.py", "tests/*.py"] - -exclude = ["dependencies"] +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", +] # Same as Black. line-length = 88 @@ -53,27 +79,28 @@ indent-width = 4 [tool.ruff.lint] select = [ - "E", # pycodestyle - "W", # pycodestyle - "PL", # pylint + # pycodestyle + "E", + # Pyflakes + "F", + # pyupgrade + "UP", + # isort + "I", + # pylint + "PL", ] + ignore = [ - "E501", # Line too long ({width} > {limit} characters) - "E701", # Multiple statements on one line (colon) - "E731", # Do not assign a lambda expression, use a def - "E402", # Module level import not at top of file - "PLR0911", # Too many return statements - "PLR0912", # Too many branches - "PLR0913", # Too many arguments in function definition - "PLR0915", # Too many statements - "PLR2004", # Magic value used instead of constant - "PLW0603", # Using the global statement - "PLW2901", # redefined-loop-name - "PLR1714", # consider-using-in - "PLR5501", # else-if-used + "F403", # 'from module import *' used; unable to detect undefined names ] + fixable = ["ALL"] +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +# this is entirely optional, you can remove this if you wish to [tool.ruff.format] # use single quotes for strings. quote-style = "single" @@ -87,9 +114,22 @@ skip-magic-trailing-comma = false # Like Black, automatically detect the appropriate line ending. line-ending = "auto" +[tool.setuptools] +package-dir = { "" = "src" } + [tool.setuptools.packages.find] -where = [ - "src", -] +where = ["src"] + +[project.entry-points.'nomad.plugin'] + +general_schema = "nomad_material_processing:schema" +solution_schema = "nomad_material_processing.solution:schema" +vd_schema = "nomad_material_processing.vapor_deposition:schema" +cvd_schema = "nomad_material_processing.vapor_deposition.cvd:schema" +pvd_schema = "nomad_material_processing.vapor_deposition.pvd:schema" +mbe_schema = "nomad_material_processing.vapor_deposition.pvd:mbe_schema" +pld_schema = "nomad_material_processing.vapor_deposition.pvd:pld_schema" +sputtering_schema = "nomad_material_processing.vapor_deposition.pvd:sputtering_schema" +thermal_schema = "nomad_material_processing.vapor_deposition.pvd:thermal_schema" [tool.setuptools_scm] diff --git a/src/nomad_material_processing/__init__.py b/src/nomad_material_processing/__init__.py index ebd15764..73d69029 100644 --- a/src/nomad_material_processing/__init__.py +++ b/src/nomad_material_processing/__init__.py @@ -15,866 +15,18 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from typing import ( - TYPE_CHECKING, -) -import numpy as np -from nomad.metainfo import ( - Package, - Quantity, - Section, - SubSection, - Datetime, - MEnum, -) -from nomad.datamodel.metainfo.basesections import ( - ElementalComposition, - SynthesisMethod, - CompositeSystem, - CompositeSystemReference, - SystemComponent, -) -from nomad.datamodel.data import ( - ArchiveSection, -) -from nomad.datamodel.metainfo.annotations import ( - ELNAnnotation, - ELNComponentEnum, -) -from nomad.datamodel.metainfo.workflow import ( - Link, -) - -if TYPE_CHECKING: - from nomad.datamodel.datamodel import ( - EntryArchive, - ) - from structlog.stdlib import ( - BoundLogger, - ) - - -m_package = Package(name='Material Processing') - - -class Geometry(ArchiveSection): - """ - Geometrical shape attributes of a system. - Sections derived from `Geometry` represent concrete geometrical shapes. - """ - - m_def = Section() - volume = Quantity( - type=float, - description='The measure of the amount of space occupied in 3D space.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - ), - unit='meter ** 3', - ) - - -class Parallelepiped(Geometry): - """ - Six-faced polyhedron with each pair of opposite faces parallel and equal in size, - characterized by rectangular sides and parallelogram faces. - """ - - m_def = Section() - height = Quantity( - type=float, - description='The z dimension of the parallelepiped.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='millimeter', - label='Height (z)', - ), - unit='meter', - ) - width = Quantity( - type=float, - description='The x dimension of the parallelepiped.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='millimeter', - label='Width (x)', - ), - unit='meter', - ) - length = Quantity( - type=float, - description='The y dimension of the parallelepiped.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='millimeter', - label='Length (y)', - ), - unit='meter', - ) - alpha = Quantity( - type=float, - description='The angle between y and z sides.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - label='Alpha (∡ y-x-z)', - ), - unit='degree', - ) - beta = Quantity( - type=float, - description='The angle between x and z sides.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - label='Beta (∡ x-y-z)', - ), - unit='degree', - ) - gamma = Quantity( - type=float, - description='The angle between x and y sides.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - label='Gamma (∡ x-z-y)', - ), - unit='degree', - ) - surface_area = Quantity( - type=float, - description=""" - The product of length and width, representing the total exposed area of the - primary surface. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='millimeter ** 2', - label='Surface Area (x*y)', - ), - unit='meter ** 2', - ) - - -class SquareCuboid(Parallelepiped): - """ - A cuboid with all sides equal in length. - """ - m_def = Section() - height = Quantity( - type=float, - description='The z dimension of the parallelepiped.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='millimeter', - label='Height (z)', - ), - unit='meter', - ) - side = Quantity( - type=float, - description='The x and y dimensions of the parallelepiped.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='millimeter', - label='Side (x = y)', - ), - unit='meter', - ) - surface_area = Quantity( - type=float, - description=""" - The product of length and width, representing the total exposed area of the - primary surface. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='millimeter ** 2', - label='Surface Area (x*y)', - ), - unit='meter ** 2', - ) - - -class RectangleCuboid(Parallelepiped): - """ - A rectangular cuboid is a specific type of parallelepiped - where all angles between adjacent faces are right angles, - and all faces are rectangles. - """ - m_def = Section() - height = Quantity( - type=float, - description='The z dimension of the parallelepiped.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='millimeter', - label='Height (z)', - ), - unit='meter', - ) - width = Quantity( - type=float, - description='The x dimension of the parallelepiped.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='millimeter', - label='Width (x)', - ), - unit='meter', - ) - length = Quantity( - type=float, - description='The y dimension of the parallelepiped.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='millimeter', - label='Length (y)', - ), - unit='meter', - ) - surface_area = Quantity( - type=float, - description=""" - The product of length and width, representing the total exposed area of the - primary surface. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='millimeter ** 2', - label='Surface Area (x*y)', - ), - unit='meter ** 2', - ) - - -class TruncatedCone(Geometry): - """ - A cone with the top cut off parallel to the cone bottom. - """ - - m_def = Section() - height = Quantity( - type=float, - description='The z dimension of the parallelepiped.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='nanometer', - ), - label='Height (z)', - unit='meter', - ) - lower_cap_radius = Quantity( - type=float, - description='Radius of the lower cap.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='millimeter', - ), - unit='meter', - ) - upper_cap_radius = Quantity( - type=float, - description='Radius of the upper cap.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='millimeter', - ), - unit='meter', - ) - lower_cap_surface_area = Quantity( - type=float, - description='Area of the lower cap.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='millimeter ** 2', - ), - unit='meter ** 2', - ) - upper_cap_surface_area = Quantity( - type=float, - description='Area of the upper cap.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='millimeter ** 2', - ), - unit='meter ** 2', - ) - lateral_surface_area = Quantity( - type=float, - description='Area of the lateral surface.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='millimeter ** 2', - ), - unit='meter ** 2', - ) - - -class Cylinder(Geometry): - """ - A cylinder, i.e. a prism with a circular base. - """ - - m_def = Section() - height = Quantity( - type=float, - description='The z dimension of the parallelepiped.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='nanometer', - ), - label='Height (z)', - unit='meter', - ) - radius = Quantity( - type=float, - description='Radius of the cylinder.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='millimeter', - ), - unit='meter', - ) - lower_cap_surface_area = Quantity( - type=float, - description='Area of the lower cap.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='millimeter ** 2', - ), - unit='meter ** 2', - ) - cap_surface_area = Quantity( - type=float, - description='Area of the cap.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='millimeter ** 2', - ), - unit='meter ** 2', - ) - lateral_surface_area = Quantity( - type=float, - description='Area of the lateral surface.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='millimeter ** 2', - ), - unit='meter ** 2', - ) - - -class CylinderSector(Cylinder): - central_angle = Quantity( - type=float, - description="""The angle that defines the portion of the cylinder. - This angle is taken at the center of the base circle - and extends to the arc that defines the cylindrical sector.""", - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - ), - unit='degree', - ) - - -class IrregularParallelSurfaces(Geometry): - """ - A shape that does not fit into any of the other geometry classes. - """ - - m_def = Section() - height = Quantity( - type=float, - description='The z dimension of the irregular shape.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='nanometer', - ), - label='Height (z)', - unit='meter', - ) - - -class Miscut(ArchiveSection): - """ - The miscut in a crystalline substrate refers to - the intentional deviation from a specific crystallographic orientation, - commonly expressed as the angular displacement of a crystal plane. - """ - - angle = Quantity( - type=float, - description=""" - The angular displacement from the crystallographic orientation of the substrate. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='deg', - label='Miscut Angle', - ), - unit='deg', - ) - angle_deviation = Quantity( - type=float, - description='The ± deviation in the angular displacement.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='deg', - label='± Miscut Angle Deviation', - ), - unit='deg', - ) - orientation = Quantity( - type=str, - description='The direction of the miscut in Miller index, [hkl].', - a_eln=ELNAnnotation( - component=ELNComponentEnum.StringEditQuantity, - label='Miscut Orientation [hkl]', - ), - ) - - -class Dopant(ElementalComposition): - """ - A dopant element in a crystalline structure - is a foreign atom intentionally introduced into the crystal lattice. - """ - - doping_level = Quantity( - type=float, - description='The chemical doping level.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='1 / cm ** 3', - ), - unit='1 / m ** 3', - ) - doping_deviation = Quantity( - type=float, - description='The ± deviation in the doping level.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='1 / cm ** 3', - ), - unit='1 / m ** 3', - ) - - -class CrystalProperties(ArchiveSection): - """ - Characteristics arising from the ordered arrangement of atoms in a crystalline structure. - These properties are defined by factors such as crystal symmetry, lattice parameters, - and the specific arrangement of atoms within the crystal lattice. - """ - - -class SubstrateCrystalProperties(CrystalProperties): - """ - Crystallographic parameters such as orientation, miscut, and surface structure. - """ - bravais_lattices = Quantity( - type=MEnum( - 'Triclinic', - 'Monoclinic Simple', - 'Monoclinic Base Centered', - 'Orthorhombic Simple', - 'Orthorhombic Base Centered', - 'Orthorhombic Body Centered', - 'Orthorhombic Face Centered', - 'Tetragonal Simple', - 'Tetragonal Body Centered', - 'Cubic Simple', - 'Cubic Body Centered', - 'Cubic Face Centered', - 'Trigonal', - 'Hexagonal', - ), - description='The crystal system of the substrate.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.EnumEditQuantity, - ), - ) - orientation = Quantity( - type=str, - description=""" - Alignment of crystal lattice with respect to a vector normal to the surface - specified using Miller indices. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.StringEditQuantity, - label='Substrate Orientation (hkl)', - ), - ) - miscut = SubSection( - section_def=Miscut, - description=""" - Section describing any miscut of the substrate with respect to the substrate - orientation. - """, - repeats=True, - ) - +from nomad.config.models.plugins import SchemaPackageEntryPoint -class ElectronicProperties(ArchiveSection): - """ - The electronic properties of a material. - """ - m_def = Section() +class GeneralSchemaPackageEntryPoint(SchemaPackageEntryPoint): + def load(self): + from nomad_material_processing.general import m_package - conductivity_type = Quantity( - type=MEnum( - 'P-type', - 'N-type', - ), - description='The type of semiconductor, N-type being electrons the majority carriers and P-type being holes the majority carriers.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.EnumEditQuantity, - ), - ) - carrier_density = Quantity( - type=np.dtype(float), - unit='1 / cm**3', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - ), - description='Concentration of free charge carriers, electrons in the conduction band and holes in the valence band.', - ) - electrical_resistivity = Quantity( - type=float, - links=['http://fairmat-nfdi.eu/taxonomy/ElectricalResistivity'], - description='Resistance of the charges to move in the presence of an electric current.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='ohm cm', - ), - unit='ohm m', - ) + return m_package -class Substrate(CompositeSystem): - """ - A thin free standing sheet of material. Not to be confused with the substrate role - during a deposition, which can be a `Substrate` with `ThinFilm`(s) on it. - """ - - m_def = Section() - - supplier = Quantity( - type=str, - description='The supplier of the current substrate specimen.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.StringEditQuantity, - ), - ) - supplier_id = Quantity( - type=str, - description='An ID string that is unique from the supplier.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.StringEditQuantity, - label='Supplier ID', - ), - ) - lab_id = Quantity( - type=str, - a_eln=ELNAnnotation( - component=ELNComponentEnum.StringEditQuantity, - label='Substrate ID', - ), - ) - image = Quantity( - type=str, - description='A photograph or image of the substrate.', - a_browser={'adaptor': 'RawFileAdaptor'}, - a_eln=ELNAnnotation( - component=ELNComponentEnum.FileEditQuantity, - ), - ) - information_sheet = Quantity( - type=str, - description='Pdf files containing certificate and other documentation.', - a_browser={'adaptor': 'RawFileAdaptor'}, - a_eln=ELNAnnotation( - component='FileEditQuantity', - ), - ) - - -class CrystallineSubstrate(Substrate): - """ - The substrate defined in this class is composed of periodic arrangement of atoms - and shows typical features of a crystal structure. - """ - - m_def = Section() - - - geometry = SubSection( - section_def=Geometry, - description='Section containing the geometry of the substrate.', - ) - crystal_properties = SubSection( - section_def=SubstrateCrystalProperties, - description='Section containing the crystal properties of the substrate.', - ) - electronic_properties = SubSection( - section_def=ElectronicProperties, - description='Section containing the electronic properties of the substrate.', - ) - dopants = SubSection( - section_def=Dopant, - repeats=True, - description=""" - Repeating section containing information on any dopants in the substrate. - """, - ) - - -class ThinFilm(CompositeSystem): - """ - A thin film of material which exists as part of a stack. - """ - - m_def = Section() - - geometry = SubSection( - section_def=Geometry, - description='Section containing the geometry of the thin film.', - ) - - -class ThinFilmReference(CompositeSystemReference): - """ - Class autogenerated from yaml schema. - """ - - lab_id = Quantity( - type=str, - a_eln=ELNAnnotation( - component=ELNComponentEnum.StringEditQuantity, - label='Thin Film ID', - ), - ) - reference = Quantity( - type=ThinFilm, - a_eln=ELNAnnotation( - component=ELNComponentEnum.ReferenceEditQuantity, - label='Thin Film', - ), - ) - - -class SubstrateReference(CompositeSystemReference): - """ - A section for describing a system component and its role in a composite system. - """ - - lab_id = Quantity( - type=str, - a_eln=ELNAnnotation( - component=ELNComponentEnum.StringEditQuantity, - label='Substrate ID', - ), - ) - reference = Quantity( - type=Substrate, - a_eln=ELNAnnotation( - component=ELNComponentEnum.ReferenceEditQuantity, - label='Substrate', - ), - ) - - -class ThinFilmStack(CompositeSystem): - """ - A stack of `ThinFilm`(s). Typically deposited on a `Substrate`. - """ - - m_def = Section( - a_eln=ELNAnnotation( - hide=[ - 'components', - ], - ), - ) - layers = SubSection( - description=""" - An ordered list (starting at the substrate) of the thin films making up the - thin film stacks. - """, - section_def=ThinFilmReference, - repeats=True, - ) - substrate = SubSection( - description=""" - The substrate which the thin film layers of the thin film stack are deposited - on. - """, - section_def=SubstrateReference, - ) - - def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - """ - The normalizer for the `ThinFilmStack` class. - - Args: - archive (EntryArchive): The archive containing the section that is being - normalized. - logger (BoundLogger): A structlog logger. - """ - self.components = [] - if self.layers: - self.components = [ - SystemComponent(system=layer.reference) - for layer in self.layers - if layer.reference - ] - if self.substrate.reference: - self.components.append(SystemComponent(system=self.substrate.reference)) - super().normalize(archive, logger) - - -class ThinFilmStackReference(CompositeSystemReference): - """ - Class autogenerated from yaml schema. - """ - - m_def = Section() - lab_id = Quantity( - type=str, - a_eln=ELNAnnotation( - component=ELNComponentEnum.StringEditQuantity, - ), - ) - reference = Quantity( - type=ThinFilmStack, - a_eln=ELNAnnotation( - component=ELNComponentEnum.ReferenceEditQuantity, - ), - ) - - -class SampleDeposition(SynthesisMethod): - """ - The process of the settling of particles (atoms or molecules) from a solution, - suspension or vapour onto a pre-existing surface, resulting in the growth of a - new phase. [database_cross_reference: https://orcid.org/0000-0002-0640-0422] - - Synonyms: - - deposition - """ - - m_def = Section( - links=['http://purl.obolibrary.org/obo/CHMO_0001310'], - ) - - def is_serial(self) -> bool: - """ - Method for determining if the steps are serial. Can be overwritten by sub class. - Default behavior is to return True if all steps start after the previous one. - - Returns: - bool: Whether or not the steps are serial. - """ - start_times = [] - durations = [] - for step in self.steps: - if step.start_time is None or step.duration is None: - return False - start_times.append(step.start_time.timestamp()) - durations.append(step.duration.to('s').magnitude) - start_times = np.array(start_times) - durations = np.array(durations) - end_times = start_times + durations - diffs = start_times[1:] - end_times[:-1] - if np.any(diffs < 0): - return False - return True - - def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - """ - The normalizer for the `SampleDeposition` class. - - Args: - archive (EntryArchive): The archive containing the section that is being - normalized. - logger (BoundLogger): A structlog logger. - """ - super().normalize(archive, logger) - if self.is_serial(): - tasks = [] - previous = None - for step in self.steps: - task = step.to_task() - task.outputs.append(Link(name=step.name, section=step)) - if previous is not None: - task.inputs.append(Link(name=previous.name, section=previous)) - tasks.append(task) - previous = step - archive.workflow2.tasks = tasks - - -class TimeSeries(ArchiveSection): - """ - A time series of data during a process step. - This is an abstract class and should not be used directly. - Instead, it should be derived and the the units of the `value` and `set_value` should - be specified. - - For example, a derived class could be `Temperature` with `value` in Kelvin: - ```python - class Temperature(TimeSeries): - value = TimeSeries.value.m_copy() - value.unit = "kelvin" - set_value = TimeSeries.set_value.m_copy() - set_value.unit = "kelvin" - set_value.a_eln.defaultDisplayUnit = "celsius" - ``` - """ - - m_def = Section( - a_plot=dict( - # x=['time', 'set_time'], - # y=['value', 'set_value'], - x='time', - y='value', - ), - ) - set_value = Quantity( - type=float, - description='The set value(s) (i.e. the intended values) set.', - shape=['*'], - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - label='Set value', - ), - ) - set_time = Quantity( - type=float, - unit='s', - description=""" - The process time when each of the set values were set. - If this is empty and only one set value is present, it is assumed that the value - was set at the start of the process step. - If two set values are present, it is assumed that a linear ramp between the two - values was set. - """, - shape=['*'], - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='s', - label='Set time', - ), - ) - value = Quantity( - type=float, - description='The observed value as a function of time.', - shape=['*'], - ) - time = Quantity( - type=float, - unit='s', - description='The process time when each of the values were recorded.', - shape=['*'], - ) - - -m_package.__init_metainfo__() +schema = GeneralSchemaPackageEntryPoint( + name='General Schema', + description="""Schema package containing basic classes used + in the nomad_material_processing plugin.""", +) diff --git a/src/nomad_material_processing/combinatorial.py b/src/nomad_material_processing/combinatorial.py index 541f9877..e112de7b 100644 --- a/src/nomad_material_processing/combinatorial.py +++ b/src/nomad_material_processing/combinatorial.py @@ -1,5 +1,3 @@ -import json - # # Copyright The NOMAD Authors. # @@ -20,17 +18,8 @@ from typing import ( TYPE_CHECKING, ) + import plotly.graph_objects as go -from nomad.datamodel.metainfo.plot import ( - PlotlyFigure, - PlotSection, -) -from nomad.metainfo import ( - Package, - Quantity, - SubSection, - Section, -) from nomad.datamodel.data import ( ArchiveSection, EntryData, @@ -44,6 +33,16 @@ CompositeSystem, CompositeSystemReference, ) +from nomad.datamodel.metainfo.plot import ( + PlotlyFigure, + PlotSection, +) +from nomad.metainfo import ( + Package, + Quantity, + Section, + SubSection, +) if TYPE_CHECKING: from nomad.datamodel.datamodel import ( @@ -65,8 +64,8 @@ class CombinatorialLibrary(CompositeSystem, EntryData, PlotSection): def plot(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: from nomad.search import ( - search, MetadataPagination, + search, ) query = { @@ -143,7 +142,7 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: normalized. logger (BoundLogger): A structlog logger. """ - super(CombinatorialLibrary, self).normalize(archive, logger) + super().normalize(archive, logger) self.figures = [] self.plot(archive, logger) @@ -191,7 +190,7 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: normalized. logger (BoundLogger): A structlog logger. """ - super(CombinatorialSamplePosition, self).normalize(archive, logger) + super().normalize(archive, logger) class CombinatorialLibraryReference(CompositeSystemReference): @@ -265,7 +264,7 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: normalized. logger (BoundLogger): A structlog logger. """ - super(CombinatorialSample, self).normalize(archive, logger) + super().normalize(archive, logger) # Discrete combinatorial library classes: @@ -285,7 +284,7 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: normalized. logger (BoundLogger): A structlog logger. """ - super(DiscreteCombinatorialSample, self).normalize(archive, logger) + super().normalize(archive, logger) class DiscreteCombinatorialSampleReference(CompositeSystemReference): @@ -325,7 +324,7 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: normalized. logger (BoundLogger): A structlog logger. """ - super(DiscreteCombinatorialSampleReference, self).normalize(archive, logger) + super().normalize(archive, logger) class DiscreteCombinatorialLibrary(Collection): @@ -361,7 +360,7 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: normalized. logger (BoundLogger): A structlog logger. """ - super(DiscreteCombinatorialLibrary, self).normalize(archive, logger) + super().normalize(archive, logger) m_package.__init_metainfo__() diff --git a/src/nomad_material_processing/crystal_growth.py b/src/nomad_material_processing/crystal_growth.py index bd14ed35..37689a37 100644 --- a/src/nomad_material_processing/crystal_growth.py +++ b/src/nomad_material_processing/crystal_growth.py @@ -18,11 +18,13 @@ from typing import ( TYPE_CHECKING, ) + from nomad.metainfo import ( Package, Section, ) -from nomad_material_processing import ( + +from nomad_material_processing.general import ( SampleDeposition, ) @@ -38,49 +40,49 @@ class CrystalGrowth(SampleDeposition): - ''' + """ Any synthesis method used to grow crystals. [database_cross_reference: https://orcid.org/0000-0002-0640-0422] - ''' + """ + m_def = Section( - links=[ - "http://purl.obolibrary.org/obo/CHMO_0002224" - ],) + links=['http://purl.obolibrary.org/obo/CHMO_0002224'], + ) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - ''' + """ The normalizer for the `CrystalGrowth` class. Args: archive (EntryArchive): The archive containing the section that is being normalized. logger (BoundLogger): A structlog logger. - ''' - super(CrystalGrowth, self).normalize(archive, logger) + """ + super().normalize(archive, logger) class CzochralskiProcess(CrystalGrowth): - ''' + """ A method of producing large single crystals (of semiconductors or metals) by inserting a small seed crystal into a crucible filled with similar molten material, then slowly pulling the seed up from the melt while rotating it. [database_cross_reference: https://orcid.org/0000-0002-0640-0422] - ''' + """ + m_def = Section( - links=[ - "http://purl.obolibrary.org/obo/CHMO_0002158" - ],) + links=['http://purl.obolibrary.org/obo/CHMO_0002158'], + ) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - ''' + """ The normalizer for the `CzochralskiProcess` class. Args: archive (EntryArchive): The archive containing the section that is being normalized. logger (BoundLogger): A structlog logger. - ''' - super(CzochralskiProcess, self).normalize(archive, logger) + """ + super().normalize(archive, logger) m_package.__init_metainfo__() diff --git a/src/nomad_material_processing/epitaxy.py b/src/nomad_material_processing/epitaxy.py index 5831e5e3..b43e2c55 100644 --- a/src/nomad_material_processing/epitaxy.py +++ b/src/nomad_material_processing/epitaxy.py @@ -18,11 +18,13 @@ from typing import ( TYPE_CHECKING, ) + from nomad.metainfo import ( Package, Section, ) -from nomad_material_processing import ( + +from nomad_material_processing.general import ( SampleDeposition, ) @@ -38,30 +40,30 @@ class Epitaxy(SampleDeposition): - ''' + """ A synthesis method which consists of depositing a monocrystalline film (from liquid or gaseous precursors) on a monocrystalline substrate. [database_cross_reference: https://orcid.org/0000-0002-0640-0422] - ''' + """ + m_def = Section( - links=[ - "http://purl.obolibrary.org/obo/CHMO_0001336" - ],) + links=['http://purl.obolibrary.org/obo/CHMO_0001336'], + ) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - ''' + """ The normalizer for the `Epitaxy` class. Args: archive (EntryArchive): The archive containing the section that is being normalized. logger (BoundLogger): A structlog logger. - ''' - super(Epitaxy, self).normalize(archive, logger) + """ + super().normalize(archive, logger) class MolecularBeamEpitaxy(Epitaxy): - ''' + """ A synthesis method which consists of depositing a monocrystalline film (from a molecular beam) on a monocrystalline substrate under high vacuum (<10^{-8} Pa). Molecular beam epitaxy is very slow, with a deposition rate of <1000 nm per hour. @@ -70,26 +72,26 @@ class MolecularBeamEpitaxy(Epitaxy): Synonyms: - MBE - molecular-beam epitaxy - ''' + """ + m_def = Section( - links=[ - "http://purl.obolibrary.org/obo/CHMO_0001341" - ],) + links=['http://purl.obolibrary.org/obo/CHMO_0001341'], + ) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - ''' + """ The normalizer for the `MolecularBeamEpitaxy` class. Args: archive (EntryArchive): The archive containing the section that is being normalized. logger (BoundLogger): A structlog logger. - ''' - super(MolecularBeamEpitaxy, self).normalize(archive, logger) + """ + super().normalize(archive, logger) class VaporPhaseEpitaxy(Epitaxy): - ''' + """ A synthesis method which consists of depositing a monocrystalline film (from vapour-phase precursors) on a monocrystalline substrate. [database_cross_reference: https://orcid.org/0000-0002-0640-0422] @@ -100,26 +102,26 @@ class VaporPhaseEpitaxy(Epitaxy): - vapor phase epitaxy - VPE - vapour phase epitaxy - ''' + """ + m_def = Section( - links=[ - "http://purl.obolibrary.org/obo/CHMO_0001346" - ],) + links=['http://purl.obolibrary.org/obo/CHMO_0001346'], + ) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - ''' + """ The normalizer for the `VaporPhaseEpitaxy` class. Args: archive (EntryArchive): The archive containing the section that is being normalized. logger (BoundLogger): A structlog logger. - ''' - super(VaporPhaseEpitaxy, self).normalize(archive, logger) + """ + super().normalize(archive, logger) class MetalOrganicVaporPhaseEpitaxy(VaporPhaseEpitaxy): - ''' + """ A synthesis method which consists of depositing a monocrystalline film, from organometallic vapour-phase precursors, on a monocrystalline substrate. [database_cross_reference: https://orcid.org/0000-0002-0640-0422] @@ -135,22 +137,22 @@ class MetalOrganicVaporPhaseEpitaxy(VaporPhaseEpitaxy): - metal organic vapor phase epitaxy - metal-organic vapour-phase epitaxy - organometallic vapour phase epitaxy - ''' + """ + m_def = Section( - links=[ - "http://purl.obolibrary.org/obo/CHMO_0001348" - ],) + links=['http://purl.obolibrary.org/obo/CHMO_0001348'], + ) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - ''' + """ The normalizer for the `MetalOrganicVaporPhaseEpitaxy` class. Args: archive (EntryArchive): The archive containing the section that is being normalized. logger (BoundLogger): A structlog logger. - ''' - super(MetalOrganicVaporPhaseEpitaxy, self).normalize(archive, logger) + """ + super().normalize(archive, logger) m_package.__init_metainfo__() diff --git a/src/nomad_material_processing/general.py b/src/nomad_material_processing/general.py new file mode 100644 index 00000000..67b4cf12 --- /dev/null +++ b/src/nomad_material_processing/general.py @@ -0,0 +1,1268 @@ +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from nomad.datamodel.datamodel import ( + EntryArchive, + ) + from structlog.stdlib import ( + BoundLogger, + ) + +import numpy as np +from nomad.config import config +from nomad.datamodel.data import ArchiveSection, EntryData +from nomad.datamodel.metainfo.annotations import ELNAnnotation, ELNComponentEnum +from nomad.datamodel.metainfo.basesections import ( + CompositeSystem, + CompositeSystemReference, + ElementalComposition, + Process, + ProcessStep, + SynthesisMethod, + SystemComponent, +) +from nomad.datamodel.metainfo.workflow import ( + Link, +) +from nomad.metainfo import ( + MEnum, + Quantity, + Reference, + SchemaPackage, + Section, + SectionProxy, + SubSection, +) + +m_package = SchemaPackage( + aliases=[ + 'nomad_material_processing', + ], +) + +configuration = config.get_plugin_entry_point('nomad_material_processing:schema') + + +class Geometry(ArchiveSection): + """ + Geometrical shape attributes of a system. + Sections derived from `Geometry` represent concrete geometrical shapes. + """ + + m_def = Section() + volume = Quantity( + type=float, + description='The measure of the amount of space occupied in 3D space.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + ), + unit='meter ** 3', + ) + + +class Parallelepiped(Geometry): + """ + Six-faced polyhedron with each pair of opposite faces parallel and equal in size, + characterized by rectangular sides and parallelogram faces. + """ + + m_def = Section() + height = Quantity( + type=float, + description='The z dimension of the parallelepiped.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='millimeter', + label='Height (z)', + ), + unit='meter', + ) + width = Quantity( + type=float, + description='The x dimension of the parallelepiped.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='millimeter', + label='Width (x)', + ), + unit='meter', + ) + length = Quantity( + type=float, + description='The y dimension of the parallelepiped.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='millimeter', + label='Length (y)', + ), + unit='meter', + ) + alpha = Quantity( + type=float, + description='The angle between y and z sides.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Alpha (∡ y-x-z)', + ), + unit='degree', + ) + beta = Quantity( + type=float, + description='The angle between x and z sides.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Beta (∡ x-y-z)', + ), + unit='degree', + ) + gamma = Quantity( + type=float, + description='The angle between x and y sides.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Gamma (∡ x-z-y)', + ), + unit='degree', + ) + surface_area = Quantity( + type=float, + description=""" + The product of length and width, representing the total exposed area of the + primary surface. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='millimeter ** 2', + label='Surface Area (x*y)', + ), + unit='meter ** 2', + ) + + +class SquareCuboid(Parallelepiped): + """ + A cuboid with all sides equal in length. + """ + + m_def = Section( + a_eln={'hide': ['length']}, + label='Square Cuboid', + ) + height = Quantity( + type=float, + description='The z dimension of the parallelepiped.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='millimeter', + label='Height (z)', + ), + unit='meter', + ) + width = Quantity( + type=float, + description='The x dimension of the parallelepiped.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='millimeter', + label='Side (x=y)', + ), + unit='meter', + ) + alpha = Quantity( + type=float, + description='The angle between y and z sides.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Alpha (∡ y-x-z)', + ), + unit='degree', + default=90.0, + ) + beta = Quantity( + type=float, + description='The angle between x and z sides.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Beta (∡ x-y-z)', + ), + unit='degree', + default=90.0, + ) + gamma = Quantity( + type=float, + description='The angle between x and y sides.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Gamma (∡ x-z-y)', + ), + unit='degree', + default=90.0, + ) + surface_area = Quantity( + type=float, + description=""" + The product of length and width, representing the total exposed area of the + primary surface. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='millimeter ** 2', + label='Surface Area (x*y)', + ), + unit='meter ** 2', + ) + + +class RectangleCuboid(Parallelepiped): + """ + A rectangular cuboid is a specific type of parallelepiped + where all angles between adjacent faces are right angles, + and all faces are rectangles. + """ + + m_def = Section() + height = Quantity( + type=float, + description='The z dimension of the parallelepiped.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='millimeter', + label='Height (z)', + ), + unit='meter', + ) + width = Quantity( + type=float, + description='The x dimension of the parallelepiped.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='millimeter', + label='Width (x)', + ), + unit='meter', + ) + length = Quantity( + type=float, + description='The y dimension of the parallelepiped.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='millimeter', + label='Length (y)', + ), + unit='meter', + ) + alpha = Quantity( + type=float, + description='The angle between y and z sides.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Alpha (∡ y-x-z)', + ), + unit='degree', + default=90.0, + ) + beta = Quantity( + type=float, + description='The angle between x and z sides.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Beta (∡ x-y-z)', + ), + unit='degree', + default=90.0, + ) + gamma = Quantity( + type=float, + description='The angle between x and y sides.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Gamma (∡ x-z-y)', + ), + unit='degree', + default=90.0, + ) + surface_area = Quantity( + type=float, + description=""" + The product of length and width, representing the total exposed area of the + primary surface. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='millimeter ** 2', + label='Surface Area (x*y)', + ), + unit='meter ** 2', + ) + + +class TruncatedCone(Geometry): + """ + A cone with the top cut off parallel to the cone bottom. + """ + + m_def = Section() + height = Quantity( + type=float, + description='The z dimension of the parallelepiped.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='nanometer', + ), + label='Height (z)', + unit='meter', + ) + lower_cap_radius = Quantity( + type=float, + description='Radius of the lower cap.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='millimeter', + ), + unit='meter', + ) + upper_cap_radius = Quantity( + type=float, + description='Radius of the upper cap.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='millimeter', + ), + unit='meter', + ) + lower_cap_surface_area = Quantity( + type=float, + description='Area of the lower cap.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='millimeter ** 2', + ), + unit='meter ** 2', + ) + upper_cap_surface_area = Quantity( + type=float, + description='Area of the upper cap.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='millimeter ** 2', + ), + unit='meter ** 2', + ) + lateral_surface_area = Quantity( + type=float, + description='Area of the lateral surface.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='millimeter ** 2', + ), + unit='meter ** 2', + ) + + +class Cylinder(Geometry): + """ + A cylinder, i.e. a prism with a circular base. + """ + + m_def = Section() + height = Quantity( + type=float, + description='The z dimension of the parallelepiped.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='nanometer', + ), + label='Height (z)', + unit='meter', + ) + radius = Quantity( + type=float, + description='Radius of the cylinder.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='millimeter', + ), + unit='meter', + ) + lower_cap_surface_area = Quantity( + type=float, + description='Area of the lower cap.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='millimeter ** 2', + ), + unit='meter ** 2', + ) + cap_surface_area = Quantity( + type=float, + description='Area of the cap.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='millimeter ** 2', + ), + unit='meter ** 2', + ) + lateral_surface_area = Quantity( + type=float, + description='Area of the lateral surface.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='millimeter ** 2', + ), + unit='meter ** 2', + ) + + +class CylinderSector(Cylinder): + central_angle = Quantity( + type=float, + description="""The angle that defines the portion of the cylinder. + This angle is taken at the center of the base circle + and extends to the arc that defines the cylindrical sector.""", + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + ), + unit='degree', + ) + + +class IrregularParallelSurfaces(Geometry): + """ + A shape that does not fit into any of the other geometry classes. + """ + + m_def = Section() + height = Quantity( + type=float, + description='The z dimension of the irregular shape.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='nanometer', + ), + label='Height (z)', + unit='meter', + ) + + +class Miscut(ArchiveSection): + """ + The miscut in a crystalline substrate refers to + the intentional deviation from a specific crystallographic orientation, + commonly expressed as the angular displacement of a crystal plane. + """ + + angle = Quantity( + type=float, + description=""" + The angular displacement from the crystallographic orientation of the substrate. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='deg', + label='Miscut Angle', + ), + unit='deg', + ) + angle_deviation = Quantity( + type=float, + description='The ± deviation in the angular displacement.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='deg', + label='± Miscut Angle Deviation', + ), + unit='deg', + ) + orientation = Quantity( + type=str, + description='The direction of the miscut in Miller index, [hkl].', + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + label='Miscut Orientation [hkl]', + ), + ) + + +class Dopant(ElementalComposition): + """ + A dopant element in a crystalline structure + is a foreign atom intentionally introduced into the crystal lattice. + """ + + doping_level = Quantity( + type=float, + description='The chemical doping level.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='1 / cm ** 3', + ), + unit='1 / m ** 3', + ) + doping_deviation = Quantity( + type=float, + description='The ± deviation in the doping level.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='1 / cm ** 3', + ), + unit='1 / m ** 3', + ) + + +class CrystalProperties(ArchiveSection): + """ + Characteristics arising from the ordered arrangement + of atoms in a crystalline structure. + These properties are defined by factors such as crystal symmetry, lattice + parameters, and the specific arrangement of atoms within the crystal lattice. + """ + + +class SubstrateCrystalProperties(CrystalProperties): + """ + Crystallographic parameters such as orientation, miscut, and surface structure. + """ + + bravais_lattices = Quantity( + type=MEnum( + 'Triclinic', + 'Monoclinic Simple', + 'Monoclinic Base Centered', + 'Orthorhombic Simple', + 'Orthorhombic Base Centered', + 'Orthorhombic Body Centered', + 'Orthorhombic Face Centered', + 'Tetragonal Simple', + 'Tetragonal Body Centered', + 'Cubic Simple', + 'Cubic Body Centered', + 'Cubic Face Centered', + 'Trigonal', + 'Hexagonal', + ), + description='The crystal system of the substrate.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.EnumEditQuantity, + ), + ) + orientation = Quantity( + type=str, + description=""" + Alignment of crystal lattice with respect to a vector normal to the surface + specified using Miller indices. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + label='Substrate Orientation (hkl)', + ), + ) + miscut = SubSection( + section_def=Miscut, + description=""" + Section describing any miscut of the substrate with respect to the substrate + orientation. + """, + repeats=True, + ) + + +class ElectronicProperties(ArchiveSection): + """ + The electronic properties of a material. + """ + + m_def = Section() + + conductivity_type = Quantity( + type=MEnum( + 'P-type', + 'N-type', + 'Semi-insulating', + ), + description="""The type of semiconductor, N-type being electrons + the majority carriers and P-type being holes the majority carriers.""", + a_eln=ELNAnnotation( + component=ELNComponentEnum.EnumEditQuantity, + ), + ) + carrier_density = Quantity( + type=float, + unit='1 / cm**3', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + ), + description="""Concentration of free charge carriers, electrons in the + conduction band and holes in the valence band.""", + ) + carrier_density_deviation = Quantity( + type=float, + unit='1 / m**3', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='1 / cm**3', + label='± Carrier Density Deviation', + ), + description="""Deviation in the concentration of free charge carriers, + electrons in the conduction band and holes in the valence band.""", + ) + electrical_resistivity = Quantity( + type=float, + links=['http://fairmat-nfdi.eu/taxonomy/ElectricalResistivity'], + description="""Resistance of the charges to move + in the presence of an electric current.""", + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='ohm cm', + ), + unit='ohm m', + ) + + +class Substrate(CompositeSystem): + """ + A thin free standing sheet of material. Not to be confused with the substrate role + during a deposition, which can be a `Substrate` with `ThinFilm`(s) on it. + """ + + m_def = Section() + + supplier = Quantity( + type=str, + description='The supplier of the current substrate specimen.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + ), + ) + supplier_id = Quantity( + type=str, + description='An ID string that is unique from the supplier.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + label='Supplier ID', + ), + ) + lab_id = Quantity( + type=str, + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + label='Substrate ID', + ), + ) + image = Quantity( + type=str, + description='A photograph or image of the substrate.', + a_browser={'adaptor': 'RawFileAdaptor'}, + a_eln=ELNAnnotation( + component=ELNComponentEnum.FileEditQuantity, + ), + ) + information_sheet = Quantity( + type=str, + description='Pdf files containing certificate and other documentation.', + a_browser={'adaptor': 'RawFileAdaptor'}, + a_eln=ELNAnnotation( + component='FileEditQuantity', + ), + ) + + +class CrystallineSubstrate(Substrate): + """ + The substrate defined in this class is composed of periodic arrangement of atoms + and shows typical features of a crystal structure. + """ + + m_def = Section() + + geometry = SubSection( + section_def=Geometry, + description='Section containing the geometry of the substrate.', + ) + crystal_properties = SubSection( + section_def=SubstrateCrystalProperties, + description='Section containing the crystal properties of the substrate.', + ) + electronic_properties = SubSection( + section_def=ElectronicProperties, + description='Section containing the electronic properties of the substrate.', + ) + dopants = SubSection( + section_def=Dopant, + repeats=True, + description=""" + Repeating section containing information on any dopants in the substrate. + """, + ) + + +class ThinFilm(CompositeSystem): + """ + A thin film of material which exists as part of a stack. + """ + + m_def = Section() + + geometry = SubSection( + section_def=Geometry, + description='Section containing the geometry of the thin film.', + ) + + +class ThinFilmReference(CompositeSystemReference): + """ + Class autogenerated from yaml schema. + """ + + lab_id = Quantity( + type=str, + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + label='Thin Film ID', + ), + ) + reference = Quantity( + type=ThinFilm, + a_eln=ELNAnnotation( + component=ELNComponentEnum.ReferenceEditQuantity, + label='Thin Film', + ), + ) + + +class SubstrateReference(CompositeSystemReference): + """ + A section for describing a system component and its role in a composite system. + """ + + lab_id = Quantity( + type=str, + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + label='Substrate ID', + ), + ) + reference = Quantity( + type=Substrate, + a_eln=ELNAnnotation( + component=ELNComponentEnum.ReferenceEditQuantity, + label='Substrate', + ), + ) + + +class ThinFilmStack(CompositeSystem): + """ + A stack of `ThinFilm`(s). Typically deposited on a `Substrate`. + """ + + m_def = Section( + a_eln=ELNAnnotation( + hide=[ + 'components', + ], + ), + ) + layers = SubSection( + description=""" + An ordered list (starting at the substrate) of the thin films making up the + thin film stacks. + """, + section_def=ThinFilmReference, + repeats=True, + ) + substrate = SubSection( + description=""" + The substrate which the thin film layers of the thin film stack are deposited + on. + """, + section_def=SubstrateReference, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + """ + The normalizer for the `ThinFilmStack` class. + + Args: + archive (EntryArchive): The archive containing the section that is being + normalized. + logger (BoundLogger): A structlog logger. + """ + self.components = [] + if self.layers: + self.components = [ + SystemComponent(system=layer.reference) + for layer in self.layers + if layer.reference + ] + if self.substrate.reference: + self.components.append(SystemComponent(system=self.substrate.reference)) + super().normalize(archive, logger) + + +class ThinFilmStackReference(CompositeSystemReference): + """ + Class autogenerated from yaml schema. + """ + + m_def = Section() + lab_id = Quantity( + type=str, + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + ), + ) + reference = Quantity( + type=ThinFilmStack, + a_eln=ELNAnnotation( + component=ELNComponentEnum.ReferenceEditQuantity, + ), + ) + + +class SampleDeposition(SynthesisMethod): + """ + The process of the settling of particles (atoms or molecules) from a solution, + suspension or vapour onto a pre-existing surface, resulting in the growth of a + new phase. [database_cross_reference: https://orcid.org/0000-0002-0640-0422] + + Synonyms: + - deposition + """ + + m_def = Section( + links=['http://purl.obolibrary.org/obo/CHMO_0001310'], + ) + + def is_serial(self) -> bool: + """ + Method for determining if the steps are serial. Can be overwritten by sub class. + Default behavior is to return True if all steps start after the previous one. + + Returns: + bool: Whether or not the steps are serial. + """ + start_times = [] + durations = [] + for step in self.steps: + if step.start_time is None or step.duration is None: + return False + start_times.append(step.start_time.timestamp()) + durations.append(step.duration.to('s').magnitude) + start_times = np.array(start_times) + durations = np.array(durations) + end_times = start_times + durations + diffs = start_times[1:] - end_times[:-1] + if np.any(diffs < 0): + return False + return True + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + """ + The normalizer for the `SampleDeposition` class. + + Args: + archive (EntryArchive): The archive containing the section that is being + normalized. + logger (BoundLogger): A structlog logger. + """ + super().normalize(archive, logger) + if self.is_serial(): + tasks = [] + previous = None + for step in self.steps: + task = step.to_task() + task.outputs.append(Link(name=step.name, section=step)) + if previous is not None: + task.inputs.append(Link(name=previous.name, section=previous)) + tasks.append(task) + previous = step + archive.workflow2.tasks = tasks + + +class TimeSeries(ArchiveSection): + """ + A time series of data during a process step. + This is an abstract class and should not be used directly. + Instead, it should be derived and the the units of the `value` and `set_value` + should be specified. + + For example, a derived class could be `Temperature` with `value` in Kelvin: + ```python + class Temperature(TimeSeries): + value = TimeSeries.value.m_copy() + value.unit = "kelvin" + set_value = TimeSeries.set_value.m_copy() + set_value.unit = "kelvin" + set_value.a_eln.defaultDisplayUnit = "celsius" + ``` + """ + + m_def = Section( + a_plot=dict( + # x=['time', 'set_time'], + # y=['value', 'set_value'], + x='time', + y='value', + ), + ) + set_value = Quantity( + type=float, + description='The set value(s) (i.e. the intended values) set.', + shape=['*'], + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Set value', + ), + ) + set_time = Quantity( + type=float, + unit='s', + description=""" + The process time when each of the set values were set. + If this is empty and only one set value is present, it is assumed that the value + was set at the start of the process step. + If two set values are present, it is assumed that a linear ramp between the two + values was set. + """, + shape=['*'], + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='s', + label='Set time', + ), + ) + value = Quantity( + type=float, + description='The observed value as a function of time.', + shape=['*'], + ) + time = Quantity( + type=float, + unit='s', + description='The process time when each of the values were recorded.', + shape=['*'], + ) + + +class Recipe(ArchiveSection): + """ + A Recipe for a material processing experiment. + + This class will be subclassed for each process that needs a recipe. + + The subclass will inherit Recipe and a specific Process class. + + The only difference between the Recipe and the actual Process is that + the datetime and the input samples Entities are hidden in the Recipe. + """ + + pass + + +class EtchingStep(ProcessStep): + """ + A step of etching process. + """ + + m_def = Section() + duration = Quantity( + type=float, + description='The elapsed time since the annealing process started.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, defaultDisplayUnit='minute' + ), + unit='second', + ) + temperature = Quantity( + type=float, + description='The temperature of the etching process.', + a_eln={'component': 'NumberEditQuantity', 'defaultDisplayUnit': 'celsius'}, + unit='celsius', + ) + agitation = Quantity( + type=MEnum( + 'Magnetic Stirring', + 'Sonication', + ), + description='The agitation method used during the etching process.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.EnumEditQuantity, + ), + ) + etching_reagents = SubSection(section_def=CompositeSystem, repeats=True) + + +class Etching(Process, EntryData): + """ + Selectively remove material from a surface using chemical or physical processes + to create specific patterns or structures. + """ + + m_def = Section( + a_eln=None, + links=['http://purl.obolibrary.org/obo/CHMO_0001558'], + ) + tags = Quantity( + type=str, + shape=['*'], + description='Searchable tags for this entry. Use Explore tab for searching.', + a_eln=ELNAnnotation( + component='StringEditQuantity', + ), + ) + recipe = Quantity( + type=Reference(SectionProxy('EtchingRecipe')), + description=""" The recipe used for the process. If a recipe is found, + all the data is copied from the Recipe within the Process. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.ReferenceEditQuantity, + ), + ) + steps = SubSection( + description=""" + The steps of the etching process. + """, + section_def=EtchingStep, + repeats=True, + ) + + +class EtchingRecipe(Etching, Recipe, EntryData): + """ + A Recipe for an etching process. + """ + + m_def = Section( + a_eln={ + 'hide': [ + 'datetime', + 'samples', + 'starting_time', + 'ending_time', + 'location', + 'recipe', + ] + }, + ) + lab_id = Quantity( + type=str, + description=""" + A unique human readable ID for the recipe. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + label='Recipe ID', + ), + ) + + +class AnnealingStep(ProcessStep): + """ + A step of annealing process. + """ + + m_def = Section() + duration = Quantity( + type=float, + description='The elapsed time since the annealing process started.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, defaultDisplayUnit='minute' + ), + unit='second', + ) + starting_temperature = Quantity( + type=float, + description='The starting T in the annealing ramp.', + a_eln={'component': 'NumberEditQuantity', 'defaultDisplayUnit': 'celsius'}, + unit='celsius', + ) + ending_temperature = Quantity( + type=float, + description='The starting T in the annealing ramp.', + a_eln={'component': 'NumberEditQuantity', 'defaultDisplayUnit': 'celsius'}, + unit='celsius', + ) + + +class Annealing(Process, EntryData): + """ + Heat treatment process used to alter the material's properties, + such as reducing defects, improving crystallinity, or relieving internal stresses. + """ + + m_def = Section( + links=['http://purl.obolibrary.org/obo/CHMO_0001465'], + ) + tags = Quantity( + type=str, + shape=['*'], + description='Searchable tags for this entry. Use Explore tab for searching.', + a_eln=ELNAnnotation( + component='StringEditQuantity', + ), + ) + recipe = Quantity( + type=Reference(SectionProxy('AnnealingRecipe')), + description=""" The recipe used for the process. If a recipe is found, + all the data is copied from the Recipe within the Process. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.ReferenceEditQuantity, + ), + ) + duration = Quantity( + type=float, + description='The elapsed time since the annealing process started.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, defaultDisplayUnit='minute' + ), + unit='second', + ) + steps = SubSection( + description=""" + The steps of the annealing process. + """, + section_def=AnnealingStep, + repeats=True, + ) + + +class AnnealingRecipe(Annealing, Recipe, EntryData): + """ + A Recipe for an annealing process. + """ + + m_def = Section( + a_eln={ + 'hide': [ + 'datetime', + 'samples', + 'starting_time', + 'ending_time', + 'location', + 'recipe', + ] + }, + ) + lab_id = Quantity( + type=str, + description=""" + A unique human readable ID for the recipe. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + label='Recipe ID', + ), + ) + + +class CleaningStep(ProcessStep): + """ + A step of cleaning process. + """ + + m_def = Section() + duration = Quantity( + type=float, + description='The elapsed time since the cleaning process started.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, defaultDisplayUnit='minute' + ), + unit='second', + ) + temperature = Quantity( + type=float, + description='The temperature of the cleaning process.', + a_eln={'component': 'NumberEditQuantity', 'defaultDisplayUnit': 'celsius'}, + unit='celsius', + ) + agitation = Quantity( + type=MEnum( + 'Magnetic Stirring', + 'Sonication', + ), + description='The agitation method used during the cleaning process.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.EnumEditQuantity, + ), + ) + cleaning_reagents = SubSection( + section_def=CompositeSystemReference, + ) + + +class Cleaning(Process, EntryData): + """ + Surface cleaning in thin film material science involves removing contaminants + and residues from a substrate's surface to ensure proper adhesion + and uniformity of the thin film deposition. + """ + + m_def = Section( + a_eln={'hide': ['steps']}, + ) + tags = Quantity( + type=str, + shape=['*'], + description='Searchable tags for this entry. Use Explore tab for searching.', + a_eln=ELNAnnotation( + component='StringEditQuantity', + ), + ) + recipe = Quantity( + type=Reference(SectionProxy('CleaningRecipe')), + description=""" The recipe used for the process. If a recipe is found, + all the data is copied from the Recipe within the Process. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.ReferenceEditQuantity, + ), + ) + duration = Quantity( + type=float, + description='The elapsed time since the annealing process started.', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, defaultDisplayUnit='minute' + ), + unit='second', + ) + steps = SubSection( + description=""" + The steps of the cleaning process. + """, + section_def=CleaningStep, + repeats=True, + ) + + +class CleaningRecipe(Cleaning, Recipe, EntryData): + """ + A Recipe for an cleaning process. + """ + + m_def = Section( + a_eln={ + 'hide': [ + 'datetime', + 'samples', + 'starting_time', + 'ending_time', + 'location', + 'recipe', + ] + }, + ) + lab_id = Quantity( + type=str, + description=""" + A unique human readable ID for the recipe. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + label='Recipe ID', + ), + ) + + +m_package.__init_metainfo__() diff --git a/src/nomad_material_processing/general/__init__.py b/src/nomad_material_processing/general/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/nomad_material_processing/general/schema.py b/src/nomad_material_processing/general/schema.py deleted file mode 100644 index f9bb2c0e..00000000 --- a/src/nomad_material_processing/general/schema.py +++ /dev/null @@ -1,271 +0,0 @@ -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from nomad.datamodel.datamodel import ( - EntryArchive, - ) - from structlog.stdlib import ( - BoundLogger, - ) - -from nomad.datamodel.metainfo.annotations import ELNAnnotation, ELNComponentEnum - -import numpy as np -from nomad.datamodel.data import EntryData, ArchiveSection - -from nomad.metainfo import ( - Quantity, - SubSection, - Section, - MEnum, -) - -from nomad.datamodel.metainfo.basesections import ( - CompositeSystem, - ProcessStep, - Process, - CompositeSystemReference, -) -from nomad.datamodel.metainfo.annotations import ( - ELNAnnotation, - ELNComponentEnum, -) - -from nomad.metainfo import ( - SectionProxy, - Reference, -) - - -class Recipe(ArchiveSection): - """ - A Recipe for a material processing experiment. - - This class will be subclassed for each process that needs a recipe. - - The subclass will inherit Recipe and a specific Process class. - - The only difference between the Recipe and the actual Process is that - the datetime and the input samples Entities are hidden in the Recipe. - """ - - pass - - -class EtchingStep(ProcessStep): - """ - A step of etching process. - """ - - m_def = Section() - duration = Quantity( - type=np.float64, - description='The elapsed time since the annealing process started.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, defaultDisplayUnit='minute' - ), - unit='second', - ) - temperature = Quantity( - type=np.float64, - description='The temperature of the etching process.', - a_eln={'component': 'NumberEditQuantity', 'defaultDisplayUnit': 'celsius'}, - unit='celsius', - ) - agitation = Quantity( - type=MEnum( - 'Magnetic Stirring', - 'Sonication', - ), - description='The agitation method used during the etching process.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.EnumEditQuantity, - ), - ) - etching_reagents = SubSection(section_def=CompositeSystem, repeats=True) - - -class Etching(Process, EntryData): - """ - Selectively remove material from a surface using chemical or physical processes - to create specific patterns or structures. - """ - - m_def = Section( - a_eln=None, - links=['http://purl.obolibrary.org/obo/CHMO_0001558'], - ) - recipe = Quantity( - type=Reference(SectionProxy('EtchingRecipe')), - description=""" The recipe used for the process. If a recipe is found, - all the data is copied from the Recipe within the Process. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.ReferenceEditQuantity, - ), - ) - steps = SubSection( - description=""" - The steps of the etching process. - """, - section_def=EtchingStep, - repeats=True, - ) - - -class EtchingRecipe(Etching, Recipe, EntryData): - """ - A Recipe for an etching process. - """ - - m_def = Section( - a_eln={'hide': ['datetime', 'samples']}, - ) - - -class AnnealingStep(ProcessStep): - """ - A step of annealing process. - """ - - m_def = Section() - duration = Quantity( - type=np.float64, - description='The elapsed time since the annealing process started.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, defaultDisplayUnit='minute' - ), - unit='second', - ) - temperature = Quantity( - type=np.float64, - description='The temperature of the etching process.', - a_eln={'component': 'NumberEditQuantity', 'defaultDisplayUnit': 'celsius'}, - unit='celsius', - ) - - -class Annealing(Process, EntryData): - """ - Heat treatment process used to alter the material's properties, - such as reducing defects, improving crystallinity, or relieving internal stresses. - """ - - m_def = Section( - links=['http://purl.obolibrary.org/obo/CHMO_0001465'], - ) - recipe = Quantity( - type=Reference(SectionProxy('AnnealingRecipe')), - description=""" The recipe used for the process. If a recipe is found, - all the data is copied from the Recipe within the Process. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.ReferenceEditQuantity, - ), - ) - duration = Quantity( - type=np.float64, - description='The elapsed time since the annealing process started.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, defaultDisplayUnit='minute' - ), - unit='second', - ) - steps = SubSection( - description=""" - The steps of the annealing process. - """, - section_def=AnnealingStep, - repeats=True, - ) - - -class AnnealingRecipe(Annealing, Recipe, EntryData): - """ - A Recipe for an annealing process. - """ - - m_def = Section( - a_eln={'hide': ['datetime', 'samples']}, - ) - - -class CleaningStep(ProcessStep): - """ - A step of cleaning process. - """ - - m_def = Section() - duration = Quantity( - type=np.float64, - description='The elapsed time since the cleaning process started.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, defaultDisplayUnit='minute' - ), - unit='second', - ) - temperature = Quantity( - type=np.float64, - description='The temperature of the cleaning process.', - a_eln={'component': 'NumberEditQuantity', 'defaultDisplayUnit': 'celsius'}, - unit='celsius', - ) - agitation = Quantity( - type=MEnum( - 'Magnetic Stirring', - 'Sonication', - ), - description='The agitation method used during the cleaning process.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.EnumEditQuantity, - ), - ) - cleaning_reagents = SubSection( - section_def=CompositeSystemReference, - ) - - -class Cleaning(Process, EntryData): - """ - Surface cleaning in thin film material science involves removing contaminants - and residues from a substrate's surface to ensure proper adhesion - and uniformity of the thin film deposition. - """ - - m_def = Section( - a_eln={'hide': ['steps']}, - ) - recipe = Quantity( - type=Reference(SectionProxy('CleaningRecipe')), - description=""" The recipe used for the process. If a recipe is found, - all the data is copied from the Recipe within the Process. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.ReferenceEditQuantity, - ), - ) - duration = Quantity( - type=np.float64, - description='The elapsed time since the annealing process started.', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, defaultDisplayUnit='minute' - ), - unit='second', - ) - steps = SubSection( - description=""" - The steps of the cleaning process. - """, - section_def=CleaningStep, - repeats=True, - ) - - -class CleaningRecipe(Cleaning, Recipe, EntryData): - """ - A Recipe for an cleaning process. - """ - - m_def = Section( - a_eln={'hide': ['datetime', 'samples']}, - ) diff --git a/src/nomad_material_processing/nomad_plugin.yaml b/src/nomad_material_processing/nomad_plugin.yaml deleted file mode 100644 index 61f6fcea..00000000 --- a/src/nomad_material_processing/nomad_plugin.yaml +++ /dev/null @@ -1,3 +0,0 @@ -description: A plugin for NOMAD containing base sections for material processing. -name: NOMAD Material Processing -plugin_type: schema diff --git a/src/nomad_material_processing/solution/__init__.py b/src/nomad_material_processing/solution/__init__.py index 341318c5..d8be012e 100644 --- a/src/nomad_material_processing/solution/__init__.py +++ b/src/nomad_material_processing/solution/__init__.py @@ -1 +1,14 @@ -from nomad_material_processing.solution.schema import * +from nomad.config.models.plugins import SchemaPackageEntryPoint + + +class SolutionSchemaPackageEntryPoint(SchemaPackageEntryPoint): + def load(self): + from nomad_material_processing.solution.general import m_package + + return m_package + + +schema = SolutionSchemaPackageEntryPoint( + name='Solution Schema', + description='Schema package containing classes for liquid solutions.', +) diff --git a/src/nomad_material_processing/solution/schema.py b/src/nomad_material_processing/solution/general.py similarity index 94% rename from src/nomad_material_processing/solution/schema.py rename to src/nomad_material_processing/solution/general.py index de609205..7d2182b5 100644 --- a/src/nomad_material_processing/solution/schema.py +++ b/src/nomad_material_processing/solution/general.py @@ -1,41 +1,55 @@ from typing import TYPE_CHECKING, Union -from nomad.units import ureg -import numpy as np + from nomad.datamodel.data import ( ArchiveSection, EntryData, ) from nomad.datamodel.metainfo.annotations import ( ELNAnnotation, - SectionProperties, Filter, + SectionProperties, ) from nomad.datamodel.metainfo.basesections import ( Component, CompositeSystem, CompositeSystemReference, - SystemComponent, InstrumentReference, Process, ProcessStep, - PureSubstanceComponent, PubChemPureSubstanceSection, + PureSubstanceComponent, + SystemComponent, ) from nomad.metainfo import ( Datetime, MEnum, Quantity, + SchemaPackage, Section, SubSection, ) +from nomad.units import ureg + from nomad_material_processing.solution.utils import ( create_archive, create_unique_filename, ) if TYPE_CHECKING: - from structlog.stdlib import BoundLogger from nomad.datamodel import EntryArchive + from structlog.stdlib import BoundLogger + +from nomad.config import config + +m_package = SchemaPackage( + aliases=[ + 'nomad_material_processing.solution', + ], +) + +configuration = config.get_plugin_entry_point( + 'nomad_material_processing.solution:schema' +) class MolarConcentration(ArchiveSection): @@ -47,7 +61,7 @@ class MolarConcentration(ArchiveSection): description='The molar concentration of a component in a solution.', ) calculated_concentration = Quantity( - type=np.float64, + type=float, description=( 'The expected concentration calculated from the component moles and ' 'total volume.' @@ -58,9 +72,10 @@ class MolarConcentration(ArchiveSection): unit='mol / liter', ) measured_concentration = Quantity( - type=np.float64, + type=float, description=( - 'The concentration observed or measured with some characterization technique.' + """The concentration observed or measured + with some characterization technique.""" ), a_eln=ELNAnnotation( component='NumberEditQuantity', @@ -94,7 +109,7 @@ class SolutionStorage(ArchiveSection): ), ) temperature = Quantity( - type=np.float64, + type=float, description='The temperature of the storage.', a_eln=ELNAnnotation( component='NumberEditQuantity', @@ -126,7 +141,7 @@ class BaseSolutionComponent(Component): """ volume = Quantity( - type=np.float64, + type=float, description='The volume of the liquid component.', a_eln=ELNAnnotation( component='NumberEditQuantity', @@ -176,13 +191,13 @@ class SolutionComponent(PureSubstanceComponent, BaseSolutionComponent): | ------------- | ----------- | | Solvent | The term applied to the whole initial liquid phase containing the extractant. | | Solute | The minor component of a solution which is regarded as having been dissolved by the solvent. | - """, + """, # noqa: E501 a_eln=ELNAnnotation( component='EnumEditQuantity', ), ) mass = Quantity( - type=np.float64, + type=float, description='The mass of the component without the container.', a_eln=ELNAnnotation( component='NumberEditQuantity', @@ -192,7 +207,7 @@ class SolutionComponent(PureSubstanceComponent, BaseSolutionComponent): unit='kilogram', ) density = Quantity( - type=np.float64, + type=float, description='The density of the liquid component.', a_eln=ELNAnnotation( component='NumberEditQuantity', @@ -239,7 +254,8 @@ def calculate_molar_concentration( self, volume: Quantity, logger: 'BoundLogger' = None ) -> None: """ - Calculate the molar concentration of the component in a given volume of solution. + Calculate the molar concentration of the component + in a given volume of solution. Args: volume (Quantity): The volume of the solution. @@ -260,8 +276,8 @@ def calculate_molar_concentration( def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: """ - Normalize method for the `SolutionComponent` section. Sets the mass if volume and - density are provided. + Normalize method for the `SolutionComponent` section. Sets the mass if volume + and density are provided. Args: archive (EntryArchive): A NOMAD archive. @@ -280,7 +296,7 @@ class Solution(CompositeSystem, EntryData): Section for decribing liquid solutions. """ - # TODO make the solvents, solutes, and elemental_composition sub-section non-editable. + # TODO make the solvents, solutes, and elemental_composition subsection noneditable. m_def = Section( description='A homogeneous liquid mixture composed of two or more substances.', a_eln=ELNAnnotation( @@ -305,7 +321,7 @@ class Solution(CompositeSystem, EntryData): ) ph_value = Quantity( description='The pH value of the solution.', - type=np.float64, + type=float, a_eln=ELNAnnotation( component='NumberEditQuantity', minValue=0, @@ -314,7 +330,7 @@ class Solution(CompositeSystem, EntryData): ) density = Quantity( description='The density of the solution.', - type=np.float64, + type=float, a_eln=ELNAnnotation( defaultDisplayUnit='gram / milliliter', ), @@ -322,7 +338,7 @@ class Solution(CompositeSystem, EntryData): ) mass = Quantity( description='The mass of the solution.', - type=np.float64, + type=float, a_eln=ELNAnnotation( defaultDisplayUnit='gram', ), @@ -332,7 +348,7 @@ class Solution(CompositeSystem, EntryData): description="""The final expected volume of the solution, which is the sum of volume of its liquid components. """, - type=np.float64, + type=float, a_eln=ELNAnnotation( defaultDisplayUnit='milliliter', ), @@ -340,7 +356,7 @@ class Solution(CompositeSystem, EntryData): ) measured_volume = Quantity( description='The volume of the solution as observed or measured.', - type=np.float64, + type=float, a_eln=ELNAnnotation( component='NumberEditQuantity', defaultDisplayUnit='milliliter', @@ -431,13 +447,13 @@ def calculate_volume(self, logger: 'BoundLogger' = None) -> None: continue self.calculated_volume += component.volume - def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: # noqa: PLR0912, PLR0915 """ Normalize method for the `Solution` section. Calculate the total volume of the solution. Populates the solvents and solutes with the components based on the `component_role`. If a component doesn't have pure_substance section, it is - skipped. In case of components that are solutions, the quantity of their solvents - and solutes is scaled based on their quantity used. Combines the + skipped. In case of components that are solutions, the quantity of their + solvents and solutes is scaled based on their quantity used. Combines the components with the same PubChem CID. Set the mass, density, and elemental composition of the solution. @@ -456,9 +472,9 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: if isinstance(component, SolutionComponent): if not component.pure_substance or not component.mass: logger.warning( - f'Either the pure_substance sub_section or mass for the component ' - f'"{component.name}" is missing. Not adding it to the ' - f'"{component.component_role.lower()}' + 's' + '" list.' + f'Either the pure_substance sub_section or mass for the ' + f'component "{component.name}" is missing. Not adding it to ' + f'the "{component.component_role.lower()}' + 's' + '" list.' ) continue component.mass_fraction = None @@ -563,7 +579,7 @@ class SolutionComponentReference(SystemComponent, BaseSolutionComponent): a_eln=dict(component='ReferenceEditQuantity'), ) mass = Quantity( - type=np.float64, + type=float, description='The mass of the solution used.', a_eln=ELNAnnotation( defaultDisplayUnit='gram', @@ -590,14 +606,13 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: if not self.volume: # assume entire volume of the solution is used self.volume = available_volume - else: - if self.volume > available_volume: - logger.warning( - f'The volume used for the "{self.name}" is greater than the ' - 'available volume of the solution. Setting it to the available ' - 'volume.' - ) - self.volume = available_volume + elif self.volume > available_volume: + logger.warning( + f'The volume used for the "{self.name}" is greater than the ' + 'available volume of the solution. Setting it to the available ' + 'volume.' + ) + self.volume = available_volume if self.system.density: self.mass = self.system.density * self.volume super().normalize(archive, logger) @@ -625,7 +640,7 @@ class Pipetting(MeasurementMethodology): # TODO populate `pipette_volume` from the instrument pipette_volume = Quantity( - type=np.float64, + type=float, description='The volume of the pipette used.', a_eln=ELNAnnotation( component='NumberEditQuantity', @@ -643,7 +658,7 @@ class Scaling(MeasurementMethodology): # TODO populate `precision` from the instrument precision = Quantity( - type=np.float64, + type=float, description='The precision of the weighing instrument.', a_eln=ELNAnnotation( component='NumberEditQuantity', @@ -653,7 +668,7 @@ class Scaling(MeasurementMethodology): unit='kilogram', ) container_mass = Quantity( - type=np.float64, + type=float, description='The mass of the container.', a_eln=ELNAnnotation( component='NumberEditQuantity', @@ -663,7 +678,7 @@ class Scaling(MeasurementMethodology): unit='kilogram', ) gross_mass = Quantity( - type=np.float64, + type=float, description='The mass of the material including the container.', a_eln=ELNAnnotation( component='NumberEditQuantity', @@ -737,7 +752,7 @@ class Agitation(SolutionPreparationStep): description='Generic agitation or mixing step for solution preparation.', ) temperature = Quantity( - type=np.float64, + type=float, description='The temperature of the mixing process.', a_eln=ELNAnnotation( component='NumberEditQuantity', @@ -775,7 +790,7 @@ class Sonication(Agitation): description='Sonication step for solution preparation.', ) frequency = Quantity( - type=np.float64, + type=float, description='The frequency of the sonication instrument.', a_eln=ELNAnnotation( component='NumberEditQuantity', @@ -794,7 +809,7 @@ class MechanicalStirring(Agitation): description='Mechanical stirring step for solution preparation.', ) rotation_speed = Quantity( - type=np.float64, + type=float, description='The rotation speed of the stirrer.', a_eln=ELNAnnotation( component='NumberEditQuantity', @@ -922,3 +937,6 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: if not self.solution: self.solution = SolutionReference() self.solution.reference = self.create_solution_entry(solution, archive, logger) + + +m_package.__init_metainfo__() diff --git a/src/nomad_material_processing/solution/nomad_plugin.yaml b/src/nomad_material_processing/solution/nomad_plugin.yaml deleted file mode 100644 index a7e2517b..00000000 --- a/src/nomad_material_processing/solution/nomad_plugin.yaml +++ /dev/null @@ -1,3 +0,0 @@ -description: A plugin for NOMAD containing base sections for solutions. -name: Solutions -plugin_type: schema diff --git a/src/nomad_material_processing/solution/utils.py b/src/nomad_material_processing/solution/utils.py index 532d49eb..bf2a7cee 100644 --- a/src/nomad_material_processing/solution/utils.py +++ b/src/nomad_material_processing/solution/utils.py @@ -15,13 +15,10 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from typing import TYPE_CHECKING - import json import math -import re +from typing import TYPE_CHECKING -import pandas as pd import yaml if TYPE_CHECKING: @@ -80,11 +77,10 @@ def dict_nan_equal(dict1, dict2): return True -def create_archive( +def create_archive( # noqa: PLR0913 entry_dict, context, filename, file_type, logger, *, overwrite: bool = False ): from nomad.datamodel.context import ClientContext - from nomad.datamodel import EntryArchive file_exists = context.raw_path_exists(filename) dicts_are_equal = None @@ -125,7 +121,10 @@ def create_unique_filename( suffix: Usually the file extension. Default is 'archive.json'. """ i = 0 - template = lambda i: f'{prefix}_{i}.{suffix}' + + def template(i): + return f'{prefix}_{i}.{suffix}' + if not archive.m_context.raw_path_exists(template(i)): return template(i) while True: diff --git a/src/nomad_material_processing/utils.py b/src/nomad_material_processing/utils.py index 984132b6..9bdfb7e5 100644 --- a/src/nomad_material_processing/utils.py +++ b/src/nomad_material_processing/utils.py @@ -23,20 +23,22 @@ def get_reference(upload_id, entry_id): def get_entry_id_from_file_name(file_name, archive): from nomad.utils import hash + return hash(archive.metadata.upload_id, file_name) def create_archive(entity, archive, file_name) -> str: import json + from nomad.datamodel.context import ClientContext + if isinstance(archive.m_context, ClientContext): return None if not archive.m_context.raw_path_exists(file_name): entity_entry = entity.m_to_dict(with_root_def=True) with archive.m_context.raw_file(file_name, 'w') as outfile: - json.dump({"data": entity_entry}, outfile) + json.dump({'data': entity_entry}, outfile) archive.m_context.process_updated_raw_file(file_name) return get_reference( - archive.metadata.upload_id, - get_entry_id_from_file_name(file_name, archive) + archive.metadata.upload_id, get_entry_id_from_file_name(file_name, archive) ) diff --git a/src/nomad_material_processing/vapor_deposition/__init__.py b/src/nomad_material_processing/vapor_deposition/__init__.py index db8a0aa2..bccd9266 100644 --- a/src/nomad_material_processing/vapor_deposition/__init__.py +++ b/src/nomad_material_processing/vapor_deposition/__init__.py @@ -15,715 +15,18 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from typing import ( - TYPE_CHECKING, -) -from nomad.metainfo import ( - Package, - Section, - SubSection, - Quantity, - MEnum, -) -from nomad.datamodel.data import ( - ArchiveSection, -) -from nomad.datamodel.metainfo.annotations import ( - ELNAnnotation, - ELNComponentEnum, -) -from nomad.datamodel.metainfo.basesections import ( - Entity, - ActivityStep, - PureSubstanceSection, - Component, - CompositeSystemReference, - PubChemPureSubstanceSection, -) -from nomad.datamodel.metainfo.plot import ( - PlotSection, -) -from nomad.datamodel.metainfo.workflow import ( - Link, - Task, -) -from nomad_material_processing import ( - SampleDeposition, - ThinFilmStackReference, - ThinFilmReference, - TimeSeries, - Geometry, -) - -if TYPE_CHECKING: - from nomad.datamodel.datamodel import ( - EntryArchive, - ) - from structlog.stdlib import ( - BoundLogger, - ) - -m_package = Package(name='Vapor Deposition') - - -class InsertReduction(Entity): - """ - The reduction that sometimes is used to lodge the substrate in the substrate holder position.. - """ - - name = Quantity( - type=str, - description=""" - A short and descriptive name for this insert reduction. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.StringEditQuantity, - ), - ) - insert_id = Quantity( - type=str, - description=""" - The ID of the insert that is placed in this position to accomodate the substrate. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.StringEditQuantity, - ), - ) - material = SubSection(section_def=PubChemPureSubstanceSection, repeats=True) - inner_geometry = SubSection( - section_def=Geometry, - ) - outer_geometry = SubSection( - section_def=Geometry, - ) - - -class SubstrateHolderPosition(ArchiveSection): - """ - One casing position of the substrate holder. - """ - - name = Quantity( - type=str, - description=""" - A short name for this position. This name is used as label of the position. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.StringEditQuantity, - ), - ) - x_position = Quantity( - type=float, - unit='meter', - description=""" - The x coordinate of the substrate holder position relative to the center of the holder. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='millimeter', - ), - ) - y_position = Quantity( - type=float, - unit='meter', - description=""" - The y coordinate of the substrate holder position relative to the center of the holder. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='millimeter', - ), - ) - slot_geometry = SubSection( - section_def=Geometry, - ) - insert_reduction = SubSection( - section_def=InsertReduction, - description='Optional description of insert if used.', - ) - - -class SubstrateHolder(Entity): - """ - The holder for the substrate. - """ - - name = Quantity( - type=str, - description=""" - A short and descriptive name for this position. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.StringEditQuantity, - ), - ) - lab_id = Quantity( - type=str, - description=""" - The lab ID of the substrate holder. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.StringEditQuantity, - ), - ) - material = SubSection(section_def=PubChemPureSubstanceSection, repeats=True) - thickness = Quantity( - type=float, - unit='meter', - description=""" - The thickness of the holder to the back of the substrate. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='micrometer', - ), - ) - outer_diameter = Quantity( - type=float, - unit='meter', - description=""" - The outer diameter of the substrate holder. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit='millimeter', - ), - ) - number_of_positions = Quantity( - type=int, - description=""" - The number of positions on the holder. - """, - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - ), - ) - image = Quantity( - type=str, - description="""An image of the substrate holder.""", - a_browser={'adaptor': 'RawFileAdaptor'}, - a_eln=ELNAnnotation( - component=ELNComponentEnum.FileEditQuantity, - ), - ) - positions = SubSection( - section_def=SubstrateHolderPosition, - repeats=True, - ) - - -class FilledSubstrateHolderPosition(SubstrateHolderPosition): - """ - One casing position of the filled substrate holder. - """ - - substrate = SubSection( - section_def=CompositeSystemReference, - description=""" - The substrate that is placed in this position. - """, - ) - - -class FilledSubstrateHolder(SubstrateHolder): - """ - A substrate holder that is filled with substrate(s). - """ - - substrate_holder = SubSection( - section_def=SubstrateHolder, - ) - positions = SubSection( - section_def=FilledSubstrateHolderPosition, - repeats=True, - ) - - -class MolarFlowRate(TimeSeries): - """ - Molar flow rate is the amount of a substance which passes per unit of time. - """ - - m_def = Section( - a_plot=dict( - # x=['time', 'set_time'], - # y=['value', 'set_value'], - x='time', - y='value', - ), - ) - measurement_type = Quantity( - type=MEnum( - 'Assumed', - 'Mass Flow Controller', - ), - a_eln=ELNAnnotation( - component=ELNComponentEnum.EnumEditQuantity, - ), - ) - value = TimeSeries.value.m_copy() - value.unit = 'mol/second' - set_value = TimeSeries.set_value.m_copy() - set_value.unit = 'mol/second' - - -class EvaporationSource(ArchiveSection): - pass - +from nomad.config.models.plugins import SchemaPackageEntryPoint -class VaporDepositionSource(ArchiveSection): - name = Quantity( - type=str, - description=""" - A short and descriptive name for this source. - """, - ) - material = SubSection( - section_def=Component, - description=""" - The source of the material that is being evaporated. - Example: A sputtering target, a powder in a crucible, etc. - """, - repeats=True, - ) - vapor_source = SubSection( - section_def=EvaporationSource, - description=""" - Example: A heater, a filament, a laser, a bubbler, etc. - """, - ) - vapor_molar_flow_rate = SubSection( - section_def=MolarFlowRate, - description=""" - The rate of the material being evaporated (mol/time). - """, - ) +class GeneralVdSchemaPackageEntryPoint(SchemaPackageEntryPoint): + def load(self): + from nomad_material_processing.vapor_deposition.general import m_package -class GrowthRate(TimeSeries): - m_def = Section( - a_plot=dict( - # x=['time', 'set_time'], - # y=['value', 'set_value'], - x='time', - y='value', - ), - ) - measurement_type = Quantity( - type=MEnum( - 'Assumed', - 'RHEED', - 'Reflectance', - ), - a_eln=ELNAnnotation( - component=ELNComponentEnum.EnumEditQuantity, - ), - ) - # value = TimeSeries.value.m_copy() - # value.unit = 'meter/second' - # set_value = TimeSeries.set_value.m_copy() - # set_value.unit = 'meter/second' - # set_value.a_eln.defaultDisplayUnit = 'nm/second' - value = Quantity( - type=float, - unit='meter/second', - shape=['*'], - ) - set_value = Quantity( - type=float, - description='The set value(s) (i.e. the intended values) set.', - shape=['*'], - unit='meter/second', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - label='Set value', - ), - ) + return m_package -class Temperature(TimeSeries): - """ - Generic Temperature monitoring - """ - - m_def = Section( - a_plot=dict( - # x=['time', 'set_time'], - # y=['value', 'set_value'], - x='time', - y='value', - ), - ) - measurement_type = Quantity( - type=MEnum( - 'Heater thermocouple', - 'Thermocouple', - 'Pyrometer', - 'Assumed', - ), - a_eln=ELNAnnotation( - component=ELNComponentEnum.EnumEditQuantity, - ), - ) - # value = TimeSeries.value.m_copy() - # value.unit = 'kelvin' - # set_value = TimeSeries.set_value.m_copy() - # set_value.unit = 'kelvin' - # set_value.a_eln.defaultDisplayUnit = 'celsius' - value = Quantity( - type=float, - unit='kelvin', - shape=['*'], - ) - set_value = Quantity( - type=float, - description='The set value(s) (i.e. the intended values) set.', - shape=['*'], - unit='kelvin', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - label='Set value', - ), - ) - - -class SampleParameters(PlotSection, ArchiveSection): - m_def = Section( - a_plotly_graph_object={ - 'label': 'Measured Temperatures', - 'index': 1, - 'dragmode': 'pan', - 'data': { - 'type': 'scattergl', - 'line': {'width': 2}, - 'marker': {'size': 2}, - 'mode': 'lines+markers', - 'name': 'Temperature', - 'x': '#temperature/time', - 'y': '#temperature/value', - }, - 'layout': { - 'title': {'text': 'Measured Temperature'}, - 'xaxis': { - 'showticklabels': True, - 'fixedrange': True, - 'ticks': '', - 'title': {'text': 'Process time [s]'}, - 'showline': True, - 'linewidth': 1, - 'linecolor': 'black', - 'mirror': True, - }, - 'yaxis': { - 'showticklabels': True, - 'fixedrange': True, - 'ticks': '', - 'title': {'text': 'Temperature [°C]'}, - 'showline': True, - 'linewidth': 1, - 'linecolor': 'black', - 'mirror': True, - }, - 'showlegend': False, - }, - 'config': { - 'displayModeBar': False, - 'scrollZoom': False, - 'responsive': False, - 'displaylogo': False, - 'dragmode': False, - }, - }, - ) - growth_rate = SubSection( - section_def=GrowthRate, - description=""" - The growth rate of the thin film (length/time). - Measured by in-situ RHEED or Reflection or assumed. - """, - ) - substrate_temperature = SubSection( - section_def=Temperature, - ) - layer = SubSection( - description=""" - The thin film that is being created during this step. - """, - section_def=ThinFilmReference, - ) - substrate = SubSection( - description=""" - The thin film stack that is being evaporated on. - """, - section_def=ThinFilmStackReference, - ) - - -class Pressure(TimeSeries): - """ - The pressure during the deposition process. - """ - - m_def = Section( - a_plot=dict( - # x=['time', 'set_time'], - # y=['value', 'set_value'], - x='time', - y='value', - ), - ) - # value = TimeSeries.value.m_copy() - # value.unit = 'pascal' - # set_value = TimeSeries.set_value.m_copy() - # set_value.unit = 'pascal' - # set_value.a_eln.defaultDisplayUnit = 'mbar' - value = Quantity( - type=float, - unit='pascal', - shape=['*'], - ) - time = Quantity( - type=float, - unit='second', - shape=['*'], - ) - set_value = Quantity( - type=float, - unit='pascal', - shape=['*'], - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - label='Set value', - ), - ) - set_time = Quantity( - type=float, - unit='second', - shape=['*'], - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - label='Set time', - ), - ) - - -class VolumetricFlowRate(TimeSeries): - """ - The volumetric flow rate of a gas at standard conditions, i.e. the equivalent rate - at a temperature of 0 °C (273.15 K) and a pressure of 1 atm (101325 Pa). - """ - - m_def = Section( - a_plot=dict( - # x=['time', 'set_time'], - # y=['value', 'set_value'], - x='time', - y='value', - ), - ) - measurement_type = Quantity( - type=MEnum( - 'Mass Flow Controller', - 'Flow Meter', - 'Other', - ), - ) - # value = TimeSeries.value.m_copy() - # value.unit = 'meter ** 3 / second' - # set_value = TimeSeries.set_value.m_copy() - # set_value.unit = 'meter ** 3 / second' - # set_value.a_eln.defaultDisplayUnit = 'centimeter ** 3 / minute' - value = Quantity( - type=float, - unit='meter ** 3 / second', - shape=['*'], - ) - set_value = Quantity( - type=float, - description='The set value(s) (i.e. the intended values) set.', - shape=['*'], - unit='meter ** 3 / second', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - label='Set value', - defaultDisplayUnit='centimeter ** 3 / minute', - ), - ) - - -class GasFlow(ArchiveSection): - """ - Section describing the flow of a gas. - """ - - m_def = Section( - a_plot=dict( - # x=['flow_rate/time', 'flow_rate/set_time'], - # y=['flow_rate/value', 'flow_rate/set_value'], - x='flow_rate/time', - y='flow_rate/value', - ), - ) - gas = SubSection( - section_def=PureSubstanceSection, - ) - flow_rate = SubSection( - section_def=VolumetricFlowRate, - ) - - -class SubstrateHeater(ArchiveSection): - pass - - -class ChamberEnvironment(ArchiveSection): - m_def = Section( - a_plot=dict( - x='pressure/time', - y='pressure/value', - ), - ) - gas_flow = SubSection( - section_def=GasFlow, - repeats=True, - ) - pressure = SubSection( - section_def=Pressure, - ) - heater = SubSection( - section_def=SubstrateHeater, - ) - - -class VaporDepositionStep(ActivityStep): - """ - A step of any vapor deposition process. - """ - - m_def = Section() - creates_new_thin_film = Quantity( - type=bool, - description=""" - Whether or not this step creates a new thin film. - """, - default=False, - a_eln=ELNAnnotation( - component='BoolEditQuantity', - ), - ) - duration = Quantity( - type=float, - unit='second', - ) - sources = SubSection( - section_def=VaporDepositionSource, - repeats=True, - ) - sample_parameters = SubSection( - section_def=SampleParameters, - repeats=True, - ) - environment = SubSection( - section_def=ChamberEnvironment, - ) - - def to_task(self) -> Task: - """ - Returns the task description of this activity step. - - Returns: - Task: The activity step as a workflow task. - """ - inputs = [] - for source in self.sources: - if source.material is not None and hasattr(source.material, 'system'): - inputs.append( - Link( - name=getattr(source.material, 'name', None), - section=getattr(source.material, 'system', None), - ) - ) - elif source.material is not None and hasattr( - source.material, 'pure_substance' - ): - inputs.append( - Link( - name=getattr(source.material, 'substance_name', None), - section=getattr(source.material, 'pure_substance', None), - ) - ) - outputs = [ - Link( - name=parameters.layer.name, - section=parameters.layer.reference, - ) - for parameters in self.sample_parameters - if parameters.layer is not None and parameters.layer.reference is not None - ] - return Task(name=self.name, inputs=inputs, outputs=outputs) - - def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - """ - The normalizer for the `VaporDepositionStep` class. - - Args: - archive (EntryArchive): The archive containing the section that is being - normalized. - logger (BoundLogger): A structlog logger. - """ - super(VaporDepositionStep, self).normalize(archive, logger) - - -class VaporDeposition(SampleDeposition): - """ - VaporDeposition is a general class that encompasses both Physical Vapor Deposition - (PVD) and Chemical Vapor Deposition (CVD). - It involves the deposition of material from a vapor phase to a solid thin film or - coating onto a substrate. - - material sources: - Both PVD and CVD involve a source material that is transformed into a vapor phase. - In PVD, the source material is physically evaporated or sputtered from a solid - target. - In CVD, gaseous precursors undergo chemical reactions to produce a solid material - on the substrate. - - substrate: - The substrate is the material onto which the thin film is deposited. - - environment: - The process typically takes place in a controlled environment. - The deposition is usually affected by the pressure in the chamber. - For some processes additional background gasses are also added. - """ - - m_def = Section( - links=[ - 'http://purl.obolibrary.org/obo/CHMO_0001314', - 'http://purl.obolibrary.org/obo/CHMO_0001356', - ], - a_plot=[ - dict( - x='steps/:/environment/pressure/time', - y='steps/:/environment/pressure/value', - ), - ], - ) - steps = SubSection( - description=""" - The steps of the deposition process. - """, - section_def=VaporDepositionStep, - repeats=True, - ) - - def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - """ - The normalizer for the `VaporDeposition` class. - - Args: - archive (EntryArchive): The archive containing the section that is being - normalized. - logger (BoundLogger): A structlog logger. - """ - super(VaporDeposition, self).normalize(archive, logger) - - -m_package.__init_metainfo__() +schema = GeneralVdSchemaPackageEntryPoint( + name='General Vapor Deposition Schema', + description="""Schema package containing basic classes used + in the vapor_deposition submodule.""", +) diff --git a/src/nomad_material_processing/vapor_deposition/cvd/__init__.py b/src/nomad_material_processing/vapor_deposition/cvd/__init__.py index b81b6ca4..4110becd 100644 --- a/src/nomad_material_processing/vapor_deposition/cvd/__init__.py +++ b/src/nomad_material_processing/vapor_deposition/cvd/__init__.py @@ -1,458 +1,14 @@ -# -# 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 typing import ( - TYPE_CHECKING, -) -from nomad.metainfo import ( - Package, - Section, - SubSection, - Quantity, - MEnum, -) - -from nomad.datamodel.metainfo.annotations import ( - ELNAnnotation, - ELNComponentEnum, -) - -from nomad.datamodel.data import ( - ArchiveSection, -) -from nomad.datamodel.metainfo.plot import PlotSection, PlotlyFigure - -from nomad.datamodel.metainfo.basesections import ( - PubChemPureSubstanceSection, - PureSubstanceComponent, -) -from nomad_material_processing import ( - TimeSeries, -) -from nomad_material_processing.vapor_deposition import ( - EvaporationSource, - VaporDepositionSource, - MolarFlowRate, - VolumetricFlowRate, - Pressure, - Temperature, - GasFlow, -) - - -if TYPE_CHECKING: - from nomad.datamodel.datamodel import ( - EntryArchive, - ) - from structlog.stdlib import ( - BoundLogger, - ) - -m_package = Package(name="Chemical Vapor Deposition") - - -class ComponentConcentration(PureSubstanceComponent): - """ - The concentration of a component in a mixed material. - """ - - theoretical_concentration = Quantity( - type=float, - description='The concentration planned for the component.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='mol / liter', - minValue=0, - ), - unit='mol / liter', - ) - effective_concentration = Quantity( - type=float, - description='The concentration calculated from the component moles and total volume.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='mol / liter', - minValue=0, - ), - unit='mol / liter', - ) - -class PushPurgeGasFlow(GasFlow): - """ - Section describing the flow of a gas. - """ - - m_def = Section( - a_plot=[dict( - # x=['flow_rate/time', 'flow_rate/set_time'], - # y=['flow_rate/value', 'flow_rate/set_value'], - x='flow_rate/time', - y='flow_rate/value', - ), - dict( - x='purge_flow_rate/time', - y='purge_flow_rate/value', - ), - ], - ) - flow_rate = SubSection( - section_def=VolumetricFlowRate, - label="push_flow_rate", - ) - purge_flow_rate = SubSection( - section_def=VolumetricFlowRate, - ) - - -class Rotation(TimeSeries): - """ - Rotation - """ - - m_def = Section(label_quantity="set_value") - - set_value = Quantity( - type=float, - description="The value scalar set for this parameter.", - a_eln=ELNAnnotation( - component="NumberEditQuantity", - defaultDisplayUnit="rpm", - ), - unit="rpm", - ) - value = Quantity( - type=float, - description="The rotation of the sample holder, or susceptor.", - # a_eln=ELNAnnotation( - # component="NumberEditQuantity", - # defaultDisplayUnit="rpm", - # ), - unit="rpm", - ) - - -class PartialVaporPressure(Pressure): - """ - The Partial Vapor Pressure (or Equilibrium Vapor Pressure), p, is the pressure exerted - by a vapor in thermodynamic equilibrium with its condensed phases (solid or liquid) - at a given temperature in a closed system. - - It can be approximately calculated by the semiempirical Antoine equation. - It is a relation between the vapor pressure and temperature of pure substances. - log10(p) = A - [B / (T + C)] - https://en.wikipedia.org/wiki/Vapor_pressure - The August-Antoine equation is a simplified version of the Antoine equation, - sometimes used to calculate Partial Vapor Pressure. - This assumes a temperature-independent heat of vaporization, i.e., C = 0. - https://en.wikipedia.org/wiki/Antoine_equation - """ - - set_value = Quantity( - type=float, - description="The value scalar set for this parameter.", - a_eln={"component": "NumberEditQuantity", "defaultDisplayUnit": "mbar"}, - unit="pascal", - ) - value = Quantity( - type=float, - unit="pascal", - shape=["*"], - ) - time = Quantity( - type=float, - unit="second", - shape=["*"], - ) - - -class BubblerMolarFlowRate(MolarFlowRate): - """ - Molar flow rate is the amount of a substance which passes per unit of time. - - The article cited below explains the equation used in MOVPE to calculate the molar flow rate. - - F_r = F_c*P_r / (P_0 - P_r) - - where: - - F_r is the molar flow rate, - F_c is the carrier gas flow rate, - P_r is the partial vapor pressure of the precursor, - P_0 is the total pressure exiting the bubbler. - - Reference: - Journal of Vacuum Science & Technology A 8, 800 (1990); doi: 10.1116/1.576921 - - """ - - value = MolarFlowRate.value.m_copy() - set_value = MolarFlowRate.set_value.m_copy() - set_value.a_eln.defaultDisplayUnit = "mol/minute" +from nomad.config.models.plugins import SchemaPackageEntryPoint -class CVDEvaporationSource(EvaporationSource): - pressure = SubSection( - section_def=Pressure, - ) - precursor_partial_pressure = SubSection( - section_def=PartialVaporPressure, - ) - temperature = SubSection( - section_def=Temperature, - ) - total_flow_rate = SubSection( - section_def=VolumetricFlowRate, - description=""" - The total flow rate exiting the source. - It can be the sum of precursor and carrier gas or only a gas, - depending on the nature of the source. - """, - ) +class CvdSchemaPackageEntryPoint(SchemaPackageEntryPoint): + def load(self): + from nomad_material_processing.vapor_deposition.cvd.general import m_package + return m_package -class BubblerEvaporator(CVDEvaporationSource): - """ - Delivers precursor materials to the reaction chamber. - It serves as a mechanism for introducing volatile liquid or solid precursors into the gas phase, - where they can react and deposit onto a substrate surface to form thin films or coatings. - Key components: - - Bubbler Vessel: This vessel holds the precursor material. - - Heating Element: To facilitate vaporization. - - Gas Inlet and Outlet: Gas delivery system via gas inlet and outlet ports. - - Temperature Control: Maintain the vapor pressure of the precursor at the desired level. - - Operation: - - Loading Precursor: The precursor material is loaded into the bubbler vessel - - Heating: The heating element is activated to form a vapor phase above the liquid or solid. - - Gas Flow: Carrier gas is bubbled through the precursor material. - - Transport: The precursor vapor is delivered to the reaction chamber. - The precursor undergoes decomposition or reaction on the substrate surface, - leading to thin film growth. - """ - - carrier_gas = SubSection( - section_def=PubChemPureSubstanceSection, - ) - carrier_push_flow_rate = SubSection( - section_def=VolumetricFlowRate, - description=""" - The flow through the push valve. - """, - ) - carrier_purge_flow_rate = SubSection( - section_def=VolumetricFlowRate, - description=""" - The flow through the purge valve. - """, - ) - dilution = Quantity( - type=float, - description="ONLY FOR DOPING PRECURSOR", - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit="cm ** 3 / minute", - ), - unit="cm ** 3 / minute", - ) - source = Quantity( - type=float, - description="ONLY FOR DOPING PRECURSOR", - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit="cm ** 3 / minute", - ), - unit="cm ** 3 / minute", - ) - inject = Quantity( - type=float, - description="ONLY FOR DOPING PRECURSOR", - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - defaultDisplayUnit="cm ** 3 / minute", - ), - unit="cm ** 3 / minute", - ) - - -class FlashEvaporator(CVDEvaporationSource): - """ - Flash Evaporator Unit: It typically comprises a reservoir where the metalorganic precursor, often in liquid form, is stored. - - Components: - - - Heating Mechanism. - - Carrier Gas Inlet. - - Precursor Delivery Pathway. - - Temperature Control System. - - Operation: - - - Loading of Precursor. - - Vaporization Process. - - Carrier Gas Introduction. - - Transport to Reaction Chamber. - - Temperature Regulation. - """ - - carrier_gas = SubSection( - section_def=PubChemPureSubstanceSection, - ) - carrier_push_flow_rate = SubSection( - section_def=VolumetricFlowRate, - description=""" - The flow through the push valve. - """, - ) - carrier_purge_flow_rate = SubSection( - section_def=VolumetricFlowRate, - description=""" - The flow through the purge valve. - """, - ) - pass - - -class MistEvaporator(CVDEvaporationSource): - """ - MIST-CVD source is a novel method for the deposition of thin films. - """ - - -class GasLineEvaporator(CVDEvaporationSource): - """ - In chemical vapor deposition (CVD), the gas supply plays a critical role - in providing the necessary precursor molecules for the deposition process. - - Gas lines are used to transport the precursor gases from their source to the reaction chamber. - These lines are often made of materials that are compatible with the precursor gases - and can withstand the process conditions. - They may also be heated or insulated to maintain the gases at the desired temperature - and prevent condensation or undesired reactions within the lines. - """ - - pass - - -class GasCylinderEvaporator(CVDEvaporationSource): - """ - In chemical vapor deposition (CVD), the gas supply plays a critical role - in providing the necessary precursor molecules for the deposition process. - - Contains the precursor gases under pressure. - These cylinders are connected to the CVD chamber through a system of valves, - regulators, and tubing. - The flow rate of each gas can be controlled precisely using flow meters - or mass flow controllers to achieve the desired deposition conditions. - """ - - dilution_in_cylinder = Quantity( - type=float, - description='The gas dilution in the cylinder.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - ), - ) - - effective_flow_rate = SubSection( - section_def=VolumetricFlowRate, - description=""" - Effective flow rate, to be defined better. - """, - ) - - -class CVDSource(VaporDepositionSource): - """ - A source of vapor for chemical vapor deposition (CVD) processes. - """ - valve = Quantity( - type=bool, - description='is the valve open?', - a_eln=ELNAnnotation( - component='BoolEditQuantity', - ), - ) - vapor_source = SubSection( - section_def=CVDEvaporationSource, - description=""" - Example: A heater, a filament, a laser, a bubbler, etc. - """, - ) - - -class BubblerSource(CVDSource): - vapor_source = SubSection( - section_def=BubblerEvaporator, - ) - - -class GasLineSource(CVDSource): - vapor_source = SubSection( - section_def=GasLineEvaporator, - ) - - -class GasCylinderSource(CVDSource): - vapor_source = SubSection( - section_def=GasCylinderEvaporator, - ) - - -class FlashSource(CVDSource): - vapor_source = SubSection( - section_def=FlashEvaporator, - description=""" - Example: A heater, a filament, a laser, a bubbler, etc. - """, - ) - - -class MistSource(CVDSource): - """ - Mist-CVD source is a novel method for the deposition of thin films. - """ - item = Quantity( - type=str, - description='An ID used to identify the solution.', - a_eln=ELNAnnotation( - component='StringEditQuantity', - ), - ) - stirring_time = Quantity( - type=float, - description='Solution stirring time.', - a_eln=ELNAnnotation( - component='NumberEditQuantity', - defaultDisplayUnit='minute', - ), - unit='second', - ) - description = Quantity( - type=str, - description='Some notes.', - a_eln=ELNAnnotation( - component='StringEditQuantity', - ), - ) - vapor_source = SubSection( - section_def=MistEvaporator, - ) - material = SubSection(section_def=ComponentConcentration, repeats=True) - - -m_package.__init_metainfo__() +schema = CvdSchemaPackageEntryPoint( + name='CVD Schema', + description='Schema package for general CVD techniques.', +) diff --git a/src/nomad_material_processing/vapor_deposition/cvd/general.py b/src/nomad_material_processing/vapor_deposition/cvd/general.py new file mode 100644 index 00000000..ce289f1b --- /dev/null +++ b/src/nomad_material_processing/vapor_deposition/cvd/general.py @@ -0,0 +1,468 @@ +# +# 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 typing import ( + TYPE_CHECKING, +) + +from nomad.datamodel.metainfo.annotations import ( + ELNAnnotation, + ELNComponentEnum, +) +from nomad.datamodel.metainfo.basesections import ( + PubChemPureSubstanceSection, + PureSubstanceComponent, +) +from nomad.metainfo import ( + Quantity, + SchemaPackage, + Section, + SubSection, +) + +from nomad_material_processing.general import ( + TimeSeries, +) +from nomad_material_processing.vapor_deposition.general import ( + EvaporationSource, + GasFlow, + MolarFlowRate, + Pressure, + Temperature, + VaporDepositionSource, + VolumetricFlowRate, +) + +if TYPE_CHECKING: + pass + +from nomad.config import config + +m_package = SchemaPackage( + aliases=[ + 'nomad_material_processing.vapor_deposition.cvd', + ] +) + +configuration = config.get_plugin_entry_point( + 'nomad_material_processing.vapor_deposition.cvd:schema', +) + + +class ComponentConcentration(PureSubstanceComponent): + """ + The concentration of a component in a mixed material. + """ + + theoretical_concentration = Quantity( + type=float, + description='The concentration planned for the component.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='mol / liter', + minValue=0, + ), + unit='mol / liter', + ) + effective_concentration = Quantity( + type=float, + description="""The concentration calculated from + the component moles and total volume.""", + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='mol / liter', + minValue=0, + ), + unit='mol / liter', + ) + + +class PushPurgeGasFlow(GasFlow): + """ + Section describing the flow of a gas. + """ + + m_def = Section( + a_plot=[ + dict( + # x=['flow_rate/time', 'flow_rate/set_time'], + # y=['flow_rate/value', 'flow_rate/set_value'], + x='flow_rate/time', + y='flow_rate/value', + ), + dict( + x='purge_flow_rate/time', + y='purge_flow_rate/value', + ), + ], + ) + flow_rate = SubSection( + section_def=VolumetricFlowRate, + label='push_flow_rate', + ) + purge_flow_rate = SubSection( + section_def=VolumetricFlowRate, + ) + + +class Rotation(TimeSeries): + """ + Rotation + """ + + m_def = Section(label_quantity='set_value') + + set_value = Quantity( + type=float, + description='The value scalar set for this parameter.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='rpm', + ), + unit='rpm', + ) + value = Quantity( + type=float, + description='The rotation of the sample holder, or susceptor.', + # a_eln=ELNAnnotation( + # component="NumberEditQuantity", + # defaultDisplayUnit="rpm", + # ), + unit='rpm', + ) + + +class PartialVaporPressure(Pressure): + """ + The Partial Vapor Pressure (or Equilibrium Vapor Pressure), p, is the pressure + exerted by a vapor in thermodynamic equilibrium with its condensed phases + (solid or liquid) at a given temperature in a closed system. + + It can be approximately calculated by the semiempirical Antoine equation. + It is a relation between the vapor pressure and temperature of pure substances. + log10(p) = A - [B / (T + C)] + https://en.wikipedia.org/wiki/Vapor_pressure + The August-Antoine equation is a simplified version of the Antoine equation, + sometimes used to calculate Partial Vapor Pressure. + This assumes a temperature-independent heat of vaporization, i.e., C = 0. + https://en.wikipedia.org/wiki/Antoine_equation + """ + + set_value = Quantity( + type=float, + description='The value scalar set for this parameter.', + a_eln={'component': 'NumberEditQuantity', 'defaultDisplayUnit': 'mbar'}, + unit='pascal', + ) + value = Quantity( + type=float, + unit='pascal', + shape=['*'], + ) + time = Quantity( + type=float, + unit='second', + shape=['*'], + ) + + +class BubblerMolarFlowRate(MolarFlowRate): + """ + Molar flow rate is the amount of a substance which passes per unit of time. + + The article cited below explains the equation used in MOVPE + to calculate the molar flow rate. + + F_r = F_c*P_r / (P_0 - P_r) + + where: + + F_r is the molar flow rate, + F_c is the carrier gas flow rate, + P_r is the partial vapor pressure of the precursor, + P_0 is the total pressure exiting the bubbler. + + Reference: + Journal of Vacuum Science & Technology A 8, 800 (1990); doi: 10.1116/1.576921 + + """ + + value = MolarFlowRate.value.m_copy() + set_value = MolarFlowRate.set_value.m_copy() + set_value.a_eln.defaultDisplayUnit = 'mol/minute' + + +class CVDEvaporationSource(EvaporationSource): + pressure = SubSection( + section_def=Pressure, + ) + precursor_partial_pressure = SubSection( + section_def=PartialVaporPressure, + ) + temperature = SubSection( + section_def=Temperature, + ) + total_flow_rate = SubSection( + section_def=VolumetricFlowRate, + description=""" + The total flow rate exiting the source. + It can be the sum of precursor and carrier gas or only a gas, + depending on the nature of the source. + """, + ) + + +class BubblerEvaporator(CVDEvaporationSource): + """ + Delivers precursor materials to the reaction chamber. + It serves as a mechanism for introducing volatile liquid or solid precursors into + the gas phase where they can react and deposit onto a substrate surface + to form thin films or coatings. + + Key components: + - Bubbler Vessel: This vessel holds the precursor material. + - Heating Element: To facilitate vaporization. + - Gas Inlet and Outlet: Gas delivery system via gas inlet and outlet ports. + - Temperature Control: Maintain the vapor pressure of the precursor + at the desired level. + + Operation: + - Loading Precursor: The precursor material is loaded into the bubbler vessel + - Heating: The heating element is activated to form a vapor phase + above the liquid or solid. + - Gas Flow: Carrier gas is bubbled through the precursor material. + - Transport: The precursor vapor is delivered to the reaction chamber. + The precursor undergoes decomposition or reaction on the substrate surface, + leading to thin film growth. + """ + + carrier_gas = SubSection( + section_def=PubChemPureSubstanceSection, + ) + carrier_push_flow_rate = SubSection( + section_def=VolumetricFlowRate, + description=""" + The flow through the push valve. + """, + ) + carrier_purge_flow_rate = SubSection( + section_def=VolumetricFlowRate, + description=""" + The flow through the purge valve. + """, + ) + dilution = Quantity( + type=float, + description='ONLY FOR DOPING PRECURSOR', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='cm ** 3 / minute', + ), + unit='cm ** 3 / minute', + ) + source = Quantity( + type=float, + description='ONLY FOR DOPING PRECURSOR', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='cm ** 3 / minute', + ), + unit='cm ** 3 / minute', + ) + inject = Quantity( + type=float, + description='ONLY FOR DOPING PRECURSOR', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='cm ** 3 / minute', + ), + unit='cm ** 3 / minute', + ) + + +class FlashEvaporator(CVDEvaporationSource): + """ + Flash Evaporator Unit: + it typically comprises a reservoir where + the metalorganic precursor, often in liquid form, is stored. + + Components: + + - Heating Mechanism. + - Carrier Gas Inlet. + - Precursor Delivery Pathway. + - Temperature Control System. + + Operation: + + - Loading of Precursor. + - Vaporization Process. + - Carrier Gas Introduction. + - Transport to Reaction Chamber. + - Temperature Regulation. + """ + + carrier_gas = SubSection( + section_def=PubChemPureSubstanceSection, + ) + carrier_push_flow_rate = SubSection( + section_def=VolumetricFlowRate, + description=""" + The flow through the push valve. + """, + ) + carrier_purge_flow_rate = SubSection( + section_def=VolumetricFlowRate, + description=""" + The flow through the purge valve. + """, + ) + pass + + +class MistEvaporator(CVDEvaporationSource): + """ + MIST-CVD source is a novel method for the deposition of thin films. + """ + + +class GasLineEvaporator(CVDEvaporationSource): + """ + In chemical vapor deposition (CVD), the gas supply plays a critical role + in providing the necessary precursor molecules for the deposition process. + + Gas lines are used to transport the precursor gases + from their source to the reaction chamber. + These lines are often made of materials that are compatible with the precursor gases + and can withstand the process conditions. + They may also be heated or insulated to maintain the gases at the desired + temperature and prevent condensation or undesired reactions within the lines. + """ + + pass + + +class GasCylinderEvaporator(CVDEvaporationSource): + """ + In chemical vapor deposition (CVD), the gas supply plays a critical role + in providing the necessary precursor molecules for the deposition process. + + Contains the precursor gases under pressure. + These cylinders are connected to the CVD chamber through a system of valves, + regulators, and tubing. + The flow rate of each gas can be controlled precisely using flow meters + or mass flow controllers to achieve the desired deposition conditions. + """ + + dilution_in_cylinder = Quantity( + type=float, + description='The gas dilution in the cylinder.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + ), + ) + + effective_flow_rate = SubSection( + section_def=VolumetricFlowRate, + description=""" + Effective flow rate, to be defined better. + """, + ) + + +class CVDSource(VaporDepositionSource): + """ + A source of vapor for chemical vapor deposition (CVD) processes. + """ + + valve = Quantity( + type=bool, + description='is the valve open?', + a_eln=ELNAnnotation( + component='BoolEditQuantity', + ), + ) + vapor_source = SubSection( + section_def=CVDEvaporationSource, + description=""" + Example: A heater, a filament, a laser, a bubbler, etc. + """, + ) + + +class BubblerSource(CVDSource): + vapor_source = SubSection( + section_def=BubblerEvaporator, + ) + + +class GasLineSource(CVDSource): + vapor_source = SubSection( + section_def=GasLineEvaporator, + ) + + +class GasCylinderSource(CVDSource): + vapor_source = SubSection( + section_def=GasCylinderEvaporator, + ) + + +class FlashSource(CVDSource): + vapor_source = SubSection( + section_def=FlashEvaporator, + description=""" + Example: A heater, a filament, a laser, a bubbler, etc. + """, + ) + + +class MistSource(CVDSource): + """ + Mist-CVD source is a novel method for the deposition of thin films. + """ + + item = Quantity( + type=str, + description='An ID used to identify the solution.', + a_eln=ELNAnnotation( + component='StringEditQuantity', + ), + ) + stirring_time = Quantity( + type=float, + description='Solution stirring time.', + a_eln=ELNAnnotation( + component='NumberEditQuantity', + defaultDisplayUnit='minute', + ), + unit='second', + ) + description = Quantity( + type=str, + description='Some notes.', + a_eln=ELNAnnotation( + component='StringEditQuantity', + ), + ) + vapor_source = SubSection( + section_def=MistEvaporator, + ) + material = SubSection(section_def=ComponentConcentration, repeats=True) + + +m_package.__init_metainfo__() diff --git a/src/nomad_material_processing/vapor_deposition/cvd/nomad_plugin.yaml b/src/nomad_material_processing/vapor_deposition/cvd/nomad_plugin.yaml deleted file mode 100644 index b3bc9a0c..00000000 --- a/src/nomad_material_processing/vapor_deposition/cvd/nomad_plugin.yaml +++ /dev/null @@ -1,3 +0,0 @@ -description: A plugin for NOMAD containing base sections for chemical vapor deposition. -name: Chemical Vapor Deposition -plugin_type: schema diff --git a/src/nomad_material_processing/vapor_deposition/general.py b/src/nomad_material_processing/vapor_deposition/general.py new file mode 100644 index 00000000..61a4e902 --- /dev/null +++ b/src/nomad_material_processing/vapor_deposition/general.py @@ -0,0 +1,761 @@ +# +# 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 typing import ( + TYPE_CHECKING, +) + +from nomad.datamodel.data import ( + ArchiveSection, +) +from nomad.datamodel.metainfo.annotations import ( + ELNAnnotation, + ELNComponentEnum, +) +from nomad.datamodel.metainfo.basesections import ( + ActivityStep, + Component, + CompositeSystemReference, + Entity, + PubChemPureSubstanceSection, + PureSubstanceSection, +) +from nomad.datamodel.metainfo.plot import ( + PlotSection, +) +from nomad.datamodel.metainfo.workflow import ( + Link, + Task, +) +from nomad.metainfo import ( + MEnum, + Quantity, + SchemaPackage, + Section, + SubSection, +) + +from nomad_material_processing.general import ( + Geometry, + SampleDeposition, + ThinFilmReference, + ThinFilmStackReference, + TimeSeries, +) + +if TYPE_CHECKING: + from nomad.datamodel.datamodel import ( + EntryArchive, + ) + from structlog.stdlib import ( + BoundLogger, + ) + +from nomad.config import config + +m_package = SchemaPackage( + name='Vapor Deposition', + aliases=[ + 'nomad_material_processing.vapor_deposition', + ], +) + +configuration = config.get_plugin_entry_point( + 'nomad_material_processing.vapor_deposition:schema', +) + + +class InsertReduction(Entity): + """ + The reduction that sometimes is used to lodge the substrate + in the substrate holder position. + """ + + name = Quantity( + type=str, + description=""" + A short and descriptive name for this insert reduction. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + ), + ) + lab_id = Quantity( + type=str, + description="""An ID string for the insert to be put in the substrate holder. + It is unique at least for the lab that produced this data.""", + a_eln=dict(component='StringEditQuantity', label='Insert ID'), + ) + image = Quantity( + type=str, + description="""A photograph or image of the insert + to be lodged in the substrate holder.""", + a_browser={'adaptor': 'RawFileAdaptor'}, + a_eln=ELNAnnotation( + component=ELNComponentEnum.FileEditQuantity, + ), + ) + material = SubSection(section_def=PubChemPureSubstanceSection, repeats=True) + inner_geometry = SubSection( + section_def=Geometry, + ) + outer_geometry = SubSection( + section_def=Geometry, + ) + + +class SubstrateHolderPosition(ArchiveSection): + """ + One casing position of the substrate holder. + """ + + name = Quantity( + type=str, + description=""" + A short name for this position. This name is used as label of the position. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + ), + ) + x_position = Quantity( + type=float, + unit='meter', + description=""" + The x coordinate of the substrate holder position + relative to the center of the holder. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='millimeter', + ), + ) + y_position = Quantity( + type=float, + unit='meter', + description=""" + The y coordinate of the substrate holder position + relative to the center of the holder. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='millimeter', + ), + ) + slot_geometry = SubSection( + section_def=Geometry, + ) + insert_reduction = Quantity( + type=InsertReduction, + description='Optional description of insert if used.', + a_eln=ELNAnnotation( + component='ReferenceEditQuantity', + label='ThinFilmStackMbe Reference', + ), + ) + + +class SubstrateHolder(Entity): + """ + The holder for the substrate. + """ + + name = Quantity( + type=str, + description=""" + A short and descriptive name for this position. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + ), + ) + lab_id = Quantity( + type=str, + description=""" + The lab ID of the substrate holder. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.StringEditQuantity, + ), + ) + material = SubSection(section_def=PubChemPureSubstanceSection, repeats=True) + thickness = Quantity( + type=float, + unit='meter', + description=""" + The thickness of the holder to the back of the substrate. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='micrometer', + ), + ) + outer_diameter = Quantity( + type=float, + unit='meter', + description=""" + The outer diameter of the substrate holder. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + defaultDisplayUnit='millimeter', + ), + ) + number_of_positions = Quantity( + type=int, + description=""" + The number of positions on the holder. + """, + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + ), + ) + image = Quantity( + type=str, + description="""An image of the substrate holder.""", + a_browser={'adaptor': 'RawFileAdaptor'}, + a_eln=ELNAnnotation( + component=ELNComponentEnum.FileEditQuantity, + ), + ) + positions = SubSection( + section_def=SubstrateHolderPosition, + repeats=True, + ) + + +class FilledSubstrateHolderPosition(SubstrateHolderPosition): + """ + One casing position of the filled substrate holder. + """ + + substrate = SubSection( + section_def=CompositeSystemReference, + description=""" + The substrate that is placed in this position. + """, + ) + + +class FilledSubstrateHolder(SubstrateHolder): + """ + A substrate holder that is filled with substrate(s). + """ + + substrate_holder = Quantity( + type=SubstrateHolder, + description='A reference to an empty substrate holder.', + a_eln=ELNAnnotation( + component='ReferenceEditQuantity', + label='ThinFilmStackMbe Reference', + ), + ) + positions = SubSection( + section_def=FilledSubstrateHolderPosition, + repeats=True, + ) + + +class MolarFlowRate(TimeSeries): + """ + Molar flow rate is the amount of a substance which passes per unit of time. + """ + + m_def = Section( + a_plot=dict( + # x=['time', 'set_time'], + # y=['value', 'set_value'], + x='time', + y='value', + ), + ) + measurement_type = Quantity( + type=MEnum( + 'Assumed', + 'Mass Flow Controller', + ), + a_eln=ELNAnnotation( + component=ELNComponentEnum.EnumEditQuantity, + ), + ) + value = TimeSeries.value.m_copy() + value.unit = 'mol/second' + set_value = TimeSeries.set_value.m_copy() + set_value.unit = 'mol/second' + + +class EvaporationSource(ArchiveSection): + pass + + +class VaporDepositionSource(ArchiveSection): + name = Quantity( + type=str, + description=""" + A short and descriptive name for this source. + """, + ) + material = SubSection( + section_def=Component, + description=""" + The source of the material that is being evaporated. + Example: A sputtering target, a powder in a crucible, etc. + """, + repeats=True, + ) + vapor_source = SubSection( + section_def=EvaporationSource, + description=""" + Example: A heater, a filament, a laser, a bubbler, etc. + """, + ) + vapor_molar_flow_rate = SubSection( + section_def=MolarFlowRate, + description=""" + The rate of the material being evaporated (mol/time). + """, + ) + + +class GrowthRate(TimeSeries): + m_def = Section( + a_plot=dict( + # x=['time', 'set_time'], + # y=['value', 'set_value'], + x='time', + y='value', + ), + ) + measurement_type = Quantity( + type=MEnum( + 'Assumed', + 'RHEED', + 'Reflectance', + ), + a_eln=ELNAnnotation( + component=ELNComponentEnum.EnumEditQuantity, + ), + ) + # value = TimeSeries.value.m_copy() + # value.unit = 'meter/second' + # set_value = TimeSeries.set_value.m_copy() + # set_value.unit = 'meter/second' + # set_value.a_eln.defaultDisplayUnit = 'nm/second' + value = Quantity( + type=float, + unit='meter/second', + shape=['*'], + ) + set_value = Quantity( + type=float, + description='The set value(s) (i.e. the intended values) set.', + shape=['*'], + unit='meter/second', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Set value', + ), + ) + + +class Temperature(TimeSeries): + """ + Generic Temperature monitoring + """ + + m_def = Section( + a_plot=dict( + # x=['time', 'set_time'], + # y=['value', 'set_value'], + x='time', + y='value', + ), + ) + measurement_type = Quantity( + type=MEnum( + 'Heater thermocouple', + 'Thermocouple', + 'Pyrometer', + 'Assumed', + ), + a_eln=ELNAnnotation( + component=ELNComponentEnum.EnumEditQuantity, + ), + ) + # value = TimeSeries.value.m_copy() + # value.unit = 'kelvin' + # set_value = TimeSeries.set_value.m_copy() + # set_value.unit = 'kelvin' + # set_value.a_eln.defaultDisplayUnit = 'celsius' + value = Quantity( + type=float, + unit='kelvin', + shape=['*'], + ) + set_value = Quantity( + type=float, + description='The set value(s) (i.e. the intended values) set.', + shape=['*'], + unit='kelvin', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Set value', + ), + ) + + +class SampleParameters(PlotSection, ArchiveSection): + m_def = Section( + a_plotly_graph_object={ + 'label': 'Measured Temperatures', + 'index': 1, + 'dragmode': 'pan', + 'data': { + 'type': 'scattergl', + 'line': {'width': 2}, + 'marker': {'size': 2}, + 'mode': 'lines+markers', + 'name': 'Temperature', + 'x': '#temperature/time', + 'y': '#temperature/value', + }, + 'layout': { + 'title': {'text': 'Measured Temperature'}, + 'xaxis': { + 'showticklabels': True, + 'fixedrange': True, + 'ticks': '', + 'title': {'text': 'Process time [s]'}, + 'showline': True, + 'linewidth': 1, + 'linecolor': 'black', + 'mirror': True, + }, + 'yaxis': { + 'showticklabels': True, + 'fixedrange': True, + 'ticks': '', + 'title': {'text': 'Temperature [°C]'}, + 'showline': True, + 'linewidth': 1, + 'linecolor': 'black', + 'mirror': True, + }, + 'showlegend': False, + }, + 'config': { + 'displayModeBar': False, + 'scrollZoom': False, + 'responsive': False, + 'displaylogo': False, + 'dragmode': False, + }, + }, + ) + growth_rate = SubSection( + section_def=GrowthRate, + description=""" + The growth rate of the thin film (length/time). + Measured by in-situ RHEED or Reflection or assumed. + """, + ) + substrate_temperature = SubSection( + section_def=Temperature, + ) + layer = SubSection( + description=""" + The thin film that is being created during this step. + """, + section_def=ThinFilmReference, + ) + substrate = SubSection( + description=""" + The thin film stack that is being evaporated on. + """, + section_def=ThinFilmStackReference, + ) + + +class Pressure(TimeSeries): + """ + The pressure during the deposition process. + """ + + m_def = Section( + a_plot=dict( + # x=['time', 'set_time'], + # y=['value', 'set_value'], + x='time', + y='value', + ), + ) + # value = TimeSeries.value.m_copy() + # value.unit = 'pascal' + # set_value = TimeSeries.set_value.m_copy() + # set_value.unit = 'pascal' + # set_value.a_eln.defaultDisplayUnit = 'mbar' + value = Quantity( + type=float, + unit='pascal', + shape=['*'], + ) + time = Quantity( + type=float, + unit='second', + shape=['*'], + ) + set_value = Quantity( + type=float, + unit='pascal', + shape=['*'], + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Set value', + ), + ) + set_time = Quantity( + type=float, + unit='second', + shape=['*'], + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Set time', + ), + ) + + +class VolumetricFlowRate(TimeSeries): + """ + The volumetric flow rate of a gas at standard conditions, i.e. the equivalent rate + at a temperature of 0 °C (273.15 K) and a pressure of 1 atm (101325 Pa). + """ + + m_def = Section( + a_plot=dict( + # x=['time', 'set_time'], + # y=['value', 'set_value'], + x='time', + y='value', + ), + ) + measurement_type = Quantity( + type=MEnum( + 'Mass Flow Controller', + 'Flow Meter', + 'Other', + ), + ) + # value = TimeSeries.value.m_copy() + # value.unit = 'meter ** 3 / second' + # set_value = TimeSeries.set_value.m_copy() + # set_value.unit = 'meter ** 3 / second' + # set_value.a_eln.defaultDisplayUnit = 'centimeter ** 3 / minute' + value = Quantity( + type=float, + unit='meter ** 3 / second', + shape=['*'], + ) + set_value = Quantity( + type=float, + description='The set value(s) (i.e. the intended values) set.', + shape=['*'], + unit='meter ** 3 / second', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Set value', + defaultDisplayUnit='centimeter ** 3 / minute', + ), + ) + + +class GasFlow(ArchiveSection): + """ + Section describing the flow of a gas. + """ + + m_def = Section( + a_plot=dict( + # x=['flow_rate/time', 'flow_rate/set_time'], + # y=['flow_rate/value', 'flow_rate/set_value'], + x='flow_rate/time', + y='flow_rate/value', + ), + ) + gas = SubSection( + section_def=PureSubstanceSection, + ) + flow_rate = SubSection( + section_def=VolumetricFlowRate, + ) + + +class SubstrateHeater(ArchiveSection): + pass + + +class ChamberEnvironment(ArchiveSection): + m_def = Section( + a_plot=dict( + x='pressure/time', + y='pressure/value', + ), + ) + gas_flow = SubSection( + section_def=GasFlow, + repeats=True, + ) + pressure = SubSection( + section_def=Pressure, + ) + heater = SubSection( + section_def=SubstrateHeater, + ) + + +class VaporDepositionStep(ActivityStep): + """ + A step of any vapor deposition process. + """ + + m_def = Section() + creates_new_thin_film = Quantity( + type=bool, + description=""" + Whether or not this step creates a new thin film. + """, + default=False, + a_eln=ELNAnnotation( + component='BoolEditQuantity', + ), + ) + duration = Quantity( + type=float, + unit='second', + ) + sources = SubSection( + section_def=VaporDepositionSource, + repeats=True, + ) + sample_parameters = SubSection( + section_def=SampleParameters, + repeats=True, + ) + environment = SubSection( + section_def=ChamberEnvironment, + ) + + def to_task(self) -> Task: + """ + Returns the task description of this activity step. + + Returns: + Task: The activity step as a workflow task. + """ + inputs = [] + for source in self.sources: + if source.material is not None and hasattr(source.material, 'system'): + inputs.append( + Link( + name=getattr(source.material, 'name', None), + section=getattr(source.material, 'system', None), + ) + ) + elif source.material is not None and hasattr( + source.material, 'pure_substance' + ): + inputs.append( + Link( + name=getattr(source.material, 'substance_name', None), + section=getattr(source.material, 'pure_substance', None), + ) + ) + outputs = [ + Link( + name=parameters.layer.name, + section=parameters.layer.reference, + ) + for parameters in self.sample_parameters + if parameters.layer is not None and parameters.layer.reference is not None + ] + return Task(name=self.name, inputs=inputs, outputs=outputs) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + """ + The normalizer for the `VaporDepositionStep` class. + + Args: + archive (EntryArchive): The archive containing the section that is being + normalized. + logger (BoundLogger): A structlog logger. + """ + super().normalize(archive, logger) + + +class VaporDeposition(SampleDeposition): + """ + VaporDeposition is a general class that encompasses both Physical Vapor Deposition + (PVD) and Chemical Vapor Deposition (CVD). + It involves the deposition of material from a vapor phase to a solid thin film or + coating onto a substrate. + - material sources: + Both PVD and CVD involve a source material + that is transformed into a vapor phase. + In PVD, the source material is physically evaporated or sputtered from a solid + target. + In CVD, gaseous precursors undergo chemical reactions to produce a solid material + on the substrate. + - substrate: + The substrate is the material onto which the thin film is deposited. + - environment: + The process typically takes place in a controlled environment. + The deposition is usually affected by the pressure in the chamber. + For some processes additional background gasses are also added. + """ + + m_def = Section( + links=[ + 'http://purl.obolibrary.org/obo/CHMO_0001314', + 'http://purl.obolibrary.org/obo/CHMO_0001356', + ], + a_plot=[ + dict( + x='steps/:/environment/pressure/time', + y='steps/:/environment/pressure/value', + ), + ], + ) + steps = SubSection( + description=""" + The steps of the deposition process. + """, + section_def=VaporDepositionStep, + repeats=True, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + """ + The normalizer for the `VaporDeposition` class. + + Args: + archive (EntryArchive): The archive containing the section that is being + normalized. + logger (BoundLogger): A structlog logger. + """ + super().normalize(archive, logger) + + +m_package.__init_metainfo__() diff --git a/src/nomad_material_processing/vapor_deposition/nomad_plugin.yaml b/src/nomad_material_processing/vapor_deposition/nomad_plugin.yaml deleted file mode 100644 index 5695f9df..00000000 --- a/src/nomad_material_processing/vapor_deposition/nomad_plugin.yaml +++ /dev/null @@ -1,3 +0,0 @@ -description: A plugin for NOMAD containing base sections for vapor deposition. -name: Vapor Deposition -plugin_type: schema diff --git a/src/nomad_material_processing/vapor_deposition/pvd/__init__.py b/src/nomad_material_processing/vapor_deposition/pvd/__init__.py index 74391ede..64e5e09e 100644 --- a/src/nomad_material_processing/vapor_deposition/pvd/__init__.py +++ b/src/nomad_material_processing/vapor_deposition/pvd/__init__.py @@ -15,257 +15,74 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from typing import ( - TYPE_CHECKING, -) -from nomad.metainfo import ( - Package, - Section, - SubSection, - Quantity, - MEnum, -) -from nomad.datamodel.metainfo.annotations import ( - ELNAnnotation, - ELNComponentEnum, -) - -from nomad_material_processing import ( - TimeSeries, -) -from nomad_material_processing.vapor_deposition import ( - EvaporationSource, - VaporDepositionSource, - SampleParameters, - VaporDepositionStep, - VaporDeposition, -) - -if TYPE_CHECKING: - from nomad.datamodel.datamodel import ( - EntryArchive, - ) - from structlog.stdlib import ( - BoundLogger, - ) - -m_package = Package(name='Physical Vapor Deposition') +from nomad.config.models.plugins import SchemaPackageEntryPoint -class SourcePower(TimeSeries): - """ - The power supplied to the source (watt). - """ +class GeneralPvdSchemaPackageEntryPoint(SchemaPackageEntryPoint): + def load(self): + from nomad_material_processing.vapor_deposition.pvd.general import m_package - m_def = Section( - a_plot=dict( - # x=['time', 'set_time'], - # y=['value', 'set_value'], - x='time', - y='value', - ), - ) + return m_package - value = Quantity( - type=float, - unit='watt', - shape=['*'], - ) - set_value = Quantity( - type=float, - description='The set value(s) (i.e. the intended values) set.', - shape=['*'], - unit='watt', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - label='Set value', - defaultDisplayUnit='watt', - ), - ) - - -class PVDEvaporationSource(EvaporationSource): - m_def = Section( - a_plot=dict( - x='power/time', - y='power/value', - ), - ) - power = SubSection( - section_def=SourcePower, - ) +schema = GeneralPvdSchemaPackageEntryPoint( + name='General PVD Schema', + description="""Schema package containing basic classes used + in the vapor_deposition submodule.""", +) -class ImpingingFlux(TimeSeries): - """ - The impinging flux of the material onto the substrate (mol/area/time). - """ - m_def = Section( - a_plot=dict( - # x=['time', 'set_time'], - # y=['value', 'set_value'], - x='time', - y='value', - ), - ) - measurement_type = Quantity( - type=MEnum( - 'Assumed', - 'Quartz Crystal Microbalance', - ), - a_eln=ELNAnnotation( - component=ELNComponentEnum.EnumEditQuantity, - ), - ) +class MbeSchemaPackageEntryPoint(SchemaPackageEntryPoint): + def load(self): + from nomad_material_processing.vapor_deposition.pvd.mbe import m_package - value = Quantity( - type=float, - unit='mol/meter ** 2/second', - shape=['*'], - ) - set_value = Quantity( - type=float, - description='The set value(s) (i.e. the intended values) set.', - shape=['*'], - unit='mol/meter ** 2/second', - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - label='Set value', - defaultDisplayUnit='mol/meter ** 2/second', - ), - ) + return m_package -class PVDSource(VaporDepositionSource): - m_def = Section( - a_plot=[ - dict( - x=[ - 'vapor_source/power/time', - 'impinging_flux/:/time', - ], - y=[ - 'vapor_source/power/value', - 'impinging_flux/:/value', - ], - ), - ], - ) - vapor_source = SubSection( - section_def=PVDEvaporationSource, - description=""" - Example: A heater, a filament, a laser, etc. - """, - ) - impinging_flux = SubSection( - section_def=ImpingingFlux, - description=""" - The deposition rate of the material onto the substrate (mol/area/time). - """, - repeats=True, - ) +mbe_schema = MbeSchemaPackageEntryPoint( + name='Mbe Schema', + description="""Schema package containing basic classes used + in the MBE submodule.""", +) -class PVDSampleParameters(SampleParameters): - heater = Quantity( - description=""" - What is the substrate heated by. - """, - type=MEnum( - 'No heating', - 'Halogen lamp', - 'Filament', - 'Resistive element', - 'CO2 laser', - ), - a_eln=ELNAnnotation( - component=ELNComponentEnum.EnumEditQuantity, - ), - ) - distance_to_source = Quantity( - type=float, - unit='meter', - description=""" - The distance between the substrate and all the sources. - In the case of multiple sources, the distances are listed in the same order as the - sources are listed in the parent `VaporDepositionStep` section. - """, - shape=['*'], - a_eln=ELNAnnotation( - component=ELNComponentEnum.NumberEditQuantity, - ), - ) +class PldSchemaPackageEntryPoint(SchemaPackageEntryPoint): + def load(self): + from nomad_material_processing.vapor_deposition.pvd.pld import m_package + return m_package -class PVDStep(VaporDepositionStep): - """ - A step of any physical vapor deposition process. - """ - sources = SubSection( - section_def=PVDSource, - repeats=True, - ) - sample_parameters = SubSection( - section_def=PVDSampleParameters, - repeats=True, - ) +pld_schema = PldSchemaPackageEntryPoint( + name='Pld Schema', + description="""Schema package containing basic classes used + in the PLD submodule.""", +) - def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - """ - The normalizer for the `PVDStep` class. - Args: - archive (EntryArchive): The archive containing the section that is being - normalized. - logger (BoundLogger): A structlog logger. - """ - super(PVDStep, self).normalize(archive, logger) +class SputteringSchemaPackageEntryPoint(SchemaPackageEntryPoint): + def load(self): + from nomad_material_processing.vapor_deposition.pvd.sputtering import m_package + return m_package -class PhysicalVaporDeposition(VaporDeposition): - """ - A synthesis technique where vaporized molecules or atoms condense on a surface, - forming a thin layer. The process is purely physical; no chemical reaction occurs - at the surface. [database_cross_reference: https://orcid.org/0000-0002-0640-0422] - Synonyms: - - PVD - - physical vapor deposition - """ +sputtering_schema = SputteringSchemaPackageEntryPoint( + name='Sputtering Schema', + description="""Schema package containing basic classes used + in the sputtering submodule.""", +) - m_def = Section( - links=['http://purl.obolibrary.org/obo/CHMO_0001356'], - a_plot=[ - dict( - x='steps/:/environment/pressure/time', - y='steps/:/environment/pressure/value', - ), - dict( - x='steps/:/source/:/vapor_source/power/time', - y='steps/:/source/:/vapor_source/power/value', - ), - ], - ) - steps = SubSection( - description=""" - The steps of the deposition process. - """, - section_def=PVDStep, - repeats=True, - ) - def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - """ - The normalizer for the `PhysicalVaporDeposition` class. +class ThermalSchemaPackageEntryPoint(SchemaPackageEntryPoint): + def load(self): + from nomad_material_processing.vapor_deposition.pvd.thermal import m_package - Args: - archive (EntryArchive): The archive containing the section that is being - normalized. - logger (BoundLogger): A structlog logger. - """ - super(PhysicalVaporDeposition, self).normalize(archive, logger) + return m_package -m_package.__init_metainfo__() +thermal_schema = ThermalSchemaPackageEntryPoint( + name='Thermal Schema', + description="""Schema package containing basic classes used + in the thermal submodule.""", +) diff --git a/src/nomad_material_processing/vapor_deposition/pvd/general.py b/src/nomad_material_processing/vapor_deposition/pvd/general.py new file mode 100644 index 00000000..7f8b6750 --- /dev/null +++ b/src/nomad_material_processing/vapor_deposition/pvd/general.py @@ -0,0 +1,283 @@ +# +# 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 typing import ( + TYPE_CHECKING, +) + +from nomad.datamodel.metainfo.annotations import ( + ELNAnnotation, + ELNComponentEnum, +) +from nomad.metainfo import ( + MEnum, + Quantity, + SchemaPackage, + Section, + SubSection, +) + +from nomad_material_processing.general import ( + TimeSeries, +) +from nomad_material_processing.vapor_deposition.general import ( + EvaporationSource, + SampleParameters, + VaporDeposition, + VaporDepositionSource, + VaporDepositionStep, +) + +if TYPE_CHECKING: + from nomad.datamodel.datamodel import ( + EntryArchive, + ) + from structlog.stdlib import ( + BoundLogger, + ) + +from nomad.config import config + +m_package = SchemaPackage( + name='Physical Vapor Deposition', + aliases=[ + 'nomad_material_processing.vapor_deposition.pvd', + ], +) + +configuration = config.get_plugin_entry_point( + 'nomad_material_processing.vapor_deposition.pvd:schema', +) + + +class SourcePower(TimeSeries): + """ + The power supplied to the source (watt). + """ + + m_def = Section( + a_plot=dict( + # x=['time', 'set_time'], + # y=['value', 'set_value'], + x='time', + y='value', + ), + ) + + value = Quantity( + type=float, + unit='watt', + shape=['*'], + ) + set_value = Quantity( + type=float, + description='The set value(s) (i.e. the intended values) set.', + shape=['*'], + unit='watt', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Set value', + defaultDisplayUnit='watt', + ), + ) + + +class PVDEvaporationSource(EvaporationSource): + m_def = Section( + a_plot=dict( + x='power/time', + y='power/value', + ), + ) + power = SubSection( + section_def=SourcePower, + ) + + +class ImpingingFlux(TimeSeries): + """ + The impinging flux of the material onto the substrate (mol/area/time). + """ + + m_def = Section( + a_plot=dict( + # x=['time', 'set_time'], + # y=['value', 'set_value'], + x='time', + y='value', + ), + ) + measurement_type = Quantity( + type=MEnum( + 'Assumed', + 'Quartz Crystal Microbalance', + ), + a_eln=ELNAnnotation( + component=ELNComponentEnum.EnumEditQuantity, + ), + ) + + value = Quantity( + type=float, + unit='mol/meter ** 2/second', + shape=['*'], + ) + set_value = Quantity( + type=float, + description='The set value(s) (i.e. the intended values) set.', + shape=['*'], + unit='mol/meter ** 2/second', + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + label='Set value', + defaultDisplayUnit='mol/meter ** 2/second', + ), + ) + + +class PVDSource(VaporDepositionSource): + m_def = Section( + a_plot=[ + dict( + x=[ + 'vapor_source/power/time', + 'impinging_flux/:/time', + ], + y=[ + 'vapor_source/power/value', + 'impinging_flux/:/value', + ], + ), + ], + ) + vapor_source = SubSection( + section_def=PVDEvaporationSource, + description=""" + Example: A heater, a filament, a laser, etc. + """, + ) + impinging_flux = SubSection( + section_def=ImpingingFlux, + description=""" + The deposition rate of the material onto the substrate (mol/area/time). + """, + repeats=True, + ) + + +class PVDSampleParameters(SampleParameters): + heater = Quantity( + description=""" + What is the substrate heated by. + """, + type=MEnum( + 'No heating', + 'Halogen lamp', + 'Filament', + 'Resistive element', + 'CO2 laser', + ), + a_eln=ELNAnnotation( + component=ELNComponentEnum.EnumEditQuantity, + ), + ) + distance_to_source = Quantity( + type=float, + unit='meter', + description=""" + The distance between the substrate and all the sources. + In the case of multiple sources, the distances are listed in the same order + as the sources are listed in the parent `VaporDepositionStep` section. + """, + shape=['*'], + a_eln=ELNAnnotation( + component=ELNComponentEnum.NumberEditQuantity, + ), + ) + + +class PVDStep(VaporDepositionStep): + """ + A step of any physical vapor deposition process. + """ + + sources = SubSection( + section_def=PVDSource, + repeats=True, + ) + sample_parameters = SubSection( + section_def=PVDSampleParameters, + repeats=True, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + """ + The normalizer for the `PVDStep` class. + + Args: + archive (EntryArchive): The archive containing the section that is being + normalized. + logger (BoundLogger): A structlog logger. + """ + super().normalize(archive, logger) + + +class PhysicalVaporDeposition(VaporDeposition): + """ + A synthesis technique where vaporized molecules or atoms condense on a surface, + forming a thin layer. The process is purely physical; no chemical reaction occurs + at the surface. [database_cross_reference: https://orcid.org/0000-0002-0640-0422] + + Synonyms: + - PVD + - physical vapor deposition + """ + + m_def = Section( + links=['http://purl.obolibrary.org/obo/CHMO_0001356'], + a_plot=[ + dict( + x='steps/:/environment/pressure/time', + y='steps/:/environment/pressure/value', + ), + dict( + x='steps/:/source/:/vapor_source/power/time', + y='steps/:/source/:/vapor_source/power/value', + ), + ], + ) + steps = SubSection( + description=""" + The steps of the deposition process. + """, + section_def=PVDStep, + repeats=True, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + """ + The normalizer for the `PhysicalVaporDeposition` class. + + Args: + archive (EntryArchive): The archive containing the section that is being + normalized. + logger (BoundLogger): A structlog logger. + """ + super().normalize(archive, logger) + + +m_package.__init_metainfo__() diff --git a/src/nomad_material_processing/vapor_deposition/pvd/mbe.py b/src/nomad_material_processing/vapor_deposition/pvd/mbe.py index e0933516..d55c1725 100644 --- a/src/nomad_material_processing/vapor_deposition/pvd/mbe.py +++ b/src/nomad_material_processing/vapor_deposition/pvd/mbe.py @@ -15,16 +15,35 @@ # See the License for the specific language governing permissions and # limitations under the License. # +from typing import ( + TYPE_CHECKING, +) +from nomad.config import config from nomad.metainfo import ( - Package, - Quantity, + SchemaPackage, Section, ) -from nomad_material_processing.vapor_deposition.pvd import ( + +from nomad_material_processing.vapor_deposition.pvd.general import ( PhysicalVaporDeposition, ) +if TYPE_CHECKING: + from nomad.datamodel.datamodel import ( + EntryArchive, + ) + from structlog.stdlib import ( + BoundLogger, + ) + + +m_package = SchemaPackage(name='Molecular Beam Epitaxy') + +configuration = config.get_plugin_entry_point( + 'nomad_material_processing.vapor_deposition.pvd:mbe_schema', +) + class MolecularBeamEpitaxy(PhysicalVaporDeposition): """ @@ -54,4 +73,4 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: normalized. logger (BoundLogger): A structlog logger. """ - super(MolecularBeamEpitaxy, self).normalize(archive, logger) + super().normalize(archive, logger) diff --git a/src/nomad_material_processing/vapor_deposition/pvd/nomad_plugin.yaml b/src/nomad_material_processing/vapor_deposition/pvd/nomad_plugin.yaml deleted file mode 100644 index 2e4393f7..00000000 --- a/src/nomad_material_processing/vapor_deposition/pvd/nomad_plugin.yaml +++ /dev/null @@ -1,3 +0,0 @@ -description: A plugin for NOMAD containing base sections for physical vapor deposition. -name: Physical Vapor Deposition -plugin_type: schema diff --git a/src/nomad_material_processing/vapor_deposition/pvd/pld.py b/src/nomad_material_processing/vapor_deposition/pvd/pld.py index d895abfa..1fa11432 100644 --- a/src/nomad_material_processing/vapor_deposition/pvd/pld.py +++ b/src/nomad_material_processing/vapor_deposition/pvd/pld.py @@ -18,27 +18,28 @@ from typing import ( TYPE_CHECKING, ) -from nomad.metainfo import ( - Package, - Section, - SubSection, - Quantity, -) + from nomad.datamodel.metainfo.annotations import ( ELNAnnotation, ELNComponentEnum, ) from nomad.datamodel.metainfo.basesections import ( CompositeSystem, - SystemComponent, ReadableIdentifiers, + SystemComponent, +) +from nomad.metainfo import ( + Quantity, + SchemaPackage, + Section, + SubSection, ) -from nomad_material_processing.vapor_deposition.pvd import ( +from nomad_material_processing.vapor_deposition.pvd.general import ( + PhysicalVaporDeposition, PVDEvaporationSource, PVDSource, PVDStep, - PhysicalVaporDeposition, ) if TYPE_CHECKING: @@ -49,7 +50,13 @@ BoundLogger, ) -m_package = Package(name='Pulsed Laser Deposition') +from nomad.config import config + +m_package = SchemaPackage(name='Pulsed Laser Deposition') + +configuration = config.get_plugin_entry_point( + 'nomad_material_processing.vapor_deposition.pvd:pld_schema', +) class PLDTarget(CompositeSystem): @@ -175,7 +182,7 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: normalized. logger (BoundLogger): A structlog logger. """ - super(PulsedLaserDeposition, self).normalize(archive, logger) + super().normalize(archive, logger) m_package.__init_metainfo__() diff --git a/src/nomad_material_processing/vapor_deposition/pvd/sputtering.py b/src/nomad_material_processing/vapor_deposition/pvd/sputtering.py index 5af3c947..930f325b 100644 --- a/src/nomad_material_processing/vapor_deposition/pvd/sputtering.py +++ b/src/nomad_material_processing/vapor_deposition/pvd/sputtering.py @@ -18,13 +18,14 @@ from typing import ( TYPE_CHECKING, ) + from nomad.metainfo import ( - Package, - Section, Quantity, + SchemaPackage, + Section, ) -from nomad_material_processing.vapor_deposition.pvd import ( +from nomad_material_processing.vapor_deposition.pvd.general import ( PhysicalVaporDeposition, ) @@ -36,7 +37,13 @@ BoundLogger, ) -m_package = Package(name="Sputter Deposition") +from nomad.config import config + +m_package = SchemaPackage(name='Sputter Deposition') + +configuration = config.get_plugin_entry_point( + 'nomad_material_processing.vapor_deposition.pvd:sputtering_schema', +) class SputterDeposition(PhysicalVaporDeposition): @@ -52,14 +59,14 @@ class SputterDeposition(PhysicalVaporDeposition): """ m_def = Section( - links=["http://purl.obolibrary.org/obo/CHMO_0001364"], + links=['http://purl.obolibrary.org/obo/CHMO_0001364'], ) method = Quantity( type=str, - default="Sputter Deposition", + default='Sputter Deposition', ) - def normalize(self, archive: "EntryArchive", logger: "BoundLogger") -> None: + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: """ The normalizer for the `SputterDeposition` class. @@ -68,7 +75,7 @@ def normalize(self, archive: "EntryArchive", logger: "BoundLogger") -> None: normalized. logger (BoundLogger): A structlog logger. """ - super(SputterDeposition, self).normalize(archive, logger) + super().normalize(archive, logger) m_package.__init_metainfo__() diff --git a/src/nomad_material_processing/vapor_deposition/pvd/thermal.py b/src/nomad_material_processing/vapor_deposition/pvd/thermal.py index cfbebebe..b48f78ea 100644 --- a/src/nomad_material_processing/vapor_deposition/pvd/thermal.py +++ b/src/nomad_material_processing/vapor_deposition/pvd/thermal.py @@ -18,26 +18,26 @@ from typing import ( TYPE_CHECKING, ) + +from nomad.datamodel.metainfo.annotations import ( + ELNAnnotation, + ELNComponentEnum, +) from nomad.metainfo import ( - Package, + Quantity, + SchemaPackage, Section, SubSection, - Quantity, ) -from nomad_material_processing import ( +from nomad_material_processing.general import ( TimeSeries, ) -from nomad_material_processing.vapor_deposition.pvd import ( +from nomad_material_processing.vapor_deposition.pvd.general import ( + PhysicalVaporDeposition, PVDEvaporationSource, PVDSource, PVDStep, - PhysicalVaporDeposition, -) - -from nomad.datamodel.metainfo.annotations import ( - ELNAnnotation, - ELNComponentEnum, ) if TYPE_CHECKING: @@ -48,7 +48,13 @@ BoundLogger, ) -m_package = Package(name='Thermal Evaporation') +from nomad.config import config + +m_package = SchemaPackage(name='Thermal Evaporation') + +configuration = config.get_plugin_entry_point( + 'nomad_material_processing.vapor_deposition.pvd:thermal_schema', +) class ThermalEvaporationHeaterTemperature(TimeSeries): @@ -228,7 +234,7 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: normalized. logger (BoundLogger): A structlog logger. """ - super(ThermalEvaporation, self).normalize(archive, logger) + super().normalize(archive, logger) m_package.__init_metainfo__() diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..d832d2ed --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,11 @@ +import pytest +from nomad.config import config +from nomad.config.models.plugins import SchemaPackageEntryPoint + + +@pytest.fixture(scope='session', autouse=True) +def import_schema_packages(): + config.load_plugins() + for entry_point in config.plugins.entry_points.filtered_values(): + if isinstance(entry_point, SchemaPackageEntryPoint): + entry_point.load() diff --git a/tests/data/test_physical_vapor_deposition.archive.yaml b/tests/data/test_physical_vapor_deposition.archive.yaml index b80f5b02..0cfa52a9 100644 --- a/tests/data/test_physical_vapor_deposition.archive.yaml +++ b/tests/data/test_physical_vapor_deposition.archive.yaml @@ -1,2 +1,2 @@ data: - m_def: nomad_material_processing.vapor_deposition.pvd.PhysicalVaporDeposition + m_def: nomad_material_processing.vapor_deposition.pvd.general.PhysicalVaporDeposition diff --git a/tests/data/test_solution.archive.yaml b/tests/data/test_solution.archive.yaml index eb9ddb4c..27755dff 100644 --- a/tests/data/test_solution.archive.yaml +++ b/tests/data/test_solution.archive.yaml @@ -1,2 +1,2 @@ data: - m_def: nomad_material_processing.solution.Solution \ No newline at end of file + m_def: nomad_material_processing.solution.Solution diff --git a/tests/data/test_solution_component.archive.yaml b/tests/data/test_solution_component.archive.yaml index 321981a1..e9ed92f1 100644 --- a/tests/data/test_solution_component.archive.yaml +++ b/tests/data/test_solution_component.archive.yaml @@ -1,2 +1,2 @@ data: - m_def: nomad_material_processing.solution.SolutionComponent \ No newline at end of file + m_def: nomad_material_processing.solution.SolutionComponent diff --git a/tests/solution/test_solution_schema.py b/tests/solution/test_solution_schema.py index 91e40f40..4f557274 100644 --- a/tests/solution/test_solution_schema.py +++ b/tests/solution/test_solution_schema.py @@ -1,10 +1,9 @@ import pytest - -from nomad.client import parse, normalize_all -from nomad.units import ureg +from nomad.client import normalize_all, parse from nomad.datamodel.metainfo.basesections import PubChemPureSubstanceSection +from nomad.units import ureg -from nomad_material_processing.solution import ( +from nomad_material_processing.solution.general import ( Solution, SolutionComponent, SolutionComponentReference, diff --git a/tests/test_schema.py b/tests/test_schema.py index b9ca0382..d8fe9e63 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -1,13 +1,15 @@ -import os.path import glob +import os.path + +import pytest +from nomad.client import normalize_all, parse -from nomad.client import parse, normalize_all +test_files = glob.glob( + os.path.join(os.path.dirname(__file__), 'data', '*.archive.yaml') +) -def test_schema(): - test_files = glob.glob( - os.path.join(os.path.dirname(__file__), 'data', '*.archive.yaml') - ) - for test_file in test_files: - entry_archive = parse(test_file)[0] - normalize_all(entry_archive) +@pytest.mark.parametrize('test_file', test_files) +def test_schema(test_file): + entry_archive = parse(test_file)[0] + normalize_all(entry_archive)