Skip to content

Commit c60700d

Browse files
authored
Merge pull request #61 from dandwhelan/claude/batpred-version-update-o41uoa
Remove FIT support (v712.35)
2 parents c3fc4a4 + a217357 commit c60700d

13 files changed

Lines changed: 7 additions & 510 deletions

File tree

‎AGENTS.md‎

Lines changed: 2 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -141,64 +141,10 @@ mkdocs serve # Live preview at http://localhost:8000
141141

142142
When adding a new doc page, add it to `mkdocs.yml`. The published site at <https://springfall2008.github.io/batpred/> is built automatically from `main` via GitHub Actions.
143143

144-
## Feed-in Tariff (FIT) Support
145-
146-
Predbat supports UK Feed-in Tariff schemes where users earn a generation tariff on all solar production plus a deemed export payment on a percentage of generation (typically 50%).
147-
148-
### How It Works
149-
150-
FIT is off by default. When the `metric_fit_enable` master switch is turned on **and** `metric_fit_generation_rate` is set above 0 (both Expert Mode), FIT mode is activated:
151-
152-
- **Export rate zeroed in optimizer**: Since deemed export pays regardless of actual export, the optimizer treats actual export as having zero additional value. This makes the optimizer prefer self-consumption of solar over exporting it.
153-
- **Battery headroom for solar**: The optimizer will not charge the battery to 100% from the grid when solar generation is forecast, leaving room for solar to charge the battery during the day.
154-
- **FIT income tracked**: Generation and deemed export income are subtracted from the cost metric for accurate cost/savings display.
155-
156-
Turning `metric_fit_enable` off zeroes the FIT rates at config-load time (`Fetch.fit_apply_enable()`), so every downstream consumer — the Python prediction engine, the C++ kernel, and the published sensors — sees FIT as inactive without the user having to clear their configured rates. Because this gate lives at the single point where rates are loaded, no kernel change or ABI bump is required for the toggle.
157-
158-
### Config Items
159-
160-
All under Expert Mode in `config.py`:
161-
162-
| Setting | Default | Description |
163-
|---------|---------|-------------|
164-
| `metric_fit_enable` | False | Master switch (off by default); must be turned on to activate FIT, otherwise all FIT behaviour is disabled and configured rates are ignored |
165-
| `metric_fit_generation_rate` | 0 p/kWh | FIT generation tariff rate |
166-
| `metric_fit_deemed_export_rate` | 0 p/kWh | Deemed export tariff rate |
167-
| `metric_fit_deemed_export_percentage` | 50% | Deemed export percentage |
168-
169-
### HA Sensors (when FIT enabled)
170-
171-
| Sensor | Description |
172-
|--------|-------------|
173-
| `predbat.fit_income` | Predicted FIT income (base plan) |
174-
| `predbat.fit_income_best` | Predicted FIT income (best/optimised plan) |
175-
| `predbat.fit_income_yesterday` | Predicted FIT income for yesterday's baseline |
176-
177-
All sensors include attributes: `generation_income`, `deemed_export_income`, `generation_rate`, `deemed_export_rate`, `deemed_export_percentage`.
178-
179-
### Key Files
180-
181-
| File | What changed |
182-
|------|-------------|
183-
| `config.py` | `metric_fit_enable` master switch plus three FIT rate `CONFIG_ITEMS` entries |
184-
| `fetch.py` | Loads FIT config values, applies the `metric_fit_enable` gate (`fit_apply_enable()`), logs when FIT is enabled or disabled by the switch |
185-
| `prediction.py` | Zeros export rate when FIT enabled; tracks FIT income per simulation step |
186-
| `plan.py` | Extracts FIT income from prediction results; publishes `fit_income` / `fit_income_best` sensors |
187-
| `output.py` | Extracts FIT income from yesterday predictions; publishes `fit_income_yesterday` sensor |
188-
| `tests/test_infra.py` | FIT defaults (including `metric_fit_enable`) added to test config and `reset_inverter()` |
189-
| `tests/test_fit.py` | Covers the FIT calculator plus the `metric_fit_enable` master-switch toggle |
190-
| `prediction_kernel.cpp` / `prediction_kernel.py` | FIT rates passed into the C++ kernel; per-step clipped-PV tracking, export-rate zeroing and FIT income metric adjustment mirrored in the kernel (fork ABI/parity revision 103) |
191-
| `tests/test_kernel_parity.py` | FIT deterministic edge cases and FIT rate randomisation in the parity sweep |
192-
193-
### C++ Kernel Note (fork)
194-
195-
This fork's kernel binaries are built with ABI/parity revision **103** (upstream uses small integers like 2). Any change to the FIT logic in `prediction.py`'s hot loop must be mirrored in `prediction_kernel.cpp` and both revision numbers bumped, then all six `prediction_kernel_lib_*.so` binaries rebuilt via `build_kernel_cross.sh` (zig). When merging from upstream, re-apply the FIT kernel support if upstream bumps its ABI, and keep this fork's revision numbers strictly above upstream's.
196-
197144
## Fork-Specific Notes
198145

