|
| 1 | +# Powder Chart Y-Range Fix Plan |
| 2 | + |
| 3 | +**Date:** 2026-05-06 **Status:** Phase 2 verified — complete |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## 1. Goal |
| 8 | + |
| 9 | +Fix the Plotly composite powder measured-vs-calculated chart so the main |
| 10 | +intensity row is not anchored to zero. The y-axis range should be |
| 11 | +derived from all displayed main-row intensity series: measured |
| 12 | +(`Imeas`), calculated (`Icalc`), and background (`Ibkg`) when present. |
| 13 | + |
| 14 | +The intended display range is: |
| 15 | + |
| 16 | +```text |
| 17 | +lower = min(Imeas, Icalc, Ibkg) - margin |
| 18 | +upper = max(Imeas, Icalc, Ibkg) + margin |
| 19 | +``` |
| 20 | + |
| 21 | +where `margin` is controlled by a dedicated constant of about 5% of the |
| 22 | +main intensity span. The lower bound must use `min - margin`, not |
| 23 | +`min + margin`, so the lowest displayed point remains visible with |
| 24 | +padding below it. |
| 25 | + |
| 26 | +--- |
| 27 | + |
| 28 | +## 2. Current Findings |
| 29 | + |
| 30 | +- The affected code is `PlotlyPlotter._get_main_intensity_range()` in |
| 31 | + `src/easydiffraction/display/plotters/plotly.py`. |
| 32 | +- It currently uses only `y_meas` and `y_calc`, then forces |
| 33 | + `lower_limit = min(0.0, main_y_min)`. That explains positive powder |
| 34 | + charts being truncated to a `0..max` range. |
| 35 | +- `PowderMeasVsCalcSpec` already carries optional `y_bkg`, and |
| 36 | + `Plotter._plot_meas_vs_calc_data()` already filters |
| 37 | + `pattern.intensity_bkg` into the spec for powder Bragg plots. |
| 38 | +- Existing Plotly unit tests assert the current `0.0..max` y-range in |
| 39 | + the residual scale-match tests, so those expectations must change. |
| 40 | +- Repository memory notes confirm the background line is part of the |
| 41 | + composite powder plot and should be considered display data. |
| 42 | + |
| 43 | +--- |
| 44 | + |
| 45 | +## 3. Scope |
| 46 | + |
| 47 | +### In Scope |
| 48 | + |
| 49 | +- Add a module-level constant in |
| 50 | + `src/easydiffraction/display/plotters/plotly.py`, likely |
| 51 | + `MAIN_INTENSITY_RANGE_MARGIN_FRACTION = 0.05`. |
| 52 | +- Update `_get_main_intensity_range()` to compute min/max over `y_meas`, |
| 53 | + `y_calc`, and non-empty `y_bkg` when present. |
| 54 | +- Apply symmetric visual padding outside the data range using the new |
| 55 | + constant. |
| 56 | +- Preserve the existing empty-filtered-range behavior: empty required |
| 57 | + series should still return a harmless fallback range. |
| 58 | +- Preserve residual scale matching by letting `_get_residual_limit()` |
| 59 | + use the newly padded main range and the existing |
| 60 | + `residual_height_fraction`, so the residual row remains adjusted to |
| 61 | + the main row size as it is now. |
| 62 | +- Add/update focused unit tests for range calculation and affected |
| 63 | + residual-scale expectations. |
| 64 | + |
| 65 | +### Out of Scope |
| 66 | + |
| 67 | +- No public plotting API changes. |
| 68 | +- No user-configurable y-axis margin in this step. |
| 69 | +- No changes to ASCII plotting unless a later review shows the same main |
| 70 | + view problem exists there. |
| 71 | +- No refactor of plot layout, Bragg tick sizing, hover templates, or |
| 72 | + facade routing. |
| 73 | + |
| 74 | +--- |
| 75 | + |
| 76 | +## 4. Decisions |
| 77 | + |
| 78 | +- Use `min(Imeas, Icalc, Ibkg) - margin` for the lower y-axis bound and |
| 79 | + `max(Imeas, Icalc, Ibkg) + margin` for the upper y-axis bound. |
| 80 | +- Keep the residual plot scaled to the main intensity row, preserving |
| 81 | + the current matched-scale behavior after the main range gains padding. |
| 82 | + |
| 83 | +--- |
| 84 | + |
| 85 | +## 5. Implementation Checklist |
| 86 | + |
| 87 | +- [ ] Create branch `feature/powder-chart-y-range` if requested. |
| 88 | +- [x] In `src/easydiffraction/display/plotters/plotly.py`, add the |
| 89 | + dedicated 5% y-range margin constant near the other Plotly layout |
| 90 | + constants. |
| 91 | +- [x] Update `_get_main_intensity_range()` so it includes background |
| 92 | + intensity when available and uses the padded min/max range instead |
| 93 | + of anchoring positive data to zero. |
| 94 | +- [x] Keep zero-span data explicit and stable, using a small fallback |
| 95 | + range around the datum because a percentage margin is undefined. |
| 96 | +- [x] Confirm `_get_residual_limit()` continues to scale the residual |
| 97 | + row from the updated main y-range and existing residual height |
| 98 | + fraction. |
| 99 | +- [x] Stop after Phase 1 and request review before adding or running |
| 100 | + tests, following the repo workflow. |
| 101 | + |
| 102 | +--- |
| 103 | + |
| 104 | +## 6. Phase 2 Verification Checklist |
| 105 | + |
| 106 | +- [x] Add or update tests in |
| 107 | + `tests/unit/easydiffraction/display/plotters/test_plotly.py` for: |
| 108 | + - positive-only `Imeas`/`Icalc` data no longer starting at zero; |
| 109 | + - `Ibkg` lowering or raising the main y-range when present; |
| 110 | + - 5% padding on both ends of the main row; |
| 111 | + - residual scale-match expectations after padding changes the main row |
| 112 | + span; |
| 113 | + - empty filtered arrays retaining the existing fallback behavior. |
| 114 | +- [x] Keep the existing facade propagation test in |
| 115 | + `tests/unit/easydiffraction/display/test_plotting.py` unless the |
| 116 | + implementation reveals a missing background handoff case. |
| 117 | +- [x] Run `pixi run fix`. |
| 118 | +- [x] Run `pixi run check` until clean. |
| 119 | +- [x] Run `pixi run unit-tests`. |
| 120 | +- [x] Run `pixi run integration-tests`. |
| 121 | +- [x] Run `pixi run script-tests`. |
| 122 | + |
| 123 | +--- |
| 124 | + |
| 125 | +## 7. Likely Files |
| 126 | + |
| 127 | +- `src/easydiffraction/display/plotters/plotly.py` |
| 128 | +- `tests/unit/easydiffraction/display/plotters/test_plotly.py` |
| 129 | +- `tests/unit/easydiffraction/display/test_plotting.py` only if a |
| 130 | + facade-level test gap is discovered during verification. |
| 131 | + |
| 132 | +--- |
| 133 | + |
| 134 | +## 8. Suggested Commit Message |
| 135 | + |
| 136 | +```text |
| 137 | +Fix powder chart y-axis range |
| 138 | +``` |
0 commit comments