// Derives the cable-sizing inputs that follow from the circuit itself, so the // planner starts from the circuit's real values instead of fixed defaults. import type { CircuitCategory, ExistingProtection, } from "../../cable-sizing/domain/cable-sizing-calculation"; import type { CircuitTreeCircuitDto, CircuitTreeProtectionDeviceDto } from "../types"; /** Three-phase if the circuit group or any device row is three-phase. */ export function deriveCircuitPhase( circuit: CircuitTreeCircuitDto, circuitCategory?: CircuitCategory ): 1 | 3 { return circuitCategory === "three_phase" || circuit.deviceRows.some((row) => row.phaseType === "three_phase") ? 3 : 1; } /** * Power-weighted cos φ of the circuit: P / S with S = Σ P_row / cos φ_row. * Rows without cos φ count as 1; a circuit without load yields 1. */ export function deriveCircuitCosPhi(circuit: CircuitTreeCircuitDto): number { let activePowerKw = 0; let apparentPowerKva = 0; for (const row of circuit.deviceRows) { activePowerKw += row.rowTotalPower; apparentPowerKva += row.rowTotalPower / (row.cosPhi || 1); } if (activePowerKw <= 0 || apparentPowerKva <= 0) { return 1; } return Math.round((activePowerKw / apparentPowerKva) * 100) / 100; } /** * Maps the circuit's protective device to the overload-protection kind of the * calculation. Circuit-breakers (LS, FI/LS, AFDD) are "breaker", D0 and NH * fuse links "fuse"; a residual-current device without overcurrent release * (FI) provides no overload protection and is ignored. */ export function toExistingProtection( protection: CircuitTreeProtectionDeviceDto | undefined ): ExistingProtection | undefined { if (!protection || protection.type === "FI") { return undefined; } const kind = protection.type === "LS" || protection.type === "FI_LS" || protection.type === "AFDD" ? "breaker" : "fuse"; return { ratedCurrentA: protection.ratedCurrentA, kind }; }