OrdinateDB

Documentation

product Pre-releaseunwritten sections are marked

A PI engineer’s guide to OrdinateDB

If you have spent years with the PI System, you already know how to think about plant data: points arrive from instruments, an asset framework gives them context, analyses derive new values, displays put them in front of operators. None of that thinking is wasted here. This page maps your vocabulary onto OrdinateDB’s and — more usefully — explains the places where the mapping is deliberately not one-to-one.

It reads best with the live demo open in another tab. The examples below — the cr-102 crusher, motor_power_kw, the multi-site mining model — all exist there, so you can check each claim as you read it.

The concept map

Ten mappings cover most of what you will reach for in the first month. The third column is the part worth reading.

PI conceptOrdinateDB conceptThe difference that matters
TagAttribute (on an asset)Series belong to modelled equipment, not a flat namespace; the tag name you used to memorise becomes a path: site/line/asset.attribute.
Digital setEnum setRegistry-owned and versioned; each state carries an explicit meaning, not just a string, and polarity variants (running/stopped vs stopped/running) are mapped once, not per tag.
UOM databaseQuantity kinds & unitsUnits belong to quantity kinds (mass flow, pressure); dimensional checking is enforced in calculations, not advisory.
AF element / element templateAsset / templateSame instinct, stronger contract — templates also generate displays, entry forms and guard rails, and instances track drift from their template.
Event frameEpisodeBatches, downtime, shifts and campaigns are one concept; genealogy links episodes; episode-relative queries are first-class.
Analysis (Asset Analytics)Calculation engineExpression tier for the common case, WASM tier for the rest; deterministic and replayable, and recalculation over corrected history is first-class and bitemporal.
PI VisionDataviz portalDisplays are generated from templates and honest by construction — gaps render as gaps; hand-tuning is tracked.
PSE / PI BuilderAdmin portalTree, grid and timeline over the same versioned model; bulk edits are model transactions with diffs.
DataLinkAPI / ordctl / client librariesThe spreadsheet path is a supported client of the same public API, not a special interface.
Archive files / tuningStorage tiers & lifecycle policiesHot/warm/cold with a documented Parquet cold tier; retention is a visible policy object, not a tuning parameter.

Tags become attributes

Everything you know about what makes a good point transfers: sensible engineering units, an expectation about how the value behaves between samples, a clear owner. What changes is where the context lives. In a flat namespace the tag name has to carry it, so you end up with conventions like SE1-CR102-M01-PWR and a naming standard document that is enforced by vigilance. In OrdinateDB the model is the namespace: the same series lives at site-east/crushing/cr-102.motor_power_kw — a path derived from where the asset sits in the model, not a string somebody typed.

Because the address is derived, it is not fragile. Series identity is stable underneath the path, so moving an asset in the model or correcting a name does not orphan history — the trend on bearing_temp_c survives the reorganisation that would once have meant a tag rename project. The habit to unlearn is memorising tag strings; the habit to build is walking the model. ordctl series list --asset cr-102 answers the question you used to answer with a tag search, and includes source and unit while it is at it.

Templates carry more weight than AF templates

If you have built AF element templates, the core instinct transfers directly: define the asset class once, instantiate the fleet, inherit attribute definitions with their units and categories. Keep that instinct — it is the intended way of working here, not an optional discipline.

The contract is stronger in two ways. First, a template generates more than structure: the standard display, the manual entry form and the limit traits (the guard rails that both displays and calculations respect) all come from the same definition. Model a crusher template with motor_power_kw, feed_rate_tph, bearing_temp_c and a run_state, and commissioning cr-201 is instantiation plus signal binding — the display exists before anyone draws anything. Second, instances track drift: cr-102 can tell you which of its attributes depart from the template and when the departure was made, so a fleet of two hundred conveyors does not quietly become two hundred bespoke configurations.

The thing to unlearn is retrofit order. In a points-first world, tags exist and context is added later if someone finds the time. Here the flow inverts: signals arrive in staging and are bound to modelled attributes, so context is the precondition, not the aspiration.

Enum sets are a registry, not a per-point setting

Digital sets map cleanly onto enum sets: an ordered collection of named states that a discrete series takes values from. Two things are different. Enum sets are owned by a registry and versioned, so a state added for one plant is a visible change with a history, not a local edit that surprises the next site. And each state carries an explicit meaning — running is a concept the system understands and can report time-in-state against, not merely the string that happens to display.

Polarity is the practical win. Two vendors’ packaged drives disagree: one reports 0 as stopped, the other reports 0 as running. In a per-tag world that is per-tag configuration, audited by hope. Here you declare the polarity variant once, at binding, and every query runs against the canonical set — so a time-in-state report over run_state across the whole crushing fleet does not begin with an inspection of each point’s digital set assignment.

Episodes replace event frames — and a few other things

The event frame shape is familiar: a start, an end, context attributes, generation from trigger conditions. If you have built event frames for batches or downtime capture, you already think in episodes. The consolidation is the point: batches, downtime, shifts and campaigns are one concept with one query surface, rather than separate mechanisms grown at different times.

Two additions do real work. Genealogy links episodes to each other, so a grinding campaign on ml-03 knows which crushing shifts fed it — the material lineage question becomes a traversal, not a join you maintain by hand. And episode-relative time is first-class in queries: average specific_energy_kw over the last ten campaigns, excluding downtime episodes, is one expression. The habit to unlearn is treating events as a separate database to be searched on its own terms; episodes participate in queries the way series do.

