Merge pull request 'Fix outflow' (#4) from experimental into main

Reviewed-on: p.vanderwilt/asm3#4
This commit is contained in:
2025-06-18 22:24:22 +00:00
6 changed files with 23 additions and 21 deletions

View File

@@ -43,7 +43,7 @@
<input type="text" id="node-input-name" placeholder="Name"> <input type="text" id="node-input-name" placeholder="Name">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-F2"><i class="fa fa-tag"></i> Recirculation debit [m3 s-1]</label> <label for="node-input-F2"><i class="fa fa-tag"></i> Recirculation debit [m3 d-1]</label>
<input type="text" id="node-input-F2" placeholder="m3 s-1"> <input type="text" id="node-input-F2" placeholder="m3 s-1">
</div> </div>
<div class="form-row"> <div class="form-row">

View File

@@ -11,12 +11,12 @@ module.exports = function(RED) {
switch (msg.topic) { switch (msg.topic) {
case "Fluent": case "Fluent":
// conserve volume flow debit // conserve volume flow debit
let F1 = msg.payload.F; let F_in = msg.payload.F;
let F_diff = Math.max(F1 - F2, 0); let F1 = Math.max(F_in - F2, 0);
let F2_corr = F1 < F2 ? F1 : F2; let F2_corr = F_in < F2 ? F_in : F2;
let msg_F1 = structuredClone(msg); let msg_F1 = structuredClone(msg);
msg_F1.payload.F = F_diff; msg_F1.payload.F = F1;
let msg_F2 = {...msg}; let msg_F2 = {...msg};
msg_F2.payload.F = F2_corr; msg_F2.payload.F = F2_corr;

View File

@@ -4,7 +4,7 @@
color: "#e4a363", color: "#e4a363",
defaults: { defaults: {
name: { value: "" }, name: { value: "" },
SVI: { value: 0.1, required: true }, TS_set: { value: 0.1, required: true },
inlet: { value: 1, required: true } inlet: { value: 1, required: true }
}, },
inputs: 1, inputs: 1,
@@ -15,7 +15,7 @@
return this.name || "Settling basin"; return this.name || "Settling basin";
}, },
oneditprepare: function() { oneditprepare: function() {
$("#node-input-SVI").typedInput({ $("#node-input-TS_set").typedInput({
type:"num", type:"num",
types:["num"] types:["num"]
}); });
@@ -25,9 +25,9 @@
}); });
}, },
oneditsave: function() { oneditsave: function() {
let SVI = parseFloat($("#node-input-SVI").typedInput("value")); let TS_set = parseFloat($("#node-input-TS_set").typedInput("value"));
if (isNaN(SVI) || SVI < 0) { if (isNaN(TS_set) || TS_set < 0) {
RED.notify("SVI is not set correctly", {type: "error"}); RED.notify("TS is not set correctly", {type: "error"});
} }
let inlet = parseInt($("#node-input-n_inlets").typedInput("value")); let inlet = parseInt($("#node-input-n_inlets").typedInput("value"));
if (inlet < 1) { if (inlet < 1) {
@@ -43,8 +43,8 @@
<input type="text" id="node-input-name" placeholder="Name"> <input type="text" id="node-input-name" placeholder="Name">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-SVI"><i class="fa fa-tag"></i> SVI (alternate)</label> <label for="node-input-TS_set"><i class="fa fa-tag"></i> Total Solids set point [g m-3]</label>
<input type="text" id="node-input-SVI" placeholder=""> <input type="text" id="node-input-TS_set" placeholder="">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-inlet"><i class="fa fa-tag"></i> Assigned inlet return line</label> <label for="node-input-inlet"><i class="fa fa-tag"></i> Assigned inlet return line</label>

View File

@@ -4,7 +4,7 @@ module.exports = function(RED) {
var node = this; var node = this;
let name = config.name; let name = config.name;
let SVI = parseFloat(config.SVI); let TS_set = parseFloat(config.TS_set);
const inlet_sludge = parseInt(config.inlet); const inlet_sludge = parseInt(config.inlet);
node.on('input', function(msg, send, done) { node.on('input', function(msg, send, done) {
@@ -13,11 +13,13 @@ module.exports = function(RED) {
// conserve volume flow debit // conserve volume flow debit
let F_in = msg.payload.F; let F_in = msg.payload.F;
let C_in = msg.payload.C; let C_in = msg.payload.C;
let X_in = (C_in[7] + C_in[8] + C_in[9] + C_in[10] + C_in[11] + C_in[12]); let F2 = (F_in * C_in[12]) / TS_set;
let F2 = (F_in * X_in) / (SVI*1000*1000);
let F1 = Math.max(F_in - F2, 0);
let F2_corr = F_in < F2 ? F_in : F2;
let msg_F1 = structuredClone(msg); let msg_F1 = structuredClone(msg);
msg_F1.payload.F = F_in - F2; msg_F1.payload.F = F1;
msg_F1.payload.C[7] = 0; msg_F1.payload.C[7] = 0;
msg_F1.payload.C[8] = 0; msg_F1.payload.C[8] = 0;
msg_F1.payload.C[9] = 0; msg_F1.payload.C[9] = 0;
@@ -26,8 +28,8 @@ module.exports = function(RED) {
msg_F1.payload.C[12] = 0; msg_F1.payload.C[12] = 0;
let msg_F2 = {...msg}; let msg_F2 = {...msg};
msg_F2.payload.F = F2; msg_F2.payload.F = F2_corr;
if (F2 != 0) { if (F2_corr > 0) {
msg_F2.payload.C[7] = F_in * C_in[7] / F2; msg_F2.payload.C[7] = F_in * C_in[7] / F2;
msg_F2.payload.C[8] = F_in * C_in[8] / F2; msg_F2.payload.C[8] = F_in * C_in[8] / F2;
msg_F2.payload.C[9] = F_in * C_in[9] / F2; msg_F2.payload.C[9] = F_in * C_in[9] / F2;

View File

@@ -19,7 +19,7 @@
X_H_init: { value: 30., required: true }, X_H_init: { value: 30., required: true },
X_STO_init: { value: 0., required: true }, X_STO_init: { value: 0., required: true },
X_A_init: { value: 0.001, required: true }, X_A_init: { value: 0.001, required: true },
X_TS_init: { value: 125., required: true } X_TS_init: { value: 125.0009, required: true }
}, },
inputs: 1, inputs: 1,
outputs: 1, outputs: 1,

View File

@@ -59,13 +59,13 @@ class Reactor_CSTR {
tick_fe(time_step) { // tick reactor state using forward Euler method tick_fe(time_step) { // tick reactor state using forward Euler method
const r = this.asm.compute_dC(this.state); const r = this.asm.compute_dC(this.state);
const dC_in = math.multiply(math.divide([this.Fs], this.Vl), this.Cs_in)[0]; const dC_in = math.multiply(math.divide([this.Fs], this.Vl), this.Cs_in)[0];
const dC_out = math.multiply(math.sum(this.Fs)/this.Vl, this.state); const dC_out = math.multiply(-1*math.sum(this.Fs)/this.Vl, this.state);
const t_O = Array(13).fill(0.0); const t_O = Array(13).fill(0.0);
t_O[0] = isNaN(this.kla) ? this.OTR : this.calcOTR(this.state[0]); // calculate OTR if kla is not NaN, otherwise use externaly calculated OTR t_O[0] = isNaN(this.kla) ? this.OTR : this.calcOTR(this.state[0]); // calculate OTR if kla is not NaN, otherwise use externaly calculated OTR
const dC_total = math.multiply(math.add(dC_in, dC_out, r, t_O), time_step); const dC_total = math.multiply(math.add(dC_in, dC_out, r, t_O), time_step);
this.state = math.add(this.state, dC_total); this.state = math.abs(math.add(this.state, dC_total)); // make sure that concentrations do not go negative
return this.state; return this.state;
} }
} }