Object Construction and Property Validation for Files of Synthesis (FOS)
Introduction
During construction of a parsed FOS into a Synthesis object, or when reassigning a property of an exising object, most classes have properties that should be expected in a certain format, or might contain nested objects with their own properties. The parsed properties are passed as either strings or (in the case of nested objects) a list or dict structure to the validation routine mapped to them in FoSpy.parsing.validation.py. In the case of nested objects, the validation routine is usually the constructor of the required object.
Classes can have required properties, which must be present at read time, or optional properties, which have validation routines but might not be expected for all syntheses.
During attribute assignment, expected properties are built up in order of parent classes. Any subclass inherits all the expected properties of its parent classes, but they may be overwritten to other validators.
Dispatching Subclasses
Properties marked with (dispatched) are used to identify and delegate construction to the respective subclass. A class with a dispatch key is rarely constructed as itself, but may be used to inherit methods or required/optional properties.
ListBlock and Simple Lists
ListBlock subclasses do not have required properties. Instead, they are a rich list of objects with a certain required SingleBlock subclass. Some ListBlock subclasses have their own methods and unique attributes. If a ListBlock is required for a given SingleBlock subclass but it doesn't need any method or attribute overrides, it can be instantiated with the class method ListBlock.Simple(SingleBlockSubclass) (referred to below as a "simple list").
TemplateLists
TemplateList is a unique subclass of ListBlock used for storing TemplateBlock subclass objects. TemplateList has a class method Simple() similar to ListBlock's method, but instead of full objects (e.g. Material), it generates hybridized templates with fields for any field-marked or missing properties. Some syntax for TemplateList entries can be found in the code example walkthrough.
Modifying Property Validation at Runtime
The intent of the FOS is to be as flexible as possible while still enforcing standards for fully capturing a synthetic method. However, it may be necessary for private or niche applications to modify standards to match your own synthesis. This can be done by mutating the dictionaries in FoSpy.parsing.validation at the start of your script. The example below creates a new block type with its own expected properties, and then adds it as an optional top-level block for a synthesis file.
These modifications should be reserved for very unique and isolated cases. If you are modifying standards in a significant way, or such that may be applicable for other researchers, consider reaching out to FoSpy developers or creating a fork of the GitHub.
Python
import FoSpy.parsing.validation as vd
from FoSpy.blocks import (
blocks.SingleBlock as SingleBlock
synthesis.Synthesis as Synthesis
)
class MySpecialBlock(SingleBlock):
pass
vd.required_keys[MySpecialBlock] = {
"special_prop1": str
"special_prop2": float
}
vd.optional_keys[Synthesis]["special"] = MySpecialBlock
New FOS Block
Expected Property Tables
AnnealSection
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| type | str (dispatched) |
Examples: "dwell", "ramp", "quench" |
Annealing
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| program | AnnealProgram |
A specialized ListBlock of AnnealSection objects |
| start_temp | validators.numbers.positive_decimal |
The initial temperature of the annealing profile. Values are attached to the required unit and constructed into a pint.Quantity object. |
| start_temp_unit | validators.units.FOSTempUnit |
FOSTempUnit is a subclass of pint's Unit which allows a little more coersion of temperature units. (Like recognizing "C" as degrees celsius as opposed to coulombs) |
Optional properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| gas_flow | FlowList |
A simple list of GasFlow objects |
Attachment
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| file_name | validators.filenames.file_name |
A name for the file that does not contain any incompatible characters ( \ / : * ? " < > \|). Extensions are allowed, but will be trimmed if matching extension property, or appended to if not matching. |
| extension | validators.filenames.file_extension (dispatched) |
A valid file extension (with or without "." prefix). |
Additional Requirements
In addition to the required properties above, all Attachment objects must be constructed with one of the following optional properties:
embeddedpath
The first matching property found will be used and the remainder will be discarded. The presence of one of these properties is used to identify what form of file attachment it is. Refer to the attachments guide for more information
Optional properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| embedded | list |
A list of raw utf-8 line strings copied from the embedded file. See attachments guide for syntax. |
| path | validators.filenames.PathPosix |
Instead of directly embedding contents, refers to a relative path from the folder containing the parent FileBlock. Can use relative characters like "." and "..". See attachments guide for more information.PathPosix validator is a subclass of pathlib.Path which always uses back slashes (/) instead of forward slashes (\) when serialized, regardless of OS |
Attachment Method Subclasses
Attachment Subclasses are hybridized between an attachment type and a file type. Attachment types share most method names to be called by file type methods, but method source code differs on the basis of how the file was attached. For example, _get_filepath() for PathFile simply returns an absolute filepath resolved from the value in its path attribute, whereas EmbeddedFile objects create a temporary file to print their embedded lines to before returning its filepath.
Attachment types are dispatched based on which optional properties they have. File types are dispatched based on extension. Unrecognized extensions simply don't add any special file type methods.
Attachment Types
EmbeddedFilePathFile
File Types
CIFFile
Dwell
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| time | validators.numbers.positive_decimal |
How long the temperature was kept constant in this section. Values are attached to the required unit and constructed into a pint.Quantity object. |
| time_unit | validators.units.FOSUnit.enforce_dims('[time]') |
FOSUnit is a subclass of pint's Unit with a class method for enforcing the correct dimensionality of the unit. |
Experimenter
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| name | str |
Name of the experimenter |
| affiliation | str |
Lab/University/Research Group/etc. |
Optional properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| orcid | str |
The experimenter's ORCID |
FileBlock
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| metadata | MetaData |
General information about the file. Lines at the beginning of a FOS-formatted file without a header will automatically be interpreted as a MetaData dictionary |
Material
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| name | str |
Identifiable name for the material |
| type | str |
How it was used in the synthesis (e.g., reagent, flux, solvent) |
| formula | ChemFormula |
Chemical composition written in a ChemFormula compatible format |
| supplier | str |
Source of purchase/synthesis (may be internal) |
| cas | str |
CAS ID. May be "unknown" |
| form | str |
Physical shape or state of the material at time of acquisition (e.g., powder, shot, wire, lump). If the material was modified after aquiring but before use in the synthesis (like grinding into powder, drying, etc.), these actions should be specified in the material's treatments property (not the synthesis treatments). |
| env | str |
What environment the material is stored in. (e.g., ambient, Ar(g)) |
| amount | validators.numbers.positive_decimal |
Amount that was used. Values are attached to the required unit and constructed into a pint.Quantity object. |
| amount_unit | str |
Descriptive unit for amount. Dimensionality may be enforced in the future once more input is gained from experimenters. |
Optional properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| purity | validators.numbers.decimal_range |
0 < purity \<= 1 |
| treatments | TreatmentList |
A simple list of Treatment objectsAny modifications to the material between acquisition and use in the synthesis. |
MetaData
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| fos_id | str |
Identifiable ID for the synthesis or sample. For Synthesis files: this should be kept informative and unique within the scope of the project_id, as it may be used as identification for future database storage.For TemplateSet files: it is for convenience. |
| fos_type | str |
What type of FileBlock subclass the file should be interpreted as. Expected values are:synthesistemplates |
| description | str |
A brief description of the intent for the file (characteristic methods, target products, template category, etc.). |
Product
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| name | str |
Identifiable name for the product. |
| expected | bool |
Whether or not it was expected from the synthesis. |
| obtained | bool |
Whether or not it was obtained from the synthesis. |
| formula | ChemFormula |
Chemical composition written in a ChemFormula compatible format. |
| observations | str |
General observations about the product. |
Optional properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| expected_amount | validators.numbers.positive_decimal |
How much of the product was nominally expected to be obtained from the synthesis. Values are attached to the required unit and constructed into a pint.Quantity object. |
| expected_amount_unit | validators.units.FOSUnit.enforce_dims(["[mass]",{"[length]":3}]) |
FOSUnit is a subclass of pint's Unit with a class method for enforcing the correct dimensionality of the unit. (In this case mass or volume) |
| obtained_amount | validators.numbers.positive_decimal |
How much of the product was actually obtained from the synthesis. Values are attached to the required unit and constructed into a pint.Quantity object. |
| obtained_amount_unit | validators.units.FOSUnit.enforce_dims(["[mass]",{"[length]":3}]) |
FOSUnit is a subclass of pint's Unit with a class method for enforcing the correct dimensionality of the unit. (In this case mass or volume) |
| characterizations | str |
Description of characterization methods used to determine/quantitate the product. |
| structure_comments | str |
General description on the structure of the product. |
Quench
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| medium | str |
What medium the reaction vessel was quenched in (e.g., water, air). |
Ramp
Required properties
During construction of a Ramp object, it is required to have at least 2 of the following optional properties, with their respective units:
temptimerate
If all three are provided, the last one found during reading will be discarded as redundant data, and the object is dipatched to a Ramp subclass with a "retrieval" method for calculating the missing property (e.g., get_temp(), get_time(), or get_rate()) When working with Ramp objects in the FoSpy framework, it is best practice to always use the "retrieval" methods. For subclasses that do have the desired property, retrieval methods default to returning it directly.
Optional properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| temp | validators.numbers.positive_decimal |
The next temperature in the program. Values are attached to the required unit and constructed into a pint.Quantity object. |
| time | validators.numbers.positive_decimal |
How long it took to get from the last temperature to the new temperature. Values are attached to the required unit and constructed into a pint.Quantity object. |
| rate | validators.numbers.any_decimal |
The sign-sensitive rate at which temperature was changed to get to the new temperature. (Increase -> positive, Decrease -> negative). Values are attached to the required unit and constructed into a pint.Quantity object. |
| temp_unit | validators.units.FOSTempUnit |
FOSTempUnit is a subclass of pint's Unit which allows a little more coersion of temperature units. (Like recognizing "C" as degrees celsius as opposed to coulombs) |
| time_unit | validators.units.FOSUnit.enforce_dims("[time]") |
FOSUnit is a subclass of pint's Unit with a class method for enforcing the correct dimensionality of the unit. |
| rate_unit | validators.units.temp_rate_unit |
Makes use of pint's dimensionality properties to verify that the value is a unit of temperature over time. |
Ramp Method Subclasses
The following subclasses are dispatched based on the redundant parameter (see Required Properties above) and override the retrieval method to calculate the missing parameter instead of getting it from attributes:
RampNoRate: overridesget_rate()RampNoTemp: overridesget_temp()RampNoTime: overridesget_time()
Reaction
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| nominal_formula | ChemFormula |
Chemical composition written in a ChemFormula compatible format |
| nominal_amount | validators.numbers.positive_decimal |
Total amount expected to be recovered from all participating reactants. Values are attached to the required unit and constructed into a pint.Quantity object. |
| nominal_amount_unit | validators.units.FOSUnit.enforce_dims(["[mass]",{"[length]":3}]) |
FOSUnit is a subclass of pint's Unit with a class method for enforcing the correct dimensionality of the unit. |
SingleBlock
Parent Block Class
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| ext | SubContainer |
Not to be passed to constructor. This property is automatically created. This attribute is used to store unexpected properties so that they don't inadvertently overwrite existing methods or attributes. |
Optional properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| rename | validators.rename.rename_dict |
A dictionary of old_name:new_name pairs for renaming properties within the SingleBlock subclass while keeping them in sync with their validators. Refer to the code example walkthrough for usage. |
SingleBlock Method Subclasses
These subclasses don't currently have any additional required properties but will either be expanded in the future or have specialized methods.
GasFlow: For specifying gas flow rates during an annealing treatment (to be expanded).LabConditions: For specifying laboratory conditions during the synthesis (to be expanded).Equipment: For specifying specialized laboratory equipment used in the synthesis (to be expanded).CSVdata: Placeholder for planned features with CSV data inserted as dataframesTraceData: Placeholder for planned features with plottable 2D dataframes
Synthesis
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| metadata | SynthesisMeta |
General information about the file. Additional requirements from basic file metadata. Lines at the beginning of a FOS-formatted file without a header will automatically be interpreted as a MetaData dictionary |
| experimenters | ExperimenterList |
A simple list of Experimenter objects |
| reaction | Reaction |
General reaction information |
| products | ProductList |
A simple list of Product objects |
| materials | MaterialList |
A specialized ListBlock of Material objects |
| treatments | TreatmentList |
A simple list of Treatment objects |
Optional properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| cif | Attachment.enforce_subtype(CIFFile) |
A single attached CIF file |
| cifs | CifList |
A simple list of attached CIF files |
| laboratory_conditions | LabConditions |
General Laboratory Conditions |
| equipment | EquipmentList |
A simple list of Equipment objects |
SynthesisMeta
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| group_id | str |
A unique identifier for the research group or organization. In the future: unique group_id values should be standardized and/or issued by a central party to ensure that all future database uploads have a unique index location. For now: group_id values can be verified unique by ending with the primary investigator's ORCID (example: kovnir-0000-0003-1152-1912). After standardization, tools will be developed to convert group_id values for entire group repositories. |
| project_id | str |
An identifier that only needs to be unique within the scope of the group_id. This can be more flexible to the needs of the group, but good practice is to keep synthesis files in a folder structure that matches project_id values. A large experimental group, for example, might categorize syntheses by experimenter then project, or vice versa for intra-collaboration. To avoid future conflicts, name-based categories should be given unique suffixes, like university ID/username or the last 4 digits of the ORCID. Some examples:travis5672/clathrates, travis(errthumt)/pnictides, thermoelectrics/travis5672/Ba2-TM5-Pn6Future Tools will expect project_ids to reflect folder structure using " /" or "\" delimiters. |
TemplateBlock
TemplateBlock is used to make hybridized subclasses of other SingleBlock subclasses. Template subclasses override required properties of the original class with template fields that can be later filled in and passed to their validators with the fill() method. Refer to the code example walkthrough for some uses of templates.
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| template_name | str |
An identifiable name for the template. |
TemplateBlock Method Subclasses
These subclasses don't currently have any additional required properties but will either be expanded in the future or have specialized methods.
FlexTemplate: Same functionality asTemplateBlock, but automatically interprets missing input as template fields rather than having preset expectations for which fields are unfilled. Used forreflex()method andTemplateListconstruction.
TemplateSet
Optional properties
In contrast with a Synthesis file, most top-level properties for a TemplateSet are expected to contain lists of templates of a given type. TemplateList.Simple() allows entries to be incomplete and generates hybridized TemplateBlock subclasses for them.
Developers are currently working on ways to flexibly allow any template list in a TemplateSet. For now, refer to modifying validation at runtime or reach out to developers if current standards are limiting how you want to use templates.
| Property | Validation Routine or Class | Description |
|---|---|---|
| experimenters | TemplateList.Simple(Experimenter) |
A list of incomplete Experimenter objects |
| materials | TemplateList.Simple(Material) |
A list of incomplete Material objects |
| treatments | TemplateList.Simple(Treatment) |
A list of incomplete Treatment objects |
| annealings | TemplateList.Simple(Annealing) |
A list of incomplete Annealing objects |
| anneal_sections | TemplateList.Simple(AnnealSection) |
A list of incomplete AnnealSection objects |
| cifs | CifList |
A simple list of attached CIF files |
Treatment
When applicable, treatments are dispatched to subclasses based on the type value. Specialized subclasses for treatments are added by developers on an as-needed basis for specialized methods, or requiring additional properties for some types. Unrecognized types are still allowed but aren't given any additional properties or methods. The current dispatch list is below:
| type | Subclass |
|---|---|
| anneal | Annealing (link) |
Required properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| type | str (dispatched) |
What type of treatment was performed |
| repeats | int |
How many times it was performed in succession (if interrupted by other treatments, add a different treatment block after the interrupting treatments) |
| observations | str |
General observations |
Optional properties
| Property | Validation Routine or Class | Description |
|---|---|---|
| recovered_amount | validators.numbers.positive_decimal |
How much material was recovered after treatment. Values are attached to the required unit and constructed into a pint.Quantity object. |
| recovered_amount_unit | validators.units.FOSUnit.enforce_dims(["[mass]",{"[length]":3}]) |
FOSUnit is a subclass of pint's Unit with a class method for enforcing the correct dimensionality of the unit. |
| start_time | str |
What time the treatment was started |
| end_time | str |
What time the treatment was finished |