diff --git a/IKZ_plugin/src/basesections/__init__.py b/IKZ_plugin/src/basesections/__init__.py index 8eeb6548..24337039 100644 --- a/IKZ_plugin/src/basesections/__init__.py +++ b/IKZ_plugin/src/basesections/__init__.py @@ -1,12 +1,85 @@ import json -from nomad.metainfo import ( - Package, Quantity, SubSection, Section, Category) + from nomad.datamodel.data import EntryData, EntryDataCategory -#from nomad.datamodel.metainfo.workflow import Link -from nomad.datamodel.metainfo.eln import Entity, Activity, SampleID -from nomad.datamodel.util import parse_path +import numpy as np +import plotly.express as px +from plotly.subplots import make_subplots +from ase.data import chemical_symbols +from nomad.datamodel.metainfo.basesections import ( + ElementalComposition, + Activity, + PureSubstance, + ProcessStep, + CompositeSystem, + Measurement, + MeasurementResult, + Process, + PureSubstanceComponent, + PureSubstanceSection, + EntityReference, + CompositeSystemReference, + PubChemPureSubstanceSection, + SectionReference, + Experiment, +) +from nomad.datamodel.metainfo.annotations import ( + ELNAnnotation, + SectionProperties, +) +from nomad.parsing.tabular import TableData +from structlog.stdlib import ( + BoundLogger, +) +from nomad.datamodel.metainfo.annotations import ( + ELNAnnotation, + ELNComponentEnum, +) +from nomad.metainfo import Package, Quantity, SubSection, MEnum, Datetime, Section, Category +from nomad.datamodel.data import EntryData, ArchiveSection, Author +from nomad.search import search, MetadataPagination + +from nomad.datamodel.metainfo.plot import PlotSection, PlotlyFigure + +from laytec_epitt import LayTecEpiTTMeasurement +from hall import HallMeasurement +from basesections import IKZMOVPECategory, IKZMOVPE1Category, IKZMOVPE2Category +from nomad_material_processing import ( + SubstrateReference, + CrystallineSubstrate, + Miscut, + SubstrateCrystalProperties, + Dopant, + Geometry, + ThinFilm, + ThinFilmStack, + ThinFilmStackReference, +) +from nomad_material_processing.vapor_deposition import ( + VaporDeposition, + VaporDepositionSource, + VaporDepositionStep, + SampleParameters, + SubstrateTemperature, + ChamberEnvironment, + GasFlow, + Pressure, + SubstrateHeater, +) +from nomad.datamodel.metainfo.workflow import ( + Link, + Task, +) +from nomad_material_processing.chemical_vapor_deposition import ( + CVDBubbler, + CVDVaporRate, + CVDSource, +) + +from nomad_measurements import ( + ActivityReference, +) m_package = Package(name='basesections_IKZ') @@ -34,6 +107,74 @@ class IKZHallCategory(EntryDataCategory): m_def = Category(label='Hall', categories=[EntryDataCategory, IKZCategory]) +class Etching(Process, EntryData): + """ + Class autogenerated from yaml schema. + """ + + m_def = Section( + a_eln=None, + categories=[IKZCategory], + ) + method = Quantity( + type=str, + default="Etching (MOVPE IKZ)", + ) + datetime = Quantity( + type=Datetime, + description="FILL", + a_eln={"component": "DateTimeEditQuantity", "label": "deposition_date"}, + ) + temperature = Quantity( + type=np.float64, + description="FILL THE DESCRIPTION", + a_eln={"component": "NumberEditQuantity", "defaultDisplayUnit": "celsius"}, + unit="celsius", + ) + elapsed_time = Quantity( + type=np.float64, + description="Past time since process started (minutes)", + a_eln={"component": "NumberEditQuantity", "defaultDisplayUnit": "minute"}, + unit="minute", + ) + etching_reagents = SubSection(section_def=CompositeSystem, repeats=True) + + +class Annealing(Process, EntryData): + """ + Class autogenerated from yaml schema. + """ + + m_def = Section( + a_eln=None, + categories=[IKZCategory], + ) + method = Quantity( + type=str, + default="Annealing (MOVPE IKZ)", + ) + datetime = Quantity( + type=Datetime, + description="FILL", + a_eln={"component": "DateTimeEditQuantity", "label": "deposition_date"}, + ) + temperature = Quantity( + type=np.float64, + description="FILL THE DESCRIPTION", + a_eln={"component": "NumberEditQuantity", "defaultDisplayUnit": "celsius"}, + unit="celsius", + ) + elapsed_time = Quantity( + type=np.float64, + description="Past time since process started (minutes)", + a_eln={"component": "NumberEditQuantity", "defaultDisplayUnit": "minute"}, + unit="minute", + ) + anealing_reagents = SubSection( + section_def=CompositeSystemReference, + ) + + # class CollectionOfSystems(Entity, EntryData): # ''' # A base class for a batch of materials. Each component of the batch is diff --git a/IKZ_plugin/src/characterization/__init__.py b/IKZ_plugin/src/characterization/__init__.py new file mode 100644 index 00000000..7a3dade8 --- /dev/null +++ b/IKZ_plugin/src/characterization/__init__.py @@ -0,0 +1,18 @@ +# 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 .schema import * diff --git a/IKZ_plugin/src/characterization/nomad_plugin.yaml b/IKZ_plugin/src/characterization/nomad_plugin.yaml new file mode 100644 index 00000000..44965ba3 --- /dev/null +++ b/IKZ_plugin/src/characterization/nomad_plugin.yaml @@ -0,0 +1,5 @@ +plugin_type: schema +name: NOMAD's example schema plugin +description: | + This is a simple hello world schema. This is meant as a template. + Fork the github project to create your own schemas. \ No newline at end of file diff --git a/IKZ_plugin/src/characterization/schema.py b/IKZ_plugin/src/characterization/schema.py new file mode 100644 index 00000000..042565e6 --- /dev/null +++ b/IKZ_plugin/src/characterization/schema.py @@ -0,0 +1,183 @@ +import numpy as np +import plotly.express as px +from plotly.subplots import make_subplots +from ase.data import chemical_symbols +from nomad.datamodel.metainfo.basesections import ( + ElementalComposition, + Activity, + PureSubstance, + ProcessStep, + CompositeSystem, + Measurement, + MeasurementResult, + Process, + PureSubstanceComponent, + PureSubstanceSection, + EntityReference, + CompositeSystemReference, + PubChemPureSubstanceSection, + SectionReference, + Experiment, +) +from nomad.datamodel.metainfo.annotations import ( + ELNAnnotation, + SectionProperties, +) +from nomad.parsing.tabular import TableData +from structlog.stdlib import ( + BoundLogger, +) +from nomad.datamodel.metainfo.annotations import ( + ELNAnnotation, + ELNComponentEnum, +) +from nomad.metainfo import Package, Quantity, SubSection, MEnum, Datetime, Section +from nomad.datamodel.data import EntryData, ArchiveSection, Author +from nomad.search import search, MetadataPagination + +from nomad.datamodel.metainfo.plot import PlotSection, PlotlyFigure + +from laytec_epitt import LayTecEpiTTMeasurement +from hall import HallMeasurement +from basesections import IKZCategory +from nomad_material_processing import ( + SubstrateReference, + CrystallineSubstrate, + Miscut, + SubstrateCrystalProperties, + Dopant, + Geometry, + Parallelepiped, + ThinFilm, + ThinFilmStack, + ThinFilmStackReference, +) + +from nomad.datamodel.metainfo.workflow import ( + Link, + Task, +) +from nomad_material_processing.chemical_vapor_deposition import ( + CVDBubbler, + CVDVaporRate, + CVDSource, +) + +from nomad_measurements import ( + ActivityReference, +) + +m_package = Package(name="characterization_IKZ") + + +class AFMresults(MeasurementResult): + """ + The results of an AFM measurement + """ + + roughness = Quantity( + type=np.float64, + description="RMS roughness value obtained by AFM", + a_eln={"component": "NumberEditQuantity", "defaultDisplayUnit": "picometer"}, + unit="picometer", + ) + surface_features = Quantity( + type=MEnum(["Step Flow", "Step Bunching", "2D Island"]), + a_eln={"component": "EnumEditQuantity"}, + ) + scale = Quantity( + type=np.float64, + description="scale of the image, to be multiplied by 5 to know the image size", + a_eln={"component": "NumberEditQuantity", "defaultDisplayUnit": "nanometer"}, + unit="nanometer", + ) + image = Quantity( + type=str, + description="image showing the thickness measurement points", + a_browser={"adaptor": "RawFileAdaptor"}, + a_eln={"component": "FileEditQuantity"}, + ) + crop_image = Quantity( + type=str, + description="crop image ready to be used for AI-based analysis", + a_browser={"adaptor": "RawFileAdaptor"}, + a_eln={"component": "FileEditQuantity"}, + ) + + +class AFMmeasurement(Measurement, EntryData): + """ + Class autogenerated from yaml schema. + """ + + m_def = Section( + a_eln={"hide": ["steps"]}, + categories=[IKZCategory], + label="AFM", + ) + + method = Quantity( + type=str, + default="AFM (IKZ MOVPE)", + ) + description = Quantity( + type=str, + a_eln={"component": "StringEditQuantity"}, + ) + datetime = Quantity( + type=Datetime, + a_eln={"component": "DateTimeEditQuantity"}, + ) + results = SubSection( + section_def=AFMresults, + repeats=True, + ) + + +class LiMiresults(MeasurementResult): + """ + The results of a Light Microscope measurement + """ + + image = Quantity( + type=str, + description="image showing the thickness measurement points", + a_browser={"adaptor": "RawFileAdaptor"}, + a_eln={"component": "FileEditQuantity"}, + ) + crop_image = Quantity( + type=str, + description="crop image ready to be used for AI-based analysis", + a_browser={"adaptor": "RawFileAdaptor"}, + a_eln={"component": "FileEditQuantity"}, + ) + scale = Quantity( + type=np.float64, + description="scale of the image", + a_eln={"component": "NumberEditQuantity", "defaultDisplayUnit": "micrometer"}, + unit="micrometer", + ) + + +class LightMicroscope(Measurement, EntryData): + """ + Class autogenerated from yaml schema. + """ + + m_def = Section( + a_eln={"hide": ["steps"]}, + categories=[IKZCategory], + label="Light Microscope", + ) + method = Quantity( + type=str, + default="Light Microscope (MOVPE IKZ)", + ) + datetime = Quantity( + type=Datetime, + a_eln={"component": "DateTimeEditQuantity"}, + ) + results = SubSection( + section_def=LiMiresults, + repeats=True, + ) \ No newline at end of file diff --git a/IKZ_plugin/src/movpe/schema.py b/IKZ_plugin/src/movpe/schema.py index a852a32c..331fe59a 100644 --- a/IKZ_plugin/src/movpe/schema.py +++ b/IKZ_plugin/src/movpe/schema.py @@ -110,6 +110,39 @@ class ElementalCompositionMovpe(ElementalComposition): ) +class Precursor(PureSubstance, EntryData): + """ + Class autogenerated from yaml schema. + """ + + m_def = Section(categories=[IKZMOVPECategory]) + name = Quantity( + type=str, + description="FILL", + a_eln=ELNAnnotation(component="StringEditQuantity", label="Substance Name"), + ) + cas_number = Quantity( + type=str, + description="FILL", + a_eln=ELNAnnotation(component="StringEditQuantity", label="CAS number"), + ) + + +class PrecursorReference(EntityReference): + """ + A section used for referencing a Precursor. + """ + + reference = Quantity( + type=Precursor, + description="A reference to a NOMAD `Precursor` entry.", + a_eln=ELNAnnotation( + component="ReferenceEditQuantity", + label="Precursor Reference", + ), + ) + + class DopantMovpe(Dopant): """ A dopant element in a crystalline structure @@ -331,39 +364,6 @@ class SubstrateCrystalPropertiesMovpe(SubstrateCrystalProperties): miscut = SubSection(section_def=MiscutMovpe) -class Precursor(PureSubstance, EntryData): - """ - Class autogenerated from yaml schema. - """ - - m_def = Section(categories=[IKZMOVPECategory]) - name = Quantity( - type=str, - description="FILL", - a_eln=ELNAnnotation(component="StringEditQuantity", label="Substance Name"), - ) - cas_number = Quantity( - type=str, - description="FILL", - a_eln=ELNAnnotation(component="StringEditQuantity", label="CAS number"), - ) - - -class Precursors(EntityReference): - """ - A section used for referencing a Precursor. - """ - - reference = Quantity( - type=Precursor, - description="A reference to a NOMAD `Precursor` entry.", - a_eln=ELNAnnotation( - component="ReferenceEditQuantity", - label="Precursor Reference", - ), - ) - - class SubstrateMovpe(CrystallineSubstrate, EntryData): """ Class autogenerated from yaml schema. @@ -2338,7 +2338,7 @@ class ExperimentMovpe2IKZ(Experiment, EntryData): section_def=Users, ) precursors = SubSection( - section_def=Precursors, + section_def=PrecursorReference, repeats=True, ) growth_run = SubSection( diff --git a/IKZ_plugin/src/substrate/__init__.py b/IKZ_plugin/src/substrate/__init__.py new file mode 100644 index 00000000..7a3dade8 --- /dev/null +++ b/IKZ_plugin/src/substrate/__init__.py @@ -0,0 +1,18 @@ +# 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 .schema import * diff --git a/IKZ_plugin/src/substrate/nomad_plugin.yaml b/IKZ_plugin/src/substrate/nomad_plugin.yaml new file mode 100644 index 00000000..fa9bfce6 --- /dev/null +++ b/IKZ_plugin/src/substrate/nomad_plugin.yaml @@ -0,0 +1,4 @@ +plugin_type: schema +name: MOVPE IKZ +description: | + This is a schema for MOVPE IKZ experiment diff --git a/IKZ_plugin/src/substrate/schema.py b/IKZ_plugin/src/substrate/schema.py new file mode 100644 index 00000000..f6bc69f4 --- /dev/null +++ b/IKZ_plugin/src/substrate/schema.py @@ -0,0 +1,401 @@ +import numpy as np +import plotly.express as px +from plotly.subplots import make_subplots +from ase.data import chemical_symbols +from nomad.datamodel.metainfo.basesections import ( + ElementalComposition, + Activity, + PureSubstance, + ProcessStep, + CompositeSystem, + Measurement, + MeasurementResult, + Process, + PureSubstanceComponent, + PureSubstanceSection, + EntityReference, + CompositeSystemReference, + PubChemPureSubstanceSection, + SectionReference, + Experiment, +) +from nomad.datamodel.metainfo.annotations import ( + ELNAnnotation, + SectionProperties, +) +from nomad.parsing.tabular import TableData +from structlog.stdlib import ( + BoundLogger, +) +from nomad.datamodel.metainfo.annotations import ( + ELNAnnotation, + ELNComponentEnum, +) +from nomad.metainfo import Package, Quantity, SubSection, MEnum, Datetime, Section +from nomad.datamodel.data import EntryData, ArchiveSection, Author +from nomad.search import search, MetadataPagination + +from nomad.datamodel.metainfo.plot import PlotSection, PlotlyFigure + +from laytec_epitt import LayTecEpiTTMeasurement +from hall import HallMeasurement +from basesections import ( + IKZCategory, + Etching, + Annealing, +) +from nomad_material_processing import ( + SubstrateReference, + CrystallineSubstrate, + Miscut, + SubstrateCrystalProperties, + Dopant, + Geometry, + Parallelepiped, + ThinFilm, + ThinFilmStack, + ThinFilmStackReference, +) + +from nomad.datamodel.metainfo.workflow import ( + Link, + Task, +) + +from nomad_measurements import ( + ActivityReference, +) + +from characterization import ( + AFMmeasurement, + LightMicroscope, +) + +m_package = Package(name="substrate_IKZ") + + +class MiscutIKZ(Miscut): + """ + 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. + """ + + m_def = Section(label="Miscut") + + b_angle = Quantity( + type=float, + description="crystallographic orientation of the substrate in [hkl]", + a_eln=ELNAnnotation( + component="NumberEditQuantity", + ), + a_tabular={ + "name": "Substrate/Miscut b angle", + # "unit": "deg" + }, + unit="deg", + ) + angle = Quantity( + type=float, + description="angular displacement from crystallographic orientation of the substrate", + a_eln=ELNAnnotation( + component="NumberEditQuantity", + defaultDisplayUnit="deg", + label="c angle", + ), + unit="deg", + a_tabular={ + "name": "Substrate/Miscut c angle", + # "unit": "deg" + }, + ) + angle_deviation = Quantity( + type=float, + description="uncertainty on the angular displacement", + a_eln=ELNAnnotation( + component="NumberEditQuantity", + defaultDisplayUnit="deg", + label="c angle deviation", + ), + unit="deg", + ) + orientation = Quantity( + type=str, + description="crystallographic orientation of the substrate in [hkl]", + a_eln=ELNAnnotation( + component="StringEditQuantity", + ), + a_tabular={"name": "Substrate/Miscut c Orientation"}, + ) + + +class SubstrateCrystalPropertiesIKZ(SubstrateCrystalProperties): + """ + 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. + """ + + m_def = Section(label="CrystalProperties") + orientation = Quantity( + type=str, + a_eln=ELNAnnotation( + component="StringEditQuantity", + ), + a_tabular={"name": "Substrate/Orientation"}, + ) + miscut = SubSection(section_def=MiscutIKZ) + + +class SubstrateIKZ(CrystallineSubstrate, EntryData): + """ + Class autogenerated from yaml schema. + """ + + m_def = Section( + label_quantity="lab_id", categories=[IKZCategory], label="Substrate" + ) + + datetime = Quantity( + type=Datetime, + description="Delivery Date of the Substrate", + a_eln=ELNAnnotation( + component="DateTimeEditQuantity", + label="Delivery Date", + ), + a_tabular={"name": "Substrate/Delivery Date"}, + ) + lab_id = Quantity( + type=str, + a_eln=ELNAnnotation( + component="StringEditQuantity", + label="Substrate ID", + ), + a_tabular={"name": "Substrate/Substrates"}, + ) + supplier = Quantity( + type=str, + a_eln=ELNAnnotation( + component="StringEditQuantity", + ), + a_tabular={"name": "Substrate/Supplier"}, + ) + supplier_id = Quantity( + type=str, + description="""An ID string that is unique from the supplier.""", + a_eln=ELNAnnotation( + component="StringEditQuantity", + label="Polishing ID", + ), + a_tabular={"name": "Substrate/Polishing Number"}, + ) + tags = Quantity( + type=str, + description="FILL", + a_eln=ELNAnnotation( + component="StringEditQuantity", + label="Box ID", + ), + a_tabular={"name": "Substrate/Substrate Box"}, + ) + as_received = Quantity( + type=bool, + description="Is the sample annealed?", + a_eln=ELNAnnotation( + component="BoolEditQuantity", + ), + a_tabular={"name": "Substrate/As Received"}, + ) + etching = Quantity( + type=bool, + description="Usable Sample", + a_eln=ELNAnnotation( + component="BoolEditQuantity", + ), + a_tabular={"name": "Substrate/Etching"}, + ) + annealing = Quantity( + type=bool, + description="Usable Sample", + a_eln=ELNAnnotation( + component="BoolEditQuantity", + ), + a_tabular={"name": "Substrate/Annealing"}, + ) + re_etching = Quantity( + type=bool, + description="Usable Sample", + a_eln=ELNAnnotation( + component="BoolEditQuantity", + ), + a_tabular={"name": "Substrate/Re-Etching"}, + ) + re_annealing = Quantity( + type=bool, + description="Usable Sample", + a_eln=ELNAnnotation( + component="BoolEditQuantity", + ), + a_tabular={"name": "Substrate/Re-Annealing"}, + ) + epi_ready = Quantity( + type=bool, + description="Sample ready for epitaxy", + a_eln=ELNAnnotation( + component="BoolEditQuantity", + ), + a_tabular={"name": "Substrate/Epi Ready"}, + ) + quality = Quantity( + type=str, + description="Defective Sample", + a_eln=ELNAnnotation( + component="StringEditQuantity", + ), + a_tabular={"name": "Substrate/Quality"}, + ) + documentation = Quantity( + type=str, + description="pdf files containing certificate and other documentation", + a_browser={"adaptor": "RawFileAdaptor"}, + a_eln=ELNAnnotation( + component="FileEditQuantity", + ), + ) + description = Quantity( + type=str, + description="description", + a_eln=ELNAnnotation( + component="StringEditQuantity", + label="Notes", + ), + ) + geometry = SubSection( + section_def=Parallelepiped, + ) + crystal_properties = SubSection(section_def=SubstrateCrystalPropertiesIKZ) + elemental_composition = SubSection( + section_def=ElementalComposition, + repeats=True, + ) + dopants = SubSection(section_def=Dopant, repeats=True) + + +class SubstrateIKZReference(SubstrateReference): + """ + 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=SubstrateIKZ, + a_eln=ELNAnnotation( + component=ELNComponentEnum.ReferenceEditQuantity, + label="Substrate", + ), + ) + + +class SubstrateInventory(EntryData, TableData): + """ + Class autogenerated from yaml schema. + """ + + m_def = Section( + a_eln=None, + #categories=[IKZMOVPECategory], + label="SubstrateInventory", + ) + substrate_data_file = Quantity( + type=str, + description="Upload here the spreadsheet file containing the substrates data", + a_tabular_parser={ + "parsing_options": {"comment": "#"}, + "mapping_options": [ + { + "mapping_mode": "row", + "file_mode": "multiple_new_entries", + "sections": ["substrates"], + } + ], + }, + a_browser={"adaptor": "RawFileAdaptor"}, + a_eln={"component": "FileEditQuantity"}, + ) + substrates = SubSection( + section_def=SubstrateIKZReference, + repeats=True, + ) + + +class SubstratePreparationStep(Activity): + """ + A section used for referencing Activities performed on Substrate. + """ + + m_def = Section() + + +class EtchingForSubstrate(SubstratePreparationStep, Etching): + pass + + +class AnnealingForSubstrate(SubstratePreparationStep, Annealing): + pass + + +class AFMmeasurementForSubstrate(SubstratePreparationStep, AFMmeasurement): + pass + + +class LightMicroscopeForSubstrate(SubstratePreparationStep, LightMicroscope): + pass + + +class SubstratePreparationStepReference(ActivityReference): + """ + A section used for referencing SubstratePreparationSteps. + """ + + reference = Quantity( + type=SubstratePreparationStep, + description="A reference to a NOMAD `SubstratePreparationSteps` entry.", + a_eln=ELNAnnotation( + component="ReferenceEditQuantity", + label="Substrate Preparation Steps", + ), + ) + + +class SubstratePreparation(Process, EntryData): + """ + Class autogenerated from yaml schema. + """ + + m_def = Section( + a_eln=None, + categories=[IKZCategory], + ) + method = Quantity( + type=str, + default="Substrate Process (MOVPE IKZ)", + ) + description = Quantity( + type=str, + description="description", + a_eln={"component": "StringEditQuantity"}, + ) + substrates = SubSection( + section_def=SubstrateReference, + repeats=True, + ) + steps = SubSection( + section_def=SubstratePreparationStepReference, + repeats=True, + ) \ No newline at end of file