psi_crit, stem_b, stem_c and beta2 are set_traits() arguments and are
not bound as fields, so from R they can be written and not read.
That is fine while a caller only ever sets them. It stops being fine as soon as
anything has to compute a quantity the model defines in terms of them. The case
that raised it: Sperry's hydraulic cost normalises by k(psi_soil) − k_crit, and
k_crit = kmax * proportion_of_conductivity(psi_crit). leaf_specific_conductance_max_
is bound and proportion_of_conductivity() is bound, so the only missing piece is
psi_crit — which the caller has to have remembered from their own
leaf_traits() call.
In leaf_calibration_test/sicangco-2026 that meant a hard-coded 5.870283 in a
probe script, which is exactly the kind of constant that outlives the value it was
copied from.
Suggested fix: bind the four as access: field read-only in practice, the way
vpd_leaf_ is in #93 — they are derived-state-adjacent rather than settable, and
hazard 10 already explains at length why writing a trait directly is wrong. If a
read-only binding is not expressible through RcppR6, an accessor returning the
current trait vector would do as well.
⚠️ Not a request to make them settable. Hazard 10 is right: set_traits() exists
because changing a trait means rebuilding two splines and clearing the solved
operating point and the temperature cache. This is only about being able to read
back what was set.
psi_crit,stem_b,stem_candbeta2areset_traits()arguments and arenot bound as fields, so from R they can be written and not read.
That is fine while a caller only ever sets them. It stops being fine as soon as
anything has to compute a quantity the model defines in terms of them. The case
that raised it: Sperry's hydraulic cost normalises by
k(psi_soil) − k_crit, andk_crit = kmax * proportion_of_conductivity(psi_crit).leaf_specific_conductance_max_is bound and
proportion_of_conductivity()is bound, so the only missing piece ispsi_crit— which the caller has to have remembered from their ownleaf_traits()call.In
leaf_calibration_test/sicangco-2026that meant a hard-coded5.870283in aprobe script, which is exactly the kind of constant that outlives the value it was
copied from.
Suggested fix: bind the four as
access: fieldread-only in practice, the wayvpd_leaf_is in #93 — they are derived-state-adjacent rather than settable, andhazard 10 already explains at length why writing a trait directly is wrong. If a
read-only binding is not expressible through RcppR6, an accessor returning the
current trait vector would do as well.
set_traits()existsbecause changing a trait means rebuilding two splines and clearing the solved
operating point and the temperature cache. This is only about being able to read
back what was set.