Compare commits
1 Commits
6be3bf92ef
...
5df3881375
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5df3881375 |
4
index.js
4
index.js
@@ -14,6 +14,7 @@ const validation = require('./src/helper/validationUtils.js');
|
|||||||
const configUtils = require('./src/helper/configUtils.js');
|
const configUtils = require('./src/helper/configUtils.js');
|
||||||
const assertions = require('./src/helper/assertionUtils.js')
|
const assertions = require('./src/helper/assertionUtils.js')
|
||||||
const coolprop = require('./src/coolprop-node/src/index.js');
|
const coolprop = require('./src/coolprop-node/src/index.js');
|
||||||
|
const gravity = require('./src/helper/gravity.js')
|
||||||
|
|
||||||
// Domain-specific modules
|
// Domain-specific modules
|
||||||
const { MeasurementContainer } = require('./src/measurements/index.js');
|
const { MeasurementContainer } = require('./src/measurements/index.js');
|
||||||
@@ -44,5 +45,6 @@ module.exports = {
|
|||||||
convert,
|
convert,
|
||||||
MenuManager,
|
MenuManager,
|
||||||
childRegistrationUtils,
|
childRegistrationUtils,
|
||||||
loadCurve
|
loadCurve,
|
||||||
|
gravity
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -409,7 +409,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"timeThresholdSeconds": {
|
"timeThresholdSeconds": {
|
||||||
"default": 120,
|
"default": 5,
|
||||||
"rules": {
|
"rules": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"min": 0,
|
"min": 0,
|
||||||
|
|||||||
@@ -3,11 +3,61 @@ const customRefs = require('./refData.js');
|
|||||||
|
|
||||||
class CoolPropWrapper {
|
class CoolPropWrapper {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
this.defaultRefrigerant = null;
|
this.defaultRefrigerant = null;
|
||||||
this.defaultTempUnit = 'K'; // K, C, F
|
this.defaultTempUnit = 'K'; // K, C, F
|
||||||
this.defaultPressureUnit = 'Pa' // Pa, kPa, bar, psi
|
this.defaultPressureUnit = 'Pa' // Pa, kPa, bar, psi
|
||||||
this.customRef = false;
|
this.customRef = false;
|
||||||
|
this.PropsSI = this._propsSI.bind(this);
|
||||||
|
|
||||||
|
|
||||||
|
// 🔹 Wastewater correction options (defaults)
|
||||||
|
this._ww = {
|
||||||
|
enabled: true,
|
||||||
|
tss_g_per_L: 3.5, // default MLSS / TSS
|
||||||
|
density_k: 2e-4, // +0.02% per g/L
|
||||||
|
viscosity_k: 0.07, // +7% per g/L (clamped)
|
||||||
|
viscosity_max_gpl: 4 // cap effect at 4 g/L
|
||||||
|
};
|
||||||
|
|
||||||
|
this._initPromise = null;
|
||||||
|
this._autoInit({ refrigerant: 'Water' });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_isWastewaterFluid(fluidRaw) {
|
||||||
|
if (!fluidRaw) return false;
|
||||||
|
const token = String(fluidRaw).trim().toLowerCase();
|
||||||
|
return token === 'wastewater' || token.startsWith('wastewater:');
|
||||||
|
}
|
||||||
|
|
||||||
|
_parseWastewaterFluid(fluidRaw) {
|
||||||
|
if (!this._isWastewaterFluid(fluidRaw)) return null;
|
||||||
|
const ww = { ...this._ww };
|
||||||
|
const [, tail] = String(fluidRaw).split(':');
|
||||||
|
if (tail) {
|
||||||
|
tail.split(',').forEach(pair => {
|
||||||
|
const [key, value] = pair.split('=').map(s => s.trim().toLowerCase());
|
||||||
|
if (key === 'tss' && !Number.isNaN(Number(value))) {
|
||||||
|
ww.tss_g_per_L = Number(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return ww;
|
||||||
|
}
|
||||||
|
|
||||||
|
_applyWastewaterCorrection(outputKey, baseValue, ww) {
|
||||||
|
if (!Number.isFinite(baseValue) || !ww || !ww.enabled) return baseValue;
|
||||||
|
switch (outputKey.toUpperCase()) {
|
||||||
|
case 'D': // density
|
||||||
|
return baseValue * (1 + ww.density_k * ww.tss_g_per_L);
|
||||||
|
case 'V': // viscosity
|
||||||
|
const effTss = Math.min(ww.tss_g_per_L, ww.viscosity_max_gpl);
|
||||||
|
return baseValue * (1 + ww.viscosity_k * effTss);
|
||||||
|
default:
|
||||||
|
return baseValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temperature conversion helpers
|
// Temperature conversion helpers
|
||||||
@@ -407,13 +457,31 @@ class CoolPropWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Direct access to CoolProp functions
|
_autoInit(defaults) {
|
||||||
async getPropsSI() {
|
if (!this._initPromise) {
|
||||||
if(!this.initialized) {
|
this._initPromise = this.init(defaults);
|
||||||
await coolprop.init();
|
|
||||||
}
|
}
|
||||||
return coolprop.PropsSI;
|
return this._initPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_propsSI(outputKey, inKey1, inVal1, inKey2, inVal2, fluidRaw) {
|
||||||
|
if (!this.initialized) {
|
||||||
|
// Start init if no one else asked yet
|
||||||
|
this._autoInit({ refrigerant: this.defaultRefrigerant || 'Water' });
|
||||||
|
throw new Error('CoolProp is still warming up, retry PropsSI in a moment');
|
||||||
|
}
|
||||||
|
const ww = this._parseWastewaterFluid(fluidRaw);
|
||||||
|
const fluid = ww ? 'Water' : (this.customRefString || fluidRaw);
|
||||||
|
const baseValue = coolprop.PropsSI(outputKey, inKey1, inVal1, inKey2, inVal2, fluid);
|
||||||
|
return ww ? this._applyWastewaterCorrection(outputKey, baseValue, ww) : baseValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Access to coolprop
|
||||||
|
async getPropsSI() {
|
||||||
|
await this._ensureInit({ refrigerant: this.defaultRefrigerant || 'Water' });
|
||||||
|
return this.PropsSI;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new CoolPropWrapper();
|
module.exports = new CoolPropWrapper();
|
||||||
|
|||||||
90
src/helper/gravity.js
Normal file
90
src/helper/gravity.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* Gravity calculations based on WGS-84 ellipsoid model.
|
||||||
|
* Author: Rene de Ren (Waterschap Brabantse Delta)
|
||||||
|
* License: EUPL-1.2
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Gravity {
|
||||||
|
constructor() {
|
||||||
|
// Standard (conventional) gravity at 45° latitude, sea level
|
||||||
|
this.g0 = 9.80665; // m/s²
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns standard gravity (constant)
|
||||||
|
* @returns {number} gravity in m/s²
|
||||||
|
*/
|
||||||
|
getStandardGravity() {
|
||||||
|
return this.g0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes local gravity based on latitude and elevation.
|
||||||
|
* Formula: WGS-84 normal gravity (Somigliana)
|
||||||
|
* @param {number} latitudeDeg Latitude in degrees (−90 → +90)
|
||||||
|
* @param {number} elevationM Elevation above sea level [m]
|
||||||
|
* @returns {number} gravity in m/s²
|
||||||
|
*/
|
||||||
|
getLocalGravity(latitudeDeg, elevationM = 0) {
|
||||||
|
const phi = (latitudeDeg * Math.PI) / 180;
|
||||||
|
const sinPhi = Math.sin(phi);
|
||||||
|
const sin2 = sinPhi * sinPhi;
|
||||||
|
const sin2_2phi = Math.sin(2 * phi) ** 2;
|
||||||
|
|
||||||
|
// WGS-84 normal gravity on the ellipsoid
|
||||||
|
const gSurface =
|
||||||
|
9.780327 * (1 + 0.0053024 * sin2 - 0.0000058 * sin2_2phi);
|
||||||
|
|
||||||
|
// Free-air correction for elevation (~ −3.086×10⁻⁶ m/s² per m)
|
||||||
|
const gLocal = gSurface - 3.086e-6 * elevationM;
|
||||||
|
return gLocal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates hydrostatic pressure difference (ΔP = ρ g h)
|
||||||
|
* @param {number} density Fluid density [kg/m³]
|
||||||
|
* @param {number} heightM Height difference [m]
|
||||||
|
* @param {number} latitudeDeg Latitude (for local g)
|
||||||
|
* @param {number} elevationM Elevation (for local g)
|
||||||
|
* @returns {number} Pressure difference [Pa]
|
||||||
|
*/
|
||||||
|
pressureHead(density, heightM, latitudeDeg = 45, elevationM = 0) {
|
||||||
|
const g = this.getLocalGravity(latitudeDeg, elevationM);
|
||||||
|
return density * g * heightM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates weight force (F = m g)
|
||||||
|
* @param {number} massKg Mass [kg]
|
||||||
|
* @param {number} latitudeDeg Latitude (for local g)
|
||||||
|
* @param {number} elevationM Elevation (for local g)
|
||||||
|
* @returns {number} Force [N]
|
||||||
|
*/
|
||||||
|
weightForce(massKg, latitudeDeg = 45, elevationM = 0) {
|
||||||
|
const g = this.getLocalGravity(latitudeDeg, elevationM);
|
||||||
|
return massKg * g;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new Gravity();
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
const gravity = gravity;
|
||||||
|
|
||||||
|
// Standard gravity
|
||||||
|
console.log('g₀ =', gravity.getStandardGravity(), 'm/s²');
|
||||||
|
|
||||||
|
// Local gravity (Breda ≈ 51.6° N, 3 m elevation)
|
||||||
|
console.log('g @ Breda =', gravity.getLocalGravity(51.6, 3).toFixed(6), 'm/s²');
|
||||||
|
|
||||||
|
// Head pressure for 5 m water column at Breda
|
||||||
|
console.log(
|
||||||
|
'ΔP =',
|
||||||
|
gravity.pressureHead(1000, 5, 51.6, 3).toFixed(1),
|
||||||
|
'Pa'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Weight of 1 kg mass at Breda
|
||||||
|
console.log('Weight =', gravity.weightForce(1, 51.6, 3).toFixed(6), 'N');
|
||||||
|
*/
|
||||||
@@ -6,9 +6,9 @@ class PhysicalPositionMenu {
|
|||||||
return {
|
return {
|
||||||
positionGroups: [
|
positionGroups: [
|
||||||
{ group: 'Positional', options: [
|
{ group: 'Positional', options: [
|
||||||
{ value: 'upstream', label: '→ Upstream', icon: '←'}, //flow is then typically left to right
|
{ value: 'upstream', label: '→ Upstream', icon: '→'}, //flow is then typically left to right
|
||||||
{ value: 'atEquipment', label: '⊥ in place' , icon: '⊥' },
|
{ value: 'atEquipment', label: '⊥ in place' , icon: '⊥' },
|
||||||
{ value: 'downstream', label: '← Downstream' , icon: '→' }
|
{ value: 'downstream', label: '← Downstream' , icon: '←' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user