Skip to content

Solve size_batch loading time analytically when sizing by V_max - #250

Merged
yoelcortes merged 1 commit into
masterfrom
fix-size-batch-V_max-solution
Aug 20, 2026
Merged

Solve size_batch loading time analytically when sizing by V_max#250
yoelcortes merged 1 commit into
masterfrom
fix-size-batch-V_max-solution

Conversation

@sarangbhagwat

@sarangbhagwat sarangbhagwat commented Aug 20, 2026

Copy link
Copy Markdown
Member

Fixes #249.

Fix

The fixed-point equations have an exact analytical solution: the quadratic's discriminant collapses to a perfect square, giving

tau_loading = V_max * V_wf / F_vol

— the time to fill a reactor at maximum working volume under constant filling. This PR uses it directly:

if N_reactors is None:
    # Time required to load a reactor filled to maximum working volume
    # (analytical solution at a continuous number of reactors)
    tau_loading = V_max * V_wf / F_vol

    # Minimum number of reactors; ceil ensures N_reactors >= 2
    N_reactors = ceil(F_vol * (tau_reaction + tau_cleaning + tau_loading) / (V_max * V_wf))

The subsequent (already-existing) recompute of V_T, V_i, and tau_loading from the integer N_reactors is unchanged. Consequences:

  • No iteration, no spurious root, no ZeroDivisionError.
  • N_reactors = ceil(F_vol*(tau_reaction + tau_cleaning)/(V_max*V_wf) + 1) is the minimum count satisfying V_i <= V_max, and is always >= 2, so the max(N_reactors, 2) clamps are removed as redundant.
  • The unused from flexsolve import wegstein import is dropped.
  • Docstring notes updated; the stale constant-loading doctest updated to the actual (integer-N-consistent) output.

Verification

All on Python 3.14.7 / thermosteam 0.53.5:

  1. Doctests (CI flags NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL NUMBER ELLIPSIS):
    pytest --doctest-modules biosteam/units/design_tools/batch.pypasses (fails on master);
    pytest --doctest-modules biosteam/units/nrel_bioreactor.py — passes (the N=8 docstring example is byte-identical, confirming the N-given path is untouched).
  2. Issue reproduction (NRELEthanolFermentation with tau=8, V_max=3785, the docstring feed):
    • 2.53.11: ZeroDivisionError in size_batch.
    • This branch: simulates cleanly — 2 reactors, 1727 m³ each (≤ 3785 m³ cap), loading time 11 hr, batch time 22 hr; self-consistent (working volume 1554 m³ = 141.3 m³/hr × 11 hr, and each reactor loads for exactly the 8 + 3 hr the other spends reacting + cleaning).
    • Identical numbers to current master's clamped-iteration result, so no behavior change where master already works.

…ng time in closed form

When called with V_max and no N_reactors/loading_time (e.g.,
NRELEthanolFermentation(..., V_max=...)), size_batch solved for the
loading time by fixed-point iteration. The iterated map always admits
the spurious root tau_loading = -(tau_reaction + tau_cleaning), which
is attracting whenever F_vol*(tau_reaction + tau_cleaning) < V_max*V_wf;
wegstein (called with convergence checks disabled) then silently
converges to it, yielding ceil(0) = 0 reactors and a ZeroDivisionError
in the released 2.53.11 (masked on master by the max(N_reactors, 2)
clamps, which leave the fragile iteration in place).

The fixed-point equations have an exact closed-form solution,
tau_loading = V_max*V_wf/F_vol (the loading time of a reactor at
maximum working volume under constant filling), from which the minimum
number of reactors follows directly and is always >= 2. Use it instead
of iterating; drop the now-unneeded clamps and flexsolve import; update
the docstring notes and the stale constant-loading doctest (failing on
master since the clamps changed the output).
@sarangbhagwat sarangbhagwat changed the title Solve size_batch loading time in closed form when sizing by V_max Solve size_batch loading time analytically when sizing by V_max Aug 20, 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.

I double check the math by hand. I never realized it had a closed solution until now. Good catch!!

@yoelcortes
yoelcortes merged commit 8a376a0 into master Aug 20, 2026
1 of 3 checks passed
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.

size_batch sized by V_max: replace wegstein iteration with analytical solution (fixes ZeroDivisionError without clamping N_reactors)

2 participants