Calculations are deterministic, and history gets corrected

The Asset Analytics workflow transfers: expressions defined on the template, mapped per instance, triggered by events or run on a schedule. The expression tier is deliberately the boring choice for the common case; when you outgrow it, the WASM tier takes anything that compiles to WASM, sandboxed, rather than asking you to bolt an external scheduler onto the historian.

Two properties are stronger than what you are used to. Every calculation is deterministic and replayable: the engine records which input values produced which output, so “why did this calc produce 741.86?” has an inspectable answer. And recalculation over corrected history is first-class and bitemporal: when a late lab result amends an input, the dependent calculations re-run over exactly the affected range, and both the value as originally computed and the value as now known remain queryable. Month-end reports stop being a race against late-arriving data.

Unit checking is enforced along the way. Define specific energy as motor_power_kw / feed_rate_tph and the result is dimensionally checked as kWh/t; bind a pressure to the flow slot and you get a compile error at authoring time, not a plausible-looking wrong number in a report.

Querying: modes are explicit, provenance is attached

Your instincts about summaries, filtered summaries and plot values all carry over. What changes is that nothing is implicit. The read mode — raw, interpolated, stepped — is stated per query, and the modes that are legal depend on the series’ continuity class: you cannot interpolate a discrete state, and the system will not quietly do it for you. The same query returns the same number for every caller and every client library.

Every summary arrives with its provenance attached — pct_good, lossy ranges if a lifecycle policy has thinned the window, manual origin where a human typed the value. The number never travels without its confession. For screens, render mode does PlotValues-style decimation and says so in the response. And the DataLink habit survives intact: the spreadsheet path is a client of the same public API, with the same authentication and the same provenance columns, so the figure in the board pack and the figure in the API response cannot drift apart.

The deliberate differences, and why

Four places where the mapping is intentionally not one-to-one. None of these is an oversight or a missing feature; each is a design decision with a stated reason and a governing spec.

Totalisers

In the PI System, totals are commonly produced as stored points: a totaliser point or an analysis accumulates a flow and writes the period figure at period end. OrdinateDB stores counter series as raw monotonic counts with a declared rollover, and computes totals at query time over whatever window you ask for — including the 23- and 25-hour days that DST transitions produce — with pct_good attached to the result.

The reason: the archive should store what the instrument said and do arithmetic on demand. One stored series then answers every window question that will ever be asked of it, and a redefinition of the reporting day is a query change, not a re-totalising project. The counter continuity class is defined in SPEC 04 §2; query-time totalisation and its provenance are governed by SPEC 04 §4.

Compression defaults

The PI System applies exception and compression filtering by default, with per-point deviation settings — defaults set when archive storage was expensive, and rational for that era’s economics. OrdinateDB writes raw by default. Any thinning is an explicit lifecycle policy — chosen, visible, audited — and query results over a thinned range disclose the lossy interval rather than presenting reconstructed data as raw.

The reason: storage economics changed, and the default should match them. Where thinning is still the right call — decade-old data on a cold tier — it is a policy object you can read, review and revoke, not a per-point parameter distributed across the estate. Governed by SPEC 03 §6.

Boundary types and retrieval

In the PI System, what a retrieval call returns is shaped by boundary type options and per-point step attributes; the combination of client, call and point configuration determines the result. OrdinateDB makes the read mode explicit in the query itself, and each series’ continuity class constrains which modes are legal. The same query always returns the same number.

For migration there is compat: pi mode, which reproduces PI retrieval semantics so you can run your existing reports against both systems and compare digit-for-digit before switching anything over. It exists for validation, not as a permanent dialect. The read model is governed by SPEC 04 §3; compat: pi by SPEC 04 §6.

Extrapolation past the last value

The PI System can extend the last recorded value forward to the requested time when asked for a current value or a trend to now. OrdinateDB will not extend a trend beyond the last known point. Past the data, the honest answer is no answer, and it renders as exactly that: a gap, marked as a gap.

The reason is provenance: a rendered value must correspond to something the archive knows, and a flat line into the future asserts knowledge nobody has. If a display makes it look as though the instrument stopped reporting, that is because it did — which is the fact an operator needs. Governed by SPEC 04 §2.

Your first week

A schedule that has worked. Each day assumes the previous one, and none of them assumes the manual has been read end to end.

  • Day 1. Open the demo and read the introduction. Find cr-102, trend motor_power_kw, and pay attention to what the display does where the data stops.
  • Day 2. Model an asset class you know from memory as a template — a crusher, a pump set — and instantiate it twice. Notice what the template generated that you expected to build by hand.
  • Day 3. Point a collector at test data (an OPC UA simulator is fine), then unplug it mid-run and plug it back in. Watch store-and-forward drain the edge buffer and backfill without a hole in the archive.
  • Day 4. Rebuild a display you know from memory. Most of it will already exist, generated from the template; what you hand-tune is recorded as a tracked departure.
  • Day 5. Port one calculation and one report query. The calculation will surface any unit assumption your original was silently making; the report query is where compat: pi earns its keep.

The getting-started section that would normally anchor this week is not yet written — the plan for it is public in the sidebar, and this page will link to it when it lands. Until then, the introduction plus the demo dataset is the path. If your question is not “how do I become productive” but “how does an estate move”, migrating from PI covers the extraction, parallel-run and validation path at the estate level.

By Friday the vocabulary should feel familiar, and the differences should feel like what they are: decisions, with reasons you have now read.