33. Calcular presión, empuje, caudal y balances con JavaScript

Convertiremos relaciones físicas en funciones comprobables, con entradas explícitas, unidades coherentes y resultados que distinguen estado, flujo y acumulación.

33.1 De los datos a los cálculos

En el tema anterior construimos representaciones para fluidos y partículas. Ahora agregaremos funciones físicas sin mezclar cálculo, interfaz y almacenamiento.

Una función bien diseñada declara qué recibe, qué devuelve, qué unidades utiliza y bajo qué hipótesis es válida. Esa claridad permite reutilizarla en una calculadora, una prueba o una simulación.

33.2 Funciones puras

Una función pura depende solo de sus argumentos y no modifica estado externo. Para los mismos datos produce el mismo resultado.

function presionHidrostatica({ densidad, profundidad, g = 9.81 }) {
  return densidad * g * profundidad;
}

console.log(presionHidrostatica({ densidad: 1000, profundidad: 5 }));

Esta función puede probarse sin HTML, canvas ni variables globales.

33.3 Contratos y unidades

Adoptaremos entradas y salidas en SI. El contrato de presión anterior es:

entrada: ρ [kg/m³], h [m], g [m/s²] · salida: Δp [Pa]

Los nombres completos reducen ambigüedad. Una función pública debe documentar si una presión es absoluta o manométrica y si un caudal es volumétrico o másico.

33.4 Validación reutilizable

function exigirPositivo(nombre, valor, permiteCero = false) {
  const valido = Number.isFinite(valor) && (permiteCero ? valor >= 0 : valor > 0);
  if (!valido) throw new RangeError(`${nombre} debe ser ${permiteCero ? "no negativo" : "positivo"}`);
  return valor;
}

console.log(exigirPositivo("densidad", 1000));
console.log(exigirPositivo("profundidad", 0, true));

Fallar temprano señala el origen del problema en vez de propagar NaN por cálculos posteriores.

33.5 Presión manométrica y absoluta

function presionesEnProfundidad({ densidad, profundidad, presionSuperficie, g = 9.81 }) {
  if (densidad <= 0 || profundidad < 0 || presionSuperficie < 0) {
    throw new RangeError("Datos físicos inválidos");
  }
  const manometrica = densidad * g * profundidad;
  return {
    manometrica,
    absoluta: presionSuperficie + manometrica
  };
}

const p = presionesEnProfundidad({ densidad: 1000, profundidad: 5, presionSuperficie: 101325 });
console.log(`Manométrica: ${(p.manometrica / 1000).toFixed(2)} kPa`);
console.log(`Absoluta: ${(p.absoluta / 1000).toFixed(2)} kPa`);

33.6 Fuerza sobre una superficie horizontal

Si toda el área A está a la misma profundidad y la presión es uniforme:

F = pA

Para calcular la fuerza neta sobre una tapa con atmósfera del otro lado se usa presión manométrica. Si el otro lado es vacío u otra presión, debe incluirse esa condición explícitamente.

33.7 Superficies planas inclinadas o verticales

En un líquido de densidad constante, la fuerza resultante sobre una superficie plana es:

F = (psuperficie + ρghc)A

hc es la profundidad del centroide. El centro de presión no coincide generalmente con el centroide porque la presión aumenta con profundidad. Calcular solo F no determina su punto de aplicación.

33.8 Empuje

function empuje({ densidadFluido, volumenDesplazado, g = 9.81 }) {
  if (densidadFluido <= 0 || volumenDesplazado < 0) throw new RangeError("Datos inválidos");
  return densidadFluido * g * volumenDesplazado;
}

console.log(`Empuje: ${empuje({ densidadFluido: 1000, volumenDesplazado: 0.01 }).toFixed(2)} N`);

La entrada es volumen desplazado, que no siempre coincide con el volumen total del cuerpo.

33.9 Peso aparente y fuerza neta

Para un cuerpo totalmente sumergido:

Fneta,vertical = B − W = ρfgVdesplazado − mg

Un resultado positivo con eje vertical hacia arriba indica tendencia ascendente. Definir el signo evita devolver únicamente un texto ambiguo.

33.10 Fracción sumergida en flotación

Para un cuerpo homogéneo que flota en equilibrio:

Vsumergido/Vtotal = ρcuerpo/ρfluido

Si el cociente supera 1, no existe equilibrio parcialmente sumergido: el cuerpo se hunde salvo que intervenga otra fuerza.

