VariableRegistry¶
Stores the allowed variable schema.
Expected behavior:¶
- Loads allowed_variables.yaml.
- Stores canonical variable names.
- Stores aliases for each canonical variable.
- Checks duplicate canonical names.
- Checks duplicate aliases.
- Checks alias/canonical-name collisions.
- Resolves aliases to canonical names.
- Stores default computation unit.
- Stores shape: 0 for scalar, 1 for profile.
- Stores default variable-level constraint strings.
- Does not store reactor-specific values.
Example:¶
R: aliases: - major_radius unit: m shape: 0 constraints: enforce: - R > 0
Internally:¶
major_radius -> R R -> R
The registry is the only source of truth for aliases.
Variable¶
Stores one scenario-specific variable instance.
Expected behavior:¶
- Stores canonical name only.
- Does not store aliases.
- Resolves input name through VariableRegistry at construction.
- Stores value, if provided.
- Converts value to registry computation unit before runtime.
- Stores unit as the registry computation unit after conversion.
- Stores rel_tol.
- Stores fixed True/False.
- Stores size for profile variables.
- Stores shape copied from VariableRegistry.
- Accepts scalar input for profile variables and expands to constant profile if size/grid is known.
- Does not own default constraints.
Recommended fields:¶
Variable: name: str # canonical after construction value: float | ndarray | None unit: str # computation unit after conversion rel_tol: float | None fixed: bool shape: int # 0 or 1, from registry size: int | None
Variable-level constraints come from VariableRegistry and are converted into Relation objects when building the active model.
Relation¶
The only equation/constraint object.
A Relation represents:
lhs_function(inputs) op rhs
where rhs may be:
- numeric constant
- variable name
- tuple of variable names
Expected behavior:¶
- Stores relation name.
- Stores input names from function signature.
- Stores constants from default-valued function parameters.
- Stores rhs.
- Stores op: ==, !=, <, <=, >, >=.
- Stores enforce True/False.
- Stores profile_mode: pointwise or global.
- Stores tags.
- Can be used independently of Pyomo.
- Can evaluate lhs from known inputs.
- Can check lhs op rhs when rhs values are also supplied.
- Does not import or expose Pyomo.
Recommended fields:¶
Relation: name: str input_names: tuple[str, ...] constant_names: tuple[str, ...] func: Callable rhs: str | tuple[str, ...] | int | float op: str = "==" enforce: bool = True profile_mode: str = "pointwise" # or "global" tags: tuple[str, ...] = () Output-defining relation @relation(rhs="A") def aspect_ratio(R, a): return R / a
Means:
R / a == A Constraint-like relation @relation(op="<=", rhs=3) def beta_limit(beta_N): return beta_N
Means:
beta_N <= 3 Global profile constraint @relation(op="==", rhs=0, profile_mode="global") def he4_particle_balance(rho, n_D, n_T, n_He4, sigmav_DT, tau_p_He4): local = n_D * n_T * sigmav_DT - n_He4 / tau_p_He4 return trapz(local * 2.0 * rho, rho)
Means:
∫ dn_He4/dt dV == 0
No dummy output variable is required.
@relation decorator¶
The only decorator.
Expected behavior:¶
- Creates a Relation object.
- Requires rhs.
- Defaults op to "==".
- Infers inputs from function signature.
- Treats parameters with defaults as constants.
- Does not use return annotations.
- Resolves variable names through VariableRegistry.
- Allows rhs to be a variable alias, canonical variable name, tuple of names, or number.
- Allows tags.
- Allows enforce True/False.
- Allows profile_mode.
- Accepts simple relation-local constraints as strings, which are converted into additional Relation objects.
Example:¶
@relation(rhs="P_fus_DT", constraints=["P_fus_DT >= 0"]) def fusion_power_dt(P_fus_DT_alpha, P_fus_DT_n): return P_fus_DT_alpha + P_fus_DT_n
Produces:¶
-
fusion_power_dt: P_fus_DT_alpha + P_fus_DT_n == P_fus_DT
-
fusion_power_dt_constraint_0: P_fus_DT >= 0
Both are Relation objects.
UI/YAML constraints¶
Since Constraint is removed, simple constraints are represented as strings and parsed into outputless Relation objects.
Example variable registry:
R: aliases: - major_radius unit: m shape: 0 constraints: enforce: - R > 0 warn: - R < 20
Parsed as:
Relation: lhs = R op = > rhs = 0 enforce = True
Relation: lhs = R op = < rhs = 20 enforce = False
Global reactor constraints:
constraints: enforce: - f_D + f_T + f_He3 + f_He4 == 1 warn: - beta_N < 4
Also become Relation objects.
Recommended parser support:
Allowed: names numbers + - * / ** parentheses comparisons
Rejected: function calls attribute access indexing strings imports boolean logic
Profile/integral constraints should be Python-decorated @relation(...), not YAML strings.
RelationRegistry¶
Stores available Relation objects.
Expected behavior:¶
- Stores registered Relation objects.
- Checks duplicate relation names.
- Emits warning when multiple relations share the same rhs/output variable set.
- Loads relation defaults from relation_defaults.yaml.
- Selects relations by name.
- Selects all relations when no include list is provided.
- Resolves duplicate-output alternatives using defaults or reactor relation-name tags.
Example defaults:¶
tau_E: tau_E_ipb98y2 P_fus: total_fusion_power
If several active relations define tau_E, the registry chooses:
- relation explicitly named in reactor tags, if present
- relation_defaults.yaml default
- otherwise error
TagRegistry¶
Stores allowed tags and tag hierarchy.
Expected behavior:¶
- Loads allowed_tags.yaml.
- Forgives upper/lower case.
- Does exact normalized matching otherwise.
- Rejects unknown tags unless they match a registered relation name.
- Supports tag groups.
- Supports child implying parent.
- Interprets tags in same group as OR.
- Interprets tags across different groups as AND.
Example YAML:¶
device: - tokamak: - spherical_tokamak - compact_tokamak - conventional_tokamak - stellarator
confinement_mode: - h_mode - l_mode
Relation:
@relation(rhs="tau_E", tags=("tokamak", "h_mode", "l_mode")) def tau_E_ipb98y2(...): ...
Means:
device: tokamak AND confinement_mode: h_mode OR l_mode
A reactor tagged spherical_tokamak also matches tokamak.
Reactor¶
Stores one scenario.
Expected behavior:¶
- Reads reactor.yaml.
- Stores metadata.
- Stores reactor tags.
- Stores raw selected relation include/exclude/order settings.
- Stores scenario variables as Variable objects.
- Converts input values to computation units.
- Expands scalar values to constant profiles when a profile variable is expected and grid size is known.
- Stores reactor-level constraint strings.
- Does not compile Pyomo directly except through relation_system() or run().
Example relation section:
relations: include: null exclude: [] order: []
Expected relation selection:¶
If include is provided: use exactly those relation names apply exclude skip tag filtering
If include is omitted/null: start from all registry relations keep generic untagged relations keep tagged relations matching reactor tags apply exclude resolve duplicate rhs/output alternatives
order does not select relations. It only changes ordered execution preference.
RelationSystem¶
Receives concrete Variable objects and active Relation objects.
Expected behavior:¶
- Stores active variables.
- Stores active relations.
- Builds variable lookup by canonical name.
- Checks duplicate active variable names.
- Checks duplicate active relation names.
- Checks that every relation input and rhs variable exists in active variables.
- Collects variable-registry constraints and reactor constraints as additional Relation objects before compile.
- Does not resolve aliases; objects should already be canonicalized.
- Does not know about YAML.
- Does not expose Pyomo to user objects.
The smallest interface between RelationSystem and Relation is:
relation.input_names relation.rhs relation.op relation.enforce relation.profile_mode relation.evaluate(namespace)
RelationSystem compiles:
lhs_function(inputs) op rhs
into Pyomo.
Pointwise mode
For profile variables:
@relation(rhs="Rr_DT", profile_mode="pointwise") def reaction_rate_DT(n_D, n_T, sigmav_DT): return n_D * n_T * sigmav_DT
Compiles to:
n_D[i] * n_T[i] * sigmav_DT[i] == Rr_DT[i] for every i Global mode @relation(op="==", rhs=0, profile_mode="global") def he4_particle_balance(...): return trapz(...)
Compiles to one global Pyomo constraint:
trapz(...) == 0 RelationSystem modes
verify¶
Check whether provided values satisfy the active relations.
Expected behavior:¶
- Variables with values are fixed.
- fixed flag is irrelevant for variables with values; they are fixed anyway.
- Variables without values are free unknowns.
- Enforced relations are compiled.
- Warning relations are evaluated after solve/check.
- If infeasible, diagnostic soft-relation mode may be used.
Interpretation:
"Do these inputs form a feasible model?"
reconcile¶
Adjust non-fixed values minimally to satisfy the model.
Expected behavior:¶
- fixed=True variables are fixed.
- fixed=False variables with values are initialized but free.
- Variables with values are penalized for deviation using rel_tol.
- Variables without values are free.
- Objective minimizes normalized deviation from supplied values.
Interpretation:
"Find the closest feasible scenario to my supplied data."
optimize¶
Optimize an objective while respecting fixed values and ranges.
Expected behavior:¶
- fixed=True variables are fixed.
- Variables with value and no active range/bounds are fixed by default.
- Variables with value and active range/bounds are initialized at value but free within range.
- Variables without value are free, bounded if constraints/ranges exist.
- Objective is user-provided.
Important consequence:
A value alone means fixed in optimize. A value plus a range means initial guess inside allowed range.
Example:
P_fus: value: 500 unit: MW
fixed in optimize unless a range exists.
P_fus: value: 500 unit: MW constraints: enforce: - P_fus >= 300e6 - P_fus <= 800e6
free in optimize within bounds.
ordered¶
Run relations in a requested order without building a full global solve, except for grouped blocks.
Expected behavior:¶
- Accepts relation names.
- Accepts tag groups.
- Relations may be repeated.
- A single relation entry is executed directly if possible.
- A tag group is executed as a verify block.
- The output of earlier steps becomes input to later steps.
- If a relation cannot be directly evaluated because multiple unknowns remain, it fails unless it is inside a verify block.
Example:¶
system.ordered([ "aspect_ratio", {"tags": ["fusion"]}, "total_fusion_power", ])
Expected behavior:
- Run aspect_ratio directly.
- Build a temporary verify block with all active fusion-tagged relations.
- Run total_fusion_power directly.
For grouped tag blocks:
- collect matching relations
- compile temporary RelationSystem in verify mode
- fix currently known values
- solve missing block variables
- merge results back into ordered state
This gives you a hybrid between sequential evaluation and local solve blocks.
Final conceptual model¶
The refactor reduces the system to:
VariableRegistry: allowed variable schema
Variable: scenario value
Relation: every equation, relation, and constraint
RelationRegistry: available model equations
Reactor: scenario + relation selection
RelationSystem: variables + relations -> Pyomo model