iapws-if97 2.2.0 → 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 (83) hide show
  1. package/CHANGELOG.md +25 -2
  2. package/README.md +69 -246
  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 +3 -3
  8. package/dist/backward/fixed-temperature-solver.js +38 -17
  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.js +28 -242
  16. package/dist/backward/ph.d.ts +2 -0
  17. package/dist/backward/ph.js +72 -21
  18. package/dist/backward/pressure-property-region.d.ts +13 -0
  19. package/dist/backward/pressure-property-region.js +262 -0
  20. package/dist/backward/ps.d.ts +6 -0
  21. package/dist/backward/ps.js +81 -28
  22. package/dist/backward/solution-validation.d.ts +7 -1
  23. package/dist/backward/solution-validation.js +138 -41
  24. package/dist/backward/th.d.ts +3 -5
  25. package/dist/backward/th.js +10 -71
  26. package/dist/backward/tolerances.d.ts +3 -0
  27. package/dist/backward/tolerances.js +9 -3
  28. package/dist/backward/ts.d.ts +3 -5
  29. package/dist/backward/ts.js +10 -71
  30. package/dist/constants.d.ts +11 -15
  31. package/dist/constants.js +14 -15
  32. package/dist/core/floating-point.d.ts +2 -0
  33. package/dist/core/floating-point.js +7 -0
  34. package/dist/core/formulation-seams.d.ts +27 -0
  35. package/dist/core/formulation-seams.js +289 -0
  36. package/dist/core/input-validation.d.ts +1 -0
  37. package/dist/core/input-validation.js +5 -0
  38. package/dist/core/region-detector.d.ts +3 -40
  39. package/dist/core/region-detector.js +37 -762
  40. package/dist/core/region3-pt.d.ts +1 -1
  41. package/dist/core/region3-pt.js +42 -16
  42. package/dist/core/solve-input-normalization.js +4 -1
  43. package/dist/core/solver.d.ts +7 -10
  44. package/dist/core/solver.js +26 -17
  45. package/dist/detect.d.ts +16 -2
  46. package/dist/detect.js +36 -2
  47. package/dist/errors.d.ts +11 -0
  48. package/dist/errors.js +16 -0
  49. package/dist/gas-constant.d.ts +5 -0
  50. package/dist/gas-constant.js +5 -0
  51. package/dist/index.d.ts +4 -5
  52. package/dist/index.js +10 -21
  53. package/dist/regions/region1.d.ts +2 -1
  54. package/dist/regions/region1.js +1 -1
  55. package/dist/regions/region2.d.ts +2 -1
  56. package/dist/regions/region2.js +1 -1
  57. package/dist/regions/region3-data.js +3 -3
  58. package/dist/regions/region3-subregions.js +15 -10
  59. package/dist/regions/region3.d.ts +4 -1
  60. package/dist/regions/region3.js +32 -2
  61. package/dist/regions/region4.d.ts +2 -0
  62. package/dist/regions/region4.js +39 -12
  63. package/dist/regions/region5.d.ts +2 -1
  64. package/dist/regions/region5.js +2 -2
  65. package/dist/saturation/common.d.ts +2 -8
  66. package/dist/saturation/common.js +64 -36
  67. package/dist/saturation/region4-boundaries.js +13 -13
  68. package/dist/saturation/region4-hs.js +282 -58
  69. package/dist/saturation/two-phase.d.ts +2 -2
  70. package/dist/saturation/two-phase.js +7 -7
  71. package/dist/solvers/bracketed-newton.d.ts +6 -3
  72. package/dist/solvers/bracketed-newton.js +15 -8
  73. package/dist/solvers/extended-arithmetic.d.ts +6 -0
  74. package/dist/solvers/extended-arithmetic.js +38 -0
  75. package/dist/transport/properties.d.ts +4 -2
  76. package/dist/transport/properties.js +14 -4
  77. package/dist/types.d.ts +18 -0
  78. package/dist/types.js +14 -0
  79. package/package.json +4 -4
  80. package/dist/core/public-normalization.d.ts +0 -3
  81. package/dist/core/public-normalization.js +0 -53
  82. /package/dist/{backward → core}/thermodynamic-derivatives.d.ts +0 -0
  83. /package/dist/{backward → core}/thermodynamic-derivatives.js +0 -0
