|
RadCluster
Graph-based cluster dynamics for irradiated materials
|
Functions | |
| dict | load_ledger () |
| tuple | detect (machine_index=None) |
| list | build_commands (str stage, dict machine, dict reg, int slots, float budget_h, list share, int grid_v=2000, int grid_vbin=20) |
| tuple | residual_rank (dict led) |
| tuple | pick_levers (dict led, list worst, int n_wanted=3) |
| box_for (str col, dict led, float base) | |
| float | usable_span (str col, dict led, float base, int prefer) |
| list | levels_for (str col, float base, int n, int prefer=0, box=None) |
| int | prefer_for (str col, str obs, ratio) |
| dict | parse_box_override (str spec) |
| tuple | build_design (dict led, list levers, list n_levels, str stage, int row_base, list why=None, dict box_override=None) |
| cost_key (dict cm, dict machine) | |
| dict | estimate_cost (dict led, dict machine, int n_rows) |
| main (argv=None) | |
Variables | |
| encoding | |
| errors | |
| HERE = Path(__file__).resolve().parent | |
| str | LEDGER = HERE / "calibration_ledger.json" |
| str | MACHINES = HERE / "machines.json" |
| dict | LEVER_MAP |
| dict | DIRECTION |
| dict | PRIOR_BOX |
| dict | VALIDITY_BOX |
| float | MIN_USABLE_SPAN = 0.10 |
| dict | INTEGER_COLS = {"i_mobile", "v_mobile", "i_discrete", "v_discrete"} |
| dict | BOOKKEEPING |
| dict | INTEGER_LEVERS = {"i_mobile"} |
Propose the next calibration stage for whatever machine this is.
READS calibration_ledger.json (written by learn.py)
WRITES design/<stage>_calib.csv, design/<stage>_labels.json, and the `next`
block of the ledger + CALIBRATION_GUIDE.md
IT DOES NOT LAUNCH ANYTHING. It prints the exact run_ensemble command, sized
to this machine slot count and row budget, and stops. A design nobody read
can burn 20 h of a machine time, and the machine is usually one the author is
not sitting at.
HOW IT CHOOSES. Greedy, and deliberately simple enough to argue with:
1. Pin every parameter to the BEST VALID ROW in the ledger. That row is the
only point known to reach full dose grid-clean, so it is the only safe
place to stand while varying something.
2. Rank the residuals |log10(model/target)| over the six observables.
3. Choose levers for the biggest residual, preferring in this order:
never-varied > inconclusive > live
and never proposing a lever the ledger calls `dead`.
4. Size the factorial to the machine slot count so every row runs
concurrently, and refuse if the estimated cost exceeds the row budget.
The lever->observable map below is the ONE piece of physics judgement in this
file. It is explicit so it can be corrected as the campaign learns, rather
than buried in a heuristic.
Usage:
python plan.py # propose for this machine
python plan.py --machine 0 # propose as if machine 0
python plan.py --levers eta,f_cl_i # override the choice
python plan.py --stage S15 # override the name
| plan.box_for | ( | str | col, |
| dict | led, | ||
| float | base | ||
| ) |
The prior box for `col`, clipped to the region measured as affordable. THE CLIFF LOCATION IS UNKNOWN. S14 shows E_b_i2 = 0.60 never reaches dose and 0.75 always does; the boundary is somewhere in between and nothing measures where. Placing a design point inside that unresolved interval is a coin flip that costs a full row budget when it loses, so the box is clipped to the nearest value KNOWN to complete -- not to just above the value known to fail.
| list plan.build_commands | ( | str | stage, |
| dict | machine, | ||
| dict | reg, | ||
| int | slots, | ||
| float | budget_h, | ||
| list | share, | ||
| int | grid_v = 2000, |
||
| int | grid_vbin = 20 |
||
| ) |
The run_ensemble invocation(s) for this stage. THE SHARD INDEX IS NOT THE REGISTRY INDEX. run_ensemble assigns a row with `assign_machine(row_id, of) == row_id % of`, so with `--of 1` only `--machine 0` is ever given any rows. Passing this host registry index (2 for the workstation) with `--of 1` silently assigns it ZERO rows and the run exits having done nothing. Shard index is therefore always the position within the participating set, while the registry index is used only to name the output file so two machines cannot collide.
| tuple plan.build_design | ( | dict | led, |
| list | levers, | ||
| list | n_levels, | ||
| str | stage, | ||
| int | row_base, | ||
| list | why = None, |
||
| dict | box_override = None |
||
| ) |
| plan.cost_key | ( | dict | cm, |
| dict | machine | ||
| ) |
Which cost_model entry is this registry machine? THE TWO FILES KEY THE SAME MACHINE DIFFERENTLY. learn.py keys cost_model by platform.node() -- the value rows actually carry -- while machines.json keys by human name. 'MATRIX-PC2' never equals 'Matrix-PC', so entry 1 matched nothing and fell through to the "scaled from another machine" branch, which then picked MATRIX-PC2's OWN numbers (it has the worst median) and inflated them by the 0.5/0.4 speed ratio: a measured 14.02 h worst row was reported as 17.5 h and tripped the budget refusal on a design that fits. Match on the registry's own node_regex, which is the one field that already means "what this host calls itself".
| dict plan.estimate_cost | ( | dict | led, |
| dict | machine, | ||
| int | n_rows | ||
| ) |
| list plan.levels_for | ( | str | col, |
| float | base, | ||
| int | n, | ||
| int | prefer = 0, |
||
box = None |
|||
| ) |
n levels for `col`, spanning the prior box. `prefer` is the corrective direction: -1 sweeps from the box floor up to the base, +1 from the base up to the ceiling, 0 spans the whole box. The base is always one of the endpoints when prefer != 0, so the stage stays anchored to the point already known to be grid-clean.
| dict plan.parse_box_override | ( | str | spec | ) |
| tuple plan.pick_levers | ( | dict | led, |
| list | worst, | ||
| int | n_wanted = 3 |
||
| ) |
| int plan.prefer_for | ( | str | col, |
| str | obs, | ||
| ratio | |||
| ) |
| tuple plan.residual_rank | ( | dict | led | ) |
Observables ordered by how badly they miss, worst first. Returns (chaseable, deferred). Deferred observables are still ranked and reported -- they are just not allowed to select levers. See `policy.deprioritized_observables` in the ledger for why each is deferred.
| float plan.usable_span | ( | str | col, |
| dict | led, | ||
| float | base, | ||
| int | prefer | ||
| ) |
| dict plan.BOOKKEEPING |
| dict plan.DIRECTION |
| dict plan.INTEGER_COLS = {"i_mobile", "v_mobile", "i_discrete", "v_discrete"} |
| dict plan.LEVER_MAP |
| dict plan.PRIOR_BOX |
| dict plan.VALIDITY_BOX |