Skip to content

Look underneath a result

In this lesson you will look inside the result handle you have already been using. By the end you will be able to distinguish a convenient name from an exact version, and use one registered operation through sample.core without moving your data elsewhere. Allow about ten minutes; this is a chance to understand the model, not learn a longer setup.

The example uses the sample and raw handle from Record your first measurement, still active after lesson 3, so a session that has followed the tutorial this far is ready. You need only the base package.

A name helps you find a result; a reference pins it down

Section titled “A name helps you find a result; a reference pins it down”

Imagine writing “the FTIR spectrum” in your lab notebook. That is enough for a conversation until someone corrects the spectrum. Now there are two questions: which result are we talking about, and which version of it did we use? MTRL keeps those questions separate.

A checkpoint is the logical result, such as your measured spectrum. Its checkpoint_id stays the same through a correction. A record is one immutable version of that checkpoint, and its record_id names that version alone. The handle’s ref pairs those identifiers into an exact record reference.

Name: ftir-replicate-01 a readable label, not a unique version
Checkpoint: C the result whose identity survives a correction
├── Record: R1 the original values; an old handle still names these
└── Record: R2 the corrected values; active selection finds these
Exact references: (C, R1) and (C, R2) name different versions of the same result.

The letters stand in as placeholders, not identifiers you type into the example. The point is that a handle does not silently follow a correction. If an analysis used R1, its reference must continue to say R1, even after R2 becomes active. Otherwise the history would describe an analysis you never actually performed.

Names are useful for finding things, but they are not a first-or-latest shortcut. select returns every exact match to your filters; one requires exactly one match and raises LookupError if it finds none or several. A name may be reused, so identifiers help you narrow a selection when the label alone is ambiguous.

The same lab folder, with a more detailed view

Section titled “The same lab folder, with a more detailed view”

So far Sample has taken care of the structure around your measurements. Underneath it, sample.core is the Mtrl application that owns the history. Think of the facade as the convenient front of the same lab folder, not a second folder with another copy of your work.

The core contains runs, which group measurement or analysis results, and the checkpoints within them. InstrumentRun and MaterialData are the core construction types. You do not need to construct them for routine recording, transforming, correcting or aggregating: Sample already takes you to that same model. There is no separate facade archive format to learn.

One reason to use the detailed view is a registry: the application’s set of approved, named operations. The facade’s transform accepts your callable, not an operation name. For a registered operation, use sample.core, then come back to facade selection.

Try one registered operation and come back

Section titled “Try one registered operation and come back”

The example first checks that the handle’s reference matches the core’s active reference. It then uses the registered resample_linear operation to put the spectrum on 51 points instead of 101. Notice the explicit x_field and y_field: the operation needs to know which field supplies coordinates and which supplies values. It does not guess from their names.

Finally, sample.one finds the new checkpoint. You have changed the level of API detail, not the application holding your results.

print(f"checkpoint: {raw.checkpoint_id}; record: {raw.record_id}")
print(f"original handle still resolves: {raw.ref == sample.core.active_ref(raw.checkpoint_id, run_id=raw.run_id)}")
aligned_checkpoint = sample.core.transform(
raw.run_id,
raw.checkpoint_id,
"resample_linear",
parameters={
"x_field": "wavenumber",
"y_field": "absorbance",
"x_targets": np.linspace(2000.0, 3000.0, 51).tolist(),
},
checkpoint_name="aligned",
)
aligned = sample.one(checkpoint_id=aligned_checkpoint.checkpoint_id)
print(f"aligned: {aligned.name}, {len(aligned.to_numpy())} points")
print(f"active records: {len(sample.records)}")
Terminal window
checkpoint: 01a18304-dadc-74b8-b8cf-70ae86c8e344; record: 01a18304-dae2-7181-9fb2-17f65805fcec
original handle still resolves: True
aligned: aligned, 51 points
active records: 2

The first line shows the two generated identifiers of the measurement and its corrected replacement. Yours will differ — identifiers mint on each commit — but the shape stays: two distinct versions side by side, the stale check passing for the original, and the aligned output holding all 51 target points across the two active records.

The handle and the core agreed on the exact version of the measured spectrum. The core then committed the registered resampling result as a new checkpoint in one revision. Selecting that checkpoint returned a facade handle, ready to use like any other result. The core remained the sole owner of the records, lineage, revisions and archive behaviour.

Keep one more distinction in mind: sample.records, select and one show active records, not the whole historical catalogue. For an older version, use sample.core.get_record(record_id). An old handle may still be readable even when a new mutation using it is refused; reading history and extending the current analysis are different requests. The next lesson makes that difference visible.

Find a result and fix a mistake puts those two identifiers to work: one result keeps its identity while its values get a new version. The registry guide covers approved operation sets.