iapws-if97 2.1.5 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/CHANGELOG.md +32 -1
  2. package/README.md +78 -215
  3. package/dist/backward/candidate-resolution.d.ts +8 -0
  4. package/dist/backward/candidate-resolution.js +40 -0
  5. package/dist/backward/fixed-temperature-candidates.d.ts +4 -0
  6. package/dist/backward/fixed-temperature-candidates.js +188 -0
  7. package/dist/backward/fixed-temperature-solver.d.ts +6 -6
  8. package/dist/backward/fixed-temperature-solver.js +56 -9
  9. package/dist/backward/fixed-temperature-stationary.d.ts +4 -0
  10. package/dist/backward/fixed-temperature-stationary.js +146 -0
  11. package/dist/backward/hs-region.d.ts +50 -0
  12. package/dist/backward/hs-region.js +695 -0
  13. package/dist/backward/hs-state.d.ts +15 -0
  14. package/dist/backward/hs-state.js +316 -0
  15. package/dist/backward/hs.d.ts +1 -2
  16. package/dist/backward/hs.js +28 -232
  17. package/dist/backward/ph.d.ts +8 -3
  18. package/dist/backward/ph.js +102 -54
  19. package/dist/backward/pressure-property-region.d.ts +13 -0
  20. package/dist/backward/pressure-property-region.js +262 -0
  21. package/dist/backward/ps.d.ts +6 -1
  22. package/dist/backward/ps.js +103 -38
  23. package/dist/backward/solution-validation.d.ts +7 -2
  24. package/dist/backward/solution-validation.js +162 -37
  25. package/dist/backward/th.d.ts +3 -6
  26. package/dist/backward/th.js +10 -73
  27. package/dist/backward/tolerances.d.ts +3 -1
  28. package/dist/backward/tolerances.js +9 -4
  29. package/dist/backward/ts.d.ts +3 -6
  30. package/dist/backward/ts.js +10 -73
  31. package/dist/boundaries.d.ts +0 -1
  32. package/dist/boundaries.js +0 -1
  33. package/dist/constants.d.ts +11 -24
  34. package/dist/constants.js +17 -28
  35. package/dist/core/floating-point.d.ts +2 -0
  36. package/dist/core/floating-point.js +7 -0
  37. package/dist/core/formulation-seams.d.ts +27 -0
  38. package/dist/core/formulation-seams.js +289 -0
  39. package/dist/core/input-validation.d.ts +1 -1
  40. package/dist/core/input-validation.js +5 -1
  41. package/dist/core/region-detector.d.ts +3 -41
  42. package/dist/core/region-detector.js +39 -597
  43. package/dist/core/region3-pt.d.ts +12 -1
  44. package/dist/core/region3-pt.js +68 -13
  45. package/dist/core/solve-input-normalization.d.ts +0 -1
  46. package/dist/core/solve-input-normalization.js +4 -2
  47. package/dist/core/solver.d.ts +7 -11
  48. package/dist/core/solver.js +26 -18
  49. package/dist/core/thermodynamic-derivatives.d.ts +17 -0
  50. package/dist/core/thermodynamic-derivatives.js +27 -0
  51. package/dist/detect.d.ts +16 -3
  52. package/dist/detect.js +36 -3
  53. package/dist/errors.d.ts +11 -0
  54. package/dist/errors.js +16 -0
  55. package/dist/gas-constant.d.ts +5 -0
  56. package/dist/gas-constant.js +5 -0
  57. package/dist/index.d.ts +4 -6
  58. package/dist/index.js +10 -22
  59. package/dist/regions/boundaries.d.ts +0 -1
  60. package/dist/regions/boundaries.js +0 -1
  61. package/dist/regions/region1.d.ts +2 -2
  62. package/dist/regions/region1.js +1 -2
  63. package/dist/regions/region2.d.ts +2 -2
  64. package/dist/regions/region2.js +1 -2
  65. package/dist/regions/region3-data.d.ts +0 -1
  66. package/dist/regions/region3-data.js +3 -4
  67. package/dist/regions/region3-eval.d.ts +0 -1
  68. package/dist/regions/region3-eval.js +0 -1
  69. package/dist/regions/region3-subregions.d.ts +0 -1
  70. package/dist/regions/region3-subregions.js +15 -11
  71. package/dist/regions/region3.d.ts +4 -2
  72. package/dist/regions/region3.js +32 -3
  73. package/dist/regions/region4.d.ts +2 -1
  74. package/dist/regions/region4.js +39 -13
  75. package/dist/regions/region5.d.ts +2 -2
  76. package/dist/regions/region5.js +2 -3
  77. package/dist/regions.d.ts +0 -1
  78. package/dist/regions.js +0 -1
  79. package/dist/saturation/common.d.ts +2 -9
  80. package/dist/saturation/common.js +66 -45
  81. package/dist/saturation/region4-boundaries.d.ts +0 -5
  82. package/dist/saturation/region4-boundaries.js +17 -18
  83. package/dist/saturation/region4-hs.d.ts +2 -1
  84. package/dist/saturation/region4-hs.js +499 -106
  85. package/dist/saturation/two-phase.d.ts +2 -3
  86. package/dist/saturation/two-phase.js +7 -8
  87. package/dist/saturation.d.ts +0 -1
  88. package/dist/saturation.js +0 -1
  89. package/dist/solvers/bracketed-newton.d.ts +10 -6
  90. package/dist/solvers/bracketed-newton.js +19 -13
  91. package/dist/solvers/damped-newton-2d.d.ts +12 -0
  92. package/dist/solvers/damped-newton-2d.js +68 -0
  93. package/dist/solvers/extended-arithmetic.d.ts +6 -0
  94. package/dist/solvers/extended-arithmetic.js +38 -0
  95. package/dist/solvers/newton-raphson.d.ts +0 -1
  96. package/dist/solvers/newton-raphson.js +0 -1
  97. package/dist/transport/properties.d.ts +4 -3
  98. package/dist/transport/properties.js +14 -5
  99. package/dist/transport.d.ts +0 -1
  100. package/dist/transport.js +0 -1
  101. package/dist/types.d.ts +18 -1
  102. package/dist/types.js +14 -1
  103. package/package.json +18 -10
  104. package/dist/backward/objective-normalization.d.ts +0 -7
  105. package/dist/backward/objective-normalization.js +0 -8
  106. package/dist/core/public-normalization.d.ts +0 -4
  107. package/dist/core/public-normalization.js +0 -54
  108. package/dist/solvers/nelder-mead.d.ts +0 -17
  109. package/dist/solvers/nelder-mead.js +0 -116
