lunartrace.temporal
The one-call Study: lt.temporal.study(...) over the exact v1 temporal method.
This module is a facade, not a method. It builds the Lunar ODE query for a site, resolves the
event interval to an lunartrace.science.temporal.EventBasis, obtains the source pages
(live, or retained bytes checked against their receipt) and hands them to
lunartrace.science.temporal.compile_study. The record that comes back is the digest-covered
Study record of method lunartrace.temporal_prepost/v1. Nothing is added to it: frontiers,
why answers and the seven-section anatomy are views over that record.
Examples
Section titled “Examples”>>> import lunartrace as lt>>> study = lt.temporal.study(... site=(-69.3741, 32.32),... event="2023-08-23T12:33Z/12:35Z",... products="LRO/LROC/CDRNAC4",... archive="vikram-2026-10-14",... )>>> study.digest[:15]'sha256:3d75a662'>>> len(study.frontier("angles"))13Only archive=None touches the network. A live Study is labelled live and is never
claimed to reproduce a retained run.
Frontier
Section titled “Frontier”class
FrontierThe pairs that no other pair beats on every objective of one screening.
Behaves as a read-only sequence of PairView. witnesses are the record’s
dominance witnesses, one per dominated pair; comparisons is the number of dominance tests
the screening ran.
IntegrityError
Section titled “IntegrityError”class
IntegrityErrorA Study record no longer hashes to the study_digest it carries.
PairView
Section titled “PairView”class
PairViewOne PRE x POST pair: both observation rows, the angle deltas and the time gaps.
deltas is (Δsun, Δview, Δphase), the absolute incidence, emission and phase
differences in degrees. gaps is (pre, post), the seconds from each observation to the
adopted event instant.
class
StudyA compiled Study: the v1 record plus views over it. Build one with study.
record is the digest-covered dict, exposed read-only at the top level; its nested
values are the record’s own and must not be mutated (verify would then fail).
source_mode is "live" for a Study fetched by this call and "retained" for one
compiled from retained bytes or loaded from a saved file. sources holds the retained
bytes when they are available, which is what save(sources=True) and replay need.
Study.record
Section titled “Study.record”The digest-covered Study record (read-only top level).
Study.digest
Section titled “Study.digest”The record’s study_digest, sha256:<hex>.
Study.summary
Section titled “Study.summary”Counts: observations, pre, post, pairs and the frontier sizes.
Study.limits
Section titled “Study.limits”The four limitations, verbatim from the record.
Study.observations
Section titled “Study.observations”Every observation row of the Study (read-only mappings).
Study.pairs
Section titled “Study.pairs”Every PRE x POST pair row of the Study, ordered by pair_id (read-only mappings).
Study.pair
Section titled “Study.pair”def pair(self, pair_id: str) -> PairViewThe pair with this id.
Raises
Section titled “Raises”KeyError— If the Study has no such pair.
Study.frontier
Section titled “Study.frontier”def frontier(self, name: str) -> FrontierThe Pareto frontier called name.
"angles" (V1_ANGLES), "angles+resolution" (V2_ANGLES_RESOLUTION),
"angles+time" (V3_ANGLES_TIME) or "all" (V4_FULL_AVAILABLE); the V-ids are
accepted as aliases.
Raises
Section titled “Raises”KeyError— Ifnameis not one of those; the message lists the valid names.
Study.why
Section titled “Study.why”def why(self, pair_id: str, frontier: str = 'angles') -> WhyWhy pair_id is on, or off, a frontier, naming the pair that beat it.
The witness is the one the record retained; it is never recomputed. str of the result
is one sentence.
Raises
Section titled “Raises”KeyError— If the pair or the frontier name is unknown.
Study.anatomy
Section titled “Study.anatomy”def anatomy(self) -> dict[str, Any]The Study’s seven sections, in reading order, as a view over the record.
what_ran, from_which_bytes, around_when, on_what, why_these (keyed by
frontier name), where_it_stops and has_anything_moved (the digest).
Study.verify
Section titled “Study.verify”def verify(self) -> boolRecompute the digest from the record; True, or raise IntegrityError.
Study.save
Section titled “Study.save”def save(self, path: str | os.PathLike[str], *, sources: bool = False) -> PathWrite the canonical record bytes to path and return it.
The file’s bytes are exactly canonical_json(record). With sources=True the
retained receipt and raw pages are also written to <stem>.sources/ beside it, which
is what replay and load look for.
Raises
Section titled “Raises”FileNotFoundError— Ifsources=Truebut this Study holds no retained bytes.
Study.replay
Section titled “Study.replay”def replay(self) -> StudyRe-compile from the retained bytes with this Study’s event basis; a new Study.
Compare replay().digest with digest to see whether the run reproduces.
Raises
Section titled “Raises”FileNotFoundError— If no retained sources are attached (save withsources=Trueor passsources=toload).ValueError— If a retained page no longer matches its receipt digest.
Study.to_notebook
Section titled “Study.to_notebook”def to_notebook( self, path: str | os.PathLike[str], *, study_path: str | os.PathLike[str] | None = None,) -> PathWrite a marimo notebook (a .py file) that loads, verifies and shows this Study.
The notebook loads a saved Study file. study_path names it; when omitted the Study is
saved beside the notebook as <notebook stem>.study.json (with its sources, if held).
Returns the notebook path.
class
WhyWhy one pair is on or off a frontier.
witness is the pair the record retained as beating it (None on the frontier).
deltas and witness_deltas are (Δsun, Δview, Δphase). everywhere says, per
frontier name, whether the pair is on that frontier. missing lists objectives the pair
could not be evaluated on, in which case it is neither on the frontier nor beaten.
function
def load( path: str | os.PathLike[str], *, sources: str | os.PathLike[str] | None = None,) -> StudyRead a saved Study file and verify it.
sources names a directory of retained bytes; by default <stem>.sources/ beside the
file is used when it exists.
Raises
Section titled “Raises”IntegrityError— If the record does not hash to itsstudy_digest.ValueError— If the file is not a Study record.
function
def study( *, site: tuple[float, float] | str, event: str, products: str, archive: str | os.PathLike[str] | None = None, box: tuple[float, float] = DEFAULT_HALF_BOX_DEG,) -> StudyCompile the temporal PRE/POST Study for a site, an event interval and a product type.
Parameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
site | `tuple[float, float] | str` |
event | str | "START/END": ISO UTC times ending in Z, END exclusive and optionally just a clock time on START’s date ("2023-08-23T12:33Z/12:35Z"); or a registered basis id such as "vikram:minute-envelope". An interval equal to a registered basis uses that basis and its cited rationale; any other is recorded as adhoc:<interval>. |
products | str | "IHID/IID/PT", for example "LRO/LROC/CDRNAC4". |
archive | `str | os.PathLike[str] |
box | tuple[float, float] | Half-width (dlat, dlon) of the query box in degrees. Default DEFAULT_HALF_BOX_DEG. |
Raises
Section titled “Raises”ValueError— For a malformed argument, an archive fetched for a different query, or a retained page whose bytes no longer match its receipt.