PWR-HYBRID-3/fret-pipeline/docs/fret_to_synth.md
Dane Sabo cebf8c167a Initial umbrella repo: thesis + FRET pipeline + plant model with first controllers
Folds three previously-separate pieces into one preliminary-example repo
for the HAHACS thesis:

- thesis/ (submodule) → gitea Thesis.git — the PhD proposal
- fret-pipeline/ — FRET requirements to AIGER controller (was
  ~/Documents/fret_processing/; prior single-commit history abandoned
  per user decision)
- plant-model/ — 10-state PKE + lumped T/H PWR model (was
  ~/Documents/PKE_Playground/; never version-controlled before)
- presentations/2026DICE/ (submodule) → gitea 2026DICE.git
- reachability/, hardware/ — empty placeholders for Thrust 3 and HIL
- docs/architecture.md — how the discrete and continuous layers compose
- claude_memory/ — session notes and scratch knowledge pattern

Plant model refactored to thesis naming (x, plant, u, ref); pke_th_rhs
now takes u as an explicit arg instead of reading rho_ext from the
params struct. First two controllers built to the contract
u = ctrl_<mode>(t, x, plant, ref): ctrl_null (baseline) and
ctrl_operation (stabilizing, proportional on T_avg). Validated under a
100% -> 80% Q_sg step: ctrl_operation reduces steady-state T_avg drift
~47% vs. the unforced plant.

Root CLAUDE.md emphasizes that CLAUDE.md files are living documents and
that any knowledge not captured before a session ends is lost forever;
claude_memory/ holds the session-level notes that haven't stabilized
enough to graduate into a CLAUDE.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 16:24:11 -04:00

5.0 KiB

fret_to_synth.py -- FRET Export to Synthesis Config

Purpose

Converts a FRET JSON export into a synthesis configuration file that ltlsynt (from the Spot library) can consume. Handles the critical encoding step: FRET's multi-valued mode variables become independent boolean propositions with automatically generated mutual exclusion constraints.

Usage

python3 scripts/fret_to_synth.py <fret_export.json> [output.json]

If output.json is omitted, writes to specs/synthesis_config_v3.json.

How It Works

Step 1: Mode Discovery (discover_modes)

Regex-scans all ftInfAUExpanded (and fallback ft) LTL formulas in the FRET export for patterns matching:

(control_GROUPNAME = q_VALUENAME)

Returns a dictionary of mode groups:

{
    "control_mode": ["q_heatup", "q_operation", "q_scram", "q_shutdown"],
    "control_pump": ["q_off", "q_running"],  # if present
}

This is fully automatic -- no hardcoded variable lists. Adding a new mode in FRET and re-exporting is all that's needed.

Step 2: Boolean Encoding (build_mode_map)

For each mode group, creates boolean proposition names:

control_mode = q_shutdown  -->  in_mode_shutdown
control_mode = q_heatup    -->  in_mode_heatup
control_pump = q_off       -->  in_pump_off

The naming rule is: in_{group_suffix}_{value_suffix}, where the group suffix strips control_ and the value suffix strips q_.

Returns a string replacement map used by encode_ltl to transform the FRET LTL formulas.

Step 3: Variable Classification (classify_variables)

Every variable in the FRET semantics is classified:

  • Mode variables (control_*, q_*): handled by boolean encoding, excluded from explicit input/output lists.
  • Everything else: classified as an environment input. These are typically continuous-domain predicates that have been abstracted to boolean (e.g., t_avg_above_min, inv1_holds).

Step 4: LTL Encoding (encode_ltl)

Simple string replacement of (control_X = q_Y) with the corresponding boolean name. A sanity check ensures no control_ or q_ references survive the transformation -- if a mode value appears in the LTL but wasn't discovered in Step 1, the script raises an error rather than producing a silent bug.

Step 5: Mutual Exclusion (mutual_exclusion_ltl)

For each mode group, generates an "exactly one active" LTL constraint:

G( (a & !b & !c & !d) | (!a & b & !c & !d) | (!a & !b & c & !d) | (!a & !b & !c & d) )

This is conjoined with the requirement formulas in the final output.

Why this is needed: FRET's control_mode = q_X is a single integer variable -- it inherently holds one value. After decomposition into independent booleans, the synthesizer could assert in_mode_heatup = true AND in_mode_scram = true simultaneously and still satisfy every requirement. The mutual exclusion constraint prevents this. It is a synthesis encoding artifact, not a gap in the FRET specifications.

Step 6: LTL Formula Selection

The script prefers ftInfAUExpanded from FRET's semantics object. This is the future-time LTL formula with:

  • Infinite-horizon semantics (no LAST boundary terms)
  • Expanded AU (always-until) operators

Fallback is ft (the raw future-time formula, which may contain LAST operators). The ftInfAUExpanded form produces cleaner synthesis results because ltlsynt works over infinite traces.

Output Format

The generated JSON contains:

Field Description
spec_name {PROJECT}_{COMPONENT} from the FRET export
mode_groups Discovered control groups, their values, and boolean names
inputs Environment (uncontrollable) variables
outputs System (controllable) boolean mode propositions
requirements Each FRET requirement with original and encoded LTL
structural_constraints Auto-generated mutual exclusion constraints
conjoined_ltl All formulas AND'd together, ready for ltlsynt

Future Improvements

  • Multi-valued non-mode variables: Currently assumes all non-mode variables are boolean. If FRET exports integer-valued environment variables (e.g., power_level = 3), the encoding would need range-based boolean decomposition.

  • Environment assumptions: No mechanism yet for adding assumptions about the environment (e.g., "t_max_exceeded cannot persist forever"). These would need to be separated from guarantees in the ltlsynt call (--ins assumptions vs. --outs guarantees). This is important for liveness properties.

  • Liveness constraints: The old synthesis config had a manual F(heatup) liveness constraint. Currently there is no automatic detection of which requirements imply liveness vs. safety. A future version could parse FRET's timing semantics to classify these.

  • Partial observability: All environment variables are currently assumed fully observable. Real reactor instrumentation has sensor noise and delays. Extending the pipeline to handle partial observability would require belief-state synthesis.