@@ -0,0 +1,15 @@
1
+ /** Backward H-S state construction within a selected region or formulation seam. */
2
+ import type { BasicProperties } from '../types.js';
3
+ import { Region } from '../types.js';
4
+ import type { FormulationSeamResolution } from '../core/formulation-seams.js';
5
+ export declare function r1Phs(h: number, s: number): number;
6
+ /** Boundary between Region 2a and Region 2b in h-s plane. */
7
+ export declare function b2abBoundary(s: number): number;
8
+ export declare function r2aPhs(h: number, s: number): number;
9
+ export declare function r2bPhs(h: number, s: number): number;
10
+ export declare function r2cPhs(h: number, s: number): number;
11
+ export declare function r3aPhs(h: number, s: number): number;
12
+ export declare function r3bPhs(h: number, s: number): number;
13
+ export declare function solveHSInRegion(h: number, s: number, region: Region | -1, seamInitial?: BasicProperties, validate?: boolean): BasicProperties;
14
+ /** Resolve one proven seam identically for the HS solver and detector. */
15
+ export declare function solveHSSeam(h: number, s: number, seam: FormulationSeamResolution): BasicProperties | null;
@@ -0,0 +1,316 @@
1
+ import { Region, IF97Error } from '../types.js';
2
+ import { NumericalStateError } from '../errors.js';
3
+ import * as C from '../constants.js';
4
+ import { region1 } from '../regions/region1.js';
5
+ import { region2 } from '../regions/region2.js';
6
+ import { region3ByRhoT } from '../regions/region3.js';
7
+ import { region5 } from '../regions/region5.js';
8
+ import { solveRegion3PTBasic } from '../core/region3-pt.js';
9
+ import { dampedNewton2D } from '../solvers/damped-newton-2d.js';
10
+ import { r1BackwardT, r2BackwardT, r3BackwardPHInitial } from './ph.js';
11
+ import { backwardCandidateError, validateBackwardState, validateRegion3PTReconstruction } from './solution-validation.js';
12
+ import { gibbsPropertyDerivatives, helmholtzPropertyDerivatives, } from '../core/thermodynamic-derivatives.js';
13
+ import { tryRegion4HSState } from '../saturation/region4-hs.js';
14
+ import { BACKWARD_HS_NEWTON_TOLERANCE, backwardConstraintTolerance } from './tolerances.js';
15
+ import { resolveBackwardCandidates } from './candidate-resolution.js';
16
+ import { nextPositiveFloat } from '../core/floating-point.js';
17
+ // ─── Backward Polynomial Evaluator ─────────────────────────────────────────
18
+ function evalPoly(table, aShift, bShift, a, b) {
19
+ let sum = 0;
20
+ for (const [I, J, N] of table) {
21
+ sum += N * Math.pow(a - aShift, I) * Math.pow(b - bShift, J);
22
+ }
23
+ return sum;
24
+ }
25
+ // ─── Region 1 P(h,s) — 19 coefficients ─────────────────────────────────────
26
+ const R1_PHS = [
27
+ [0, 0, -0.691997014660582], [0, 1, -18.361254878756], [0, 2, -9.28332409297335],
28
+ [0, 4, 65.9639569909906], [0, 5, -16.2060388912024], [0, 6, 450.620017338667],
29
+ [0, 8, 854.68067822417], [0, 14, 6075.23214001162], [1, 0, 32.6487682621856],
30
+ [1, 1, -26.9408844582931], [1, 4, -319.9478483343], [1, 6, -928.35430704332],
31
+ [2, 0, 30.3634537455249], [2, 1, -65.0540422444146], [2, 10, -4309.91316516130],
32
+ [3, 4, -747.512324096068], [4, 1, 730.000345529245], [4, 4, 1142.84032569021],
33
+ [5, 0, -436.407041874559],
34
+ ];
35
+ export function r1Phs(h, s) {
36
+ const mu = h / 3400;
37
+ const sig = s / 7.6;
38
+ return 100 * evalPoly(R1_PHS, -0.05, -0.05, mu, sig);
39
+ }
40
+ // ─── Region 2 P(h,s) — Subregions 2a, 2b, 2c ──────────────────────────────
41
+ /** Boundary between Region 2a and Region 2b in h-s plane. */
42
+ export function b2abBoundary(s) {
43
+ return -3498.98083432139 + 2575.60716905876 * s - 421.073558227969 * s * s + 27.6349063799944 * s * s * s;
44
+ }
45
+ const R2A_PHS = [
46
+ [0, 1, -0.0182575361923032], [0, 3, -0.125229548799536], [0, 6, 0.592290437320145],
47
+ [0, 16, 6.04769706185122], [0, 20, 238.624965444474], [0, 22, -298.639090222922],
48
+ [1, 0, 0.051225081304075], [1, 1, -0.437266515606486], [1, 2, 0.413336902999504],
49
+ [1, 3, -5.16468254574773], [1, 5, -5.57014838445711], [1, 6, 12.8555037824478],
50
+ [1, 10, 11.414410895329], [1, 16, -119.504225652714], [1, 20, -2847.7798596156],
51
+ [1, 22, 4317.57846408006], [2, 3, 1.1289404080265], [2, 16, 1974.09186206319],
52
+ [2, 20, 1516.12444706087], [3, 0, 0.0141324451421235], [3, 2, 0.585501282219601],
53
+ [3, 3, -2.97258075863012], [3, 6, 5.94567314847319], [3, 16, -6236.56565798905],
54
+ [4, 16, 9659.86235133332], [5, 3, 6.81500934948134], [5, 16, -6332.07286824489],
55
+ [6, 3, -5.5891922446576], [7, 1, 0.0400645798472063],
56
+ ];
57
+ export function r2aPhs(h, s) {
58
+ const mu = h / 4200;
59
+ const sig = s / 12;
60
+ const P = evalPoly(R2A_PHS, 0.5, 1.2, mu, sig);
61
+ return 4 * P * P * P * P;
62
+ }
63
+ const R2B_PHS = [
64
+ [0, 0, 0.0801496989929495], [0, 1, -0.543862807146111], [0, 2, 0.337455597421283],
65
+ [0, 4, 8.9055545115745], [0, 8, 313.840736431485], [1, 0, 0.797367065977789],
66
+ [1, 1, -1.2161697355624], [1, 2, 8.72803386937477], [1, 3, -16.9769781757602],
67
+ [1, 5, -186.552827328416], [1, 12, 95115.9274344237], [2, 1, -18.9168510120494],
68
+ [2, 6, -4334.0703719484], [2, 18, 543212633.012715], [3, 0, 0.144793408386013],
69
+ [3, 1, 128.024559637516], [3, 7, -67230.9534071268], [3, 12, 33697238.0095287],
70
+ [4, 1, -586.63419676272], [4, 16, -22140322476.9889], [5, 1, 1716.06668708389],
71
+ [5, 12, -570817595.806302], [6, 1, -3121.09693178482], [6, 8, -2078413.8463301],
72
+ [6, 18, 3056059461577.86], [7, 1, 3221.57004314333], [7, 16, 326810259797.295],
73
+ [8, 1, -1441.04158934487], [8, 3, 410.694867802691], [8, 14, 109077066873.024],
74
+ [8, 18, -24796465425889.3], [12, 10, 1888019068.65134], [14, 16, -123651009018773],
75
+ ];
76
+ export function r2bPhs(h, s) {
77
+ const mu = h / 4100;
78
+ const sig = s / 7.9;
79
+ const P = evalPoly(R2B_PHS, 0.6, 1.01, mu, sig);
80
+ return 100 * P * P * P * P;
81
+ }
82
+ const R2C_PHS = [
83
+ [0, 0, 0.112225607199012], [0, 1, -3.39005953606712], [0, 2, -32.0503911730094],
84
+ [0, 3, -197.5973051049], [0, 4, -407.693861553446], [0, 8, 13294.3775222331],
85
+ [1, 0, 1.70846839774007], [1, 2, 37.3694198142245], [1, 5, 3581.44365815434],
86
+ [1, 8, 423014.446424664], [1, 14, -751071025.760063], [2, 2, 52.3446127607898],
87
+ [2, 3, -228.351290812417], [2, 7, -960652.417056937], [2, 10, -80705929.2526074],
88
+ [2, 18, 1626980172256.69], [3, 0, 0.772465073604171], [3, 5, 46392.9973837746],
89
+ [3, 8, -13731788.5134128], [3, 16, 1704703926305.12], [3, 18, -25110462818730.8],
90
+ [4, 18, 31774883083552.0], [5, 1, 53.8685623675312], [5, 4, -55308.9094625169],
91
+ [5, 6, -1028615.22421405], [5, 14, 2042494187562.34], [6, 8, 273918446.626977],
92
+ [6, 18, -2.63963146312685e15], [10, 7, -1078908541.08088], [12, 7, -29649262098.0124],
93
+ [16, 10, -1.11754907323424e15],
94
+ ];
95
+ export function r2cPhs(h, s) {
96
+ const mu = h / 3500;
97
+ const sig = s / 5.9;
98
+ const P = evalPoly(R2C_PHS, 0.7, 1.1, mu, sig);
99
+ return 100 * P * P * P * P;
100
+ }
101
+ function r2Phs(h, s) {
102
+ const hBound = b2abBoundary(s);
103
+ // Region 2 h-s subregion selection: 2a below the h(s) boundary,
104
+ // otherwise split 2b/2c at the published entropy crossover.
105
+ if (h <= hBound)
106
+ return r2aPhs(h, s);
107
+ if (s >= C.R2_S_CRT)
108
+ return r2bPhs(h, s);
109
+ return r2cPhs(h, s);
110
+ }
111
+ // ─── Region 3 P(h,s) — Subregions 3a, 3b ───────────────────────────────────
112
+ const R3A_PHS = [
113
+ [0, 0, 7.70889828326934], [0, 1, -26.0835009128688], [0, 5, 267.416218930389],
114
+ [1, 0, 17.2221089496844], [1, 3, -293.54233214597], [1, 4, 614.135601882478],
115
+ [1, 8, -61056.2757725674], [1, 14, -65127225.1118219], [2, 6, 73591.9313521937],
116
+ [2, 16, -11664650591.4191], [3, 0, 35.5267086434461], [3, 2, -596.144543825955],
117
+ [3, 3, -475.842430145708], [4, 0, 69.6781965359503], [4, 1, 335.674250377312],
118
+ [4, 4, 25052.6809130882], [4, 5, 146997.380630766], [5, 28, 5.38069315091534e19],
119
+ [6, 28, 1.43619827291346e21], [7, 24, 3.64985866165994e19], [8, 1, -2547.41561156775],
120
+ [10, 32, 2.40120197096563e27], [10, 36, -3.93847464679496e29], [14, 22, 1.47073407024852e24],
121
+ [18, 28, -4.26391250432059e31], [20, 36, 1.94509340621077e38], [22, 16, 6.66212132114896e23],
122
+ [22, 28, 7.06777016552858e33], [24, 36, 1.75563621975576e41], [28, 16, 1.08408607429124e28],
123
+ [28, 36, 7.30872705175151e43], [32, 10, 1.5914584739887e24], [32, 28, 3.77121605943324e40],
124
+ ];
125
+ export function r3aPhs(h, s) {
126
+ const mu = h / 2300;
127
+ const sig = s / 4.4;
128
+ return 99 * evalPoly(R3A_PHS, 1.01, 0.75, mu, sig);
129
+ }
130
+ const R3B_PHS = [
131
+ [-12, 2, 1.25244360717979e-13], [-12, 10, -0.0126599322553713], [-12, 12, 5.06878030140626],
132
+ [-12, 14, 31.7847171154202], [-12, 20, -391041.161399932], [-10, 2, -9.75733406392044e-11],
133
+ [-10, 10, -18.6312419488279], [-10, 14, 510.973543414101], [-10, 18, 373847.005822362],
134
+ [-8, 2, 2.99804024666572e-8], [-8, 8, 20.0544393820342], [-6, 2, -4.98030487662829e-6],
135
+ [-6, 6, -10.230180636003], [-6, 7, 55.2819126990325], [-6, 8, -206.211367510878],
136
+ [-5, 10, -7940.12232324823], [-4, 4, 7.82248472028153], [-4, 5, -58.6544326902468],
137
+ [-4, 8, 3550.73647696481], [-3, 1, -1.15303107290162e-4], [-3, 3, -1.75092403171802],
138
+ [-3, 5, 257.98168774816], [-3, 6, -727.048374179467], [-2, 0, 1.21644822609198e-4],
139
+ [-2, 1, 0.0393137871762692], [-1, 0, 7.04181005909296e-3], [0, 3, -82.910820069811],
140
+ [2, 0, -0.26517881813125], [2, 1, 13.7531682453991], [5, 0, -52.2394090753046],
141
+ [6, 1, 2405.56298941048], [8, 1, -22736.1631268929], [10, 1, 89074.6343932567],
142
+ [14, 3, -23923456.5822486], [14, 7, 5687958081.29714],
143
+ ];
144
+ export function r3bPhs(h, s) {
145
+ const mu = h / 2800;
146
+ const sig = s / 5.3;
147
+ return 16.6 / evalPoly(R3B_PHS, 0.681, 0.792, mu, sig);
148
+ }
149
+ function r3Phs(h, s) {
150
+ if (s <= C.R3_S_CRT)
151
+ return r3aPhs(h, s);
152
+ return r3bPhs(h, s);
153
+ }
154
+ function solveGibbsHS(h, s, initial, stateAt) {
155
+ const hScale = Math.max(1, Math.abs(h));
156
+ const sScale = Math.max(1, Math.abs(s));
157
+ const [p, T] = dampedNewton2D(([candidateP, candidateT]) => {
158
+ const state = stateAt(candidateP, candidateT);
159
+ const derivatives = gibbsPropertyDerivatives(state);
160
+ return {
161
+ residual: [
162
+ (state.enthalpy - h) / hScale,
163
+ (state.entropy - s) / sScale,
164
+ ],
165
+ jacobian: [
166
+ [derivatives.dhDp / hScale, derivatives.dhDT / hScale],
167
+ [derivatives.dsDp / sScale, derivatives.dsDT / sScale],
168
+ ],
169
+ };
170
+ }, initial, {
171
+ // Entropy convergence must also resolve pressure at the P_MIN boundary.
172
+ tolerance: BACKWARD_HS_NEWTON_TOLERANCE,
173
+ isValid: ([candidateP, candidateT]) => candidateP > 0 && candidateT > 0,
174
+ });
175
+ return stateAt(p, T);
176
+ }
177
+ function solveRegion3HS(h, s, rho0, T0) {
178
+ const hScale = Math.max(1, Math.abs(h));
179
+ const sScale = Math.max(1, Math.abs(s));
180
+ const [rho, T] = dampedNewton2D(([candidateRho, candidateT]) => {
181
+ const state = region3ByRhoT(candidateRho, candidateT);
182
+ const derivatives = helmholtzPropertyDerivatives(state, candidateRho);
183
+ return {
184
+ residual: [
185
+ (state.enthalpy - h) / hScale,
186
+ (state.entropy - s) / sScale,
187
+ ],
188
+ jacobian: [
189
+ [derivatives.dhDrho / hScale, derivatives.dhDT / hScale],
190
+ [derivatives.dsDrho / sScale, derivatives.dsDT / sScale],
191
+ ],
192
+ };
193
+ }, [rho0, T0], {
194
+ // Tighter than the 1e-10 default and load-bearing: near the 100 MPa ceiling
195
+ // Region 3 is nearly incompressible, so a residual the default would accept
196
+ // still lands p far enough outside [P_MIN, P_MAX] to fail envelope
197
+ // validation. Covered by the p=100 MPa cases in newton-regressions.test.ts.
198
+ tolerance: BACKWARD_HS_NEWTON_TOLERANCE,
199
+ isValid: ([candidateRho, candidateT]) => candidateRho > 0 && candidateT > 0,
200
+ });
201
+ return region3ByRhoT(rho, T);
202
+ }
203
+ // ─── Main HS Solver ─────────────────────────────────────────────────────────
204
+ export function solveHSInRegion(h, s, region, seamInitial, validate = true) {
205
+ // Re-evaluating a converged Region 3 seed can round pressure outside its closed boundary.
206
+ if (!validate && seamInitial?.region === region &&
207
+ Math.abs(seamInitial.enthalpy - h) <= BACKWARD_HS_NEWTON_TOLERANCE * Math.max(1, Math.abs(h)) &&
208
+ Math.abs(seamInitial.entropy - s) <= BACKWARD_HS_NEWTON_TOLERANCE * Math.max(1, Math.abs(s))) {
209
+ return seamInitial;
210
+ }
211
+ switch (region) {
212
+ case Region.Region1: {
213
+ const P0 = seamInitial?.pressure ?? r1Phs(h, s);
214
+ const state = solveGibbsHS(h, s, [P0, seamInitial?.temperature ?? r1BackwardT(P0, h)], region1);
215
+ return !validate ? state : validateBackwardState(state, [
216
+ { label: 'enthalpy', expected: h },
217
+ { label: 'entropy', expected: s },
218
+ ], { solverName: 'solveHS', expectedRegion: Region.Region1 });
219
+ }
220
+ case Region.Region2: {
221
+ const P0 = seamInitial?.pressure ?? Math.max(C.P_MIN, Math.min(C.P_MAX, r2Phs(h, s)));
222
+ const state = solveGibbsHS(h, s, [P0, seamInitial?.temperature ?? r2BackwardT(P0, h)], region2);
223
+ return !validate ? state : validateBackwardState(state, [
224
+ { label: 'enthalpy', expected: h },
225
+ { label: 'entropy', expected: s },
226
+ ], { solverName: 'solveHS', expectedRegion: Region.Region2 });
227
+ }
228
+ case Region.Region3: {
229
+ const P0 = seamInitial?.pressure ?? r3Phs(h, s);
230
+ const initial = seamInitial === undefined
231
+ ? r3BackwardPHInitial(P0, h)
232
+ : { rho: 1 / seamInitial.specificVolume, T: seamInitial.temperature };
233
+ const state = solveRegion3HS(h, s, initial.rho, initial.T);
234
+ if (!validate) {
235
+ if (state.pressure > C.P_MAX &&
236
+ state.pressure - C.P_MAX <= backwardConstraintTolerance('pressure', C.P_MAX)) {
237
+ const constraints = [
238
+ { label: 'enthalpy', expected: h, tolerance: 1e-9 },
239
+ { label: 'entropy', expected: s, tolerance: 1e-12 },
240
+ ];
241
+ const options = { solverName: 'solveHS', expectedRegion: Region.Region3 };
242
+ // Inspect the raw EOS before reconstructing only its closed pressure boundary.
243
+ const error = backwardCandidateError({ ...state, pressure: C.P_MAX }, constraints, options);
244
+ if (error instanceof NumericalStateError)
245
+ throw error;
246
+ if (error === null) {
247
+ const projected = solveRegion3PTBasic(C.P_MAX, state.temperature);
248
+ if (backwardCandidateError(projected, constraints, options) === null)
249
+ return projected;
250
+ }
251
+ }
252
+ if (state.temperature >= C.Tc || backwardCandidateError(state, [
253
+ { label: 'enthalpy', expected: h },
254
+ { label: 'entropy', expected: s },
255
+ ], { solverName: 'solveHS', expectedRegion: Region.Region3 }) !== null)
256
+ return state;
257
+ return solveRegion3PTBasic(state.pressure, state.temperature);
258
+ }
259
+ const validated = validateBackwardState(state, [
260
+ { label: 'enthalpy', expected: h },
261
+ { label: 'entropy', expected: s },
262
+ ], { solverName: 'solveHS', expectedRegion: Region.Region3 });
263
+ if (validated.temperature < C.Tc)
264
+ validateRegion3PTReconstruction(validated, 'solveHS');
265
+ return validated;
266
+ }
267
+ case Region.Region4: {
268
+ const state = tryRegion4HSState(h, s);
269
+ if (state === null) {
270
+ throw new IF97Error('solveHS identified Region 4 but failed to construct a valid saturation state');
271
+ }
272
+ return !validate ? state : validateBackwardState(state, [
273
+ { label: 'enthalpy', expected: h },
274
+ { label: 'entropy', expected: s },
275
+ ], { solverName: 'solveHS', expectedRegion: Region.Region4 });
276
+ }
277
+ case Region.Region5: {
278
+ const state = solveGibbsHS(h, s, [seamInitial?.pressure ?? 1, seamInitial?.temperature ?? 1400], region5);
279
+ return !validate ? state : validateBackwardState(state, [
280
+ { label: 'enthalpy', expected: h },
281
+ { label: 'entropy', expected: s },
282
+ ], { solverName: 'solveHS', expectedRegion: Region.Region5 });
283
+ }
284
+ default:
285
+ throw new IF97Error(`Cannot determine region for h=${h} kJ/kg, s=${s} kJ/(kg·K)`);
286
+ }
287
+ }
288
+ /** Resolve one proven seam identically for the HS solver and detector. */
289
+ export function solveHSSeam(h, s, seam) {
290
+ const constraints = [
291
+ { label: 'enthalpy', expected: h },
292
+ { label: 'entropy', expected: s },
293
+ ];
294
+ const options = { solverName: 'solveHS', seam };
295
+ const adjacent = seam.pair.lower.region === seam.state.region ? seam.pair.upper : seam.pair.lower;
296
+ const solveRegion = (region, initial) => {
297
+ if (initial?.region === Region.Region5 && initial.temperature === C.R5_T_MIN) {
298
+ initial = region5(initial.pressure, nextPositiveFloat(C.R5_T_MIN, 1));
299
+ }
300
+ else if (initial?.region === Region.Region3 && initial.temperature === C.R2_T_MIN) {
301
+ initial = solveRegion3PTBasic(initial.pressure, nextPositiveFloat(C.R2_T_MIN, 1));
302
+ }
303
+ else if (initial?.region === Region.Region2 && seam.pair.seam === '23') {
304
+ initial = region2(initial.pressure, nextPositiveFloat(initial.temperature, 1));
305
+ }
306
+ return solveHSInRegion(h, s, region, initial, false);
307
+ };
308
+ return resolveBackwardCandidates([
309
+ () => tryRegion4HSState(h, s),
310
+ () => seam.state.enthalpy === h && seam.state.entropy === s ? seam.state : null,
311
+ () => solveRegion(seam.state.region),
312
+ () => solveRegion(adjacent.region),
313
+ () => solveRegion(seam.state.region, seam.state),
314
+ () => solveRegion(adjacent.region, adjacent),
315
+ ], constraints, options);
316
+ }
@@ -2,7 +2,7 @@
2
2
  * IAPWS-IF97 Backward Equations: H-S -> P, T
