Spaces:
Sleeping
Sleeping
| /** | |
| * Pure utility to extract stream info — direct port of the Python | |
| * get_stream_info() from potential_analysis.py (lines 2289-2418). | |
| */ | |
| import type { Stream } from '../types/stream'; | |
| import type { StreamInfo } from '../types/analysis'; | |
| function toFloat(v: unknown): number | null { | |
| if (v === null || v === undefined || v === '') return null; | |
| const n = Number(v); | |
| return Number.isFinite(n) ? n : null; | |
| } | |
| export function getStreamInfo(stream: Stream): StreamInfo { | |
| const sv = stream.stream_values ?? {}; | |
| const properties = stream.properties ?? {}; | |
| const values = 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. Try stream_values (new structure) | |
| if (sv) { | |
| tin = toFloat(sv['Tin']); | |
| tout = toFloat(sv['Tout']); | |
| mdot = toFloat(sv['ṁ']); | |
| cpVal = toFloat(sv['cp']); | |
| cpDirect = toFloat(sv['CP']); | |
| } | |
| // 2. Check properties/values dict structure | |
| if (properties && values) { | |
| for (const [pk, pname] of Object.entries(properties)) { | |
| const vk = pk.replace('prop', 'val'); | |
| const v = (values as Record<string, string>)[vk] ?? ''; | |
| if (pname === 'Tin' && v && tin === null) tin = toFloat(v); | |
| else if (pname === 'Tout' && v && tout === null) tout = toFloat(v); | |
| else if (pname === 'ṁ' && v && mdot === null) mdot = toFloat(v); | |
| else if (pname === 'cp' && v && cpVal === null) cpVal = toFloat(v); | |
| else if (pname === 'CP' && v && cpDirect === null) cpDirect = toFloat(v); | |
| } | |
| } | |
| // 3. Fallback to legacy fields | |
| if (tin === null && stream.temp_in) tin = toFloat(stream.temp_in); | |
| if (tout === null && stream.temp_out) tout = toFloat(stream.temp_out); | |
| if (mdot === null && stream.mdot) mdot = toFloat(stream.mdot); | |
| if (cpVal === null && stream.cp) cpVal = toFloat(stream.cp); | |
| // Determine stream type | |
| let type: StreamInfo['type'] = null; | |
| if (tin !== null && tout !== null) { | |
| type = tin > tout ? 'Hot stream (Heat Source)' : 'Cold stream (Heat sink)'; | |
| } | |
| // CP: use direct if provided, otherwise mdot * cp | |
| let CP: number | null = null; | |
| if (cpDirect !== null) { | |
| CP = cpDirect; | |
| } else if (mdot !== null && cpVal !== null) { | |
| CP = mdot * cpVal; | |
| } | |
| // Q = |CP * (Tout - Tin)| | |
| let Q: number | null = null; | |
| if (CP !== null && tin !== null && tout !== null) { | |
| Q = Math.abs(CP * (tout - tin)); | |
| } | |
| return { tin, tout, mdot, cp: cpVal, CP, Q, type }; | |
| } | |