@@ -35,6 +35,7 @@ const MU1_H = [
35
35
  * Dynamic viscosity of water/steam [Pa·s]
36
36
  * @param T Temperature [K]
37
37
  * @param rho Density [kg/m³]
38
+ * @throws {IF97Error} If the calculated viscosity is non-finite or non-positive.
38
39
  */
39
40
  export function viscosity(T, rho) {
40
41
  assertFiniteNumber('Temperature', T);
@@ -64,7 +65,10 @@ export function viscosity(T, rho) {
64
65
  // (within ~1 K and ~1 MPa). The original reference library also omits it.
65
66
  // Keeping μ₂ = 1 (no enhancement) for practical use.
66
67
  const mu2 = 1;
67
- return mu0 * mu1 * mu2 / 1e6; // Convert μPa·s to Pa·s
68
+ const mu = mu0 * mu1 * mu2 / 1e6; // Convert μPa·s to Pa·s
69
+ assertFiniteNumber('Dynamic viscosity', mu);
70
+ assertPositiveNumber('Dynamic viscosity', mu);
71
+ return mu;
68
72
  }
69
73
  // ─── Thermal Conductivity (IAPWS 2011) ──────────────────────────────────────
70
74
  const K0_L = [2.443221e-3, 1.323095e-2, 6.770357e-3, -3.454586e-3, 4.096266e-4];
@@ -92,7 +96,7 @@ const DRHO_REF_COEFFS = [
92
96
  *
93
97
  * @param T Temperature [K]
94
98
  * @param rho Density [kg/m³]
95
- * @param cp Isobaric heat capacity [kJ/(kg·K)] (optional)
99
+ * @param cp Positive isobaric heat capacity [kJ/(kg·K)] (optional)
96
100
  * @param cv Isochoric heat capacity [kJ/(kg·K)] (optional)
97
101
  * @param drhodP_T Isothermal density derivative ∂ρ/∂P|_T [kg/m³/MPa] (optional)
98
102
  * @param mu Dynamic viscosity [Pa·s] (optional)
@@ -102,8 +106,10 @@ export function thermalConductivity(T, rho, cp, cv, drhodP_T, mu) {
102
106
  assertFiniteNumber('Density', rho);
103
107
  assertPositiveNumber('Temperature', T);
104
108
  assertNonNegativeNumber('Density', rho);
105
- if (cp !== undefined)
109
+ if (cp !== undefined) {
106
110
  assertFiniteNumber('Isobaric heat capacity', cp);
111
+ assertPositiveNumber('Isobaric heat capacity', cp);
112
+ }
107
113
  if (cv !== undefined) {
108
114
  assertFiniteNumber('Isochoric heat capacity', cv);
109
115
  assertPositiveNumber('Isochoric heat capacity', cv);
@@ -198,14 +204,18 @@ const DC_Ih = [1, 1, 1, 2, 3, 3, 4, 5, 6, 7, 10];
198
204
  const DC_Jh = [0.25, 1, 2.5, 1.5, 1.5, 2.5, 2, 2, 5, 0.5, 10];
199
205
  /**
200
206
  * Static dielectric constant [dimensionless]
201
- * @param T Temperature [K]
207
+ * @param T Temperature [K], must be greater than 228 K
202
208
  * @param rho Density [kg/m³]
209
+ * @throws {IF97Error} If T is at or below the equation's 228 K singularity.
203
210
  */
204
211
  export function dielectricConstant(T, rho) {
205
212
  assertFiniteNumber('Temperature', T);
206
213
  assertFiniteNumber('Density', rho);
207
214
  assertPositiveNumber('Temperature', T);
208
215
  assertNonNegativeNumber('Density', rho);
216
+ if (T <= 228) {
217
+ throw new IF97Error('Temperature must be greater than 228 K for the dielectric constant');
218
+ }
209
219
  const MW = 0.018015268;
210
220
  const rhoM = rho / MW;
211
221
  const rhoRc = rhoM / (RHOc / MW);
package/dist/types.d.ts CHANGED
@@ -46,6 +46,8 @@ export interface SteamState {
46
46
  speedOfSound: number | null;
47
47
  /** Vapour quality (0–1), only defined on accepted Region 4 saturation states */
48
48
  quality: number | null;
49
+ /** Saturation phases without transport properties, only present for Region 4. */
50
+ saturation?: SaturationEndpoints;
49
51
  /** Dynamic viscosity [Pa·s], null for two-phase mixtures */
50
52
  viscosity: number | null;
51
53
  /** Thermal conductivity [W/(m·K)], null for two-phase mixtures */
@@ -79,6 +81,14 @@ export interface BasicProperties {
79
81
  quality: number | null;
80
82
  isobaricExpansion: number | null;
81
83
  isothermalCompressibility: number | null;
84
+ saturation?: SaturationEndpoints;
85
+ }
86
+ /** Saturated liquid/vapor EOS states without nested saturation metadata. */
87
+ export interface SaturationEndpoints {
88
+ pressure: number;
89
+ temperature: number;
90
+ liquid: Omit<BasicProperties, 'saturation'>;
91
+ vapor: Omit<BasicProperties, 'saturation'>;
82
92
  }
83
93
  /** Supported thermodynamic input combinations */
84
94
  export type InputMode = 'PT' | 'PH' | 'PS' | 'HS' | 'Px' | 'Tx' | 'TH' | 'TS';
@@ -137,6 +147,14 @@ export type CoefficientTable = readonly (readonly [number, number, number])[];
137
147
  export declare class IF97Error extends Error {
138
148
  constructor(message: string);
139
149
  }
150
+ /** Thrown when TH or TS inputs identify multiple physical states. */
151
+ export declare class AmbiguousStateError extends IF97Error {
152
+ /** Validated basic states in ascending pressure order; order does not imply preference. */
153
+ readonly candidates: readonly BasicProperties[];
154
+ constructor(T: number, value: number, property?: 'enthalpy' | 'entropy',
155
+ /** Validated basic states in ascending pressure order; order does not imply preference. */
156
+ candidates?: readonly BasicProperties[]);
157
+ }
140
158
  /** Thrown when input values are outside IAPWS-IF97 valid ranges */
141
159
  export declare class OutOfRangeError extends IF97Error {
142
160
  readonly parameter: string;
package/dist/types.js CHANGED
@@ -27,6 +27,20 @@ export class IF97Error extends Error {
27
27
  this.name = 'IF97Error';
28
28
  }
29
29
  }
30
+ /** Thrown when TH or TS inputs identify multiple physical states. */
31
+ export class AmbiguousStateError extends IF97Error {
32
+ candidates;
33
+ constructor(T, value, property = 'enthalpy',
34
+ /** Validated basic states in ascending pressure order; order does not imply preference. */
35
+ candidates = []) {
36
+ const symbol = property === 'enthalpy' ? 'h' : 's';
37
+ const unit = property === 'enthalpy' ? 'kJ/kg' : 'kJ/(kg·K)';
38
+ const pressureMode = property === 'enthalpy' ? 'PH' : 'PS';
39
+ super(`Ambiguous T-${symbol} state for T=${T} K, ${symbol}=${value} ${unit}: multiple physical states exist; supply an independent state constraint, for example using PT, ${pressureMode}, or Tx`);
40
+ this.candidates = candidates;
41
+ this.name = 'AmbiguousStateError';
42
+ }
43
+ }
30
44
  /** Thrown when input values are outside IAPWS-IF97 valid ranges */
31
45
  export class OutOfRangeError extends IF97Error {
32
46
  parameter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iapws-if97",
3
- "version": "2.2.0",
3
+ "version": "2.8.0",
4
4
  "description": "TypeScript implementation of IAPWS-IF97 for water and steam properties, including forward/backward solvers, saturation states, and transport properties.",
5
5
  "author": "jltonghui",
6
6
  "repository": {
@@ -64,15 +64,15 @@
64
64
  "scripts": {
65
65
  "prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
66
66
  "build": "tsc",
67
+ "typecheck": "tsc -p tsconfig.test.json",
67
68
  "test": "vitest run",
68
- "test:package": "npm run build && node scripts/check-package.mjs",
69
+ "test:package": "npm run build && node --test scripts/check-package.test.mjs",
69
70
  "test:watch": "vitest",
70
71
  "test:coverage": "vitest run --coverage",
71
72
  "test:standards": "vitest run tests/standards-asme tests/standards-cn",
72
73
  "test:standards:asme": "vitest run tests/standards-asme",
73
74
  "test:standards:cn": "vitest run tests/standards-cn",
74
- "prepack": "npm run test:package && npm test",
75
- "prepublishOnly": "npm run build && npm test"
75
+ "prepack": "npm run typecheck && npm run test:package && npm run test:coverage"
76
76
  },
77
77
  "devDependencies": {
78
78
  "@vitest/coverage-v8": "^4.1.0",
@@ -1,3 +0,0 @@
1
- import type { BasicProperties, SteamState } from '../types.js';
2
- export declare function normalizePublicState(state: SteamState): SteamState;
3
- export declare function normalizePublicState(state: BasicProperties): BasicProperties;
@@ -1,53 +0,0 @@
1
- // Only snap values that are effectively simple decimals plus machine-noise.
2
- const SIMPLE_DECIMAL_PLACES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
3
- const SNAP_ULPS = 8;
4
- function snapSimpleDecimal(value) {
5
- if (!Number.isFinite(value)) {
6
- return value;
7
- }
8
- for (const decimals of SIMPLE_DECIMAL_PLACES) {
9
- const factor = 10 ** decimals;
10
- const candidate = Math.round(value * factor) / factor;
11
- // Scale the acceptance window with magnitude so we only absorb
12
- // binary floating-point residue, not meaningful engineering deltas.
13
- const tolerance = SNAP_ULPS * Number.EPSILON * Math.max(1, Math.abs(value), Math.abs(candidate));
14
- if (Math.abs(value - candidate) <= tolerance) {
15
- return Object.is(candidate, -0) ? 0 : candidate;
16
- }
17
- }
18
- return Object.is(value, -0) ? 0 : value;
19
- }
20
- function normalizeNullableNumber(value) {
21
- return value === null ? null : snapSimpleDecimal(value);
22
- }
23
- function normalizeBasicProperties(state) {
24
- return {
25
- region: state.region,
26
- pressure: snapSimpleDecimal(state.pressure),
27
- temperature: snapSimpleDecimal(state.temperature),
28
- specificVolume: snapSimpleDecimal(state.specificVolume),
29
- internalEnergy: snapSimpleDecimal(state.internalEnergy),
30
- entropy: snapSimpleDecimal(state.entropy),
31
- enthalpy: snapSimpleDecimal(state.enthalpy),
32
- cp: normalizeNullableNumber(state.cp),
33
- cv: normalizeNullableNumber(state.cv),
34
- speedOfSound: normalizeNullableNumber(state.speedOfSound),
35
- quality: normalizeNullableNumber(state.quality),
36
- isobaricExpansion: normalizeNullableNumber(state.isobaricExpansion),
37
- isothermalCompressibility: normalizeNullableNumber(state.isothermalCompressibility),
38
- };
39
- }
40
- function normalizeSteamState(state) {
41
- return {
42
- ...normalizeBasicProperties(state),
43
- density: snapSimpleDecimal(state.density),
44
- viscosity: normalizeNullableNumber(state.viscosity),
45
- thermalConductivity: normalizeNullableNumber(state.thermalConductivity),
46
- surfaceTension: normalizeNullableNumber(state.surfaceTension),
47
- dielectricConstant: normalizeNullableNumber(state.dielectricConstant),
48
- ionizationConstant: normalizeNullableNumber(state.ionizationConstant),
49
- };
50
- }
51
- export function normalizePublicState(state) {
52
- return 'density' in state ? normalizeSteamState(state) : normalizeBasicProperties(state);
53
- }