3
3
  *
4
4
  * Given specific enthalpy and entropy, compute pressure and temperature.
5
- * Uses backward P(h,s) equations for initial guess + Nelder-Mead refinement.
5
+ * Uses backward P(h,s) equations for initial guesses + damped Newton refinement.
6
6
  *
7
7
  * Reference: IAPWS-IF97 Supplementary Release on Backward Equations p(h,s)
8
8
  */
@@ -13,4 +13,3 @@ import type { BasicProperties } from '../types.js';
13
13
  * @param s - Specific entropy [kJ/(kg·K)]
14
14
  */
15
15
  export declare function solveHS(h: number, s: number): BasicProperties;
16
- //# sourceMappingURL=hs.d.ts.map
@@ -2,191 +2,17 @@
2
2
  * IAPWS-IF97 Backward Equations: H-S -> P, T
3
3
  *
4
4
  * Given specific enthalpy and entropy, compute pressure and temperature.
5
- * Uses backward P(h,s) equations for initial guess + Nelder-Mead refinement.
5
+ * Uses backward P(h,s) equations for initial guesses + damped Newton refinement.
6
6
  *
7
7
  * Reference: IAPWS-IF97 Supplementary Release on Backward Equations p(h,s)
8
8
  */
9
9
  import { Region, IF97Error, OutOfRangeError } from '../types.js';
