File size: 2,967 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
77
78
79
80
81
82
83
84
85
86
87
/**
 * 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<string, any>): StreamInfo {
  const sv: Record<string, any> =
    stream.stream_values || stream.product_values || {};
  const properties: Record<string, string> = stream.properties || {};
  const values: Record<string, string> = 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 };
}