Skip to content

Commit 7741e5b

Browse files
authored
Per-plot residual regression v30: BA_t1 is the lever, not BGI (#44)
Reframed: instead of using BGI as model input (v27c/v29 null), test whether BGI predicts per-plot residuals. Ran 12.3.9 production posture (CSI_SCALE=0.7) emitting per-plot predictions, regressed residual on each candidate covariate. predictor R^2 p BGI 0.0006 0.82 log(BGI) 0.0014 0.73 SICOND 0.0015 0.72 ClimateSI_ft 0.0077 0.40 BA_t1 0.1877 1.4e-5 BA_t1 + I(BA_t1^2) 0.2390 4.6e-6 BGI is truly null. BA_t1 (initial stand basal area) is the strongest single predictor of per-plot residual. Fit: residual = 40.6 - 0.334 * BA_t1. Model overshoots low-density stands and undershoots high-density ones. Crossover at BA_t1 ~121 ft^2/ac. Applying the correction in-sample: Original BA bias +11.04% R^2 (BA_pred vs obs) 0.382 Corrected BA bias 0.00% R^2 0.533 This is the signature of a density-dependent miscalibration in either the BAL competition term of the Kuehne dDBH equation, or missing density-acceleration in the Glover/Hool mortality. Both are paper-sized refits. Pragmatic ship path: bridge-level post-projection correction. v31 will implement capped linear correction with 50/50 holdout validation. BGI was the wrong lens; the lever was always stand density.
1 parent 5fd55f3 commit 7741e5b

5 files changed

Lines changed: 487 additions & 0 deletions

File tree

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Per-plot residual analysis on 12.3.9 (CSI_SCALE = 0.7): density-dependent calibration lever
2+
3+
2026-05-28. The v27c, v29 input-based tests for BGI were null. v30 asks the
4+
orthogonal question via post-hoc residual regression: does any per-plot
5+
covariate predict the model's per-plot BA error well enough to support a
6+
fitted calibration factor?
7+
8+
## Result
9+
10+
100 ME FIA plots, 12.3.9 production posture (MORTCAL on, CutPoint = 0,
11+
CSI_SCALE = 0.7). 93 plots had complete covariate coverage. Univariate
12+
regressions on per-plot residual = BA_pred - BA_obs:
13+
14+
| predictor | R^2 | adj R^2 | p-value |
15+
|--------------------------|---------|---------|---------|
16+
| BGI | 0.0006 | -0.011 | 0.82 |
17+
| log(BGI) | 0.0014 | -0.010 | 0.73 |
18+
| SICOND | 0.0015 | -0.010 | 0.72 |
19+
| ClimateSI_ft | 0.0077 | -0.003 | 0.40 |
20+
| **BA_t1** | **0.1877** | **0.1788** | **1.4e-5** |
21+
| BA_t1 + I(BA_t1^2) | 0.2390 | 0.2220 | 4.6e-6 |
22+
| BA_t1 + ClimateSI_ft | 0.1918 | 0.1739 | 6.9e-5 |
23+
| BA_t1 + BGI | 0.1891 | 0.1710 | 8.0e-5 |
24+
25+
The clean finding: **BA_t1 (initial stand basal area) is the strongest single
26+
predictor of the model's per-plot residual.** It explains roughly 19 percent
27+
of residual variance with p = 1.4e-5. Adding a quadratic term lifts to 24
28+
percent. BGI, SICOND, ClimateSI_ft add essentially nothing beyond BA_t1.
29+
30+
## Fitted correction
31+
32+
residual = 40.6345 - 0.3344 * BA_t1 (R^2 = 0.188, n = 93)
33+
34+
The model overshoots low-density stands and undershoots high-density stands,
35+
with the crossover near BA_t1 = 121 ft^2/ac.
36+
37+
| starting BA (ft^2/ac) | predicted overshoot |
38+
|-----------------------|----------------------|
39+
| 50 | +23.9 |
40+
| 90 | +10.5 |
41+
| 120 | +0.5 |
42+
| 180 | -19.6 |
43+
44+
## Effect of applying the correction (in-sample)
45+
46+
| metric | original | corrected |
47+
|-----------------------|-----------|-----------|
48+
| BA_pred mean | 107.26 | 96.60 |
49+
| BA_obs mean | 96.60 | 96.60 |
50+
| BA bias % | +11.04 | 0.00 |
51+
| R^2 (BA_pred vs obs) | 0.382 | **0.533** |
52+
53+
BA bias closes to exactly zero (by construction) and per-plot R^2 lifts by
54+
0.15. The lever the entire BGI investigation was looking for is here, just
55+
not in BGI: it's in stand density.
56+
57+
## Interpretation
58+
59+
This is the signature of a density-dependent miscalibration in the dDBH or
60+
mortality equation. The Kuehne et al. 2020 diameter increment uses a BAL
61+
(basal area in larger trees) competition term. If the BAL coefficient is too
62+
weak, the model under-suppresses diameter growth in dense stands, which
63+
shows as overshoot in low-density stands (where BAL is small) and undershoot
64+
in high-density stands (where BAL is large) — exactly the pattern we see.
65+
66+
Alternative explanation: the Glover/Hool mortality is missing density-dependent
67+
acceleration in dense stands, so high-density stands keep too many trees and
68+
their BA undershoots because the model is removing the wrong ones.
69+
70+
Both stories are plausible; both are paper-sized refits. The post-hoc
71+
correction is the practical fix that ships now.
72+
73+
## Why BGI was null even though density isn't
74+
75+
BGI is fundamentally a remote-sensing measure of realized growth: a stand
76+
with high BGI grew faster than the model expected based on what was
77+
visible from the air. The model already sees stand density through BA_t1
78+
explicitly (and through BAL inside the dDBH equation). So BGI is mostly
79+
proxying what the model already knows about density. The residual it
80+
could explain is what is left after density is accounted for, and
81+
within-stand productivity variation conditional on density is small on
82+
this sample.
83+
84+
## What to ship
85+
86+
Three nested options for the customRun bridge in 12.3.9 (no model code
87+
changes; just a post-projection multiplier on BA_pred):
88+
89+
1. **Linear**: BA_corrected = BA_pred - (40.6 - 0.334 * BA_t1).
90+
In-sample bias 0 percent, R^2 0.533. Risk: only validated on n = 93,
91+
could over-fit.
92+
93+
2. **Capped linear**: same formula but clamp the correction to within
94+
+/- 25 ft^2/ac so it can't introduce a worse error than the original
95+
baseline residual sd of 39 ft^2/ac.
96+
97+
3. **Hold off and refit BAL coefficient**. Re-fit the Kuehne BAL term
98+
against ME FIA. This is the paper-sized version; closes the bias at
99+
the equation level instead of post-hoc.
100+
101+
The right autopilot move is option 2 with holdout validation. v31 will
102+
implement it as a bridge function `apply_density_correction(BA_pred, BA_t1)`
103+
and validate on a 50/50 holdout split of the v30 sample.
104+
105+
## Files
106+
107+
`cardinal_acadgy_residualcal_v30.R`, `acdgy_residualcal_v30_perplot.csv`
108+
(per-plot data), `acdgy_residualcal_v30_results.csv` (aggregate). Cardinal
109+
SLURM job 10968650.

0 commit comments

Comments
 (0)