🚀 Auto-deploy: BrainWind atualizado em 27/07/2026 23:01:47
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
/**
|
||||
* Módulo de Ações de Vento para Abrigos, Marquises e Pórticos com Fechamentos Parciais
|
||||
* NBR 6123:2023 — Combinação de Coberturas Isoladas (Tab. 24 e 25), Pressão Interna e Aberturas (sec. 6.3)
|
||||
* e Placas / Painéis (Tab. 23).
|
||||
* e Placas / Painéis (Tab. 23), suportando incidências do vento a 0°, 45° e 90°.
|
||||
*/
|
||||
|
||||
export type ShelterCondition = 'cond1' | 'cond2' | 'cond3' | 'cond4';
|
||||
export type OpeningPosition = 'top' | 'bottom' | 'distributed';
|
||||
export type ShelterWindAngle = 0 | 45 | 90;
|
||||
|
||||
export interface ShelterInput {
|
||||
/** Condição topológica padrão (1=Estanque 3 lados, 2=Fundo fechado, 3=Túnel, 4=Aberto) */
|
||||
@@ -28,9 +29,13 @@ export interface ShelterInput {
|
||||
rightClosure: number;
|
||||
/** Posição da abertura na parede de fundo ('top' | 'bottom' | 'distributed') */
|
||||
openingPos: OpeningPosition;
|
||||
/** Ângulo de incidência do vento (0° = frontal, 45° = oblíquo, 90° = lateral) */
|
||||
windAngle?: ShelterWindAngle;
|
||||
}
|
||||
|
||||
export interface ShelterResult {
|
||||
/** Ângulo de incidência do vento calculado (0, 45 ou 90) */
|
||||
windAngle: ShelterWindAngle;
|
||||
/** Coeficiente de pressão superior da cobertura (Ce superior) */
|
||||
cpeTop: number;
|
||||
/** Coeficiente de pressão interna efetivo sob a cobertura (Cpi inferior) */
|
||||
@@ -61,14 +66,25 @@ export interface ShelterResult {
|
||||
/** Carga vertical/horizontal na viga em balanço (kN/m) */
|
||||
beamLoad: number;
|
||||
};
|
||||
/** Alívio percentual de arrancamento obtido pela posição da abertura no topo (%) */
|
||||
/** Alívio percentual de arrancamento obtido pela posição da abertura (%) */
|
||||
reliefPercentage: number;
|
||||
/** Explicação normativa do comportamento */
|
||||
statusText: string;
|
||||
}
|
||||
|
||||
export interface ShelterEnvelopeResult {
|
||||
res0: ShelterResult;
|
||||
res45: ShelterResult;
|
||||
res90: ShelterResult;
|
||||
criticalUp: { angle: ShelterWindAngle; value: number };
|
||||
criticalFx: { angle: ShelterWindAngle; value: number };
|
||||
criticalFy: { angle: ShelterWindAngle; value: number };
|
||||
criticalColumnLoad: { angle: ShelterWindAngle; value: number };
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcula os coeficientes e forças para Abrigos, Marquises e Pórticos.
|
||||
* Calcula os coeficientes e forças para Abrigos, Marquises e Pórticos
|
||||
* considerando incidência do vento a 0° (frontal), 45° (oblíquo) ou 90° (lateral).
|
||||
*/
|
||||
export function calculateShelterWind(input: ShelterInput, q: number): ShelterResult {
|
||||
const {
|
||||
@@ -84,6 +100,8 @@ export function calculateShelterWind(input: ShelterInput, q: number): ShelterRes
|
||||
openingPos,
|
||||
} = input;
|
||||
|
||||
const windAngle: ShelterWindAngle = input.windAngle ?? 0;
|
||||
|
||||
const areaRoof = depth * width;
|
||||
const areaBack = width * height;
|
||||
const areaSide = depth * height;
|
||||
@@ -92,26 +110,58 @@ export function calculateShelterWind(input: ShelterInput, q: number): ShelterRes
|
||||
const bClose = Math.max(0, Math.min(100, backClosure)) / 100;
|
||||
const lClose = Math.max(0, Math.min(100, leftClosure)) / 100;
|
||||
const rClose = Math.max(0, Math.min(100, rightClosure)) / 100;
|
||||
const sideAvgClose = (lClose + rClose) / 2;
|
||||
|
||||
// 1. Coeficiente externo superior na cobertura (Tabela 24 para uma água)
|
||||
// Varia entre -0.8 a -1.5 de sucção de barlavento, dependendo de theta
|
||||
const cpeTop = theta <= 5 ? -1.0 : theta <= 15 ? -1.2 : -1.4;
|
||||
// 1. Coeficiente externo superior na cobertura (Ce superior)
|
||||
// Varia conforme o ângulo de incidência (0°, 45° ou 90°)
|
||||
let cpeTop = -1.0;
|
||||
if (windAngle === 0) {
|
||||
// Frontal: -1.0 a -1.4 de sucção de barlavento, dependendo de theta
|
||||
cpeTop = theta <= 5 ? -1.0 : theta <= 15 ? -1.2 : -1.4;
|
||||
} else if (windAngle === 45) {
|
||||
// Oblíquo 45°: forte vórtice cônico de canto nas bordas de barlavento (aumento de sucção em ~20% a 30%)
|
||||
cpeTop = theta <= 5 ? -1.3 : theta <= 15 ? -1.4 : -1.6;
|
||||
} else {
|
||||
// Lateral 90°: escoamento transversal sobre a cobertura, sucção constante por descolamento
|
||||
cpeTop = -1.1;
|
||||
}
|
||||
|
||||
// 2. Cálculo do Cpi sob a cobertura (efeito estagnação da parede traseira / laterais)
|
||||
// Quando o fundo é fechado e o ar é contido, cpiBottom torna-se positivo e elevado
|
||||
let baseCpi = 0.0;
|
||||
if (condition === 'cond1') {
|
||||
// 3 lados fechados -> grande armadilha aerodinâmica (+0.75 estanque)
|
||||
baseCpi = 0.75 * bClose * ((lClose + rClose) / 2);
|
||||
} else if (condition === 'cond2') {
|
||||
// Fundo fechado, lados abertos -> sobrepressão moderada (+0.45)
|
||||
baseCpi = 0.45 * bClose;
|
||||
} else if (condition === 'cond3') {
|
||||
// Túnel (fundo aberto, lados fechados) -> efeito Venturi / sucção inferior (-0.35)
|
||||
baseCpi = -0.35 * ((lClose + rClose) / 2);
|
||||
if (windAngle === 0) {
|
||||
if (condition === 'cond1') {
|
||||
baseCpi = 0.75 * bClose * sideAvgClose;
|
||||
} else if (condition === 'cond2') {
|
||||
baseCpi = 0.45 * bClose;
|
||||
} else if (condition === 'cond3') {
|
||||
baseCpi = -0.35 * sideAvgClose;
|
||||
} else {
|
||||
baseCpi = -0.1 * bClose;
|
||||
}
|
||||
} else if (windAngle === 45) {
|
||||
if (condition === 'cond1') {
|
||||
baseCpi = 0.68 * bClose * sideAvgClose;
|
||||
} else if (condition === 'cond2') {
|
||||
baseCpi = 0.35 * bClose;
|
||||
} else if (condition === 'cond3') {
|
||||
baseCpi = 0.25 * sideAvgClose;
|
||||
} else {
|
||||
baseCpi = -0.1 * bClose;
|
||||
}
|
||||
} else {
|
||||
// Condição 4: Livre (apenas obstrução leve ou nula)
|
||||
baseCpi = -0.1 * bClose;
|
||||
// windAngle === 90
|
||||
if (condition === 'cond1') {
|
||||
// Estanque 3 lados: vento a 90° incide sobre lateral fechada com fundo fechado retendo o ar
|
||||
baseCpi = 0.65 * sideAvgClose * bClose;
|
||||
} else if (condition === 'cond2') {
|
||||
// Fundo fechado, mas laterais ABERTAS: vento a 90° passa livremente sob o telhado -> sucção
|
||||
baseCpi = -0.25;
|
||||
} else if (condition === 'cond3') {
|
||||
// Túnel (laterais fechadas, fundo aberto): lateral se comporta como parede frontal
|
||||
baseCpi = 0.50 * sideAvgClose;
|
||||
} else {
|
||||
baseCpi = -0.1 * bClose;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Efeito da posição da abertura (Topo vs Base vs Distribuída)
|
||||
@@ -120,67 +170,101 @@ export function calculateShelterWind(input: ShelterInput, q: number): ShelterRes
|
||||
|
||||
if (backRange && bClose > 0.05 && bClose < 0.99) {
|
||||
const topGapPct = Math.max(0, Math.min(100, 100 - backRange[1])) / 100;
|
||||
// O alívio de arrancamento da cobertura depende diretamente da abertura no topo (fresta superior)
|
||||
reliefPercentage = Math.round(35 * Math.min(1, topGapPct * 2.5));
|
||||
effectiveCpi = baseCpi * (1 - reliefPercentage / 100);
|
||||
} else if (bClose > 0.05 && bClose < 0.99) {
|
||||
const openRatio = 1 - bClose; // fração aberta da parede de fundo
|
||||
const openRatio = 1 - bClose;
|
||||
if (openingPos === 'top') {
|
||||
// Fresta superior permite o escape de sobrepressão debaixo do telhado
|
||||
// Reduz o Cpi positivo de forma proporcional ao tamanho da fresta no topo
|
||||
reliefPercentage = Math.round(35 * openRatio);
|
||||
effectiveCpi = baseCpi * (1 - reliefPercentage / 100);
|
||||
} else if (openingPos === 'bottom') {
|
||||
// Abertura inferior: o ar flui embaixo, mantendo pressão sob o telhado
|
||||
reliefPercentage = 0;
|
||||
effectiveCpi = baseCpi * 1.05;
|
||||
} else {
|
||||
// Distribuída: alívio proporcional à permeabilidade aberta
|
||||
reliefPercentage = Math.round(18 * openRatio);
|
||||
effectiveCpi = baseCpi * (1 - reliefPercentage / 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Cnf vertical total na cobertura (arrancamento para cima = negativo)
|
||||
// Sugção para cima (FzUp): cpeTop (-) - effectiveCpi (+) => força de sucção efetiva total
|
||||
const cnfUp = cpeTop - Math.max(0, effectiveCpi);
|
||||
const cnfDown = 0.2 - Math.min(0, effectiveCpi); // Carregamento descendente
|
||||
const cnfDown = 0.2 - Math.min(0, effectiveCpi);
|
||||
|
||||
// 4. Coeficientes em paredes de fundo e laterais (Tabela 23 / 6)
|
||||
const cfBack = 1.3 * bClose;
|
||||
const cfSide = 0.9 * ((lClose + rClose) / 2);
|
||||
// 4. Coeficientes horizontais em paredes de fundo e laterais
|
||||
let cfBack = 1.3 * bClose;
|
||||
let cfSide = 0.9 * sideAvgClose;
|
||||
|
||||
if (windAngle === 45) {
|
||||
cfBack = 1.05 * bClose;
|
||||
cfSide = 1.05 * sideAvgClose;
|
||||
} else if (windAngle === 90) {
|
||||
// Vento a 90°: as paredes laterais viram superfície frontal barlavento
|
||||
cfSide = 1.3 * sideAvgClose;
|
||||
cfBack = 0.7 * bClose;
|
||||
}
|
||||
|
||||
// 5. Forças globais (kN)
|
||||
const fzUp = Math.abs(cnfUp) * q * areaRoof;
|
||||
const fzDown = Math.abs(cnfDown) * q * areaRoof;
|
||||
const fxBack = cfBack * q * areaBack;
|
||||
const fySide = cfSide * q * (areaSide * 2);
|
||||
const fFriction = 0.04 * q * areaRoof; // Atrito no telhado
|
||||
const fFriction = 0.04 * q * areaRoof;
|
||||
|
||||
// 6. Carga linear em pórticos (assumindo 2 pórticos principais como no croqui, ex: vão b/2)
|
||||
// Carga no pilar (kN/m)
|
||||
// 6. Carga linear em pórticos (2 pórticos principais)
|
||||
const numPorticos = 2;
|
||||
const columnLoad = (fxBack / height / numPorticos) + (q * 1.8 * 0.15); // arrasto do tubo 150x150
|
||||
let columnLoad = 0;
|
||||
if (windAngle === 0) {
|
||||
columnLoad = (fxBack / height / numPorticos) + (q * 1.8 * 0.15);
|
||||
} else if (windAngle === 45) {
|
||||
const combForce = Math.sqrt(fxBack * fxBack + fySide * fySide);
|
||||
columnLoad = (combForce / height / numPorticos) + (q * 1.8 * 0.15);
|
||||
} else {
|
||||
// Em 90°, a carga transversal principal atua nas paredes laterais (Fy)
|
||||
columnLoad = (fySide / height / numPorticos) + (q * 1.8 * 0.15);
|
||||
}
|
||||
|
||||
const beamLoad = (fzUp / depth / numPorticos);
|
||||
|
||||
// Texto explicativo do regime
|
||||
// Texto explicativo por condição e ângulo
|
||||
let statusText = '';
|
||||
const anglePrefix =
|
||||
windAngle === 0
|
||||
? 'Vento Frontal (0°)'
|
||||
: windAngle === 45
|
||||
? 'Vento Oblíquo (45°)'
|
||||
: 'Vento Transversal / Lateral (90°)';
|
||||
|
||||
switch (condition) {
|
||||
case 'cond1':
|
||||
statusText = 'Abrigo estanque em 3 lados (Condição 1): Forte acúmulo de pressão positiva sob a cobertura, gerando pico crítico de arrancamento vertical e empuxo na parede de fundo.';
|
||||
statusText = `${anglePrefix} — Abrigo estanque em 3 lados (Condição 1): Forte retenção de pressão positiva sob a cobertura. ${
|
||||
windAngle === 45
|
||||
? 'O vento a 45° gera pico crítico de arrancamento devido a vórtices de canto em barlavento.'
|
||||
: windAngle === 90
|
||||
? 'O vento a 90° incide diretamente na parede lateral com acúmulo contra o fundo fechado.'
|
||||
: 'Gera pico crítico de arrancamento vertical e empuxo na parede de fundo.'
|
||||
}`;
|
||||
break;
|
||||
case 'cond2':
|
||||
statusText = 'Abrigo com fundo fechado e laterais abertas (Condição 2): A parede de fundo atua como anteparo, elevando a sobrepressão sob o balanço junto à testeira traseira.';
|
||||
statusText = `${anglePrefix} — Abrigo com fundo fechado e laterais abertas (Condição 2): ${
|
||||
windAngle === 90
|
||||
? 'Com incidência a 90°, o vento escoa livremente entre as laterais abertas sem gerar sobrepressão (+Cpi) sob o telhado.'
|
||||
: 'A parede de fundo atua como anteparo, elevando a sobrepressão sob o balanço.'
|
||||
}`;
|
||||
break;
|
||||
case 'cond3':
|
||||
statusText = 'Abrigo tipo túnel (Condição 3): Abertura frontal e traseira provocam efeito Venturi, com sucção interna na cobertura e paredes laterais sujeitas a arrasto.';
|
||||
statusText = `${anglePrefix} — Abrigo tipo túnel (Condição 3): ${
|
||||
windAngle === 90
|
||||
? 'Com vento a 90°, o fechamento lateral comporta-se como anteparo barlavento, gerando empuxo transversal elevado.'
|
||||
: 'Aberturas frontal e traseira provocam efeito Venturi, com sucção interna na cobertura.'
|
||||
}`;
|
||||
break;
|
||||
case 'cond4':
|
||||
statusText = 'Cobertura livre / aberta em 4 lados (Condição 4): Escoamento desimpedido ao redor e sob o telhado, seguindo os coeficientes líquidos da Tabela 24 da NBR 6123.';
|
||||
statusText = `${anglePrefix} — Cobertura livre (Condição 4): Escoamento desimpedido ao redor e sob o telhado, com ações orientadas pela Tabela 24 da NBR 6123.`;
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
windAngle,
|
||||
cpeTop: Number(cpeTop.toFixed(2)),
|
||||
cpiBottom: Number(effectiveCpi.toFixed(2)),
|
||||
cnfVertical: Number(cnfUp.toFixed(2)),
|
||||
@@ -201,3 +285,38 @@ export function calculateShelterWind(input: ShelterInput, q: number): ShelterRes
|
||||
statusText,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcula a envoltória e os resultados para as 3 direções de vento (0°, 45° e 90°).
|
||||
*/
|
||||
export function calculateShelterWindAllAngles(input: ShelterInput, q: number): ShelterEnvelopeResult {
|
||||
const res0 = calculateShelterWind({ ...input, windAngle: 0 }, q);
|
||||
const res45 = calculateShelterWind({ ...input, windAngle: 45 }, q);
|
||||
const res90 = calculateShelterWind({ ...input, windAngle: 90 }, q);
|
||||
|
||||
const angles: ShelterWindAngle[] = [0, 45, 90];
|
||||
const results = [res0, res45, res90];
|
||||
|
||||
let critUpIdx = 0;
|
||||
let critFxIdx = 0;
|
||||
let critFyIdx = 0;
|
||||
let critColIdx = 0;
|
||||
|
||||
for (let i = 1; i < 3; i++) {
|
||||
if (results[i].forces.fzUp > results[critUpIdx].forces.fzUp) critUpIdx = i;
|
||||
if (results[i].forces.fxBack > results[critFxIdx].forces.fxBack) critFxIdx = i;
|
||||
if (results[i].forces.fySide > results[critFyIdx].forces.fySide) critFyIdx = i;
|
||||
if (results[i].lineLoads.columnLoad > results[critColIdx].lineLoads.columnLoad) critColIdx = i;
|
||||
}
|
||||
|
||||
return {
|
||||
res0,
|
||||
res45,
|
||||
res90,
|
||||
criticalUp: { angle: angles[critUpIdx], value: results[critUpIdx].forces.fzUp },
|
||||
criticalFx: { angle: angles[critFxIdx], value: results[critFxIdx].forces.fxBack },
|
||||
criticalFy: { angle: angles[critFyIdx], value: results[critFyIdx].forces.fySide },
|
||||
criticalColumnLoad: { angle: angles[critColIdx], value: results[critColIdx].lineLoads.columnLoad },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user