/* global React */
/* ===========================================================
VOLTLAB · SPDA — Dimensionamento Completo
ABNT NBR 5419:2026 (Partes 1, 2, 3 e 4)
Aba 1 · Análise de Risco (Parte 2)
Aba 2 · SPDA Externo (Parte 3)
Aba 3 · SPDA Interno / MPS/DPS (Parte 4)
=========================================================== */
const { useState, useMemo, useEffect } = React;
const usePersist = (key, init) =>
window.useLocalStorage ? window.useLocalStorage(key, init) : React.useState(init);
/* ─── UI atoms (re-exposed locally so this file is standalone) ─── */
const RIcon = ({ n, sz = 16 }) => {
const p = { width: sz, height: sz, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round" };
const paths = {
ok: ,
warn: <> >,
info: <> >,
bolt: ,
shield: <> >,
zap: ,
};
return {paths[n]} ;
};
/* ─── Design-system components ──────────────────────────────── */
function FieldBlock({ label, hint, children }) {
return (
{label}
{hint && {hint} }
{children}
);
}
function NumberInput({ value, onChange, suffix, step, min }) {
return (
onChange(e.target.value)}
step={step ?? "0.1"} min={min ?? "0"}/>
{suffix && {suffix} }
);
}
function Seg({ value, onChange, options }) {
return (
{options.map(o => (
onChange(o.value)}>
{o.label}
))}
);
}
function ResultBig({ label, value, unit, status }) {
return (
);
}
function MetricCell({ label, value, unit, status }) {
return (
{label}
{value}
{unit && {unit} }
);
}
function StatusBanner({ kind, children }) {
const icon = kind === "ok" ? "ok" : "warn";
return (
{children}
);
}
function MemoryStep({ n, title, isFormula, children }) {
return (
{String(n).padStart(2, "0")}
{title}
{children}
);
}
/* ═══════════════════════════════════════════════════════════════
TABELAS NORMATIVAS EMBUTIDAS
Fonte: ABNT NBR 5419-2:2026 e NBR 5419-3:2026
════════════════════════════════════════════════════════════════ */
/* Tabela 4 / NBR 5419-2 — Risco Tolerável */
const RT = { R1: 1e-5, R3: 1e-4 };
/* Tabela A.1 — Fator de localização CD */
const TAB_CD = [
{ value: "0.25", label: "Rodeada por objetos mais altos" },
{ value: "0.5", label: "Rodeada por objetos da mesma altura" },
{ value: "1", label: "Estrutura isolada" },
{ value: "2", label: "Isolada no topo de colina/morro" },
];
/* Tabela A.2 — Fator de instalação da linha CI */
const TAB_CI = [
{ value: "1", label: "Aérea" },
{ value: "0.5", label: "Enterrada" },
{ value: "0.01", label: "Enterrada em malha aterramento" },
];
/* Tabela A.3 — Fator tipo de linha CT */
const TAB_CT = [
{ value: "1", label: "Energia BT ou sinal" },
{ value: "0.2", label: "Energia AT (com trafo AT/BT)" },
];
/* Tabela A.4 — Fator ambiental CE */
const TAB_CE = [
{ value: "1", label: "Rural" },
{ value: "0.5", label: "Suburbano" },
{ value: "0.1", label: "Urbano" },
{ value: "0.01", label: "Urbano c/ struct. > 20m" },
];
/* Tabela B.1 — PTA (medidas contra tensão toque/passo) */
const TAB_PTA = [
{ value: "1", label: "Nenhuma medida" },
{ value: "0.1", label: "Avisos visíveis" },
{ value: "0.01", label: "Isolação elétrica (toque) ou malha equip. solo (passo)" },
{ value: "0.001",label: "Estrutura metálica / concreto armado c/ descida natural" },
];
/* Tabela B.2 — PB: prob. danos físicos por NP */
const TAB_PB = [
{ value: "1", label: "Sem SPDA" },
{ value: "0.2", label: "SPDA NP IV" },
{ value: "0.1", label: "SPDA NP III" },
{ value: "0.05", label: "SPDA NP II" },
{ value: "0.02", label: "SPDA NP I" },
{ value: "0.01", label: "SPDA NP I + descida natural concreto" },
{ value: "0.001", label: "Cobertura metálica natural + descida natural" },
];
/* Tabela B.3 — PSPD */
const TAB_PSPD = [
{ value: "1", label: "Sem sistema coordenado de DPS" },
{ value: "0.05", label: "DPS NP III-IV" },
{ value: "0.02", label: "DPS NP II" },
{ value: "0.01", label: "DPS NP I" },
];
/* Tabela B.4 — CLD (fator blindagem/aterramento da linha) */
const TAB_CLD = [
{ value: "1", label: "Linha aérea ou subterr. sem blindagem" },
{ value: "0.5", label: "Linha com neutro multiaterrado" },
{ value: "0.3", label: "Subterr. blindada, blindagem não equip." },
{ value: "0.1", label: "Aérea blindada, blindagem não equip." },
{ value: "0", label: "Blindagem equip. ao mesmo barramento" },
];
/* Tabela C.2 — Valores típicos LT, LF, LO */
const TAB_LOSSES = {
"residencial": { LT: 1e-2, LF: 1e-2, LO: 1e-3, label: "Residencial / Outros" },
"industrial": { LT: 1e-2, LF: 2e-2, LO: 1e-3, label: "Industrial / Comercial" },
"publico": { LT: 1e-2, LF: 5e-2, LO: 1e-3, label: "Público (igreja, museu, entret.)" },
"hospital": { LT: 1e-2, LF: 1e-1, LO: 1e-2, label: "Hospital / Hotel / Escola / Cívico" },
"hospital_uti": { LT: 1e-2, LF: 1e-1, LO: 1e-2, label: "Hospital (UTI / Bloco cirúrgico)" },
"explosao": { LT: 1e-2, LF: 1e-1, LO: 1e-1, label: "Risco de explosão" },
};
/* Tabela C.3 — Fator rt (tipo de solo/piso) */
const TAB_RT_SOLO = [
{ value: "0.01", label: "Terra / Concreto" },
{ value: "0.001", label: "Mármore / Cerâmica" },
{ value: "0.0001", label: "Brita / Tapete / Carpete" },
{ value: "0.00001", label: "Asfalto / Linóleo / Madeira" },
];
/* Tabela C.4 — Fator rp (combate incêndio) */
const TAB_RP = [
{ value: "1", label: "Nenhuma providência" },
{ value: "0.5", label: "Extintores / alarme manual / compartimentos" },
{ value: "0.2", label: "Instalações automáticas de supressão ou alarme" },
];
/* Tabela C.5 — Fator rf (risco incêndio/explosão) */
const TAB_RF = [
{ value: "0", label: "Sem incêndio / sem explosão" },
{ value: "0.001",label: "Risco baixo de incêndio (< 400 MJ/m²)" },
{ value: "0.01", label: "Risco normal de incêndio (400–800 MJ/m²)" },
{ value: "0.1", label: "Risco alto de incêndio (> 800 MJ/m²) / explosão Zonas 1,21" },
{ value: "1", label: "Explosão — Zonas 0, 20 e explosivos sólidos" },
];
/* Tabela C.6 — Fator hz (perigo especial) */
const TAB_HZ = [
{ value: "1", label: "Sem perigo especial" },
{ value: "2", label: "Baixo pânico (≤ 2 andares, ≤ 100 pessoas)" },
{ value: "5", label: "Pânico médio (100–1000 pessoas) / Dif. evacuação" },
{ value: "10", label: "Alto pânico (> 1000 pessoas)" },
];
/* Tabela C.7 — Fator rs (tipo estrutura) */
const TAB_RS = [
{ value: "2", label: "Simples (madeira / alvenaria)" },
{ value: "1", label: "Robusta (metálica / concreto armado)" },
];
/* ─── NBR 5419-3: Parâmetros SPDA Externo por NP ─────────────
Fonte: ABNT NBR 5419-3:2026 (Tabelas 1 e 2)
r = raio esfera rolante (m)
mesh = módulo malha subsistema captação (m)
angle_10m = ângulo proteção para h=10m (graus) — franklin
angle_20m = ângulo para h=20m (graus)
angle_30m = ângulo para h=30m (graus)
descidas_max = dist. máx. entre descidas (m)
aterr_tipo = tipo recomendado eletrodo aterramento
*/
const SPDA_EXT = {
"I": { r: 20, mesh: 5, angle_10: 75, angle_20: 70, angle_30: 65, descidas_max: 10, label: "Nível I" },
"II": { r: 30, mesh: 10, angle_10: 70, angle_20: 60, angle_30: 55, descidas_max: 10, label: "Nível II" },
"III": { r: 45, mesh: 15, angle_10: 60, angle_20: 50, angle_30: 45, descidas_max: 15, label: "Nível III" },
"IV": { r: 60, mesh: 20, angle_10: 50, angle_20: 45, angle_30: 43, descidas_max: 20, label: "Nível IV" },
};
/* ─── Seções mínimas condutores SPDA Externo ────────────────── */
const SECAO_MIN_CAPTACAO = {
"I": { Cu: 50, Al: 70, Fe: 50, label: "Cap." },
"II": { Cu: 50, Al: 70, Fe: 50, label: "Cap." },
"III": { Cu: 50, Al: 70, Fe: 50, label: "Cap." },
"IV": { Cu: 50, Al: 70, Fe: 50, label: "Cap." },
};
const SECAO_MIN_DESCIDA = {
"I": { Cu: 50, Al: 70, Fe: 50 },
"II": { Cu: 50, Al: 70, Fe: 50 },
"III": { Cu: 25, Al: 35, Fe: 50 },
"IV": { Cu: 16, Al: 25, Fe: 50 },
};
const SECAO_MIN_ATERR = {
"I": { Cu: 50, Al: 70, Fe: 80 },
"II": { Cu: 50, Al: 70, Fe: 80 },
"III": { Cu: 16, Al: 25, Fe: 50 },
"IV": { Cu: 16, Al: 25, Fe: 50 },
};
/* ─── SPDA Interno / DPS ─────────────────────────────────────── */
/* Correntes Iimp mínimas por NP (kA) — classe I (10/350µs)
Fonte: NBR 5419-1:2026, Anexo B e NBR 5419-4:2026, Anexo C/D
50% vai pro aterramento, 50% retorna pelas linhas
Sistema trifásico: Iimp = 50% × Imax / 4 condutores = 12.5% × Imax */
const DPS_DATA = {
"I": { Imax: 200, Iimp_classe1: 25, In_classe2: 20, Up_max: 2.5, label: "NP I" },
"II": { Imax: 150, Iimp_classe1: 18.75, In_classe2: 15, Up_max: 2.5, label: "NP II" },
"III": { Imax: 100, Iimp_classe1: 12.5, In_classe2: 10, Up_max: 2.5, label: "NP III/IV" },
"IV": { Imax: 100, Iimp_classe1: 12.5, In_classe2: 10, Up_max: 2.5, label: "NP III/IV" },
};
/* ─── Mapa NP → Classe visual ───────────────────────────────── */
const NP_LABEL = { "I": "Classe I", "II": "Classe II", "III": "Classe III", "IV": "Classe IV" };
const NP_COLOR = { "I": "danger", "II": "warn", "III": "warn", "IV": "ok" };
/* ═══════════════════════════════════════════════════════════════
TABELAS DE PROJETO — DIMENSIONAMENTO SPDA
════════════════════════════════════════════════════════════════ */
/* Tabela B.2 → PB por NP (para iteração automática do nível) */
const PB_POR_NP = { "IV": 0.2, "III": 0.1, "II": 0.05, "I": 0.02 };
/* Tabela B.3 → PSPD recomendado por NP */
const PSPD_POR_NP = { "IV": 0.05, "III": 0.05, "II": 0.02, "I": 0.01 };
/* Tabela 7 NBR 5419-3 — Coeficiente ki para distância de segurança s */
const KI_POR_NP = { "I": 0.08, "II": 0.06, "III": 0.04, "IV": 0.04 };
/* Coeficiente km — material da separação (NBR 5419-3 Tab. 6) */
const TAB_KM = [
{ value: "1", label: "Ar" },
{ value: "0.5", label: "Concreto / alvenaria" },
];
/* Tipo de cobertura (define captação preferencial) */
const TAB_COBERTURA = [
{ value: "plana", label: "Plana (malha)" },
{ value: "inclinada", label: "Inclinada (cumeeira+beiral)" },
{ value: "metalica", label: "Metálica (natural)" },
];
/* Tipo de eletrodo de aterramento (override do usuário) */
const TAB_TIPO_ATERR = [
{ value: "A", label: "Tipo A — hastes/radiais" },
{ value: "B", label: "Tipo B — anel/malha" },
];
/* Categoria de instalação (Uw) — IEC 60364-4-44 */
const TAB_UW = [
{ value: "1.5", label: "Cat. I (1,5 kV) — eletrônico" },
{ value: "2.5", label: "Cat. II (2,5 kV) — eletrodomést." },
{ value: "4.0", label: "Cat. III (4,0 kV) — QGBT" },
{ value: "6.0", label: "Cat. IV (6,0 kV) — entrada" },
];
/* Comprimento mínimo l1 da haste (Tipo A) — Fig. 3 NBR 5419-3:2026
Interpolação linear sobre a curva. ρ em Ω·m → l1 em m. */
function calcL1Haste(NP, rho) {
if (rho <= 500) return 5;
if (NP === "III" || NP === "IV") return 5;
if (NP === "II") {
if (rho <= 1500) return 5 + (rho - 500) * 5 / 1000;
if (rho <= 2500) return 10 + (rho - 1500) * 10 / 1000;
return 20 + (rho - 2500) * 10 / 500;
}
// NP I
if (rho <= 1000) return 5 + (rho - 500) * 5 / 500;
if (rho <= 2000) return 10 + (rho - 1000) * 20 / 1000;
return 30 + (rho - 2000) * 40 / 1000;
}
/* Raio médio re do eletrodo Tipo B — Fig. 3 NBR 5419-3:2026 */
function calcReAnel(NP, rho) {
if (rho <= 500) return 5;
if (NP === "III" || NP === "IV") return 5;
if (NP === "II") {
if (rho <= 1000) return 5 + (rho - 500) * 3 / 500;
if (rho <= 2000) return 8 + (rho - 1000) * 7 / 1000;
return 15 + (rho - 2000) * 9 / 1000;
}
// NP I
if (rho <= 1000) return 5 + (rho - 500) * 7 / 500;
if (rho <= 2000) return 12 + (rho - 1000) * 21 / 1000;
return 33 + (rho - 2000) * 47 / 1000;
}
/* ═══════════════════════════════════════════════════════════════
MOTOR DE CÁLCULO CENTRAL — tudo via useMemo
════════════════════════════════════════════════════════════════ */
function calcularSPDA(inp) {
/* ──── 1. Geometria ────────────────────────────────────────── */
const L = parseFloat(inp.L) || 0;
const W = parseFloat(inp.W) || 0;
const H = parseFloat(inp.H) || 0;
if (L <= 0 || W <= 0 || H <= 0) return null;
/* ──── 2. Áreas de exposição (Anexo A NBR 5419-2:2026) ────── */
const H3 = 3 * H;
const AD = L * W + 2 * H3 * (L + W) + Math.PI * H3 * H3;
const AM = 2 * 500 * (L + W) + Math.PI * 500 * 500;
const LL = parseFloat(inp.LL) || 1000;
const AL = 40 * LL;
const AI = 4000 * LL;
/* ──── 3. Eventos perigosos N (Eqs. A.3–A.10) ─────────────── */
const NG = parseFloat(inp.NG) || 0;
const CD = parseFloat(inp.CD) || 1;
const CI = parseFloat(inp.CI) || 1;
const CT = parseFloat(inp.CT) || 1;
const CE = parseFloat(inp.CE) || 1;
const ND = NG * AD * CD * 1e-6;
const NM = NG * AM * 1e-6;
const NL = NG * AL * CI * CT * CE * 1e-6;
const NI = NG * AI * CI * CT * CE * 1e-6;
/* ──── 4. Inputs de probabilidade / perdas ─────────────────── */
const PTA = parseFloat(inp.PTA) || 1;
const PB_user = parseFloat(inp.PB) || 1;
const PSPD_user = parseFloat(inp.PSPD) || 1;
const CLD = parseFloat(inp.CLD) || 1;
const tipoEst = inp.tipoEst || "residencial";
const losses = TAB_LOSSES[tipoEst] || TAB_LOSSES["residencial"];
const rt = parseFloat(inp.rt) || 0.01;
const rp = parseFloat(inp.rp) || 1;
const rf = parseFloat(inp.rf) || 0.01;
const hz = parseFloat(inp.hz) || 1;
const rs = parseFloat(inp.rs) || 1;
const nz_nt = parseFloat(inp.nz_nt) || 1;
const tz_h = parseFloat(inp.tz_h) || 8760;
const f_occ = nz_nt * (tz_h / 8760);
const LA = rt * losses.LT * f_occ * rs;
const LB = rp * rf * hz * losses.LF * f_occ * rs;
const LC = losses.LO * f_occ * rs;
const LU = rt * losses.LT * f_occ * rs;
const LV = rp * rf * hz * losses.LF * f_occ * rs;
const LW = losses.LO * f_occ * rs;
const LM = losses.LO * f_occ * rs;
const LZ = losses.LO * f_occ * rs;
/* ──── 5. Função para calcular R1/R3 dado PB e PSPD ─────────── */
const calcRisks = (PB, PSPD) => {
const PA = PTA * PB;
const PC = PSPD * CLD;
const PM = PSPD;
const PU = PTA;
const PV = PB;
const PW = PSPD * CLD;
const PZ = PSPD;
const RA = ND * PA * LA;
const RB = ND * PB * LB;
const RC = ND * PC * LC;
const RM = NM * PM * LM;
const RU = NL * PU * LU;
const RV = NL * PV * LV;
const RW = NL * PW * LW;
const RZ = NI * PZ * LZ;
const R1 = RA + RB + RC + RM + RU + RV + RW + RZ;
const R3 = RB + RV;
return { PA, PB, PC, PM, PU, PV, PW, PZ, RA, RB, RC, RM, RU, RV, RW, RZ, R1, R3 };
};
/* ──── 6. Risco com proteção atual informada pelo usuário ──── */
const atual = calcRisks(PB_user, PSPD_user);
const needsSpda = atual.R1 > RT.R1 || atual.R3 > RT.R3;
/* ──── 7. ITERAÇÃO DO NP (NBR 5419-2 §5.5) ──────────────────
Tenta NP IV → III → II → I até R1 ≤ RT1 e R3 ≤ RT3. */
let NP = null, recom = null;
if (needsSpda) {
for (const np of ["IV", "III", "II", "I"]) {
const tentativa = calcRisks(PB_POR_NP[np], PSPD_POR_NP[np]);
if (tentativa.R1 <= RT.R1 && tentativa.R3 <= RT.R3) {
NP = np;
recom = tentativa;
break;
}
}
if (!NP) { // Mesmo NP I + medidas máximas não basta
NP = "I";
recom = calcRisks(0.001, 0.01); // SPDA NP I + cobertura natural + DPS NP I
}
}
/* ──── 8. SPDA EXTERNO — Parâmetros NBR 5419-3:2026 ────────── */
const ext = NP ? SPDA_EXT[NP] : null;
let perimetro = 0, nDescidas = 0, espDescidas = 0;
if (ext) {
perimetro = 2 * (L + W);
espDescidas = ext.descidas_max;
/* NBR 5419-3 §5.3.5: mínimo 2 descidas em qualquer NP */
nDescidas = Math.max(2, Math.ceil(perimetro / espDescidas));
}
/* ──── 9. DIMENSIONAMENTO DA CAPTAÇÃO ───────────────────────
Malha sobre toda a cobertura (método mais comum):
n_linhas_X = ceil(W/malha)+1, n_linhas_Y = ceil(L/malha)+1
L_malha = n_linhas_X × L + n_linhas_Y × W */
const cobertura = inp.cobertura || "plana";
const malhaSize = ext ? ext.mesh : 0;
let nLinX = 0, nLinY = 0, LMalha = 0;
if (ext) {
nLinX = Math.ceil(W / malhaSize) + 1;
nLinY = Math.ceil(L / malhaSize) + 1;
LMalha = nLinX * L + nLinY * W;
}
/* Comprimento de descidas (H + 1m de conexão ao aterramento) */
const LDesc = ext ? nDescidas * (H + 1) : 0;
const LCaboTotal = LMalha + LDesc;
/* ──── 10. ATERRAMENTO ────────────────────────────────────── */
const rho = parseFloat(inp.rho_solo) || 300;
/* NP I e II → Tipo B obrigatório; NP III/IV pode ser A ou B */
const tipoAterrUser = inp.tipoAterr || "B";
const tipoAterr = (NP === "I" || NP === "II") ? "B" : tipoAterrUser;
const areaTopo = L * W;
const reCalc = Math.sqrt(areaTopo / Math.PI); // raio equivalente da estrutura
let l1_haste = 0, reReq = 0, anelL = 0, hastesAdicionais = 0;
if (ext) {
if (tipoAterr === "A") {
l1_haste = calcL1Haste(NP, rho);
} else {
reReq = calcReAnel(NP, rho);
anelL = perimetro + 8; // anel 1m afastado da estrutura
if (reReq > reCalc) {
const extra = reReq - reCalc;
hastesAdicionais = Math.max(2, Math.ceil(extra / 5));
}
}
}
/* ──── 11. DISTÂNCIA DE SEGURANÇA s (Eq. NBR 5419-3 §6.3) ───
s = ki × kc × L / km
kc simplificado para malha + anel: kc ≈ 1/n + 0,1 */
const km = parseFloat(inp.km) || 1;
const ki = NP ? KI_POR_NP[NP] : 0;
const kc = ext ? Math.max(0.1, 1 / nDescidas + 0.1) : 0;
const s = ext ? (ki * kc * H) / km : 0;
/* ──── 12. SPDA INTERNO / DPS ─────────────────────────────── */
const dps = NP ? DPS_DATA[NP] : null;
const Uw = parseFloat(inp.Uw) || 2.5;
const Lcabo = parseFloat(inp.Lcabo) || 5;
/* Tensão efetiva no equipamento considerando indução nos cabos:
Lcabo ≤ 10m: Up_ef ≈ Up × (1 + Lcabo/20)
Lcabo > 10m: oscilação dobra → Up_ef ≈ 2 × Up */
const Up_dps = dps ? dps.Up_max : 2.5;
const Up_ef = Lcabo > 10 ? Up_dps * 2 : Up_dps * (1 + Lcabo / 20);
const dpsOk = Up_ef <= Uw;
/* ZPR */
const zpr = {
ZPR0: "Exterior (sem proteção)",
ZPR1: "Interior — SPDA externo como fronteira",
ZPR2: "Sala/ambiente interno — blindagem (malha ≤ 5m)",
ZPR3: "Equipamento / rack — gabinete blindado",
};
return {
/* Áreas e eventos */
AD, AM, AL, AI, ND, NM, NL, NI,
LA, LB, LC, LU, LV, LW, LM, LZ,
/* Análise de risco — estado atual e recomendado */
atual, recom, needsSpda, NP,
PA: atual.PA, PB: PB_user, PC: atual.PC, PM: atual.PM,
PU: atual.PU, PV: atual.PV, PW: atual.PW, PZ: atual.PZ,
RA: atual.RA, RB: atual.RB, RC: atual.RC, RM: atual.RM,
RU: atual.RU, RV: atual.RV, RW: atual.RW, RZ: atual.RZ,
R1: atual.R1, R3: atual.R3,
/* SPDA Externo — parâmetros + projeto */
ext, perimetro, nDescidas, espDescidas,
rEsfera: ext ? ext.r : null, malha: malhaSize,
ang10: ext ? ext.angle_10 : null,
ang20: ext ? ext.angle_20 : null,
ang30: ext ? ext.angle_30 : null,
cobertura, nLinX, nLinY, LMalha, LDesc, LCaboTotal,
/* Aterramento */
tipoAterr, rho, l1_haste, reCalc, reReq, anelL, hastesAdicionais,
/* Distância de segurança */
s, ki, kc, km,
/* SPDA Interno / DPS */
dps, zpr, Uw, Lcabo, Up_dps, Up_ef, dpsOk,
/* Inputs espelhados para memória de cálculo */
L, W, H, NG, CD, LL, H3,
};
}
/* ═══════════════════════════════════════════════════════════════
COMPONENTE PRINCIPAL
════════════════════════════════════════════════════════════════ */
function SPDA() {
const [tab, setTab] = useState("risco");
/* ─── Aba 1: Dados da Estrutura ──────────────────────────── */
const [L, setL] = usePersist("vl_spda_L", "30");
const [W, setW] = usePersist("vl_spda_W", "20");
const [H, setH] = usePersist("vl_spda_H", "10");
const [NG, setNG] = usePersist("vl_spda_NG", "5");
const [CD, setCD] = usePersist("vl_spda_CD", "1");
const [LL, setLL] = usePersist("vl_spda_LL", "500");
const [CI, setCI] = usePersist("vl_spda_CI", "1");
const [CT, setCT] = usePersist("vl_spda_CT", "1");
const [CE, setCE] = usePersist("vl_spda_CE", "0.5");
/* ─── Aba 1: Probabilidades ──────────────────────────────── */
const [PTA, setPTA] = usePersist("vl_spda_PTA", "1");
const [PB, setPB] = usePersist("vl_spda_PB", "1");
const [PSPD, setPSPD] = usePersist("vl_spda_PSPD", "1");
const [CLD, setCLD] = usePersist("vl_spda_CLD", "1");
/* ─── Aba 1: Perdas ─────────────────────────────────────── */
const [tipoEst, setTipoEst] = usePersist("vl_spda_tipo", "residencial");
const [rt, setRt] = usePersist("vl_spda_rt", "0.01");
const [rp, setRp] = usePersist("vl_spda_rp", "1");
const [rf, setRf] = usePersist("vl_spda_rf", "0.01");
const [hz, setHz] = usePersist("vl_spda_hz", "1");
const [rs, setRs] = usePersist("vl_spda_rs", "1");
const [nz_nt, setNzNt] = usePersist("vl_spda_nznt", "1");
const [tz_h, setTzH] = usePersist("vl_spda_tzh", "8760");
/* ─── Aba 2: Inputs de Projeto (SPDA Externo) ──────────── */
const [rho_solo, setRhoSolo] = usePersist("vl_spda_rho", "300");
const [cobertura, setCobertura] = usePersist("vl_spda_cobert", "plana");
const [tipoAterr, setTipoAterr] = usePersist("vl_spda_taterr", "B");
const [km, setKm] = usePersist("vl_spda_km", "1");
/* ─── Aba 3: Inputs de Projeto (SPDA Interno / DPS) ───── */
const [Uw, setUw] = usePersist("vl_spda_Uw", "2.5");
const [Lcabo, setLcabo] = usePersist("vl_spda_Lcabo", "5");
/* ─── Motor ─────────────────────────────────────────────── */
const inp = { L, W, H, NG, CD, LL, CI, CT, CE, PTA, PB, PSPD, CLD,
tipoEst, rt, rp, rf, hz, rs, nz_nt, tz_h,
rho_solo, cobertura, tipoAterr, km, Uw, Lcabo };
const calc = useMemo(() => calcularSPDA(inp), [
L, W, H, NG, CD, LL, CI, CT, CE, PTA, PB, PSPD, CLD,
tipoEst, rt, rp, rf, hz, rs, nz_nt, tz_h,
rho_solo, cobertura, tipoAterr, km, Uw, Lcabo
]);
/* ─── Export hook ───────────────────────────────────────── */
useEffect(() => {
window._VL_lastExport = () => calc ? ({
modulo: "spda-nbr5419-2026",
norma: "ABNT NBR 5419:2026 (Partes 1–4)",
geradoEm: new Date().toLocaleString("pt-BR"),
entradas: {
comprimento_m: L, largura_m: W, altura_m: H,
densidade_ng: NG, fator_cd: CD, comprimento_linha_m: LL,
tipo_estrutura: tipoEst,
resistividade_solo: rho_solo, tipo_cobertura: cobertura,
tipo_aterramento: tipoAterr, material_separacao_km: km,
uw_equipamento_kV: Uw, distancia_dps_equip_m: Lcabo,
},
resultado: {
R1: calc.R1.toExponential(2), R3: calc.R3.toExponential(2),
RT1: "1e-5", RT3: "1e-4",
necessita_spda: calc.needsSpda,
nivel_protecao: calc.NP || "—",
R1_recomendado: calc.recom ? calc.recom.R1.toExponential(2) : "—",
},
spda_externo: calc.ext ? {
raio_esfera_m: calc.rEsfera,
malha: `${calc.malha}×${calc.malha} m`,
n_descidas: calc.nDescidas,
esp_max_descidas_m: calc.espDescidas,
L_malha_captacao_m: calc.LMalha.toFixed(1),
L_descidas_m: calc.LDesc.toFixed(1),
L_cabo_total_m: calc.LCaboTotal.toFixed(1),
tipo_aterramento: calc.tipoAterr,
l1_haste_m: calc.tipoAterr === "A" ? calc.l1_haste.toFixed(1) : "—",
anel_perimetro_m: calc.tipoAterr === "B" ? calc.anelL.toFixed(1) : "—",
re_requerido_m: calc.reReq ? calc.reReq.toFixed(1) : "—",
hastes_adicionais: calc.hastesAdicionais,
distancia_seguranca_s_m: calc.s.toFixed(3),
} : null,
spda_interno: calc.dps ? {
iimp_kA: calc.dps.Iimp_classe1,
in_classe2_kA: calc.dps.In_classe2,
up_max_kV: calc.dps.Up_max,
up_efetivo_kV: calc.Up_ef.toFixed(2),
uw_equipamento_kV: calc.Uw,
coordenacao_ok: calc.dpsOk,
} : null,
}) : null;
return () => { window._VL_lastExport = null; };
}, [calc]);
const fmt = (v, digits = 2) => {
if (!isFinite(v) || v === 0) return "0,00";
if (Math.abs(v) < 1e-3) return v.toExponential(2);
return v.toFixed(digits).replace(".", ",");
};
const fmtExp = (v) => v.toExponential(2).replace("e-", "×10⁻").replace("e+", "×10⁺");
const tabList = [
{ id: "risco", label: "① Análise de Risco", eyebrow: "PARTES 1 & 2" },
{ id: "externo", label: "② SPDA Externo", eyebrow: "PARTE 3" },
{ id: "interno", label: "③ SPDA Interno / DPS",eyebrow: "PARTE 4" },
];
const npStatus = calc?.NP ? NP_COLOR[calc.NP] : "ok";
return (
{/* ── Tabs ────────────────────────────────────────────── */}
{tabList.map(t => (
setTab(t.id)}
style={{
flex: 1, padding: "10px 8px", borderRadius: 7, cursor: "pointer", transition: "all .18s",
border: "1px solid transparent",
background: tab === t.id ? "var(--accent-soft)" : "transparent",
borderColor: tab === t.id ? "oklch(72% 0.18 245 / 0.3)" : "transparent",
color: tab === t.id ? "var(--accent)" : "var(--fg-mute)",
}}>
{t.eyebrow}
{t.label}
))}
{/* ══════════════════════════════════════════════════════
ABA 1 — ANÁLISE DE RISCO (NBR 5419-2)
══════════════════════════════════════════════════════ */}
{tab === "risco" && (
{/* COLUNA ESQUERDA: Entradas */}
{/* Dimensões */}
GEOMETRIA DA ESTRUTURA · NBR 5419-2 ANEXO A
Dimensões
{/* Linha Elétrica */}
LINHA ELÉTRICA CONECTADA · FONTES S3 E S4
Parâmetros da linha
{/* Probabilidades */}
PROBABILIDADES DE DANOS · ANEXO B
Medidas de proteção existentes
{/* Perdas */}
FATORES DE PERDA · ANEXO C
Características da estrutura
({ value: k, label: v.label }))
}/>
{/* COLUNA DIREITA: Resultados */}
{!calc && (
Preencha as dimensões e NG para calcular.
)}
{calc && (
<>
{/* Resultado principal */}
RESULTADO PRINCIPAL · R1 e R3
Riscos calculados
RT.R1 ? "danger" : "ok"}
/>
RT.R1 ? "danger" : "ok"}/>
RT.R3 ? "warn" : "ok"}
/>
RT.R3 ? "warn" : "ok"}/>
{calc.needsSpda
? SPDA NECESSÁRIO — R1 ou R3 acima do tolerável (NBR 5419-2, 5.4.4)
: Sem necessidade de SPDA — Risco dentro do tolerável (NBR 5419-2, 5.4.3)
}
{calc.NP && (
NÍVEL DE PROTEÇÃO RECOMENDADO
NP {calc.NP}
{NP_LABEL[calc.NP]} · ABNT NBR 5419
)}
{/* Componentes de risco detalhados */}
COMPONENTES DE RISCO · TABELA 6
Decomposição de R1
0 ? "ok" : undefined}/>
0 ? "ok" : undefined}/>
0 ? "ok" : undefined}/>
0 ? "ok" : undefined}/>
0 ? "ok" : undefined}/>
0 ? "ok" : undefined}/>
0 ? "ok" : undefined}/>
0 ? "ok" : undefined}/>
{/* Número de eventos */}
EVENTOS PERIGOSOS / ANO · ANEXO A
ND / NM / NL / NI
{/* Memória de cálculo */}
MEMÓRIA DE CÁLCULO
Passo a passo
AD = L×W + 2×(3H)×(L+W) + π×(3H)² = {calc.L}×{calc.W} + 2×{fmt(calc.H3,1)}×{calc.L+calc.W} + π×{fmt(calc.H3,1)}² = {fmt(calc.AD,1)} m²
ND = NG × AD × CD × 10⁻⁶ = {calc.NG} × {fmt(calc.AD,0)} × {calc.CD} × 10⁻⁶ = {calc.ND.toExponential(3)}
NL = NG × AL × CI × CT × CE × 10⁻⁶ = {calc.NG} × {fmt(calc.AL,0)} × {calc.CI} × {calc.CT} × {calc.CE} × 10⁻⁶ = {calc.NL.toExponential(3)}
RB = ND × PB × LB = {calc.ND.toExponential(2)} × {PB} × {fmt(calc.LB,5)} = {calc.RB.toExponential(2)}
R1 = RA+RB+RC+RM+RU+RV+RW+RZ = {fmtExp(calc.R1)} {calc.R1 > RT.R1 ? "⚠ > RT1=10⁻⁵" : "✓ ≤ RT1=10⁻⁵"}
>
)}
)}
{/* ══════════════════════════════════════════════════════
ABA 2 — SPDA EXTERNO (NBR 5419-3)
══════════════════════════════════════════════════════ */}
{tab === "externo" && (
{/* COLUNA ESQUERDA: Inputs de projeto + Parâmetros NP */}
{!calc?.NP && (
Complete a Aba 1 — Análise de Risco para dimensionar o SPDA Externo.
)}
{calc?.NP && (
<>
{/* Inputs de projeto */}
DADOS DO LOCAL · PROJETO
Parâmetros de campo
{/* Parâmetros do NP */}
NÍVEL DE PROTEÇÃO · NBR 5419-3:2026
Parâmetros normativos NP {calc.NP}
NP {calc.NP}
Tabelas 1 e 2 — NBR 5419-3:2026
{/* Seções mínimas */}
SEÇÕES MÍNIMAS · TABELA 6 NBR 5419-3
Materiais dos condutores
{["Subsistema", "Cu (mm²)", "Al (mm²)", "Fe (mm²)"].map(h => (
{h}
))}
{[
{ label: "Captação", s: SECAO_MIN_CAPTACAO[calc.NP] },
{ label: "Descida", s: SECAO_MIN_DESCIDA[calc.NP] },
{ label: "Aterramento", s: SECAO_MIN_ATERR[calc.NP] },
].map(row => (
{row.label}
{row.s.Cu}
{row.s.Al}
{row.s.Fe}
))}
>
)}
{/* COLUNA DIREITA: Dimensionamento do projeto */}
{calc?.NP && (
{/* Captação dimensionada */}
SUBSISTEMA DE CAPTAÇÃO · DIMENSIONADO
Malha sobre a cobertura
Malha {calc.malha}×{calc.malha} m sobre cobertura de {fmt(calc.L)}×{fmt(calc.W)} m → {calc.nLinX} × {fmt(calc.L)} m + {calc.nLinY} × {fmt(calc.W)} m = {fmt(calc.LMalha, 1)} m .
{/* Descidas dimensionadas */}
SUBSISTEMA DE DESCIDA · DIMENSIONADO
Condutores verticais
{calc.nDescidas} descidas distribuídas no perímetro (mín. 2 por NBR 5419-3 §5.3.5) · espaçamento máx. {calc.espDescidas} m · comprimento total {fmt(calc.LDesc, 1)} m (H+1m por descida).
{/* Aterramento dimensionado */}
SUBSISTEMA DE ATERRAMENTO · DIMENSIONADO
Eletrodo Tipo {calc.tipoAterr}
{calc.tipoAterr === "A" ? (
<>
Por descida: 2 hastes verticais de {fmt(calc.l1_haste, 1)} m ou 1 condutor radial de {fmt(calc.l1_haste * 2, 1)} m (Fig. 3 NBR 5419-3:2026).
>
) : (
<>
calc.reCalc ? "warn" : "ok"}/>
0 ? "warn" : "ok"}/>
0 ? "warn" : "ok"}>
{calc.hastesAdicionais > 0
? `Solo de alta resistividade (ρ=${calc.rho} Ω·m): re necessário (${fmt(calc.reReq,1)} m) > re da estrutura (${fmt(calc.reCalc,1)} m). Adicionar ${calc.hastesAdicionais} hastes radiais para complementar.`
: `re da estrutura (${fmt(calc.reCalc,1)} m) ≥ re necessário (${fmt(calc.reReq,1)} m). Apenas o anel atende.`}
Anel a 0,5 m de profundidade, 1 m afastado da estrutura · seção mín. {SECAO_MIN_ATERR[calc.NP].Cu} mm² Cu ou {SECAO_MIN_ATERR[calc.NP].Fe} mm² Fe.
>
)}
{/* Distância de segurança s */}
DISTÂNCIA DE SEGURANÇA · NBR 5419-3 §6.3
Separação SPDA × partes internas
s = ki × kc × L / km = {calc.ki} × {fmt(calc.kc, 2)} × {calc.H} / {calc.km} = {fmt(calc.s, 3)} m . Partes metálicas internas devem estar a no mínimo essa distância das descidas, OU equipotencializadas.
{/* Lista de materiais */}
LISTA DE MATERIAIS (BoM)
Resumo do projeto
{["Item", "Qtd.", "Unid."].map(h => (
{h}
))}
{[
{ item: `Cabo Cu nu ${SECAO_MIN_CAPTACAO[calc.NP].Cu} mm² — captação`, qtd: fmt(calc.LMalha, 1), un: "m" },
{ item: `Cabo Cu nu ${SECAO_MIN_DESCIDA[calc.NP].Cu} mm² — descidas`, qtd: fmt(calc.LDesc, 1), un: "m" },
calc.tipoAterr === "A"
? { item: `Haste de aterramento Cu/Aço L ≥ ${fmt(calc.l1_haste,1)} m`, qtd: calc.nDescidas * 2, un: "un." }
: { item: `Cabo Cu nu ${SECAO_MIN_ATERR[calc.NP].Cu} mm² — anel`, qtd: fmt(calc.anelL, 1), un: "m" },
calc.tipoAterr === "B" && calc.hastesAdicionais > 0
? { item: "Haste radial complementar", qtd: calc.hastesAdicionais, un: "un." } : null,
{ item: "Conector de medição (TAP)", qtd: calc.nDescidas, un: "un." },
{ item: "Caixa de inspeção", qtd: calc.nDescidas, un: "un." },
{ item: "Suporte/isolador para cabo", qtd: Math.ceil(calc.LCaboTotal / 1.5), un: "un." },
].filter(Boolean).map((r, i) => (
{r.item}
{r.qtd}
{r.un}
))}
{/* Memória de cálculo */}
MEMÓRIA DE CÁLCULO
SPDA Externo
P = 2 × (L + W) = 2 × ({calc.L} + {calc.W}) = {fmt(calc.perimetro)} m
n = max(2, ceil(P / d_max)) = max(2, ceil({fmt(calc.perimetro)}/{calc.espDescidas})) = {calc.nDescidas}
L_malha = nx × L + ny × W = {calc.nLinX} × {fmt(calc.L)} + {calc.nLinY} × {fmt(calc.W)} = {fmt(calc.LMalha, 1)} m
{calc.tipoAterr === "A" ? (
Fig. 3 NBR 5419-3 → l1 mín = {fmt(calc.l1_haste, 1)} m por haste
) : (
re necessário = {fmt(calc.reReq, 1)} m · re estrutura = {fmt(calc.reCalc, 1)} m
)}
s = ki × kc × L / km = {calc.ki} × {fmt(calc.kc, 2)} × {calc.H} / {calc.km} = {fmt(calc.s, 3)} m
)}
)}
{/* ══════════════════════════════════════════════════════
ABA 3 — SPDA INTERNO / MPS / DPS (NBR 5419-4)
══════════════════════════════════════════════════════ */}
{tab === "interno" && (
{!calc?.NP && (
Complete a Aba 1 para calcular as MPS e DPS.
)}
{calc?.NP && (
<>
{/* Inputs de projeto DPS */}
DADOS DO EQUIPAMENTO · PROJETO DPS
Parâmetros de instalação
{/* ZPR */}
ZONAS DE PROTEÇÃO CONTRA RAIOS · NBR 5419-4 SEC. 4.3
Conceito de ZPR
{Object.entries(calc.zpr).map(([key, desc]) => (
))}
Todas as linhas elétricas que cruzam fronteiras de ZPR devem ter DPS ou interface isolante instalada no ponto de cruzamento (NBR 5419-4, 5.5).
{/* Equipotencialização */}
EQUIPOTENCIALIZAÇÃO · NBR 5419-4 SEC. 5
Seções mínimas
{["Componente", "Cu (mm²)", "Al (mm²)", "Fe (mm²)"].map(h => (
{h}
))}
{[
{ comp: "Barras de equipotencializ.", Cu: 50, Al: "—", Fe: 50 },
{ comp: "Condut. barra → aterramento", Cu: 50, Al: "—", Fe: 70 },
{ comp: "Condut. entre barras (corrente signif.)", Cu: 16, Al: 25, Fe: 50 },
{ comp: "Partes metálicas internas → barra", Cu: 6, Al: 10, Fe: 16 },
{ comp: "Interlig. DPS classe I", Cu: 16, Al: "—", Fe: "—" },
{ comp: "Interlig. DPS classe II", Cu: 6, Al: "—", Fe: "—" },
{ comp: "Interlig. DPS classe III", Cu: 2.5, Al: "—", Fe: "—" },
].map(r => (
{r.comp}
{r.Cu}
{r.Al}
{r.Fe}
))}
Fonte: NBR 5419-4:2026, Tabela 1. Ligação equipotencial em rede: malha típica ≤ 5 m × 5 m (Seção 5.3).
>
)}
{/* Coluna direita: DPS */}
{calc?.NP && (
{/* DPS — Especificação por NP */}
SISTEMA COORDENADO DE DPS · NBR 5419-4 ANEXO C
Dispositivos de Proteção contra Surtos
{calc.NP === "I"
? "NP I: DPS Classe I obrigatório (10/350µs · Iimp ≥ 25 kA) na entrada do quadro elétrico (fronteira ZPR 0→1)."
: calc.NP === "II"
? "NP II: DPS Classe I na entrada (Iimp ≥ 18,75 kA) + Classe II nos quadros secundários (fronteira ZPR 1→2)."
: "NP III/IV: DPS Classe I/II na entrada (Iimp ≥ 12,5 kA). DPS Classe II nos circuitos terminais."}
{/* Verificação de coordenação */}
VERIFICAÇÃO DE COORDENAÇÃO · NBR 5419-4 §C.2.1
Up efetivo vs. Uw do equipamento
10 ? "warn" : "ok"}/>
{calc.dpsOk
? `Coordenação OK — Up_ef (${fmt(calc.Up_ef,2)} kV) ≤ Uw (${calc.Uw} kV). DPS no quadro protege o equipamento.`
: `INSUFICIENTE — Up_ef (${fmt(calc.Up_ef,2)} kV) > Uw (${calc.Uw} kV). ${calc.Lcabo > 10
? "Instale DPS adicional (Classe III) a menos de 10 m do equipamento."
: "Use DPS com menor Up ou eleve a categoria do equipamento."}`}
{/* Plano de instalação de DPS */}
PLANO DE INSTALAÇÃO DE DPS
Localização e especificação
{["Local", "Classe", "Iimp / In", "Up máx."].map(h => (
{h}
))}
{[
{
local: "Entrada (QGBT) — ZPR 0→1",
classe: "Classe I (B)",
corr: `Iimp ≥ ${calc.dps?.Iimp_classe1.toFixed(2)} kA (10/350µs)`,
up: "≤ 4,0 kV",
},
{
local: "Quadros secundários — ZPR 1→2",
classe: "Classe II (C)",
corr: `In ≥ ${calc.dps?.In_classe2} kA (8/20µs)`,
up: "≤ 2,5 kV",
},
{
local: "Próximo equip. sensível — ZPR 2→3",
classe: "Classe III (D)",
corr: "In ≥ 5 kA (8/20µs)",
up: "≤ 1,5 kV",
},
].map(r => (
{r.local}
{r.classe}
{r.corr}
{r.up}
))}
Cada cruzamento de ZPR exige DPS ou interface isolante (NBR 5419-4 §5.5). Cabo Cu mín. 16 mm² (Cl. I), 6 mm² (Cl. II), 2,5 mm² (Cl. III) — Tabela 1.
MEMÓRIA — DPS
50% no aterramento · 50% pelas linhas → {calc.dps?.Imax} × 0,5 / 4 cond. = {calc.dps?.Iimp_classe1.toFixed(2)} kA por DPS
{calc.Lcabo > 10
? `Lcabo > 10m → oscilação dobra: Up_ef = 2 × ${calc.Up_dps} = `
: `Lcabo ≤ 10m → Up_ef = Up × (1 + Lcabo/20) = ${calc.Up_dps} × (1 + ${calc.Lcabo}/20) = `}
{fmt(calc.Up_ef, 2)} kV
Up_ef ≤ Uw → {fmt(calc.Up_ef, 2)} ≤ {calc.Uw} → {calc.dpsOk ? "ATENDE" : "NÃO ATENDE"}
{/* Blindagem */}
BLINDAGEM MAGNÉTICA E ROTEAMENTO · SEC. 6
Recomendações MPS
{[
{ titulo: "Malha de equipotencialização interna", desc: "Largura típica ≤ 5 m × 5 m. Interligar estruturas metálicas, trilhos de elevador, tubulações, bandejas de cabos, armações de janelas (NBR 5419-4, 5.3)." },
{ titulo: "Roteamento de linhas internas", desc: "Minimizar área dos laços de indução. Instalar cabos de energia e sinal próximos e paralelos. KS3 = 0,01 para cabos no mesmo eletroduto (Tabela B.5 da NBR 5419-2)." },
{ titulo: "Blindagem de cabos externos", desc: "Blindagem interligada ao mesmo barramento de equipotencialização do equipamento → CLD = 0 (linha completamente blindada). Reduz PC significativamente." },
{ titulo: "Interfaces isolantes", desc: "Usar para bloquear surtos conduzidos quando DPS não for suficiente. Fibra óptica (sem metálico) = zero corrente conduzida entre ZPRs (NBR 5419-4, Sec. 8)." },
].map(item => (
▸ {item.titulo}
{item.desc}
))}
{/* Inspeção */}
INSPEÇÃO E MANUTENÇÃO · NBR 5419-4 SEC. 9
Periodicidade recomendada
Inspecionar imediatamente após suspeita de descarga direta na estrutura (contador de raios ou danos visíveis) — NBR 5419-4, 9.2.2-e).
)}
)}
{/* ── Referência normativa ─────────────────────────────── */}
REFERÊNCIA NORMATIVA
ABNT NBR 5419:2026 — Proteção contra descargas atmosféricas
2ª edição · 10.03.2026
PARTE TÍTULO ESCOPO PRINCIPAL MÓDULO
{[
{ parte: "Parte 1", titulo: "Princípios gerais", escopo: "Parâmetros corrente descarga, esfera rolante, gaiola Faraday", modulo: "Base" },
{ parte: "Parte 2", titulo: "Análise de risco", escopo: "Cálculo de R1, R3 · Determinação do NP · Fórmulas ND, NL, probabilidades PX", modulo: "Aba 1" },
{ parte: "Parte 3", titulo: "Danos físicos e perigos à vida", escopo: "SPDA externo: captação, descida, aterramento · Seções mínimas · Distância de segurança", modulo: "Aba 2" },
{ parte: "Parte 4", titulo: "Sistemas elétricos e eletrônicos internos", escopo: "ZPR · Equipotencialização em rede · Sistema coordenado DPS · Seleção Up/Iimp", modulo: "Aba 3" },
].map(r => (
{r.parte}
{r.titulo}
{r.escopo}
{r.modulo}
))}
);
}
window.VL_SPDA = SPDA;