10
+ import { RegionMismatchError } from '../errors.js';
10
11
  import * as C from '../constants.js';
11
- import { region1 } from '../regions/region1.js';
12
- import { region2 } from '../regions/region2.js';
13
- import { region3ByRhoT } from '../regions/region3.js';
14
- import { region5 } from '../regions/region5.js';
15
- import { assertFiniteNumber } from '../core/input-validation.js';
16
- import { detectRegionHS } from '../core/region-detector.js';
17
- import { newtonRaphson } from '../solvers/newton-raphson.js';
18
- import { nelderMead } from '../solvers/nelder-mead.js';
19
- import { solvePH } from './ph.js';
20
- import { validateBackwardState } from './solution-validation.js';
21
- import { sumNormalizedResiduals } from './objective-normalization.js';
22
- import { backwardConstraintTolerance } from './tolerances.js';
23
- import { tryRegion4HSState, } from '../saturation/region4-hs.js';
24
- import { assertCriticalRegion4HSInput, } from '../saturation/region4-boundaries.js';
25
- // ─── Backward Polynomial Evaluator ─────────────────────────────────────────
26
- function evalPoly(table, aShift, bShift, a, b) {
27
- let sum = 0;
28
- for (const [I, J, N] of table) {
29
- sum += N * Math.pow(a - aShift, I) * Math.pow(b - bShift, J);
30
- }
31
- return sum;
32
- }
33
- // ─── Region 1 P(h,s) — 19 coefficients ─────────────────────────────────────
34
- const R1_PHS = [
35
- [0, 0, -0.691997014660582], [0, 1, -18.361254878756], [0, 2, -9.28332409297335],
36
- [0, 4, 65.9639569909906], [0, 5, -16.2060388912024], [0, 6, 450.620017338667],
37
- [0, 8, 854.68067822417], [0, 14, 6075.23214001161], [1, 0, 32.6487682621856],
38
- [1, 1, -26.9408844582931], [1, 4, -319.9478483343], [1, 6, -928.35430704332],
39
- [2, 0, 30.3634537455249], [2, 1, -65.0540422444146], [2, 10, -4309.1316516130],
40
- [3, 4, -747.512324096068], [4, 1, 730.000345529245], [4, 4, 1142.84032569021],
41
- [5, 0, -436.407041874559],
42
- ];
43
- function r1Phs(h, s) {
44
- const mu = h / 3400;
45
- const sig = s / 7.6;
46
- return 100 * evalPoly(R1_PHS, -0.05, -0.05, mu, sig);
47
- }
48
- // ─── Region 2 P(h,s) — Subregions 2a, 2b, 2c ──────────────────────────────
49
- /** Boundary between Region 2a and Region 2b in h-s plane. */
50
- function b2abBoundary(s) {
51
- return -3498.98083432139 + 2575.60716905876 * s - 421.073558227969 * s * s + 27.6349063799944 * s * s * s;
52
- }
53
- const R2A_PHS = [
54
- [0, 1, -0.0182575361923032], [0, 3, -0.125229548799536], [0, 6, 0.592290437320145],
55
- [0, 16, 6.04769706185122], [0, 20, 238.624965444474], [0, 22, -298.639090222922],
56
- [1, 0, 0.051225081304075], [1, 1, -0.437266515606486], [1, 2, 0.413336902999504],
57
- [1, 3, -5.16468254574773], [1, 5, -5.57014838445711], [1, 6, 12.8555037824478],
58
- [1, 10, 11.414410895329], [1, 16, -119.504225652714], [1, 20, -2847.7798596156],
59
- [1, 22, 4317.57846408006], [2, 3, 1.1289404080265], [2, 16, 1974.09186206319],
60
- [2, 20, 1516.12444706087], [3, 0, 0.0141324451421235], [3, 2, 0.585501282219601],
61
- [3, 3, -2.97258075863012], [3, 6, 5.94567314847319], [3, 16, -6236.56565798905],
62
- [4, 16, 9659.86235133332], [5, 3, 6.81500934948134], [5, 16, -6332.07286824489],
63
- [6, 3, -5.5891922446576], [7, 1, 0.0400645798472063],
64
- ];
65
- function r2aPhs(h, s) {
66
- const mu = h / 4200;
67
- const sig = s / 12;
68
- const P = evalPoly(R2A_PHS, 0.5, 1.2, mu, sig);
69
- return 4 * P * P * P * P;
70
- }
71
- const R2B_PHS = [
72
- [0, 0, 0.0801496989929495], [0, 1, -0.543862807146111], [0, 2, 0.337455597421283],
73
- [0, 4, 8.9055545115745], [0, 8, 313.840736431485], [1, 0, 0.797367065977789],
74
- [1, 1, -1.2161697355624], [1, 2, 8.72803386937477], [1, 3, -16.9769781757602],
75
- [1, 5, -186.552827328416], [1, 12, 95115.9274344237], [2, 1, -18.9168510120494],
76
- [2, 6, -4334.0703719484], [2, 18, 543212633.012715], [3, 0, 0.144793408386013],
77
- [3, 1, 128.024559637516], [3, 7, -67230.9534071268], [3, 12, 33697238.0095287],
78
- [4, 1, -586.63419676272], [4, 16, -22140322476.9889], [5, 1, 1716.06668708389],
79
- [5, 12, -570817595.806302], [6, 1, -3121.09693178482], [6, 8, -2078413.8463301],
80
- [6, 18, 3056059461577.86], [7, 1, 3221.57004314333], [7, 16, 326810259797.295],
81
- [8, 1, -1441.04158934487], [8, 3, 410.694867802691], [8, 14, 109077066873.024],
82
- [8, 18, -24796465425889.3], [12, 10, 1888019068.65134], [14, 16, -123651009018773],
83
- ];
84
- function r2bPhs(h, s) {
85
- const mu = h / 4100;
86
- const sig = s / 7.9;
87
- const P = evalPoly(R2B_PHS, 0.6, 1.01, mu, sig);
88
- return 100 * P * P * P * P;
89
- }
90
- const R2C_PHS = [
91
- [0, 0, 0.112225607199012], [0, 1, -3.39005953606712], [0, 2, -32.0503911730094],
92
- [0, 3, -197.5973051049], [0, 4, -407.693861553446], [0, 8, 13294.3775222331],
93
- [1, 0, 1.70846839774007], [1, 2, 37.3694198142245], [1, 5, 3581.44365815434],
94
- [1, 8, 423014.446424664], [1, 14, -751071025.760063], [2, 2, 52.3446127607898],
95
- [2, 3, -228.351290812417], [2, 7, -960652.417056937], [2, 10, -80705929.2526074],
96
- [2, 18, 1626980172256.69], [3, 0, 0.772465073604171], [3, 5, 46392.9973837746],
97
- [3, 8, -13731788.5134128], [3, 16, 1704703926305.12], [3, 18, -25110462818730.8],
98
- [4, 18, 31774883083552.0], [5, 1, 53.8685623675312], [5, 4, -55308.9094625169],
99
- [5, 6, -1028615.22421405], [5, 14, 2042494187562.34], [6, 8, 273918446.626977],
100
- [6, 18, -2.63963146312685e15], [10, 7, -1078908541.08088], [12, 7, -29649262098.0124],
101
- [16, 10, -1.11754907323424e15],
102
- ];
103
- function r2cPhs(h, s) {
104
- const mu = h / 3500;
105
- const sig = s / 5.9;
106
- const P = evalPoly(R2C_PHS, 0.7, 1.1, mu, sig);
107
- return 100 * P * P * P * P;
108
- }
109
- function r2Phs(h, s) {
110
- const hBound = b2abBoundary(s);
111
- // Region 2 h-s subregion selection: 2a below the h(s) boundary,
112
- // otherwise split 2b/2c at the published entropy crossover.
113
- if (h <= hBound)
114
- return r2aPhs(h, s);
115
- if (s >= C.R2_S_CRT)
116
- return r2bPhs(h, s);
117
- return r2cPhs(h, s);
118
- }
119
- // ─── Region 3 P(h,s) — Subregions 3a, 3b ───────────────────────────────────
120
- const R3A_PHS = [
121
- [0, 0, 7.70889828326934], [0, 1, -26.0835009128688], [0, 5, 267.416218930389],
122
- [1, 0, 17.2221089496844], [1, 3, -293.54233214597], [1, 4, 614.135601882478],
123
- [1, 8, -61056.2757725674], [1, 14, -65127225.1118219], [2, 6, 73591.9313521937],
124
- [2, 16, -11664650591.4191], [3, 0, 35.5267086434461], [3, 2, -596.144543825955],
125
- [3, 3, -475.842430145708], [4, 0, 69.6781965359503], [4, 1, 335.674250377312],
126
- [4, 4, 25052.6809130882], [4, 5, 146997.380630766], [5, 28, 5.38069315091534e19],
127
- [6, 28, 1.43619827291346e21], [7, 24, 3.64985866165990e19], [8, 1, -2547.41561156775],
128
- [10, 32, 2.40120197096563e27], [10, 36, -3.93847464679496e29], [14, 22, 1.47073407024852e24],
129
- [18, 28, -4.26391250432059e31], [20, 36, 1.94509340621077e38], [22, 16, 6.66212132114896e23],
130
- [22, 28, 7.06777016552858e33], [24, 36, 1.75563621975576e41], [28, 16, 1.08408607429124e28],
131
- [28, 36, 7.30872705175151e43], [32, 10, 1.5914584739887e24], [32, 28, 3.77121605943324e40],
132
- ];
133
- function r3aPhs(h, s) {
134
- const mu = h / 2300;
135
- const sig = s / 4.4;
136
- return 99 * evalPoly(R3A_PHS, 1.01, 0.75, mu, sig);
137
- }
138
- const R3B_PHS = [
139
- [-12, 2, 1.25244360717979e-13], [-12, 10, -0.0126599322553713], [-12, 12, 5.06878030140626],
140
- [-12, 14, 31.7847171154202], [-12, 20, -391041.161399932], [-10, 2, -9.75733406392044e-11],
141
- [-10, 10, -18.6312419488279], [-10, 14, 510.973543414101], [-10, 18, 373847.005822362],
142
- [-8, 2, 2.99804024666572e-8], [-8, 8, 20.0544393820342], [-6, 2, -4.98030487662829e-6],
143
- [-6, 6, -10.230180636003], [-6, 7, 55.2819126990325], [-6, 8, -206.211367510878],
144
- [-5, 10, -7940.12232324823], [-4, 4, 7.82248472028153], [-4, 5, -58.6544326902468],
145
- [-4, 8, 3550.73647696481], [-3, 1, -1.15303107290162e-4], [-3, 3, -1.75092403171802],
146
- [-3, 5, 257.98168774816], [-3, 6, -727.048374179467], [-2, 0, 1.21644822609198e-4],
147
- [-2, 1, 0.0393137871762692], [-1, 0, 7.04181005909296e-3], [0, 3, -82.910820069811],
148
- [2, 0, -0.26517881813125], [2, 1, 13.7531682453991], [5, 0, -52.3940907530460],
149
- [6, 1, 2405.56298941048], [8, 1, -22736.1631268929], [10, 1, 89074.6343932567],
150
- [14, 3, -23923456.5822486], [14, 7, 5687958081.29714],
151
- ];
152
- function r3bPhs(h, s) {
153
- const mu = h / 2800;
154
- const sig = s / 5.3;
155
- return 16.6 / evalPoly(R3B_PHS, 0.681, 0.792, mu, sig);
156
- }
157
- function r3Phs(h, s) {
158
- if (s <= C.R3_S_CRT)
159
- return r3aPhs(h, s);
160
- return r3bPhs(h, s);
161
- }
162
- const REGION5_HS_OUT_OF_DOMAIN_PENALTY = 1e18;
163
- function normalizedHsObjective(h, s, stateAt) {
164
- const enthalpyTolerance = backwardConstraintTolerance('enthalpy', h);
165
- const entropyTolerance = backwardConstraintTolerance('entropy', s);
166
- return (pair) => {
167
- const state = stateAt(pair);
168
- return sumNormalizedResiduals([
169
- { actual: state.enthalpy, expected: h, tolerance: enthalpyTolerance },
170
- { actual: state.entropy, expected: s, tolerance: entropyTolerance },
171
- ]);
172
- };
173
- }
174
- function normalizedRegion5HsObjective(h, s) {
175
- const enthalpyTolerance = backwardConstraintTolerance('enthalpy', h);
176
- const entropyTolerance = backwardConstraintTolerance('entropy', s);
177
- return (pair) => {
178
- const [p, T] = pair;
179
- if (p <= 0 || p > C.R5_P_MAX || T <= C.R5_T_MIN || T > C.R5_T_MAX) {
180
- return REGION5_HS_OUT_OF_DOMAIN_PENALTY;
181
- }
182
- const state = region5(p, T);
183
- return sumNormalizedResiduals([
184
- { actual: state.enthalpy, expected: h, tolerance: enthalpyTolerance },
185
- { actual: state.entropy, expected: s, tolerance: entropyTolerance },
186
- ]);
187
- };
188
- }
189
- // ─── Main HS Solver ─────────────────────────────────────────────────────────
12
+ import { assertFiniteNumber, withinClosedLowerBound } from '../core/input-validation.js';
13
+ import { classifyRegionHS } from './hs-region.js';
14
+ import { solveHSInRegion } from './hs-state.js';
15
+ import { assertCriticalRegion4HSInput } from '../saturation/region4-boundaries.js';
190
16
  /**
191
17
  * Solve for thermodynamic state given H and S.
192
18
  * @param h - Specific enthalpy [kJ/kg]
@@ -195,66 +21,36 @@ function normalizedRegion5HsObjective(h, s) {
195
21
  export function solveHS(h, s) {
196
22
  assertFiniteNumber('Enthalpy', h);
197
23
  assertFiniteNumber('Entropy', s);
198
- if (h < C.H_MIN || h > C.H_MAX) {
24
+ if (!withinClosedLowerBound(h, C.H_MIN, 'enthalpy') || h > C.H_MAX) {
199
25
  throw new OutOfRangeError('Enthalpy', h, C.H_MIN, C.H_MAX);
200
26
  }
201
- if (s < C.S_MIN || s > C.S_MAX) {
27
+ if (!withinClosedLowerBound(s, C.S_MIN, 'entropy') || s > C.S_MAX) {
202
28
  throw new OutOfRangeError('Entropy', s, C.S_MIN, C.S_MAX);
203
29
  }
204
30
  assertCriticalRegion4HSInput(h, s, 'solveHS');
205
- const region4State = tryRegion4HSState(h, s);
206
- if (region4State !== null) {
207
- return validateBackwardState(region4State, [
208
- { label: 'enthalpy', expected: h },
209
- { label: 'entropy', expected: s },
210
- ], { solverName: 'solveHS', expectedRegion: Region.Region4 });
31
+ const candidate = {};
32
+ const region = classifyRegionHS(h, s, candidate);
33
+ if (candidate.state !== undefined)
34
+ return candidate.state;
35
+ try {
36
+ return solveHSInRegion(h, s, region);
211
37
  }
212
- const region = detectRegionHS(h, s);
213
- switch (region) {
214
- case Region.Region1: {
215
- const P0 = r1Phs(h, s);
216
- const objective = normalizedHsObjective(h, s, (pair) => region1(pair[0], pair[1]));
217
- const sol = nelderMead(objective, [P0, 400], { maxIterations: 1000, minErrorDelta: 1e-8, minTolerance: 1e-9 });
218
- return validateBackwardState(region1(sol.x[0], sol.x[1]), [
219
- { label: 'enthalpy', expected: h },
220
- { label: 'entropy', expected: s },
221
- ], { solverName: 'solveHS', expectedRegion: Region.Region1 });
222
- }
223
- case Region.Region2: {
224
- const P0 = r2Phs(h, s);
225
- const T0 = newtonRaphson((T) => region2(P0, T).enthalpy - h, 500);
226
- const objective = normalizedHsObjective(h, s, (pair) => region2(pair[0], pair[1]));
227
- const sol = nelderMead(objective, [P0, T0], { maxIterations: 1000, minErrorDelta: 1e-8, minTolerance: 1e-9 });
228
- return validateBackwardState(region2(sol.x[0], sol.x[1]), [
229
- { label: 'enthalpy', expected: h },
230
- { label: 'entropy', expected: s },
231
- ], { solverName: 'solveHS', expectedRegion: Region.Region2 });
232
- }
233
- case Region.Region3: {
234
- const P0 = r3Phs(h, s);
235
- const init = solvePH(P0, h);
236
- const rho0 = 1 / init.specificVolume;
237
- const T0 = init.temperature;
238
- const objective = normalizedHsObjective(h, s, (pair) => region3ByRhoT(pair[0], pair[1]));
239
- const sol = nelderMead(objective, [rho0, T0], { maxIterations: 1000, minErrorDelta: 1e-8, minTolerance: 1e-9 });
240
- return validateBackwardState(region3ByRhoT(sol.x[0], sol.x[1]), [
241
- { label: 'enthalpy', expected: h },
242
- { label: 'entropy', expected: s },
243
- ], { solverName: 'solveHS', expectedRegion: Region.Region3 });
38
+ catch (error) {
39
+ if (!(error instanceof RegionMismatchError) || error.solverName !== 'solveHS' ||
40
+ error.expectedRegion !== region)
41
+ throw error;
42
+ const adjacent = error.actualRegion;
43
+ if (!(adjacent === Region.Region3 && (region === Region.Region1 || region === Region.Region2)) &&
44
+ !(region === Region.Region3 && (adjacent === Region.Region1 || adjacent === Region.Region2))) {
45
+ throw error;
244
46
  }
245
- case Region.Region4: {
246
- throw new IF97Error('solveHS identified Region 4 but failed to construct a valid saturation state');
47
+ try {
48
+ return solveHSInRegion(h, s, adjacent);
247
49
  }
248
- case Region.Region5: {
249
- const objective = normalizedRegion5HsObjective(h, s);
250
- const sol = nelderMead(objective, [1, 1400], { maxIterations: 1000, minErrorDelta: 1e-8, minTolerance: 1e-9 });
251
- return validateBackwardState(region5(sol.x[0], sol.x[1]), [
252
- { label: 'enthalpy', expected: h },
253
- { label: 'entropy', expected: s },
254
- ], { solverName: 'solveHS', expectedRegion: Region.Region5 });
50
+ catch (alternateError) {
51
+ if (!(alternateError instanceof IF97Error))
52
+ throw alternateError;
53
+ throw error;
255
54
  }
256
- default:
257
- throw new IF97Error(`Cannot determine region for h=${h} kJ/kg, s=${s} kJ/(kg·K)`);
258
55
  }
259
56
  }
260
- //# sourceMappingURL=hs.js.map
@@ -7,15 +7,20 @@
7
7
  * Reference: IAPWS-IF97 Supplementary Release on Backward Equations
8
8
  */
9
9
  import type { BasicProperties } from '../types.js';
10
+ export declare function r1BackwardT(p: number, h: number): number;
11
+ /** B2bc boundary: h → p, discriminating subregions 2b and 2c.
12
+ * Ref: IAPWS-IF97, Eq. 20. Check point: h = 3516.004323 → p = 100 MPa. */
10
13
  export declare function b2bc_H_P(h: number): number;
14
+ export declare function r2BackwardT(p: number, h: number): number;
11
15
  /** B3ab boundary: P → H */
12
16
  export declare function b3ab_P_to_H(p: number): number;
13
- /** R4: enthalpy → saturation pressure for PH boundary detection */
14
- export declare function r4EnthalpyToPsat(h: number): number;
17
+ export declare function r3BackwardPHInitial(p: number, h: number): {
18
+ rho: number;
19
+ T: number;
20
+ };
15
21
  /**
16
22
  * Solve for thermodynamic state given P and H.
17
23
  * @param p - Pressure [MPa]
18
24
  * @param h - Specific enthalpy [kJ/kg]
19
25
  */
20
26
  export declare function solvePH(p: number, h: number): BasicProperties;
21
- //# sourceMappingURL=ph.d.ts.map