Does the ePSF change with magnitude?
Residual analysis of the newest and best ePSF/WCS fits on F1 (S24 C2K2) and F2 (S20 C3K3), fold 0, and a plan for modelling what they show.
Current answer (2026-10-13, §12): yes. Excess PSF variance grows linearly with peak charge per read, $(1.33\pm0.06)\times10^{-3}$ px² per $10^4$ e⁻ (true $\Delta\Sigma$ units; earlier sections quote estimator units, ×0.59). It is a brighter–fatter-type effect, seen in all 5 fields and 9/9 detector cells. Paper 1 reports it; the linear term we tried captures about half and goes to Appendix D, so the adopted model has no term. The text below is the 10-07 analysis.
Yes. The fits use one ePSF for every star (bright_width: none). Their residuals show that the real PSF of stars brighter than T≈10 is broader than the model, with a central-pixel deficit and an excess in the first ring at r ≈ 1.2 px. F1 and F2 show the same pattern. The bright-minus-faint width difference is $\Delta c \approx +1.0\times10^{-3}$ px² (held-out stars: 0.65–0.95).
The width rises with peak charge per read. Expressed as the existing bright_width law, the implied coefficient is $b = 1.24\pm0.18$ (F1) and $0.50\pm0.09$ (F2), in units of $10^{-3}$ px² per $10^4$ e⁻. The 09-30 measurement, $b = 0.80$, lies between them. About $0.4\times10^{-3}$ px² of the earlier "faint stars are sharper" signal was light from unmodelled Gaia neighbours, not PSF.
Fits examined
All fits come from dev_runs/training_fixes_20261005, fold 0, under the 10-04 A3 recipe run to a smoothed-stop plateau. "Best" is the arm with the lowest paired held-out core χ² on both fields.
| Arm | What it adds | paired held-out χ² vs base (F1 / F2) | bright gap T8–9 (F1 / F2) |
|---|---|---|---|
| v6 base | 10-04 recipe from the training-only start | 1 / 1 | 5.85 / 22.8 |
| v8 nbr only | omitted Gaia neighbours T ≤ 17 rendered as nuisance stars | 0.85 / 0.77 | 5.5 / 15.2 |
| v8 best | neighbours + weight cap T10.5 (D) + anchors train the shape (#3) | 0.56 / 0.54 | 0.92 / 1.00 |
"Bright gap" is the held-out median χ² divided by the in-fold median χ² at T8–9. Values far above 1 mean the ePSF memorised its training stars.
What the recipe labels mean
Every star in the scene has a role. Contributors (T8–11, 1,655 stars in F1 fold 0) train the ePSF shape: the loss gradient flows into the ePSF pixels through their stamps. Anchors (T11–13, 9,822 stars) are fitted for flux and position and pin the WCS. By default, the gradient of their stamps with respect to the ePSF is stopped, so they do not shape it. Nuisance stars (blended or rejected stars, and the added neighbours) have their template held fixed and only their flux solved. The arms change these rules:
| Label | Flag | What it does |
|---|---|---|
| D, cap T10.5 | --star-weight-cap-tmag 10.5 | Caps each star's total weight in the loss at that of a T = 10.5 star (formula below). Without it, the few brightest stars dominate the ePSF and are memorised; the bright gap is 5.9 (F1) and 22.8 (F2). |
| #3, "T11–13 train shape" | --anchors-train-epsf | Option 3 of the training-fixes plan. Relabels every anchor as a contributor at scene load, so the 9,822 faint T11–13 stars also train the ePSF shape (about 7× more training stars). Their pixels then constrain the core and centroid, which the bright stars alone pinned poorly. |
| nbr17, "neighbours" | augmented scene nbr/F*_fold0_T17 | Adds the Gaia neighbours with T ≤ 17 that the PS1 removal ledger recorded but the scene had omitted (F1: 2,969). They enter as nuisance stars, with a weak prior toward their catalogue flux, so their light is no longer attributed to the target's PSF. |
$w_s$: weight factor of star $s$ (dimensionless). $f_s$: its flux, in e⁻/s. $f_{\rm cap}$: flux of a T = 10.5 star (15,000 e⁻/s × 10^{−0.2} ≈ 9,460 e⁻/s). $\sigma^{-2}(p)$: the pixel's inverse variance from the NOISE plane. A star's χ² contribution grows roughly as $f^2$, so this caps each bright star's total pull at that of a T10.5 star without changing the relative weighting of pixels inside the star. The paired held-out χ² gains in the arms table come from adding these three changes together: D alone 0.90 / 0.83, #3 alone 0.91 / 0.87, neighbours alone 0.85 / 0.77, all three 0.56 / 0.54 (F1 / F2).
Status. On 2026-10-07 the user finalised v8 nbr17_D105_E (neighbours + D + #3, on the 10-04 recipe with the smoothed stop rule) as the Paper 1 recipe (forward_model/recipes/paper1_final.yaml, main 677a6c5e). The fits on this page are therefore fold-0 fits of the adopted recipe. The bright-star residual they leave is the open modelling item.
Method
For each star, the evaluation writes the residual divided by the fitted flux on a 33×33 grid at 0.25 px (±4 px). I pool stars over the whole CCD, bin them by catalogue Tmag (0.5 mag bins) or by peak charge, and stack each bin with a 5-MAD clipped mean. I then fit the stack with the derivatives of the bin's median model image:
$$R(\mathbf r)\;\approx\;a\,P+b_x\,\partial_xP+b_y\,\partial_yP+c_{xx}\,\partial_x^2P+c_{yy}\,\partial_y^2P+c_{xy}\,\partial_x\partial_yP+d,\qquad r<3.5\ \text{px}$$$R$: stacked (data − model)/flux, in px⁻². $P$: median model/flux image of the bin, in px⁻². $a$: flux scale. $b_x, b_y$: centroid shifts, in px. $c_{xx}, c_{yy}$: width terms, in px², with negative meaning the data are sharper than the model. A Gaussian blur of variance $\Delta\Sigma$ per axis gives $c=\Delta\Sigma/2$. $d$: constant pedestal. All quantities are fitted by least squares. I report $c=(c_{xx}+c_{yy})/2$ in $10^{-3}$ px², with ±1σ from bootstrap resampling of stars. The primary sample is mid-colour stars (BP−RP 0.75–1.25), so the change of colour mix with magnitude cannot imitate a trend. Code: diagnostics/residual_grid.width_terms, called from magdep_v8.py.
Reading the zero point. Because the ePSF is shared by all stars, it sits at a weighted compromise between bright and faint stars. The absolute level of $c$ therefore says where the training weight went. Only the change of $c$ with brightness is evidence of a magnitude-dependent PSF.
1 · Width against magnitude
| Fit (mid colour, all stars) | T8–9.5 | T10.5–11.5 | T12–13 | Δ bright − faint | held-out Δ |
|---|---|---|---|---|---|
| F1 base | +0.20 | −0.77 | −1.40 | 1.6 | 1.9 |
| F1 nbr only | +0.20 | −0.79 | −1.04 | 1.25 | 1.65 |
| F1 best v8 | +0.76 | −0.10 | −0.34 | 1.1 | 0.95 |
| F2 base | +0.10 | −0.67 | −1.21 | 1.3 | 1.6 |
| F2 nbr only | +0.10 | −0.61 | −0.82 | 0.9 | 1.05 |
| F2 best v8 | +0.70 | −0.12 | −0.25 | 0.95 | 0.65 |
Units are $10^{-3}$ px². Statistical errors are about ±0.2–0.3 for the bright bins (26–120 stars per 0.5 mag) and ±0.06–0.1 for the faint bins (600–2,600 stars). The held-out Δ compares T8.5–10 with T12–13; the held-out sample has only 10–18 stars per bright bin.
2 · The images the widths are measured on
2b · Does the width fit describe the stacks?
The width numbers come from fitting each stack with seven images: the PSF, its two first derivatives (shifts), its three second derivatives (widths) and a constant. Below, each stack appears with its fit, the fit residual (R − fit), and the width part of the fit alone, all on the same colour scale. This shows how much of the stacked residual the width term actually describes.
| Bin (all stars) | F1 c | F1 power explained | F1 χ²/pix raw → after fit | F2 c | F2 power explained | F2 χ² raw → fit |
|---|---|---|---|---|---|---|
| T8–9 | +1.33 | 46% | 11.9 → 17.7 | +1.19 | 53% | 18.3 → 22.1 |
| T9–10 | +0.71 | 44% | 10.5 → 12.4 | +0.26 | 20% | 12.0 → 10.4 |
| T10–11 | −0.04 | 9% | 8.7 → 6.9 | −0.03 | 30% | 11.3 → 10.9 |
| T11–12 | −0.08 | 34% | 21.7 → 10.9 | −0.35 | 40% | 13.0 → 11.6 |
| T12–13 | −0.34 | 67% | 58.4 → 16.0 | −0.24 | 33% | 27.3 → 19.7 |
What this shows.
- Bright stars (T8–10): the sign and the ring are right, the core is not. The width part reproduces the broad shape: a deficit inside r ≈ 0.8 px and an excess at r ≈ 1–1.6 px. But the real central deficit is about twice as deep and much narrower: −14 to −17×10⁻⁴ in the stack against −6 to −8 in the fit. What is left over (green) is a narrow hole confined to the central pixel plus a ring at r ≈ 1 px, at 3–5σ. A blur of the PSF ($\nabla^2P$) is smooth on the scale of the PSF. Charge moving from the brightest pixel into its four neighbours, which is what the 09-30 study saw, is a pixel-scale feature. The stack prefers the second description.
- Faint stars (T11–13): the width term catches the main part, a central excess, so the data are slightly sharper. What is left is low-level non-radial structure: small dipoles and pixel-grid speckle. That is ePSF shape error at the 1–3×10⁻⁴ level that no width term can describe.
- T10–11 carries almost no signal, so the fit explains little there. That is expected where the curve crosses zero.
- Unweighted fit. χ² can rise after the fit (bright bins), because the fit minimises unweighted squared residuals while χ² weights pixels by their noise. The bright core pixels are both the noisiest and the worst fitted.
Consequences. $c$ is the projection of the residual onto a smooth blur. It measures the sign, the onset and the second-moment size reliably, but it underestimates the central-pixel loss of bright stars by about 2×. The bright_width term, which is exactly such a blur, should therefore remove the ring and about half of the core deficit, but not all of it. This adds an arm to the plan (§5): a pixel-boundary brighter-fatter operator that moves charge from a pixel to its four neighbours in proportion to its charge. 09-30 found that operator degenerate with the blur in moments; the stack images above are where the two differ, so the test should be judged on these images and on the central-pixel residual, not on $c$ alone.
2c · §2b for all five Paper-1 fields on the final run-3 fits, and the dipole terms (2026-10-09)
The user saw that the width fit describes F1 very well, while on F2 the R − fit residual shows dipoles in the fainter bins. They asked for the §2b figure for every Paper-1 field and for the shift terms. The §2b panels above are the 10-07 v8 fits on fold 0 only, with no brightness-width term (bright_width: none). They remain the reference. The figures below use the run-3 final fits (the Paper-1 recipe), with held-out stars from all K = 5 folds, which is about 5× more stars per bin. Each field is shown twice. LAW OFF is the frozen fit with no brightness term. LAW ON adds the run-3 held-out-chosen charge law $\Delta\Sigma=a_0+b\,q/10^4$, post hoc (§9). The method is the same as §2b: mid-colour stars (0.75 ≤ colour < 1.25), 1-mag bins, 5-MAD-clipped stack $R$ of (data − model)/flux, least-squares fit of the 7-term basis over r < 3.5 px.
$P$: the bin's median model/flux image. $a$: flux term. $b_x,b_y$: shift terms (px); the data − model centroid offset is $\delta=-b$, quoted in mpx (10⁻³ px). $c_{xx},c_{yy},c_{xy}$: width terms (px²); $c=(c_{xx}+c_{yy})/2$, negative means the data are sharper than the model. $k$: constant. Errors are 30 bootstrap resamples of stars. Axis frame: each stamp (residual and model) is rotated so that +x′ points from the star toward the optical axis (the fit's chroma_axis), then stacked. $\delta_{\rm rad}$ is positive toward the axis; $\delta_{\rm tan}$ is 90° counter-clockwise from it.
| field | Tmag | n | width $c$ [1e-3 px²] off / on | power explained off / on | χ²/pix → χ²/dof after fit, off / on | δx [mpx] | δy [mpx] |
|---|---|---|---|---|---|---|---|
| F1 | 8–9 | 71 | +1.26 / +0.73 | 39 / 26% | 17.1 → 23.1 / 16.7 → 16.7 | -1.5±0.9 | +0.1±0.8 |
| F1 | 9–10 | 196 | +0.90 / +0.76 | 49 / 43% | 9.6 → 9.9 / 9.3 → 9.4 | +0.1±0.5 | +0.4±0.4 |
| F1 | 10–11 | 502 | -0.09 / +0.08 | 19 / 12% | 8.2 → 6.1 / 7.3 → 6.1 | -0.4±0.3 | +0.1±0.2 |
| F1 | 11–12 | 1847 | -0.00 / +0.31 | 31 / 41% | 15.3 → 10.5 / 14.0 → 10.4 | -0.3±0.2 | +0.2±0.1 |
| F1 | 12–13 | 4189 | -0.34 / +0.03 | 53 / 28% | 28.8 → 16.3 / 21.4 → 15.9 | -0.4±0.1 | -0.0±0.1 |
| F2 | 8–9 | 59 | +1.01 / +0.23 | 49 / 34% | 19.6 → 24.2 / 21.6 → 21.1 | +0.3±0.6 | +2.6±1.0 |
| F2 | 9–10 | 135 | +0.24 / +0.09 | 18 / 20% | 15.2 → 9.2 / 15.9 → 9.3 | +0.3±0.3 | +0.3±0.5 |
| F2 | 10–11 | 401 | -0.04 / +0.09 | 31 / 27% | 10.4 → 9.4 / 9.9 → 9.3 | +0.4±0.3 | +0.5±0.3 |
| F2 | 11–12 | 1476 | -0.39 / -0.11 | 44 / 17% | 10.7 → 11.4 / 10.1 → 11.7 | +0.3±0.1 | -0.1±0.1 |
| F2 | 12–13 | 3402 | -0.30 / +0.06 | 28 / 12% | 18.9 → 20.5 / 21.6 → 20.3 | +0.2±0.1 | -0.2±0.1 |
| S22 | 8–9 | 56 | +1.37 / +0.27 | 49 / 15% | 18.9 → 17.6 / 21.2 → 15.2 | +0.1±0.7 | -0.3±0.9 |
| S22 | 9–10 | 164 | +0.70 / +0.41 | 45 / 35% | 15.8 → 13.4 / 17.2 → 13.0 | +0.5±0.4 | +0.6±0.5 |
| S22 | 10–11 | 456 | +0.09 / +0.30 | 22 / 25% | 16.2 → 7.8 / 14.4 → 7.5 | +0.2±0.2 | +0.0±0.2 |
| S22 | 11–12 | 1558 | -0.41 / +0.02 | 53 / 20% | 18.5 → 13.7 / 15.2 → 13.9 | +0.2±0.1 | -0.0±0.2 |
| S22 | 12–13 | 3304 | -0.39 / +0.14 | 34 / 17% | 17.8 → 21.9 / 25.9 → 21.9 | +0.2±0.1 | +0.0±0.1 |
| C1 | 8–9 | 66 | +2.34 / +0.87 | 64 / 32% | 12.0 → 15.6 / 12.2 → 12.2 | +1.6±1.6 | -0.9±0.8 |
| C1 | 9–10 | 189 | +0.65 / +0.25 | 27 / 20% | 14.3 → 14.6 / 15.3 → 12.7 | -1.0±0.7 | +0.8±0.7 |
| C1 | 10–11 | 456 | -0.29 / -0.09 | 42 / 28% | 10.5 → 6.3 / 9.1 → 6.6 | -0.3±0.6 | +0.6±0.3 |
| C1 | 11–12 | 1550 | -0.45 / -0.02 | 49 / 15% | 18.3 → 12.6 / 13.9 → 13.0 | -0.4±0.2 | -0.2±0.2 |
| C1 | 12–13 | 3389 | -0.20 / +0.34 | 39 / 42% | 23.6 → 21.6 / 30.0 → 22.0 | -0.6±0.1 | -0.5±0.2 |
| C4 | 8–9 | 56 | +1.16 / +0.49 | 51 / 42% | 20.2 → 13.9 / 22.3 → 18.0 | -1.2±0.5 | +2.4±1.0 |
| C4 | 9–10 | 179 | +0.32 / +0.29 | 45 / 43% | 13.1 → 9.3 / 13.0 → 9.2 | -0.4±0.4 | +1.1±0.4 |
| C4 | 10–11 | 482 | -0.35 / -0.07 | 62 / 38% | 31.7 → 7.6 / 26.8 → 7.3 | +0.3±0.3 | -0.1±0.3 |
| C4 | 11–12 | 2294 | -0.48 / -0.02 | 68 / 35% | 54.1 → 15.8 / 37.2 → 16.3 | +0.4±0.1 | -0.4±0.1 |
| C4 | 12–13 | 5617 | -0.28 / +0.25 | 42 / 31% | 47.7 → 37.3 / 46.0 → 38.0 | +0.4±0.1 | -0.5±0.1 |
Table: width $c$, fraction of stack power the 7-term fit explains, and χ² per pixel of the stack and per degree of freedom after the fit (noise from the stars' MAD), for LAW OFF / LAW ON. Shift terms (LAW OFF) are data − model centroid offsets; they are the same with the law on, because the law is symmetric.
Conclusion. (1) The brightness-width term does not create or remove dipoles; the shift terms are the same with LAW ON and LAW OFF. (2) Bright stars (T8–10) carry a small brightness-dependent centroid offset toward the optical axis: ≈ +0.8 mpx in the stacks, +1.5–2 mpx per stamp at T8–9, fading by T10.5. It changes smoothly with brightness and is consistent across fields at T9–10, but is only about 3σ pooled, and C1 at T8–9 disagrees at 2σ. It is the sign expected from the WCS↔ePSF-centroid compromise (faint stars dominate the astrometric solution) or from radially asymmetric charge spreading; these data cannot separate the two. Confidence that it exists: moderate. Size: ≤ 0.002 px, below current astrometric needs. (3) The faint-bin dipoles are not one smooth brightness effect. They are field-specific shape asymmetries of 0.4–0.9 mpx in equivalent shift: coma-like on C4 (core and wing on opposite sides), tangential on C1. Bootstrap errors put them at 6–7σ, so they are not noise. On C1 the per-stamp clean-sample (no neighbours) curve agrees, so it is not neighbour light. On F2 they were small-sample noise of the 10-07 fold-0 panels. Confidence: high that they are real and field-specific, low on cause; the most likely is an odd-order (coma-like) ePSF shape term that the fit's spatial/colour model does not carry on C4. Next step: a held-out test of a coma generator ($r^2\,\partial_rP$-type, radial to the axis) per field, if the user wants it.
3 · Controls
Colour
The median BP−RP of the mid-colour bin drifts from 1.15 (bright) to 0.90 (faint). To rule this out I repeated the measurement in narrow colour slices. Bright (T8–9.5) minus faint (T12.5–13), in $10^{-3}$ px²:
| Fit | BP−RP 0.6–0.85 | 0.85–1.05 | 1.05–1.35 |
|---|---|---|---|
| F1 best v8 | +1.2 | +0.9 | +1.1 |
| F1 nbr only | +1.2 | +1.0 | +0.9 |
| F2 nbr only | +0.75 | +0.5 | +0.5 |
| F1 v7 (no neighbours) | +1.9 | +1.8 | +1.6 |
The trend is present in every slice. Separately, red stars sit higher overall (+0.4 to +1.0 at all magnitudes), which is a colour residual of the A3 model and a different effect.
Neighbours
Rendering the omitted Gaia neighbours (T ≤ 17) as nuisance stars moves the T12–13 width from −1.48 to −1.02 on F1 and from −1.19 to −0.80 on F2, with no change at the bright end. Without neighbours, the light of fainter companions was attributed to the target star's PSF. That made faint stars look sharper relative to bright ones, which inflated the first-pass number (F1 v7: Δ = 2.0). That number is superseded.
Held-out stars
Held-out stars show the same sign and onset, so this is not memorisation. They cannot measure the slope against peak charge, because the held-out set has no stars above $q\approx1.2\times10^4$ e⁻.
4 · What it scales with: peak charge per read
The 09-30 study (bfwidth_20260929) found that the extra width is isotropic and linear in the charge collected in the brightest pixel during one 2-s read. The same quantity organises these residuals:
$q_s$: peak charge of star $s$, in e⁻ per read. $t_{\rm read}=2$ s, the TESS exposure that is co-added into every FFI. $f_s$: fitted flux, in e⁻/s. $\pi_s$: fraction of the star's flux in its brightest pixel, taken as the maximum of the pixel-integrated model/flux raster (about 0.2–0.3 here). T = 10 corresponds to $q\approx 1\times10^4$ e⁻, which is where the width starts to rise.
A linear fit of $c$ against $q$ (log-spaced bins at high $q$) measures the coefficient of the existing bright_width law, because $c = \tfrac12 b\,(q-q_{\rm ref})/10^4$:
| Implied $b$ [$10^{-3}$ px² per $10^4$ e⁻] | all stars | held-out |
|---|---|---|
| F1 best v8 | 1.24 ± 0.18 | 1.55 ± 0.56 |
| F2 best v8 | 0.50 ± 0.09 | 0.57 ± 0.70 |
| 09-30 independent measurement (7 datasets) | 0.80 ± 0.10 | |
These are post-hoc estimates from residuals of a fit without the term, so they are not fitted values. The F1–F2 difference (about 3.6σ) could be real, because the CCDs differ (F1 is cam2/CCD2, which earlier showed its own width band) or because exposure times differ. It could also come from the high-$q$ bins, which carry few stars.
5 · How to model it
The physics is already in the code. forward_model/bright_width.py (in the pinned training-fixes tree) adds a per-star isotropic blur proportional to peak charge on top of the shared ePSF and the A3 colour model:
$P_s$: rendered unit-flux template of star $s$. $P$: the shared spatially varying, colour-dependent ePSF at its position. $b$: one global coefficient, in px² of $\Delta\Sigma$ per $10^4$ e⁻ per read; a leaf of the fit, trained or fixed. $q_s$: static peak charge from the warm-start flux and the rendered peak, capped at $2\times10^5$ e⁻. $q_{\rm ref}$: loss-weighted mean $q$ of the trained stars, so that $P$ stays the PSF of a typical trained star. The term rides on the raw-gauge blur_raw generator (exactly calibrated: $\Delta\Sigma = 2w$, tested to 1%) and applies to every rendered star, including neighbours. CLI: --bright-width lin [--bright-width-init 0.8e-3] [--bright-width-train/--no-…].
Why it was not adopted before, and why that may now change. The 10-04 closure tested only the fixed historical $b=0.8\times10^{-3}$ on the old A3 recipe, and found +1.9% worse paired χ². On 09-30, a trained $b$ went negative (−0.7 to −1.1), because the faint anchors used the Laplacian to smooth pixel-scale ePSF structure. Three things are different in the v8 recipe. (1) Faint T11–13 stars now train the shape, so the Laplacian is no longer a free knob for anchors. (2) Neighbours are modelled, which removes a fake faint-sharper signal that pushed $b$ the wrong way. (3) The weight cap makes the shared ePSF a faint-star PSF, so the bright mismatch sits openly in the residual for $b$ to absorb.
Proposed test (one round, F1 + F2 fold 0, same scenes and held-out cores as v8; arm E needs code)
v8 best, as is
Neighbours + cap T10.5 + #3, bright_width none. Already done.
v8 + trained global b
--bright-width lin, init 0.8e-3, trained. Primary question: does $b$ settle positive, near 0.5–1.2, and agree between fields?
v8 + fixed b = 0.8e-3
The 09-30 value, not trained. Separates "the term helps" from "training finds it".
v8 + pixel-boundary BF operator
Charge moved from each pixel to its 4 neighbours, proportional to its charge. Tests whether the narrow central-pixel deficit left over by the blur (§2b) needs a pixel-scale term. Judge it on the stacked images, not on $c$.
v8 + term linear in Tmag
The same bright_width code with $q_s\propto T_{\rm ref}-T_s$ supplied as the q file (needs a small load-q-file option), trained. A direct held-out comparison with arm B (charge) on the same stars (§6).
trained b, cap lifted to T9
With brightness in the model, bright stars may be informative again rather than harmful. This tests whether $b$ lets us drop part of the weight cap.
Gates (each judged on held-out stars, fixed eval set, same scorer)
- Physics gate. The width-vs-$q$ curve of held-out and all stars (this page's §4 figure, re-made per arm) is flat within errors from T8 to T13, and the stacked T8–9.5 core deficit and ring are gone on the shared colour scale.
- No-regression gate. Paired held-out core χ² at T10.5–13 is ≤ 1.00 vs arm A on both fields. The term must not buy bright stars at the faint stars' expense.
- Bright gate. Paired held-out χ² at T8–10.5 improves vs arm A, and the bright memorisation gap does not grow.
- Consistency gate. Trained $b$ is positive and agrees between F1 and F2 within errors, or the difference repeats on a second fold. Otherwise move to a per-CCD $b$, which is the user's standing goal from 10-01.
- Flux gate. A PSF-vs-aperture flux trend against Tmag (r < 3 px) shrinks. 09-30 found bright PSF fluxes about 0.3% low at T8; the core-χ² score cannot see this.
If the linear law is not enough
- Turnover at high charge. Both fields' highest-$q$ bins (T≈8.3) fall below the line. If arm B leaves T<8.5 over-corrected, try a saturating form $b\,q/(1+q/q_{\rm sat})$ or the existing $q$ cap lowered toward $3\times10^4$. Do this only with more bright stars, for example by pooling folds.
- Per-CCD $b$. One leaf per CCD (F1 cam2/CCD2 against F2) if the consistency gate fails.
- Pixel-boundary operator is now arm E, because §2b shows a central-pixel deficit narrower than the blur can make.
- Not recommended: separate ePSFs per magnitude bin. That was tested on 09-25; on one FFI the bin differences are below their split-half noise.
6 · Can a term linear in magnitude model it?
The candidate is the same machinery as bright_width, with magnitude in place of charge:
$T_s$: catalogue TESS magnitude of star $s$. $\beta$: width change per magnitude, in px² of $\Delta\Sigma$ per mag. $T_{\rm ref}$: reference magnitude, which the ePSF absorbs. The other symbols are as in §5. Since $q_s\propto 10^{-0.4T_s}$, the two laws differ in shape: the magnitude law changes the width by the same amount per magnitude everywhere, while the charge law is nearly flat for faint stars and steep for bright ones ($q$ changes 100× from T13 to T8).
Test 1: which law fits the measured width curve?
| χ² / dof | linear in Tmag | linear in q | linear in flux | hinge | constant |
|---|---|---|---|---|---|
| F1 all stars | 5.6/8 | 11.0/8 | 11.0/8 | 4.6/7 (T 12.4) | 53/9 |
| F2 all stars | 20.2/8 | 9.8/8 | 9.7/8 | 7.1/7 (T 11.3) | 40/9 |
| F1 held-out | 2.3/7 | 5.8/7 | 6.4/7 | 2.6/6 | 27/8 |
| F2 held-out | 4.0/6 | 4.7/6 | 4.6/6 | 4.5/5 | 14/7 |
The binned curve does not decide. F1 prefers the magnitude law, because its faint end still slopes by about 0.12 per magnitude, which a straight line follows and the charge law cannot. F2 prefers the charge law, because its faint end is flat. Summed over both fields: magnitude 25.8/16 against charge 20.8/16 (all stars), and 6.3/13 against 10.5/13 (held-out).
Where a magnitude slope is learned matters. A magnitude term is constrained mostly by the 10,000 faint stars, because they dominate the count and have the same lever arm per magnitude as the bright ones. Fitted on T > 10.5 only and extrapolated, it predicts $c$ = −0.18 at T8.25 on F2, where +0.76 is observed. On F1 it predicts +0.42 against +0.80. The faint-end slope does not continue into the bright effect. It is a different thing, and the neighbour test already showed that the faint end is sensitive to contamination: modelling neighbours moved it by 0.4.
Test 2: at a fixed magnitude, does the width follow the pixel charge?
At fixed flux, the charge in the brightest pixel depends on where the star falls within its pixel. A magnitude law predicts no dependence on sub-pixel phase; a charge law, or a pixel-boundary operator, does. Below, stars in each magnitude range are split into terciles by their distance from the pixel centre (mid colour, matched median colour).
Brightest bin (T8–9.5): stars near a pixel centre are broader. On F1 the change is 0.13 → 0.88 (3.3σ), much more than the charge law's +0.13. On F2 it is 0.23 → 0.60 (about 2σ), close to the charge law. The next bin (T9.5–10.5) shows no clean trend: F1 has an outlying middle tercile and F2 is flat. Faint stars show a small decrease toward the pixel centre on both fields (−0.12 and −0.15, 3–5σ), a phase-dependent ePSF error unrelated to brightness. This is suggestive evidence for a pixel-charge effect, which a magnitude law cannot produce, but it is not decisive on these star counts.
Answer
- Over T8–13 the data cannot rule a magnitude-linear term out. On F1 it fits the binned curve as well as anything. It would remove most of the bright-star residual in these fields.
- It is the wrong variable, for four reasons. (1) Its slope is set by faint stars, whose small width trend is not the bright effect: it does not extrapolate, and it moved with neighbour modelling. That is the same failure mode as the 09-30 trained $b$, which faint anchors drove negative. (2) It extrapolates badly at both ends. At F1's slope it makes T19 stars 1.4e-3 px² sharper than T13 stars, which matters for the faint-target photometry. It also under-predicts brighter than T8, where charge keeps growing by 2.5× per magnitude. (3) It cannot carry the sub-pixel-phase dependence or the central-pixel shape (§2b), which both point to charge in pixels. (4) Charge per 2-s read is portable between sectors, cadences and fields; a magnitude slope is tied to the zero point and to each field's crowding.
- Recommended: model the bright effect with charge (the existing
bright_widthterm), plus the pixel-boundary operator (arm E) if the central-pixel and phase residual remain. Treat the F1 faint-end slope as a separate item to explain, for example remaining contamination or background level, and not as PSF. - Cheap to settle by experiment: the
bright_widthterm already takes its per-star $q$ frombright_width_q.npz.scene_fitreads that file only when resuming, so it needs a small option (about 10 lines) to load a supplied file. Writing $q_s\propto T_{\rm ref}-T_s$ into it then gives the magnitude law as arm F. Arms B and F then compete on the same held-out stars, so the comparison does not rest on residual stacks.
7 · Plan: per-stamp tests on the trained Paper-1 fits (awaiting approval)
This uses the five finalised Paper-1 fits (F1, F2, S22, C1, C4) with K = 5 folds, so every star is scored by a fit that never trained on it. That gives 8,200–13,500 out-of-fold stars per field, 270–380 of them at T8–9.5. Nothing is retrained. Each star is fitted on its own stamp, its real pixels with their noise, rather than on a stack. Trends then come from a regression over stars. Full plan: dev/forward_epsf_wcs/docs/BRIGHT_WIDTH_PERSTAMP_PLAN_20261008.md.
Per-stamp model
$$d_p = m_p + f_s\big[\alpha_s T_{s,p} + \delta x_s\,\partial_xT_{s,p} + \delta y_s\,\partial_yT_{s,p} + c^{xx}_s\,\partial_x^2T_{s,p} + c^{yy}_s\,\partial_y^2T_{s,p} + c^{xy}_s\,\partial_x\partial_yT_{s,p} + \kappa_s K_{s,p}\big] + \beta_s + \epsilon_p$$$d_p$: data in stamp pixel $p$, in e⁻/s. $m_p$: frozen scene model, including neighbours, with fluxes re-solved. $T_{s,p}$: the star's rendered pixel-integrated unit template, in px⁻². $\partial$: its derivatives with respect to the star's position, rendered by the real renderer as finite differences ($\pm$0.05 px). $f_s$: flux, in e⁻/s. $\alpha_s$: flux correction. $\delta x_s, \delta y_s$: shifts, in px. $c_s$: width terms, in px². $K_{s,p}$: pixel-boundary charge-transfer image, with dimensionless amplitude $\kappa_s$. $\beta_s$: local background. $\epsilon_p$: noise with variance $\sigma_p^2$ from the NOISE plane. The fit is weighted linear least squares in $r<3$ px. Every fit is repeated with fixed normalised weights, because noise weights that depend on S/N can fake brightness trends.
$$\bar c_s=\tfrac12(c^{xx}_s+c^{yy}_s)=c_0+\tfrac12\,b\,\frac{q_s-q_{\rm ref}}{10^4}+\gamma^\top\mathbf z_s+\eta_s$$$q_s$: peak charge per read, which includes the sub-pixel phase. $b$: brightness-width coefficient. $\mathbf z_s$: nuisance covariates (colour, position), with coefficients $\gamma$. $\eta_s$: per-star scatter, with variance = the formal error plus a fitted intrinsic $\tau^2$.
Tests
- Null and injection (gate). Simulate each stamp from the frozen model plus noise, where the ePSF is exact and has no brightness effect. The fitted slope against $q$ and against $T$ must be 0 within 2σ, and any residual slope is subtracted as the method's bias. Inject $b$ = 0.8e-3 through the real
bright_widthrender and recover it to within 10%. - Law selection. Regress $\bar c_s$ on charge, magnitude and a threshold, plus the joint fit $c_0+\tfrac12 b\,q_s/10^4+\beta(T_s-11)$. At fixed magnitude, the sub-pixel phase discriminates between the laws.
- Spatial structure. Per-CCD $b$ (likelihood-ratio test, 4 dof), a linear gradient across each CCD (2 dof), per-axis $b^{xx}$ and $b^{yy}$, and descriptive 3×3 maps.
- Pixel-scale operator. Regress $\kappa_s$ on $q_s$. Check whether it removes the bright central-pixel deficit that the blur reproduces only half of (§2b).
- Decisive test. Render the frozen fits plus each candidate law with the real renderer, re-solve fluxes, and score with the existing held-out core-χ² scorer. Parameters are chosen on four folds' held-out stars and scored on the fifth. Gates: (G1) T8–10.5 improves on ≥4 of 5 fields; (G2) T10.5–13 does not get worse; (G3) the width trend against $q$ and against phase goes flat; (G4) the PSF−aperture flux trend shrinks. The charge and magnitude laws need only a wrapper around the existing
bright_widthpath; the pixel operator is a post-render step. - Retrain one field with the adopted law, only if test 5 passes. This needs a separate approval.
Should it be spatially variable? Power check on the OOF stacks
| Field | implied $b$ [1e-3] | bright $c_{xx}$ / $c_{yy}$ [1e-3 px²] | OOF stars T<10 per 1/9 of the CCD |
|---|---|---|---|
| F1 (S24 C2K2) | 1.11 ± 0.19 | 1.35 / 1.35 | 51–90 |
| F2 (S20 C3K3) | 0.58 ± 0.11 | 0.78 / 1.09 | 42–65 |
| S22 (S22 C3K3) | 1.53 ± 0.20 | 1.37 / 1.31 | 51–71 |
| C1 (S24 C1K2) | 0.82 ± 0.16 | 1.13 / 1.39 | 49–70 |
| C4 (S23 C4K2) | 0.86 ± 0.10 | 1.10 / 0.66 | 46–83 |
The coefficient may differ between CCDs: $b$ ranges over 0.58–1.53, which is χ² ≈ 20/4 against a single value with stack-bootstrap errors, still significant if those errors are 1.5× too small. Note that F2 and S22 are the same camera/CCD (C3K3) in different sectors, at 0.58 and 1.53. If that difference holds on the per-stamp fits, it argues against a pure CCD property and toward something that changes with the sector, such as focus or background, and per-sector rather than per-CCD fitting. Within a CCD there are only about 40–90 bright stars per ninth, so a per-cell $b$ has σ ≈ 50% of the signal. Only a linear gradient is testable, and part of any spatial variation is already carried by $q$, because a sharper local PSF puts more charge in its brightest pixel. No anisotropy is seen at ±0.3–0.45. Recommendation: test per-CCD (per-sector) $b$ first, then a gradient, and keep it isotropic unless the per-stamp fits show $b^{xx}\ne b^{yy}$.
8 · Results of the per-stamp tests (Paper 1 W3d, 2026-10-08)
The §7 plan was run on the five finalised Paper-1 fits. All 49,541 stars are out-of-fold (K = 5), and nothing was retrained. These were measured on the reference fits; the rerun on the end-to-end run-3 fits (§9, 10-09) reproduces every number within its error. The full record is the "3d brightness law" section of dev_runs/epsf_dependence_20261008/README.md.
$\Delta\Sigma_s$: extra blur covariance per axis of star $s$, in px². $a_0$: width offset applied to every star; it absorbs the frozen ePSF's zero point. $b$: brightness coefficient, in px² per $10^4$ e⁻ per 2-s read. $q_s$: peak charge per read.
| Question | Answer |
|---|---|
| Checks | Noise null $b$ = 0.00; cross-fold null 0.10 ± 0.02 (subtracted); injected 0.80 recovered as 0.74–0.78 |
| Charge or magnitude? | Charge. ΔAIC −278 against −79 for magnitude. On held-out stars at T8–10.5, χ² improves by 0.5–1.3% on all 5 fields with charge; magnitude gives 0–0.5%; a width offset alone makes bright stars worse |
| Size | $b$ = 1.42 / 0.68 / 1.01 / 0.94 / 0.82 ×10⁻³ (F1 / F2 / S22 / C1 / C4), pooled 0.98. The fields differ (p ≈ 0.007). F2 and S22 are the same CCD in different sectors, so $b$ should be fitted per FFI |
| Crowding? | No. The clean sample gives the same $b$, and $b$ is flat across contamination bins |
| Spatial? | No gradient within a CCD and no x/y anisotropy. Bright stars elongate toward the optical axis (6σ, stronger off-axis), but modelling that gives no held-out gain |
| Pixel operator? | Its amplitude rises with charge and it absorbs 60% of the blur slope. It has not yet been scored on held-out stars |
Conclusion. The bright-star broadening is a peak-charge effect of about $10^{-3}$ px² per $10^4$ e⁻ per read, varying by field. It is not a magnitude law, not crowding, and has no position dependence within a CCD. With the ePSF frozen, the existing bright_width blur plus a width offset removes most of the visible bright-star core residual but buys only about 1% in held-out χ². The next step is a retrain with the term (Test 5).
8b · Faint-end check: the width offset over-sharpens T12.5–13
The user noticed that the law seemed to make T11–13 worse. That is correct for the faintest stars. The law chosen on held-out stars applies a global width offset of $a_0\approx-1.2\times10^{-3}$ px². That offset fits T11–12, but it makes the model too sharp at T12.5–13.
Conclusion. The faint-end mismatch is a core/wing shape change, not a constant width, so no single width offset fits both T11–12 and T12.5–13. It is not crowding (the clean sample is the same), not a background offset (adding a local background changes nothing), and not the estimator (the nulls are flat). Keep the charge term for the bright stars. In the retrain, let the ePSF set its own zero point and do not add a global offset. Treat the faint-end shape change as a separate, still-unexplained effect.
9 · Run-3 rerun and the charge-transport alternative (2026-10-09)
Everything in §8–8b was rerun unchanged on the final Paper-1 fits (run 3: K = 5 on the final per-band image, evaluated on its neighbour scene, run 3's own code). The per-stamp tables are the W3 lead's run-3 tables, made with the same perstamp.py. Law scoring, stacks and the faint check are new. Separately, the Codex session tested a nearest-neighbour charge-transport model (dev_runs/bf_transport_20261008/current) on the reference F1/F2 fits. I reviewed that code and replayed it, unchanged except for the paths, on all five run-3 fields.
$T_{p\to r}$: electrons moved from pixel $p$ to its neighbour $r$ per 2-s read. $Q_p$: predicted charge in pixel $p$ (scene model × 2 s). $Q_0=10^4$ e⁻ (fixed). $\kappa_d$: fitted dimensionless coefficient, one value for both axes ("isotropic") or separate x and y values ("x/y"). The control arm "peak-charge blur" is the model's blur generator scaled by $q_s/10^4$ with no offset, i.e. the §8 law with $a_0=0$. Both arms make the relative change scale as $Q$, so they have the same brightness law and differ only in spatial shape: smooth blur against a pixel-boundary flow. The transport model therefore tests the shape, not the law.
| Run 3, T8–10.5 | F1 | F2 | S22 | C1 | C4 |
|---|---|---|---|---|---|
| blur, χ² reduction | 3.4% | 1.5% | 7.7% | 4.0% | 2.6% |
| x/y transport | 2.6% | 1.8% | 6.9% | 1.9% | 1.8% |
| paired χ²(x/y transport)/χ²(blur), 95% | 1.008 [1.001, 1.015] | 0.997 [0.991, 1.004] | 1.009 [0.997, 1.021] | 1.022 [1.011, 1.044] | 1.009 [1.001, 1.019] |
| W3d charge law, held-out χ² ratio (all held stars) | 0.9945 | 0.9931 | 0.9881 | 0.9866 | 0.9941 |
Review of the Codex record. The record is sound. It uses the right reference; its per-star χ² and fluxes replay mine to 5×10⁻¹⁶; and its tile split is genuinely held out. It conserves charge, recovers injections (1.0/0.6 → 1.00/0.60), and its noise null is zero. I found no bug, and the run-3 replay reproduces its F1/F2 numbers to 0.1%. Four points to add. (i) Transport and the blur arm share the brightness law (both ∝ Q), so "transport works" supports the charge law but says nothing new about its scaling. (ii) The paired comparison favours the one-parameter smooth blur over two-parameter x/y transport on F1, C1 and C4, and ties on F2 and S22. The extra x/y freedom does worse than isotropic on C1 and C4. (iii) About 11% of a charge-independent width defect leaks into the transport coefficients (its own leakage gate). (iv) The intervals hold coefficients fixed, so they are slightly optimistic. Its χ² gains are 2–3× mine because it scores only clean stars within r < 3 px, where the effect lives. That is the same signal, not a contradiction.
Conclusion (run 3). The peak-charge law holds on the final Paper-1 fits: $b$ = 1.01 ± 0.06 ×10⁻³ px² per 10⁴ e⁻ per read pooled (0.8–1.4 by field, differing between fields at p ≈ 0.01), with every gate passed. It beats a magnitude law on 5/5 fields. On the frozen fits it removes most of the T8–9 core deficit and gains 0.6–1.3% held-out χ² on all held stars, or 1.5–7.7% on clean stars within r < 3 px. A pixel-boundary transport law of the same brightness scaling fits no better than the smooth blur, and worse on 3/5 fields, so keep the smooth bright_width generator. Asymmetric bright-star residuals and the faint-end core/wing change remain for both. Confidence: high for the charge law and its size, high that transport is not preferred. Adoption, and whether to add an offset, depends on a retrain (Test 5), which needs the user's approval.
10 · Retraining with the brightness term on the new colour model (Arm A, 2026-10-10)
Post hoc, the charge law needed a global width offset, and that offset over-sharpened T12–13 (§8b, §9). Two cheap checks came first. Test 1: the faint-star sharpness survives the new colour model (s1c: radial rings and coma, A3 round terms dropped), in mid-colour stars too, so it is not a colour effect. Step 3: it shows no trend with CCD row and none inside the readout blocks, and it is flat over peak charge 300–5000 e⁻, so it is neither charge-transfer trailing nor a low-signal effect. Arm A then retrained s1c with the term trained and its zero point at zero charge ($q_{\rm ref}=0$), so the ePSF is the zero-charge PSF and no offset exists. It used F1/F2/C4, folds 0–1, the same scenes, initialisation and schedule as the s1c fits, and was scored on the same held-out stars.
$$P_s = P + \tfrac12\,b\,\frac{q_s}{10^4}\,\nabla^2 P$$$P$: the ePSF after the colour model (zero-charge PSF). $b$: brightness coefficient, extra blur covariance per axis (px²) per $10^4$ e⁻ per 2-s read, trained. $q_s$: the star's peak charge per read. For held-out stars it is computed on the evaluation scene from a solve without the term; see the bug note below.
Bug found and fixed before the verdict. In the first evaluation the held-out stars had $q = 0$. Their pixels are withheld from the fold scene, so their warm-start flux, and hence their q, is zero, and the scorer took q from the fit's file. The bright stars were therefore scored without the term. The scorer now computes q for those stars on the evaluation scene (branch bwidth-rf5-20261009 f6364402). The buggy evaluations are kept for reference.
Conclusion. Trained together with the ePSF, the peak-charge term removes most of the bright-star width excess (fully on F1) and does not over-sharpen T12–13. The faint end improves (width mismatch −10…−40%, held-out χ² −0.2%). Held-out χ² at T8–10.5 does not improve overall (+0.02%), and about two thirds of the faint-star sharpness remains, so that is a separate effect, not the ePSF compromise. The trained $b$ differs by field, 1.43 / 0.64 / 0.57 ×10⁻³ (F1 / F2 / C4, stable between folds), so the term needs a per-FFI coefficient. Adoption is the user's decision, together with the colour shape.
11 · Brightness-term campaign: all five fields, forms, chain, injection, fixed vs trained b (2026-10-12)
Preliminary result (10-12, 13:xx EDT). Trained with the ePSF on the run-4 final image (5 fields × 5 folds, held-out stars), the linear peak-charge term lowers the bright-star χ²: the bin-averaged T8–10.5 ratio is 0.988 ± 0.002 and T8–9.5 is 0.989 ± 0.002. It also improves the faint stars slightly (T10.5–13: −0.15%). Trained through the whole run-4 chain (boot → kernels → final image → fold fits) on F1 and C4, the gain is larger: bright 0.987 ± 0.004, faint −0.36%, and no cost at T9.5–10.5. The pre-registered gate, the pooled T8–10.5 median ≤ 0.994, still fails (0.9990 ± 0.0004), because the gain sits in the few hundred T8–9.5 stars while the median is set by the many T9.5–10.5 stars. b is recovered without bias in injection tests on F1 and C4. It varies by field (0.4–1.4 ×10⁻³) for reasons not yet found, and it is not reproducible fold to fold in S22.
$P$: the ePSF after the colour model, i.e. the zero-charge PSF (fitted). $q_s$: peak charge of star $s$ per 2-s read (e⁻) $=2\,{\rm s}\times f_s\times$ peak-pixel fraction of its unit-flux template, where $f_s$ is the warm-start solved flux (e⁻/s). It is fixed during training and capped at $2\times10^5$ e⁻. For held-out stars it is computed on the evaluation scene from a solve without the term. $q_{\rm ref}=0$ (fixed). $\Delta\Sigma$: extra Gaussian covariance per axis (px²). Forms, with $b,d,c$ fitted and $q_0$ fixed: lin $\Delta\Sigma_{xx}=\Delta\Sigma_{yy}=b\,u_s$; aniso $\Delta\Sigma_{xx}=(b+d)\,u_s$, $\Delta\Sigma_{yy}=(b-d)\,u_s$; quad $\Delta\Sigma=b\,u_s+c\,u_s^2$; onset $\Delta\Sigma=b\,\max(q_s-q_0,0)/10^4$ with $q_0=3\times10^3$ (the knee of the run-3 per-star curve, §4) or $10^4$ e⁻ (≈ T10). Units: $b,d$ in px² per $10^4$ e⁻; $c$ in px² per $(10^4\ e^-)^2$. Metric: $r_s=\chi^2_{{\rm arm},s}/\chi^2_{{\rm ctrl},s}$, the held-out core χ² of the same star under the arm and under the control, summarised as medians with bootstrap errors.
Setup. Every arm uses run 4's own fold argv: same fold scene, warm start, colour file, step budget and smoothed stop rule. Only the term flags are added. The controls are run 4's folds_final fits, re-scored with this branch; re-scoring agrees with run 4's own scores to within 1.6×10⁻⁴ in χ². Run 4's C4 folds were trained on a T4, so the CPU arms use matched CPU control fits for C4, while the chain, also trained on T4, uses run 4's C4 folds. Branch bwidth-campaign-20261012 @65c2e367 (not merged).
Held-out χ² ratios (median per star, ± bootstrap)
| arm (folds) | scope | bin-avg T8–10.5 | T8–9.5 | gate: pooled T8–10.5 (≤0.994) | T9.5–10.5 | T10.5–13 |
|---|---|---|---|---|---|---|
| lin (5) | F1 | 0.994 ± 0.006 | 0.992 ± 0.006 | 1.0009 | 1.0037 | 0.9962 |
| lin (5) | F2 | 0.987 ± 0.005 | 0.985 ± 0.004 | 0.9991 | 1.0013 | 0.9988 |
| lin (5) | S22 | 0.995 ± 0.003 | 0.993 ± 0.002 | 0.9981 | 0.9995 | 0.9991 |
| lin (5) | C1 | 0.985 ± 0.006 | 0.989 ± 0.004 | 0.9992 | 1.0016 | 0.9993 |
| lin (5) | C4 | 0.981 ± 0.004 | 0.985 ± 0.004 | 0.9979 | 1.0006 | 0.9983 |
| lin (25) | pooled | 0.988 ± 0.002 | 0.989 ± 0.002 | 0.9990 ± 0.0004 | 1.0010 ± 0.0006 | 0.9985 ± 0.0001 |
| chain (5) | F1 | 0.992 ± 0.006 | 0.991 ± 0.006 | 0.9997 | 1.0019 | 0.9959 |
| chain (5) | C4 | 0.980 ± 0.004 | 0.983 ± 0.006 | 0.9968 | 0.9985 | 0.9966 |
| chain (10) | pooled | 0.987 ± 0.004 | 0.988 ± 0.004 | 0.9979 ± 0.0011 | 1.0000 ± 0.0012 | 0.9964 ± 0.0002 |
| hyb_term: control ePSF + lin b (25) | pooled | 0.984 ± 0.003 | 0.986 ± 0.003 | 0.9991 | 1.0021 ± 0.0007 | 1.0003 |
| hyb_shape: lin ePSF, term off (25) | per field | 1.003–1.006 | 1.004–1.009 | 1.000–1.005 | 0.998–1.001 | 0.996–0.999 |
paper1_final_step1_bw: boot fit → template shift → kernels → final image → scene_final → folds_final). F1 and C4, 5 folds each, against run 4 on the same held-out stars. Bright $c$: F1 +1.3 → +0.3, C4 +1.5 → +0.8 (T8–8.5). χ²: T8–8.5 −3% (F1) and −8.5% (C4). The ratio is ≤ 1 in every bin except T10–10.5 (1.00). Both final images with the term pass G4.Measured b: what it is and whether it trends
The trained $b$ is one scalar per ePSF fit, i.e. per field per fold. It applies to every star on the CCD and to every ePSF node: there is no per-quadrant or per-node $b$ in the model. The only position-resolved measurement is the model-independent slope $\hat b$ of the control fits' held-out stack width against $u=q/10^4$, per CCD quadrant.
Fixed global b vs trained b (25 folds, same stars)
Arm fixed065 uses the same recipe with $b$ frozen at $0.65\times10^{-3}$ (not trained). The χ² ratio is vs the control, median per star. "fixed/trained" is the per-star ratio of the two term arms.
| field | bin-avg T8–10.5 trained → fixed | T8–9.5 trained → fixed | fixed / trained T8–10.5 | fixed / trained T10.5–13 |
|---|---|---|---|---|
| F1 | 0.994 → 0.991 | 0.992 → 0.991 | 1.0002 ± 0.0011 | 1.0015 ± 0.0002 |
| F2 | 0.987 → 0.987 | 0.985 → 0.983 | 1.0007 ± 0.0004 | 0.9998 ± 0.0001 |
| S22 | 0.995 → 0.984 | 0.993 → 0.985 | 0.9988 ± 0.0008 | 0.9994 ± 0.0002 |
| C1 | 0.985 → 0.986 | 0.989 → 0.990 | 1.0001 ± 0.0007 | 1.0003 ± 0.0001 |
| C4 | 0.981 → 0.982 | 0.985 → 0.986 | 1.0000 ± 0.0002 | 0.9999 ± 0.0000 |
| pooled | 0.988 → 0.986 | 0.989 → 0.988 | 1.0000 ± 0.0002 | 1.0001 ± 0.0000 |
Overall, fixed and trained $b$ are the same fit. Trained $b$ is better only for F1's faint stars (+0.15% with fixed $b$). Fixed $b$ is better on S22, where training collapsed. Transfer: S22 with F2's trained $b$ (0.57, same camera 3 CCD 3) equals S22's own trained $b$ (per star 0.9995 ± 0.0007). F2 at its own measured slope (0.41) equals both. Held-out χ² is flat in $b$ over about 0.4–1.4 ×10⁻³: the data cannot pin $b$ per field. Production consistency: the all-star refit, i.e. the production fit through the full chain, gives F1 1.45 and C4 0.64, against fold means 1.39 ± 0.09 and 0.62 ± 0.08. The boot fits give 1.41 and 0.62.
Decision (user, 2026-10-12 evening). Keep the current model: the linear round peak-charge term, $\Delta\Sigma=b\,q/10^4$ with $q_{\rm ref}=0$, $b$ trained per fit with the ePSF, and the term carried through the whole chain. Production runs with this recipe (paper1_final_step1_bw) on all five fields: F1 and C4 complete; S22, F2 and C1 running. $b$ is reported as a fitted parameter, with its fold spread next to the model-free slope, not as a detector constant. Whether S22 needs a safeguard (weak prior or fallback to fixed $b$) will be decided from its production run and star diagnosis. The pre-registered pooled-median gate fails (0.9990) and stays reported.
Production run with the adopted model (status 2026-10-12 23:20)
Recipe paper1_final_step1_bw through the whole chain: boot fit → template shift → kernels → final image → all-star refit (the production fit) + 5 folds. Fits ran on Colab T4/L4 and Condor; every fit is installed with its provenance, and no session is left running.
| field | status | boot $b$ | production (refit) $b$ | folds $b$ (mean ± sample sd) | G4 (final image with term) |
|---|---|---|---|---|---|
| S24 C2K2 (F1) | complete | 1.41 | 1.45 | 1.39 ± 0.09 | PASS |
| S23 C4K2 (C4) | complete | 0.62 | 0.64 | 0.62 ± 0.09 | PASS |
| S20 C3K3 (F2) | complete | 0.50 | 0.58 | 0.57 ± 0.08 | PASS |
| S22 C3K3 (S22) | final image done; refit + folds running | 0.36 | pending | pending | PASS |
| S24 C1K2 (C1) | final image done; refit + folds running | 0.95 | pending | pending | PASS |
Units ×10⁻³ px² per 10⁴ e⁻. Where complete, the production $b$ agrees with the fold values within the fold spread. S22's boot value (0.36) lies at the low end of its lin-arm fold range (0.10–1.15) and below the transfer/fixed values (0.57–0.65). Whether S22 gets the fixed-$b$ safeguard will be decided from its production refit and folds. GPU note: L4 sessions were unreliable tonight (C1 boot failed 3×; one S22 run trained but its session vanished before the final sync). Both were restarted on T4, which completed.
S22: why trained $b$ is not reproducible
Correction: the model-free slope depends on the estimator
Two binnings of the same control curves give different straight-line slopes: 4 charge bins (test 3, used in the b-vs-field and quadrant figures and for the fixed value 0.65): F1 0.70, F2 0.41, S22 0.66, C1 0.65, C4 0.65; 8 finer bins (slope figure, Fig 14 draft): 0.51, 0.41, 0.52, 0.57, 0.36. They differ by up to 0.3 (C4) because the curves are not exactly straight (C4 χ² 25/6), so the model-free slope carries a systematic uncertainty of about ±0.15–0.3 ×10⁻³. The finer-bin values are now the quoted ones. The conclusions do not change: the slope's spread across fields (0.36–0.70) is smaller than trained $b$'s (0.42–1.39), F1's trained $b$ is still 2–3× its slope, and χ² is flat in $b$ over 0.4–1.4. Fold spreads are now quoted as sample sd (F1 ±0.09, F2 ±0.08).
Where the leftover bullseye comes from (diagnostics, 10-12 night)
With the term, the held-out stacks still show a bullseye: a blue core and red ring for bright stars (data less peaked than the model), and a red core with a blue ring for faint stars (data sharper). Four checks:
| check | result | verdict |
|---|---|---|
| Trained vs held-out stars (5 fields × 5 folds) | Same bullseye: control T8–9 core −8.2 vs −8.4 ×10⁻⁴; T12–13 +1.56 vs +1.65; with term −3.9 vs −3.6 | Not memorisation; the fit has converged to what the model can represent |
| 3×3 CCD cells | Bright core negative in all 9 cells, faint core positive in all 9 | Not a stacking artefact |
| Sub-pixel position | Bright core: centred −7.8, between −3.8, corner −2.3; faint: +3.1 / +0.5 / +1.6 (with term) | 2–6× stronger for pixel-centred stars: a pixel-scale effect (intra-pixel response or ePSF resolution) |
| ePSF node phase (figure below) | Faint core falls ≈6× from node to midway (T12–13 2.0 → 0.35, edges excluded); bright core more negative midway | The faint bullseye is mostly a node-interpolation artefact |
| Smoothing-prior strength (figure below) | A strong prior inflates the faint bullseye (×4 core, ×8 ring), but the production fits sit below even the prior-free level of that scan | Not the main cause in production |
| Smooth blur vs pixel charge operator (figure below) | Shapes collinear (cos 0.93–0.97); the charge operator is not preferred and behaves identically with and without the term | The term's smooth shape is not the limitation |
Paper figure draft (Fig 14 v2)
dev/paper/paper1_single_ffi/figures/final_20261009/fig14_brightness/fig14_brightness_v2.py; text draft drafts/brightness_term_20261012.md). (a) Extra width against peak charge without (grey) and with (red) the term, five fields, with the straight-line model-free slope. (b) Held-out χ² ratio per magnitude bin. (c) T8–9 and T12–13 residual stacks without and with the term. (d) Fitted $b$ per field (folds, production value marked) next to the model-free slope. It currently uses the term trained on the final image for all five fields, and will switch to the production-chain fits once S22 and C1 finish.Injection–recovery (test 4)
Synthetic scene $=$ data $+\,M(b_{\rm true}) - M(b_{\rm fit})$, where $M$ is the lin fit's own model with fluxes held at its solved values. The real residual, data $- M(b_{\rm fit})$, keeps real noise, background and neighbours, and also the model's own errors. Each scene is refitted with the training pipeline from $b_{\rm init}=1.0\times10^{-3}$.
| base fit | b_fit | b_true = 0 | 0.5 | 1.2 | 1.5 (×10⁻³) |
|---|---|---|---|---|---|
| F1 fold 0 (Arm A, run3_rf5) | 1.49 | −0.012 | 0.498 | — | 1.495 |
| C4 fold 0 (Arm A, run3_rf5) | 0.60 | −0.002 | 0.507 | — | 1.501 |
| S22 fold 1 (run4 lin) | 0.29 | −0.012 | — | 1.137 (still falling) | — |
b is recovered without bias on F1 and C4 (|Δb| ≤ 0.012×10⁻³). The residual alone does not produce a b (b_true = 0 → 0), so the faint-end χ² is not distorted by the term. On S22 the null is also recovered (−0.012), and 1.2 comes back 5% low, still drifting when training stopped.
S22: b not reproducible fold to fold
Trained b for S22 folds 0–4: 1.15, 0.29, 0.38, 0.20, 0.10 ×10⁻³. Every fold starts at 1.0. Fold 0 settles while folds 1–4 fall slowly through stage 3; the loss plateau stops them with b nearly flat (last-quarter drift ≤ 0.006×10⁻³). The same image and recipe give F2 0.46–0.66. The capped saturated stars (all nuisance) number 41–53 per fold and do not track b. The control-stack slope for S22 is an ordinary 0.66 ± 0.08. In S22 the loss is flat along a b–ePSF direction and the trained b depends on which stars are trained. Under investigation.
Explorer: residual grids per field and arm
Held-out stars. "all folds pooled" = every available fold (5 for control, lin, chain, hybrids; 2 for Arm A and the forms; the image title states n folds). Use "folds 0–1 pooled" to compare any arm with the control on the same stars: grid-cell noise falls as √(number of stars), so a 5-fold grid looks cleaner than a 2-fold grid even at equal model quality. Single folds are available for control, lin and chain. Fixed colour scales for every arm: absolute ±30e-4, relative ±20e-4, arm − control ±10e-4 (cell titles Δcxx/Δcyy, 1e-3 px²), stacks ±8e-4. Combinations that do not exist are greyed out. The forms cover only F1/F2/C4 folds 0–1, the chain only F1/C4, Arm A (run3_rf5) only F1/F2/C4. "CPU control" = matched C4 CPU control. Injection fits have no held-out evaluation (table above).
Bugs found and fixed in this campaign
- A test of the new aniso form passed only when the worktree was on PYTHONPATH: the editable install imports the live checkout. Every job now pins PYTHONPATH to the branch pin, and the launcher asserts the import path.
- My
pkill -f inject_submit.shmatched the shell running it and killed that command. The watcher was stopped by pid instead. - Per-star width regression (test 3, first version) gave near-zero negative slopes: single-star residual rasters are noise- and NaN-dominated. It was replaced by stacks per q bin.
- Earlier (10-10, Arm A): the scorer gave held-out stars q = 0. Fixed in bwidth-rf5 f6364402, which is in this branch.
Compute
54 + 30 + 25 + 8 Condor jobs. 12 chain fits on Colab T4: 6 sessions, never more than 2 at once, all self-stopped. `colab sessions` is empty and usage 0.00/hr; ≈ 9 CU. GPU landing: hold the Condor fold job, install the verified GPU fit, release; the job then only evaluates.
Records
/astro/armin/koji/syndiff/dev_runs/bwidth_campaign_20261012/README.md (log, all numbers), eval/*.json, figs/, explorer/; residual-grid canvas tabs bwchain_* / run4ctrl_*; Colab RUN_CATALOG entries bwcamp-*.
SUPERSEDED 10-13 by §12 (decision revised; "removes most of the excess" was wrong, it removes about half). Conclusion (PRELIMINARY, 2026-10-12). The brightness term is real, recoverable and helps. The control's own residual stacks show a positive width-vs-charge slope in every quadrant of all five fields. Trained with the ePSF it removes most of the bright-star excess, lowers bright held-out χ² by 1.2% bin-averaged (more through the full chain), and slightly improves faint stars, because the ePSF stops compromising toward the bright stars. It changes photometry by under 1 mmag and apertures by under 0.4 ppt. Open: the trained b's field-to-field spread (0.4–1.4 ×10⁻³) is not detector-driven (the control slopes are uniform), and S22 does not reproduce fold to fold. A per-field trained b is therefore partly an optimisation outcome rather than a physical constant. The pre-registered pooled-median gate fails and stays reported. Recommendation pending the final round.
12 · Decision revised (user, 2026-10-13): report the effect, the term goes to an appendix
Decision. The adopted Paper 1 model is run4 without the brightness term. The main text (§5.3, Fig. 14) reports the effect as an observation; the linear term is Appendix D (Fig. D1). Recorded in MODEL_FREEZE.md "D9 REVISED" (dev 646b527). The term chains keep running to complete the appendix record; the S22 all-star fit has finished ($b=0.38\times10^{-3}$).
Width measurement. Stacks of $R=(\text{data}-\text{model})/f$ are fitted inside $r<3.5$ px with $R\simeq a_0P+a_x\partial_xP+a_y\partial_yP+c_{xx}\partial_x^2P+c_{yy}\partial_y^2P+c_{xy}\partial_x\partial_yP+a_1$. Here $P$ is the median unit-flux model of the field's $T\ge10$ stars; $a_0$, $a_x$, $a_y$ and $a_1$ are the flux, centroid and background offsets; and $c=\tfrac12(c_{xx}+c_{yy})$. The extra Gaussian variance per axis is $\Delta\Sigma=c/0.59$. The factor 0.59 is a calibration: an exact Gaussian blur of known variance pushed through the same fit (0.586–0.592 in every field). $q$ is the peak charge per 2-s read, $q=2\,\mathrm{s}\times f\times\phi$, with $f$ the fitted flux (e⁻ s⁻¹) and $\phi$ the model's peak-pixel fraction.
Two corrections to earlier sections.
- Units. The "model-free slopes" of §11 (0.36–0.57) and the Fig. 14 v2 panel (d) were in estimator units $c$, not $\Delta\Sigma$. In $\Delta\Sigma$ they are 1.05–1.65, above the trained $b$ in every field. The 10-12 reading "trained $b$ exceeds the slope because training re-sharpens the ePSF" is withdrawn. The fixed $b=0.65$ test was set from the mis-scaled slope.
- Core concentration. The 10-12 night proposal that the leftover is core-concentrated does not hold on identical stars. The shortfall is the amplitude of $b$; candidate causes are the $T<10.5$ weight cap and compensation by the ePSF. The first follow-up test is therefore a fixed $b$ near the model-free slope, or a weaker bright-star cap, before any new operator.
Conclusion (2026-10-13). The effect is robust: a brighter–fatter-type broadening linear in peak charge per read, seen in all fields and detector cells, stronger for pixel-centred stars, about $1.3\times10^{-3}$ px² per $10^4$ e⁻. Our model of it is not robust: the trained linear term captures half, its coefficient differs between fields, and it failed its gate. Leaving it out costs about 1 % bright-star $\chi^2$ and under 1 mmag of photometry. Paper: observation in the main text, term in the appendix. The investigation continues off the paper's critical path, starting with the amplitude question.
Conclusion
Current conclusion: §12 (2026-10-13). The paragraph below is the 10-07 conclusion, kept for the record; its "narrower than a blur" reading is superseded by §12.
The best current fits leave a brightness-dependent PSF width that is significant, repeated on two fields, present in held-out stars and in every colour slice, and shaped like charge spreading from the brightest pixel. It starts near $q\approx10^4$ e⁻ per read (T≈10). Expressed as the existing linear law, it implies $b\approx0.5$–$1.2\times10^{-3}$ px² per $10^4$ e⁻, consistent with the independent 09-30 value of 0.8. Confidence in the effect is high. The exact size, and whether one global $b$ fits both CCDs, is uncertain, because the bright end rests on tens of stars per bin and held-out stars cannot measure the slope. Modelling neighbours removed a spurious faint-end part of about 0.4e-3 px², so earlier magnitude-trend numbers that ignored neighbours overstate the effect. The width-term fit (§2b) captures the ring and the sign but only about half of the bright stars’ central-pixel deficit, which is narrower than a blur of the PSF. So the existing bright_width blur is expected to be necessary but not sufficient, and a pixel-scale charge-redistribution term should be tested alongside it. A term linear in magnitude cannot be ruled out on the binned curve, but it is the wrong variable (§6). Next step: the test in §5, arms B–F.
Limits: fold 0 only. The residual stacks use fitted fluxes as the denominator, which affects the absolute level but not the width terms. The width estimator uses the bin's median model and is linear in small perturbations; 1e-3 px² is about 0.1% of the PSF's second moment.
Records
- Run record:
/astro/armin/koji/syndiff/dev_runs/magdep_epsf_20261007/README.md, with codemagdep_v8.py,colour_control_v8.py,b_estimate.py, numbersv8_results.json,v8_colour_control.json,b_estimate.json, and figures infigs/. - Fits and evaluation:
/astro/armin/koji/syndiff/dev_runs/training_fixes_20261005/(runs/,eval/, canvas training fixes). - Prior measurement:
dev_runs/bfwidth_20260929anddev_runs/bfwidth_prod_20260930. Closure verdict:dev_runs/epsf_model_closure_20261004/FINAL_CONCLUSION.md. - Term implementation:
training_fixes_20261005/pin_5058723/syndiff_pipeline/forward_model/bright_width.py. - Run-3 rerun (§9):
/astro/armin/koji/syndiff/dev_runs/epsf_dependence_20261008/w3d/run3/(analysis/,stacks*/,faint_check/,transport/,figs/); law scoreslawscore/run3_v2,run3_v3; driverscode/perstamp/run_laws_run3.sh,run_stacks_run3.sh,run3_compare_fig.py. Codex record reviewed:dev_runs/bf_transport_20261008/current/README.md. - Canvas source:
/home/kshukawa/syndiff_pipeline/doc/magdep_epsf_canvas/(template.html+build.py).