🚀 Auto-deploy: BrainWind atualizado em 13/07/2026 15:00:47
This commit is contained in:
@@ -5,6 +5,7 @@ import { calculateVault } from '../modules/vault';
|
||||
import { calculateDome } from '../modules/dome';
|
||||
import { calculateTrussLattice } from '../modules/truss';
|
||||
import { calculateBridgeDeckForces } from '../modules/bridge';
|
||||
import { calculatePiperack } from '../modules/piperack';
|
||||
import { getWallCpeOfficial, getRoofCpeOfficial } from '../coefficients';
|
||||
|
||||
describe('Audit Simulations for NBR 6123:2023 Models', () => {
|
||||
@@ -175,4 +176,49 @@ describe('Audit Simulations for NBR 6123:2023 Models', () => {
|
||||
}
|
||||
expect(anomalies).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Simulates Piperack model', () => {
|
||||
// Cenário 1: Piperack vazio (sem tubos) com 3 pórticos
|
||||
const res1 = calculatePiperack({
|
||||
width: 6,
|
||||
height: 2,
|
||||
elevation: 10,
|
||||
spacing: 6,
|
||||
numFrames: 3,
|
||||
phiStruct: 0.2,
|
||||
pipes: [],
|
||||
q: 1, // kN/m²
|
||||
});
|
||||
|
||||
expect(res1.phiTotal).toBe(0.2); // Sem tubos, mantém a solidez da estrutura
|
||||
expect(res1.eta).toBe(1.0); // Tabela 28: phi=0.2 e e/h=(6/2)=3. Eta é 1.0.
|
||||
|
||||
// Ca(0.2) = 2.4
|
||||
expect(res1.caFrontal).toBe(2.4);
|
||||
|
||||
// Carga linear pórtico frontal = q * (phi * h) * Ca = 1 * (0.2 * 2) * 2.4 = 0.96 kN/m
|
||||
expect(res1.linearLoadFront).toBeCloseTo(0.96, 2);
|
||||
// Carga linear traseiros = 0.96 * 1.0 = 0.96 kN/m
|
||||
expect(res1.linearLoadBack).toBeCloseTo(0.96, 2);
|
||||
|
||||
// Cenário 2: Piperack super denso com tubulações
|
||||
const res2 = calculatePiperack({
|
||||
width: 6,
|
||||
height: 2,
|
||||
elevation: 10,
|
||||
spacing: 6,
|
||||
numFrames: 3,
|
||||
phiStruct: 0.2,
|
||||
pipes: [{ id: 'p1', diameter: 1.0 }, { id: 'p2', diameter: 1.0 }], // total 2.0m de tubo num pórtico de 2.0m!
|
||||
q: 1,
|
||||
});
|
||||
|
||||
// phi pipes = 2.0 / 2.0 = 1.0
|
||||
// phi raw = 1.2, saturado em 1.0
|
||||
expect(res2.phiTotal).toBe(1.0);
|
||||
// Ca = extrapolado: 1.6 - (1.0-0.6)*1.0 = 1.2
|
||||
expect(res2.caFrontal).toBeCloseTo(1.2, 2);
|
||||
// Eta: para phi=1.0, e/h=3. Interpolado entre 2.0 e 4.0 (onde é 1.0). Então eta = 1.0.
|
||||
expect(res2.eta).toBe(1.0);
|
||||
});
|
||||
|
||||
@@ -47,7 +47,7 @@ export interface BridgeClassificationResult {
|
||||
* Pse = ρ · V_it² / (m · f_v²)
|
||||
*/
|
||||
export function classifyBridge(input: BridgeClassificationInput): BridgeClassificationResult {
|
||||
const { lp, width, massPerLength, fv, v0, s1, deckHeight, category } = input;
|
||||
const { massPerLength, fv, v0, s1, deckHeight, category } = input;
|
||||
const { b, p } = getBridgeParams(deckHeight, category);
|
||||
const vit = 0.65 * v0 * s1 * b * Math.pow(deckHeight / 10, p);
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { getEtaTable28 } from '../nbr-tables/table-28';
|
||||
import { linearInterp1D } from '../log-interp';
|
||||
|
||||
export interface PipeDef {
|
||||
id: string;
|
||||
diameter: number; // diâmetro ou altura do equipamento em metros
|
||||
}
|
||||
|
||||
export interface PiperackInput {
|
||||
width: number; // b: largura do pórtico ortogonal ao vento (m)
|
||||
height: number; // h: altura do pórtico/seção transversal (m)
|
||||
elevation: number; // z: elevação média em relação ao solo (m)
|
||||
spacing: number; // e: distância entre pórticos subsequentes (m)
|
||||
numFrames: number; // n: número de pórticos paralelos na direção do vento
|
||||
phiStruct: number; // φ_str: índice de solidez apenas da estrutura metálica
|
||||
pipes: PipeDef[]; // Lista de tubulações correndo longitudinalmente
|
||||
q: number; // q: Pressão dinâmica na elevação z (kN/m²)
|
||||
}
|
||||
|
||||
export interface PiperackResult {
|
||||
phiTotal: number;
|
||||
caFrontal: number;
|
||||
eta: number;
|
||||
linearLoadFront: number; // kN/m (carga no pórtico 1)
|
||||
linearLoadBack: number; // kN/m (carga nos pórticos 2..n)
|
||||
globalForce: number; // kN (força total na estrutura)
|
||||
}
|
||||
|
||||
/** Tabela de Ca genérica para reticulados planos (faces planas) baseada na solidez φ */
|
||||
const PHI_ARRAY = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6];
|
||||
const CA_FLAT_ARRAY = [2.8, 2.4, 2.0, 1.8, 1.7, 1.6];
|
||||
|
||||
export function calculatePiperack(input: PiperackInput): PiperackResult {
|
||||
const { width, height, spacing, numFrames, phiStruct, pipes, q } = input;
|
||||
|
||||
// 1. Calcular o Índice de Solidez Total (φ)
|
||||
const phiPipes = pipes.reduce((sum, pipe) => sum + pipe.diameter / height, 0);
|
||||
const rawPhi = phiStruct + phiPipes;
|
||||
const phiTotal = Math.min(Math.max(rawPhi, 0.1), 1.0);
|
||||
|
||||
// 2. Coeficiente de Arrasto Frontal (Ca) para o primeiro pórtico
|
||||
let caFrontal = 1.6;
|
||||
if (phiTotal <= 0.6) {
|
||||
caFrontal = linearInterp1D(PHI_ARRAY, CA_FLAT_ARRAY, phiTotal);
|
||||
} else {
|
||||
caFrontal = 1.6 - (phiTotal - 0.6) * 1.0;
|
||||
}
|
||||
|
||||
// 3. Fator de proteção (η) para pórticos a sotavento
|
||||
const eh = spacing / height;
|
||||
const eta = getEtaTable28(phiTotal, eh);
|
||||
|
||||
// 4. Forças Lineares (kN/m)
|
||||
const effectiveAreaPerMeter = phiTotal * height;
|
||||
const linearLoadFront = q * effectiveAreaPerMeter * caFrontal;
|
||||
const linearLoadBack = linearLoadFront * eta;
|
||||
|
||||
// 5. Força Global (kN)
|
||||
const totalLinearLoad = linearLoadFront + (numFrames > 1 ? linearLoadBack * (numFrames - 1) : 0);
|
||||
const globalForce = totalLinearLoad * width;
|
||||
|
||||
return {
|
||||
phiTotal,
|
||||
caFrontal,
|
||||
eta,
|
||||
linearLoadFront,
|
||||
linearLoadBack,
|
||||
globalForce
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { bilinearInterp } from '../bilinear-interp';
|
||||
|
||||
/**
|
||||
* Tabela 28 — Fator de proteção η para estruturas reticuladas múltiplas
|
||||
* (Pórticos paralelos).
|
||||
* NBR 6123:2023 (correspondente à antiga Tabela 8).
|
||||
*
|
||||
* Linhas: Índice de solidez (φ)
|
||||
* Colunas: Distância relativa (e/h ou e/b)
|
||||
*/
|
||||
|
||||
const PHI = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 1.0] as const;
|
||||
const EH = [0.5, 1.0, 2.0, 4.0, 8.0] as const;
|
||||
|
||||
const ETA_DATA: ReadonlyArray<ReadonlyArray<number>> = [
|
||||
// e/h = 0.5, 1.0, 2.0, 4.0, 8.0
|
||||
[ 1.00, 1.00, 1.00, 1.00, 1.00 ], // φ = 0.1
|
||||
[ 0.90, 1.00, 1.00, 1.00, 1.00 ], // φ = 0.2
|
||||
[ 0.80, 0.90, 1.00, 1.00, 1.00 ], // φ = 0.3
|
||||
[ 0.60, 0.80, 1.00, 1.00, 1.00 ], // φ = 0.4
|
||||
[ 0.45, 0.70, 1.00, 1.00, 1.00 ], // φ = 0.5
|
||||
[ 0.30, 0.60, 1.00, 1.00, 1.00 ], // φ = 0.6
|
||||
[ 0.30, 0.60, 1.00, 1.00, 1.00 ], // φ = 1.0 (mantido para saturação)
|
||||
];
|
||||
|
||||
/**
|
||||
* Retorna o fator de proteção η.
|
||||
* @param phi Índice de solidez (0 a 1)
|
||||
* @param eh Razão entre a distância e a menor dimensão ortogonal ao vento (e/h ou e/b)
|
||||
*/
|
||||
export function getEtaTable28(phi: number, eh: number): number {
|
||||
return bilinearInterp(
|
||||
{
|
||||
xs: EH as unknown as number[],
|
||||
ys: PHI as unknown as number[],
|
||||
values: ETA_DATA as unknown as (readonly number[])[],
|
||||
},
|
||||
eh,
|
||||
phi
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user