File size: 2,533 Bytes
c993983
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/**
 * 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 };
}