1 Commits
Author SHA1 Message Date
leonarski_fandClaude Opus 5 cb08f63a52 model validation: the bulk solvent is searched inside its physical range, not fitted without bounds
gemmi offers two scalers and we were using the one without bounds. Its Levenberg-Marquardt path
has nothing stopping the flat-solvent parameters from leaving the range the model means anything
in; the alternative path that does declare bounds is behind a compile guard we have never
enabled. On this corpus six datasets in fifty-one fitted a b_sol outside it, the worst at 1707
A^2.

What that does is subtler than a bad scale, and worth recording because it is why nobody noticed:
a b_sol that large does not corrupt the solvent term, it switches it off - 1.4% of it survives at
10 A - so the model is simply scaled without a solvent contribution and the R-factors look
unremarkable.

k_sol and b_sol now come from a grid search over the physical box, with the scale and the
anisotropic B refitted at each candidate pair, following Afonine et al. Refitting at each point is
what makes it work: clamping the parameters after an unbounded fit costs up to 0.044 in R-free,
because it leaves the scale and B where the rejected fit put them. Non-physical fits go from six
in fifty-one to none, and both R-work and R-free come out slightly but significantly better
rather than merely no worse.

Which reflections are fitted remains the caller's business - the function scales whatever it is
handed - so the working-set restriction of the previous commit is not something this can undo.

A crystal with no solvent-accessible volume needs no special case: its mask is empty, so the
solvent term is identically zero whatever the parameters say. There is a test for that.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EFEJG6WBQv8th4UJFNe53N
2026-09-07 10:34:21 +02:00