Using the Python package
The lunartrace package is the numerical authority of the project. Downstream readers consume
its results and validated projections; they do not reimplement its rules. This guide shows how to
call the public functions. Every signature, argument and raised exception is in the generated
Python API reference, which is built from the source docstrings.
Run scripts with the project environment, for example uv run --frozen python my_script.py. The
slope-quality functions are imported from Python. The one console script, lunartrace, belongs to the
temporal Study.
The temporal PRE/POST Study has its own section. lt.temporal.study(...) compiles it in one call,
and its frontiers, why answers, saved files and replay are covered in
The one-call Study.
Choose an entry point
Section titled “Choose an entry point”| Need | Use | API page |
|---|---|---|
| Evaluate grids you already hold in memory | slope_quality.evaluate | slope_quality |
| Evaluate from files, with optional frozen-hash checks | slope_quality.run_paths | slope_quality |
| Run the frozen Site01 study with CAS verification and a receipt | study.run_study | study |
| Build a summary projection from canonical result bytes | study.build_projection | study |
| Emit per-cell raster sidecars | raster_projection.emit_from_manifest | raster_projection |
| Compile a temporal PRE/POST Study, screen its frontiers, save and replay it | lunartrace.temporal.study | The one-call Study |
| Model the cross-client contracts | lunartrace.projection | projection |
| Read the standing levels | lunartrace.standing | standing |
DIRECT evaluation
Section titled “DIRECT evaluation”evaluate is the in-memory interface. It assumes that its Grid and RoiSet inputs are already
verified. Construct a Grid from an array, an affine transform, a CRS and a label, and an RoiSet
from closed polygons:
from lunartrace.science import slope_quality as sq
slope = sq.Grid(slope_array, transform, crs, "my-slope")rms = sq.Grid(rms_array, transform, crs, "my-rms")rois = sq.RoiSet((polygon,), "my-rois")
result = sq.evaluate(slope, rms, rois, taus=(0.5, 2.0))for row in result.rows: print(row.roi, row.tau, row.joint_cells, row.included_cells, row.included_fraction)Each Row carries the ROI and τ, the ROI, joint and included cell counts, the valid-cell fraction,
slope quantiles and mean, and the histogram. The label "union" is the union of all ROIs. A None
mean or quantile means that no cell was included. Do not treat a None as zero.
When the inputs come from files, use run_paths. It loads them and can check caller-supplied SHA-256
values against the bytes:
from pathlib import Path
from lunartrace.science import slope_quality as sq
frozen = {"slope": "<sha256>", "slope_rms": "<sha256>", "roi_shp": "<sha256>"}result = sq.run_paths( Path("slope.tif"), Path("slope_rms.tif"), Path("roi.shp"), frozen=frozen)The hashes you supply are caller identity. run_paths checks them, but it does not establish that
the bytes came from a frozen source. For that, use STUDY.
STUDY: custody-checked execution
Section titled “STUDY: custody-checked execution”run_study is the path with custody. It reads a source-witness manifest, resolves each role blob from
the local content-addressed store, re-hashes every blob, and writes result.json and an execution
receipt:
import osfrom pathlib import Path
from lunartrace.science.study import run_study
receipt = run_study( Path("research/sources/pgda78-site01.manifest.json"), Path(os.environ["LUNARTRACE_SOURCE_STORE"]), Path(".build/site01-study"),)print(receipt["status"], receipt["result_sha256"])The receipt’s status is METHOD_EXECUTED. Its bytes differ between runs because it records timestamps
and the environment. The result.json bytes are deterministic and match the documented result hash for
the same inputs. Read the workflows guide for what a receipt does and does not
establish.
load_manifest and resolve_inputs expose the same manifest and CAS resolution steps separately, if you
need them without running the method.
Typed projections
Section titled “Typed projections”build_projection wraps canonical result bytes with provenance from the witness and validates the typed
projection model. The model classes live in lunartrace.projection. They are frozen Pydantic models with
extra fields forbidden, so a misspelled field is an error, not a silently dropped value.
The Standing type is a literal union of METHOD_EXECUTED, NUMERIC_REPRODUCED,
SCIENCE_VERTICAL_QUALIFIED and BLOCKED. A client must not raise the evidence level. Choosing a
standing is the caller’s assertion, and it has to match the evidence that exists.
Per-cell raster projections are built by raster_projection. encode_layers applies the same
validity and inclusion rules and encodes slope values as little-endian float32 only when that conversion
is lossless. write_projection validates the manifest and writes the JSON with its binary sidecars.
Errors
Section titled “Errors”Inputs outside the method’s preconditions raise MethodInputError, a subclass of ValueError. Examples
are a non-finite or negative τ, or grids that are not compatible with one another. Cell-level rules are
applied as data rules rather than errors: a slope outside 0 to 90 degrees, or a non-positive slope RMS,
makes that cell invalid and it is excluded from the denominator. Nothing is clipped or interpolated. Catch this exception only to report or to choose a different input; do not catch it
to substitute a default.
Conventions
Section titled “Conventions”- Angles are degrees. τ is a slope-RMS cutoff in degrees, not propagated uncertainty.
- Counts and
included_fractionare cells and fractions of jointly valid cells. Neither is a physical area. - The histogram uses 2° bins from 0 to 90 degrees.
- A standing is a label, not a computed property. Admission, landing safety and physical area are not established by these functions.
Related
Section titled “Related”- Method: the cell rules these functions implement.
- Provenance: what the witness, CAS and receipt establish.
- Command line: the same operations as module entry points.