A common failure mode in residential and light-commercial backup power design is sizing a generator or off-grid inverter strictly against steady-state running wattage.
When a 3-ton heat pump compressor or well pump attempts to start, the generator voltage collapses, the alternator frequency sags below 58 Hz, and the internal overcurrent trip disconnects the transfer switch.
The root physical cause is the induction motor starting transient, governed by NEMA MG-1 locked-rotor kVA codes, coupled with generator sub-transient reactance ($X''_d$).
In this post, we examine the governing electro-mechanical equations and construct a pure deterministic TypeScript calculation engine to model motor starting transients and non-coincident load stacking.
1. The Physics of Locked Rotor Current
At the instant AC voltage is applied across a stationary induction motor, the rotor slip $s$ equals 1.0:
$$s = \frac{n_s - n_r}{n_s} = 1.0$$
Because no back-electromotive force (back-EMF) exists, the effective impedance of the motor is limited strictly to stator leakage reactance and rotor leakage reactance.
Under NEMA MG-1 standards, motor starting performance is classified by letter codes ($A$ through $V$), defining the locked-rotor kilovolt-amperes per rated horsepower:
$$\text{kVA}_{\text{LR}} = \text{HP} \times \text{CodeMidpoint}$$
$$\text{LRA} = \frac{\text{kVA}{\text{LR}} \times 1000}{V{\text{LL}} \times \sqrt{3}} \quad (\text{Three-Phase})$$
$$\text{LRA} = \frac{\text{kVA}{\text{LR}} \times 1000}{V{\text{LN}}} \quad (\text{Single-Phase})$$
For typical residential single-phase induction motors (NEMA Code G), starting kVA ranges from 5.6 to 6.29 kVA/HP, resulting in an inrush current multiplier between 5.0 and 7.0 times the full load running current (FLA).
2. Generator Voltage Dip and Sub-Transient Reactance
Unlike utility grid connections backed by large distribution transformers, portable and standby synchronous alternators possess significant internal impedance.
When an instantaneous LRA step-load occurs, alternator terminal voltage drops precipitously based on direct-axis sub-transient reactance ($X''_d$):
$$V_{\text{dip}} \approx \frac{\text{kVA}{\text{starting}}}{\text{kVA}{\text{generator}} \times \frac{1}{X''d} + \text{kVA}{\text{starting}}}$$
Standard synchronous alternators feature $X''_d$ values between 0.12 and 0.18 per-unit. If instantaneous voltage sag exceeds 30 to 35 percent, magnetic motor contactors chatter, digital control boards reset, and the engine stalls from sudden torque overload.
3. Non-Coincident Peak Load Stacking Algorithm
To size a generator correctly without over-engineering by 300%, we implement a non-coincident starting peak model:
- Sum all steady-state running loads ($P_{\text{run}}$).
- Identify the single largest motor with the highest inrush requirement ($\max(\Delta P_{\text{surge}})$).
- Add the single largest surge delta to the total running baseload:
$$P_{\text{generator_peak}} = \sum_{i=1}^{N} P_{\text{run}, i} + \max_{1 \le j \le N} \left( P_{\text{surge}, j} - P_{\text{run}, j} \right)$$
4. Deterministic TypeScript Engine
Here is the deterministic calculation engine used in the PowerLab platform, completely decoupled from UI state and side effects:
export interface LoadItem {
id: string;
name: string;
runningWatts: number;
startingWatts: number;
powerFactor: number;
dutyCycle: number;
isMotor: boolean;
}
export interface InrushAnalysisResult {
totalRunningWatts: number;
totalConnectedWatts: number;
largestMotorInrushWatts: number;
recommendedContinuousKw: number;
recommendedSurgeKw: number;
voltageDipRisk: "low" | "moderate" | "severe";
assumptions: string[];
}
export function calculateInrushStacking(
loads: LoadItem[],
safetyMarginPct: number = 20
): InrushAnalysisResult {
if (!loads.length) {
return {
totalRunningWatts: 0,
totalConnectedWatts: 0,
largestMotorInrushWatts: 0,
recommendedContinuousKw: 0,
recommendedSurgeKw: 0,
voltageDipRisk: "low",
assumptions: ["No active loads provided."],
};
}
// 1. Calculate base running load
const totalRunningWatts = loads.reduce(
(sum, load) => sum + load.runningWatts,
0
);
// 2. Identify the single largest motor surge differential
let maxSurgeDelta = 0;
let largestInrush = 0;
for (const load of loads) {
const surgeDelta = Math.max(0, load.startingWatts - load.runningWatts);
if (surgeDelta > maxSurgeDelta) {
maxSurgeDelta = surgeDelta;
}
if (load.startingWatts > largestInrush) {
largestInrush = load.startingWatts;
}
}
// 3. Non-coincident peak demand
const peakStackingWatts = totalRunningWatts + maxSurgeDelta;
// 4. Apply engineering safety headroom (default 20%)
const multiplier = 1 + safetyMarginPct / 100;
const recommendedContinuousKw = Number(
((totalRunningWatts * multiplier) / 1000).toFixed(2)
);
const recommendedSurgeKw = Number(
((peakStackingWatts * multiplier) / 1000).toFixed(2)
);
// 5. Assess alternator voltage dip risk
const surgeToRunRatio = peakStackingWatts / Math.max(1, totalRunningWatts);
let voltageDipRisk: "low" | "moderate" | "severe" = "low";
if (surgeToRunRatio > 2.5) {
voltageDipRisk = "severe";
} else if (surgeToRunRatio > 1.8) {
voltageDipRisk = "moderate";
}
return {
totalRunningWatts,
totalConnectedWatts: loads.reduce((s, l) => s + l.startingWatts, 0),
largestMotorInrushWatts: largestInrush,
recommendedContinuousKw,
recommendedSurgeKw,
voltageDipRisk,
assumptions: [
`Safety margin applied: ${safetyMarginPct}%`,
`Governed by NEMA MG-1 single-motor starting non-coincidence`,
`Alternator sub-transient reactance assumes 15% standard drop limit`,
],
};
}
5. Verification & Mathematical Monotonicity
To ensure the engine behaves predictably under edge conditions, unit tests verify monotonic behavior:
describe("calculateInrushStacking", () => {
it("computes non-coincident starting peak correctly", () => {
const sampleLoads: LoadItem[] = [
{ id: "1", name: "Refrigerator", runningWatts: 150, startingWatts: 1200, powerFactor: 0.85, dutyCycle: 0.4, isMotor: true },
{ id: "2", name: "Central AC", runningWatts: 3500, startingWatts: 18000, powerFactor: 0.9, dutyCycle: 0.6, isMotor: true },
{ id: "3", name: "LED Lighting", runningWatts: 200, startingWatts: 200, powerFactor: 0.95, dutyCycle: 1.0, isMotor: false }
];
const result = calculateInrushStacking(sampleLoads, 20);
// Total running: 150 + 3500 + 200 = 3850 W
expect(result.totalRunningWatts).toBe(3850);
// Largest surge delta: AC (18000 - 3500 = 14500 W)
// Non-coincident peak: 3850 + 14500 = 18350 W
// Continuous kW with 20% margin: 3850 * 1.2 / 1000 = 4.62 kW
expect(result.recommendedContinuousKw).toBe(4.62);
// Surge kW with 20% margin: 18350 * 1.2 / 1000 = 22.02 kW
expect(result.recommendedSurgeKw).toBe(22.02);
expect(result.voltageDipRisk).toBe("severe");
});
});
Open Research & Calculators
The complete open-source mathematical derivation, empirical lookup tables across NEMA code letters, and interactive simulation calculators are available at:
- Interactive Generator Size Simulator: powelab.org/home-energy/generator-size-calculator
- Technical Whitepaper: powelab.org/research/deterministic-inrush-load-stacking-generator-sizing
- Standards Reference: NFPA 70 (NEC Article 702), NEMA MG-1 Motors & Generators.
Top comments (0)