Skip to content

fix SolidsCentrifuge costing: use the pusher correlation and cost N centrifuges in parallel - #259

Merged
sarangbhagwat merged 1 commit into
masterfrom
fix-solids-centrifuge-cost-type
Oct 9, 2026
Merged

sarangbhagwat merged 1 commit into
masterfrom
fix-solids-centrifuge-cost-type

Conversation

@sarangbhagwat

@sarangbhagwat sarangbhagwat commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Issue addressed

SolidsCentrifuge._design picked its cost correlation with

cost = 68040*(ts**0.5) if centrifuge_type else 170100*(ts**0.3)

The centrifuge_type setter only accepts the non-empty strings 'reciprocating_pusher' and 'scroll_solid_bowl', so the condition is always true. As a result:

  • Every reciprocating pusher was priced with the scroll solid bowl correlation, and the pusher correlation was dead code.
  • 'Number of centrifuges' = ceil(S/S_max) was computed but never applied, so loadings above the correlation's upper limit were costed as one extrapolated machine.

Underlying cause

Both regressions date to 203a0a9 (2021). That commit replaced the scroll-only @cost('Solids loading', cost=68040, CE=567, n=0.50, ub=40, N='Number of centrifuges'), which costed N = ceil(S/40) parallel units at S/N each, with hand-written code to support two types. The rewrite tested the type string's truthiness instead of its value, and dropped the N scaling.

Published correlations: Seider et al., Product and Process Design Principles, Table 16.32 (CE = 567). The 3rd edition's Table 22.32 has the same values at CE = 500 (60,000 and 150,000). Costs are per centrifuge, with S in short ton/hr of solids:

Type C_P S range
continuous scroll solid bowl 68,040 S^0.50 2–40
continuous reciprocating pusher 170,100 S^0.30 1–20

Fix

  • A new class attribute, purchase_cost_coefficients, sits next to solids_loading_range, and _design looks the coefficients up by type.
  • Loadings above S_max are split across N = ceil(S/S_max) centrifuges at S/N each, via parallel['Centrifuges'] = N. This is the same convention as @cost and LiquidsCentrifuge.
  • Unchanged:
    • Power stays at 1.40 kWh/m³ of feed and is not scaled by N.
    • F_BM stays at 2.03.
    • The cost key stays 'Centrifuges'.
    • Loadings below the lower limit still only warn.
  • Zero solids gives zero cost, with no division by N = 0.
  • The docstring has a Notes section citing the correlations.

Effect on default costs

I measured this by simulating 14 Bioindustrial-Park configurations before and after the fix.

Case Change
Scroll centrifuges at ≤ 40 ton/hr (sugarcane, HP, TAL, oxalic, succinic sugarcane/glucose, cane sugar) none
Pusher centrifuges: the high-rate WWT SludgeCentrifuge (cornstover, HP ×4, TAL ×4, oxalic) purchase cost +112–289%; installed equipment cost +0.04–0.30%; selling price +0.02–0.16%
Scroll units above 40 ton/hr: succinic corn and cornstover, N = 4–7 purchase cost +100–165%; installed equipment cost about +0.35%; price +0.13–0.15%

The biosteam.wastewater MESP doctest still prints $1.4/gal (1.3729 → 1.3739). No doctest output changed.

Validation

  • New regression tests in tests/test_solids_separation.py:

    • each type's cost at 10 ton/hr matches its published correlation;
    • at 60 ton/hr, scroll uses N = 2 and pusher N = 3, each costed in parallel;
    • power is not scaled by N;
    • zero solids gives zero cost.

    The two cost tests fail on the old code (pusher priced at 215,351 instead of 339,694; 60 ton/hr scroll priced at 527,500 instead of 745,998) and pass with the fix.

  • Full suite (local stack; --noconftest, because the repo conftest.py uses the path hook argument that pytest 9 removed). Some failures are pre-existing on unmodified master because of local stack drift:

    Run Failed Passed Skipped
    Unmodified base 33ae06ab 87 459 62
    With this change 87 463 62

    The failure list and failure reasons are identical; the 4 extra passes are the new tests.

  • Independent code review: it confirmed the parallel handling, the _setup/_load_costs interaction (no double counting on repeated simulation), and the SludgeCentrifuge paths (including SKIPPED). It found that nothing downstream reads the 'Centrifuges' cost item directly.

  • The branch merges cleanly onto current master (5b2836c). The upstream commits since the base don't touch costing.

