Hybrid DC (PV + Battery)¶
DERSim's HybridDC-3Phase device type models a DC-coupled hybrid
where a PV array and a battery share a single inverter on the same
DC bus. This is structurally different from AC coupling (where each
source has its own inverter) — there's one grid connection, one
inverter rating to share, and the coordinator has to decide every
tick what to do with each kW of PV.
./sim --device_type HybridDC-3Phase
By default the simulator sizes the system as a residential 10 kW PV / 10 kW × 15 kWh battery / 15 kVA inverter system. Override any of the four sizing flags to match what you're testing:
| Flag | Default | Unit | Notes |
|---|---|---|---|
--hybrid_pv_kw |
10.0 | kW | PV array nameplate rating |
--hybrid_batt_kw |
10.0 | kW | Battery power rating (charge + discharge symmetric) |
--hybrid_batt_kwh |
15.0 | kWh | Battery energy capacity |
--hybrid_inverter_kva |
15.0 | kVA | Shared inverter apparent-power rating |
Plus two SoC bounds and the dispatch-policy selector:
| Flag | Default | Unit | Notes |
|---|---|---|---|
--hybrid_soc_min_pct |
10.0 | % | SoC floor — battery refuses to discharge below this |
--hybrid_soc_max_pct |
95.0 | % | SoC ceiling — battery refuses to charge above this |
--hybrid_dispatch |
self_consumption |
enum | One of self_consumption, pv_priority, manual |
A typical lab run for a small commercial site:
./sim --device_type HybridDC-3Phase \
--hybrid_pv_kw 50 \
--hybrid_batt_kw 30 \
--hybrid_batt_kwh 100 \
--hybrid_inverter_kva 40
Dispatch policies¶
The hybrid coordinator implements three policies. They're hot-swappable at runtime via REST without restarting the simulator (see REST control below).
self_consumption — default¶
Park PV surplus in the battery, discharge to fill grid-export gaps. The coordinator runs PV at MPP, takes the difference between PV production and the inverter's current grid-side target, and routes the delta into / out of the battery. Net result: the grid only sees what self-consumption can't cover.
This is the policy residential and small-commercial sites pick when their utility rate has poor PV export pricing.
pv_priority¶
Export everything PV produces; leave the battery alone unless explicitly commanded. Battery only charges if PV exceeds the inverter rating (curtailment-replacement); otherwise the battery sits at its current SoC waiting for a manual command.
Use this when the site's export PPA pays well enough that storing PV is uneconomical.
manual¶
Operator-driven inverter output target. The coordinator runs PV at
MPP and the battery fills the gap between PV power and the operator
target. The current target is set via REST (see below). If the
operator commands an export larger than pv_kw + batt_kw, the
coordinator caps at that ceiling and reports dispatch_state:
"inverter-clamp".
Use this for grid-services testing — frequency response, capacity bids, scripted demand-response programs.
Dashboard¶
The dashboard exposes a Hybrid DC panel (sidebar 🔀) showing the live PV / battery / inverter split, SoC, dispatch policy chip, energy capacity, and an interactive dispatch-policy switcher:

Under PV-priority the battery sits at zero and PV exports unconstrained:

Under manual dispatch the operator can set an inverter target via REST and the coordinator splits across PV (at MPP) and battery:

The overview tile rolls the live AC metrics up the way every other device type does, with the hybrid SoC visible alongside the inverter's instantaneous power:

