Continuing the discussion from Clarification on annual_excess_gen sign convention in hybrids battery simulation:
This is a follow-up to my previous thread on the excess-generation calculation in hour_simulation(). After correcting the sign issue discussed there, I found a second, independent issue related to the battery charging C-rate limitation, which explains the remaining energy-balance discrepancy in my model.
During charging, soc_usage is calculated as:
soc_usage = max(net_load * n_chg * battery_inv_eff / battery_size, -c_rate_chg)
When the available excess generation would require a charging rate above the battery C-rate, soc_usage is therefore clipped to -c_rate_chg.
The problem is that the difference between the available excess generation and the C-rate-limited charging amount is then not included in annual_excess_gen:
if (soc_prev - soc_usage > 1) or (soc > 1):
# If State of Charge...
annual_excess_gen += (soc_prev - soc_usage - 1) / n_chg / battery_inv_eff \*
battery_size
soc = 1
(The linked previous thread describes why I changed the sign - soc_usage in the calculation of above’s annual_excess_gen - I am still looking for confirmation of this).
For example:
soc_prev = 1.0
net_load = -8 kW
n_chg = 0.92
battery_inv_eff = 0.93
battery_size = 5.2 kWh
c_rate_chg = 1
The unconstrained SOC change would be:
-8 × 0.92 × 0.93 / 5.2 = -1.3163
but the C-rate limits this to:
soc_usage = -1
This corresponds to only:
1 × 5.2 / (0.92 × 0.93) = 6.0776 kWh
of charging, although 8 kWh of excess generation is available.
Therefore:
8.0000 - 6.0776 = 1.9224 kWh
of excess generation is already clipped by the C-rate and needs to be accounted for separately.
This appears to explain the remaining energy-balance discrepancy in my model. With the original calculation, I obtain:
Annual excess generation = 14856.735 kWh
Energy balance = 1214.710 kWh
After accounting for the C-rate-clipped excess, I obtain:
Annual excess generation = 16071.034 kWh
Energy balance = 0.411 kWh
The difference in annual excess generation is 1214.299 kWh, which corresponds almost exactly to the previously unexplained energy in the annual balance.
I therefore think the excess-generation calculation needs to account for both:
- energy that would cause the SOC to exceed 1, and
- energy that is already clipped because the battery charging C-rate has been reached.
I found that this can be corrected with a relatively small modification to the existing calculation by explicitly adding the C-rate-clipped amount:
if (soc_prev - soc_usage > 1) or (soc > 1):
# If State of Charge...
if (-c_rate_chg > net_load \* n_chg \* battery_inv_eff / battery_size):
c_rate_clipped_charge = -c_rate_chg - (net_load \* n_chg \* battery_inv_eff
/ battery_size)
else:
c_rate_clipped_charge = 0
annual_excess_gen += (soc_prev - soc_usage - 1 + c_rate_clipped_charge) /n_chg
/ battery_inv_eff \* battery_size
soc = 1
A potentially easier solution altogether would be to define annual_excess_gen based on a newly defined actual battery charging energy and the net_load:
# Store how much battery energy was charged (if used).
if net_load < 0:
battery_charge = 0
if battery_size > 0:
battery_charge = min(-net_load, battery_chargeable)
annual_battery_charge += battery_charge
# If State of Charge...
annual_excess_gen += (-net_load - battery_charge)
if soc > 1:
soc = 1
else:
annual_excess_gen += -net_load
battery_chargeable thereby simply follows its previous definition in hour_simulation_pv_biomass and represents the maximum energy that can actually be accepted by the battery in that hour.
I would be interested to hear whether this interpretation is consistent with the intended battery dispatch logic in OnSSET and would highly appreciate if someone could verify these findings.
I am also very happy to contribute in any way that helps move this fantastic tool forward. ![]()
Best,
Stuart