/** * Extract stream info (Tin, Tout, mdot, cp, CP, Q) from a stream object, * checking multiple fallback sources in the same order as the original * Streamlit app: * 1. stream_values / product_values dict * 2. properties/values mapping (prop1→val1 …) * 3. top-level temp_in / temp_out / mdot / cp */ export interface StreamInfo { tin: number | null; tout: number | null; mdot: number | null; cp: number | null; CP: number | null; Q: number | null; type: 'hot' | 'cold' | null; density: string | null; pressure: string | null; water_in: string | null; water_out: string | null; } function tryFloat(v: unknown): number | null { if (v == null || v === '') return null; const n = Number(v); return isNaN(n) ? null : n; } export function extractStreamInfo(stream: Record): StreamInfo { const sv: Record = stream.stream_values || stream.product_values || {}; const properties: Record = stream.properties || {}; const values: Record = stream.values || {}; let tin: number | null = null; let tout: number | null = null; let mdot: number | null = null; let cpVal: number | null = null; let cpDirect: number | null = null; // 1. stream_values if (sv.Tin != null) tin = tryFloat(sv.Tin); if (sv.Tout != null) tout = tryFloat(sv.Tout); if (sv['ṁ'] != null) mdot = tryFloat(sv['ṁ']); if (sv.cp != null) cpVal = tryFloat(sv.cp); if (sv.CP != null) cpDirect = tryFloat(sv.CP); // 2. properties / values mapping (prop1→val1, prop2→val2 …) for (const [pk, pname] of Object.entries(properties)) { const vk = pk.replace('prop', 'val'); const v = values[vk]; if (!v) continue; if (pname === 'Tin' && tin == null) tin = tryFloat(v); else if (pname === 'Tout' && tout == null) tout = tryFloat(v); else if (pname === 'ṁ' && mdot == null) mdot = tryFloat(v); else if (pname === 'cp' && cpVal == null) cpVal = tryFloat(v); else if (pname === 'CP' && cpDirect == null) cpDirect = tryFloat(v); } // 3. top-level fallback if (tin == null) tin = tryFloat(stream.temp_in); if (tout == null) tout = tryFloat(stream.temp_out); if (mdot == null) mdot = tryFloat(stream.mdot); if (cpVal == null) cpVal = tryFloat(stream.cp); const density = sv.Density || null; const pressure = sv.Pressure || null; const water_in = sv['Water Content In'] || null; const water_out = sv['Water Content Out'] || null; // Compute CP (heat capacity flow rate) let CP: number | null = cpDirect ?? (mdot != null && cpVal != null ? mdot * cpVal : null); // Compute Q const Q = CP != null && tin != null && tout != null ? Math.abs(CP * (tout - tin)) : null; // Stream type const type: 'hot' | 'cold' | null = tin != null && tout != null ? (tin > tout ? 'hot' : 'cold') : null; return { tin, tout, mdot, cp: cpVal, CP, Q, type, density, pressure, water_in, water_out }; }