33.11 Función completa de flotación

function analizarCuerpo({ masa, volumen, densidadFluido, g = 9.81 }) {
  if (masa <= 0 || volumen <= 0 || densidadFluido <= 0) throw new RangeError("Datos inválidos");
  const densidadCuerpo = masa / volumen;
  const peso = masa * g;
  const empujeTotal = densidadFluido * g * volumen;
  return {
    densidadCuerpo,
    peso,
    empujeTotal,
    fuerzaNetaSumergido: empujeTotal - peso,
    fraccionSumergidaEquilibrio: Math.min(densidadCuerpo / densidadFluido, 1),
    flota: densidadCuerpo < densidadFluido
  };
}

console.log(analizarCuerpo({ masa: 8, volumen: 0.01, densidadFluido: 1000 }));

33.12 Caudal volumétrico

Para velocidad media normal a una sección:

Q = Av̄

La función debe recibir velocidad media, no una velocidad puntual arbitraria. En una sección circular A = πD²/4.

33.13 Continuidad entre secciones

function continuidadCircular({ diametro1, diametro2, velocidad1 }) {
  if (diametro1 <= 0 || diametro2 <= 0 || velocidad1 < 0) throw new RangeError("Datos inválidos");
  const area1 = Math.PI * diametro1 ** 2 / 4;
  const area2 = Math.PI * diametro2 ** 2 / 4;
  const caudal = area1 * velocidad1;
  return { area1, area2, caudal, velocidad2: caudal / area2 };
}

const r = continuidadCircular({ diametro1: 0.08, diametro2: 0.04, velocidad1: 2 });
console.log(`Q = ${(r.caudal * 1000).toFixed(3)} L/s`);
console.log(`v2 = ${r.velocidad2.toFixed(2)} m/s`);

33.14 Caudal másico

Para densidad uniforme sobre la sección:

ṁ = ρQ = ρAv̄

En flujo compresible estacionario se conserva ṁ, no necesariamente Q. Una función general de continuidad debe permitir densidades diferentes en entrada y salida.

33.15 Ramificaciones

En régimen estacionario:

Σṁentrada = Σṁsalida

Para líquido incompresible de igual densidad, se simplifica a suma de caudales volumétricos. Es útil representar cada conexión con un signo: positivo entrando al volumen de control y negativo saliendo.

33.16 Balance genérico entrada–salida

function balance({ entradas, salidas }) {
  const suma = valores => valores.reduce((total, valor) => total + valor, 0);
  return {
    entradaTotal: suma(entradas),
    salidaTotal: suma(salidas),
    acumulacion: suma(entradas) - suma(salidas)
  };
}

const masa = balance({ entradas: [5, 2], salidas: [4.5, 1] });
console.log(`Acumulación: ${masa.acumulacion.toFixed(2)} kg/s`);

La misma estructura sirve para masa, energía o cantidad de una especie si las unidades de todos los términos son compatibles.

33.17 Nivel de un tanque

En un tanque de sección horizontal constante A y líquido incompresible:

A dh/dt = Qin − Qout

Si ambos caudales son constantes, dh/dt es constante y el tiempo para pasar de h0 a hobjetivo se obtiene directamente. Si Qout depende de h, aparece una ecuación diferencial.

33.18 Balance estacionario y transitorio

Estacionario significa acumulación cero, no ausencia de flujo:

estacionario: Qin = Qout · transitorio: Qin − Qout = A dh/dt

El código debe distinguir una tasa de cambio de un cambio finito. dhdt y deltaH no representan lo mismo.

33.19 Paso de Euler para un tanque

function avanzarTanque({ nivel, area, caudalEntrada, caudalSalida, dt }) {
  if (area <= 0 || dt <= 0) throw new RangeError("Área y dt deben ser positivos");
  const dhdt = (caudalEntrada - caudalSalida) / area;
  return Math.max(0, nivel + dhdt * dt);
}

let nivel = 1;
for (let paso = 0; paso < 10; paso++) {
  nivel = avanzarTanque({ nivel, area: 2, caudalEntrada: 0.012, caudalSalida: 0.005, dt: 1 });
}
console.log(`Nivel después de 10 s: ${nivel.toFixed(3)} m`);

Limitar a cero evita nivel negativo, pero una simulación rigurosa también debe ajustar el instante exacto de vaciado.

