Skip to content

feat: weight power distribution by battery capacity (kWh)#1194

Open
Nicolas-nwb wants to merge 1 commit intoZendure:masterfrom
Nicolas-nwb:feature/kwh-weight-distribution
Open

feat: weight power distribution by battery capacity (kWh)#1194
Nicolas-nwb wants to merge 1 commit intoZendure:masterfrom
Nicolas-nwb:feature/kwh-weight-distribution

Conversation

@Nicolas-nwb
Copy link
Copy Markdown

Problem

When mixing batteries of different capacities (e.g. SolarFlow 2400AC with 17 kWh and Hyper 2000 with 4 kWh), the power distribution is unfair. The current weight formula pwr_max * SOC ignores total energy capacity, so a 17 kWh battery only receives ~56% of discharge power despite holding ~73% of the total stored energy.

This has been reported in #806 and #1084.

Solution

Multiply the distribution weight by max(d.kWh, 1.0) so that larger batteries receive proportionally more charge/discharge power.

New formula: weight = pwr_max × SOC × max(kWh, 1.0)

6 lines changed in manager.py:

  • powerChanged() — weight accumulation for charge and discharge
  • power_charge() — per-device power calculation and weight decrement
  • power_discharge() — per-device power calculation and weight decrement

Backward compatibility

Scenario Behavior
Same-capacity batteries kWh factor cancels out → no change
Single battery Weight ratio is always 100% → no change
kWh not yet known (0.0) Fallback max(0.0, 1.0) = 1.0original behavior
Physical power limits Still enforced via min(pwr, d.pwr_max) / max(pwr, d.pwr_max) caps

Test results

Tested on a real setup with 3 batteries (SolarFlow 2400AC 17 kWh + 2× Hyper 2000 4 kWh):

Battery Before After
SolarFlow 2400AC (17 kWh, 71% SOC) 0.89 kW (56%) 1.56 kW (93%)
Hyper 2000 (4 kWh, 56% SOC) 0.35 kW (22%) 0.12 kW (7%)
Hyper 2000 2 (4 kWh, 55% SOC) 0.34 kW (22%) idle

The larger battery now carries the bulk of the discharge, which is the expected behavior given its much higher stored energy. Peak power limits (2400W / 1200W) are still respected.

The power distribution formula uses `pwr_max * SOC` to calculate each
device's share. This ignores total energy capacity, causing unfair
distribution when mixing batteries of different sizes (e.g. SolarFlow
2400AC with 17 kWh vs Hyper 2000 with 4 kWh).

Multiply the weight by `max(d.kWh, 1.0)` so that larger batteries
receive proportionally more charge/discharge power.

Backward compatible:
- Same-capacity batteries: kWh factor cancels out, no behavior change
- Single battery: weight ratio is always 100%, no change
- kWh not yet known (0.0): fallback to 1.0, original behavior preserved
- Physical power limits (pwr_max) still enforced via min()/max() caps

Addresses Zendure#806, Zendure#1084
@FireSon
Copy link
Copy Markdown
Collaborator

FireSon commented Mar 30, 2026

I did not test this is real life, but the goal of the current software, is that all batteries are discharged at more or less the same electriclevel. Is that not the case? Would in your case not 1 device to all the work? I am just trying to understand, I do not have the same configuration as you...

@Nicolas-nwb
Copy link
Copy Markdown
Author

Hi FireSon, thanks for taking the time to review this PR! This is my first contribution to the project, so I appreciate the feedback.

You're right that the current software tries to discharge all batteries at the same SOC level. The issue is that with batteries of very different capacities, equal SOC does not mean equal energy contribution.

Here's a concrete example from my setup:

Battery Capacity SOC Stored energy
SolarFlow 2400AC 17 kWh 70% 11.9 kWh
Hyper 2000 4 kWh 70% 2.8 kWh

Both are at 70% SOC, but the 2400AC holds 4.25x more energy. Without the kWh factor, the weight formula pwr_max × SOC gives them similar power shares, so the small battery empties much faster than the large one. They end up at different SOC levels despite the software trying to keep them balanced.

With the kWh factor, the 17 kWh battery naturally receives more power, which means both batteries actually reach the same SOC at the same time. The kWh weighting doesn't fight the SOC balancing goal; it makes it work correctly for mixed-capacity setups.