199-
This repository is a personal fork of `springfall2008/batpred` (currently based on upstream v8.46.4). Fork changes on top of upstream:
146+
This repository is a personal fork of `springfall2008/batpred` (currently based on upstream v9.3.0). Fork changes on top of upstream:
200147

201-
- **FIT support** — see the Feed-in Tariff section above
202148
- **Custom web dashboard** — the port-5052 web UI has a `/dash_entities` page and a redesigned power flow diagram (`web.py`, `web_helper.py`)
203149
- **DB history fix** — `db_manager`/HA history returns correct results for entities with no state change inside the query window
204150
- **Fork release pipeline** — see below
@@ -214,7 +160,7 @@ Installations tracking this fork self-update from these releases via Predbat's b
214160

215161
### Merging from upstream
216162

217-
When merging `upstream/main`, preserve the FIT feature (Python + C++ kernel), the custom dashboard, and the fork release workflow. If upstream bumps its kernel ABI revision, re-apply FIT kernel support and keep the fork's revision strictly above upstream's, then rebuild all six kernel binaries.
163+
When merging `upstream/main`, preserve the custom dashboard, the GivTCP export-target handling, the database changes and the fork release workflow. FIT support was removed in v712.35.
218164

219165
<!-- gitnexus:start -->
220166
## GitNexus — Code Intelligence

‎CLAUDE.md‎

Lines changed: 3 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -166,68 +166,16 @@ mkdocs serve # Live preview at http://localhost:8000
166166

167167
When adding a new doc page, add it to `mkdocs.yml`. The published site at <https://springfall2008.github.io/batpred/> is built automatically from `main` via GitHub Actions.
168168

169-
## Feed-in Tariff (FIT) Support
170-
171-
Predbat supports UK Feed-in Tariff schemes where users earn a generation tariff on all solar production plus a deemed export payment on a percentage of generation (typically 50%).
172-
173-
### How It Works
174-
175-
FIT is off by default. When the `metric_fit_enable` master switch is turned on **and** `metric_fit_generation_rate` is set above 0 (both Expert Mode), FIT mode is activated:
176-
177-
- **Export rate zeroed in optimizer**: Since deemed export pays regardless of actual export, the optimizer treats actual export as having zero additional value. This makes the optimizer prefer self-consumption of solar over exporting it.
178-
- **Battery headroom for solar**: The optimizer will not charge the battery to 100% from the grid when solar generation is forecast, leaving room for solar to charge the battery during the day.
179-
- **FIT income tracked**: Generation and deemed export income are subtracted from the cost metric for accurate cost/savings display.
180-
181-
Turning `metric_fit_enable` off zeroes the FIT rates at config-load time (`Fetch.fit_apply_enable()`), so every downstream consumer — the Python prediction engine, the C++ kernel, and the published sensors — sees FIT as inactive without the user having to clear their configured rates. Because this gate lives at the single point where rates are loaded, no kernel change or ABI bump is required for the toggle.
182-
183-
### Config Items
184-
185-
All under Expert Mode in `config.py`:
186-
187-
| Setting | Default | Description |
188-
|---------|---------|-------------|
189-
| `metric_fit_enable` | False | Master switch (off by default); must be turned on to activate FIT, otherwise all FIT behaviour is disabled and configured rates are ignored |
190-
| `metric_fit_generation_rate` | 0 p/kWh | FIT generation tariff rate |
191-
| `metric_fit_deemed_export_rate` | 0 p/kWh | Deemed export tariff rate |
192-
| `metric_fit_deemed_export_percentage` | 50% | Deemed export percentage |
193-
194-
### HA Sensors (when FIT enabled)
195-
196-
| Sensor | Description |
197-
|--------|-------------|
198-
| `predbat.fit_income` | Predicted FIT income (base plan) |
199-
| `predbat.fit_income_best` | Predicted FIT income (best/optimised plan) |
200-
| `predbat.fit_income_yesterday` | Predicted FIT income for yesterday's baseline |
201-
202-
All sensors include attributes: `generation_income`, `deemed_export_income`, `generation_rate`, `deemed_export_rate`, `deemed_export_percentage`.
203-
204-
### Key Files
205-
206-
| File | What changed |
207-
|------|-------------|
208-
| `config.py` | `metric_fit_enable` master switch plus three FIT rate `CONFIG_ITEMS` entries |
209-
| `fetch.py` | Loads FIT config values, applies the `metric_fit_enable` gate (`fit_apply_enable()`), logs when FIT is enabled or disabled by the switch |
210-
| `prediction.py` | Zeros export rate when FIT enabled; tracks FIT income per simulation step |
211-
| `plan.py` | Extracts FIT income from prediction results; publishes `fit_income` / `fit_income_best` sensors |
212-
| `output.py` | Extracts FIT income from yesterday predictions; publishes `fit_income_yesterday` sensor |
213-
| `tests/test_infra.py` | FIT defaults (including `metric_fit_enable`) added to test config and `reset_inverter()` |
214-
| `tests/test_fit.py` | Covers the FIT calculator plus the `metric_fit_enable` master-switch toggle |
215-
| `prediction_kernel.py` | `kernel_supported()` returns False when a FIT rate is set, so a FIT run uses the Python engine rather than the FIT-blind C++ kernel |
216-
| `tests/test_kernel_parity.py` | FIT deterministic edge cases and FIT rate randomisation in the parity sweep |
217-
218-
### C++ Kernel Note (fork)
219-
220-
**FIT is Python-only.** The kernel carried FIT fields in `PkContext` at fork ABI/parity 103-105 until the 2026-08-24 upstream merge dropped them; the fork is now back on upstream's plain numbering (currently **7/14**) and the C++ kernel knows nothing about FIT. `kernel_supported()` therefore returns False whenever `metric_fit_generation_rate` or `metric_fit_deemed_export_rate` is above zero, so a FIT user falls back to the Python engine and gets the plan they configured for, just more slowly. Covered by `tests/test_fit.py`. If FIT is ever re-added to the kernel, that gate is what to remove.
169+
## C++ Kernel Note (fork)
221170