33.20 Salida dependiente del nivel

Con un orificio:

Qout(h) = CdAo√(2gh)

La salida debe recalcularse en cada paso. Guardar una única Qout inicial y usarla durante todo el vaciado rompe el acoplamiento físico.

33.21 Cálculos directos e inversos

Una función directa obtiene salida desde parámetros. Un problema inverso despeja un parámetro desde el resultado deseado: profundidad desde presión, volumen desde empuje o diámetro desde caudal.

Cuando no hay despeje simple, puede usarse bisección. El intervalo debe contener un cambio de signo y la función debe ser continua dentro de él.

33.22 Resultados con significado

Devolver un objeto evita recordar posiciones:

{ valor, unidad, tipo, referencia, supuestos }

En aplicaciones mayores puede emplearse un sistema de cantidades con unidades. En este curso mantendremos números SI y nombres descriptivos para no ocultar la física.

33.23 Redondear solo para mostrar

toFixed devuelve texto. Si se redondean resultados intermedios, el error se acumula. La regla práctica es calcular con el número completo y formatear al presentar.

const presionPa = 1000 * 9.81 * 3.4567;
const paraMostrar = `${(presionPa / 1000).toFixed(2)} kPa`;

console.log(presionPa);    // número para cálculos posteriores
console.log(paraMostrar);  // texto para la interfaz

33.24 Comparar números con tolerancia

Las operaciones de punto flotante no siempre producen igualdad decimal exacta. Para pruebas:

function casiIguales(a, b, toleranciaRelativa = 1e-10, toleranciaAbsoluta = 1e-12) {
  const diferencia = Math.abs(a - b);
  return diferencia <= Math.max(toleranciaAbsoluta, toleranciaRelativa * Math.max(Math.abs(a), Math.abs(b)));
}

console.log(casiIguales(0.1 + 0.2, 0.3));

La tolerancia debe responder a la escala y precisión del problema, no elegirse para hacer pasar cualquier resultado.

33.25 Pruebas con casos límite

  • h = 0 debe producir presión manométrica cero;
  • Vdesplazado = 0 debe producir empuje cero;
  • áreas iguales deben conservar velocidad en continuidad;
  • entrada igual a salida debe producir acumulación cero;
  • duplicar profundidad debe duplicar Δp;
  • duplicar diámetro debe cuadruplicar el área;
  • entradas negativas no físicas deben generar error.

33.26 Pruebas de invariantes

Además de casos conocidos, pueden probarse propiedades generales: el empuje no disminuye al aumentar volumen, la presión hidrostática no disminuye con profundidad y un balance cerrado conserva masa.

Estas pruebas encuentran errores en muchos valores sin enumerarlos uno por uno.

33.27 Procesamiento por lotes

const profundidades = [0, 1, 2, 5, 10];
const densidad = 1000;
const tabla = profundidades.map(profundidad => ({
  profundidad,
  presionKPa: densidad * 9.81 * profundidad / 1000
}));

console.log(tabla);

Separar la generación de datos de la tabla HTML permite exportar los mismos resultados a CSV, gráfico o prueba automatizada.

33.28 Sensibilidad e incertidumbre

Una calculadora no debería ocultar que sus entradas son aproximadas. En Δp = ρgh, los errores relativos pequeños de ρ, g y h se combinan aproximadamente.

Puede evaluarse sensibilidad repitiendo el cálculo en extremos plausibles. Esta técnica también revela qué medición conviene mejorar.

33.29 Biblioteca mínima integrada

const fisica = Object.freeze({
  presion: ({ rho, h, g = 9.81 }) => rho * g * h,
  empuje: ({ rho, volumen, g = 9.81 }) => rho * g * volumen,
  areaCircular: diametro => Math.PI * diametro ** 2 / 4,
  caudal: ({ area, velocidadMedia }) => area * velocidadMedia,
  caudalMasico: ({ rho, caudal }) => rho * caudal,
  acumulacion: ({ entradas, salidas }) =>
    entradas.reduce((s, x) => s + x, 0) - salidas.reduce((s, x) => s + x, 0)
});

const area = fisica.areaCircular(0.08);
const Q = fisica.caudal({ area, velocidadMedia: 2 });