To address your concern about one device doing all the work: no, because the SOC term is still in the formula. As a battery discharges and its SOC drops, its weight decreases, so the other batteries progressively take over. Both batteries still participate and absorb peaks together: if there's a large charge or discharge spike, the power is split across all active batteries proportionally to their capacity, which also reduces stress on individual devices.

For setups where all batteries have the same capacity, the kWh factor cancels out (same multiplier on both sides), so it changes absolutely nothing.

Possible improvement: the current formula uses total kWh, but doesn't account for minSoc. A battery with 17 kWh and minSoc 10% has 15.3 kWh of usable capacity, while a 4 kWh battery with minSoc 30% only has 2.8 kWh usable. The code already computes availableKwh = (electricLevel - minSoc) / 100 × kWh per device. A further refinement could use effective capacity kWh × (100 - minSoc) / 100 instead of raw kWh in the weight formula. I'll experiment with this on my setup and can submit a follow-up PR if the results are good.

@harrymayr
Copy link
Copy Markdown
Collaborator

I just checked the actual implementation and created a EXCEL-Sheet for this. But in my opinion, the capacity is implizit already included. All batteries are empty at nearly the same time. So I don't think there is a need for changing that. (I did not include the checking on optimal_power, which will change only slightly the result).
image
Discharge_Distribution.xlsx

@Nicolas-nwb
Copy link
Copy Markdown
Author

Hi @harrymayr Thanks,

Your math is correct: without minSoc, all batteries do reach 0% at roughly the same time.

However, in practice, batteries stop discharging when they hit minSoc. I took your spreadsheet and added two columns — "Effective total (minSoc ≥ B6%)" and "Grid import" — plus a configurable minSoc parameter in cell B6. Nothing else was changed; your formulas and values are untouched.

image

Discharge_Distribution_with_minSoc.xlsx

At 13:00, both Hypers cross below 10% and go SOCEMPTY, while the SF is still at 31%. From that point, only the SF contributes (capped at 2400W), leaving a 1100W deficit from the grid for ~1h40.

The theoretical convergence to 0% is real, but minSoc prevents it from happening in practice. The small batteries exhaust their usable range much earlier, and the system loses peak capacity. Both batteries still participate and absorb peaks together, but only as long as they're above minSoc.

With the kWh factor, the SF carries more of the load (proportional to its 17 kWh vs 4 kWh), so all batteries cross 10% within ~40 minutes of each other instead of 1h40. The house stays closer to 3500W for longer.

You can try it yourself: I attached your spreadsheet with just the two extra columns added. Change B6 to any minSoc value and see the impact.

For setups where all batteries have the same capacity, the kWh factor cancels out (same multiplier on both sides), so it changes absolutely nothing.

Possible improvement: the current formula uses total kWh, but a further refinement could use effective capacity kWh × (100 - minSoc) / 100 instead of raw kWh, since the code already computes availableKwh per device. I'll experiment with this on my setup and can submit a follow-up PR if the results are good.

@harrymayr
Copy link
Copy Markdown
Collaborator

Both EXCEL files are not "correct". I added now the limiting at the discharge_limits and the starting of devices according to the optimal_power.
Finaly it is depending on the power which is demanded, if there is an advantage to increase the power for the SF2400 or not. At some point the discharge-limit is the limiting factor.
The EXCEL formulas become a little bit confused , so I don't want to add more features.
In general I aggree to use a "relative" SoC related to the available capacity. But this is only necessary for higher power demands for longer times, which need 2-3 devices to discharge. For demanded power which can be fulfilled from one device, there is no issue.
Discharge_Distribution_with_minSoc_corrected.xlsx

@Nicolas-nwb
Copy link
Copy Markdown
Author

You're right, at lower demand where one device can handle it alone, the kWh factor doesn't change anything. The benefit only shows up when multiple devices discharge together at higher demand, which is my use case. And for setups where all batteries have the same capacity, the kWh multiplier is identical on both sides, so it cancels out completely: zero behavioral change.

That said, I think mixed-capacity setups will become more and more common as the project grows. I started with a Hyper 2000 myself and later added a SolarFlow 2400AC. It's a natural upgrade path, and this change would make the transition smoother for anyone doing the same.

I'm happy to keep this on my fork if you'd rather not merge it. Either way, thanks for taking the time to analyze it in detail. I came here to improve power distribution and ended up leveling up my Excel skills too!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants