Compare commits
10 Commits
1857031027
...
9147a3f7d0
| Author | SHA1 | Date | |
|---|---|---|---|
| 9147a3f7d0 | |||
| 8f64fbe4e5 | |||
| 7a70f60655 | |||
| 223c4555b8 | |||
| 972d33355e | |||
| 94ea4fe76b | |||
| f6b026928e | |||
| c2cd29db56 | |||
| 0b49642668 | |||
| a4a5266040 |
24
package-lock.json
generated
24
package-lock.json
generated
@@ -9,14 +9,14 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "SEE LICENSE",
|
"license": "SEE LICENSE",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"generalFunctions": "git+https://gitea.centraal.wbd-rd.nl/p.vanderwilt/generalFunctions.git",
|
"generalFunctions": "git+https://gitea.centraal.wbd-rd.nl/p.vanderwilt/generalFunctions.git#fix-missing-references",
|
||||||
"mathjs": "^14.5.2"
|
"mathjs": "^14.5.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/runtime": {
|
"node_modules/@babel/runtime": {
|
||||||
"version": "7.28.2",
|
"version": "7.28.3",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz",
|
||||||
"integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==",
|
"integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@@ -48,12 +48,12 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fraction.js": {
|
"node_modules/fraction.js": {
|
||||||
"version": "5.2.2",
|
"version": "5.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
|
||||||
"integrity": "sha512-uXBDv5knpYmv/2gLzWQ5mBHGBRk9wcKTeWu6GLTUEQfjCxO09uM/mHDrojlL+Q1mVGIIFo149Gba7od1XPgSzQ==",
|
"integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12"
|
"node": "*"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/generalFunctions": {
|
"node_modules/generalFunctions": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "git+https://gitea.centraal.wbd-rd.nl/p.vanderwilt/generalFunctions.git#f13ee68938ea9d4b3a17ad90618c72634769c777",
|
"resolved": "git+https://gitea.centraal.wbd-rd.nl/p.vanderwilt/generalFunctions.git#302e12238745766a679ef11ca6ed5f4ea1548f87",
|
||||||
"license": "SEE LICENSE"
|
"license": "SEE LICENSE"
|
||||||
},
|
},
|
||||||
"node_modules/javascript-natural-sort": {
|
"node_modules/javascript-natural-sort": {
|
||||||
@@ -72,9 +72,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/mathjs": {
|
"node_modules/mathjs": {
|
||||||
"version": "14.6.0",
|
"version": "14.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/mathjs/-/mathjs-14.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/mathjs/-/mathjs-14.7.0.tgz",
|
||||||
"integrity": "sha512-5vI2BLB5GKQmiSK9BH6hVkZ+GgqpdnOgEfmHl7mqVmdQObLynr63KueyYYLCQMzj66q69mV2XZZGQqqxeftQbA==",
|
"integrity": "sha512-RaMhb+9MSESjDZNox/FzzuFpIUI+oxGLyOy1t3BMoW53pGWnTzZtlucJ5cvbit0dIMYlCq00gNbW1giZX4/1Rg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.26.10",
|
"@babel/runtime": "^7.26.10",
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -4,7 +4,7 @@
|
|||||||
"description": "Implementation of the asm3 model for Node-Red",
|
"description": "Implementation of the asm3 model for Node-Red",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://gitea.centraal.wbd-rd.nl/p.vanderwilt/asm3.git"
|
"url": "https://gitea.centraal.wbd-rd.nl/RnD/reactor.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"asm3",
|
"asm3",
|
||||||
@@ -15,19 +15,19 @@
|
|||||||
],
|
],
|
||||||
"license": "SEE LICENSE",
|
"license": "SEE LICENSE",
|
||||||
"author": "P.R. van der Wilt",
|
"author": "P.R. van der Wilt",
|
||||||
"main": "advancedReactor.js",
|
"main": "reactor.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node advancedReactor.js"
|
"test": "node reactor.js"
|
||||||
},
|
},
|
||||||
"node-red": {
|
"node-red": {
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"advancedReactor": "advancedReactor.js",
|
"reactor": "reactor.js",
|
||||||
"recirculation-pump": "additional_nodes/recirculation-pump.js",
|
"recirculation-pump": "additional_nodes/recirculation-pump.js",
|
||||||
"settling-basin": "additional_nodes/settling-basin.js"
|
"settling-basin": "additional_nodes/settling-basin.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"generalFunctions": "git+https://gitea.centraal.wbd-rd.nl/p.vanderwilt/generalFunctions.git",
|
"generalFunctions": "git+https://gitea.centraal.wbd-rd.nl/p.vanderwilt/generalFunctions.git#fix-missing-references",
|
||||||
"mathjs": "^14.5.2"
|
"mathjs": "^14.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
X_A_init: { value: 0.001, required: true },
|
X_A_init: { value: 0.001, required: true },
|
||||||
X_TS_init: { value: 125.0009, required: true },
|
X_TS_init: { value: 125.0009, required: true },
|
||||||
|
|
||||||
|
timeStep: { value: 1, required: true },
|
||||||
|
|
||||||
enableLog: { value: false },
|
enableLog: { value: false },
|
||||||
logLevel: { value: "error" },
|
logLevel: { value: "error" },
|
||||||
|
|
||||||
@@ -99,6 +101,10 @@
|
|||||||
type:"num",
|
type:"num",
|
||||||
types:["num"]
|
types:["num"]
|
||||||
})
|
})
|
||||||
|
$("#node-input-timeStep").typedInput({
|
||||||
|
type:"num",
|
||||||
|
types:["num"]
|
||||||
|
})
|
||||||
// Set initial visibility on dialog open
|
// Set initial visibility on dialog open
|
||||||
const initialType = $("#node-input-reactor_type").typedInput("value");
|
const initialType = $("#node-input-reactor_type").typedInput("value");
|
||||||
if (initialType === "CSTR") {
|
if (initialType === "CSTR") {
|
||||||
@@ -222,6 +228,11 @@
|
|||||||
<label for="node-input-X_TS_init"><i class="fa fa-tag"></i> Initial total suspended solids [g TSS m-3]</label>
|
<label for="node-input-X_TS_init"><i class="fa fa-tag"></i> Initial total suspended solids [g TSS m-3]</label>
|
||||||
<input type="text" id="node-input-X_TS_init" class="concentrations">
|
<input type="text" id="node-input-X_TS_init" class="concentrations">
|
||||||
</div>
|
</div>
|
||||||
|
<h2> Simulation parameters </h2>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-timeStep"><i class="fa fa-tag"></i> Time step [s]</label>
|
||||||
|
<input type="text" id="node-input-timeStep" placeholder="s">
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Logger fields injected here -->
|
<!-- Logger fields injected here -->
|
||||||
<div id="logger-fields-placeholder"></div>
|
<div id="logger-fields-placeholder"></div>
|
||||||
@@ -49,7 +49,7 @@ class nodeClass {
|
|||||||
this.source.setDispersion = msg;
|
this.source.setDispersion = msg;
|
||||||
break;
|
break;
|
||||||
case 'registerChild':
|
case 'registerChild':
|
||||||
// Register this node as a child of the parent node
|
// Register this node as a parent of the child node
|
||||||
const childId = msg.payload;
|
const childId = msg.payload;
|
||||||
const childObj = this.RED.nodes.getNode(childId);
|
const childObj = this.RED.nodes.getNode(childId);
|
||||||
this.source.childRegistrationUtils.registerChild(childObj.source, msg.positionVsParent);
|
this.source.childRegistrationUtils.registerChild(childObj.source, msg.positionVsParent);
|
||||||
@@ -104,7 +104,8 @@ class nodeClass {
|
|||||||
parseFloat(uiConfig.X_STO_init),
|
parseFloat(uiConfig.X_STO_init),
|
||||||
parseFloat(uiConfig.X_A_init),
|
parseFloat(uiConfig.X_A_init),
|
||||||
parseFloat(uiConfig.X_TS_init)
|
parseFloat(uiConfig.X_TS_init)
|
||||||
]
|
],
|
||||||
|
timeStep: parseFloat(uiConfig.timeStep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,22 +40,10 @@ class Reactor {
|
|||||||
this.kla = config.kla; // if NaN, use externaly provided OTR [d-1]
|
this.kla = config.kla; // if NaN, use externaly provided OTR [d-1]
|
||||||
|
|
||||||
this.currentTime = Date.now(); // milliseconds since epoch [ms]
|
this.currentTime = Date.now(); // milliseconds since epoch [ms]
|
||||||
this.timeStep = 1 / (24*60*15); // time step [d]
|
this.timeStep = 1 / (24*60*60) * this.config.timeStep; // time step [d]
|
||||||
this.speedUpFactor = 60; // speed up factor for simulation, 60 means 1 minute per simulated second
|
this.speedUpFactor = 60; // speed up factor for simulation, 60 means 1 minute per simulated second
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMeasurement(variant, subType, value, position) {
|
|
||||||
this.logger.debug(`---------------------- updating ${subType} ------------------ `);
|
|
||||||
switch (subType) {
|
|
||||||
case "temperature":
|
|
||||||
this.temperature = value;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.logger.error(`Type '${subType}' not recognized for measured update.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setter for influent data.
|
* Setter for influent data.
|
||||||
* @param {object} input - Input object (msg) containing payload with inlet index, flow rate, and concentrations.
|
* @param {object} input - Input object (msg) containing payload with inlet index, flow rate, and concentrations.
|
||||||
@@ -109,6 +97,78 @@ class Reactor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerChild(child, softwareType) {
|
||||||
|
switch (softwareType) {
|
||||||
|
case "measurement":
|
||||||
|
this.logger.debug(`Registering measurement child.`);
|
||||||
|
this._connectMeasurement(child);
|
||||||
|
break;
|
||||||
|
case "reactor":
|
||||||
|
this.logger.debug(`Registering reactor child.`);
|
||||||
|
this._connectReactor(child);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this.logger.error(`Unrecognized softwareType: ${softwareType}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_connectMeasurement(measurement) {
|
||||||
|
if (!measurement) {
|
||||||
|
this.logger.warn("Invalid measurement provided.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const position = measurement.config.functionality.positionVsParent;
|
||||||
|
const measurementType = measurement.config.asset.type;
|
||||||
|
const key = `${measurementType}_${position}`;
|
||||||
|
const eventName = `${measurementType}.measured.${position}`;
|
||||||
|
|
||||||
|
// Register event listener for measurement updates
|
||||||
|
this.measurements.emitter.on(eventName, (eventData) => {
|
||||||
|
this.logger.debug(`${position} ${measurementType} from ${eventData.childName}: ${eventData.value} ${eventData.unit}`);
|
||||||
|
|
||||||
|
// Store directly in parent's measurement container
|
||||||
|
this.measurements
|
||||||
|
.type(measurementType)
|
||||||
|
.variant("measured")
|
||||||
|
.position(position)
|
||||||
|
.value(eventData.value, eventData.timestamp, eventData.unit);
|
||||||
|
|
||||||
|
this._updateMeasurement(measurementType, eventData.value, position, eventData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_connectReactor(reactor) {
|
||||||
|
if (!reactor) {
|
||||||
|
this.logger.warn("Invalid reactor provided.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.upstreamReactor = reactor;
|
||||||
|
|
||||||
|
reactor.emitter.on("stateChange", (data) => {
|
||||||
|
this.logger.debug(`State change of upstream reactor detected.`);
|
||||||
|
this.updateState(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_updateMeasurement(measurementType, value, position, context) {
|
||||||
|
this.logger.debug(`---------------------- updating ${measurementType} ------------------ `);
|
||||||
|
switch (measurementType) {
|
||||||
|
case "temperature":
|
||||||
|
if (position == "atEquipment") {
|
||||||
|
this.temperature = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.logger.error(`Type '${measurementType}' not recognized for measured update.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the reactor state based on the new time.
|
* Update the reactor state based on the new time.
|
||||||
* @param {number} newTime - New time to update reactor state to, in milliseconds since epoch.
|
* @param {number} newTime - New time to update reactor state to, in milliseconds since epoch.
|
||||||
@@ -255,14 +315,14 @@ class Reactor_PFR extends Reactor {
|
|||||||
return stateNew;
|
return stateNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMeasurement(variant, subType, value, position) {
|
_updateMeasurement(measurementType, value, position, context) {
|
||||||
switch(subType) {
|
switch(measurementType) {
|
||||||
case "oxygen":
|
case "oxygen":
|
||||||
grid_pos = Math.round(position * this.n_x);
|
grid_pos = Math.round(position * this.n_x);
|
||||||
this.state[grid_pos][S_O_INDEX] = value; // naive approach for reconciling measurements and simulation
|
this.state[grid_pos][S_O_INDEX] = value; // naive approach for reconciling measurements and simulation
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
super.updateMeasurement(variant, subType, value, position);
|
super._updateMeasurement(measurementType, value, position, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user