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 Team and Dataset
One of your first actions might be to create a new Team and a Dataset, which you and your collaborators can populate. The code below creates a Team and one Dataset associated with it. It also inspects the newly registered Team to get its unique id. Note that all resources are given descriptive names and summaries.
from citrine.resources.team import Team
from citrine.resources.dataset import Dataset
band_gaps_team = citrine.teams.register(name="Band gaps",
description="Actual and DFT computed band gaps")
print("My new team has name {} and id {}".format(
band_gaps_team.name, band_gaps_team.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_team.datasets.register(Strehlow_Cook_dataset)
1.9.3. Find an existing Team and Dataset
Often you will work with existing resources.
The code below retrieves a Team with the name “Copper oxides team” and a dataset with a known unique id that is stored as dataset_A_uid
.
For more information on retrieving resources, see Reading Resources.
team_name = "Copper oxides team"
all_teams = citrine.teams.list()
copper_oxides_team = next((team for team in all_teams
if team.name == team_name), None)
assert copper_oxides_team is not None
dataset_A = copper_oxides_team.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_team.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.