From 087acf8395de615f94157c4ab71ff210b3fda0ae Mon Sep 17 00:00:00 2001
From: "p.vanderwilt"
Date: Wed, 4 Jun 2025 14:24:12 +0200
Subject: [PATCH 01/13] added initial file (unfinised) Add ASM3 class with
kinetic parameters and rate computation method
---
dependencies/asm3_class.js | 41 ++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 dependencies/asm3_class.js
diff --git a/dependencies/asm3_class.js b/dependencies/asm3_class.js
new file mode 100644
index 0000000..6215671
--- /dev/null
+++ b/dependencies/asm3_class.js
@@ -0,0 +1,41 @@
+class ASM3 {
+
+ kin_params = {
+ // kinetic parameters (20 C for now)
+
+ // Hydrolysis
+ k_H: 3., // hydrolysis rate constant [g X_S g-1 X_H d-1]
+ K_x: 1., // hydrolysis saturation constant [g X_S g-1 X_H]
+ // Heterotrophs
+ k_STO: 5., // storage rate constant [g S_S g-1 X_H d-1]
+ nu_NO: 0.6, // Anoxic reduction factor [-]
+ K_O: 0.2, // Saturation constant S_0 [g O2 m-3]
+ K_NO: 0.5, // Saturation constant S_NO [g NO3-N m-3]
+ K_S: 2., // Saturation constant S_s [g COD m-3]
+ K_STO: 1., // Saturation constant X_STO [g X_STO g-1 X_H]
+ mu_H: 2.,
+ K_NH: 0.01,
+ K_HCO: 0.1,
+ b_H_O2: 0.2,
+ b_H_NO: 0.1,
+ b_STO_O2: 0.2,
+ b_STO_NO: 0.1,
+ // Autotrophs
+ mu_A: 1.0,
+ K_A_NH: 1.,
+ K_A_O: 0.5,
+ K_A_HCO: 0.5,
+ b_A_O2: 0.15,
+ b_A_NO: 0.05
+ }
+
+
+ constructor() {
+ }
+
+ compute_rates(state) {
+ const rates = new Array(12);
+ rates[0] = this.parameters[]
+ return rates;
+ }
+}
\ No newline at end of file
From 6ae7b5bf53e15f052518d60e40801f79038cfea3 Mon Sep 17 00:00:00 2001
From: "p.vanderwilt"
Date: Wed, 4 Jun 2025 15:19:17 +0200
Subject: [PATCH 02/13] Finished parameter objects
---
dependencies/asm3_class.js | 60 ++++++++++++++++++++++++++------------
1 file changed, 41 insertions(+), 19 deletions(-)
diff --git a/dependencies/asm3_class.js b/dependencies/asm3_class.js
index 6215671..2b85fe5 100644
--- a/dependencies/asm3_class.js
+++ b/dependencies/asm3_class.js
@@ -1,34 +1,56 @@
class ASM3 {
kin_params = {
- // kinetic parameters (20 C for now)
+ // Kinetic parameters (20 C for now)
// Hydrolysis
k_H: 3., // hydrolysis rate constant [g X_S g-1 X_H d-1]
K_x: 1., // hydrolysis saturation constant [g X_S g-1 X_H]
// Heterotrophs
k_STO: 5., // storage rate constant [g S_S g-1 X_H d-1]
- nu_NO: 0.6, // Anoxic reduction factor [-]
- K_O: 0.2, // Saturation constant S_0 [g O2 m-3]
- K_NO: 0.5, // Saturation constant S_NO [g NO3-N m-3]
- K_S: 2., // Saturation constant S_s [g COD m-3]
- K_STO: 1., // Saturation constant X_STO [g X_STO g-1 X_H]
- mu_H: 2.,
- K_NH: 0.01,
- K_HCO: 0.1,
- b_H_O2: 0.2,
- b_H_NO: 0.1,
- b_STO_O2: 0.2,
- b_STO_NO: 0.1,
+ nu_NO: 0.6, // anoxic reduction factor [-]
+ K_O: 0.2, // saturation constant S_0 [g O2 m-3]
+ K_NO: 0.5, // saturation constant S_NO [g NO3-N m-3]
+ K_S: 2., // saturation constant S_s [g COD m-3]
+ K_STO: 1., // saturation constant X_STO [g X_STO g-1 X_H]
+ mu_H_max: 2., // maximum specific growth rate [d-1]
+ K_NH: 0.01, // saturation constant S_NH3 [g NH3-N m-3]
+ K_HCO: 0.1, // saturation constant S_HCO [mole HCO3 m-3]
+ b_H_O2: 0.2, // aerobic respiration rate [d-1]
+ b_H_NO: 0.1, // anoxic respiration rate [d-1]
+ b_STO_O2: 0.2, // aerobic respitation rate X_STO [d-1]
+ b_STO_NO: 0.1, // anoxic respitation rate X_STO [d-1]
// Autotrophs
- mu_A: 1.0,
- K_A_NH: 1.,
- K_A_O: 0.5,
- K_A_HCO: 0.5,
- b_A_O2: 0.15,
- b_A_NO: 0.05
+ mu_A_max: 1.0, // maximum specific growth rate [d-1]
+ K_A_NH: 1., // saturation constant S_NH3 [g NH3-N m-3]
+ K_A_O: 0.5, // saturation constant S_0 [g O2 m-3]
+ K_A_HCO: 0.5, // saturation constant S_HCO [mole HCO3 m-3]
+ b_A_O2: 0.15, // aerobic respiration rate [d-1]
+ b_A_NO: 0.05 // anoxic respiration rate [d-1]
}
+ stoi_params = {
+ // Stoichiometric and composition parameters
+
+ f_S_I: 0., // fraction S_I from hydrolysis [g S_I g-1 X_S]
+ // Yields
+ Y_STO_O: 0.85, // Aerobic yield X_STO per S_S [g X_STO g-1 S_S]
+ Y_STO_NO: 0.80, // Anoxic yield X_STO per S_S [g X_STO g-1 S_S]
+ Y_H_O: 0.63, // Aerobic yield X_H per X_STO [g X_H g-1 X_STO]
+ Y_H_NO: 0.54, // Anoxic yield X_H per X_STO [g X_H g-1 X_STO]
+ Y_A: 0.24, // anoxic yield X_A per S_NO [g X_A g-1 NO3-N]
+ // Composition (nitrogen)
+ i_NSI: 0.01, // nitrogen content S_I [g N g-1 S_I]
+ i_NSS: 0.03, // nitrogen content S_S [g N g-1 S_S]
+ i_NXI: 0.02, // nitrogen content X_I [g N g-1 X_I]
+ i_NXS: 0.04, // nitrogen content X_S [g N g-1 X_S]
+ i_NBM: 0.07, // nitrogen content X_H / X_A [g N g-1 X_H / X_A]
+ // Composition (TSS)
+ i_TS: 0.75, // TSS content X_I [g TS g-1 X_I]
+ i_TS: 0.75, // TSS content X_S [g TS g-1 X_S]
+ i_TS: 0.90, // TSS content X_H / X_A [g TS g-1 X_H / X_A]
+ i_TS: 0.60 // TSS content X_STO (PHB based) [g TS g-1 X_STO]
+ }
constructor() {
}
From c2122e95372bb268f2902d42bf5323e306b4346a Mon Sep 17 00:00:00 2001
From: "p.vanderwilt"
Date: Wed, 4 Jun 2025 17:28:52 +0200
Subject: [PATCH 03/13] Implemented rates in rates function
---
dependencies/asm3_class.js | 42 ++++++++++++++++++++++++++++++++------
1 file changed, 36 insertions(+), 6 deletions(-)
diff --git a/dependencies/asm3_class.js b/dependencies/asm3_class.js
index 2b85fe5..1b214db 100644
--- a/dependencies/asm3_class.js
+++ b/dependencies/asm3_class.js
@@ -5,7 +5,7 @@ class ASM3 {
// Hydrolysis
k_H: 3., // hydrolysis rate constant [g X_S g-1 X_H d-1]
- K_x: 1., // hydrolysis saturation constant [g X_S g-1 X_H]
+ K_X: 1., // hydrolysis saturation constant [g X_S g-1 X_H]
// Heterotrophs
k_STO: 5., // storage rate constant [g S_S g-1 X_H d-1]
nu_NO: 0.6, // anoxic reduction factor [-]
@@ -16,16 +16,16 @@ class ASM3 {
mu_H_max: 2., // maximum specific growth rate [d-1]
K_NH: 0.01, // saturation constant S_NH3 [g NH3-N m-3]
K_HCO: 0.1, // saturation constant S_HCO [mole HCO3 m-3]
- b_H_O2: 0.2, // aerobic respiration rate [d-1]
+ b_H_O: 0.2, // aerobic respiration rate [d-1]
b_H_NO: 0.1, // anoxic respiration rate [d-1]
- b_STO_O2: 0.2, // aerobic respitation rate X_STO [d-1]
+ b_STO_O: 0.2, // aerobic respitation rate X_STO [d-1]
b_STO_NO: 0.1, // anoxic respitation rate X_STO [d-1]
// Autotrophs
mu_A_max: 1.0, // maximum specific growth rate [d-1]
K_A_NH: 1., // saturation constant S_NH3 [g NH3-N m-3]
K_A_O: 0.5, // saturation constant S_0 [g O2 m-3]
K_A_HCO: 0.5, // saturation constant S_HCO [mole HCO3 m-3]
- b_A_O2: 0.15, // aerobic respiration rate [d-1]
+ b_A_O: 0.15, // aerobic respiration rate [d-1]
b_A_NO: 0.05 // anoxic respiration rate [d-1]
}
@@ -51,13 +51,43 @@ class ASM3 {
i_TS: 0.90, // TSS content X_H / X_A [g TS g-1 X_H / X_A]
i_TS: 0.60 // TSS content X_STO (PHB based) [g TS g-1 X_STO]
}
+ // S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS
+ state = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
- constructor() {
+ constructor(state) {
+ this.state = state;
+ }
+
+ _monod(c, K){
+ return c / (K + c);
+ }
+
+ _inv_monod(c, K){
+ return K / (K + c);
}
compute_rates(state) {
const rates = new Array(12);
- rates[0] = this.parameters[]
+ const [S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS] = state;
+
+ // Hydrolysis
+ rates[0] = this.kin_params.k_H * this._monod(X_S / X_H, this.kin_params.K_X) * X_H;
+
+ // Heterotrophs
+ rates[1] = this.kin_params.k_STO * this._monod(S_O, this.kin_params.K_O) * this._monod(S_S, this.kin_params.K_S) * X_H;
+ rates[2] = this.kin_params.k_STO * this.kin_params.nu_NO * this._inv_monod(S_O, this.kin_params.K_O) * this._monod(S_NO, this.kin_params.K_NO) * this._monod(S_S, this.kin_params.K_S) * X_H;
+ rates[3] = this.kin_params.mu_H_max * this._monod(S_O, this.kin_params.K_O) * this._monod(S_NH, this.kin_params.K_NH) * this._monod(S_HCO, this.kin_params.K_HCO) * this._monod(X_STO/X_H, K_STO) * X_H;
+ rates[4] = this.kin_params.mu_H_max * this.kin_params.nu_NO * this._inv_monod(S_O, this.kin_params.K_O) * this._monod(S_NO, this.kin_params.NO) * this._monod(S_NH, this.kin_params.K_NH) * this._monod(S_HCO, this.kin_params.K_HCO) * this._monod(X_STO/X_H, this.kin_params.K_STO) * X_H;
+ rates[5] = this.kin_params.b_H_O * this._monod(S_O, this.kin_params.K_O) * X_H;
+ rates[6] = this.kin_params.b_H_NO * this._inv_monod(S_O, this.kin_params.K_O) * this._monod(S_NO, this.kin_params.K_NO) * X_H;
+ rates[7] = this.kin_params.b_STO_O * this._monod(S_O, this.kin_params.K_O) * X_H;
+ rates[8] = this.kin_params.b_STO_NO * this._inv_monod(S_O, this.kin_params.K_O) * this._monod(S_NO, this.kin_params.K_NO) * X_STO;
+
+ // Autotrophs
+ rates[9] = this.kin_params.mu_A_max * this._monod(S_O, this.kin_params.K_A_O) * this._monod(S_NH, this.kin_params.K_A_NH) * this._monod(S_HCO, this.kin_params.K_A_HCO) * X_A;
+ rates[10] = this.kin_params.b_A_O * this._monod(S_O, this.kin_params.K_O) * X_A;
+ rates[11] = this.kin_params.b_A_NO * this._inv_monod(S_O, this.kin_params.K_A_O) * this._monod(S_NO, this.kin_params.K_NO) * X_A;
+
return rates;
}
}
\ No newline at end of file
From c5a9a2e6109d6bbaaa300ee945c6e6df503ddd23 Mon Sep 17 00:00:00 2001
From: "p.vanderwilt"
Date: Thu, 5 Jun 2025 14:52:23 +0200
Subject: [PATCH 04/13] Implemented first five stoichiometric equations
---
dependencies/asm3_class.js | 86 ++++++++++++++++++++++++++------------
1 file changed, 59 insertions(+), 27 deletions(-)
diff --git a/dependencies/asm3_class.js b/dependencies/asm3_class.js
index 1b214db..c7aa16f 100644
--- a/dependencies/asm3_class.js
+++ b/dependencies/asm3_class.js
@@ -32,13 +32,17 @@ class ASM3 {
stoi_params = {
// Stoichiometric and composition parameters
- f_S_I: 0., // fraction S_I from hydrolysis [g S_I g-1 X_S]
+ f_SI: 0., // fraction S_I from hydrolysis [g S_I g-1 X_S]
+ f_XI: 0.2, // fraction X_I from decomp X_H [g X_I g-1 X_H]
// Yields
- Y_STO_O: 0.85, // Aerobic yield X_STO per S_S [g X_STO g-1 S_S]
- Y_STO_NO: 0.80, // Anoxic yield X_STO per S_S [g X_STO g-1 S_S]
- Y_H_O: 0.63, // Aerobic yield X_H per X_STO [g X_H g-1 X_STO]
- Y_H_NO: 0.54, // Anoxic yield X_H per X_STO [g X_H g-1 X_STO]
+ Y_STO_O: 0.85, // aerobic yield X_STO per S_S [g X_STO g-1 S_S]
+ Y_STO_NO: 0.80, // anoxic yield X_STO per S_S [g X_STO g-1 S_S]
+ Y_H_O: 0.63, // aerobic yield X_H per X_STO [g X_H g-1 X_STO]
+ Y_H_NO: 0.54, // anoxic yield X_H per X_STO [g X_H g-1 X_STO]
Y_A: 0.24, // anoxic yield X_A per S_NO [g X_A g-1 NO3-N]
+ // Composition (COD via DoR)
+ i_CODN: -1.71, // COD content (DoR) [g COD g-1 N2-N]
+ i_CODNO: -4.57, // COD content (DoR) [g COD g-1 NO3-N]
// Composition (nitrogen)
i_NSI: 0.01, // nitrogen content S_I [g N g-1 S_I]
i_NSS: 0.03, // nitrogen content S_S [g N g-1 S_S]
@@ -46,16 +50,40 @@ class ASM3 {
i_NXS: 0.04, // nitrogen content X_S [g N g-1 X_S]
i_NBM: 0.07, // nitrogen content X_H / X_A [g N g-1 X_H / X_A]
// Composition (TSS)
- i_TS: 0.75, // TSS content X_I [g TS g-1 X_I]
- i_TS: 0.75, // TSS content X_S [g TS g-1 X_S]
- i_TS: 0.90, // TSS content X_H / X_A [g TS g-1 X_H / X_A]
- i_TS: 0.60 // TSS content X_STO (PHB based) [g TS g-1 X_STO]
+ i_TSXI: 0.75, // TSS content X_I [g TS g-1 X_I]
+ i_TSXS: 0.75, // TSS content X_S [g TS g-1 X_S]
+ i_TSBM: 0.90, // TSS content X_H / X_A [g TS g-1 X_H / X_A]
+ i_TSSTO: 0.60, // TSS content X_STO (PHB based) [g TS g-1 X_STO]
+ // Composition (charge)
+ i_cNH: 1/14, // charge per S_NH [mole H+ g-1 NH3-N]
+ i_cNO: -1/14 // charge per S_NO [mole H+ g-1 NO3-N]
}
- // S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS
- state = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
constructor(state) {
+ // S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS
this.state = state;
+ this.stoi_matrix = this._initialise_stoi_matrix()
+ }
+
+ _initialise_stoi_matrix(){
+ const { f_SI, f_XI, Y_STO_O, Y_STO_NO, Y_H_O, Y_H_NO, Y_A, i_CODN, i_CODNO, i_NSI, i_NSS, i_NXI, i_NXS, i_NBM, i_TSXI, i_TSXS, i_TSBM, i_TSSTO, i_cNH, i_cNO } = this.stoi_params;
+
+ const stoi_matrix = Array(12);
+ // S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS
+ stoi_matrix[0] = [0., f_SI, 1.-f_SI, (1.-f_SI)*i_NSS-f_SI*i_NSI+i_NXS, 0., 0., ((1.-f_SI)*i_NSS-f_SI*i_NSI+i_NXS)*i_cNH, 0., 0., -1., 0., 0., 0., -i_TSXS];
+ stoi_matrix[1] = [-(1.-Y_STO_O), 0, -1., i_NSS, 0., 0., i_NSS*i_cNH, 0., 0., 0., Y_STO_O, 0., Y_STO_O*i_TSSTO];
+ stoi_matrix[2] = [0., 0., -1., i_NSS, -(1.-Y_STO_NO)/(i_CODNO-i_CODN), (1.-Y_STO_NO)/(i_CODNO-i_CODN), i_NSS*i_cNH + (1.-Y_STO_NO)/(i_CODNO-i_CODN)*i_cNO, 0., 0., 0., Y_STO_NO, 0., Y_STO_NO*i_TSSTO];
+ stoi_matrix[3] = [-(1.-Y_H_O)/Y_H_O, 0., 0., -i_NBM, 0., 0., -i_NBM*i_cNH, 0., 0., 1., -1./Y_H_O, 0., i_TSBM-i_TSSTO/Y_H_O];
+ stoi_matrix[4] = [0., 0., 0., -i_NBM, -(1.-Y_H_NO)/(Y_H_NO*(i_CODNO-i_CODN)), (1.-Y_H_NO)/(Y_H_NO*(i_CODNO-i_CODN)), -i_NBM*i_cNH+(1.-Y_H_NO)/(Y_H_NO*(i_CODNO-i_CODN))*i_cNO, 0., 0., 1., -1./Y_H_NO, 0., i_TSBM-i_TSSTO/Y_H_NO];
+ stoi_matrix[5] = [-(1-f_XI), 0., 0., i_NBM-f_XI*i_NXI, 0., 0., (i_NBM-f_XI*i_NXI)*i_cNH, f_XI, 0., -1., 0., 0., f_XI*i_TSXI-i_TSBM];
+ stoi_matrix[6] = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.];
+ stoi_matrix[7] = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.];
+ stoi_matrix[8] = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.];
+ stoi_matrix[9] = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.];
+ stoi_matrix[10] = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.];
+ stoi_matrix[11] = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.];
+
+ return _.zip(...stoi_matrix); // transpose matrix
}
_monod(c, K){
@@ -66,28 +94,32 @@ class ASM3 {
return K / (K + c);
}
- compute_rates(state) {
- const rates = new Array(12);
- const [S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS] = state;
+ compute_rates() {
+ const rates = Array(12);
+ const [S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS] = this.state;
+ const { k_H, k_STO, nu_NO, K_O, K_NO, K_S, K_STO, mu_H_max, K_NH, K_HCO, b_H_O, b_H_NO, b_STO_O, b_STO_NO, mu_A_max, K_A_NH, K_A_O, K_A_HCO, b_A_O, b_A_NO } = this.kin_params;
// Hydrolysis
- rates[0] = this.kin_params.k_H * this._monod(X_S / X_H, this.kin_params.K_X) * X_H;
+ rates[0] = k_H * this._monod(X_S / X_H, K_X) * X_H;
// Heterotrophs
- rates[1] = this.kin_params.k_STO * this._monod(S_O, this.kin_params.K_O) * this._monod(S_S, this.kin_params.K_S) * X_H;
- rates[2] = this.kin_params.k_STO * this.kin_params.nu_NO * this._inv_monod(S_O, this.kin_params.K_O) * this._monod(S_NO, this.kin_params.K_NO) * this._monod(S_S, this.kin_params.K_S) * X_H;
- rates[3] = this.kin_params.mu_H_max * this._monod(S_O, this.kin_params.K_O) * this._monod(S_NH, this.kin_params.K_NH) * this._monod(S_HCO, this.kin_params.K_HCO) * this._monod(X_STO/X_H, K_STO) * X_H;
- rates[4] = this.kin_params.mu_H_max * this.kin_params.nu_NO * this._inv_monod(S_O, this.kin_params.K_O) * this._monod(S_NO, this.kin_params.NO) * this._monod(S_NH, this.kin_params.K_NH) * this._monod(S_HCO, this.kin_params.K_HCO) * this._monod(X_STO/X_H, this.kin_params.K_STO) * X_H;
- rates[5] = this.kin_params.b_H_O * this._monod(S_O, this.kin_params.K_O) * X_H;
- rates[6] = this.kin_params.b_H_NO * this._inv_monod(S_O, this.kin_params.K_O) * this._monod(S_NO, this.kin_params.K_NO) * X_H;
- rates[7] = this.kin_params.b_STO_O * this._monod(S_O, this.kin_params.K_O) * X_H;
- rates[8] = this.kin_params.b_STO_NO * this._inv_monod(S_O, this.kin_params.K_O) * this._monod(S_NO, this.kin_params.K_NO) * X_STO;
+ rates[1] = k_STO * this._monod(S_O, K_O) * this._monod(S_S, K_S) * X_H;
+ rates[2] = k_STO * nu_NO * this._inv_monod(S_O, K_O) * this._monod(S_NO, K_NO) * this._monod(S_S, K_S) * X_H;
+ rates[3] = mu_H_max * this._monod(S_O, K_O) * this._monod(S_NH, K_NH) * this._monod(S_HCO, K_HCO) * this._monod(X_STO/X_H, K_STO) * X_H;
+ rates[4] = mu_H_max * nu_NO * this._inv_monod(S_O, K_O) * this._monod(S_NO, K_NO) * this._monod(S_NH, K_NH) * this._monod(S_HCO, K_HCO) * this._monod(X_STO/X_H, K_STO) * X_H;
+ rates[5] = b_H_O * this._monod(S_O, K_O) * X_H;
+ rates[6] = b_H_NO * this._inv_monod(S_O, K_O) * this._monod(S_NO, K_NO) * X_H;
+ rates[7] = b_STO_O * this._monod(S_O, K_O) * X_H;
+ rates[8] = b_STO_NO * this._inv_monod(S_O, K_O) * this._monod(S_NO, K_NO) * X_STO;
// Autotrophs
- rates[9] = this.kin_params.mu_A_max * this._monod(S_O, this.kin_params.K_A_O) * this._monod(S_NH, this.kin_params.K_A_NH) * this._monod(S_HCO, this.kin_params.K_A_HCO) * X_A;
- rates[10] = this.kin_params.b_A_O * this._monod(S_O, this.kin_params.K_O) * X_A;
- rates[11] = this.kin_params.b_A_NO * this._inv_monod(S_O, this.kin_params.K_A_O) * this._monod(S_NO, this.kin_params.K_NO) * X_A;
+ rates[9] = mu_A_max * this._monod(S_O, K_A_O) * this._monod(S_NH, K_A_NH) * this._monod(S_HCO, K_A_HCO) * X_A;
+ rates[10] = b_A_O * this._monod(S_O, K_O) * X_A;
+ rates[11] = b_A_NO * this._inv_monod(S_O, K_A_O) * this._monod(S_NO, K_NO) * X_A;
return rates;
}
-}
\ No newline at end of file
+}
+
+const asm3 = new ASM3([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1])
+console.log(asm3.compute_rates())
\ No newline at end of file
From 6261ae9c47d558f5dd0c06f126039a19ef4b6b25 Mon Sep 17 00:00:00 2001
From: "p.vanderwilt"
Date: Thu, 5 Jun 2025 16:31:28 +0200
Subject: [PATCH 05/13] Finish stoichiometric matrix calculations and include
missing kinetic parameter in compute_rates
---
dependencies/asm3_class.js | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/dependencies/asm3_class.js b/dependencies/asm3_class.js
index c7aa16f..499583c 100644
--- a/dependencies/asm3_class.js
+++ b/dependencies/asm3_class.js
@@ -70,20 +70,20 @@ class ASM3 {
const stoi_matrix = Array(12);
// S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS
- stoi_matrix[0] = [0., f_SI, 1.-f_SI, (1.-f_SI)*i_NSS-f_SI*i_NSI+i_NXS, 0., 0., ((1.-f_SI)*i_NSS-f_SI*i_NSI+i_NXS)*i_cNH, 0., 0., -1., 0., 0., 0., -i_TSXS];
+ stoi_matrix[0] = [0., f_SI, 1.-f_SI, i_NXS-(1.-f_SI)*i_NSS-f_SI*i_NSI, 0., 0., (i_NXS-(1.-f_SI)*i_NSS-f_SI*i_NSI)*i_cNH, 0., -1., 0., 0., 0., -i_TSXS];
stoi_matrix[1] = [-(1.-Y_STO_O), 0, -1., i_NSS, 0., 0., i_NSS*i_cNH, 0., 0., 0., Y_STO_O, 0., Y_STO_O*i_TSSTO];
stoi_matrix[2] = [0., 0., -1., i_NSS, -(1.-Y_STO_NO)/(i_CODNO-i_CODN), (1.-Y_STO_NO)/(i_CODNO-i_CODN), i_NSS*i_cNH + (1.-Y_STO_NO)/(i_CODNO-i_CODN)*i_cNO, 0., 0., 0., Y_STO_NO, 0., Y_STO_NO*i_TSSTO];
stoi_matrix[3] = [-(1.-Y_H_O)/Y_H_O, 0., 0., -i_NBM, 0., 0., -i_NBM*i_cNH, 0., 0., 1., -1./Y_H_O, 0., i_TSBM-i_TSSTO/Y_H_O];
stoi_matrix[4] = [0., 0., 0., -i_NBM, -(1.-Y_H_NO)/(Y_H_NO*(i_CODNO-i_CODN)), (1.-Y_H_NO)/(Y_H_NO*(i_CODNO-i_CODN)), -i_NBM*i_cNH+(1.-Y_H_NO)/(Y_H_NO*(i_CODNO-i_CODN))*i_cNO, 0., 0., 1., -1./Y_H_NO, 0., i_TSBM-i_TSSTO/Y_H_NO];
- stoi_matrix[5] = [-(1-f_XI), 0., 0., i_NBM-f_XI*i_NXI, 0., 0., (i_NBM-f_XI*i_NXI)*i_cNH, f_XI, 0., -1., 0., 0., f_XI*i_TSXI-i_TSBM];
- stoi_matrix[6] = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.];
- stoi_matrix[7] = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.];
- stoi_matrix[8] = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.];
- stoi_matrix[9] = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.];
- stoi_matrix[10] = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.];
- stoi_matrix[11] = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.];
+ stoi_matrix[5] = [f_XI-1., 0., 0., i_NBM-f_XI*i_NXI, 0., 0., (i_NBM-f_XI*i_NXI)*i_cNH, f_XI, 0., -1., 0., 0., f_XI*i_TSXI-i_TSBM];
+ stoi_matrix[6] = [0., 0., 0., i_NBM-f_XI*i_NXI, -(1.-f_XI)/(i_CODNO-i_CODN), (1.-f_XI)/(i_CODNO-i_CODN), (i_NBM-f_XI*i_NXI)*i_cNH+(1-f_XI)/(i_CODNO-i_CODN)*i_cNO, f_XI, 0., -1., 0., 0., f_XI*i_TSXI-i_TSBM];
+ stoi_matrix[7] = [-1., 0., 0., 0., 0., 0., 0., 0., 0., 0., -1., 0., -i_TSSTO];
+ stoi_matrix[8] = [0., 0., 0., 0., -1./(i_CODNO-i_CODN), 1./(i_CODNO-i_CODN), i_cNO/(i_CODNO-i_CODN), 0., 0., 0., -1., 0., -i_TSSTO];
+ stoi_matrix[9] = [1.+i_CODNO/Y_A, 0., 0., -1./Y_A-i_NBM, 0., 1./Y_A, (-1./Y_A-i_NBM)*i_cNH+i_cNO/Y_A, 0., 0., 0., 0., 1., i_TSBM];
+ stoi_matrix[10] = [f_XI-1., 0., 0., i_NBM-f_XI*i_NXI, 0., 0., (i_NBM-f_XI*i_NXI)*i_cNH, f_XI, 0., 0., 0., -1., f_XI*i_TSXI-i_TSBM];
+ stoi_matrix[11] = [0., 0., 0., i_NBM-f_XI*i_NXI, -(1.-f_XI)/(i_CODNO-i_CODN), (1.-f_XI)/(i_CODNO-i_CODN), (i_NBM-f_XI*i_NXI)*i_cNH+(1-f_XI)/(i_CODNO-i_CODN)*i_cNO, 0., 0., 0., 0., -1., f_XI*i_TSXI-i_TSBM];
- return _.zip(...stoi_matrix); // transpose matrix
+ return stoi_matrix[0].map((col, i) => stoi_matrix.map(row => row[i])); // transpose matrix
}
_monod(c, K){
@@ -97,7 +97,7 @@ class ASM3 {
compute_rates() {
const rates = Array(12);
const [S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS] = this.state;
- const { k_H, k_STO, nu_NO, K_O, K_NO, K_S, K_STO, mu_H_max, K_NH, K_HCO, b_H_O, b_H_NO, b_STO_O, b_STO_NO, mu_A_max, K_A_NH, K_A_O, K_A_HCO, b_A_O, b_A_NO } = this.kin_params;
+ const { k_H, K_X, k_STO, nu_NO, K_O, K_NO, K_S, K_STO, mu_H_max, K_NH, K_HCO, b_H_O, b_H_NO, b_STO_O, b_STO_NO, mu_A_max, K_A_NH, K_A_O, K_A_HCO, b_A_O, b_A_NO } = this.kin_params;
// Hydrolysis
rates[0] = k_H * this._monod(X_S / X_H, K_X) * X_H;
From f9bd4279aa8f04deb599c4d7cf4df64cc3d1f0e1 Mon Sep 17 00:00:00 2001
From: "p.vanderwilt"
Date: Thu, 5 Jun 2025 16:57:31 +0200
Subject: [PATCH 06/13] Add project files including package.json,
package-lock.json, .gitignore
---
.gitignore | 136 +++++++++++++++++++++++++++++++++++++
dependencies/asm3_class.js | 1 +
package-lock.json | 113 ++++++++++++++++++++++++++++++
package.json | 30 ++++++++
4 files changed, 280 insertions(+)
create mode 100644 .gitignore
create mode 100644 package-lock.json
create mode 100644 package.json
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1170717
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,136 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+web_modules/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional stylelint cache
+.stylelintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+out
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and not Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# vuepress v2.x temp and cache directory
+.temp
+.cache
+
+# vitepress build output
+**/.vitepress/dist
+
+# vitepress cache directory
+**/.vitepress/cache
+
+# Docusaurus cache and generated files
+.docusaurus
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+.vscode-test
+
+# yarn v2
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
diff --git a/dependencies/asm3_class.js b/dependencies/asm3_class.js
index 499583c..6aea036 100644
--- a/dependencies/asm3_class.js
+++ b/dependencies/asm3_class.js
@@ -121,5 +121,6 @@ class ASM3 {
}
}
+// testing stuff
const asm3 = new ASM3([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1])
console.log(asm3.compute_rates())
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..e2e89fc
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,113 @@
+{
+ "name": "asm3",
+ "version": "0.0.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "asm3",
+ "version": "0.0.1",
+ "license": "SEE LICENSE",
+ "dependencies": {
+ "mathjs": "^14.5.2"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.27.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz",
+ "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/complex.js": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.2.tgz",
+ "integrity": "sha512-qtx7HRhPGSCBtGiST4/WGHuW+zeaND/6Ld+db6PbrulIB1i2Ev/2UPiqcmpQNPSyfBKraC0EOvOKCB5dGZKt3g==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/decimal.js": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz",
+ "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==",
+ "license": "MIT"
+ },
+ "node_modules/escape-latex": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz",
+ "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==",
+ "license": "MIT"
+ },
+ "node_modules/fraction.js": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.2.2.tgz",
+ "integrity": "sha512-uXBDv5knpYmv/2gLzWQ5mBHGBRk9wcKTeWu6GLTUEQfjCxO09uM/mHDrojlL+Q1mVGIIFo149Gba7od1XPgSzQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/javascript-natural-sort": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
+ "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==",
+ "license": "MIT"
+ },
+ "node_modules/mathjs": {
+ "version": "14.5.2",
+ "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-14.5.2.tgz",
+ "integrity": "sha512-51U6hp7j4M4Rj+l+q2KbmXAV9EhQVQzUdw1wE67RnUkKKq5ibxdrl9Ky2YkSUEIc2+VU8/IsThZNu6QSHUoyTA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@babel/runtime": "^7.26.10",
+ "complex.js": "^2.2.5",
+ "decimal.js": "^10.4.3",
+ "escape-latex": "^1.2.0",
+ "fraction.js": "^5.2.1",
+ "javascript-natural-sort": "^0.7.1",
+ "seedrandom": "^3.0.5",
+ "tiny-emitter": "^2.1.0",
+ "typed-function": "^4.2.1"
+ },
+ "bin": {
+ "mathjs": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/seedrandom": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
+ "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==",
+ "license": "MIT"
+ },
+ "node_modules/tiny-emitter": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
+ "license": "MIT"
+ },
+ "node_modules/typed-function": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.1.tgz",
+ "integrity": "sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..279c4a8
--- /dev/null
+++ b/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "asm3",
+ "version": "0.0.1",
+ "description": "Implementation of the asm3 model for Node-Red",
+ "repository": {
+ "type": "git",
+ "url": "https://gitea.centraal.wbd-rd.nl/p.vanderwilt/asm3.git"
+ },
+ "keywords": [
+ "asm3",
+ "activated sludge",
+ "wastewater",
+ "biological model",
+ "node-red"
+ ],
+ "license": "SEE LICENSE",
+ "author": "P.R. van der Wilt",
+ "main": "asm3.js",
+ "scripts": {
+ "test": "node asm3.js"
+ },
+ "node-red": {
+ "nodes": {
+ "asm3": "asm3.js"
+ }
+ },
+ "dependencies": {
+ "mathjs": "^14.5.2"
+ }
+}
From 333efcda5299d9520e7d68baedfed0fda6092bba Mon Sep 17 00:00:00 2001
From: "p.vanderwilt"
Date: Thu, 5 Jun 2025 17:23:51 +0200
Subject: [PATCH 07/13] Add optional state parameter to compute_rates, improve
comments, and implement compute_dC method
---
dependencies/asm3_class.js | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/dependencies/asm3_class.js b/dependencies/asm3_class.js
index 6aea036..1cf4ec0 100644
--- a/dependencies/asm3_class.js
+++ b/dependencies/asm3_class.js
@@ -1,3 +1,5 @@
+const math = require('mathjs')
+
class ASM3 {
kin_params = {
@@ -65,7 +67,7 @@ class ASM3 {
this.stoi_matrix = this._initialise_stoi_matrix()
}
- _initialise_stoi_matrix(){
+ _initialise_stoi_matrix(){ // initialise stoichiometric matrix
const { f_SI, f_XI, Y_STO_O, Y_STO_NO, Y_H_O, Y_H_NO, Y_A, i_CODN, i_CODNO, i_NSI, i_NSS, i_NXI, i_NXS, i_NBM, i_TSXI, i_TSXS, i_TSBM, i_TSSTO, i_cNH, i_cNO } = this.stoi_params;
const stoi_matrix = Array(12);
@@ -94,9 +96,10 @@ class ASM3 {
return K / (K + c);
}
- compute_rates() {
+ compute_rates(state) { // computes reaction rates. state is optional, if not provided, use class state
+
const rates = Array(12);
- const [S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS] = this.state;
+ const [S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS] = state || this.state;
const { k_H, K_X, k_STO, nu_NO, K_O, K_NO, K_S, K_STO, mu_H_max, K_NH, K_HCO, b_H_O, b_H_NO, b_STO_O, b_STO_NO, mu_A_max, K_A_NH, K_A_O, K_A_HCO, b_A_O, b_A_NO } = this.kin_params;
// Hydrolysis
@@ -119,8 +122,13 @@ class ASM3 {
return rates;
}
+
+ compute_dC(dt){ // compute change in concentration over time step dt
+ return math.multiply(math.multiply(this.stoi_matrix, this.compute_rates()), dt);
+ }
}
// testing stuff
const asm3 = new ASM3([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1])
-console.log(asm3.compute_rates())
\ No newline at end of file
+console.log(asm3.compute_rates())
+console.log(asm3.compute_dC(0.001))
\ No newline at end of file
From 341af6db4dd8899f9492ef16784009a0e3f50d19 Mon Sep 17 00:00:00 2001
From: "p.vanderwilt"
Date: Wed, 11 Jun 2025 15:17:09 +0200
Subject: [PATCH 08/13] Refactor ASM3 constructor to remove state parameter and
update compute_dC method to use state directly; add Reactor_CSTR class for
reactor simulation with forward Euler method
---
dependencies/asm3_class.js | 16 ++++++----------
dependencies/reactor_class.js | 21 +++++++++++++++++++++
2 files changed, 27 insertions(+), 10 deletions(-)
create mode 100644 dependencies/reactor_class.js
diff --git a/dependencies/asm3_class.js b/dependencies/asm3_class.js
index 1cf4ec0..93c7364 100644
--- a/dependencies/asm3_class.js
+++ b/dependencies/asm3_class.js
@@ -61,9 +61,8 @@ class ASM3 {
i_cNO: -1/14 // charge per S_NO [mole H+ g-1 NO3-N]
}
- constructor(state) {
+ constructor() {
// S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS
- this.state = state;
this.stoi_matrix = this._initialise_stoi_matrix()
}
@@ -96,10 +95,10 @@ class ASM3 {
return K / (K + c);
}
- compute_rates(state) { // computes reaction rates. state is optional, if not provided, use class state
+ compute_rates(state) { // computes reaction rates. state is optional
const rates = Array(12);
- const [S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS] = state || this.state;
+ const [S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS] = state;
const { k_H, K_X, k_STO, nu_NO, K_O, K_NO, K_S, K_STO, mu_H_max, K_NH, K_HCO, b_H_O, b_H_NO, b_STO_O, b_STO_NO, mu_A_max, K_A_NH, K_A_O, K_A_HCO, b_A_O, b_A_NO } = this.kin_params;
// Hydrolysis
@@ -123,12 +122,9 @@ class ASM3 {
return rates;
}
- compute_dC(dt){ // compute change in concentration over time step dt
- return math.multiply(math.multiply(this.stoi_matrix, this.compute_rates()), dt);
+ compute_dC(state){ // compute changes in concentrations
+ return math.multiply(this.stoi_matrix, this.compute_rates(state));
}
}
-// testing stuff
-const asm3 = new ASM3([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1])
-console.log(asm3.compute_rates())
-console.log(asm3.compute_dC(0.001))
\ No newline at end of file
+module.exports = ASM3;
\ No newline at end of file
diff --git a/dependencies/reactor_class.js b/dependencies/reactor_class.js
new file mode 100644
index 0000000..04527e8
--- /dev/null
+++ b/dependencies/reactor_class.js
@@ -0,0 +1,21 @@
+const ASM3 = require('./asm3_class')
+const math = require('mathjs')
+
+class Reactor_CSTR {
+
+ constructor(initial_state){
+ this.state = initial_state;
+ this.asm = new ASM3();
+ }
+
+ tick_fe(time_step){ // tick reactor state using forward Euler method
+ const delta = this.asm.compute_dC(this.state);
+ this.state = math.add(this.state, math.multiply(delta, time_step));
+ return this.state;
+ }
+}
+
+// testing stuff
+let state = [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1];
+const Reactor = new Reactor_CSTR(state);
+console.log(Reactor.tick_fe(0.001));
\ No newline at end of file
From 603a1d228322f861c4a2cb54f7d4170777ccf9f2 Mon Sep 17 00:00:00 2001
From: "p.vanderwilt"
Date: Wed, 11 Jun 2025 16:24:27 +0200
Subject: [PATCH 09/13] Expand reactor class to build a simple CSTR model.
Moved some functionality from asm3_class to reactor.
---
dependencies/asm3_class.js | 4 ++--
dependencies/reactor_class.js | 28 +++++++++++++++++++++++-----
2 files changed, 25 insertions(+), 7 deletions(-)
diff --git a/dependencies/asm3_class.js b/dependencies/asm3_class.js
index 93c7364..ce44f96 100644
--- a/dependencies/asm3_class.js
+++ b/dependencies/asm3_class.js
@@ -62,7 +62,6 @@ class ASM3 {
}
constructor() {
- // S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS
this.stoi_matrix = this._initialise_stoi_matrix()
}
@@ -96,7 +95,7 @@ class ASM3 {
}
compute_rates(state) { // computes reaction rates. state is optional
-
+ // state: S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS
const rates = Array(12);
const [S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS] = state;
const { k_H, K_X, k_STO, nu_NO, K_O, K_NO, K_S, K_STO, mu_H_max, K_NH, K_HCO, b_H_O, b_H_NO, b_STO_O, b_STO_NO, mu_A_max, K_A_NH, K_A_O, K_A_HCO, b_A_O, b_A_NO } = this.kin_params;
@@ -123,6 +122,7 @@ class ASM3 {
}
compute_dC(state){ // compute changes in concentrations
+ // state: S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS
return math.multiply(this.stoi_matrix, this.compute_rates(state));
}
}
diff --git a/dependencies/reactor_class.js b/dependencies/reactor_class.js
index 04527e8..5863dd0 100644
--- a/dependencies/reactor_class.js
+++ b/dependencies/reactor_class.js
@@ -6,16 +6,34 @@ class Reactor_CSTR {
constructor(initial_state){
this.state = initial_state;
this.asm = new ASM3();
+
+ this.Vl = 10.0; // fluid volume reactor [m3]
+ this.F = 1.0; // fluid debit [m3 d-1]
+ this.C_in = Array(13).fill(0.0); // composition influent
+ this.OTR = 1000.0; // oxygen transfer rate [g O2 d-1]
}
tick_fe(time_step){ // tick reactor state using forward Euler method
- const delta = this.asm.compute_dC(this.state);
- this.state = math.add(this.state, math.multiply(delta, time_step));
+ const r = this.asm.compute_dC(this.state);
+ const dC_in = math.multiply(this.C_in, this.F/this.Vl);
+ const dC_out = math.multiply(this.state, this.F/this.Vl);
+ const T_O = Array(13).fill(0.0);
+ T_O[0] = this.OTR;
+
+ const dC_total = math.multiply(math.add(dC_in, dC_out, r, T_O), time_step);
+
+ this.state = math.add(this.state, dC_total);
return this.state;
}
}
// testing stuff
-let state = [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1];
-const Reactor = new Reactor_CSTR(state);
-console.log(Reactor.tick_fe(0.001));
\ No newline at end of file
+// state: S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS
+let initial_state = [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1];
+const Reactor = new Reactor_CSTR(initial_state);
+Reactor.C_in = [0.0, 30., 100., 16., 0., 0., 5., 25., 75., 30., 0., 0., 125.];
+N = 0;
+while (N < 15) {
+ console.log(Reactor.tick_fe(0.001));
+ N += 1;
+}
\ No newline at end of file
From b210a71657d93c0815b2af907ea9bba3b8a51d4c Mon Sep 17 00:00:00 2001
From: "p.vanderwilt"
Date: Wed, 11 Jun 2025 17:18:27 +0200
Subject: [PATCH 10/13] Fix structure and improve comments in ASM3 and
Reactor_CSTR classes
---
dependencies/asm3_class.js | 4 ++--
dependencies/reactor_class.js | 22 ++++++++++++++++++----
2 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/dependencies/asm3_class.js b/dependencies/asm3_class.js
index ce44f96..b5c795e 100644
--- a/dependencies/asm3_class.js
+++ b/dependencies/asm3_class.js
@@ -65,7 +65,7 @@ class ASM3 {
this.stoi_matrix = this._initialise_stoi_matrix()
}
- _initialise_stoi_matrix(){ // initialise stoichiometric matrix
+ _initialise_stoi_matrix() { // initialise stoichiometric matrix
const { f_SI, f_XI, Y_STO_O, Y_STO_NO, Y_H_O, Y_H_NO, Y_A, i_CODN, i_CODNO, i_NSI, i_NSS, i_NXI, i_NXS, i_NBM, i_TSXI, i_TSXS, i_TSBM, i_TSSTO, i_cNH, i_cNO } = this.stoi_params;
const stoi_matrix = Array(12);
@@ -121,7 +121,7 @@ class ASM3 {
return rates;
}
- compute_dC(state){ // compute changes in concentrations
+ compute_dC(state) { // compute changes in concentrations
// state: S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS
return math.multiply(this.stoi_matrix, this.compute_rates(state));
}
diff --git a/dependencies/reactor_class.js b/dependencies/reactor_class.js
index 5863dd0..4db5522 100644
--- a/dependencies/reactor_class.js
+++ b/dependencies/reactor_class.js
@@ -3,17 +3,31 @@ const math = require('mathjs')
class Reactor_CSTR {
- constructor(initial_state){
+ constructor(initial_state) {
this.state = initial_state;
this.asm = new ASM3();
this.Vl = 10.0; // fluid volume reactor [m3]
this.F = 1.0; // fluid debit [m3 d-1]
this.C_in = Array(13).fill(0.0); // composition influent
- this.OTR = 1000.0; // oxygen transfer rate [g O2 d-1]
+ this.OTR = 100.0; // oxygen transfer rate [g O2 d-1]
+
+ this.currentTime = Date.now(); // milliseconds since epoch [ms]
+ this.timeStep = 1/(24*60) // time step [d]
}
- tick_fe(time_step){ // tick reactor state using forward Euler method
+ // expect update with timestamp
+ updateState(input) {
+ throw new Error("Not implemented yet");
+
+ let newTime = input.payload;
+
+ const day2ms = 1000 * 60 * 60 * 24;
+
+ let n_iter = (newTime - this.currentTime) % (this.timeStep * day2ms);
+ }
+
+ tick_fe(time_step) { // tick reactor state using forward Euler method
const r = this.asm.compute_dC(this.state);
const dC_in = math.multiply(this.C_in, this.F/this.Vl);
const dC_out = math.multiply(this.state, this.F/this.Vl);
@@ -33,7 +47,7 @@ let initial_state = [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1,
const Reactor = new Reactor_CSTR(initial_state);
Reactor.C_in = [0.0, 30., 100., 16., 0., 0., 5., 25., 75., 30., 0., 0., 125.];
N = 0;
-while (N < 15) {
+while (N < 500) {
console.log(Reactor.tick_fe(0.001));
N += 1;
}
\ No newline at end of file
From 49334f59e99a376cd6e513c1f6642b1579309746 Mon Sep 17 00:00:00 2001
From: "p.vanderwilt"
Date: Thu, 12 Jun 2025 11:55:17 +0200
Subject: [PATCH 11/13] Add advanced-reactor node-red implementation and update
package.json references
---
advanced-reactor.js | 9 +++++++++
package.json | 6 +++---
reactor.html | 26 ++++++++++++++++++++++++++
3 files changed, 38 insertions(+), 3 deletions(-)
create mode 100644 advanced-reactor.js
create mode 100644 reactor.html
diff --git a/advanced-reactor.js b/advanced-reactor.js
new file mode 100644
index 0000000..b8942b4
--- /dev/null
+++ b/advanced-reactor.js
@@ -0,0 +1,9 @@
+module.exports = function(RED) {
+ function reactor(config) {
+ RED.nodes.createNode(this, config);
+ var node = this;
+
+ let name = config.name;
+ }
+ RED.nodes.registerType("advanced-reactor", reactor);
+}
diff --git a/package.json b/package.json
index 279c4a8..b4a086b 100644
--- a/package.json
+++ b/package.json
@@ -15,13 +15,13 @@
],
"license": "SEE LICENSE",
"author": "P.R. van der Wilt",
- "main": "asm3.js",
+ "main": "advanced-reactor.js",
"scripts": {
- "test": "node asm3.js"
+ "test": "node advanced-reactor.js"
},
"node-red": {
"nodes": {
- "asm3": "asm3.js"
+ "advanced-reactor": "advanced-reactor.js"
}
},
"dependencies": {
diff --git a/reactor.html b/reactor.html
new file mode 100644
index 0000000..f9ad921
--- /dev/null
+++ b/reactor.html
@@ -0,0 +1,26 @@
+
+
+
+
+
From 2182bed343da891632d0a5166a0ed1ca2e7e8661 Mon Sep 17 00:00:00 2001
From: "p.vanderwilt"
Date: Thu, 12 Jun 2025 12:52:32 +0200
Subject: [PATCH 12/13] Fixed node not showing up in pallete.
---
reactor.html => advanced-reactor.html | 4 ++--
advanced-reactor.js | 7 ++++++-
2 files changed, 8 insertions(+), 3 deletions(-)
rename reactor.html => advanced-reactor.html (89%)
diff --git a/reactor.html b/advanced-reactor.html
similarity index 89%
rename from reactor.html
rename to advanced-reactor.html
index f9ad921..eade6d7 100644
--- a/reactor.html
+++ b/advanced-reactor.html
@@ -1,6 +1,6 @@