562 lines
24 KiB
TypeScript
562 lines
24 KiB
TypeScript
import type { AuditScenario, ModuleType } from './types';
|
||
import { MODULE_LABELS } from './types';
|
||
import {
|
||
determineStructureClass,
|
||
calculateS2,
|
||
calculateVk,
|
||
calculateDynamicPressure,
|
||
type TerrainCategory,
|
||
} from '../wind-kernel';
|
||
import { computeCpiSimplified, clampCpi, type PermeabilityCase } from '../internal-pressure';
|
||
import { getWallCpeOfficial, getRoofCpeOfficial } from '../coefficients';
|
||
import { getDragCoefficient } from '../drag';
|
||
import { calculateFriction } from '../friction';
|
||
import { getCpeCylinderProfile } from '../nbr-tables/table-13';
|
||
import { calculateSign, type SignInput } from '../nbr-tables/table-23';
|
||
import { evaluateComfort } from '../comfort';
|
||
import { classifyBridge, type BridgeClassificationInput } from '../modules/bridge';
|
||
import { calculateTower } from '../modules/tower';
|
||
import { calculatePiperack, type PiperackInput } from '../modules/piperack';
|
||
|
||
function calcWind(
|
||
v0: number, s1: number, s3: number,
|
||
cat: TerrainCategory, dim: number, z: number,
|
||
) {
|
||
const structCls = determineStructureClass(dim);
|
||
const s2 = calculateS2(z, cat, structCls);
|
||
const vk = calculateVk(v0, s1, s2, s3);
|
||
const q = calculateDynamicPressure(vk);
|
||
return { structClass: structCls, s2, vk, q };
|
||
}
|
||
|
||
function makeScenario(
|
||
id: string,
|
||
module: ModuleType,
|
||
description: string,
|
||
inputs: Record<string, unknown>,
|
||
intermediates: Record<string, number>,
|
||
outputs: Record<string, unknown>,
|
||
ranges: Record<string, { min: number; max: number }>,
|
||
diagramType: string,
|
||
diagramProps: Record<string, unknown>,
|
||
nbrSection: string,
|
||
isBibliographic?: boolean,
|
||
bibliografiaReferencia?: string,
|
||
enunciadoParafraseado?: string,
|
||
): AuditScenario {
|
||
return {
|
||
id,
|
||
module,
|
||
moduleLabel: MODULE_LABELS[module]?.['pt-BR'] ?? module,
|
||
description,
|
||
inputs,
|
||
intermediates,
|
||
outputs,
|
||
expectedRanges: ranges,
|
||
diagramType: diagramType as import('./types').DiagramType,
|
||
diagramProps,
|
||
nbrSection,
|
||
isBibliographic,
|
||
bibliografiaReferencia,
|
||
enunciadoParafraseado,
|
||
};
|
||
}
|
||
|
||
function galpaoScenario(
|
||
id: string,
|
||
description: string,
|
||
v0: number, s1: number, s3: number,
|
||
cat: TerrainCategory,
|
||
width: number, length: number, height: number, roofPitch: number,
|
||
windAngle: 0 | 90,
|
||
permCase: PermeabilityCase, cpiRatio: number,
|
||
isBibliographic?: boolean,
|
||
bibliografiaReferencia?: string,
|
||
enunciadoParafraseado?: string,
|
||
): AuditScenario {
|
||
const dim = Math.max(width, length);
|
||
const { s2, vk, q } = calcWind(v0, s1, s3, cat, dim, height);
|
||
const cpiRaw = computeCpiSimplified({ case: permCase, ratio: cpiRatio, windAngle });
|
||
const cpi = clampCpi(cpiRaw);
|
||
const wallCpe = getWallCpeOfficial(length, width, height, windAngle);
|
||
const roofCpe = getRoofCpeOfficial(length, width, height, roofPitch, windAngle);
|
||
const drag = getDragCoefficient(width, length, height, 'low');
|
||
const fric = calculateFriction({ roughness: 'smooth', length, height, width, roofPitch, q, windAngle });
|
||
const pressures: Record<string, number> = {};
|
||
for (const [zone, cpe] of Object.entries({ ...wallCpe, ...roofCpe })) {
|
||
if (typeof cpe === 'number') pressures[zone] = Number((q * (cpe - cpi)).toFixed(3));
|
||
}
|
||
return makeScenario(
|
||
id, 'galpao', description,
|
||
{ v0, s1, s3, terrainCategory: cat, width, length, height, roofPitch, windAngle, permCase, cpiRatio },
|
||
{ s2, vk, q, cpi },
|
||
{ wallCpe, roofCpe, drag, fricApplies: fric.applies ? 1 : 0, fricForce: fric.forceKN, pressures },
|
||
{
|
||
q: { min: 0.3, max: 5.0 },
|
||
cpi: { min: -0.9, max: 0.9 },
|
||
s2: { min: 0.5, max: 1.5 },
|
||
'wallCpe.A': { min: -1.5, max: 0.5 },
|
||
'wallCpe.C': { min: 0.0, max: 1.5 },
|
||
'roofCpe.E': { min: -2.5, max: 0.5 },
|
||
drag: { min: 0.9, max: 2.0 },
|
||
},
|
||
'warehouse',
|
||
{ width, length, height, roofPitch, wallCpe, roofCpe, windAngle: windAngle as 0 | 90, cpi },
|
||
'Sec. 6.1',
|
||
isBibliographic,
|
||
bibliografiaReferencia,
|
||
enunciadoParafraseado,
|
||
);
|
||
}
|
||
|
||
function cylinderScenario(
|
||
id: string,
|
||
description: string,
|
||
v0: number, s1: number, s3: number,
|
||
cat: TerrainCategory,
|
||
d: number, h: number,
|
||
surface: 'rough' | 'smooth',
|
||
endType: 'closed' | 'open-top' | 'open-bottom' | 'open-both',
|
||
_windAngle: 0 | 90,
|
||
isBibliographic?: boolean,
|
||
bibliografiaReferencia?: string,
|
||
enunciadoParafraseado?: string,
|
||
): AuditScenario {
|
||
const { s2, vk, q } = calcWind(v0, s1, s3, cat, Math.max(d, h), h);
|
||
const re = 70000 * vk * d;
|
||
const hOverD = h / d;
|
||
let cpiVal: number;
|
||
if (endType === 'open-top') cpiVal = hOverD >= 0.3 ? -0.8 : -0.5;
|
||
else if (endType === 'open-bottom') cpiVal = -0.5;
|
||
else if (endType === 'open-both') cpiVal = -0.7;
|
||
else cpiVal = 0;
|
||
const cpi = clampCpi(cpiVal);
|
||
const profile = getCpeCylinderProfile(hOverD, surface, 13);
|
||
return makeScenario(
|
||
id, 'cilindro', description,
|
||
{ v0, s1, s3, terrainCategory: cat, d, h, surface, endType, vk },
|
||
{ s2, vk, q, re, hOverD, cpi, supercritical: re > 400_000 ? 1 : 0 },
|
||
{ profile: profile.map(p => ({ angle: p.angle, cpe: p.cpe })), cpi },
|
||
{
|
||
q: { min: 0.3, max: 5.0 },
|
||
cpi: { min: -0.9, max: 0.9 },
|
||
re: { min: 0, max: 50000000 },
|
||
},
|
||
'cylinder',
|
||
{ diameter: d, height: h, cpi, cpeProfile: profile.map(p => ({ angle: p.angle, cpe: p.cpe })) },
|
||
'Sec. 6.2.1',
|
||
isBibliographic,
|
||
bibliografiaReferencia,
|
||
enunciadoParafraseado,
|
||
);
|
||
}
|
||
|
||
export function generateAllScenarios(): AuditScenario[] {
|
||
const scenarios: AuditScenario[] = [];
|
||
|
||
// 1. BLESSMANN CASES
|
||
scenarios.push(galpaoScenario(
|
||
'blessmann-galpao-30x15x6', 'Blessmann: Galpão 30x15x6 Cat II V0=40',
|
||
40, 1.0, 1.0, 'II', 15, 30, 6, 10, 0, 'four-equally-permeable', 1.0,
|
||
true,
|
||
'Pfeil & Pfeil, Estruturas de Aço, 8ª ed. (Exemplo clássico de galpão adaptado e Blessmann Cap. 5, Exemplo 5.1)',
|
||
'Cenário de Validação estruturado com base nos parâmetros físicos típicos da literatura. O teste valida a ação do vento transversal (0°) em pórtico de duas águas simétrico (30x15x6 m) em terreno plano.'
|
||
));
|
||
|
||
{
|
||
const v0 = 40, s1 = 1.0, s3 = 1.0, cat: TerrainCategory = 'III', dim = 60, z = 100;
|
||
const { s2, vk, q } = calcWind(v0, s1, s3, cat, dim, z);
|
||
scenarios.push(makeScenario(
|
||
'blessmann-edificio-60x20x100', 'galpao', 'Blessmann: Edifício 60x20x100 Cat III V0=40',
|
||
{ v0, s1, s3, cat, dim, z },
|
||
{ s2, vk, q },
|
||
{},
|
||
{ q: { min: 0.8, max: 2.0 }, s2: { min: 1.0, max: 1.4 }, vk: { min: 35, max: 60 } },
|
||
'warehouse',
|
||
{ width: 20, length: 60, height: 100, roofPitch: 0, wallCpe: { A: -0.9, B: -0.6, C: 0.7, D: -0.5 }, roofCpe: { E: -0.8, F: -0.4, G: 0.2, H: -0.3 }, windAngle: 0 as const, cpi: 0 },
|
||
'Sec. 5.3',
|
||
true,
|
||
'Blessmann, J. O Vento na Engenharia Estrutural, Cap. 9',
|
||
'Validação de edifício alto paralelepipédico de seção retangular 20x60m e altura 100m. Avaliação da variação do fator S2 com a altura (z) até 100m.'
|
||
));
|
||
}
|
||
|
||
scenarios.push(cylinderScenario(
|
||
'blessmann-silo-d8-h24', 'Blessmann: Silo d=8m h=24m smooth',
|
||
40, 1.0, 1.0, 'II', 8, 24, 'smooth', 'open-top', 0,
|
||
true,
|
||
'NBR 6123 Tabela 13 (Cilindros) / Blessmann Cap. 6',
|
||
'Cilindro liso com diâmetro de 8m e altura de 24m. Validação do Número de Reynolds (Re) no topo do silo e coeficientes de pressão externa nos ângulos característicos.'
|
||
));
|
||
|
||
for (const z of [5, 10, 20, 50, 100]) {
|
||
const { s2 } = calcWind(40, 1.0, 1.0, 'II', 30, z);
|
||
scenarios.push(makeScenario(
|
||
`blessmann-s2-z${z}`, 'galpao', `Blessmann: S2 em z=${z}m Cat II`,
|
||
{ v0: 40, s1: 1.0, s3: 1.0, cat: 'II', dim: 30, z },
|
||
{ s2 },
|
||
{ s2 },
|
||
{ s2: { min: 0.5, max: 1.5 } },
|
||
'warehouse',
|
||
{ width: 15, length: 30, height: 10, roofPitch: 10, wallCpe: { A: -0.8, B: -0.5, C: 0.7, D: -0.4 }, roofCpe: { E: -0.8, F: -0.4, G: 0.2, H: -0.3, I: -0.5, J: 0 }, windAngle: 0 as const, cpi: 0 },
|
||
'Sec. 5.3',
|
||
));
|
||
}
|
||
|
||
{
|
||
const S3_VALS: Record<string, number> = { '1': 1.11, '2': 1.06, '3': 1.00, '4': 0.95, '5': 0.83 };
|
||
for (const [grp, s3val] of Object.entries(S3_VALS)) {
|
||
scenarios.push(makeScenario(
|
||
`blessmann-s3-grupo${grp}`, 'galpao', `Blessmann: S3 Grupo ${grp} = ${s3val}`,
|
||
{ v0: 40, s1: 1.0, s3: s3val, cat: 'II', dim: 30, z: 10 },
|
||
{ s3: s3val },
|
||
{ s3: s3val },
|
||
{ s3: { min: 0.7, max: 1.2 } },
|
||
'warehouse',
|
||
{ width: 15, length: 30, height: 10, roofPitch: 10, wallCpe: { A: -0.8, B: -0.5, C: 0.7, D: -0.4 }, roofCpe: { E: -0.8, F: -0.4, G: 0.2, H: -0.3, I: -0.5, J: 0 }, windAngle: 0 as const, cpi: 0 },
|
||
'Sec. 5.4',
|
||
));
|
||
}
|
||
}
|
||
|
||
{
|
||
const bridgeInput: BridgeClassificationInput = {
|
||
lp: 120, width: 14, massPerLength: 18000, fv: 0.6,
|
||
v0: 40, s1: 1.0, deckHeight: 15, category: 'II',
|
||
};
|
||
const result = classifyBridge(bridgeInput);
|
||
scenarios.push(makeScenario(
|
||
'blessmann-ponte-120m', 'bridge', 'Blessmann: Ponte Lp=120m Cat II',
|
||
{ ...bridgeInput },
|
||
{ pse: result.pse, vit: result.vit },
|
||
{ pse: result.pse, vit: result.vit, bridgeClass: result.bridgeClass },
|
||
{ pse: { min: 0.01, max: 2.0 }, vit: { min: 15, max: 50 } },
|
||
'bridge',
|
||
{ lp: 120, width: 14, deckHeight: 15, heg: 1.5, cx: 1.2, fxPerLength: 16.8, fzPerLength: -2.1 },
|
||
'Sec. 11.2',
|
||
true,
|
||
'Blessmann, J. NBR 6123 Aplicações, Cap. 11',
|
||
'Determinação das forças de arrasto e sustentação para uma ponte de 120m de vão em terreno Categoria II. O tabuleiro possui 14m de largura, 15m de altura livre e massa de 18 ton/m. Avalia-se as reações estáticas aerodinâmicas.'
|
||
));
|
||
}
|
||
|
||
scenarios.push(cylinderScenario(
|
||
'blessmann-chamine-d1.5-h30', 'Blessmann: Chaminé d=1.5m h=30m rough',
|
||
40, 1.0, 1.0, 'II', 1.5, 30, 'rough', 'closed', 0,
|
||
true,
|
||
'Blessmann, J. Ação do Vento em Chaminés, Exercício Resolvido',
|
||
'Cálculo das pressões normais em uma chaminé circular de 30 metros de altura por 1.5m de diâmetro (superfície rugosa, topo fechado).'
|
||
));
|
||
|
||
{
|
||
const signInput: SignInput = { length: 6, height: 2, groundClearance: 3, alpha: 90, hasEndPlates: false };
|
||
const q = 0.981;
|
||
const signResult = calculateSign(signInput, q);
|
||
scenarios.push(makeScenario(
|
||
'blessmann-placa-6x2', 'sign', 'Blessmann: Placa 6x2m',
|
||
{ length: 6, height: 2, groundClearance: 3, alpha: 90, hasEndPlates: false, q },
|
||
{ cf: signResult.cf },
|
||
{ cf: signResult.cf, forceKN: signResult.forceKN },
|
||
{ cf: { min: 0.8, max: 2.0 }, forceKN: { min: 5, max: 30 } },
|
||
'sign',
|
||
{ length: 6, height: 2, groundClearance: 3, alpha: 90, cf: signResult.cf, forceKN: signResult.forceKN },
|
||
'Sec. 7.1',
|
||
true,
|
||
'Pfeil & Pfeil, Estruturas de Aço, Cap. Placas e Muros',
|
||
'Determinação da força de arrasto do vento a 90° em uma placa retangular isolada de 6x2m. A placa encontra-se içada com uma folga em relação ao solo de 3 metros, sem placas de extremidade (endplates).'
|
||
));
|
||
}
|
||
|
||
// 2. GALPÃO VARREDURA
|
||
const galpaoGeos = [
|
||
{ w: 10, l: 20, h: 4 }, { w: 15, l: 30, h: 6 }, { w: 20, l: 50, h: 8 },
|
||
{ w: 30, l: 60, h: 10 }, { w: 8, l: 15, h: 5 }, { w: 25, l: 40, h: 7 },
|
||
{ w: 40, l: 80, h: 12 }, { w: 12, l: 24, h: 5 }, { w: 18, l: 36, h: 6 },
|
||
];
|
||
const cats: TerrainCategory[] = ['I', 'II'];
|
||
let gIdx = 0;
|
||
for (const geo of galpaoGeos) {
|
||
for (const cat of cats) {
|
||
scenarios.push(galpaoScenario(
|
||
`galpao-varredura-${gIdx++}`, `Galpão ${geo.w}x${geo.l}x${geo.h} Cat ${cat}`,
|
||
40, 1.0, 1.0, cat, geo.w, geo.l, geo.h, 10, 0, 'four-equally-permeable', 1.0,
|
||
));
|
||
}
|
||
}
|
||
|
||
// 3. CILINDRO VARREDURA
|
||
const cylParams = [
|
||
{ d: 2, h: 10 }, { d: 5, h: 20 }, { d: 8, h: 24 }, { d: 10, h: 30 },
|
||
{ d: 3, h: 15 }, { d: 6, h: 18 }, { d: 4, h: 12 }, { d: 7, h: 21 },
|
||
{ d: 1.2, h: 8 }, { d: 9, h: 27 },
|
||
];
|
||
let cIdx = 0;
|
||
for (const p of cylParams) {
|
||
scenarios.push(cylinderScenario(
|
||
`cilindro-varredura-${cIdx++}`, `Cilindro d=${p.d} h=${p.h} smooth closed`,
|
||
40, 1.0, 1.0, 'II', p.d, p.h, 'smooth', 'closed', 0,
|
||
));
|
||
}
|
||
|
||
// 4. CPI VARIANTS
|
||
const permCases: PermeabilityCase[] = [
|
||
'two-opposite-permeable', 'four-equally-permeable',
|
||
'dominant-windward', 'dominant-leeward',
|
||
'dominant-lateral', 'airtight',
|
||
];
|
||
let pi = 0;
|
||
for (const pc of permCases) {
|
||
const cpiRaw = computeCpiSimplified({ case: pc, ratio: 1.0, windAngle: 0 });
|
||
const cpi = clampCpi(cpiRaw);
|
||
scenarios.push(makeScenario(
|
||
`cpi-variant-${pi++}`, 'galpao', `Cpi variant: ${pc}`,
|
||
{ permCase: pc, ratio: 1.0, windAngle: 0 },
|
||
{ cpi, cpiRaw },
|
||
{ cpi },
|
||
{ cpi: { min: -0.9, max: 0.9 } },
|
||
'warehouse',
|
||
{ width: 15, length: 30, height: 6, roofPitch: 10, wallCpe: { A: -0.8, B: -0.5, C: 0.7, D: -0.4 }, roofCpe: { E: -0.8, F: -0.4, G: 0.2, H: -0.3, I: -0.5, J: 0 }, windAngle: 0 as const, cpi },
|
||
'Sec. 6.3',
|
||
));
|
||
}
|
||
|
||
// 5. EDGE CASES
|
||
scenarios.push(cylinderScenario(
|
||
'edge-cilindro-hd-extremo', 'Edge: Cilindro h/d=0.3',
|
||
40, 1.0, 1.0, 'II', 10, 3, 'smooth', 'closed', 0,
|
||
));
|
||
|
||
{
|
||
const { s2, vk, q } = calcWind(40, 1.0, 1.0, 'V', 30, 5);
|
||
scenarios.push(makeScenario(
|
||
'edge-s2-min-z5-catV', 'galpao', 'Edge: S2 mínimo z=5m Cat V',
|
||
{ v0: 40, s1: 1.0, s3: 1.0, cat: 'V', dim: 30, z: 5 },
|
||
{ s2, vk, q },
|
||
{ s2, vk, q },
|
||
{ s2: { min: 0.5, max: 0.8 }, q: { min: 0.3, max: 1.5 } },
|
||
'warehouse',
|
||
{ width: 15, length: 30, height: 5, roofPitch: 10, wallCpe: { A: -0.8, B: -0.5, C: 0.7, D: -0.4 }, roofCpe: { E: -0.8, F: -0.4, G: 0.2, H: -0.3, I: -0.5, J: 0 }, windAngle: 0 as const, cpi: 0 },
|
||
'Sec. 5.3',
|
||
));
|
||
}
|
||
|
||
{
|
||
const { s2, vk, q } = calcWind(40, 1.0, 1.0, 'IV', 30, 500);
|
||
scenarios.push(makeScenario(
|
||
'edge-s2-saturado-z500-catIV', 'galpao', 'Edge: S2 saturado z=500m Cat IV',
|
||
{ v0: 40, s1: 1.0, s3: 1.0, cat: 'IV', dim: 30, z: 500 },
|
||
{ s2, vk, q },
|
||
{ s2, vk, q },
|
||
{ s2: { min: 1.2, max: 1.4 } },
|
||
'warehouse',
|
||
{ width: 15, length: 30, height: 10, roofPitch: 10, wallCpe: { A: -0.8, B: -0.5, C: 0.7, D: -0.4 }, roofCpe: { E: -0.8, F: -0.4, G: 0.2, H: -0.3, I: -0.5, J: 0 }, windAngle: 0 as const, cpi: 0 },
|
||
'Sec. 5.3',
|
||
));
|
||
}
|
||
|
||
for (const dim of [20, 21, 50, 51]) {
|
||
const { structClass, s2 } = calcWind(40, 1.0, 1.0, 'II', dim, 10);
|
||
scenarios.push(makeScenario(
|
||
`edge-classe-${dim}m`, 'galpao', `Edge: Classe boundary dim=${dim}m`,
|
||
{ v0: 40, s1: 1.0, s3: 1.0, cat: 'II', dim, z: 10 },
|
||
{ s2 },
|
||
{ s2, structClass },
|
||
{ s2: { min: 0.8, max: 1.3 } },
|
||
'warehouse',
|
||
{ width: 15, length: 30, height: 10, roofPitch: 10, wallCpe: { A: -0.8, B: -0.5, C: 0.7, D: -0.4 }, roofCpe: { E: -0.8, F: -0.4, G: 0.2, H: -0.3, I: -0.5, J: 0 }, windAngle: 0 as const, cpi: 0 },
|
||
'Sec. 5.3.2',
|
||
));
|
||
}
|
||
|
||
for (const v0 of [20, 50, 60]) {
|
||
const { vk, q } = calcWind(v0, 1.0, 1.0, 'II', 30, 10);
|
||
scenarios.push(makeScenario(
|
||
`edge-v0-${v0}`, 'galpao', `Edge: V0=${v0} m/s`,
|
||
{ v0, s1: 1.0, s3: 1.0, cat: 'II', dim: 30, z: 10 },
|
||
{ vk, q },
|
||
{ vk, q },
|
||
{ q: { min: 0.1, max: 3.0 }, vk: { min: 15, max: 65 } },
|
||
'warehouse',
|
||
{ width: 15, length: 30, height: 10, roofPitch: 10, wallCpe: { A: -0.8, B: -0.5, C: 0.7, D: -0.4 }, roofCpe: { E: -0.8, F: -0.4, G: 0.2, H: -0.3, I: -0.5, J: 0 }, windAngle: 0 as const, cpi: 0 },
|
||
'Sec. 4.2',
|
||
));
|
||
}
|
||
|
||
// 6. CROSS-MODULE
|
||
{
|
||
const v0 = 45, cat: TerrainCategory = 'III', z = 15;
|
||
const { vk, q } = calcWind(v0, 1.0, 1.0, cat, 25, z);
|
||
scenarios.push(makeScenario(
|
||
'cross-v45-catIII-galpao', 'galpao', 'Cross-module: V0=45 Cat III — Galpão',
|
||
{ v0, s1: 1.0, s3: 1.0, cat, dim: 25, z },
|
||
{ vk, q },
|
||
{ vk, q },
|
||
{ q: { min: 0.8, max: 2.0 } },
|
||
'warehouse',
|
||
{ width: 15, length: 25, height: 8, roofPitch: 12, wallCpe: { A: -0.8, B: -0.5, C: 0.7, D: -0.4 }, roofCpe: { E: -0.8, F: -0.4, G: 0.2, H: -0.3, I: -0.5, J: 0 }, windAngle: 0 as const, cpi: 0 },
|
||
'Sec. 6.1',
|
||
));
|
||
|
||
const cylWind = calcWind(v0, 1.0, 1.0, cat, 20, 20); // cil h=20, max(d,h)=20, z=20
|
||
scenarios.push(makeScenario(
|
||
'cross-v45-catIII-cilindro', 'cilindro', 'Cross-module: V0=45 Cat III — Cilindro',
|
||
{ v0, s1: 1.0, s3: 1.0, cat, d: 5, h: 20 },
|
||
{ vk: cylWind.vk, q: cylWind.q },
|
||
{ vk: cylWind.vk, q: cylWind.q },
|
||
{ q: { min: 0.8, max: 2.0 } },
|
||
'cylinder',
|
||
{ diameter: 5, height: 20, cpi: -0.8, cpeProfile: [{ angle: 0, cpe: 1.0 }, { angle: 90, cpe: -1.2 }] },
|
||
'Sec. 6.2.1',
|
||
));
|
||
}
|
||
|
||
// 7. DINÂMICA
|
||
const comfortFreqs = [0.1, 0.2, 0.5, 0.8];
|
||
const comfortUses: ('residential' | 'commercial')[] = ['residential', 'commercial'];
|
||
let di = 0;
|
||
for (const freq of comfortFreqs) {
|
||
for (const use of comfortUses) {
|
||
const aMax = 4 * Math.PI * Math.PI * freq * freq * 0.01;
|
||
const result = evaluateComfort({ freq, aMax, use });
|
||
scenarios.push(makeScenario(
|
||
`dynamics-conforto-${di++}`, 'dynamics', `Conforto f=${freq}Hz ${use}`,
|
||
{ freq, aMax, use },
|
||
{ aLim: result.aLim, ratio: result.ratio },
|
||
{ ok: result.ok ? 1 : 0, aLim: result.aLim },
|
||
{ aLim: { min: 0, max: 0.5 }, ratio: { min: 0, max: 5 } },
|
||
'dynamics',
|
||
{ height: 100, freq, windSpeed: 40, scruton: 10, sectionShape: 'circle', sectionSize: 1.5, showVortexStreet: true, showModeShape: true },
|
||
'Sec. 9.6',
|
||
));
|
||
}
|
||
}
|
||
|
||
// 8. PONTES VARREDURA
|
||
const bridgeParams = [
|
||
{ lp: 80, width: 10, m: 12000, fv: 0.4, z: 10 },
|
||
{ lp: 120, width: 14, m: 18000, fv: 0.6, z: 15 },
|
||
{ lp: 200, width: 20, m: 30000, fv: 0.3, z: 20 },
|
||
{ lp: 60, width: 8, m: 8000, fv: 0.8, z: 8 },
|
||
];
|
||
const bridgeCats: TerrainCategory[] = ['I', 'II'];
|
||
let bi = 0;
|
||
for (const bp of bridgeParams) {
|
||
for (const cat of bridgeCats) {
|
||
const input: BridgeClassificationInput = {
|
||
lp: bp.lp, width: bp.width, massPerLength: bp.m, fv: bp.fv,
|
||
v0: 40, s1: 1.0, deckHeight: bp.z, category: cat,
|
||
};
|
||
const result = classifyBridge(input);
|
||
scenarios.push(makeScenario(
|
||
`ponte-varredura-${bi++}`, 'bridge', `Ponte Lp=${bp.lp}m Cat ${cat}`,
|
||
{ ...input },
|
||
{ pse: result.pse, vit: result.vit },
|
||
{ pse: result.pse, vit: result.vit, bridgeClass: result.bridgeClass },
|
||
{ pse: { min: 0.001, max: 3.0 }, vit: { min: 10, max: 60 } },
|
||
'bridge',
|
||
{ lp: bp.lp, width: bp.width, deckHeight: bp.z, heg: 1.5, cx: 1.2, fxPerLength: 14.1, fzPerLength: -1.8 },
|
||
'Sec. 11.2',
|
||
));
|
||
}
|
||
}
|
||
|
||
// 9. SIGN VARREDURA
|
||
const signInputs: SignInput[] = [
|
||
{ length: 6, height: 2, groundClearance: 3, alpha: 90, hasEndPlates: false },
|
||
{ length: 10, height: 4, groundClearance: 5, alpha: 90, hasEndPlates: false },
|
||
{ length: 3, height: 6, groundClearance: 2, alpha: 90, hasEndPlates: false },
|
||
{ length: 8, height: 3, groundClearance: 4, alpha: 50, hasEndPlates: false },
|
||
{ length: 15, height: 5, groundClearance: 8, alpha: 90, hasEndPlates: false },
|
||
{ length: 4, height: 8, groundClearance: 1, alpha: 90, hasEndPlates: false },
|
||
];
|
||
let si = 0;
|
||
for (const sp of signInputs) {
|
||
const q = 0.981;
|
||
const result = calculateSign(sp, q);
|
||
scenarios.push(makeScenario(
|
||
`sign-varredura-${si++}`, 'sign', `Sign: ${sp.length}x${sp.height}m α=${sp.alpha}°`,
|
||
{ ...sp, q },
|
||
{ cf: result.cf },
|
||
{ cf: result.cf, forceKN: result.forceKN },
|
||
{ cf: { min: 0.8, max: 2.5 }, forceKN: { min: 1, max: 50 } },
|
||
'sign',
|
||
{ length: sp.length, height: sp.height, groundClearance: sp.groundClearance, alpha: sp.alpha, cf: result.cf, forceKN: result.forceKN },
|
||
'Sec. 7.1',
|
||
));
|
||
}
|
||
|
||
// 10. TOWERS VARREDURA
|
||
const towerParams = [
|
||
{ section: 'square' as const, phi: 0.2, alphaWind: 0 as const },
|
||
{ section: 'square' as const, phi: 0.4, alphaWind: 45 as const },
|
||
{ section: 'triangular' as const, phi: 0.3, alphaWind: 0 as const },
|
||
{ section: 'square' as const, phi: 0.5, alphaWind: 45 as const },
|
||
{ section: 'triangular' as const, phi: 0.25, alphaWind: 0 as const },
|
||
{ section: 'square' as const, phi: 0.6, alphaWind: 90 as const },
|
||
{ section: 'triangular' as const, phi: 0.35, alphaWind: 45 as const },
|
||
{ section: 'square' as const, phi: 0.45, alphaWind: 0 as const },
|
||
];
|
||
let ti = 0;
|
||
for (const tp of towerParams) {
|
||
const q = 0.981;
|
||
const aFace = 5 * 30; // baseWidth = 5, height = 30
|
||
const tr = calculateTower({
|
||
section: tp.section,
|
||
barType: 'flat',
|
||
phi: tp.phi,
|
||
aFace,
|
||
alphaWind: tp.alphaWind,
|
||
q,
|
||
});
|
||
scenarios.push(makeScenario(
|
||
`tower-varredura-${ti++}`, 'tower', `Torre ${tp.section} phi=${tp.phi} α=${tp.alphaWind}°`,
|
||
{ ...tp, q },
|
||
{ phi: tp.phi },
|
||
{ ca: tr.caEff, forceKN: tr.forceKN },
|
||
{ phi: { min: 0.05, max: 1.0 }, ca: { min: 0.6, max: 3.6 }, forceKN: { min: 1, max: 1000 } },
|
||
'tower',
|
||
{ section: tp.section, baseWidth: 5, height: 30, panels: 6, phi: tp.phi, alphaWind: tp.alphaWind, forceKN: tr.forceKN },
|
||
'Sec. 8.5',
|
||
));
|
||
}
|
||
|
||
// 11. PIPERACK VARREDURA
|
||
const piperackParams: Array<{ name: string; input: Omit<PiperackInput, 'q'> }> = [
|
||
{ name: 'Pipe-1', input: { width: 10, height: 4, elevation: 6, spacing: 5, numFrames: 3, phiStruct: 0.15, pipes: [{ id: 'p1', diameter: 1.0, elevationOffset: 4 }] } },
|
||
{ name: 'Pipe-2', input: { width: 12, height: 5, elevation: 8, spacing: 6, numFrames: 4, phiStruct: 0.20, pipes: [{ id: 'p1', diameter: 0.5, elevationOffset: 5 }, { id: 'p2', diameter: 0.5, elevationOffset: 5 }] } },
|
||
{ name: 'Pipe-3', input: { width: 8, height: 3, elevation: 5, spacing: 4, numFrames: 2, phiStruct: 0.10, pipes: [] } },
|
||
{ name: 'Pipe-4', input: { width: 15, height: 6, elevation: 10, spacing: 6, numFrames: 5, phiStruct: 0.30, pipes: [{ id: 'p1', diameter: 1.5, elevationOffset: 6 }] } },
|
||
{ name: 'Pipe-5', input: { width: 20, height: 8, elevation: 12, spacing: 8, numFrames: 3, phiStruct: 0.25, pipes: [{ id: 'p1', diameter: 2.0, elevationOffset: 8 }, { id: 'p2', diameter: 1.0, elevationOffset: 6 }] } },
|
||
{ name: 'Pipe-6', input: { width: 10, height: 5, elevation: 7, spacing: 3, numFrames: 6, phiStruct: 0.40, pipes: [{ id: 'p1', diameter: 0.8, elevationOffset: 5 }] } },
|
||
{ name: 'Pipe-7', input: { width: 6, height: 4, elevation: 4, spacing: 4, numFrames: 2, phiStruct: 0.50, pipes: [{ id: 'p1', diameter: 1.0, elevationOffset: 4 }] } },
|
||
{ name: 'Pipe-8', input: { width: 25, height: 10, elevation: 15, spacing: 10, numFrames: 4, phiStruct: 0.20, pipes: [{ id: 'p1', diameter: 3.0, elevationOffset: 10 }] } },
|
||
];
|
||
let pi_idx = 0;
|
||
for (const pp of piperackParams) {
|
||
const q = 0.981;
|
||
const input: PiperackInput = { ...pp.input, q };
|
||
const tr = calculatePiperack(input);
|
||
scenarios.push(makeScenario(
|
||
`piperack-varredura-${pi_idx++}`, 'piperack', `Piperack ${pp.name}: w=${input.width} h=${input.height} n=${input.numFrames}`,
|
||
{ ...input },
|
||
{ phiTotal: tr.phiTotal, effectiveElevation: tr.effectiveElevation },
|
||
{ caFrontal: tr.caFrontal, eta: tr.eta, globalForce: tr.globalForce },
|
||
{ phiTotal: { min: 0.1, max: 1.0 }, caFrontal: { min: 1.6, max: 3.0 }, eta: { min: 0.3, max: 1.0 }, globalForce: { min: 1, max: 5000 } },
|
||
'warehouse', // Usamos warehouse como fallback visual ou você pode ter um piperack
|
||
{ width: input.width, height: input.height, length: input.spacing * input.numFrames }, // Props mínimas
|
||
'Sec. 8.4', // Estruturas reticuladas
|
||
));
|
||
}
|
||
|
||
return scenarios;
|
||
}
|
||
|
||
export function groupScenariosByModule(scenarios: AuditScenario[]): Record<ModuleType, AuditScenario[]> {
|
||
const groups: Partial<Record<ModuleType, AuditScenario[]>> = {};
|
||
for (const s of scenarios) {
|
||
if (!groups[s.module]) groups[s.module] = [];
|
||
groups[s.module]!.push(s);
|
||
}
|
||
return groups as Record<ModuleType, AuditScenario[]>;
|
||
}
|