console.log(`Presión: ${fisica.presion({ rho: 1000, h: 5 })} Pa`);
console.log(`Empuje: ${fisica.empuje({ rho: 1000, volumen: 0.01 })} N`);
console.log(`Caudal: ${(Q * 1000).toFixed(3)} L/s`);
console.log(`Acumulación: ${fisica.acumulacion({ entradas: [0.012], salidas: [0.005] })} m³/s`);

33.30 Laboratorio de cálculos

Elegí una magnitud. Todos los controles muestran unidades de entrada y los resultados se derivan nuevamente en cada cambio.

Presión manométrica49,05 kPa
Presión absoluta150,38 kPa
Fuerza neta9,81 kN
Carga de presión5,00 m
Gradiente dp/dh9,81 kPa/m
Energía por volumen49,05 kJ/m³
ReferenciaSuperficie
Modeloρ constante

Δp = 1000·9,81·5,00 = 49,05 kPa

La fuerza neta supone la misma presión superficial actuando sobre la cara opuesta.

33.31 Flujo de trabajo recomendado

  1. Definir el sistema, las hipótesis y el sentido positivo.
  2. Convertir entradas a SI en la frontera del programa.
  3. Validar finitud, signo y rango.
  4. Ejecutar funciones puras que no dependan de la interfaz.
  5. Comprobar unidades, límites e invariantes.
  6. Mantener números completos para cálculos encadenados.
  7. Formatear y convertir unidades únicamente para presentar.
  8. Agregar pruebas antes de ampliar el modelo.

33.32 Ejercicio integrador

Un tanque cilíndrico vertical de área 1,50 m² recibe agua a 8,00 L/s. Descarga por un orificio inferior de diámetro 50 mm con Cd = 0,62. El nivel inicial es 0,80 m.

  1. Implementá Qout(h) con Torricelli.
  2. Calculá salida y dh/dt iniciales.
  3. Obtené el nivel de equilibrio.
  4. Simulá con Euler y dt = 1 s hasta alcanzar 95 % del cambio hacia el equilibrio.
  5. Comprobá que el balance tienda a cero.
Ver solución y explicación
const areaTanque = 1.5;
const entrada = 8e-3;
const diametroOrificio = 0.05;
const cd = 0.62;
const g = 9.81;
const areaOrificio = Math.PI * diametroOrificio ** 2 / 4;
const salida = nivel => cd * areaOrificio * Math.sqrt(2 * g * Math.max(nivel, 0));

let nivel = 0.8;
const salidaInicial = salida(nivel);
const dhdtInicial = (entrada - salidaInicial) / areaTanque;
const nivelEquilibrio = (entrada / (cd * areaOrificio)) ** 2 / (2 * g);
const objetivo95 = nivelEquilibrio - 0.05 * (nivelEquilibrio - nivel);
let tiempo = 0;
const dt = 1;

while (nivel < objetivo95 && tiempo < 10000) {
  nivel += (entrada - salida(nivel)) / areaTanque * dt;
  tiempo += dt;
}

console.log(`Salida inicial: ${(salidaInicial * 1000).toFixed(3)} L/s`);
console.log(`dh/dt inicial: ${dhdtInicial.toFixed(6)} m/s`);
console.log(`Nivel de equilibrio: ${nivelEquilibrio.toFixed(3)} m`);
console.log(`95 % alcanzado en ${tiempo} s, nivel ${nivel.toFixed(3)} m`);
console.log(`Balance final: ${((entrada - salida(nivel)) * 1000).toFixed(3)} L/s`);

La salida inicial es 4,823 L/s, dh/dt inicial = 0,002118 m/s y el nivel de equilibrio es 2,201 m. Con Euler y dt = 1 s, se alcanza 95 % del cambio alrededor de 2333 s.

33.33 Ideas para recordar

  • Las funciones físicas deben declarar entradas, salidas, unidades y supuestos.
  • Presión absoluta y manométrica no son intercambiables sin una referencia.
  • El empuje usa volumen desplazado y el caudal usa velocidad media.
  • En un balance, entrada menos salida es acumulación.
  • Un estado estacionario puede tener grandes flujos con acumulación cero.
  • Los caudales dependientes del estado deben recalcularse en cada paso.
  • El redondeo pertenece a la presentación, no a los cálculos intermedios.
  • Casos límite, tolerancias e invariantes hacen confiable una biblioteca.
  • Separar física e interfaz permite reutilizar y probar el código.

En el próximo tema utilizaremos estas funciones para simular partículas transportadas por un flujo.