fix: linCmtB(which1 = -3) now handles infusions (#1236) - #1273
Merged
Conversation
The dose-time (moving-boundary) sensitivity needs dA/dt, which includes the infusion rate -- but ind->InfusionRate is only maintained while solving, so the output pass (which recomputes lhs from the saved amounts after iniSubject() clears the rates) previously saw rate 0 and had to refuse with NA_REAL for any individual with an infusion. linCmtB() now caches the live rate per output index while genuinely solving it (mirroring the existing fx/Jacobian Alast cache keyed on ind->idx/ind->solvedIdx/ind->doSS), and linCmtBdoseTime() reads that cache back on a re-query instead of the by-then-cleared live rate. A steady-state infusion still returns NA_REAL: it establishes its amounts analytically and, for a one-shot SS dose with no following schedule, InfusionRate afterward does not reflect a well-defined ongoing rate (verified empirically, not just reasoned, that using the cache there gives a plausible but physically wrong answer).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #1236.
linCmtB(which1 = -3)-- the dose-time (moving-boundary)sensitivity added in #1235 -- previously returned
NA_REALfor any individualwhose regimen contained an infusion, because the derivative needs
dA/dt(which includes the infusion rate), and
ind->InfusionRateis only maintainedwhile solving.
rxode2_df.cpp's output pass recomputeslhsfrom the savedamounts after
iniSubject()clears the rates, so the rate read there wasalways 0.
linCmtB()now caches the live infusion rate per output index while it isgenuinely solving that index, mirroring the existing
fx/JacobianAlastcache (
getAdvan(idx)/lc.restoreFx()/lc.restoreJac()), keyed on the same!ind->doSS && ind->solvedIdx >= idxcondition that already distinguishes afresh solve from a re-query (e.g. the output pass).
linCmtBdoseTime()reads that cache back on a re-query instead of theby-then-cleared live rate.
NA_REAL: it establishes its amountsanalytically (
handleSSinf8()/solveSSinf()), and for a one-shot SS dosewith no following schedule the solver deliberately leaves
InfusionRateat0 afterward -- the cache-based value there is well-defined but not
physically meaningful (verified empirically: true steady state should give
-dA/dt = 0, but reading the cache at that index gave a nonzero answer).Steady-state bolus never touches
InfusionRateand was already exact.Both correctness bugs an independent Antigravity/Gemini review raised (a
stale
ind->idxsentinel during extra-dose injection; densedop853intra-segment observations) were checked against the actual code and do not
apply here: the extra-dose/
pushDosingEventpath is reachable only frominside the steady-state branch of
handle_evid, which the newlinCmtHasSsInfusion()guard already refuses regardless of cache state; anddense output is unconditionally disabled for any model with
numLin > 0(
rxData.cpp), i.e. any model that could calllinCmtB()at all. Both wereconfirmed unreachable by tracing the call graph and by passing tests that
exercise the surrounding code paths (steady-state infusions,
method = "dop853").Test plan
tests/testthat/test-lincmt-dose-time-sens.R: infusion regimens (single,multiple, duration-specified, oral-into-depot, IV-with-peripheral, mixed
linCmt()+d/dt(),method = "dop853") match a central finitedifference to ~1e-10; steady-state infusions (periodic and continuous)
still report
NA.tests/testthat/test-lincmt-dose-time-sens-guard.R(unaffected linCmtB(which1 = -3) assumes every dose shares one alag(): no per-compartment dose-time sensitivity #1237guard) still passes.
tests/testthat/test-lincmt-solve-sens.R,test-steady-state.R,test-event-sensitivities*.R,test-infusion-bolus.R,test-lincmt-*.Rall pass with no new failures.rm -f src/*.o, required since a shared headerchanged) with no new warnings from the changed files.
triaged above.