1.9. Code Examples

1.9.1. Create a Session

All interaction with the Citrine API begins by creating a Citrine session. This session establishes a link to the Citrine Platform with your API key. Your API key is unique and must be kept secret. Best practice is to store is as an environment variable, here named CITRINE_API_KEY. Assuming that your Citrine deployment is https://matsci.citrine-platform.com and uses port 443, the code below would establish a Citrine session.

from citrine import Citrine
API_KEY = os.environ.get('CITRINE_API_KEY')
API_SCHEME = 'https'
API_HOST = 'matsci.citrine-platform.com'
API_PORT = '443'
citrine = Citrine(API_KEY, API_SCHEME, API_HOST, API_PORT)

1.9.2. Create a Project and Dataset

One of your first actions might be to create a new Project and a Dataset, which you and your collaborators can populate. The code below creates a Project and one Dataset associated with it. It also inspects the newly registered Project to get its unique id. Note that all resources are given descriptive names and summaries.

from citrine.resources.project import Project
from citrine.resources.dataset import Dataset
band_gaps_project = citrine.projects.register(name="Band gaps",
    description="Actual and DFT computed band gaps")
print("My new project has name {} and id {}".format(
    band_gaps_project.name, band_gaps_project.uid))

Strehlow_Cook_description = "Band gaps for elemental and binary " \
    "semiconductors with phase and temperature of measurement. DOI 10.1063/1.3253115"
Strehlow_Cook_dataset = Dataset(name="Strehlow and Cook",
    summary="Strehlow and Cook band gaps", description=Strehlow_Cook_description)
Strehlow_Cook_dataset = band_gaps_project.datasets.register(Strehlow_Cook_dataset)

1.9.3. Find an existing Project and Dataset

Often you will work with existing resources. The code below retrieves a Project with the name “Copper oxides project” and a datset with a known unique id that is stored as dataset_A_uid. For more information on retrieving resources, see Reading Resources.

project_name = "Copper oxides project"
all_projects = citrine.projects.list()
copper_oxides_project = next((project for project in all_projects
    if project.name == project_name), None)
assert copper_oxides_project is not None
dataset_A = copper_oxides_project.datasets.get(uid=dataset_A_uid)

1.9.4. Find a template

Templates provide a controlled vocabulary for organizing your data, and should be established before uploading other data. Generally you will work with existing templates. The example below searches for a process template with the tag “Oven_17” and asserts that one result is returned.

firing_templates = list(band_gaps_project.process_templates.list_by_tag(tag="Oven_17"))
assert len(firing_templates) == 1
firing_template_17 = firing_templates[0]

1.9.5. Create a linked process, material, and measurement

Imagine you purchase some toluene, measure its index of refraction, and then use it as an ingredient in a chemical reaction. The code below converts those actions into data model objects: the process of purchasing, the material of toluene, the optical measurement, and the use of the toluene as an ingredient in a subsequent process. Specs relate the intent and runs relate what actually happened, which may or may not be the same. This assumes that you have a Dataset named solvents_dataset and that you have already created or retrieved the following: a process template purchase_template, a material template toluene_template, a measurement template refractive_index_template, a process template reaction_template, a condition template temperature_template, a parameter template wavelength_template, and a property template refractive_index_template.

from gemd import Condition, Parameter, Property, NominalReal, IngredientRun, IngredientSpec, \
    MaterialRun, MaterialSpec, MeasurementRun, MeasurementSpec, ProcessRun, ProcessSpec

buy_toluene_spec = solvents_dataset.process_specs.register(
    ProcessSpec("Buy toluene", template=purchase_template))
toluene_spec = solvents_dataset.material_specs.register(
    MaterialSpec("Toluene", process=buy_toluene_spec, template=toluene_template))
refractive_index_spec = solvents_dataset.measurement_specs.register(
    MeasurementSpec("Index of refraction", template=refractive_index_template,
    conditions=[Condition("Room temperature", template=temperature_template, value=NominalReal(22, 'degC'))],
    parameters=[Parameter("Optical wavelength", template=wavelength_template, value=NominalReal(633, 'nm'))]))
reaction_spec = solvents_dataset.process_specs.register(ProcessSpec("A chemical reaction", template=reaction_template))
toluene_ingredient_spec = solvents_dataset.ingredient_specs.register(
    IngredientSpec("Toluene solvent", material=toluene_spec, process=reaction_spec, absolute_quantity=NominalReal(34, 'mL')))

buy_toluene_run = solvents_dataset.process_runs.register(
    ProcessRun("Buy 1 liter of toluene", tags=["lot2019-140B"], spec=buy_toluene_spec))
toluene = solvents_dataset.material_runs.register(
    MaterialRun("Toluene", process=buy_toluene_run, spec=toluene_spec))
refractive_index_run = solvents_dataset.measurement_runs.register(
    MeasurementRun("Index of refraction", spec=refractive_index_spec, material=toluene,
    conditions=[Condition("Room temperature", template=temperature_template, value=NominalReal(24, 'degC'))],
    parameters=[Parameter("Optical wavelength", template=wavelength_template, value=NominalReal(633, 'nm'))],
    properties=[Property("Refractive index", template=refractive_index_template, value=NominalReal(1.49, 'dimensionless'))]))
reaction_run = solvents_dataset.process_runs.register(
    ProcessRun("A chemical reaction", spec=reaction_spec))
toluene_ingredient = solvents_dataset.ingredient_runs.register(
    IngredientRun(spec=toluene_ingredient_spec,
    material=toluene, process=reaction_run, absolute_quantity=NominalReal(40, 'mL'), notes="I poured too much!"))

1.9.6. Getting a material history

Continuing the above example, the following code would retrieve the material history for toluene by using its Citrine id.

scope = 'id'
toluene_history = solvents_dataset.material_runs.get_history(id=toluene.to_link(scope))

toluene_history is a MaterialRun that can be traced back to see its spec, the measurement performed on it, that measurement’s spec, the process that created it, and that process’s spec. The following statements are true:

toluene_history.measurements == [refractive_index_run]
toluene_history.measurements[0].spec == refractive_index_spec
toluene_history.process == buy_toluene_run
toluene_history.process.spec == toluene_history.spec.process == buy_toluene_spec

Note that the material history does not include a reference to the ingredients derived from the material. Traversal “forward in time” is not possible.