Integraciones API REST y MCP
API REST de cálculo: entradas, resultados y diagnóstico
Contratos actuales de seis motores de serpentines aleteados, ocho rutas de cálculo y dos rutas de consulta/estado. Ejemplos sintéticos completos evaluados localmente.
Revisado el
1. Acceso y rutas disponibles
Utilice el origen de la instalación ExCoil con las rutas exactas indicadas. Envíe Authorization: Bearer <REST_API_KEY> y Content-Type: application/json en POST. Las claves REST empiezan por exk_; manténgalas en el servidor, fuera de registros, repositorios y código del navegador. Se requiere Enterprise o Manufacturer con estado active o trialing. Poder calcular en la aplicación con Free/Professional no concede acceso REST.
Los cálculos y /refrigerants comprueban calc:read cuando existe una lista de permisos. Una lista null no restringe; una lista vacía no concede permisos; * satisface la comprobación. /health exige autenticación, sin comprobar calc:read. Todas estas llamadas consumen el límite de la clave. Aquí no se exponen Shell & Tube, psicrometría, escritura de proyectos, PDF/CAD ni exportaciones de fabricación. MCP utiliza otro token y /api/mcp; es exclusivo de Manufacturer.
| Método | Ruta | Raíz JSON |
|---|---|---|
| POST | /api/v1/calculate/dx-evaporator | air, coil, ref, calib |
| POST | /api/v1/calculate/dx-evaporator/balanced | inputs, options? |
| POST | /api/v1/calculate/dx-condenser | air, coil, ref, calib |
| POST | /api/v1/calculate/dx-condenser/balanced | air, coil, ref, calib |
| POST | /api/v1/calculate/chilled-water | air, water, coil, hydraulic, calib |
| POST | /api/v1/calculate/hot-water | air, water, coil, hydraulic, calib |
| POST | /api/v1/calculate/steam-coil | air, steam, coil, hydraulic, calib |
| POST | /api/v1/calculate/dry-cooler | air, fluid, coil, hydraulic, calib |
| GET | /api/v1/refrigerants | — |
| GET | /api/v1/health | — |
2. Construya entradas SI, no formularios de interfaz
Los ejemplos separan request y response: envíe solamente request, no el contenedor ni response. Sus números son puntos sintéticos ilustrativos, no diseños recomendados. Cada ejemplo pasó el esquema actual y su motor puro en memoria. REST no ejecuta los esquemas Zod de tRPC/MCP: entrega el cuerpo al motor tras comprobaciones superficiales de objeto/refrigerante. No espere errores por campo, conversiones automáticas ni valores predeterminados añadidos por el esquema.
Salvo dry cooler, air contiene Vdot_m3_s, T_in_C, RH_in y P_Pa: m³/s, °C de bulbo seco, humedad relativa 0–1 y presión atmosférica absoluta local en Pa. RH_in es opcional en el condensador (predeterminado 0.5). La geometría se llama coil: height_m, width_m, Pt_m, Pl_m, Do_m, wall_m y fin_m en metros; rows entero positivo; fpi en aletas por pulgada; tube_k/fin_k en W/(m·K); freeAreaRatio como fracción. Exija dimensiones y conductividades positivas, Do_m > 2·wall_m y pasos mayores que Do_m. En DX, tubesPerRow fija altura activa en tubesPerRow × Pt_m. hydraulic.tubeCountOverride de agua/vapor/dry cooler significa tubos por fila, no total.
finType admite plain, wavy, louver o slit; el condensador solo plain/wavy/louver. coil de agua/vapor/dry cooler también lleva tubeMat y finMat; la conductividad se proporciona explícitamente. Incluya calib específico del módulo, no lo coloque en options. segments es resolución numérica opcional, no cantidad de tubos. El esquema habitual de freeAreaRatio es 0.1–0.9; el del condensador permite 0–1, pero su motor exige >0 y ≤1. DX exige f_maldistribution >0 y ≤1. Valide relaciones físicas: aprobar el esquema no demuestra viabilidad.
Use el campo opcional circuitryPD bajo ref en DX o hydraulic en los demás módulos. Las matrices obligatorias siguientes deben representar los mismos circuitos reales y orden: conteos enteros, longitudes en metros, coeficientes locales adimensionales y fracciones de caudal 0–1. Las líneas requieren diámetro interno positivo y longitud, rugosidad y localK no negativos. Sistemas y trayectos accesorios opcionales aportan diagnóstico hidráulico, no una simulación térmica detallada por sistema/circuito. Sin trayectos explícitos las pérdidas accesorias son aproximadas. No invente esta estructura con conteos arbitrarios.
text
// Optional: ref.circuitryPD (DX) or hydraulic.circuitryPD (water/steam/dry cooler)
{
tubesPerCircuit: number[], returnBendsPerCircuit: number[],
crossoversPerCircuit: number[], circuitLengths_m: number[],
maxTubesPerCircuit: number, maxReturnBends: number, isUnbalanced: boolean,
systemCount?: number, circuitSystemIds?: string[], feederLengths_m?: number[],
localKPerCircuit?: number[], lengthImbalance_pct?: number,
hydraulicPaths?: Array<{
circuitId: number, systemId: string, systemFlowFraction: number,
circuitFlowFraction: number, tubeLength_m: number, connectorLength_m: number,
inletCommon: Line, inletBranch: Line, outletBranch: Line, outletCommon: Line
}>
}
// Line = { length_m: number, innerDiameter_m: number, roughness_m: number, localK: number }3. Evaporador DX: evaluación con caudal fijo
POST /api/v1/calculate/dx-evaporator recibe directamente la entrada. ref incluye fluid, Tevap_C, superheat_K, circuits, mode y mdot_ref_kg_s positivo en RATING. x_in es título de entrada (predeterminado 0.2); el motor exige 0 ≤ x_in < 1, aunque el esquema admite 1. Tevap_C es saturación en la conexión de aspiración; el acoplamiento hidráulico puede elevar la saturación en la entrada de los tubos. fluid debe coincidir exactamente con una clave de tabla admitida.
RATING conserva el caudal proporcionado: título de salida y achievedSuperheat_K son resultados, superheat_K es objetivo. SIZING exige Q_target_W, calcula caudal a partir de carga y aumento de entalpía de diseño, y evalúa la misma geometría; no aumenta automáticamente el serpentín. fouling.R_ext/R_int opcionales son resistencias no negativas en m²·K/W que el motor lee aunque el esquema Zod compartido del evaporador no las incluya. Revise outlet, perf, zones, htc, geometry y flags, especialmente targetMet, hydraulicFeasible, pressureDropExcessive y notes. No se resuelven escarcha ni física próxima al secado de película.
json
{
"request": {
"air": {
"Vdot_m3_s": 1.5,
"T_in_C": 27,
"RH_in": 0.5,
"P_Pa": 101325
},
"coil": {
"height_m": 0.508,
"tubesPerRow": 20,
"width_m": 1,
"rows": 4,
"fpi": 12,
"Pt_m": 0.0254,
"Pl_m": 0.022,
"Do_m": 0.00952,
"wall_m": 0.0003,
"fin_m": 0.0001,
"tube_k": 386,
"fin_k": 200,
"freeAreaRatio": 0.6,
"finType": "plain"
},
"ref": {
"fluid": "R410A",
"Tevap_C": 5,
"superheat_K": 5,
"circuits": 10,
"mode": "RATING",
"mdot_ref_kg_s": 0.04,
"x_in": 0.2
},
"calib": {
"C_air": 1,
"C_tp": 1,
"C_sh": 1,
"f_maldistribution": 1
},
"segments": 40
},
"response": {
"success": true,
"data": {
"outlet": {
"T_out_C": 23.49883696449892,
"RH_out": 0.5951224553222765,
"W_out": 0.01076357974635124,
"h_out": 51029.9957183522,
"Tdp_out_C": 15.165690704059124
},
"perf": {
"Q_total_W": 7909.303263873674,
"Q_sens_W": 6229.595529054708,
"Q_lat_W": 1679.7077348189664,
"SHR": 0.7876288620147928,
"mcond_kg_s": 0.0006600788155553462,
"v_face_m_s": 2.952755905511811,
"dP_air_Pa": 83.01172194416893,
"dP_ref_Pa": 1516.4350589804817,
"Q_ref_W": 7909.303263873688,
"balanceError_pct": 1.4948764421579912e-13,
"mdot_ref_kg_s": 0.04,
"x_out": 1,
"achievedSuperheat_K": 21.745191335740323,
"targetSuperheat_K": 5,
"refrigerantOutlet": {
"T_C": 26.745191335740323,
"h_in_J_kg": 250687.86399999997,
"h_out_J_kg": 448420.44559684215,
"phase": "superheated"
},
"refrigerantPressure": {
"P_in_Pa": 937723.0350589807,
"P_out_Pa": 936206.6000000002,
"T_sat_in_C": 5.0521987063866804,
"T_sat_out_C": 5,
"satDrop_K": 0.05219870638668045,
"x_in_local": 0.19968718691574538,
"dP_twoPhase_Pa": 520.582915294799,
"dP_vapor_Pa": 995.8955933460854,
"dP_accessoriesInlet_Pa": 0,
"dP_accessoriesOutlet_Pa": 0,
"massFlux_kg_m2s": 64.00890550459303,
"hydraulicFeasible": true
}
},
"geometry": {
"A_face_m2": 0.508,
"A_ext_m2": 39.10246205044688,
"A_int_m2": 2.2418405176016765,
"n_tubes": 80,
"n_tubes_H": 20,
"L_circuit_m": 8,
"Di_m": 0.00892
},
"htc": {
"h_air_avg": 66.80366999738932,
"h_ref_avg": 507.4101139521869,
"U_overall": 15.758299149814324,
"eta_fin": 0.7703542918753953,
"eta_surface": 0.7837297035150003
},
"flags": {
"wetCoil": true,
"superheated": true,
"converged": true,
"oversized": true,
"targetMet": true,
"hydraulicFeasible": true,
"pressureDropExcessive": false,
"notes": [
"Refrigerant ΔP 1.5 kPa (G = 64 kg/m²s per circuit): saturation 5.05 °C at the tube inlets → 5.00 °C at the suction connection (drop 0.05 K, coupled into capacity).",
"Warning: Liquid velocity (0.06 m/s) is too low (<0.3 m/s). Risk of poor oil return and flow stratification.",
"Warning: Vapor velocity (1.78 m/s) is too low (<3 m/s). Risk of poor oil return.",
"Refrigerant-limited: achieved superheat 21.7 K exceeds target by more than 2 K (66 % of area superheating vapour). A TXV would increase mass flow and capacity.",
"Wet surface on 34 % of the coil area: SHR = 0.788, condensate 2.38 kg/h",
"Model: mean-circuit crossflow moving boundary with pressure-coupled saturation (MSH friction, Domanski–Hermes bends, homogeneous momentum); Chen two-phase HTC does not model dryout near x → 1."
]
},
"zones": {
"tp_fraction": 0.34306774086665626,
"sh_fraction": 0.6569322591333437,
"wet_fraction": 0.34306774086665626,
"Q_tp_W": 6885.446862985299,
"Q_sh_W": 1023.8564008883747
},
"timestamp": "2026-11-02T15:44:41.927Z",
"engineVersion": "6.1.0"
}
}
}4. Condensador DX: calor rechazado
POST /api/v1/calculate/dx-condenser utiliza ref.fluid, Tcond_C, Tin_gas_C, subcool_K, circuits, mode y mdot_ref_kg_s en RATING. Tin_gas_C es temperatura absoluta de entrada del gas en °C, no recalentamiento de descarga. Exija Tin_gas_C ≥ Tcond_C y subcool_K ≥ 0. Tcond_C debe estar dentro del rango subcrítico de la tabla. En SIZING, Q_target_W es calor rechazado, incluido el aporte del compresor calculado aguas arriba; el caudal procede de la caída de entalpía objetivo, no de buscar una carga artificialmente menor.
calib incluye C_air, C_dsh, C_cond, C_sc y f_maldistribution; fouling opcional contiene R_ext/R_int en m²·K/W. El resultado usa airOut, perf.Q_dsh_W/Q_cond_W/Q_sc_W, achievedSubcool_K, status y refrigerantOutlet.phase. zones representa fracciones de área/longitud, no de calor; heat_fractions es independiente. Revise capacityShortfall_pct, targetMet y pressureDropExcessive. La evaluación térmica mantiene Tcond constante: la ΔP estimada no realimenta saturación a lo largo del circuito. No se resuelven deslizamiento térmico ni interacciones detalladas entre filas y circuitos.
json
{
"request": {
"air": {
"Vdot_m3_s": 1.5,
"T_in_C": 30,
"RH_in": 0.5,
"P_Pa": 101325
},
"coil": {
"height_m": 0.508,
"tubesPerRow": 20,
"width_m": 1,
"rows": 4,
"fpi": 12,
"Pt_m": 0.0254,
"Pl_m": 0.022,
"Do_m": 0.00952,
"wall_m": 0.0003,
"fin_m": 0.0001,
"tube_k": 386,
"fin_k": 200,
"freeAreaRatio": 0.6,
"finType": "plain"
},
"ref": {
"fluid": "R410A",
"Tcond_C": 45,
"subcool_K": 5,
"Tin_gas_C": 75,
"circuits": 10,
"mode": "RATING",
"mdot_ref_kg_s": 0.04
},
"calib": {
"C_air": 1,
"C_dsh": 1,
"C_cond": 1,
"C_sc": 1,
"f_maldistribution": 1
},
"fouling": {
"R_ext": 0,
"R_int": 0
},
"segments": 40
},
"response": {
"success": true,
"data": {
"engineVersion": "dx-cond-6.1.0",
"airOut": {
"T_out_C": 34.905833102639996,
"RH_out": 0.37920635932683405,
"W_out": 0.01331020383863019,
"h_out_J_kgda": 69268.25088365117
},
"perf": {
"Q_total_W": 8646.863272072569,
"Q_dsh_W": 1798.625303999998,
"Q_cond_W": 5929.402399999996,
"Q_sc_W": 918.8355680725742,
"v_face_m_s": 2.952755905511811,
"dP_air_Pa": 82.1050884443191,
"dP_ref_Pa": 265.3632161326799,
"mdot_ref_kg_s": 0.04,
"achievedSubcool_K": 12.142056810891162,
"status": "OK",
"satDrop_K": 0.004153328462578543,
"propertyModel": "coolprop_grid",
"Q_ref_W": 8646.863272072569,
"balanceError_pct": 1.8932766850595476e-13,
"requiredHeatRejection_W": 8124.959035999998,
"capacityShortfall_pct": 0,
"refrigerantOutlet": {
"T_C": 32.85794318910884,
"h_in_J_kg": 469048.89259999996,
"h_out_J_kg": 252877.31079818573,
"quality": null,
"phase": "subcooled"
},
"pressureDropBreakdown": {
"vapor_Pa": 103.73388610449132,
"liquid_Pa": 24.3684210978554,
"condensationFriction_Pa": 158.1821536620547,
"condensationBends_Pa": 8.76982049279011,
"acceleration_Pa": -29.691065224511664,
"accessories_Pa": 0
}
},
"zones": {
"dsh_fraction": 0.18317599713921148,
"cond_fraction": 0.5251466837984806,
"sc_fraction": 0.29167731906230787,
"heat_fractions": {
"dsh": 0.20800899093769123,
"cond": 0.6857287103348375,
"sc": 0.10626229872747119
}
},
"htc": {
"h_air_W_m2K": 66.77541103267734,
"h_ref_avg_W_m2K": 600.8301332918751,
"U_avg_W_m2K": 18.87836136527455,
"U_fouled_W_m2K": 18.87836136527455,
"eta_fin": 0.7704249008937955,
"eta_surface": 0.7837962000041732,
"h_dsh_W_m2K": 237.6991690054887,
"h_cond_W_m2K": 904.9932282548654,
"h_sc_W_m2K": 281.2531278597104
},
"geometry": {
"A_face_m2": 0.508,
"A_ext_m2": 39.10246205044688,
"A_int_m2": 2.2418405176016765,
"totalTubes": 80,
"tubesH": 20,
"L_circuit_m": 8,
"Di_m": 0.00892
},
"flags": {
"converged": true,
"oversized": false,
"targetMet": true,
"pressureDropExcessive": false,
"notes": [
"Mean-circuit pure-crossflow moving-boundary model: uniform air and refrigerant flow; not a row-by-row circuit simulation.",
"Single-phase enthalpies from the CoolProp superheated/subcooled property grid (piecewise cp = Δh/ΔT); temperature glide and saturation-pressure variation along the circuit are not resolved.",
"Target duty is condenser heat rejection, not evaporator cooling capacity; compressor power must be included upstream.",
"Multiple depth rows use a mean-crossflow approximation; row mixing and circuit/airflow interaction require independent validation.",
"Refrigerant ΔP uses mean-circuit area lengths, MSH friction, homogeneous acceleration and distributed bend estimates; gravity and pressure/thermal coupling are not resolved.",
"Warning: Liquid velocity (0.07 m/s) is too low (<0.3 m/s). Risk of poor oil return and flow stratification.",
"Warning: Vapor velocity (0.53 m/s) is too low (<3 m/s). Risk of poor oil return."
]
}
}
}
}5. Agua fría: proporcione caudal másico
POST /api/v1/calculate/chilled-water exige water.flowType (mass o volumetric), mdot_kg_s, T_in_C y glycol_pct. Vdot_m3_s es metadato opcional: el motor usa mdot_kg_s directamente y no convierte una entrada solo volumétrica. glycol_pct es porcentaje másico de etilenglicol de 0–60; cero es agua. El agua debe entrar más fría que el aire. Esta ruta no tiene objetivo water.T_out_C ni campo mode.
hydraulic incluye circuits y passes, con tubeCountOverride/circuitryPD opcionales. calib contiene C_air, C_w, f_fouling, f_maldistribution y approach_K. approach_K se conserva por compatibilidad pero se ignora; utilice 0, no una restricción de salida. passes no vuelve a multiplicar la longitud del circuito. El motor limita los factores globales de suciedad y distribución a 0.5–1. Devuelve airOutlet, waterOutlet, carga sensible/latente, condensado kg/s, SHR, wetAreaFraction y rowProfile. dP_water_kPa está en kPa; dP_air_Pa en Pa. Revise regime, notes y convergencia, incluidas advertencias de humedad y velocidad.
json
{
"request": {
"air": {
"Vdot_m3_s": 1.5,
"T_in_C": 27,
"RH_in": 0.5,
"P_Pa": 101325
},
"water": {
"flowType": "mass",
"mdot_kg_s": 0.8,
"T_in_C": 7,
"glycol_pct": 0
},
"coil": {
"height_m": 0.508,
"width_m": 1,
"rows": 4,
"fpi": 12,
"Pt_m": 0.0254,
"Pl_m": 0.022,
"Do_m": 0.00952,
"wall_m": 0.0003,
"fin_m": 0.0001,
"tube_k": 386,
"fin_k": 200,
"freeAreaRatio": 0.6,
"finType": "plain",
"tubeMat": "Cu",
"finMat": "Al"
},
"hydraulic": {
"circuits": 10,
"passes": 1,
"tubeCountOverride": 20
},
"calib": {
"C_air": 1,
"C_w": 1,
"f_fouling": 1,
"f_maldistribution": 1,
"approach_K": 0
},
"segments": 40
},
"response": {
"success": true,
"data": {
"airOutlet": {
"T_out_C": 17.59280443908688,
"RH_out": 0.7765505714650607,
"W_out": 0.009740516735050291,
"h_out": 42378.12881134521,
"Tdp_out_C": 13.647149565122389
},
"waterOutlet": {
"T_out_C": 13.83298923251135,
"dT_K": 6.83298923251135
},
"perf": {
"Q_total_W": 22902.7552948312,
"Q_sens_W": 16738.15895260657,
"Q_lat_W": 6164.596342224632,
"SHR": 0.7308360385959376,
"mcond_kg_s": 0.0024330194247277745,
"v_face_m_s": 2.952755905511811,
"v_water_m_s": 1.2806100545209298,
"dP_air_Pa": 102.16101340852097,
"dP_water_kPa": 31.131087580896573,
"balanceError_pct": 1.588445914152848e-14
},
"geometry": {
"A_face_m2": 0.508,
"A_ext_m2": 39.10246205044688,
"A_int_m2": 2.2418405176016765,
"n_tubes": 80,
"n_tubes_H": 20,
"L_circuit_m": 8,
"Di_m": 0.00892
},
"htc": {
"h_air_avg": 66.82202962012009,
"h_water_avg": 5727.92773098421,
"U_overall": 45.13852142146351,
"eta_fin": 0.7703084264602427,
"eta_surface": 0.7836865094705112
},
"flags": {
"wetCoil": true,
"converged": true,
"oversized": false,
"regime": "Turbulent",
"notes": [
"Air ΔP includes a +30 % condensate allowance on the wet area (75 % wet); dry value 83 Pa.",
"Wet coil: 75 % of the area condensing, SHR = 0.731, latent 6.16 kW",
"Condensate rate: 8.76 kg/h"
],
"wetAreaFraction": 0.75,
"topology": "counter-cross-flow",
"rowProfile": [
{
"row": 1,
"Q_W": 5499.31813897424,
"T_air_out_C": 23.90926813806179,
"W_air_out": 0.011144473622459834,
"T_w_mean_C": 13.026969682536295,
"wetFraction": 0
},
{
"row": 2,
"Q_W": 5494.681101553189,
"T_air_out_C": 21.71020036768415,
"W_air_out": 0.010785290417758279,
"T_w_mean_C": 11.3995916157579,
"wetFraction": 1
},
{
"row": 3,
"Q_W": 5820.603392148445,
"T_air_out_C": 19.617081166279927,
"W_air_out": 0.010308018617014768,
"T_w_mean_C": 9.711871818476336,
"wetFraction": 1
},
{
"row": 4,
"Q_W": 6088.1526621553285,
"T_air_out_C": 17.59280443908688,
"W_air_out": 0.009740516735050291,
"T_w_mean_C": 7.936579335221592,
"wetFraction": 1
}
]
},
"timestamp": "2026-11-02T15:44:41.973Z",
"engineVersion": "6.0.0"
}
}
}6. Agua caliente: calentamiento sensible
POST /api/v1/calculate/hot-water comparte nombres de water, coil, hydraulic y calib con agua fría, pero exige water.T_in_C > air.T_in_C. Aporte siempre mdot_kg_s positivo, incluso con flowType volumetric. glycol_pct es etilenglicol en masa; esta ruta no dispone de fluidType para propilenglicol.
Evalúa geometría y caudales dados; no fija temperatura de salida del agua ni dimensiona automáticamente el serpentín. Ignora approach_K y no duplica passes en longitud. El calentamiento conserva W_out del aire, mientras cambia RH. Devuelve airOutlet, waterOutlet, perf.Q_total_W, dP_water_kPa y diagnósticos por fila; no hay campos universales sensibleCapacity_kW ni coilCode. Compruebe regime, advertencias de velocidad y flags.converged antes de seleccionar.
json
{
"request": {
"air": {
"Vdot_m3_s": 1.5,
"T_in_C": 15,
"RH_in": 0.5,
"P_Pa": 101325
},
"water": {
"flowType": "mass",
"mdot_kg_s": 0.8,
"T_in_C": 70,
"glycol_pct": 0
},
"coil": {
"height_m": 0.508,
"width_m": 1,
"rows": 4,
"fpi": 12,
"Pt_m": 0.0254,
"Pl_m": 0.022,
"Do_m": 0.00952,
"wall_m": 0.0003,
"fin_m": 0.0001,
"tube_k": 386,
"fin_k": 200,
"freeAreaRatio": 0.6,
"finType": "plain",
"tubeMat": "Cu",
"finMat": "Al"
},
"hydraulic": {
"circuits": 10,
"passes": 1,
"tubeCountOverride": 20
},
"calib": {
"C_air": 1,
"C_w": 1,
"f_fouling": 1,
"f_maldistribution": 1,
"approach_K": 0
},
"segments": 40
},
"response": {
"success": true,
"data": {
"airOutlet": {
"T_out_C": 45.761722198035464,
"RH_out": 0.08548275287573397,
"W_out": 0.0052785443390815584,
"h_out": 59687.224743425075
},
"waterOutlet": {
"T_out_C": 52.970971133489016,
"dT_K": 17.029028866510984
},
"perf": {
"Q_total_W": 56935.16608498108,
"v_face_m_s": 2.952755905511811,
"v_water_m_s": 1.3104547892703775,
"dP_air_Pa": 82.60160250830555,
"dP_water_kPa": 26.25240208261644,
"balanceError_pct": 2.5558747306799374e-14
},
"geometry": {
"A_face_m2": 0.508,
"A_ext_m2": 39.10246205044688,
"A_int_m2": 2.2418405176016765,
"n_tubes": 80,
"n_tubes_H": 20,
"L_circuit_m": 8,
"Di_m": 0.00892
},
"htc": {
"h_air_avg": 66.7732116534083,
"h_water_avg": 9792.613943179225,
"U_overall": 47.844060775088444,
"eta_fin": 0.7704303970347292,
"eta_surface": 0.7838013760296212
},
"flags": {
"converged": true,
"oversized": false,
"regime": "Turbulent",
"notes": [],
"topology": "counter-cross-flow",
"rowProfile": [
{
"row": 1,
"Q_W": 16668.64454630135,
"T_air_out_C": 24.00596675498907,
"T_w_mean_C": 55.41380080380971
},
{
"row": 2,
"Q_W": 14933.338963764221,
"T_air_out_C": 32.07435851144329,
"T_w_mean_C": 60.1476061140732
},
{
"row": 3,
"Q_W": 13369.768306539849,
"T_air_out_C": 39.297962599714644,
"T_w_mean_C": 64.38509129379582
},
{
"row": 4,
"Q_W": 11963.414268375656,
"T_air_out_C": 45.761722198035464,
"T_w_mean_C": 68.17579603667396
}
]
},
"timestamp": "2026-11-02T15:44:41.983Z",
"engineVersion": "6.0.0"
}
}
}7. Vapor: presión manométrica y drenaje
POST /api/v1/calculate/steam-coil utiliza steam.P_steam_kPa en kPa manométricos referidos a air.P_Pa. La presión absoluta de entrada debe ser >20 y ≤2000 kPa; la saturación debe superar el aire de entrada en más de 1 °C. steam.controlType admite modulating/on_off, sin simulación dinámica del control. Envíe mode RATING o SIZING explícitamente: REST no aplica el predeterminado del esquema. subcooling_K es opcional (predeterminado 5 K). No existe entrada quality: se supone vapor seco saturado.
calib lleva C_air, C_steam, f_fouling y f_noncondensables. SIZING exige steam.Q_target_W positivo, prueba 1–4 filas y devuelve la menor cantidad viable que cumpla carga; si ninguna cumple, devuelve cuatro filas con targetMet false. Compruebe selectedRows, pressureDropLimited, notes, steamOutlet y dP_steam_kPa. steamTrapCapacity_kg_h y dP_steamAssemblies_kPa son orientaciones preliminares, no certificación de drenaje o purgador. El área de subenfriamiento no se modela por separado; drenaje gravitatorio, golpe de ariete, diferencial del purgador y resistencia mecánica requieren revisión independiente.
json
{
"request": {
"air": {
"Vdot_m3_s": 1.5,
"T_in_C": 15,
"RH_in": 0.5,
"P_Pa": 101325
},
"steam": {
"P_steam_kPa": 100,
"controlType": "modulating",
"subcooling_K": 5,
"mode": "RATING"
},
"coil": {
"height_m": 0.508,
"width_m": 1,
"rows": 1,
"fpi": 8,
"Pt_m": 0.0254,
"Pl_m": 0.022,
"Do_m": 0.01588,
"wall_m": 0.0008,
"fin_m": 0.0001,
"tube_k": 386,
"fin_k": 200,
"freeAreaRatio": 0.65,
"finType": "plain",
"tubeMat": "Cu",
"finMat": "Al"
},
"hydraulic": {
"circuits": 20,
"passes": 1,
"tubeCountOverride": 20
},
"calib": {
"C_air": 1,
"C_steam": 1,
"f_fouling": 1,
"f_noncondensables": 1
},
"segments": 40
},
"response": {
"success": true,
"data": {
"airOutlet": {
"T_out_C": 34.079225932632134,
"RH_out": 0.15946330926782845,
"W_out": 0.0052785443390815584,
"h_out": 47819.933671807084
},
"steamOutlet": {
"T_sat_C": 120.4008265,
"T_condensate_C": 115.40022902366081,
"mdot_steam_kg_s": 0.015890787995762996,
"mdot_steam_kg_h": 57.206836784746784
},
"perf": {
"Q_total_W": 35312.68146348002,
"v_face_m_s": 2.952755905511811,
"dP_air_Pa": 79.84088218559907,
"dP_steamAssemblies_kPa": 0,
"steamTrapCapacity_kg_h": 143.01709196186695,
"dP_steam_kPa": 0.004148565054761704,
"v_steam_in_m_s": 4.3669848655833485,
"satTempDrop_K": 0.0005974763391805027,
"balanceError_pct": 2.0604375857744306e-14,
"effectiveness": 0.18101641744854888,
"NTU": 0.19969124105470984,
"UA_W_K": 369.59744652696935
},
"geometry": {
"A_face_m2": 0.508,
"A_ext_m2": 5.494324834912423,
"A_int_m2": 0.8972388618652448,
"n_tubes": 20,
"n_tubes_H": 20,
"L_circuit_m": 1,
"Di_m": 0.014279999999999998
},
"htc": {
"h_air_avg": 69.05378275896463,
"h_steam_avg": 16452.208245445134,
"U_overall": 67.26894707397848,
"eta_fin": 0.9040208158032803,
"eta_surface": 0.9208501857263931
},
"flags": {
"converged": true,
"oversized": false,
"notes": [
"In-tube condensation: max(Chato gravity-driven, Shah shear-driven) — governing: Chato (stratified); inlet vapour Re = 5474.",
"Steam ΔP in governing circuit 0.00 kPa → T_sat drop 0.00 K; capacity uses T_sat at the mean circuit pressure (120.4 °C).",
"Condensate hydrostatic head across the coil: 4.7 kPa (0 return bends per circuit). Ensure gravity drainage and trap differential.",
"Steam use includes 5.0 K condensate subcooling (h_fg + cp·ΔT_sub); the subcooling area is not modelled separately."
],
"pressureDropLimited": false,
"condensationModel": "Chato (stratified)",
"rowProfile": [
{
"row": 1,
"Q_W": 35312.68146348002,
"T_air_out_C": 34.079225932632134,
"h_steam_W_m2K": 16452.208245445134
}
]
},
"timestamp": "2026-11-02T15:44:41.989Z",
"engineVersion": "5.0.0"
}
}
}8. Dry cooler: REST evalúa un solo serpentín
POST /api/v1/calculate/dry-cooler usa air distinto: T_db_C, RH, altitude_m y Vdot_m3_s o v_face_m_s. v_face_m_s positivo tiene prioridad. fluid contiene fluidType water/eg/pg, glycolPct (masa %, 0–60), T_in_C y mdot_kg_s o Vdot_m3_s positivo; prevalece caudal másico. El fluido debe entrar más caliente que el aire. calib usa C_air, C_fluid, f_fouling_air, f_fouling_fluid, R_fouling_air_m2KW/R_fouling_fluid_m2KW opcionales en m²·K/W, f_maldistribution y approach_K; este último se ignora.
El controlador llama solo a solveDryCooler. Aunque los tipos compartidos mencionen fluid.T_out_C, vShape y vAngle_deg, no activan dimensionamiento por temperatura ni el motor en V mediante esta ruta. No existe /dry-cooler/balanced aquí. La respuesta es evaluación de una batería con airOutlet, fluidOutlet, Q_total_W, LMTD_K, F_correction, effectiveness, NTU y diagnóstico de congelación. Q_lat_W es cero y wetCoil false en este modelo de rechazo; no es un enfriador adiabático por aspersión. Revise nearFreezing, freezingMargin_K, regime, convergencia y notes.
json
{
"request": {
"air": {
"T_db_C": 30,
"RH": 0.5,
"altitude_m": 0,
"Vdot_m3_s": 1.5
},
"fluid": {
"fluidType": "eg",
"glycolPct": 30,
"T_in_C": 45,
"mdot_kg_s": 0.8
},
"coil": {
"height_m": 0.508,
"width_m": 1,
"rows": 4,
"fpi": 10,
"Pt_m": 0.0254,
"Pl_m": 0.022,
"Do_m": 0.01588,
"wall_m": 0.0005,
"fin_m": 0.0002,
"tube_k": 386,
"fin_k": 200,
"freeAreaRatio": 0.55,
"finType": "plain",
"tubeMat": "Cu",
"finMat": "Al"
},
"hydraulic": {
"circuits": 10,
"passes": 1,
"tubeCountOverride": 20
},
"calib": {
"C_air": 1,
"C_fluid": 1,
"f_fouling_air": 1,
"f_fouling_fluid": 1,
"R_fouling_air_m2KW": 0,
"R_fouling_fluid_m2KW": 0,
"f_maldistribution": 1,
"approach_K": 0
},
"segments": 40
},
"response": {
"success": true,
"data": {
"airOutlet": {
"T_out_C": 37.963588732151216,
"RH_out": 0.32077369527978405,
"W_out": 0.01331020383863019,
"h_out": 72420.05383927401
},
"fluidOutlet": {
"T_out_C": 40.1532216213745,
"dT_K": 4.846778378625501
},
"perf": {
"Q_total_W": 14036.364768477986,
"Q_sens_W": 14036.364768477986,
"Q_lat_W": 0,
"v_face_m_s": 2.952755905511811,
"v_fluid_m_s": 0.44169081528152904,
"dP_air_Pa": 220.66091438182707,
"dP_fluid_kPa": 3.170390159951273,
"balanceError_pct": 2.2030504600253869e-13,
"LMTD_K": 8.499786553833324,
"F_correction": 0.9964684620436388,
"effectiveness": 0.530905915476746,
"NTU": 0.9402368011687094
},
"geometry": {
"A_face_m2": 0.508,
"A_ext_m2": 26.353776660889263,
"A_int_m2": 3.7397518948332893,
"n_tubes": 80,
"n_tubes_H": 20,
"L_circuit_m": 8,
"Di_m": 0.014879999999999997
},
"htc": {
"h_air_avg": 158.41035024054196,
"h_fluid_avg": 784.605812708763,
"U_overall": 62.88400693514512,
"UA_W_K": 1657.2310743106261,
"eta_fin": 0.8992749036562225,
"eta_surface": 0.9133028338448336
},
"flags": {
"wetCoil": false,
"wetFraction": 0,
"converged": true,
"oversized": false,
"regime": "Transitional",
"nearFreezing": false,
"freezingPoint_C": -13.544999999999998,
"freezingMargin_K": 53.6982216213745,
"notes": [
"Warning: Glycol mixture velocity (0.44 m/s) below 0.5 m/s. Glycol mixtures need higher velocity for adequate heat transfer.",
"Notice: Fluid flow is transitional (Re = 2798). Results may have higher uncertainty.",
"Dry cooler: the fin surface is warmer than the air, so no condensation can occur (latent capacity = 0)."
],
"topology": "counter-cross-flow",
"rowProfile": [
{
"row": 1,
"Q_W": 3885.8246219918224,
"T_air_out_C": 32.20463842919675,
"T_w_mean_C": 40.80778501827438
},
{
"row": 2,
"Q_W": 3634.220318092688,
"T_air_out_C": 34.266528131601234,
"T_w_mean_C": 42.10730054946271
},
{
"row": 3,
"Q_W": 3382.1877594860885,
"T_air_out_C": 36.18542613516059,
"T_w_mean_C": 43.31977794477613
},
{
"row": 4,
"Q_W": 3134.1320689073873,
"T_air_out_C": 37.963588732151216,
"T_w_mean_C": 44.445822404284186
}
]
},
"timestamp": "2026-11-02T15:44:42.006Z",
"engineVersion": "2.0.0"
}
}
}9. Contratos distintos para balanced
POST /api/v1/calculate/dx-evaporator/balanced exige {inputs: <entrada completa>, options?: {tolPct?, maxIt?}}. Busca caudal de refrigerante para alcanzar el recalentamiento de salida solicitado, como punto de funcionamiento de válvula de expansión; capacidad es resultado, no simple cierre Q_aire = Q_ref. Usa RATING internamente y una semilla positiva si falta caudal. maxIt predetermina 60. Para entrada original SIZING, tolPct (predeterminado 1) evalúa déficit de capacidad, no la tolerancia de recalentamiento, que es 0.05 K. targetMet/converged pueden ser false. No modifica calibración.
POST /api/v1/calculate/dx-condenser/balanced recibe directamente la entrada del condensador, NO {inputs, options}. Llama al motor normal, mantiene caudal RATING, fija iterations en 1 y en SIZING comprueba carga, fase líquida y subenfriamiento. REST no pasa un tercer argumento options: enviar options no habilita límites internos de caudal, maxIt ni tolerancias. No describa esta ruta como búsqueda de menor caudal para ocultar insuficiencia. Ambas devuelven el formato normal de su módulo.
json
[
{
"method": "POST",
"path": "/api/v1/calculate/dx-evaporator/balanced",
"request": {
"inputs": {
"air": {
"Vdot_m3_s": 1.5,
"T_in_C": 27,
"RH_in": 0.5,
"P_Pa": 101325
},
"coil": {
"height_m": 0.508,
"tubesPerRow": 20,
"width_m": 1,
"rows": 4,
"fpi": 12,
"Pt_m": 0.0254,
"Pl_m": 0.022,
"Do_m": 0.00952,
"wall_m": 0.0003,
"fin_m": 0.0001,
"tube_k": 386,
"fin_k": 200,
"freeAreaRatio": 0.6,
"finType": "plain"
},
"ref": {
"fluid": "R410A",
"Tevap_C": 5,
"superheat_K": 5,
"circuits": 10,
"mode": "RATING",
"mdot_ref_kg_s": 0.04,
"x_in": 0.2
},
"calib": {
"C_air": 1,
"C_tp": 1,
"C_sh": 1,
"f_maldistribution": 1
},
"segments": 40
},
"options": {
"tolPct": 1,
"maxIt": 60
}
},
"response": {
"success": true,
"data": {
"outlet": {
"T_out_C": 16.37866583468195,
"RH_out": 0.7700295163271693,
"W_out": 0.008931554267298955,
"h_out": 39086.848765679584,
"Tdp_out_C": 12.341546239274933
},
"perf": {
"Q_total_W": 28606.45485650264,
"Q_sens_W": 18898.46749721315,
"Q_lat_W": 9707.987359289491,
"SHR": 0.660636474949893,
"mcond_kg_s": 0.0038349295656768378,
"v_face_m_s": 2.952755905511811,
"dP_air_Pa": 83.4970954841905,
"dP_ref_Pa": 18853.52619300515,
"Q_ref_W": 28606.45485650279,
"balanceError_pct": 5.214107509615256e-13,
"mdot_ref_kg_s": 0.1606828269347608,
"x_out": 1,
"achievedSuperheat_K": 5.013566038697411,
"targetSuperheat_K": 5,
"refrigerantOutlet": {
"T_C": 10.01356603869741,
"h_in_J_kg": 250687.86399999997,
"h_out_J_kg": 428718.4314365398,
"phase": "superheated"
},
"refrigerantPressure": {
"P_in_Pa": 955060.1261930054,
"P_out_Pa": 936206.6000000002,
"T_sat_in_C": 5.648975814871859,
"T_sat_out_C": 5,
"satDrop_K": 0.648975814871859,
"x_in_local": 0.19609440443287166,
"dP_twoPhase_Pa": 17333.776101730764,
"dP_vapor_Pa": 1524.1357220685973,
"dP_accessoriesInlet_Pa": 0,
"dP_accessoriesOutlet_Pa": 0,
"massFlux_kg_m2s": 257.12829713694947,
"hydraulicFeasible": true
}
},
"geometry": {
"A_face_m2": 0.508,
"A_ext_m2": 39.10246205044688,
"A_int_m2": 2.2418405176016765,
"n_tubes": 80,
"n_tubes_H": 20,
"L_circuit_m": 8,
"Di_m": 0.00892
},
"htc": {
"h_air_avg": 66.82588196002244,
"h_ref_avg": 3152.8354872556492,
"U_overall": 39.48790359750422,
"eta_fin": 0.7702988035387852,
"eta_surface": 0.7836774470234328
},
"flags": {
"wetCoil": true,
"superheated": true,
"converged": true,
"oversized": false,
"targetMet": true,
"hydraulicFeasible": true,
"pressureDropExcessive": false,
"notes": [
"Refrigerant ΔP 18.9 kPa (G = 257 kg/m²s per circuit): saturation 5.65 °C at the tube inlets → 5.00 °C at the suction connection (drop 0.65 K, coupled into capacity).",
"Warning: Liquid velocity (0.22 m/s) is too low (<0.3 m/s). Risk of poor oil return and flow stratification.",
"Wet surface on 93 % of the coil area: SHR = 0.661, condensate 13.81 kg/h",
"Model: mean-circuit crossflow moving boundary with pressure-coupled saturation (MSH friction, Domanski–Hermes bends, homogeneous momentum); Chen two-phase HTC does not model dryout near x → 1.",
"TXV: mdot_ref = 0.16068 kg/s gives 5.01 K superheat (12 evaluations)."
],
"iterations": 12
},
"zones": {
"tp_fraction": 0.9259358677567262,
"sh_fraction": 0.0740641322432738,
"wet_fraction": 0.9259358677567262,
"Q_tp_W": 27660.548469301222,
"Q_sh_W": 945.9063872014204
},
"timestamp": "2026-11-02T15:44:42.158Z",
"engineVersion": "6.1.0"
}
}
},
{
"method": "POST",
"path": "/api/v1/calculate/dx-condenser/balanced",
"request": {
"air": {
"Vdot_m3_s": 1.5,
"T_in_C": 30,
"RH_in": 0.5,
"P_Pa": 101325
},
"coil": {
"height_m": 0.508,
"tubesPerRow": 20,
"width_m": 1,
"rows": 4,
"fpi": 12,
"Pt_m": 0.0254,
"Pl_m": 0.022,
"Do_m": 0.00952,
"wall_m": 0.0003,
"fin_m": 0.0001,
"tube_k": 386,
"fin_k": 200,
"freeAreaRatio": 0.6,
"finType": "plain"
},
"ref": {
"fluid": "R410A",
"Tcond_C": 45,
"subcool_K": 5,
"Tin_gas_C": 75,
"circuits": 10,
"mode": "RATING",
"mdot_ref_kg_s": 0.04
},
"calib": {
"C_air": 1,
"C_dsh": 1,
"C_cond": 1,
"C_sc": 1,
"f_maldistribution": 1
},
"fouling": {
"R_ext": 0,
"R_int": 0
},
"segments": 40
},
"response": {
"success": true,
"data": {
"engineVersion": "dx-cond-6.1.0",
"airOut": {
"T_out_C": 34.905833102639996,
"RH_out": 0.37920635932683405,
"W_out": 0.01331020383863019,
"h_out_J_kgda": 69268.25088365117
},
"perf": {
"Q_total_W": 8646.863272072569,
"Q_dsh_W": 1798.625303999998,
"Q_cond_W": 5929.402399999996,
"Q_sc_W": 918.8355680725742,
"v_face_m_s": 2.952755905511811,
"dP_air_Pa": 82.1050884443191,
"dP_ref_Pa": 265.3632161326799,
"mdot_ref_kg_s": 0.04,
"achievedSubcool_K": 12.142056810891162,
"status": "OK",
"satDrop_K": 0.004153328462578543,
"propertyModel": "coolprop_grid",
"Q_ref_W": 8646.863272072569,
"balanceError_pct": 1.8932766850595476e-13,
"requiredHeatRejection_W": 8124.959035999998,
"capacityShortfall_pct": 0,
"refrigerantOutlet": {
"T_C": 32.85794318910884,
"h_in_J_kg": 469048.89259999996,
"h_out_J_kg": 252877.31079818573,
"quality": null,
"phase": "subcooled"
},
"pressureDropBreakdown": {
"vapor_Pa": 103.73388610449132,
"liquid_Pa": 24.3684210978554,
"condensationFriction_Pa": 158.1821536620547,
"condensationBends_Pa": 8.76982049279011,
"acceleration_Pa": -29.691065224511664,
"accessories_Pa": 0
}
},
"zones": {
"dsh_fraction": 0.18317599713921148,
"cond_fraction": 0.5251466837984806,
"sc_fraction": 0.29167731906230787,
"heat_fractions": {
"dsh": 0.20800899093769123,
"cond": 0.6857287103348375,
"sc": 0.10626229872747119
}
},
"htc": {
"h_air_W_m2K": 66.77541103267734,
"h_ref_avg_W_m2K": 600.8301332918751,
"U_avg_W_m2K": 18.87836136527455,
"U_fouled_W_m2K": 18.87836136527455,
"eta_fin": 0.7704249008937955,
"eta_surface": 0.7837962000041732,
"h_dsh_W_m2K": 237.6991690054887,
"h_cond_W_m2K": 904.9932282548654,
"h_sc_W_m2K": 281.2531278597104
},
"geometry": {
"A_face_m2": 0.508,
"A_ext_m2": 39.10246205044688,
"A_int_m2": 2.2418405176016765,
"totalTubes": 80,
"tubesH": 20,
"L_circuit_m": 8,
"Di_m": 0.00892
},
"flags": {
"converged": true,
"oversized": false,
"targetMet": true,
"pressureDropExcessive": false,
"notes": [
"Mean-circuit pure-crossflow moving-boundary model: uniform air and refrigerant flow; not a row-by-row circuit simulation.",
"Single-phase enthalpies from the CoolProp superheated/subcooled property grid (piecewise cp = Δh/ΔT); temperature glide and saturation-pressure variation along the circuit are not resolved.",
"Target duty is condenser heat rejection, not evaporator cooling capacity; compressor power must be included upstream.",
"Multiple depth rows use a mean-crossflow approximation; row mixing and circuit/airflow interaction require independent validation.",
"Refrigerant ΔP uses mean-circuit area lengths, MSH friction, homogeneous acceleration and distributed bend estimates; gravity and pressure/thermal coupling are not resolved.",
"Warning: Liquid velocity (0.07 m/s) is too low (<0.3 m/s). Risk of poor oil return and flow stratification.",
"Warning: Vapor velocity (0.53 m/s) is too low (<3 m/s). Risk of poor oil return."
],
"iterations": 1
}
}
}
}
]10. Interpretar éxitos y consultas
Los cálculos devuelven HTTP 200 con {success:true,data:<resultado del motor>}, sin renombrado universal de capacidad, coilCode, paginación ni guardado de proyecto. Cargas en W, caudales de refrigerante/condensado en kg/s salvo kg_h explícito; pérdidas de aire y DX en Pa, líquidos/vapor en kPa donde así termina el campo. Los ejemplos incluyen respuestas sintéticas completas. El catálogo siguiente incluye campos condicionales; es notación de campos, no TypeScript ejecutable ni solicitud JSON. Las propiedades opcionales pueden faltar.
Éxito HTTP solo indica que el motor devolvió resultado, no que se cumpla carga ni seguridad de fabricación. Compruebe flags.converged, flags.notes, targetMet cuando exista, recalentamiento/subenfriamiento real, fase de salida y diagnósticos hidráulicos/de congelación. Un balanceError_pct pequeño es residuo interno, no validación de laboratorio independiente. El condensador lleva engineVersion pero no timestamp; no suponga metadatos idénticos.
GET /api/v1/refrigerants devuelve únicamente nombres exactos en data, no GWP, ODP, clases de seguridad ni tablas de propiedades. GET /api/v1/health devuelve directamente status, version y timestamp ISO, sin success/data; version corresponde a la API, no a todos los motores. Ambos GET requieren autenticación. Disponibilidad de refrigerante no implica operación transcrítica ni temperatura ilimitada.
text
// HTTP 200 calculators: { success: true, data: Result }
// A trailing ? marks an optional field; names and units are literal API keys.
Air = {T_out_C, RH_out, W_out, h_out}; // numbers: °C, fraction, kg/kg dry air, J/kg dry air
Geometry = {A_face_m2, A_ext_m2, A_int_m2, n_tubes, n_tubes_H, L_circuit_m, Di_m};
HTC = {h_air_avg, U_overall, eta_fin, eta_surface}; // W/(m²·K), efficiencies 0–1
Row = {row, Q_W, T_air_out_C, T_w_mean_C};
DXEvaporator = {
outlet: Air & {Tdp_out_C}, geometry: Geometry, htc: HTC & {h_ref_avg},
perf: {Q_total_W,Q_sens_W,Q_lat_W,SHR,mcond_kg_s,v_face_m_s,dP_air_Pa,dP_ref_Pa,
Q_ref_W,balanceError_pct,mdot_ref_kg_s,x_out,achievedSuperheat_K?,targetSuperheat_K?,
targetCapacity_W?,capacityShortfall_pct?,
refrigerantOutlet?: {T_C,h_in_J_kg,h_out_J_kg,phase},
refrigerantPressure?: {P_in_Pa,P_out_Pa,T_sat_in_C,T_sat_out_C,satDrop_K,x_in_local,
dP_twoPhase_Pa,dP_vapor_Pa,dP_accessoriesInlet_Pa,dP_accessoriesOutlet_Pa,massFlux_kg_m2s,hydraulicFeasible}},
zones?: {tp_fraction,sh_fraction,wet_fraction,Q_tp_W,Q_sh_W},
flags: {wetCoil,superheated,converged,oversized,targetMet?,hydraulicFeasible?,pressureDropExcessive?,iterations?,notes},
timestamp, engineVersion
};
DXCondenser = {
airOut: {T_out_C,RH_out,W_out,h_out_J_kgda},
perf: {Q_total_W,Q_dsh_W,Q_cond_W,Q_sc_W,v_face_m_s,dP_air_Pa,dP_ref_Pa?,
mdot_ref_kg_s,achievedSubcool_K,status,satDrop_K?,propertyModel?,Q_ref_W,balanceError_pct,
targetCapacity_W?,targetError_pct?,requiredHeatRejection_W?,capacityShortfall_pct?,
refrigerantOutlet?: {T_C,h_in_J_kg,h_out_J_kg,quality,phase},
pressureDropBreakdown?: {vapor_Pa,liquid_Pa,condensationFriction_Pa,condensationBends_Pa,acceleration_Pa,accessories_Pa}},
zones: {dsh_fraction,cond_fraction,sc_fraction,heat_fractions?:{dsh,cond,sc}},
htc: {h_air_W_m2K,h_ref_avg_W_m2K,U_avg_W_m2K,U_fouled_W_m2K?,eta_fin,eta_surface,h_dsh_W_m2K?,h_cond_W_m2K?,h_sc_W_m2K?},
geometry: {A_face_m2,A_ext_m2,A_int_m2,totalTubes,tubesH?,L_circuit_m?,Di_m?},
flags: {converged,iterations?,oversized,targetMet?,pressureDropExcessive?,notes}, engineVersion?
};
ChilledWater = {
airOutlet: Air & {Tdp_out_C}, waterOutlet:{T_out_C,dT_K}, geometry:Geometry,
htc:HTC & {h_water_avg},
perf:{Q_total_W,Q_sens_W,Q_lat_W,SHR,mcond_kg_s,v_face_m_s,v_water_m_s?,dP_air_Pa,dP_water_kPa,balanceError_pct},
flags:{wetCoil,converged,oversized,regime,notes,wetAreaFraction?,topology?,
rowProfile?: Array<Row & {W_air_out,wetFraction}>}, timestamp,engineVersion
};
HotWater = {
airOutlet:Air,waterOutlet:{T_out_C,dT_K},geometry:Geometry,htc:HTC & {h_water_avg},
perf:{Q_total_W,v_face_m_s,v_water_m_s?,dP_air_Pa,dP_water_kPa,balanceError_pct},
flags:{converged,oversized,regime,notes,topology?,rowProfile?:Row[]},timestamp,engineVersion
};
Steam = {
airOutlet:Air,steamOutlet:{T_sat_C,T_condensate_C,mdot_steam_kg_s,mdot_steam_kg_h},
geometry:Geometry,htc:HTC & {h_steam_avg},
perf:{Q_total_W,v_face_m_s,dP_air_Pa,dP_steamAssemblies_kPa,steamTrapCapacity_kg_h,
dP_steam_kPa?,v_steam_in_m_s?,satTempDrop_K?,balanceError_pct?,effectiveness?,NTU?,UA_W_K?},
flags:{converged,oversized,notes,targetMet?,selectedRows?,pressureDropLimited?,condensationModel?,
rowProfile?:Array<{row,Q_W,T_air_out_C,h_steam_W_m2K}>},timestamp,engineVersion
};
DryCooler = {
airOutlet:Air,fluidOutlet:{T_out_C,dT_K},geometry:Geometry,htc:HTC & {h_fluid_avg,UA_W_K},
perf:{Q_total_W,Q_sens_W,Q_lat_W,v_face_m_s,v_fluid_m_s,dP_air_Pa,dP_fluid_kPa,
balanceError_pct,LMTD_K,F_correction,effectiveness,NTU},
flags:{wetCoil,wetFraction,converged,oversized,regime,nearFreezing,freezingPoint_C,freezingMargin_K,
notes,topology?,rowProfile?:Row[],targetMet?},timestamp,engineVersion
};
// Numbers unless named otherwise: flags booleans; notes string[]; rowProfile arrays.
// timestamp: ISO string; engineVersion/regime/topology/propertyModel/status/phase/condensationModel: strings.
// Condenser refrigerantOutlet.quality: number | null. No condenser timestamp is returned.
// Evaporator phase: two_phase | saturated_vapor | superheated.
// Condenser phase: superheated | two_phase | saturated_liquid | subcooled.
// Condenser status type: OK | NO_CONDENSE | NO_SUBCOOL | LIMITED_UA | PARTIAL.
// Current condenser solver emits OK, NO_CONDENSE, NO_SUBCOOL or PARTIAL.
// propertyModel: coolprop_grid | constant_cp_fallback; topology: counter-cross-flow.
// condensationModel: Chato (stratified) | Shah (annular).
// GET /api/v1/health: {status:"ok",version:"1.0.0",timestamp:"<ISO>"}
// GET /api/v1/refrigerants: {success:true,data:["R32","R410A","R134a","R22","R290","R1234yf","R404A","R507A","R407C","R1234ze(E)","R717","R744","R600a","R1270","R123","R245fa"]}11. Errores, límites y diagnóstico seguro
El error JSON de ruta es {error:<código>,message:<texto>}, sin success:false ni lista de errores por campo. Las plantillas siguientes son literales; los valores entre signos angulares varían. DX normal distingue refrigerante ausente y desconocido; evaporador balanced comprueba primero inputs y ambas balanced usan el mensaje corto Unknown refrigerant. Campos internos ausentes pueden lanzar excepción y resultar en 422. La comprobación de objeto es superficial, incluso matrices son objetos JavaScript: no sustituye validación completa.
401: compruebe Bearer, formato exk_ y validez sin revelar la clave. 403: revocación, caducidad, estado de plan y calc:read. 400: corrija estructura o refrigerante. 422: use el mensaje para corregir unidades, campos, orden térmico, caudales, geometría y rango de propiedades; no repita entradas erróneas a ciegas. Los errores no capturados de autenticación/almacenamiento no son normalizados por wrapSolver. Express puede rechazar JSON malformado o cuerpos excesivos antes de REST; el parser tiene límite 50mb y no garantiza este formato de error.
El límite es por clave y memoria del proceso, con ventana de 60 segundos desde la primera autenticación aceptada. Se utiliza la configuración de la clave, o 100 peticiones/minuto si es un valor falso; no es cuota universal del plan ni límite distribuido. X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset (segundos Unix) aparecen tras autenticación/límite y en 429, no en todo 401/403. Fallos de permiso/entrada posteriores consumen cuota. Espere al reset y use retroceso acotado ante 429; no se garantiza Retry-After. Oculte secretos y registre solo ruta, entrada sintética/anonimizada, estado HTTP y diagnóstico.
text
HTTP 401: {"error":"unauthorized","message":"Missing or invalid Authorization header. Use: Bearer exk_your_api_key"}
HTTP 401: {"error":"unauthorized","message":"Invalid API key format. Keys start with 'exk_'."}
HTTP 401: {"error":"unauthorized","message":"Invalid API key."}
HTTP 403: {"error":"forbidden","message":"API key has been revoked."}
HTTP 403: {"error":"forbidden","message":"API key has expired."}
HTTP 403: {"error":"forbidden","message":"API access requires an active Enterprise or Manufacturer subscription."}
HTTP 403: {"error":"forbidden","message":"This API key does not have the 'calc:read' permission."}
HTTP 429: {"error":"rate_limit_exceeded","message":"Rate limit of <limit> requests per minute exceeded. Try again later."}
HTTP 400: {"error":"bad_request","message":"Request body must be a JSON object with calculation inputs."}
HTTP 400: {"error":"bad_request","message":"Missing refrigerant in input. Provide ref.fluid (or legacy ref.refrigerant)."}
HTTP 400: {"error":"bad_request","message":"Unknown refrigerant '<name>'. Available: <keys>"}
HTTP 400: {"error":"bad_request","message":"Request body must contain 'inputs' and optional 'options' fields."}
HTTP 400: {"error":"bad_request","message":"Unknown refrigerant '<name>'."}
HTTP 422: {"error":"calculation_error","message":"<engine error>"}
// Non-Error fallbacks: normal routes = Calculation failed; balanced routes = Balanced solve failed.