Possible follow-ups (not in this PR)

  • The cost steps up when a loading crosses S_max, as with any @cost item that has ub.
  • The high-rate WWT sludge centrifuge is set to reciprocating_pusher and runs below that correlation's 1 ton/hr minimum. Most of the pusher cost increase comes from extrapolating there. Whether to clamp low loadings, or to use scroll_solid_bowl for sludge, is a separate decision about default costs.

🤖 Generated with Claude Code

…entrifuges in parallel

SolidsCentrifuge._design selected its cost correlation with
`68040*(ts**0.5) if centrifuge_type else 170100*(ts**0.3)`. The
centrifuge_type setter only admits the non-empty strings
'reciprocating_pusher' and 'scroll_solid_bowl', so the condition was always
true: every reciprocating pusher was priced with the scroll solid bowl
correlation and the pusher correlation was dead code. The same code computed
'Number of centrifuges' = ceil(S/S_max) but never used it, so loadings above
the upper limit of the correlation were extrapolated as a single machine.

Both regressions date to 203a0a9 (2021), which replaced the scroll-only
`@cost('Solids loading', cost=68040, CE=567, n=0.50, ub=40,
N='Number of centrifuges')` with hand-written code to support two types.
The decorator had costed N = ceil(S/40) parallel units at S/N each; the
rewrite tested the truthiness of the type string instead of its value and
dropped the N scaling.

Seider et al., Table 16.32 (CE = 567; 3rd ed. Table 22.32 at CE = 500 gives
60,000 and 150,000), per centrifuge with S in short ton/hr of solids:
- continuous scroll solid bowl:      68,040 S^0.50, S = 2-40
- continuous reciprocating pusher:  170,100 S^0.30, S = 1-20

The fix looks up the coefficients by type (new class attribute
`purchase_cost_coefficients`, next to `solids_loading_range`) and costs
N = ceil(S/S_max) centrifuges at S/N each via `parallel['Centrifuges']`,
the same convention as @cost and LiquidsCentrifuge. Power stays 1.40 kWh/m3
of feed (not scaled by N), F_BM stays 2.03, loadings below the lower limit
still only warn, and zero solids gives zero cost (no division by N = 0).

Effect on default costs (approved by S. Bhagwat): scroll centrifuges at
<= 40 ton/hr are unchanged. Pusher centrifuges (the high-rate WWT
SludgeCentrifuge) rise 112-289% in purchase cost at the loadings found in
HP/TAL/cornstover biorefineries (installed equipment +0.04-0.30%, selling
price +0.02-0.16%). Scroll units above 40 ton/hr (succinic corn/cornstover,
N = 4-7) rise 100-165% (installed equipment ~+0.35%, price +0.13-0.15%).
The wastewater MESP doctest still prints $1.4/gal (1.3729 -> 1.3739); no
doctest output changed.

Validation: new tests/test_solids_separation.py (type-specific cost at
10 ton/hr, N = 2/3 parallel costs at 60 ton/hr, power not scaled by N,
zero solids) fails on the old code for the two cost tests and passes now.
Full suite (IBO_2026, --noconftest workaround): 87 failed, 463 passed,
62 skipped, with the identical failure list and reasons as the unmodified
baseline (87 failed, 459 passed, 62 skipped); +4 passed are the new tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sarangbhagwat sarangbhagwat self-assigned this Oct 9, 2026

@yoelcortes yoelcortes left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good! Thank you for catching this, taking the time to fix it, and adding tests!

@sarangbhagwat
sarangbhagwat merged commit 49e4bed into master Oct 9, 2026
1 of 3 checks passed
@sarangbhagwat
sarangbhagwat deleted the fix-solids-centrifuge-cost-type branch October 9, 2026 06:20
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