FAQ¶
Where do I start if I am new?¶
End-to-End Guide: Building a Complete System — the recommended starting point, a full walkthrough
Quickstart — shorter examples for each major path
Mental Model — the three-layer execution model
API Reference — API reference
When should I use ConfiguredModel.evaluate vs compile_graph + Evaluator?¶
Default: call ConfiguredModel.evaluate after instantiate. Pass slot handles
(ValueSlot objects like cm.root.some_param) and unitflow quantities. The library
compiles the graph on first use, validates by default, and returns a RunResult.
Use the explicit pipeline when you need:
Evaluator.evaluate_async(async external compute backends)direct inspection or reuse of the
DependencyGraphcontrol over validation timing (e.g. validate once, then loop with
evaluate(..., validate=False))integration with tooling that constructs
EvaluatorandRunContextexternally
Both paths share the same compiled graph cache on the ConfiguredModel when you mix them.
See Quickstart and Concept: Evaluation.
Why do I need RunContext?¶
ConfiguredModel is frozen topology. RunContext stores per-run mutable values and results.
Default
evaluate()path: you do not create aRunContext; the method creates a fresh one per call unless you passrun_context=explicitly.Explicit
Evaluatorpath: createRunContext()and pass it toEvaluator.evaluateorevaluate_async.
Why are inputs bound by stable id in the explicit pipeline?¶
The evaluator’s wire format is keyed by stable slot ids (strings). Stable ids are deterministic and unambiguous for a configured topology.
On the facade, prefer slot handles as keys (cm.root.some_param) — same binding,
less error-prone. String keys are allowed when they refer to a parameter/attribute slot’s
stable_id.
I changed code in a notebook and behavior is weird.¶
If declarations changed, restart the kernel and re-run compile/instantiate cells. Old class artifacts may still be cached.
When should I use Requirement?¶
Use Requirement whenever requirements need structure, local acceptance logic,
and traceability to a “shall” statement.
Every Requirement class must call model.name(...) and model.doc(...) exactly once.
Inside define(), use the same authoring surface as Part:
model.parameter, model.attribute, model.constraint, and model.composed_of.
Mount the requirement tree on a System with model.composed_of(name, RequirementType),
then wire values in with model.allocate(req_ref, part_ref, inputs={...}).
See Concept: Requirements for the full pattern.
How do I split requirements into a hierarchy?¶
Create a separate Requirement subclass for each coherent group of checks, then compose
them with model.composed_of:
class ThermalReq(Requirement):
@classmethod
def define(cls, model):
model.name("thermal_req")
model.doc("Thermal envelope shall not be exceeded.")
temp = model.parameter("peak_temp_c", unit=degC)
limit = model.parameter("temp_limit_c", unit=degC)
model.constraint("temp_within_limit", expr=temp <= limit)
class L1SafetyReqs(Requirement):
@classmethod
def define(cls, model):
model.name("l1_safety_reqs")
model.doc("Level-1 safety requirements.")
model.composed_of("thermal", ThermalReq)
# ...more children...
There is no limit to nesting depth.
When does model.composed_of return a PartRef vs RequirementRef?¶
model.composed_of(name, ChildType) dispatches on the type of ChildType:
If
ChildTypeis aPartsubclass → returns aPartRefIf
ChildTypeis aRequirementsubclass → returns aRequirementRefAnything else →
ModelDefinitionError
Use the returned ref to navigate children in allocate calls:
model.allocate(reqs.child_name, part_ref, inputs={...}).
The graph compiled but evaluate failed or results look wrong.¶
Compilation only builds topology; it does not prove inputs, units, or external bindings
are coherent. With the explicit pipeline, call validate_graph(graph, configured_model=cm)
after compile_graph and inspect ValidationResult.failures before evaluating.
With ConfiguredModel.evaluate, validation runs automatically unless you pass validate=False.
If validation passes but evaluation still fails, check:
missing required parameters (not bound in
inputs=)wrong keys in the
inputsmap (use slot handles on the facade, stable id strings on the explicit path)external
computeexceptions — the failing check name inRunResult.constraint_resultsusually points to the cause
How do I filter constraint results by requirement?¶
ConstraintResult rows carry requirement_path (dot-separated path string of the
requirement package) and allocation_target_path (path of the allocated Part) when
the constraint belongs to an allocated requirement package.
# All failing constraints from a specific requirement subtree
failures = [
cr for cr in result.constraint_results
if not cr.passed and cr.requirement_path and "l1_safety" in cr.requirement_path
]
The graph compiled but evaluate is very slow.¶
For tight evaluation loops after you have validated once, pass validate=False to skip
the static checks:
result = cm.evaluate(inputs={...}, validate=False)
How do I run a parameter sweep?¶
Use tg_model.analysis.sweep:
from tg_model.analysis import sweep
results = sweep(cm, param_slot=cm.root.analysis.payload_kg, values=[500 * kg, 750 * kg, 1000 * kg])
for r in results:
print(r.passed, r.outputs[...])
See tg_model.analysis for the full signature.