The PV / battery / inverter bar visualises the split each tick —
positive bars are export (PV generating, battery discharging),
negative bars are import (battery charging from PV). The exact
numbers come from the same /api/dersim/hybrid-dc snapshot.
SoC bounds¶
The coordinator obeys --hybrid_soc_min_pct and
--hybrid_soc_max_pct strictly:
- At the floor (
soc <= min) the battery refuses to discharge. Self-consumption can no longer cover export gaps; the gaps come from the grid instead. The snapshot reportsdispatch_state: "soc-floor". - At the ceiling (
soc >= max) the battery refuses to charge. PV surplus that would normally be soaked is curtailed to the inverter limit instead. The snapshot reportsdispatch_state: "soc-ceiling".
The defaults (10 % / 95 %) match typical Li-ion warranty floor and ceiling. Tighten them for accelerated cycling tests; widen them only when testing battery-management edge cases.
REST control — policy switching¶
Hot-swap the dispatch policy without restarting:
POST /api/dersim/hybrid-dc/dispatch
Content-Type: application/json
{"dispatch_policy": "pv_priority"}
dispatch_policy accepts self_consumption, pv_priority, or
manual. Switching to manual does not populate a setpoint —
the inverter target stays at whatever the coordinator was last
producing until the operator posts to /hybrid-dc/manual.
Manual-mode setpoint¶
When the active policy is manual, set the inverter output target:
POST /api/dersim/hybrid-dc/manual
Content-Type: application/json
{"inverter_kw": 7.5}
Sign convention: + = export to grid, − = import from grid. The
coordinator splits this across PV (at MPP) and the battery. If PV is
already producing more than the target, the surplus charges the
battery (SoC ceiling permitting). If PV is producing less, the
battery discharges to fill the gap (SoC floor permitting).
Returns HTTP 400 if posted while the active policy isn't manual —
switch policy first, then set the target.
Dispatch-state tag¶
Every snapshot includes a dispatch_state string explaining why
this tick's split looks the way it does. The values that aren't a
literal echo of the policy name are the constrained cases:
dispatch_state |
When |
|---|---|
self-consumption / pv-priority / manual |
Nominal — no clamp active |
soc-floor |
Battery wants to discharge but SoC is at min |
soc-ceiling |
Battery wants to charge but SoC is at max |
charge-limit |
Charge rate clamped against batt_kw |
discharge-limit |
Discharge rate clamped against batt_kw |
inverter-clamp |
Combined PV + battery exceeds inverter_kva |
For programmatic test harnesses, watching this tag is the cleanest way to assert "policy X engaged correctly" without having to reason about the floating-point splits directly.
SunSpec model exposure¶
The hybrid device populates the same general DER SunSpec model set as PV (701 nameplate + live AC measurements, 702 ratings, 703 enter-service, 704 fixed PF, 705 Volt-Var, 706 Volt-Watt, 711 Frequency-Droop, 712 Watt-Var), with the DC-side coverage spread across both sources:
| Model | Purpose | Key points |
|---|---|---|
714 |
DC-side input | Two ports: PrtTyp = 0 (PV) for the array and PrtTyp = 1 (ESS) for the battery |
802 |
Battery bank | Populated for the battery branch — the SoC, voltage, and current the dashboard shows come from this model |
715 |
DC port detail | Per-port DC voltage / current / energy |
The Modbus client sees a single SunSpec inverter with two DC ports of different types — exactly the wire shape a real DC-coupled hybrid inverter (Tesla Powerwall, SolarEdge Energy Hub, Enphase IQ Battery + Microinverter, etc.) presents.
Why DC coupling¶
The choice between AC coupling and DC coupling matters because:
- DC coupling routes PV directly into the battery DC bus, so the PV-to-battery round-trip pays only one DC/DC conversion. AC coupling pays a DC/AC + AC/DC round-trip for the same energy.
- DC coupling shares one inverter — cheaper per kVA, but the PV and battery have to time-share the inverter's apparent-power rating. AC coupling pays for two inverters but they're independent.
- DC coupling lets the coordinator clamp PV at the MPP curve without going through the inverter — useful for fast PV curtailment during ride-through events. AC coupling has to use the inverter's active-power command instead.
Lab tests that need to exercise the time-sharing behavior or the
dispatch-policy semantics specifically should pick HybridDC-3Phase
over running separate PV-3Phase + Batt-3Phase instances.