Skip to content

FEAT: swap cost model purge and estimate block - #195

Merged
decay71 merged 2 commits into
mainfrom
feat/estimate-engine-purge
Oct 25, 2026
Merged

decay71 merged 2 commits into
mainfrom
feat/estimate-engine-purge

Conversation

@stefanodangelo

@stefanodangelo stefanodangelo commented Oct 24, 2026 •

Copy link
Copy Markdown
Collaborator

Estimate lane, slice E1b of 2. Completes swap_cost.py (E1a is #193). Reworked after review so the purge model follows what the engine does today.

  • Purge length for a swap: the flush-matrix stamp from the preflight, else swap_purge_length if above 0, else the stock 80 mm. Only the stamp is clamped, to 40..150 mm as in the post-processor; the other two are used as set. purge_matrix off ignores the stamp, as ACE_SET_PURGE MATRIX=0 does.
  • Purge time: about 0.166 s per mm plus about 15 s fixed (travel, wipe), per purging swap. Not flow-limited. A background swap that finished loading purges on the parked head, so its purge time is not on the critical path (only in transport mode or after an abort, flagged purge_inline on the timeline event); its grams still count, with the 40 mm bg prime (BG_LOAD_PRIME_EXTRA) on top. Both numbers are named constants.
  • Slicer header parser: time, per-tool mm and grams, layers, prime tower or flush destination.
  • build_estimate: totals, per-colour breakdown, swap counts and assumptions. Always labelled estimated, never measured. No price or cost yet; that waits for a spool price field.
  • Adds tools/swap_cost.py to managed/manifest.json.

The per-swap constants in UNMEASURED_S are unchanged and stay in one table so they are easy to replace. They are the seconds without the purge (about 209 s inline and 30 s bg), which is billed on top, so the old 210/30 "day one" claim no longer holds with the default 80 mm flush. Nothing calls the module yet, so there is no behaviour change.

Tests: 96 in total in tests/unit/test_swap_cost.py. The header tests use a small inline slicer footer, because the shared sample gcode is trimmed and has no footer. pytest tests/unit: 381 passed.
Size: about 540 lines of code, 330 of tests.

@decay71 decay71 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the plan and the work on this.

On groups 5 and 6, my feeling is that purge should stay in the slicer: the flush matrix is where people are used to set it, and two places to tune the same thing would confuse more than help. So I would rather not add per-pair overrides, a colour-aware guess or bin tracking on our side, unless you see a strong reason for them that I am missing. If anything, a single purge scale (one percent value on the length the engine already uses) and making the existing 40/150 mm clamp configurable would fit as small extensions of what is there.

For E1b it would help if the model followed what the engine does today: the flush-matrix stamp from the preflight, else swap_purge_length if > 0, else the stock 80 mm, clamped to 40/150 mm. The other inputs can come later if we add them. Purge time also does not seem to be flow-limited in practice: on my machine it is about 0.166 s per mm plus about 15 s of fixed choreography (travel, wipe). The spool price could wait until there is a price field.

Here are measured numbers from last night's print (axolotl test file, head mode, 4 ACEs, 91 swaps, medians):

  • Inline swap: 204 s (unload 104 s, of which 69 s pre-prime and tip forming; load 103 s)
  • Background unload done, load inline: 108 s
  • Background transport done (filament staged at the toolhead sensor): 85 s
  • Purge/flush: 32.5 s

These depend a lot on the setup. This run used transport mode and a tip-form table with a 30 mm pre-prime. On an earlier print without pre-prime and with full background swaps I measured 146 s inline, about 60 s staged and 5 to 10 s when the background swap had already loaded and primed the head.

One more thing: docx-tech is working on #186, which lets the tool change wait for a background cold pull instead of aborting it. In this run 43 of 56 background swaps were aborted in the cold pull, so the background numbers will likely change soon. Keeping the constants easy to replace is probably better than tuning them closely now.

Dirk

@stefanodangelo
stefanodangelo force-pushed the feat/estimate-engine-purge branch from a93572e to f12cc6e Compare October 25, 2026 10:33

@decay71 decay71 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the purge chain and the timing now follow the engine. Three things left, the last one is my mistake:

  1. Day one is no longer the old 210/30. The seed constants from #193 reach 209 s inline and 30 s bg without purge, and those two numbers were whole-swap costs including the flush. With the purge billed on top (28 s at 80 mm) it is now 237 s and 58 s. Please take the default purge out of the seed, or drop the "day one behaves exactly as before" line.

  2. Purge seconds for cross_ace_bg: when the background swap has loaded and primed the head, the purge ran on the parked head and is not on the critical path. It is only inline in transport mode or when the bg swap was aborted. The grams count either way, and the bg prime is the purge length plus 40 mm (BG_LOAD_PRIME_EXTRA).

  3. My wording in the review was off: only the flush-matrix stamp is clamped to 40..150, in the post-processor. swap_purge_length is used as set (0..200 in the config) and the stock 80 is not clamped either, so resolve_swap_purge_mm should only clamp the stamp.

@stefanodangelo

Copy link
Copy Markdown
Collaborator Author

@decay71 Agreed on keeping purge in the slicer. I've dropped pair overrides, the colour-aware guess and bin tracking, and the old flow limit too, and I updated this PR.

The model now resolves a swap's purge the way the engine does: the preflight's flush stamp, else swap_purge_length if above 0, else 80 mm, clamped to 40..150. Purge time is 0.166 s/mm plus 15 s per swap. The price and cost fields are out until there is a spool price field.

The per-swap constants stay in one table and I haven't tuned them to your numbers. I'd rather wait until #186 lands and the background numbers settle.

I only have one question: does the engine also clamp a swap_purge_length between 1 and 39 up to 40? I assumed it does, because you described the clamp that way, but I only checked the flush-stamp path.

@decay71

decay71 commented Oct 25, 2026

Copy link
Copy Markdown
Owner

No, it doesn't, sorry, that was my wording again. swap_purge_length is used exactly as set: 0 means the stock 80, anything from 1 to 200 goes to INNER_FLUSH_FILAMENT unchanged, and the stock macro doesn't clamp LENGTH either. Only the preflight clamps its own flush-matrix stamps to 40..150 (point 3 of my last review, our comments crossed).

@stefanodangelo
stefanodangelo force-pushed the feat/estimate-engine-purge branch from c61c1c1 to 2765de0 Compare October 25, 2026 13:29
@stefanodangelo

Copy link
Copy Markdown
Collaborator Author

No problem, all three are in.

  • Day one: I kept the seeds and dropped the claim. They're about 209 s inline and 30 s bg without the purge, which is billed on top, so the default inline lands about 28 s above the old 210. I can re-seed them lower if you prefer.
  • Parked bg swap: no purge seconds. The grams still count, with the 40 mm prime. With purge_inline set (transport mode or an abort), the seconds are billed.
  • Clamp: only the stamp is clamped to 40..150.

One question: in transport mode, does the bg load still prime to purge + 40 mm? I only add the 40 mm when the purge is parked.

@decay71 decay71 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, all three are in, and the numbers check out. Merged!

@decay71
decay71 merged commit 8d7beb3 into main Oct 25, 2026
7 checks passed
@decay71
decay71 deleted the feat/estimate-engine-purge branch October 25, 2026 15:51
@decay71

decay71 commented Oct 25, 2026

Copy link
Copy Markdown
Owner

Transport mode: no. A transport bg load ends at the toolhead sensor and stages there, there is no grip and no prime, so the +40 mm bg prime does not happen. The arrival then loads inline with the full flush, so in transport mode the purge time is on the critical path (purge_inline) and the grams are just the purge length. The +40 mm only exists in the full bg mode, where the parked head is gripped and primed through the open dock.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants