Files
BrainWind/app/src/lib/wind-direction.ts
T

102 lines
3.1 KiB
TypeScript

/**
* Mudança de rugosidade do terreno — NBR 6123:2023, sec. 5.5
*
* Implementa o perfil de velocidades (fatores S₂) na zona de transição
* entre duas categorias de rugosidade.
*/
import type { TerrainCategory } from './wind-kernel';
import { Z0_BY_CATEGORY, ZG_BY_CATEGORY } from './nbr-tables/table-5';
export interface RoughnessTransitionInput {
/** Categoria da edificação (terreno próximo) */
near: TerrainCategory;
/** Categoria do terreno a barlavento (mais afastado) */
far: TerrainCategory;
/** Distância horizontal x a partir da linha de mudança (m) */
distance: number;
/** Altura z acima do terreno (m) */
height: number;
}
export interface RoughnessTransitionResult {
z1: number;
z2: number;
/** S₂ equivalente na posição (x, z) */
s2: number;
}
/** Calcula A conforme sec. 5.5.1 ou 5.5.2 */
function coefficientA(z02: number, z01: number, case2to1: boolean): number {
if (case2to1) {
return 0.63 - 0.03 * Math.log(z02 / z01);
}
return 0.73 - 0.03 * Math.log(z02 / z01);
}
/**
* Cálculo da altura z₁ (topo da camada de transição — perfil da
* categoria mais próxima). Caso 5.5.1: z_{02} > z_{01}.
*/
function computeZ1(z02: number, z01: number, x: number, case2to1: boolean): number {
const A = coefficientA(z02, z01, case2to1);
return A * z02 * Math.pow(x / z02, 0.8);
}
/** Caso 5.5.2: z_{02} < z_{01} — calcula z₁ diferente */
function computeZ1Case5_5_2(z02: number, x: number): number {
const A = 0.73 - 0.03 * Math.log(z02 / 0.07);
return A * z02 * Math.pow(x / z02, 0.8);
}
/** S₂ da categoria para uma altura z (usa tabela de parâmetros) */
function s2FromCategory(cat: TerrainCategory, z: number, params: Map<TerrainCategory, { b: number; p: number; fr: number }>): number {
const p = params.get(cat);
if (!p) throw new Error(`Categoria inválida: ${cat}`);
const zEff = Math.max(5, Math.min(z, ZG_BY_CATEGORY[cat]));
return p.b * p.fr * Math.pow(zEff / 10, p.p);
}
/**
* Calcula S₂ efetivo considerando mudança de rugosidade.
* Requer tabela de parâmetros já construída pelo chamador (vide
* `getDefaultParams` em wind-kernel).
*/
export function applyRoughnessChange(
input: RoughnessTransitionInput,
params: Map<TerrainCategory, { b: number; p: number; fr: number }>,
): RoughnessTransitionResult {
const { near, far, distance, height } = input;
const z01 = Z0_BY_CATEGORY[near];
const z02 = Z0_BY_CATEGORY[far];
const x = Math.max(0, distance);
const case2to1 = z02 > z01;
let z1: number;
if (case2to1) {
z1 = computeZ1(z02, z01, x, true);
} else {
z1 = computeZ1Case5_5_2(z02, x);
}
const s2Far = s2FromCategory(far, height, params);
const s2Near = s2FromCategory(near, height, params);
let s2: number;
if (height >= z1) {
s2 = s2Far;
} else {
const z2 = case2to1
? 0.36 * z02 * Math.pow(x / z02, 0.8)
: 0.36 * z02 * Math.pow(x / z02, 0.8);
if (height <= z2) {
s2 = s2Near;
} else {
const t = (height - z2) / (z1 - z2);
s2 = s2Near + t * (s2Far - s2Near);
}
}
return { z1, z2: 0.36 * Z0_BY_CATEGORY[far] * Math.pow(x / Z0_BY_CATEGORY[far], 0.8), s2 };
}