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 @@