M0 interpretations

Populations of individuals, Annex-A sequences, and roll-ups over the actual population — see the design document for the semantics and the adopted decisions, and tutorial 09 for a worked tour.

longeron.m0

M0 interpretations: populations of identified individuals over a model.

Where instantiate() produces one anonymous deep instance, this module produces an Interpretation: a population of Individual runtime instances with stable identities (qname#index paths), built under a strategy ("nominal" follows declared multiplicities and variant order; "random" draws seeded population sizes, variant choices, and unvalued enum/Boolean attribute values), with KerML Annex A sequence semantics (Interpretation.sequences()) and expression roll-ups over the actual population (Interpretation.rollup()sum(rotors.mass) adds the four real rotor individuals instead of hand-encoding 4.0 * rotorMass at M1).

The same representation covers dynamic semantics: every contiguous state activation recorded by longeron.replay.record_timeline() is an occurrence with a lifetime, and from_timeline() turns it into an occurrence Individual (start/end/duration slots) in an ordinary Interpretation – simulation traces and static populations share one M0 story. from_architecture() reads a trade study’s Architecture as a partial interpretation (the variant selection), so M0 roll-ups can be checked against the metrics the trades machinery computes at M1.

Concepts follow pymbe’s interpretation package (random interpretations, Annex A atoms, calc roll-ups) but individuals stay runtime values – the M1 model is never mutated.

The OMG Systems Modeling API has no M0 payload; Interpretation. to_dict() is a deliberate longeron extension (JSON-able, ids included) and is not part of to_api_json.

class longeron.m0.Individual(id, type_name, definition=None)[source]

Bases: Instance

An M0 individual: a runtime Instance with a stable identity.

id is a qname#index path (Pkg::Quad#0.rotors#2); occurrence individuals from from_timeline() use qname@k and carry start/end/duration slots.

to_json()[source]

Alias for to_dict() – the lossless case-recorder seam.

OpenMDAO’s make_serializable tries to_json first when a recorded value is not JSON-native; without this hook a recorded entity case silently degrades to the class-name string 'Individual' (see the mdao-objects design, finding 2). Equal individuals produce equal dicts, so recorded cases compare stably.

Return type:

dict[str, Any]

class longeron.m0.Interpretation(source, strategy, seed, root, selection=<factory>, gaps=<factory>, _interpreter=None, _bindings=<factory>, _pins=<factory>)[source]

Bases: object

A population of M0 individuals for one M1 element.

source: str

qualified name of the interpreted element

strategy: Literal['nominal', 'random', 'trace']

‘trace’: replayed by from_timeline()

selection: dict[str, str]

variant chosen per variation-typed feature path (per-index for heterogeneous random populations, e.g. motors#2)

gaps: list[str]

feature paths whose evaluation degraded to None (with the reason)

individuals(classifier=None)[source]

All individuals (root first, depth-first), optionally filtered to those conforming to classifier (a resolvable qualified name; occurrence individuals match on their recorded type name).

Return type:

list[Individual]

sequences(feature_path)[source]

KerML Annex A sequences for a (dotted) feature path.

A feature is interpreted as a set of sequences whose prefix is an individual of the featuring type: sequences("rotors") yields (quad, rotor_i) tuples, sequences("rotors.mass") yields (quad, rotor_i, 0.06) – nested features are longer sequences.

Return type:

list[tuple[Any, ...]]

rollup(expr)[source]

Evaluate an expression over the actual population.

Feature references resolve against the root individual’s slots, so multi-individual features yield the real value sequences and aggregates aggregate over them: rollup("sum(rotors.mass)") adds the four rotor individuals’ masses. A plain feature name evaluates that feature’s declared M1 expression instead (which fails, honestly, when the M1 expression leans on the homogeneous 4.0 * x convention).

Return type:

Any

sample(n)[source]

n fresh interpretations of the same element under derived seeds (random strategy only).

Return type:

list[Interpretation]

to_dict()[source]

A JSON-able projection (a longeron extension – the OMG API has no M0 representation; see the module docstring).

Return type:

dict[str, Any]

longeron.m0.Strategy

the population-construction strategies interpret() accepts (Interpretation.strategy additionally records "trace" for interpretations replayed from a timeline by from_timeline())

alias of Literal[‘nominal’, ‘random’]

longeron.m0.from_architecture(study, architecture)[source]

Read a trade-study Architecture as the partial M0 interpretation it is: the architecture pins every variation point’s variant, the multiplicities populate the individuals (motors : MotorChoice[4] becomes four individuals of the selected variant), and roll-ups over those individuals reproduce the metrics the trades machinery computed at M1.

Return type:

Interpretation

longeron.m0.from_timeline(timeline, interpreter=None, *, source='<execution>')[source]

Turn a recorded Timeline into an interpretation of occurrence individuals.

Every contiguous activation of a state becomes one occurrence Individual (id qname@k) with start/end/duration slots; the root individual spans the whole execution and owns them in activation order under occurrences. Roll-ups work as usual: rollup("sum(occurrences.duration)").

Return type:

Interpretation

longeron.m0.interpret(model, element=None, *, strategy='nominal', seed=None, bindings=None, selection=None)[source]

Build an M0 interpretation of a part/item definition or usage.

strategy="nominal" follows declared multiplicities (exact bounds expand fully, ranges take their lower bound – matching Interpreter.instantiate) and picks the first declared variant at unresolved variation points. strategy="random" draws population sizes uniformly within multiplicity bounds (unbounded uppers capped at lower + 3), chooses variants per individual, and samples unvalued enum/Boolean attributes from their literal domains – all from one seeded generator, so equal seeds reproduce equal populations.

bindings override root feature values by name; selection pins variation-typed feature paths ({"motors": "emax2306"}) under any strategy. Evaluation failures degrade to None and are recorded in Interpretation.gaps.

Return type:

Interpretation