IGL

Isotope Geochemistry Language, a text format for isotopic process models



Describes IGL 0.5. Page revision 1.0, 2026-07-31. IR 1.3, results 1.1, IGDB 1.1.

Companion formats

Three documents sit beside the language, each versioned on its own. Tying them to the language version would force a language revision every time a results file gained a field.

Intermediate representation 1.3

What a front end produces and an engine consumes: model.iglir.json. It is the lowered form of a model, with names resolved to indices, units canonicalised, and provenance attached.

The boundary matters more than the encoding. An engine that reads this file and writes a results file is conforming whatever language it is written in, and the conformance suite measures a binary on disk rather than a library, precisely so that a second implementation can be measured at all.

{
  "ir_version": "1.3",
  "provenance": {
    "source_path": "model.igl",
    "source_sha256": "bc8abdb...",
    "databases": [
      { "path": "cr_53cr_52cr.igdb",
        "resolved": "C:/Program Files/IGL/databases/cr_53cr_52cr.igdb",
        "sha256": "d20ee19..." }
    ],
    "generator": "igl 0.1.0"
  },
  "species": [ ... ], "reservoirs": [ ... ], "processes": [ ... ]
}

resolved records where a database name was answered from, because a bare name may be met by a file beside the model or by one an installation provides, and a record naming only what the model wrote leaves a reader unable to tell which.

Results format 1.1

What an engine writes: results.json. Columns are declared with their unit, rows are values in column order, and an undefined value is null rather than zero, because a pool that does not exist yet has no composition and writing zero there would be a measurement nobody made.

{
  "format_version": "1.1",
  "engine": { "name": "igl-engine", "version": "0.1.0" },
  "model": { "ir_version": "1.3", "source_sha256": "bc8abdb...", "simulation": "Closed" },
  "columns": [
    { "name": "Groundwater.Cr(VI).delta53Cr", "kind": "pool_composition",
      "unit": "permil", "reservoir": "Groundwater",
      "species": "Cr(VI)", "isotope": "53Cr/52Cr" }
  ],
  "rows": [ [0.0], [3.212165544] ],
  "diagnostics": [ ],
  "backends": [ ]
}

engine.name names what computed the numbers, not what invoked it: a command line tool, a graphical front end and a batch job driving the same library all report that library, because a reader comparing two results files is asking which code produced them.

backends describes the chemistry engines that fed the run, one entry per declared backend, each naming the executable, the thermodynamic database with its digest, and the values that crossed into the model. Written whether or not the model also outputs those values, and written on a failed run too: a run a chemistry engine refused is exactly the one whose reader most needs to know which engine that was.

IGDB 1.1

A fractionation database: standards, factors, and the citations that justify them. Every numeric value carries a citation, without exception, which is what makes a database auditable rather than merely convenient.

[[fractionation]]
ratio       = "53Cr/52Cr"
process     = "reduction"
reactant    = "Cr(VI)"
product     = "Cr(III)"
regime      = "kinetic"
context     = "natural"
epsilon     = -3.5
ph_range    = [6.5, 8.5]
citation    = "Basu et al., 2014"
doi         = "10.1016/..."
notation    = "epsilon53Cr(III)-Cr(VI)"

A query names the ratio, the mechanism and the two species; the entry answering it is the one that covers the model's conditions and states the most. A tie is an error and not a coin toss: two entries equally applicable to one query mean the database has not decided, and an engine that picked one would hide a disagreement in the literature behind a lookup.

notation is for human readers. No engine reads it, and none should: the quantity is already given by ratio, reactant and product, and parsing a caption to recover it would be recovering by guesswork what is stated next to it.

The published databases are available for download.