222171
**The fork's kernel source is not upstream's.** Both sit at ABI/parity 7/14, but this fork carries its own C++ work on top: an exact-integer `round_py` fast path (~7ns vs upstream's ~96ns snprintf/strtod), a `shared_ptr` context map so `pk_context_free` on another thread cannot free a running context, a `calc_percent_limit` clamp at zero, the `pk_round_py_test` hook, and a `kernel_n_steps` guard. Because the revision numbers match upstream's, **the loader cannot tell fork binaries from upstream ones** — so on any upstream merge that touches `prediction_kernel.cpp`, taking either side's `.so` files wholesale is wrong. Merge the source, then rebuild all six `prediction_kernel_lib_*.so` from the merged tree via `build_kernel_cross.sh` (needs zig; `ZIG=/path/to/zig bash apps/predbat/build_kernel_cross.sh`) and confirm with `./run_all --test kernel_parity`.
223172

224173
Any behavioural change to `prediction.py`'s hot loop must still be mirrored in `prediction_kernel.cpp` with `KERNEL_PARITY_REVISION` and `PK_PARITY_REVISION` both bumped, followed by a rebuild of all six binaries.
225174

226175
## Fork-Specific Notes
227176

228-
This repository is a personal fork of `springfall2008/batpred` (currently based on upstream v9.0.3). Fork changes on top of upstream:
177+
This repository is a personal fork of `springfall2008/batpred` (currently based on upstream v9.3.0). Fork changes on top of upstream:
229178

230-
- **FIT support** — see the Feed-in Tariff section above
231179
- **Custom web dashboard** — the port-5052 web UI has a `/dash_entities` page and a redesigned power flow diagram (`web.py`, `web_helper.py`)
232180
- **DB history fix** — `db_manager`/HA history returns correct results for entities with no state change inside the query window
233181
- **GivTCP export-target robustness** — `givtcp_rest.py` treats GivTCP's own POST acknowledgement as proof a discharge-target write landed (some inverters never read the register back), and suppresses the every-cycle rewrite for a target already seen to be unwritable (`base.rest_discharge_target_unwritable`). Ported into the component when upstream moved REST out of `inverter.py`.
@@ -244,7 +192,7 @@ Installations tracking this fork self-update from these releases via Predbat's b
244192

245193
### Merging from upstream
246194

247-
When merging `upstream/main`, preserve the FIT feature (Python + C++ kernel), the custom dashboard, and the fork release workflow. If upstream bumps its kernel ABI revision, re-apply FIT kernel support and keep the fork's revision strictly above upstream's, then rebuild all six kernel binaries.
195+
When merging `upstream/main`, preserve the custom dashboard, the GivTCP export-target handling, the database changes and the fork release workflow. FIT support was removed in v712.35.
248196

249197
<!-- gitnexus:start -->
250198
## GitNexus — Code Intelligence

‎README.md‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@
66

77
## About this fork
88

9-
This is a personal fork of [springfall2008/batpred](https://github.com/springfall2008/batpred) with the following additions on top of upstream (currently based on upstream v8.49.0):
10-
11-
### Feed-in Tariff (FIT) support
12-
13-
UK FIT scheme support for solar self-consumption optimisation. FIT is off by default; when the `metric_fit_enable` master switch is turned on and `metric_fit_generation_rate` is set (Expert Mode), the optimiser treats actual export as having no additional value (deemed export pays regardless), prefers self-consumption of solar, and leaves battery headroom for forecast solar rather than grid-charging to 100%. Turning `metric_fit_enable` off disables all FIT behaviour while leaving the configured rates in place. Adds config items `metric_fit_enable`, `metric_fit_generation_rate`, `metric_fit_deemed_export_rate` and `metric_fit_deemed_export_percentage`, plus sensors `predbat.fit_income`, `predbat.fit_income_best` and `predbat.fit_income_yesterday`. FIT logic is also implemented in the C++ prediction kernel (fork ABI revision 103) with parity tests, and all six platform kernel binaries are rebuilt with FIT support.
9+
This is a personal fork of [springfall2008/batpred](https://github.com/springfall2008/batpred) with the following additions on top of upstream (currently based on upstream v9.3.0):
1410

1511
### Custom web dashboard
1612

@@ -23,7 +19,6 @@ This fork publishes its own GitHub releases (versioned `v712.xx`) via a release
2319
### Fixes
2420

2521
* Database history now returns correct results for entities with no state change inside the query window
26-
* FIT payment calculator bug fixes
2722
* cspell dictionary ordering fixed to match the pre-commit hook
2823

2924
### Fork agent docs

‎apps/predbat/config.py‎

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -606,49 +606,6 @@
606606
"enable": "expert_mode",
607607
"default": 0.0,
608608
},
609-
{
610-
"name": "metric_fit_enable",
611-
"friendly_name": "FIT Enable",
612-
"type": "switch",
613-
"enable": "expert_mode",
614-
"default": False,
615-
},
616-
{
617-
"name": "metric_fit_generation_rate",
618-
"friendly_name": "FIT Generation Rate",
619-
"type": "input_number",
620-
"min": 0,
621-
"max": 100,
622-
"step": 0.1,
623-
"unit": "p/kWh",
624-
"icon": "mdi:solar-power",
625-
"enable": "expert_mode",
626-
"default": 0,
627-
},
628-
{
629-
"name": "metric_fit_deemed_export_rate",
630-
"friendly_name": "FIT Deemed Export Rate",
631-
"type": "input_number",
632-
"min": 0,
633-
"max": 100,
634-
"step": 0.1,
635-
"unit": "p/kWh",
636-
"icon": "mdi:transmission-tower-export",
637-
"enable": "expert_mode",
638-
"default": 0,
639-
},
640-
{
641-
"name": "metric_fit_deemed_export_percentage",
642-
"friendly_name": "FIT Deemed Export Percentage",
643-
"type": "input_number",
644-
"min": 0,
645-
"max": 100,
646-
"step": 1,
647-
"unit": "%",
648-
"icon": "mdi:percent",
649-
"enable": "expert_mode",
650-
"default": 50,
651-
},
652609
{
653610
"name": "set_reserve_min",
654611
"friendly_name": "Set Reserve Min (Inverter 1)",

‎apps/predbat/fetch.py‎

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3118,19 +3118,6 @@ def validate_curve(self, curve, curve_name):
31183118

31193119
return validated_curve
31203120

3121-
def fit_apply_enable(self):
3122-
"""
3123-
Apply the FIT master switch.
3124-
3125-
When metric_fit_enable is off the configured FIT rates are forced to zero so that
3126-
FIT is fully disabled everywhere - the prediction engine, the C++ kernel and the
3127-
published sensors all key off these rates being greater than zero.
3128-
"""
3129-
if not getattr(self, "metric_fit_enable", True):
3130-
self.metric_fit_generation_rate = 0
3131-
self.metric_fit_deemed_export_rate = 0
3132-
self.metric_fit_deemed_export_percentage = 0
3133-
31343121
def check_export_more_solar_effective(self):
31353122
"""
31363123
Warn when export_more_solar is on but cannot have any effect.
@@ -3241,16 +3228,6 @@ def fetch_config_options(self):
32413228
self.metric_min_improvement_export_freeze = self.get_arg("metric_min_improvement_export_freeze")
32423229
self.metric_battery_cycle = self.get_arg("metric_battery_cycle")
32433230
self.metric_self_sufficiency = self.get_arg("metric_self_sufficiency")
3244-
self.metric_fit_enable = self.get_arg("metric_fit_enable")
3245-
self.metric_fit_generation_rate = self.get_arg("metric_fit_generation_rate")
3246-
self.metric_fit_deemed_export_rate = self.get_arg("metric_fit_deemed_export_rate")
3247-
self.metric_fit_deemed_export_percentage = self.get_arg("metric_fit_deemed_export_percentage")
3248-
fit_configured = self.metric_fit_generation_rate > 0 or self.metric_fit_deemed_export_rate > 0
3249-
self.fit_apply_enable()
3250-
if not self.metric_fit_enable and fit_configured:
3251-
self.log("FIT disabled: master switch (metric_fit_enable) is off, configured FIT rates ignored")
3252-
elif self.metric_fit_generation_rate > 0:
3253-
self.log("FIT enabled: generation rate {}p/kWh, deemed export rate {}p/kWh at {}%".format(dp2(self.metric_fit_generation_rate), dp2(self.metric_fit_deemed_export_rate), dp2(self.metric_fit_deemed_export_percentage)))
32543231
self.metric_future_rate_offset_import = self.get_arg("metric_future_rate_offset_import")
32553232
self.metric_future_rate_offset_export = self.get_arg("metric_future_rate_offset_export")
32563233
self.metric_inday_adjust_damping = self.get_arg("metric_inday_adjust_damping")

0 commit comments

Comments
 (0)