29.1 Del modelo físico al programa
Un cálculo confiable comienza antes del código. Hay que definir sistema, hipótesis, variables de entrada, unidades y resultados esperados. Luego se implementan ecuaciones y verificaciones independientes.
29.2 Usar unidades coherentes
Una estrategia simple consiste en convertir todas las entradas al Sistema Internacional antes de calcular: kg, m, s, N, J y W. La interfaz puede aceptar otras unidades, pero el núcleo trabaja con una convención única.
const convertir = {
kilometrosHoraAMetrosSegundo: valor => valor / 3.6,
kilojoulesAJoules: valor => valor * 1000,
kilowattsAWatts: valor => valor * 1000,
horasASegundos: valor => valor * 3600
};
console.log(convertir.kilometrosHoraAMetrosSegundo(72)); // 2029.3 Funciones pequeñas y puras
Separar física, interfaz y formato facilita las pruebas y evita que una modificación visual altere el cálculo.
29.4 Trabajo de una fuerza constante
function productoEscalar(a, b) {
return a.x * b.x + a.y * b.y + (a.z ?? 0) * (b.z ?? 0);
}
function trabajoConstante(fuerza, desplazamiento) {
return productoEscalar(fuerza, desplazamiento);
}
console.log(trabajoConstante(
{ x: 12, y: -3 },
{ x: 4, y: 2 }
)); // 42 JUsar componentes evita calcular ángulos y conserva naturalmente el signo del producto escalar.
29.5 Energías cinética y potencial
function energiaCinetica(masa, rapidez) {
if (masa <= 0 || rapidez < 0) throw new RangeError("Datos no físicos");
return 0.5 * masa * rapidez ** 2;
}
function potencialGravitatorio(masa, altura, gravedad = 9.8, referencia = 0) {
if (masa <= 0 || gravedad <= 0) throw new RangeError("Datos no físicos");
return masa * gravedad * (altura - referencia);
}
console.log(energiaCinetica(2, 3) + potencialGravitatorio(2, 5));29.6 Balance entre estados
Una función puede devolver no solo el resultado principal, sino también cantidades auditables:
function auditarBalance({ Ki, Ui, Kf, Uf, trabajoNoConservativo }) {
const cambioMecanico = Kf + Uf - Ki - Ui;
return {
cambioMecanico,
trabajoNoConservativo,
residuo: cambioMecanico - trabajoNoConservativo
};
}
console.log(auditarBalance({
Ki: 10, Ui: 50, Kf: 35, Uf: 20, trabajoNoConservativo: -5
}));Un residuo próximo a cero confirma ΔEm = Wnc.
29.7 Fuerzas variables e integración
Para W = ∫F(x)dx puede usarse la regla del punto medio:
function integrarPuntoMedio(funcion, inicio, final, pasos = 1000) {
if (pasos < 1 || final <= inicio) throw new RangeError("Intervalo inválido");
const dx = (final - inicio) / pasos;
let suma = 0;
for (let i = 0; i < pasos; i += 1) {
suma += funcion(inicio + (i + 0.5) * dx) * dx;
}
return suma;
}
console.log(integrarPuntoMedio(x => 3 * x ** 2, 0, 2)); // ≈ 8 J29.8 Potencia a partir de vectores o energía
Ambas rutas permiten una verificación cruzada cuando se dispone de fuerzas, velocidades y cambios energéticos. No son intercambiables sin comprobar que describen el mismo sistema e intervalo.
29.9 Integrar datos de potencia
Con muestras irregulares (tj, Pj), la regla trapezoidal calcula:
function energiaDesdePotencia(muestras) {
let energia = 0;
for (let i = 1; i < muestras.length; i += 1) {
const anterior = muestras[i - 1];
const actual = muestras[i];
const dt = actual.t - anterior.t;
if (dt <= 0) throw new RangeError("Los tiempos deben crecer");
energia += 0.5 * (anterior.p + actual.p) * dt;
}
return energia;
}
console.log(energiaDesdePotencia([
{ t: 0, p: 100 }, { t: 2, p: 140 }, { t: 5, p: 80 }
])); // 570 J29.10 Validar entradas
JavaScript permite valores como NaN e Infinity. Una validación reutilizable debe comprobar tipo, finitud y dominio físico:
function exigirNumeroFinito(nombre, valor) {
if (typeof valor !== "number" || !Number.isFinite(valor)) {
throw new TypeError(`${nombre} debe ser un número finito`);
}
return valor;
}
function exigirPositivo(nombre, valor) {
exigirNumeroFinito(nombre, valor);
if (valor <= 0) throw new RangeError(`${nombre} debe ser positivo`);
return valor;
}
const masa = exigirPositivo("masa", 2.5);
const gravedad = exigirPositivo("gravedad", 9.8);
const altura = exigirNumeroFinito("altura", -3.5);
console.log("Masa validada:", masa, "kg");
console.log("Gravedad validada:", gravedad, "m/s²");
console.log("Altura validada:", altura, "m");
console.log("Todos los datos son números finitos y válidos.");29.11 Errores de punto flotante
Muchos decimales no se representan exactamente en binario. Por eso no conviene comprobar resultados físicos calculados con igualdad estricta:
function casiIguales(a, b, toleranciaRelativa = 1e-9, toleranciaAbsoluta = 1e-12) {
const diferencia = Math.abs(a - b);
const escala = Math.max(Math.abs(a), Math.abs(b));
return diferencia <= Math.max(toleranciaAbsoluta, toleranciaRelativa * escala);
}
console.log(casiIguales(0.1 + 0.2, 0.3)); // true29.12 Invariantes físicos como pruebas
Las verificaciones deben ejecutarse con datos normales, límites, ceros y casos deliberadamente inválidos.
29.13 Pruebas automatizadas mínimas
function comprobar(condicion, mensaje) {
if (!condicion) throw new Error(`Prueba fallida: ${mensaje}`);
}
comprobar(energiaCinetica(2, 3) === 9, "energía cinética");
comprobar(trabajoConstante({ x: 5, y: 0 }, { x: 2, y: 0 }) === 10,
"trabajo paralelo");
comprobar(casiIguales(integrarPuntoMedio(x => 2 * x, 0, 3), 9, 1e-6),
"integración lineal");
console.log("Pruebas superadas");29.14 Datos estructurados y trazabilidad
Guardar entradas, hipótesis y resultados juntos facilita reproducir un cálculo:
const simulacion = {
unidades: "SI",
modelo: { gravedadUniforme: true, rozamientoConstante: true },
parametros: { masa: 2, gravedad: 9.8 },
estadoInicial: { x: 0, y: 2, rapidez: 3 },
tramos: [
{ dx: 4, dy: 1, fx: 10, fy: 0, rozamiento: 2, dt: 2 }
]
};
console.log(JSON.stringify(simulacion, null, 2));29.15 Separar cálculo y presentación
El núcleo debería devolver números sin redondear. La interfaz decide cuántos decimales mostrar mediante Intl.NumberFormat. Redondear en cada operación acumula error y dificulta auditar el resultado.
29.16 Fallos que deben hacerse visibles
Un programa no debería convertir silenciosamente una raíz negativa en cero ni reemplazar un dato ausente por un valor arbitrario. Es preferible devolver un estado explícito:
{ ok: false, error: "estado_inaccesible" }{ ok: false, error: "duracion_no_positiva" }{ ok: false, error: "unidad_desconocida" }
Los mensajes deben explicar qué dato corregir y conservar los valores que originaron el problema.
29.17 Actividad interactiva: auditar una trayectoria
Cada tramo define desplazamiento, fuerza aplicada, rozamiento medio y duración. El programa actualiza el estado y verifica ΔK = Wneto y ΔEm = Wnc.
Analizador energético por tramos
Unidades SI; gravedad uniforme y fuerza de rozamiento opuesta al recorrido.
| Tramo | Δx (m) | Δy (m) | Fx (N) | Fy (N) | Rozamiento (N) | Δt (s) |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | ||||||
| 3 |
29.18 Procesar una trayectoria por tramos
function procesarTramo(estado, tramo, { masa, gravedad = 9.8 }) {
const distancia = Math.hypot(tramo.dx, tramo.dy);
const trabajoAplicado = tramo.fx * tramo.dx + tramo.fy * tramo.dy;
const trabajoRozamiento = -tramo.rozamiento * distancia;
const trabajoGravedad = -masa * gravedad * tramo.dy;
const cineticaFinal = estado.cinetica + trabajoAplicado
+ trabajoRozamiento + trabajoGravedad;
if (cineticaFinal < -1e-10) {
return { ok: false, error: "se_detiene_en_el_tramo" };
}
return {
ok: true,
cinetica: Math.max(0, cineticaFinal),
altura: estado.altura + tramo.dy,
rapidez: Math.sqrt(2 * Math.max(0, cineticaFinal) / masa),
trabajoNoConservativo: trabajoAplicado + trabajoRozamiento
};
}
const estadoInicial = { cinetica: 25, altura: 4 };
const parametros = { masa: 2, gravedad: 9.8 };
const tramoAccesible = {
dx: 3, dy: -1,
fx: 5, fy: 0,
rozamiento: 2
};
const tramoImposible = {
dx: 2, dy: 5,
fx: 0, fy: 0,
rozamiento: 4
};
console.log("Tramo accesible:",
procesarTramo(estadoInicial, tramoAccesible, parametros));
console.log("Tramo imposible:",
procesarTramo(estadoInicial, tramoImposible, parametros));29.19 Estrategia de depuración
- Probar cada función con un caso calculable a mano.
- Registrar contribuciones antes de sumarlas.
- Comprobar unidades y signos en cada frontera.
- Comparar rutas independientes: trabajos contra cambios de estado.
- Reducir el problema a un solo tramo cuando falle el balance.
- Conservar el caso que reveló el error como prueba de regresión.
29.20 Comprobaciones y errores frecuentes
- Mezclar km/h con m/s o kJ con J.
- Usar
parseInty perder la parte decimal. - Ocultar una raíz negativa mediante
Math.abs. - Comparar resultados calculados con
===. - Actualizar el DOM dentro de cada operación del modelo.
29.21 Ejercicios propuestos
- Escribir una función que calcule Ue = ½kx² y valide k ≥ 0.
- Implementar potenciaMedia(energia, duracion) rechazando duración no positiva.
- Integrar F(x) = 4x³ entre 0 y 2 y comparar con el resultado exacto de 16 J.
- Crear una prueba que compruebe conservación mecánica en una caída desde 10 m.
- Modificar procesarTramo para devolver el residuo ΔK − Wneto.
- Con muestras E = [0, 4, 16, 36] J en t = [0, 1, 2, 3] s, estimar potencias medias por intervalo.
Ver respuestas orientativas
if (k < 0) throw new RangeError(...); return 0.5 * k * x ** 2;if (duracion <= 0) throw new RangeError(...); return energia / duracion;- La integral es x⁴ evaluada entre 0 y 2: 16 J. La aproximación debe converger hacia ese valor.
- Comparar mgh con ½mv² usando v = √(2gh) y una tolerancia relativa.
- Guardar
trabajoNetoy devolverresiduo: cineticaFinal - estado.cinetica - trabajoNeto. - ΔE/Δt: 4 W, 12 W y 20 W.
29.22 Ideas para recordar
- El programa comienza con un modelo físico y una convención de unidades.
- Funciones puras y pequeñas son más fáciles de probar.
- Los cálculos deben devolver contribuciones auditables, no solo un total.
- Las integrales pueden aproximarse con sumas controlando paso y error.
- Los flotantes se comparan con tolerancias.
- Balances e invariantes físicos funcionan como pruebas automatizadas.
- Los estados imposibles deben informarse, no ocultarse.
En el próximo tema reuniremos todo el curso en un analizador energético de un recorrido con gravedad y rozamiento.