diff --git a/doc/interface/reaction/activated_sludge_model.rst b/doc/interface/reaction/activated_sludge_model.rst new file mode 100644 index 000000000..b968696c8 --- /dev/null +++ b/doc/interface/reaction/activated_sludge_model.rst @@ -0,0 +1,529 @@ +.. _activated_sludge_model_config: + +Activated Sludge Model (ASM3h) +~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Group /input/model/unit_XXX/reaction - REACTION_MODEL = ACTIVATED_SLUDGE_MODEL3** + +For information on model equations, refer to :ref:`activated_sludge_model`. + +**Component configuration** + +``ASM3_COMP_IDX`` + + Optional component indexes. Set this in case the relevant components start at a certain offset or are provided + in a different order than listed below: + + ============= ========= + **Component** **Index** + ============= ========= + SO 0 + SS 1 + SNH 2 + SNO 3 + SN2 4 + SALK 5 + SI 6 + XI 7 + XS 8 + XH 9 + XSTO 10 + XA 11 + XMI 12 + SI_ad (optinal) 13 + SS_ad (optionl) 14 + ============= ========= + + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{N}` **Length:** 13 + ================ ============================= ======================================================== + +**Environment/process parameters** + +``ASM3_T`` + + Temperature :math:`T`. + + **Unit:** :math:`°C` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_V`` + + (Optional) Reactor volume :math:`V`. If not set, the volume of the unit operation is used. It is left to the user to + ensure consistency between the volume set here and the unit operation volume. + + **Unit:** :math:`m^3` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\gt 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_IO2`` + + (Optional) Aeration oxygen input :math:`iO_2`. If not set, aeration is not considered in the model. + + **Unit:** :math:`g~O_2~m^{-3}~d^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\ge 0` **Length:** 1 + ================ ============================= ======================================================== + +**Stoichiometric parameters** + +``ASM3_INSI`` + + Nitrogen content of soluble inert organics :math:`i_{N,SI}`. + + **Unit:** :math:`g~N~(g~COD)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\ge 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_INSS`` + + Nitrogen content of readily biodegradable substrate :math:`i_{N,SS}`. + + **Unit:** :math:`g~N~(g~COD)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\ge 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_INXI`` + + Nitrogen content of inert particulate organics :math:`i_{N,XI}`. + + **Unit:** :math:`g~N~(g~COD)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\ge 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_INXS`` + + Nitrogen content of slowly biodegradable substrate :math:`i_{N,XS}`. + + **Unit:** :math:`g~N~(g~COD)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\ge 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_INBM`` + + Nitrogen content of biomass :math:`i_{N,BM}`. + + **Unit:** :math:`g~N~(g~COD)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\ge 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_IVSS_XI`` + + VSS to COD ratio for inert particulates :math:`i_{VSS,XI}`. + + **Unit:** :math:`g~VSS~(g~COD)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\gt 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_IVSS_XS`` + + VSS to COD ratio for slowly biodegradable substrate :math:`i_{VSS,XS}`. + + **Unit:** :math:`g~VSS~(g~COD)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\gt 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_IVSS_STO`` + + VSS to COD ratio for storage products :math:`i_{VSS,STO}`. + + **Unit:** :math:`g~VSS~(g~COD)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\gt 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_IVSS_BM`` + + VSS to COD ratio for biomass :math:`i_{VSS,BM}`. + + **Unit:** :math:`g~VSS~(g~COD)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\gt 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_ITSS_VSS_BM`` + + TSS to VSS ratio for biomass :math:`i_{TSS/VSS,BM}`. + + **Unit:** :math:`g~TSS~(g~VSS)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\gt 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_FISS_BM_PROD`` + + Fraction of ISS from biomass production :math:`f_{ISS,BM,prod}`. + + **Unit:** dimensionless + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`[0, 1]` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_FSI`` + + Fraction of SI from hydrolysis :math:`f_{SI}`. + + **Unit:** dimensionless + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`[0, 1]` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_FXI`` + + Fraction of XI from endogenous respiration :math:`f_{XI}`. + + **Unit:** dimensionless + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`[0, 1]` **Length:** 1 + ================ ============================= ======================================================== + +**Yield coefficients** + +``ASM3_YH_AER`` + + Aerobic yield of heterotrophs :math:`Y_{H,aer}`. + + **Unit:** :math:`g~COD~(g~COD)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\gt 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_YH_ANOX`` + + Anoxic yield of heterotrophs :math:`Y_{H,anox}`. + + **Unit:** :math:`g~COD~(g~COD)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\gt 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_YSTO_AER`` + + Aerobic yield of storage :math:`Y_{STO,aer}`. + + **Unit:** :math:`g~COD~(g~COD)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\gt 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_YSTO_ANOX`` + + Anoxic yield of storage :math:`Y_{STO,anox}`. + + **Unit:** :math:`g~COD~(g~COD)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\gt 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_YA`` + + Yield of autotrophs :math:`Y_A`. + + **Unit:** :math:`g~COD~(g~N)^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\gt 0` **Length:** 1 + ================ ============================= ======================================================== + +**Adsorption fractionation (optional)** + +``ASM3_FSI_AD`` + + (Optional) Fraction of adsorbable SI :math:`f_{SI,ad}`. Only used if 15 components are defined. + + **Unit:** dimensionless + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`[0, 1]` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_FSS_AD`` + + (Optional) Fraction of adsorbable SS :math:`f_{SS,ad}`. Only used if 15 components are defined. + + **Unit:** dimensionless + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`[0, 1]` **Length:** 1 + ================ ============================= ======================================================== + +**Maximum rates** + +``ASM3_KH20`` + + Hydrolysis rate constant :math:`k_H` at 20 °C. + + **Unit:** :math:`d^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_KSTO20`` + + Maximum storage rate :math:`K_{STO}` at 20 °C. + + **Unit:** :math:`d^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\gt 0` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_MU_H20`` + + Heterotrophic max. growth rate :math:`\mu_{H}` at 20 °C. + + **Unit:** :math:`d^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_BH20`` + + Rate constant for lysis and decay :math:`b_H` at 20 °C. + + **Unit:** :math:`d^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_MU_AUT20`` + + Autotrophic max. growth rate :math:`\mu_{AUT}` at 20 °C. + + **Unit:** :math:`d^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_BAUT20`` + + Rate constant :math:`b_{AUT}` for decay of autotrophs at 20 °C. + + **Unit:** :math:`d^{-1}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +**Anoxic reduction factors** + +``ASM3_ETA_HNO3`` + + Anoxic reduction factor :math:`\eta_{HNO_3}` – heterotrophic growth. + + **Unit:** dimensionless + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_ETAH_END`` + + Anoxic reduction factor :math:`\eta_{H_{end}}` – endogenous respiration XH. + + **Unit:** dimensionless + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_ETAN_END`` + + Anoxic reduction factor :math:`\eta_{N_{end}}` – endogenous respiration XA. + + **Unit:** dimensionless + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +**Saturation/inhibition coefficients** + +``ASM3_KX`` + + Saturation/inhibition coefficient :math:`KX` for particulate COD. + + **Unit:** :math:`g~COD~m_{SP}^{-3}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_KHO2`` + + Saturation/inhibition coefficient :math:`KHO_2` for oxygen, heterotrophic growth. + + **Unit:** :math:`g~O_2~m_{SP}^{-3}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_KHSS`` + + Saturation/inhibition coefficient :math:`KHSS` for readily biodegradable substrates. + + **Unit:** :math:`g~COD~m_{SP}^{-3}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_KHNO3`` + + Saturation/inhibition coefficient :math:`KHNO_3` for nitrate. + + **Unit:** :math:`g~N~m_{SP}^{-3}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_KHNH4`` + + Saturation/inhibition coefficient :math:`KHNH_4` for ammonium (nutrient). + + **Unit:** :math:`g~N~m_{SP}^{-3}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_KHALK`` + + Saturation coefficient :math:`KH_{ALK}` for alkalinity (HCO3-). + + **Unit:** :math:`mol~m_{SP}^{-3}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_KHSTO`` + + Saturation coefficient :math:`KH_{STO}` for storage products. + + **Unit:** dimensionless + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_KNO2`` + + Saturation coefficient :math:`K_{NO_2}` for oxygen, autotrophic growth. + + **Unit:** :math:`g~O_2~m_{SP}^{-3}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_KNNH4`` + + Saturation coefficient :math:`K_{NNH_4}` for ammonium (substrate), autotrophic growth. + + **Unit:** :math:`g~N~m_{SP}^{-3}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +``ASM3_KNALK`` + + Saturation coefficient :math:`K_{NALK}` for alkalinity (HCO3-), autotrophic growth. + + **Unit:** :math:`mol~m_{SP}^{-3}` + + ================ ============================= ======================================================== + **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 + ================ ============================= ======================================================== + +**Example** + +.. python:: + + # Example of setting up an ASM3 reaction model in a unit operation with bulk reaction + + # Setup ASM3 reaction for unit 000 with example values + model.root.input.model.unit_000.reaction_model = 'ACTIVATED_SLUDGE_MODEL3' + model.root.input.model.unit_000.reaction_bulk.asm3_insi = 0.01 + model.root.input.model.unit_000.reaction_bulk.asm3_inss = 0.03 + model.root.input.model.unit_000.reaction_bulk.asm3_inxi = 0.04 + model.root.input.model.unit_000.reaction_bulk.asm3_inxs = 0.03 + model.root.input.model.unit_000.reaction_bulk.asm3_inbm = 0.07 + model.root.input.model.unit_000.reaction_bulk.asm3_ivss_xi = 0.751879699 + model.root.input.model.unit_000.reaction_bulk.asm3_ivss_xs = 0.555555556 + model.root.input.model.unit_000.reaction_bulk.asm3_ivss_sto = 0.6 + model.root.input.model.unit_000.reaction_bulk.asm3_ivss_bm = 0.704225352 + model.root.input.model.unit_000.reaction_bulk.asm3_itss_vss_bm = 1.086956522 + + + model.root.input.model.unit_000.reaction_bulk.asm3_fiss_bm_prod = 1 + model.root.input.model.unit_000.reaction_bulk.asm3_fsi = 0 + model.root.input.model.unit_000.reaction_bulk.asm3_yh_aer = 0.8 + model.root.input.model.unit_000.reaction_bulk.asm3_yh_anox = 0.65 + + model.root.input.model.unit_000.reaction_bulk.asm3_ysto_aer = 0.8375 + model.root.input.model.unit_000.reaction_bulk.asm3_ysto_anox = 0.7 + model.root.input.model.unit_000.reaction_bulk.asm3_fxi = 0.2 + model.root.input.model.unit_000.reaction_bulk.asm3_ya = 0.24 + model.root.input.model.unit_000.reaction_bulk.asm3_kh20 = 9 + model.root.input.model.unit_000.reaction_bulk.asm3_kx = 1 + model.root.input.model.unit_000.reaction_bulk.asm3_ksto20 = 12 + model.root.input.model.unit_000.reaction_bulk.asm3_mu_h20 = 3 + model.root.input.model.unit_000.reaction_bulk.asm3_bh20 = 0.33 + model.root.input.model.unit_000.reaction_bulk.asm3_eta_hno3 = 0.5 + model.root.input.model.unit_000.reaction_bulk.asm3_khO2 = 0.2 + model.root.input.model.unit_000.reaction_bulk.asm3_khss = 10 + model.root.input.model.unit_000.reaction_bulk.asm3_khno3 = 0.5 + model.root.input.model.unit_000.reaction_bulk.asm3_khnh4 = 0.01 + model.root.input.model.unit_000.reaction_bulk.asm3_khalk = 0.1 + model.root.input.model.unit_000.reaction_bulk.asm3_khsto = 0.1 + model.root.input.model.unit_000.reaction_bulk.asm3_mu_aut20 = 1.12 + model.root.input.model.unit_000.reaction_bulk.asm3_baut20 = 0.18 + model.root.input.model.unit_000.reaction_bulk.asm3_etah_end = 0.5 + model.root.input.model.unit_000.reaction_bulk.asm3_etan_end = 0.5 + model.root.input.model.unit_000.reaction_bulk.asm3_kno2 = 0.5 + model.root.input.model.unit_000.reaction_bulk.asm3_knnh4 = 0.7 + model.root.input.model.unit_000.reaction_bulk.asm3_knalk = 0.5 + model.root.input.model.unit_000.reaction_bulk.asm3_t = 12 + + + model.root.input.model.unit_000.reaction_bulk.asm3_v = 1000.0 + model.root.input.model.unit_000.reaction_bulk.asm3_io2 = 0.0 diff --git a/doc/interface/reaction/index.rst b/doc/interface/reaction/index.rst index a53b00f7f..8059693a3 100644 --- a/doc/interface/reaction/index.rst +++ b/doc/interface/reaction/index.rst @@ -30,6 +30,7 @@ The input group for parameters of single reactions is given by ``phase_reaction_ mass_action_law michaelis_menten_kinetics + activated_sludge_model Cross Phase Reactions diff --git a/doc/modelling/reaction/activated_sludge_model.rst b/doc/modelling/reaction/activated_sludge_model.rst new file mode 100644 index 000000000..852f4c2c3 --- /dev/null +++ b/doc/modelling/reaction/activated_sludge_model.rst @@ -0,0 +1,615 @@ +.. _activated_sludge_model: + +Activated Sludge Model (ASM3h) +=============================== + +The Activated Sludge Model 3 extended (ASM3h) is a comprehensive reaction model for simulating biological +wastewater treatment processes. It belongs to the Activated Sludge Models (ASM) family, a set of standardized +models developed by the International Water Association (IWA). The ASM3h model describes the fate of 13 +biochemical components in wastewater treatment systems. + +Model Components +~~~~~~~~~~~~~~~~ + +The ASM3h model tracks 13 biochemical components representing species and compounds in wastewater: + +.. list-table:: + :header-rows: 1 + :widths: 20 80 + + * - Abbreviation + - Component + * - :math:`SO` + - Dissolved oxygen + * - :math:`SS` + - Readily biodegradable substrate + * - :math:`SNH` + - Ammonium + * - :math:`SNO` + - Nitrite and nitrate + * - :math:`SN2` + - Dinitrogen, released by denitrification + * - :math:`SALK` + - Alkalinity, bicarbonate + * - :math:`SI` + - Soluble inert organic + * - :math:`XI` + - Inert Particulate organic + * - :math:`XS` + - Slowly biodegradable substrate + * - :math:`XH` + - Heterotrophic biomass + * - :math:`XSTO` + - Organics stored by heterotrophs + * - :math:`XA` + - Autotrophic, nitrifying biomass + * - :math:`XMI` + - Mineral particulate matter from biomass + +The net flux for each component is computed using the stoichiometric matrix :math:`S \in \mathbb{R}^{13 \times 13}` +and a reaction rate vector :math:`\varphi_j(c)`, where subscript :math:`j` denotes the reaction index. +The ASM3h model comprises 13 biochemical reactions detailed below. + +Biochemical Reaction Equations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: + :header-rows: 1 + :widths: 15 35 50 + + * - Reaction Number + - Reaction Description + - Rate Equation + * - :math:`r_1` + - Hydrolysis of organic structures + - :math:`k_{h,20} \cdot f_T^{0.04} \cdot \frac{XS/XH}{XS/XH + K_X} \cdot XH` + * - :math:`r_2` + - Aerobic storage of SS + - :math:`k_{STO} \cdot \frac{SO}{SO + K_{h,O2}} \cdot \frac{SS}{SS + K_{h,SS}} \cdot XH` + * - :math:`r_3` + - Anoxic storage of SS + - :math:`k_{STO} \cdot \eta_{h,NO3} \cdot \frac{K_{h,O2}}{SO + K_{h,O2}} \cdot \frac{SNO}{SNO + K_{h,NO3}} \cdot \frac{SS}{SS + K_{h,SS}} \cdot XH` + * - :math:`r_4` + - Aerobic growth of XH + - :math:`\mu_H \cdot \frac{SO}{SO + K_{h,O2}} \cdot \frac{XSTO/XH}{XSTO/XH + K_{h,STO}} \cdot \frac{SNH}{SNH + K_{h,NH4}} \cdot \frac{SALK}{SALK + K_{h,ALK}} \cdot XH` + * - :math:`r_5` + - Anoxic growth of XH (denitrification) + - :math:`\mu_H \cdot \eta_{h,NO3} \cdot \frac{K_{h,O2}}{SO + K_{h,O2}} \cdot \frac{SNO}{SNO + K_{h,NO3}} \cdot \frac{XSTO/XH}{XSTO/XH + K_{h,STO}} \cdot \frac{SNH}{SNH + K_{h,NH4}} \cdot \frac{SALK}{SALK + K_{h,ALK}} \cdot XH` + * - :math:`r_6` + - Aerobic endogenous respiration of XH + - :math:`b_H \cdot \frac{SO}{SO + K_{h,O2}} \cdot XH` + * - :math:`r_7` + - Anoxic endogenous respiration of XH + - :math:`b_H \cdot \eta_{h,end} \cdot \frac{K_{h,O2}}{SO + K_{h,O2}} \cdot \frac{SNO}{SNO + K_{h,NO3}} \cdot XH` + * - :math:`r_8` + - Aerobic respiration of internal cell storage products + - :math:`b_H \cdot \frac{SO}{SO + K_{h,O2}} \cdot \frac{XSTO}{XSTO + K_{h,STO}} \cdot XH` + * - :math:`r_9` + - Anoxic respiration of internal cell storage products + - :math:`b_H \cdot \eta_{h,end} \cdot \frac{K_{h,O2}}{SO + K_{h,O2}} \cdot \frac{SNO}{SNO + K_{h,NO3}} \cdot \frac{XSTO}{XSTO + K_{h,STO}} \cdot XH` + * - :math:`r_{10}` + - Aerobic growth of XA + - :math:`\mu_{AUT} \cdot \frac{SO}{SO + K_{N,O2}} \cdot \frac{SNH}{SNH + K_{N,NH4}} \cdot \frac{SALK}{SALK + K_{N,ALK}} \cdot XA` + * - :math:`r_{11}` + - Aerobic endogenous respiration of XA + - :math:`b_{AUT} \cdot \frac{SO}{SO + K_{N,O2}} \cdot XA` + * - :math:`r_{12}` + - Anoxic endogenous respiration of XA + - :math:`b_{AUT} \cdot \eta_{N,end} \cdot \frac{K_{N,O2}}{SO + K_{N,O2}} \cdot \frac{SNO}{SNO + K_{h,NO3}} \cdot XA` + * - :math:`r_{13}` + - Aeration + - :math:`\frac{iO_2}{1000}` + * - :math:`r_{14}` + - Anoxic storage of SS\ :sub:`ad` (optional) + - :math:`k_{STO} \cdot \eta_{h,NO3} \cdot \frac{K_{h,O2}}{SO + K_{h,O2}} \cdot \frac{SS_{ad}}{SS + SS_{ad} + K_{h,SS}} \cdot \frac{SNO}{SNO + K_{h,NO3}} \cdot XH` + * - :math:`r_{15}` + - Aerobic storage of SS\ :sub:`ad` (optional) + - :math:`k_{STO} \cdot \frac{SO}{SO + K_{h,O2}} \cdot \frac{SS_{ad}}{SS + SS_{ad} + K_{h,SS}} \cdot XH` + +Reactions :math:`r_{14}` and :math:`r_{15}` are only active when 15 components are defined (including SS\ :sub:`ad` and SI\ :sub:`ad`). + + +Stoichiometric Matrix +~~~~~~~~~~~~~~~~~~~~~ + +The stoichiometric matrix :math:`S` encodes the mass balance coefficients for each biochemical reaction: + +.. list-table:: + :header-rows: 1 + :widths: 10 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 + + * - Component\\Reaction + - :math:`r_1` + - :math:`r_2` + - :math:`r_3` + - :math:`r_4` + - :math:`r_5` + - :math:`r_6` + - :math:`r_7` + - :math:`r_8` + - :math:`r_9` + - :math:`r_{10}` + - :math:`r_{11}` + - :math:`r_{12}` + - :math:`r_{13}` + - :math:`r_{14}^*` + - :math:`r_{15}^*` + * - :math:`SO` + - 0 + - :math:`Y_{STO,aer} - 1` + - :math:`Y_{STO,anox} - 1` + - :math:`1 - 1/Y_{H,aer}` + - :math:`1 - 1/Y_{H,anox}` + - :math:` f_{XI} - 1` + - :math:`f_{XI} - 1` + - -1 + - -1 + - :math:`-(64/14) \cdot 1/Y_A + 1` + - :math:`f_{XI} - 1` + - :math:`f_{XI} - 1` + - 1 + - 0 + - 0 + * - :math:`SS` + - :math:`(1 - f_{SI})(1 - f_{SS,ad})` + - -1 + - -1 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + * - :math:`SS_{ad}^*` + - :math:`(1 - f_{SI}) \cdot f_{SS,ad}` + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - -1 + - -1 + * - :math:`SNH` + - :math:`i_{N,XS} - i_{N,SI} \cdot f_{SI} - (1 - f_{SI}) \cdot i_{N,SS}` + - :math:`i_{N,SS}` + - :math:`i_{N,SS}` + - :math:`-i_{N,BM}` + - :math:`-i_{N,BM}` + - :math:`i_{N,BM} - f_{XI} \cdot i_{N,XI}` + - :math:`i_{N,BM} -f_{XI} \cdot i_{N,XI}` + - 0 + - 0 + - :math:`-1/Y_A - i_{N,BM}` + - :math:`f_{XI} \cdot i_{N,XI} + i_{N,BM}` + - :math:`f_{XI} \cdot i_{N,XI} + i_{N,BM}` + - 0 + - 0 + - 0 + * - :math:`SNO` + - 0 + - 0 + - :math:`(Y_{STO,anox} - 1) / (40/14)` + - 0 + - :math:`(1 - 1/Y_{H,anox}) / (40/14)` + - 0 + - :math:`(f_{XI} - 1) / (40/14)` + - 0 + - :math:`-14/40` + - :math:`1/Y_A` + - 0 + - :math:`(f_{XI} - 1) / (40/14)` + - 0 + - 0 + - 0 + * - :math:`SN2` + - 0 + - 0 + - :math:`(1 - Y_{STO,anox}) / (40/14)` + - 0 + - :math:`(1/Y_{H,anox} - 1) / (40/14)` + - 0 + - :math:`(-f_{XI} - 1) / (40/14)` + - 0 + - :math:`--14/40` + - 0 + - 0 + - :math:`(1 + f_{XI} ) / (40/14)` + - 0 + - 0 + - 0 + * - :math:`SALK` + - :math:`(i_{N,XS} - i_{N,SI} \cdot f_{SI} - (1 - f_{SI}) \cdot i_{N,SS})/14` + - :math:`i_{N,SS} / 14` + - :math:`i_{N,SS} / 14 - (Y_{STO,anox} - 1) / 40` + - :math:`-i_{N,BM} / 14` + - :math:`-i_{N,BM} / 14 - (1 - 1/Y_{H,anox}) / 40` + - :math:`(-f_{XI} \cdot i_{N,XI} + i_{N,BM}) / 14` + - :math:`(-f_{XI} \cdot i_{N,XI} + i_{N,BM}) / 14 (-f_{XI} + 1) / 40` + - 0 + - :math:`1/40` + - :math:`-(1/Y_A) 1/7- i_{N,BM}/14` + - :math:`f_{XI} \cdot i_{N,XI} + i_{N,BM} / 14` + - :math:`(-f_{XI} \cdot i_{N,XI} + i_{N,BM})/14 - (f_{XI} - 1) / 40` + - 0 + - 0 + - 0 + * - :math:`SI` + - :math:`f_{SI}(1 - f_{SI,ad})` + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + * - :math:`SI_{ad}^*` + - :math:`f_{SI} \cdot f_{SI,ad}` + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + * - :math:`XI` + - 0 + - 0 + - 0 + - 0 + - 0 + - :math:`f_{XI}` + - :math:`f_{XI}` + - 0 + - 0 + - 0 + - :math:`f_{XI}` + - :math:`f_{XI}` + - 0 + - 0 + - 0 + * - :math:`XS` + - -1 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + * - :math:`XH` + - 0 + - 0 + - 0 + - 1 + - 1 + - -1 + - -1 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + * - :math:`XSTO` + - 0 + - :math:`Y_{STO,aer}` + - :math:`Y_{STO,anox}` + - :math:`-1/Y_{H,aer}` + - :math:`-1/Y_{H,anox}` + - 0 + - 0 + - -1 + - -1 + - 0 + - 0 + - 0 + - 0 + - :math:`Y_{STO,anox}` + - :math:`Y_{STO,aer}` + * - :math:`XA` + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 1 + - -1 + - -1 + - 0 + - 0 + - 0 + * - :math:`XMI` + - 0 + - 0 + - 0 + - 0 + - 0 + - :math:`f_{XMI,BI}` + - :math:`f_{XMI,BI}` + - 0 + - 0 + - 0 + - :math:`f_{XMI,BI}` + - :math:`f_{XMI,BI}` + - 0 + - 0 + - 0 + +:sup:`*` Optional: only active when 15 components are defined (SS\ :sub:`ad` and SI\ :sub:`ad`). + + +.. figure:: images/ASM3h+.png + :width: 100% + :align: center + + Network representation of the ASM3h model. + +Model Parameters +~~~~~~~~~~~~~~~~ + +Stoichiometric Parameters +------------------------- + +.. list-table:: + :header-rows: 1 + :widths: 20 60 20 + + * - Symbol + - Description + - Unit + * - :math:`i_{N,SI}` + - Nitrogen content of soluble inert organics + - :math:`g~N~(g~COD)^{-1}` + * - :math:`i_{N,SS}` + - Nitrogen content of readily biodegradable substrate + - :math:`g~N~(g~COD)^{-1}` + * - :math:`i_{N,XI}` + - Nitrogen content of inert particulate organics + - :math:`g~N~(g~COD)^{-1}` + * - :math:`i_{N,XS}` + - Nitrogen content of slowly biodegradable substrate + - :math:`g~N~(g~COD)^{-1}` + * - :math:`i_{N,BM}` + - Nitrogen content of biomass + - :math:`g~N~(g~COD)^{-1}` + * - :math:`i_{VSS,XI}` + - VSS to COD ratio for inert particulates + - :math:`g~VSS~(g~COD)^{-1}` + * - :math:`i_{VSS,XS}` + - VSS to COD ratio for slowly biodegradable substrate + - :math:`g~VSS~(g~COD)^{-1}` + * - :math:`i_{VSS,STO}` + - VSS to COD ratio for storage products + - :math:`g~VSS~(g~COD)^{-1}` + * - :math:`i_{VSS,BM}` + - VSS to COD ratio for biomass + - :math:`g~VSS~(g~COD)^{-1}` + * - :math:`i_{TSS/VSS,BM}` + - TSS to VSS ratio for biomass + - :math:`g~TSS~(g~VSS)^{-1}` + * - :math:`f_{SI}` + - Fraction of SI from hydrolysis + - dimensionless + * - :math:`f_{XI}` + - Fraction of XI from endogenous respiration + - dimensionless + * - :math:`f_{ISS,BM,prod}` + - Fraction of ISS from biomass production + - dimensionless + * - :math:`Y_{H,aer}` + - Aerobic yield of heterotrophs + - :math:`g~COD~(g~COD)^{-1}` + * - :math:`Y_{H,anox}` + - Anoxic yield of heterotrophs + - :math:`g~COD~(g~COD)^{-1}` + * - :math:`Y_{STO,aer}` + - Aerobic yield of storage + - :math:`g~COD~(g~COD)^{-1}` + * - :math:`Y_{STO,anox}` + - Anoxic yield of storage + - :math:`g~COD~(g~COD)^{-1}` + * - :math:`Y_A` + - Yield of autotrophs + - :math:`g~COD~(g~N)^{-1}` + +Kinetic Parameters +------------------ + +.. list-table:: + :header-rows: 1 + :widths: 20 60 20 + + * - Symbol + - Description + - Unit + * - :math:`k_H` + - Hydrolysis rate constant (20 °C) + - :math:`d^{-1}` + * - :math:`K_{STO}` + - Maximum storage rate (20 °C) + - :math:`d^{-1}` + * - :math:`\mu_H` + - Heterotrophic max. growth rate (20 °C) + - :math:`d^{-1}` + * - :math:`b_H` + - Rate constant for lysis and decay (20 °C) + - :math:`d^{-1}` + * - :math:`\mu_{AUT}` + - Autotrophic max. growth rate (20 °C) + - :math:`d^{-1}` + * - :math:`b_{AUT}` + - Rate constant for decay of autotrophs (20 °C) + - :math:`d^{-1}` + +Anoxic Reduction Factors +------------------------ + +.. list-table:: + :header-rows: 1 + :widths: 20 60 20 + + * - Symbol + - Description + - Unit + * - :math:`\eta_{HNO_3}` + - Anoxic reduction factor for heterotrophic growth + - dimensionless + * - :math:`\eta_{H,end}` + - Anoxic reduction factor for endogenous respiration of XH + - dimensionless + * - :math:`\eta_{N,end}` + - Anoxic reduction factor for endogenous respiration of XA + - dimensionless + +Saturation/Inhibition Coefficients +---------------------------------- + +.. list-table:: + :header-rows: 1 + :widths: 20 60 20 + + * - Symbol + - Description + - Unit + * - :math:`K_X` + - Saturation coefficient for particulate COD + - :math:`g~COD~(g~COD)^{-1}` + * - :math:`K_{H,O_2}` + - Saturation coefficient for oxygen (heterotrophs) + - :math:`g~O_2~m^{-3}` + * - :math:`K_{H,SS}` + - Saturation coefficient for readily biodegradable substrates + - :math:`g~COD~m^{-3}` + * - :math:`K_{H,NO_3}` + - Saturation coefficient for nitrate (heterotrophs) + - :math:`g~N~m^{-3}` + * - :math:`K_{H,NH_4}` + - Saturation coefficient for ammonium (nutrient) + - :math:`g~N~m^{-3}` + * - :math:`K_{H,ALK}` + - Saturation coefficient for alkalinity (heterotrophs) + - :math:`mol~m^{-3}` + * - :math:`K_{H,STO}` + - Saturation coefficient for storage products + - :math:`g~COD~(g~COD)^{-1}` + * - :math:`K_{N,O_2}` + - Saturation coefficient for oxygen (autotrophs) + - :math:`g~O_2~m^{-3}` + * - :math:`K_{N,NH_4}` + - Saturation coefficient for ammonium (substrate) + - :math:`g~N~m^{-3}` + * - :math:`K_{N,ALK}` + - Saturation coefficient for alkalinity (autotrophs) + - :math:`mol~m^{-3}` + +Temperature Dependence +---------------------- + +Kinetic rates are corrected for temperature using Arrhenius-type expressions: + +.. math:: + + k(T) = k_{20} \cdot \exp\left(-\theta \cdot (20 - T)\right) + +with temperature coefficients: + +- :math:`\theta = 0.04` for hydrolysis +- :math:`\theta = 0.06952` for heterotrophic processes (:math:`K_{STO}`, :math:`\mu_H`, :math:`b_H`) +- :math:`\theta = 0.105` for autotrophic processes (:math:`\mu_{AUT}`, :math:`b_{AUT}`) + +Optional Parameters +------------------- + +.. list-table:: + :header-rows: 1 + :widths: 20 60 20 + + * - Symbol + - Description + - Unit + * - :math:`f_{SI,ad}` + - Fraction of adsorbable SI + - dimensionless + * - :math:`f_{SS,ad}` + - Fraction of adsorbable SS + - dimensionless + * - :math:`iO_2` + - Aeration oxygen input rate + - :math:`g~O_2~m^{-3}~d^{-1}` + * - :math:`V` + - Reactor volume (for aeration) + - :math:`m^3` + +Configuration in CADET +~~~~~~~~~~~~~~~~~~~~~~ + +For configuration details, see :ref:`activated_sludge_model_config`. + +Aeration Strategy +----------------- + +The ASM3h model includes an aeration reaction (:math:`r_{13}`) that is activated when both ``ASM3_IO2`` and ``ASM3_V`` are set. +The aeration rate is computed as :math:`iO_2 / 1000`. To disable aeration, omit these parameters. +For more flexible aeration handling, model oxygen input explicitly via the Inlet unit operation (see :ref:`inlet_operation`). + +Substrate Fractionation +----------------------- + +The organic substrates can be fractionated into adsorbable and non-adsorbable components via ``ASM3_FSI_AD`` and ``ASM3_FSS_AD``. +This requires at least 15 components (including ``SI_ad`` and ``SS_ad``) and enables coupling with binding models for +adsorptive interactions during biological treatment. If fewer than 15 components are defined, adsorbed components are disabled. + +When fractionation is enabled, reactions :math:`r_{14}` and :math:`r_{15}` become active for aerobic and anoxic storage of adsorbed substrate SS\ :sub:`ad`. + + +Implementation Notes +-------------------- + +- Component indices can be customized via ``ASM3_COMP_IDX`` if components are ordered differently or offset. +- A minimum threshold of :math:`X_H = 0.1` is applied to prevent division by zero in reactions involving :math:`X_S/X_H` and :math:`X_{STO}/X_H` ratios. + + +References +~~~~~~~~~~ + +TODO \ No newline at end of file diff --git a/doc/modelling/reaction/images/ASM3h+.png b/doc/modelling/reaction/images/ASM3h+.png new file mode 100644 index 000000000..4ede650b6 Binary files /dev/null and b/doc/modelling/reaction/images/ASM3h+.png differ diff --git a/doc/modelling/reaction/images/netzwerk.jpg b/doc/modelling/reaction/images/netzwerk.jpg new file mode 100644 index 000000000..fae20be83 Binary files /dev/null and b/doc/modelling/reaction/images/netzwerk.jpg differ diff --git a/doc/modelling/reaction/index.rst b/doc/modelling/reaction/index.rst index 805e275f9..2506a3372 100644 --- a/doc/modelling/reaction/index.rst +++ b/doc/modelling/reaction/index.rst @@ -39,6 +39,10 @@ Historically, a chromatography system is modeled as a reaction system without co - :ref:`thomas_model` - :ref:`rate_constant_distribution_theory` +More specialized models are also implemented: + + - :ref:`activated_sludge_model` + .. _dependence-on-external-function_react: Dependence on external function diff --git a/src/libcadet/CMakeLists.txt b/src/libcadet/CMakeLists.txt index 7d0362753..d9c51f6a6 100644 --- a/src/libcadet/CMakeLists.txt +++ b/src/libcadet/CMakeLists.txt @@ -118,7 +118,7 @@ set(LIBCADET_REACTIONMODEL_SOURCES ${CMAKE_SOURCE_DIR}/src/libcadet/model/reaction/MassActionLawReaction.cpp ${CMAKE_SOURCE_DIR}/src/libcadet/model/reaction/MassActionLawReactionCrossPhase.cpp ${CMAKE_SOURCE_DIR}/src/libcadet/model/reaction/MichaelisMentenReaction.cpp - + ${CMAKE_SOURCE_DIR}/src/libcadet/model/reaction/ActivatedSludgeModelThree.cpp ${CMAKE_SOURCE_DIR}/src/libcadet/model/reaction/CrystallizationReaction.cpp ) diff --git a/src/libcadet/ReactionModelFactory.cpp b/src/libcadet/ReactionModelFactory.cpp index d0b06d2e5..dce5f036e 100644 --- a/src/libcadet/ReactionModelFactory.cpp +++ b/src/libcadet/ReactionModelFactory.cpp @@ -23,6 +23,7 @@ namespace cadet void registerMassActionLawReactionCrossPhase(std::unordered_map>& reactions); void registerDummyReaction(std::unordered_map>& reactions); void registerMichaelisMentenReaction(std::unordered_map>& reactions); + void registerActivatedSludgeModelThreeReaction(std::unordered_map>& reactions); void registerCrystallizationReaction(std::unordered_map>& reactions); } @@ -35,6 +36,7 @@ namespace cadet model::reaction::registerMassActionLawReaction(_dynamicModels); model::reaction::registerMassActionLawReactionCrossPhase(_dynamicModels); model::reaction::registerMichaelisMentenReaction(_dynamicModels); + model::reaction::registerActivatedSludgeModelThreeReaction(_dynamicModels); model::reaction::registerCrystallizationReaction(_dynamicModels); } diff --git a/src/libcadet/model/reaction/ActivatedSludgeModelThree.cpp b/src/libcadet/model/reaction/ActivatedSludgeModelThree.cpp new file mode 100644 index 000000000..2536497d4 --- /dev/null +++ b/src/libcadet/model/reaction/ActivatedSludgeModelThree.cpp @@ -0,0 +1,999 @@ +// ============================================================================= +// CADET - The Chromatography Analysis and Design Toolkit +// +// Copyright © 2008-present: The CADET-Core Authors +// Please see the AUTHORS.md file. +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the GNU Public License v3.0 (or, at +// your option, any later version) which accompanies this distribution, and +// is available at http://www.gnu.org/licenses/gpl.html +// ============================================================================= + +#include "model/reaction/ReactionModelBase.hpp" +#include "model/ExternalFunctionSupport.hpp" +#include "model/ModelUtils.hpp" +#include "cadet/Exceptions.hpp" +#include "model/Parameters.hpp" +#include "LocalVector.hpp" +#include "SimulationTypes.hpp" +#include "linalg/ActiveDenseMatrix.hpp" +#include "MathUtil.hpp" +#include "Memory.hpp" + +#include +#include +#include +#include + +/* +{ + "name": "ActivatedSludgeModelThreeParamHandler", + "externalName": "ExtActivatedSludgeModelThreeParamHandler", + "parameters": + [ + { "type": "ScalarParameter", "varName": "kh20", "confName": "ASM3_KH20"}, + { "type": "ScalarParameter", "varName": "T", "confName": "ASM3_T"}, + { "type": "ScalarParameter", "varName": "io2", "confName": "ASM3_IO2"}, + { "type": "ScalarParameter", "varName": "V", "confName": "ASM3_V"}, + { "type": "ScalarParameter", "varName": "k_sto20", "confName": "ASM3_KSTO20"}, + { "type": "ScalarParameter", "varName": "kx", "confName": "ASM3_KX"}, + { "type": "ScalarParameter", "varName": "kho2", "confName": "ASM3_KHO2"}, + { "type": "ScalarParameter", "varName": "khss", "confName": "ASM3_KHSS"}, + { "type": "ScalarParameter", "varName": "khn03", "confName": "ASM3_KHNO3"}, + { "type": "ScalarParameter", "varName": "etahno3", "confName": "ASM3_ETA_HNO3"}, + { "type": "ScalarParameter", "varName": "khnh4", "confName": "ASM3_KHNH4"}, + { "type": "ScalarParameter", "varName": "khalk", "confName": "ASM3_KHALK"}, + { "type": "ScalarParameter", "varName": "khsto", "confName": "ASM3_KHSTO"}, + { "type": "ScalarParameter", "varName": "muh2o", "confName": "ASM3_MU_H20"}, + { "type": "ScalarParameter", "varName": "etahend", "confName": "ASM3_ETAH_END"}, + { "type": "ScalarParameter", "varName": "bh20", "confName": "ASM3_BH20"}, + { "type": "ScalarParameter", "varName": "muAUT20", "confName": "ASM3_MU_AUT20"}, + { "type": "ScalarParameter", "varName": "kno2", "confName": "ASM3_KNO2"}, + { "type": "ScalarParameter", "varName": "knnh4", "confName": "ASM3_KNNH4"}, + { "type": "ScalarParameter", "varName": "knalk", "confName": "ASM3_KNALK"}, + { "type": "ScalarParameter", "varName": "baut20", "confName": "ASM3_BAUT20"}, + { "type": "ScalarParameter", "varName": "etanend", "confName": "ASM3_ETAN_END"} + ] +} +*/ + +/* Parameter description + ------------------------ + + +*/ + + +namespace cadet +{ + +namespace model +{ + +inline const char* ActivatedSludgeModelThreeParamHandler::identifier() CADET_NOEXCEPT { return "ACTIVATED_SLUDGE_MODEL3"; } + +inline bool ActivatedSludgeModelThreeParamHandler::validateConfig(unsigned int nReactions, unsigned int nComp, unsigned int const* nBoundStates) +{ + return true; +} + +inline const char* ExtActivatedSludgeModelThreeParamHandler::identifier() CADET_NOEXCEPT { return "EXT_ACTIVATED_SLUDGE_MODEL3"; } + +inline bool ExtActivatedSludgeModelThreeParamHandler::validateConfig(unsigned int nReactions, unsigned int nComp, unsigned int const* nBoundStates) +{ + return true; +} + +namespace +{ + /** + * @brief Registers a matrix-valued parameter (row-major storage) with components as rows + * @details The matrix-valued parameter has as many rows as there are components in the system. + * @param [in,out] parameters Parameter map + * @param [in] unitOpIdx Unit operation id + * @param [in] parTypeIdx Particle type index + * @param [in] paramName Name of the parameter + * @param [in] mat Matrix to register + */ + inline void registerCompRowMatrix(std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, const std::string& paramName, cadet::linalg::ActiveDenseMatrix& mat) + { + const cadet::StringHash hashName = cadet::hashStringRuntime(paramName); + cadet::registerParam2DArray(parameters, mat.data(), mat.elements(), [=](bool multi, unsigned int row, unsigned int col) + { + return cadet::makeParamId(hashName, unitOpIdx, row, parTypeIdx, cadet::BoundStateIndep, col, cadet::SectionIndep); + }, + mat.columns() + ); + } +} + + +/** + * @brief Activated Sludge Model No. 3 (ASM3) Reaction Kinetics + * @details Implements the ASM3 model for biological wastewater treatment with 13 liquid phase components: + * - Soluble components: SO (dissolved oxygen), SS (readily biodegradable substrate), + * SNH (ammonia-nitrogen), SNO (nitrate-nitrogen), SN2 (dinitrogen gas), + * SALK (alkalinity), SI (soluble inert organic matter) + * - Particulate components: XI (inert particulates), XS (slowly biodegradable substrate), + * XH (heterotrophic biomass), XSTO (internal storage products), XA (autotrophic biomass), + * XMI (inert biomass) + * - Optional adsorbed components (14-15): SI_ad, SS_ad + * + * The model includes 13-15 biokinetic processes: + * - Hydrolysis of organic structures + * - Aerobic and anoxic storage of substrate + * - Aerobic and anoxic growth of heterotrophic biomass (with denitrification) + * - Aerobic and anoxic endogenous respiration of heterotrophs + * - Respiration of internal storage products + * - Aerobic growth of autotrophic biomass (nitrification) + * - Endogenous respiration of autotrophic biomass (aerobic and anoxic) + * - Optional aeration and adsorbed substrate processes + * + * Temperature dependence is modeled using exponential correction factors (Arrhenius-type). + * Component indices can be customized via ASM3_COMP_IDX parameter. + * + * @tparam ParamHandler_t Type that can add support for external function dependence + */ +template +class ActivatedSludgeModelThreeBase : public DynamicReactionModelBase +{ +public: + + ActivatedSludgeModelThreeBase() CADET_NOEXCEPT { } + virtual ~ActivatedSludgeModelThreeBase() CADET_NOEXCEPT { } + + static const char* identifier() { return ParamHandler_t::identifier(); } + virtual const char* name() const CADET_NOEXCEPT { return ParamHandler_t::identifier(); } + + virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) { _paramHandler.setExternalFunctions(extFuns, size); } + virtual bool dependsOnTime() const CADET_NOEXCEPT { return ParamHandler_t::dependsOnTime(); } + virtual bool requiresWorkspace() const CADET_NOEXCEPT { return true; } + virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT + { + return _paramHandler.cacheSize(_stoichiometry.columns(), nComp, totalNumBoundStates) + std::max(_stoichiometry.columns() * sizeof(active), 2 * (_nComp + totalNumBoundStates) * sizeof(double)); + } + + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + { + DynamicReactionModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); + _stoichiometry.resize(nComp, 13); + return true; + } + + virtual unsigned int numReactionsLiquid() const CADET_NOEXCEPT { return _stoichiometry.columns(); } + virtual unsigned int numReactionsCombined() const CADET_NOEXCEPT { return 0; } + + CADET_DYNAMICREACTIONMODEL_BOILERPLATE + +protected: + ParamHandler_t _paramHandler; //!< Handles parameters and their dependence on external functions + + linalg::ActiveDenseMatrix _stoichiometry; + + unsigned int _idxSO = 0; //!< SO component index, default 0 + unsigned int _idxSS = 1; //!< SS / SS_ad component index, default 1 + unsigned int _idxSNH = 2; //!< SNH component index, default 2 + unsigned int _idxSNO = 3; //!< SNO component index, default 3 + unsigned int _idxSN2 = 4; //!< SN2 component index, default 4 + unsigned int _idxSALK = 5; //!< SALK component index, default 5 + unsigned int _idxSI = 6; //!< SI component index, default 6 + unsigned int _idxXI = 7; //!< XI component index, default 7 + unsigned int _idxXS = 8; //!< XS component index, default 8 + unsigned int _idxXH = 9; //!< XH component index, default 9 + unsigned int _idxXSTO = 10; //!< XSTO component index, default 10 + unsigned int _idxXA = 11; //!< XA component index, default 11 + unsigned int _idxXMI = 12; //!< XMI component index, default 12 + unsigned int _idxSI_ad = 13; //!< SI_ad component index, default 13 + unsigned int _idxSS_ad = 14; //!< SS_ad component index, default 14 + + bool _givenAd = true; + bool _activeAeration = false; + + static constexpr double XH_MIN_THRESHOLD = 0.1; + + + + virtual bool configureStoich(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) + { + return true; + } + + + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) + { + _paramHandler.configure(paramProvider, _stoichiometry.columns(), _nComp, _nBoundStates); + _paramHandler.registerParameters(_parameters, unitOpIdx, parTypeIdx, _nComp, _nBoundStates); + + if (_nComp < 13) + throw InvalidParameterException("ASM3 configuration: To use the ASM3 the number of components must be at least 13"); + + if (_nComp < 15) + { + LOG(Debug) << "ASM3 configuration: Less than 15 components are defined, adsorbed components will not be used"; + _givenAd = false; + } + + if (paramProvider.exists("ASM3_COMP_IDX")) + { + const std::vector compIdx = paramProvider.getUint64Array("ASM3_COMP_IDX"); + + if (compIdx.size() != 15 && _givenAd == true) + throw InvalidParameterException("ASM3 configuration: ASM3_COMP_IDX must have 15 elements"); + + if (compIdx.size() != 13 && _givenAd == false) + throw InvalidParameterException("ASM3 configuration: ASM3_COMP_IDX must have 13 elements when not using adsorbed components"); + + if (_nComp < compIdx.size()) + throw InvalidParameterException("ASM3 configuration: ASM3_COMP_IDX has more elements than there are components defined in the system"); + + LOG(Debug) << "ASM3_COMP_IDX set: " << compIdx; + + _idxSO = compIdx[0]; + _idxSS = compIdx[1]; + _idxSNH = compIdx[2]; + _idxSNO = compIdx[3]; + _idxSN2 = compIdx[4]; + _idxSALK = compIdx[5]; + _idxSI = compIdx[6]; + _idxXI = compIdx[7]; + _idxXS = compIdx[8]; + _idxXH = compIdx[9]; + _idxXSTO = compIdx[10]; + _idxXA = compIdx[11]; + _idxXMI = compIdx[12]; + + if(_givenAd) + { + _idxSI_ad = compIdx[13]; + _idxSS_ad = compIdx[14]; + } + } + else + { + LOG(Debug) << "ASM3_COMP_IDX not set, using defaults"; + } + + // handle optional Aeration + double V = 0.0; + double IO2 = 0.0; + if (paramProvider.exists("ASM3_IO2") && paramProvider.exists("ASM3_V")) + { + double V = paramProvider.getDouble("ASM3_V"); + double IO2 = paramProvider.getDouble("ASM3_IO2"); + + _activeAeration = true; + LOG(Debug) << "Active aeration set to " << _activeAeration; + } + else + { + LOG(Debug) << "Active aeration not set, using default (false)"; + } + + + _stoichiometry.resize(15, 15); // option to optimize: Depending on whether adsorbed components are used or not 13/12 or 15 reactions are needed + _stoichiometry.setAll(0); + + // parameter set ASM3h + const double iNSI = paramProvider.getDouble("ASM3_INSI"); + const double iNSS = paramProvider.getDouble("ASM3_INSS"); + const double iNXI = paramProvider.getDouble("ASM3_INXI"); + + const double iNXS = paramProvider.getDouble("ASM3_INXS"); + const double iNBM = paramProvider.getDouble("ASM3_INBM"); + + const double fSI = paramProvider.getDouble("ASM3_FSI"); + const double fXI = paramProvider.getDouble("ASM3_FXI"); + + const double YH_aer = paramProvider.getDouble("ASM3_YH_AER"); + if (YH_aer < 1e-16) + throw InvalidParameterException("ASM3 configuration: YH_aer must be bigger than zero"); + const double YH_anox = paramProvider.getDouble("ASM3_YH_ANOX"); + const double YSTO_aer = paramProvider.getDouble("ASM3_YSTO_AER"); + if (YSTO_aer < 1e-16) + throw InvalidParameterException("ASM3 configuration: YSTO_aer must be bigger than zero"); + const double YSTO_anox = paramProvider.getDouble("ASM3_YSTO_ANOX"); + const double YA = paramProvider.getDouble("ASM3_YA"); + if (YA < 1e-16) + throw InvalidParameterException("ASM3 configuration: YA must be bigger than zero"); + + const double fiSS_BM_prod = paramProvider.getDouble("ASM3_FISS_BM_PROD"); + const double iVSS_BM = paramProvider.getDouble("ASM3_IVSS_BM"); + const double iTSS_VSS_BM = paramProvider.getDouble("ASM3_ITSS_VSS_BM"); + + double fSI_ad = 0.0; + double fSS_ad = 0.0; + if (paramProvider.exists("ASM3_FSI_AD")) + fSI_ad = paramProvider.getDouble("ASM3_FSI_AD"); + if (paramProvider.exists("ASM3_FSS_AD")) + fSS_ad = paramProvider.getDouble("ASM3_FSS_AD"); + + // internal variables + const double fXMI_BM = fiSS_BM_prod * fXI * iVSS_BM * (iTSS_VSS_BM - 1); + const double c1n = iNXS - iNSI * fSI - (1 - fSI) * iNSS; + const double c2n = iNSS; + const double c3n = iNSS; + const double c4n = -iNBM; + const double c5n = c4n; + const double c6n = -fXI * iNXI + iNBM; + const double c7n = c6n; + const double c10n = -1 / YA - iNBM; + const double c11n = -fXI * iNXI + iNBM; + const double c12n = c11n; + const double c3no = (YSTO_anox - 1.0) / (40.0 / 14.0); + const double c5no = (1.0 - 1 / YH_anox) / (40.0 / 14.0); + const double c7no = (fXI - 1) / (40.0 / 14.0); + const double c9no = -14.0 / 40.0; + const double c10no = 1 / YA; + const double c12no = c7no; + const double c1a = c1n / 14.0; + const double c2a = c2n / 14.0; + const double c3a = (c3n - c3no) / 14.0; + const double c4a = c4n / 14.0; + const double c5a = (c5n - c5no) / 14.0; + const double c6a = c6n / 14.0; + const double c7a = (c7n - c7no) / 14.0; + const double c9a = 1 / 40.0; + const double c10a = (c10n - c10no) / 14.0; + const double c11a = c11n / 14.0; + const double c12a = (c12n - c12no) / 14.0; + + // SO + _stoichiometry.native(_idxSO, 1) = YSTO_aer - 1; + _stoichiometry.native(_idxSO, 3) = 1 - 1 / YH_aer; + _stoichiometry.native(_idxSO, 5) = -1 * (1 - fXI); + _stoichiometry.native(_idxSO, 7) = -1; + _stoichiometry.native(_idxSO, 9) = -(64.0 / 14.0) * 1 / YA + 1; + _stoichiometry.native(_idxSO, 10) = -1 * (1 - fXI); + _stoichiometry.native(_idxSO, 13) = YSTO_aer - 1; + + + if (_activeAeration) + _stoichiometry.native(_idxSO, 12) = 1; + else + _stoichiometry.native(_idxSO, 12) = 0; + + + // SS + _stoichiometry.native(_idxSS, 0) = (1 - fSI) * (1 - fSS_ad); + _stoichiometry.native(_idxSS, 1) = -1; + _stoichiometry.native(_idxSS, 2) = -1; + + // SS_ad + _stoichiometry.native(_idxSS_ad, 0) = (1 - fSI) * fSS_ad; + _stoichiometry.native(_idxSS_ad, 13) = -1; + _stoichiometry.native(_idxSS_ad, 14) = -1; + + + // SNH + _stoichiometry.native(_idxSNH, 0) = c1n; + _stoichiometry.native(_idxSNH, 1) = c2n; + _stoichiometry.native(_idxSNH, 2) = c3n; + _stoichiometry.native(_idxSNH, 3) = c4n; + _stoichiometry.native(_idxSNH, 4) = c5n; + _stoichiometry.native(_idxSNH, 5) = c6n; + _stoichiometry.native(_idxSNH, 6) = c7n; + _stoichiometry.native(_idxSNH, 9) = c10n; + _stoichiometry.native(_idxSNH, 10) = c11n; + _stoichiometry.native(_idxSNH, 11) = c12n; + _stoichiometry.native(_idxSNH, 13) = c2n; + _stoichiometry.native(_idxSNH, 14) = c3n; + + // SNO + _stoichiometry.native(_idxSNO, 2) = c3no; + _stoichiometry.native(_idxSNO, 4) = c5no; + _stoichiometry.native(_idxSNO, 6) = c7no; + _stoichiometry.native(_idxSNO, 8) = c9no; + _stoichiometry.native(_idxSNO, 9) = c10no; + _stoichiometry.native(_idxSNO, 11) = c12no; + _stoichiometry.native(_idxSNO, 14) = c3no; + + + // SN2 + _stoichiometry.native(_idxSN2, 2) = -c3no; + _stoichiometry.native(_idxSN2, 4) = -c5no; + _stoichiometry.native(_idxSN2, 6) = -c7no; + _stoichiometry.native(_idxSN2, 8) = -c9no; + _stoichiometry.native(_idxSN2, 11) = -c12no; + _stoichiometry.native(_idxSN2, 14) = -c3no; + + + + // SALK + _stoichiometry.native(_idxSALK, 0) = c1a; + _stoichiometry.native(_idxSALK, 1) = c2a; + _stoichiometry.native(_idxSALK, 2) = c3a; + _stoichiometry.native(_idxSALK, 3) = c4a; + _stoichiometry.native(_idxSALK, 4) = c5a; + _stoichiometry.native(_idxSALK, 5) = c6a; + _stoichiometry.native(_idxSALK, 6) = c7a; + _stoichiometry.native(_idxSALK, 8) = c9a; + _stoichiometry.native(_idxSALK, 9) = c10a; + _stoichiometry.native(_idxSALK, 10) = c11a; + _stoichiometry.native(_idxSALK, 11) = c12a; + _stoichiometry.native(_idxSALK, 12) = c2a; + _stoichiometry.native(_idxSALK, 13) = c3a; + + // SI + _stoichiometry.native(_idxSI, 0) = fSI * (1 - fSI_ad); + + //SI_ad + _stoichiometry.native(_idxSI_ad, 0) = fSI * fSI_ad; + + // XI + _stoichiometry.native(_idxXI, 5) = fXI; + _stoichiometry.native(_idxXI, 6) = fXI; + _stoichiometry.native(_idxXI, 10) = fXI; + _stoichiometry.native(_idxXI, 11) = fXI; + + // XS + _stoichiometry.native(_idxXS, 0) = -1; + + // XH + _stoichiometry.native(_idxXH, 3) = 1; + _stoichiometry.native(_idxXH, 4) = 1; + _stoichiometry.native(_idxXH, 5) = -1; + _stoichiometry.native(_idxXH, 6) = -1; + + + // XSTO + _stoichiometry.native(_idxXSTO, 1) = YSTO_aer; + _stoichiometry.native(_idxXSTO, 2) = YSTO_anox; + _stoichiometry.native(_idxXSTO, 3) = -1 / YH_aer; + _stoichiometry.native(_idxXSTO, 4) = -1 / YH_anox; + _stoichiometry.native(_idxXSTO, 7) = -1; + _stoichiometry.native(_idxXSTO, 8) = -1; + _stoichiometry.native(_idxXSTO, 13) = YSTO_aer; + _stoichiometry.native(_idxXSTO, 14) = YSTO_anox; + + // XA + _stoichiometry.native(_idxXA, 9) = 1; + _stoichiometry.native(_idxXA, 10) = -1; + _stoichiometry.native(_idxXA, 11) = -1; + + // XMI + _stoichiometry.native(_idxXMI, 5) = fXMI_BM; + _stoichiometry.native(_idxXMI, 6) = fXMI_BM; + _stoichiometry.native(_idxXMI, 10) = fXMI_BM; + _stoichiometry.native(_idxXMI, 11) = fXMI_BM; + + return true; + } + + template + int residualFluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, const unsigned int nStates, + StateType const* y, ResidualType* res, const FactorType& factor, LinearBufferAllocator workSpace) const + { + typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + + // Calculate fluxes + typedef typename DoubleActivePromoter::type flux_t; + BufferedArray fluxes = workSpace.array(_stoichiometry.columns()); + + const flux_t T = static_cast::type>(p->T); + if (T < 0) + throw InvalidParameterException("ASM3 configuration: Temperature T must be non-negative"); + + flux_t io2; + flux_t V; + if (_activeAeration) + { + io2 = static_cast::type>(p->io2); + V = static_cast::type>(p->V); + + if(V < 0) + throw InvalidParameterException("ASM3 configuration: Aeration volume V must be non-negative"); + } + + const flux_t kh20 = static_cast::type>(p->kh20); + const flux_t k_sto20 = static_cast::type>(p->k_sto20); + const flux_t kx = static_cast::type>(p->kx); + const flux_t kho2 = static_cast::type>(p->kho2); + const flux_t khss = static_cast::type>(p->khss); + const flux_t khn03 = static_cast::type>(p->khn03); + const flux_t etahno3 = static_cast::type>(p->etahno3); + const flux_t khnh4 = static_cast::type>(p->khnh4); + const flux_t khalk = static_cast::type>(p->khalk); + const flux_t khsto = static_cast::type>(p->khsto); + const flux_t muh2o = static_cast::type>(p->muh2o); + const flux_t etahend = static_cast::type>(p->etahend); + const flux_t bh20 = static_cast::type>(p->bh20); + const flux_t muAUT20 = static_cast::type>(p->muAUT20); + const flux_t kno2 = static_cast::type>(p->kno2); + const flux_t knnh4 = static_cast::type>(p->knnh4); + const flux_t knalk = static_cast::type>(p->knalk); + const flux_t baut20 = static_cast::type>(p->baut20); + const flux_t etanend = static_cast::type>(p->etanend); + + // derived parameters + const double ft04 = exp(-0.04 * (20.0 - static_cast(T))); + const double ft07 = exp(-0.06952 * (20.0 - static_cast(T))); + const double ft105 = exp(-0.105 * (20.0 - static_cast(T))); + const double k_sto = static_cast(k_sto20) * ft07; + const double muH = static_cast(muh2o) * ft07; + const double bH = static_cast(bh20) * ft07; + const double muAUT = static_cast(muAUT20) * ft105; + const double bAUT = static_cast(baut20) * ft105; + + StateType SO = y[_idxSO]; + StateType SS = y[_idxSS]; + StateType SS_ad = 0.0; + if (_givenAd) SS_ad = y[_idxSS_ad]; + StateType SNH = y[_idxSNH]; + StateType SNO = y[_idxSNO]; + // StateType SN2 = y[_idxSN2]; unused + StateType SALK = y[_idxSALK]; + // StateType SI = y[_idxSI]; // unused + // StateType XI = y[_idxXI]; // unused + StateType XS = y[_idxXS]; + StateType XH = y[_idxXH]; + StateType XSTO = y[_idxXSTO]; + StateType XA = y[_idxXA]; + // StateType XMI = y[_idxXMI]; // unused + + + // p1: Hydrolysis of organic structures + fluxes[0] = kh20 * ft04 * (XS/XH) / ((XS/XH) + kx) * XH; + if (XH < XH_MIN_THRESHOLD) + fluxes[0] = kh20 * ft04 * (XS/0.1) / ((XS/0.1) + kx) * XH; + + // p2: Aerobic storage of SS + fluxes[1] = k_sto * SO / (SO + kho2) * (SS) / ( ( SS + SS_ad ) + khss ) * XH; + + // p3: Anoxic storage of SS + fluxes[2] = k_sto * etahno3 * kho2 / (SO + kho2) * (SS) / ( ( SS_ad + SS ) + khss ) * SNO / (SNO + khn03) * XH; + + // p4: Aerobic growth of heterotrophic biomass (XH) + fluxes[3] = muH * SO / (SO + kho2) * SNH / (SNH + khnh4) * SALK / (SALK + khalk) * ((XSTO/XH)) / (((XSTO/XH)) + khsto) * XH; + if (XH < XH_MIN_THRESHOLD) + fluxes[3] = muH * SO / (SO + kho2) * SNH / (SNH + khnh4) * SALK / (SALK + khalk) * (XSTO / 0.1) / ((XSTO / 0.1) + khsto) * XH; + + // p5: Anoxic growth of heterotrophic biomass (XH, denitrification) + fluxes[4] = muH * etahno3 * kho2 / (kho2 + SO) * SNH / (khnh4 + SNH) * SALK / (khalk + SALK) * (XSTO / XH) * 1 / (khsto + (XSTO / XH)) * SNO / (khn03 + SNO) * XH; + if (XH < XH_MIN_THRESHOLD) + fluxes[4] = muH * etahno3 * kho2 / (kho2 + SO) * SNH / (khnh4 + SNH) * SALK / (khalk + SALK) * (XSTO / 0.1) * 1 / (khsto + (XSTO/0.1)) * SNO / (khn03 + SNO) * XH; + + // r6: Aerobic endogenous respiration of heterotroph microorganisms (XH) + fluxes[5] = bH * SO / (SO + kho2) * XH; + + // r7: Anoxic endogenous respiration of heterotroph microorganisms (XH) + fluxes[6] = bH * etahend * kho2 / (SO + kho2) * SNO / (SNO + khn03) * XH; + + // r8: Aerobic respiration of internal cell storage products + fluxes[7] = bH * SO / (SO + kho2) * XSTO; + + // r9: Anoxic respiration of internal cell storage products + fluxes[8] = bH * etahend * kho2 / (SO + kho2) * SNO / (SNO + khn03) * XSTO; + + // r10: Aerobic growth of autotrophic biomass (XAUT, nitrification) + fluxes[9] = muAUT * SO / (SO + kno2) * SNH / (SNH + knnh4) * SALK / (SALK + knalk) * XA; + + // r11: Aerobic endogenous respiration of autotrophic biomass (XAUT) + fluxes[10] = bAUT * SO / (SO + kho2) * XA; + + // r12: Anoxic endogenous respiration of autotrophic biomass (XAUT) + fluxes[11] = bAUT * etanend * SNO / (SNO + khn03) * kho2 / (SO + kho2) * XA; + + // r13: Aeration + if (_activeAeration) + fluxes[12] = io2 / V; + else + fluxes[12] = 0.0; + + // p14: Anoxic storage of SS_ad + if (_givenAd) + fluxes[13] = k_sto * SO / (SO + kho2) * (SS_ad) / ( ( SS + SS_ad ) + khss ) * XH; + else + fluxes[13] = 0.0; + + + // p15: Aerobic storage of SS_ad + if(_givenAd) + fluxes[14] = k_sto * etahno3 * kho2 / (SO + kho2) * (SS_ad) / ( ( SS_ad + SS ) + khss ) * SNO / (SNO + khn03) * XH; + else + fluxes[14] = 0.0; + + // Add reaction terms to residual + _stoichiometry.multiplyVector(static_cast(fluxes), factor, res); + return 0; + } + + template + int residualCombinedImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, + StateType const* yLiquid, StateType const* ySolid, ResidualType* resLiquid, ResidualType* resSolid, double factor, LinearBufferAllocator workSpace) const + { + std::fill_n(resLiquid, _nComp, 0.0); + + if (_nTotalBoundStates == 0) + return 0; + + std::fill_n(resSolid, _nTotalBoundStates, 0.0); + + return 0; + } + + template + void jacobianFluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, const unsigned int nStates, double const* y, double factor, const RowIterator& jac, LinearBufferAllocator workSpace) const + { + typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + + //parameters + const double kh20 = static_cast(p->kh20); + const double T = static_cast(p->T); + const double io2 = static_cast(p->io2); + const double V = static_cast(p->V); + const double k_sto20 = static_cast(p->k_sto20); + const double kx = static_cast(p->kx); + const double kho2 = static_cast(p->kho2); + const double khss = static_cast(p->khss); + const double khn03 = static_cast(p->khn03); + const double etahno3 = static_cast(p->etahno3); + const double khnh4 = static_cast(p->khnh4); + const double khalk = static_cast(p->khalk); + const double khsto = static_cast(p->khsto); + const double muh2o = static_cast(p->muh2o); + const double etahend = static_cast(p->etahend); + const double bh20 = static_cast(p->bh20); + const double muAUT20 = static_cast(p->muAUT20); + const double kno2 = static_cast(p->kno2); + const double knnh4 = static_cast(p->knnh4); + const double knalk = static_cast(p->knalk); + const double baut20 = static_cast(p->baut20); + const double etanend = static_cast(p->etanend); + + // derived parameters + const double ft04 = exp(-0.04 * (20.0 - static_cast(T))); + const double ft07 = exp(-0.06952 * (20.0 - static_cast(T))); + const double ft105 = exp(-0.105 * (20.0 - static_cast(T))); + const double k_sto = static_cast(k_sto20) * ft07; + const double muH = static_cast(muh2o) * ft07; + const double bH = static_cast(bh20) * ft07; + const double muAUT = static_cast(muAUT20) * ft105; + const double bAUT = static_cast(baut20) * ft105; + + double SO = y[_idxSO]; + double SS = y[_idxSS]; + double SS_ad = 0.0; + double SNH = y[_idxSNH]; + double SNO = y[_idxSNO]; + //double SN2 = y[_idxSN2]; + double SALK = y[_idxSALK]; + //double SI = y[_idxSI]; + //double SI_ad = 0.0; + double XS = y[_idxXS]; + double XH = y[_idxXH]; + double XSTO = y[_idxXSTO]; + double XA = y[_idxXA]; + + if (_givenAd) + { + SS_ad = y[_idxSS_ad]; + //SI_ad = y[_idxSI_ad]; + } + + // initialize jacobian + double d[15][15] = {}; + + // p1: Hydrolysis: kh20 * ft04 * XS/XH_S / (XS/XH_S + kx) * XH; + d[0][_idxXS] = kh20 * ft04 + * XH / ((XS + XH * kx) + * (XS + XH * kx)) * XH; + d[0][_idxXH] = kh20 * ft04 + * (XS * XS) / ((XS + kx * XH) + * (XS + kx * XH)); + if (XH < XH_MIN_THRESHOLD) + { + d[0][_idxXS] = kh20 * ft04 + * 0.1 / ((XS + 0.1 * kx) * (XS + 0.1 * kx)) * XH; + d[0][_idxXH] = 0.0; + } + + // p2: Aerobic storage of SS: k_sto * SO / (SO + kho2) * ( SS ) / ( ( SS_ad + SS ) + khss ) * XH; + d[1][_idxSO] = k_sto + * (SS_ad + SS) / ((SS_ad + SS) + khss) + * kho2 / ((SO + kho2) * (SO + kho2)) * XH; + d[1][_idxSS_ad] = k_sto + * SO / (SO + kho2) + * -SS / ((SS_ad + SS + khss) * (SS_ad + SS + khss)) * XH; + d[1][_idxSS] = k_sto + * SO / (SO + kho2) + * (khss + SS_ad) / ((SS_ad + SS + khss) * (SS_ad + SS + khss)) * XH; + d[1][_idxXH] = k_sto + * SO / (SO + kho2) + * (SS_ad + SS) / ((SS_ad + SS) + khss); + + // p3: Anoxic storage of SS: k_sto * etahno3 * kho2 / (SO + kho2) * ( SS ) / ( ( SS_ad + SS ) + khss ) * SNO / (SNO + khn03) * XH; + d[2][_idxSO] = k_sto * etahno3 + * -kho2 / ((SO + kho2) * (SO + kho2)) + * (SS_ad + SS) / (SS_ad + SS + khss) + * SNO / (SNO + khn03) * XH; + d[2][_idxSS_ad] = k_sto * etahno3 + * kho2 / (SO + kho2) + * -SS / ((SS_ad + SS + khss) * (SS_ad + SS + khss)) + * SNO / (SNO + khn03) * XH; + d[2][_idxSS] = k_sto * etahno3 + * kho2 / (SO + kho2) + * (khss + SS_ad) / ((SS_ad + SS + khss) * (SS_ad + SS + khss)) + * SNO / (SNO + khn03) * XH; + d[2][_idxSNO] = k_sto * etahno3 + * kho2 / (SO + kho2) + * (SS_ad + SS) / ((SS_ad + SS) + khss) + * khn03 / ((SNO + khn03) * (SNO + khn03)) * XH; + d[2][_idxXH] = k_sto * etahno3 + * kho2 / (SO + kho2) + * (SS_ad + SS) / ((SS_ad + SS) + khss) + * SNO / (SNO + khn03); + + // p4: Aerobic growth: muH * SO / (SO + kho2) * SNH / (SNH + khnh4) * SALK / (SALK + khalk) * (XSTO/XH_S) / ((XSTO/XH_S) + khsto) * XH; + d[3][_idxSO] = muH + * kho2 / ((SO + kho2) * (SO + kho2)) + * SNH / (SNH + khnh4) + * SALK / (SALK + khalk) + * (XSTO / XH) / ((XSTO / XH) + khsto) * XH; + d[3][_idxSNH] = muH + * SO / (SO + kho2) + * khnh4 / ((SNH + khnh4) * (SNH + khnh4)) + * SALK / (SALK + khalk) + * (XSTO / XH) / ((XSTO / XH) + khsto) * XH; + d[3][_idxSALK] = muH + * SO / (SO + kho2) + * SNH / (SNH + khnh4) + * khalk / ((SALK + khalk) * (SALK + khalk)) + * (XSTO / XH) / ((XSTO / XH) + khsto) * XH; + d[3][_idxXSTO] = muH + * SO / (SO + kho2) + * SNH / (SNH + khnh4) + * SALK / (SALK + khalk) + * (khsto * XH) / ((XSTO + khsto * XH) * (XSTO + khsto * XH)) * XH; + d[3][_idxXH] = muH + * SO / (SO + kho2) + * SNH / (SNH + khnh4) + * SALK / (SALK + khalk) + * (XSTO * XSTO) / ((XSTO + khsto * XH) * (XSTO + khsto * XH)); + + if (XH < XH_MIN_THRESHOLD) + { + d[3][_idxSO] = muH + * -kho2 / ((SO + kho2) * (SO + kho2)) + * SNH / (SNH + khnh4) + * SALK / (SALK + khalk) + * (XSTO / 0.1) / ((XSTO / 0.1) + khsto) * 0.1; + d[3][_idxSNH] = muH + * SO / (SO + kho2) + * khnh4 / ((SNH + khnh4) * (SNH + khnh4)) + * khnh4 / ((SNH + khnh4) * (SNH + khnh4)) + * SALK / (SALK + khalk) + * (XSTO / 0.1) / ((XSTO / 0.1) + khsto) * 0.1; + d[3][_idxSALK] = muH + * SO / (SO + kho2) + * SNH / (SNH + khnh4) + * khalk / ((SALK + khalk) * (SALK + khalk)) + * (XSTO / 0.1) / ((XSTO / 0.1) + khsto) * 0.1; + d[3][_idxXSTO] = muH + * SO / (SO + kho2) + * SNH / (SNH + khnh4) + * SALK / (SALK + khalk) + * (khsto * 0.1) / ((XSTO + khsto * 0.1) * (XSTO + khsto * 0.1)) * 0.1; + d[3][_idxXH] = 0.0; + } + + // p5: Anoxic growth: muH * etahno3 * kho2 / (kho2 + SO) * SNH / (khnh4 + SNH) * SALK / (khalk + SALK) * SNO / (khn03 + SNO)* (XSTO / XH_S) / (khsto + (XSTO/XH)_S) * XH; + d[4][_idxSO] = muH * etahno3 + * -kho2 / ((kho2 + SO) * (kho2 + SO)) + * SNH / (khnh4 + SNH) + * SALK / (khalk + SALK) + * (XSTO / XH) / (khsto + (XSTO / XH)) + * SNO / (khn03 + SNO) * XH; + d[4][_idxSNH] = muH * etahno3 + * kho2 / (kho2 + SO) + * khnh4 / ((khnh4 + SNH) * (khnh4 + SNH)) + * SALK / (khalk + SALK) + * (XSTO / XH) / (khsto + (XSTO / XH)) + * SNO / (khn03 + SNO) * XH; + d[4][_idxSALK] = muH * etahno3 + * kho2 / (kho2 + SO) + * SNH / (khnh4 + SNH) + * khalk / ((khalk + SALK) * (khalk + SALK)) + * (XSTO / XH) / (khsto + (XSTO / XH)) + * SNO / (khn03 + SNO) * XH; + d[4][_idxSNO] = muH * etahno3 + * kho2 / (kho2 + SO) + * SNH / (khnh4 + SNH) + * SALK / (khalk + SALK) + * (XSTO / XH) / (khsto + (XSTO / XH)) + * khn03 / ((khn03 + SNO) * (khn03 + SNO)) * XH; + d[4][_idxXSTO] = muH * etahno3 + * kho2 / (kho2 + SO) + * SNH / (khnh4 + SNH) + * SALK / (khalk + SALK) + * (1.0 / XH) + * SNO / (khn03 + SNO) + * (khsto * XH * XH) / ((XSTO + khsto * XH) * (XSTO + khsto * XH)) * XH; + d[4][_idxXH] = muH * etahno3 + * kho2 / (kho2 + SO) + * SNH / (khnh4 + SNH) + * SALK / (khalk + SALK) + * SNO / (khn03 + SNO) + * (XSTO * XSTO) / ((XSTO + khsto * XH) * (XSTO + khsto * XH)); + + + if (XH < XH_MIN_THRESHOLD) + { + d[4][_idxSO] = muH * etahno3 + * -kho2 / ((kho2 + SO) * (kho2 + SO)) + * SNH / (khnh4 + SNH) + * SALK / (khalk + SALK) + * (XSTO / 0.1) / (khsto + (XSTO / 0.1)) + * SNO / (khn03 + SNO) * 0.1; + d[4][_idxSNH] = muH * etahno3 + * kho2 / (kho2 + SO) + * khnh4 / ((khnh4 + SNH) * (khnh4 + SNH)) + * SALK / (khalk + SALK) + * (XSTO / 0.1) / (khsto + (XSTO / 0.1)) + * SNO / (khn03 + SNO) * 0.1; + d[4][_idxSALK] = muH * etahno3 + * kho2 / (kho2 + SO) + * SNH / (khnh4 + SNH) + * khalk / ((khalk + SALK) * (khalk + SALK)) + * (XSTO / 0.1) / (khsto + (XSTO / 0.1)) + * SNO / (khn03 + SNO) * 0.1; + d[4][_idxSNO] = muH * etahno3 + * kho2 / (kho2 + SO) + * SNH / (khnh4 + SNH) + * SALK / (khalk + SALK) + * (XSTO / 0.1) / (khsto + (XSTO / 0.1)) + * khn03 / ((khn03 + SNO) * (khn03 + SNO)) * 0.1; + d[4][_idxXSTO] = muH * etahno3 + * kho2 / (kho2 + SO) + * SNH / (khnh4 + SNH) + * SALK / (khalk + SALK) + * (1.0 / 0.1) + * SNO / (khn03 + SNO) + * (khsto * 0.1 * 0.1) / ((XSTO + khsto * 0.1) * (XSTO + khsto * 0.1)) * 0.1; + d[4][_idxXH] = 0.0; + } + + //reaction6: bH * SO / (SO + kho2) * XH; + d[5][_idxSO] = bH + * kho2 / ((SO + kho2) * (SO + kho2)) * XH; + d[5][_idxXH] = bH * SO / (SO + kho2); + + //reaction7: bH * etahend * kho2 / (SO + kho2) * SNO / (SNO + khn03) * XH; + d[6][_idxSO] = bH * etahend + * -kho2 / ((SO + kho2) * (SO + kho2)) + * SNO / (SNO + khn03) * XH; + d[6][_idxSNO] = bH * etahend + * kho2 / (SO + kho2) + * khn03 / ((SNO + khn03) * (SNO + khn03)) * XH; + d[6][_idxXH] = bH * etahend + * kho2 / (SO + kho2) + * SNO / (SNO + khn03); + + //reaction8: bH * SO / (SO + kho2) * XSTO; + d[7][_idxSO] = bH + * kho2 / ((SO + kho2) * (SO + kho2)) * XSTO; + d[7][_idxXSTO] = bH * SO / (SO + kho2); + + //reaction9: bH * etahend * kho2 / (SO + kho2) * SNO / (SNO + khn03) * XSTO; + d[8][_idxSO] = bH * etahend + * -kho2 / ((SO + kho2) * (SO + kho2)) + * SNO / (SNO + khn03) * XSTO; + d[8][_idxSNO] = bH * etahend + * kho2 / (SO + kho2) + * khn03 / ((SNO + khn03) * (SNO + khn03)) * XSTO; + d[8][_idxXSTO] = bH * etahend + * kho2 / (SO + kho2) + * SNO / (SNO + khn03); + + //reaction10: muAUT * SO / (SO + kno2) * SNH / (SNH + knnh4) * SALK / (SALK + knalk) * XA; + d[9][_idxSO] = muAUT + * kno2 / ((SO + kno2) * (SO + kno2)) + * SNH / (SNH + knnh4) + * SALK / (SALK + knalk) * XA; + d[9][_idxSALK] = muAUT + * SO / (SO + kno2) + * SNH / (SNH + knnh4) + * knalk / ((SALK + knalk) * (SALK + knalk)) * XA; + d[9][_idxSNH] = muAUT + * SO / (SO + kno2) + * SALK / (SALK + knalk) + * knnh4 / ((SNH + knnh4) * (SNH + knnh4)) * XA; + d[9][_idxXA] = muAUT + * SO / (SO + kno2) + * SNH / (SNH + knnh4) + * SALK / (SALK + knalk); + + //reaction11: bAUT * SO / (SO + kho2) * XA; + d[10][_idxSO] = bAUT + * kho2 / ((SO + kho2) * (SO + kho2)) * XA; + d[10][_idxXA] = bAUT + * SO / (SO + kho2); + + //reaction12: bAUT * etanend * SNO / (SNO + khn03) * kho2 / (SO + kho2) * XA; + d[11][_idxSO] = bAUT * etanend + * SNO / (SNO + khn03) + * -kho2 / ((SO + kho2) * (SO + kho2)) * XA; + d[11][_idxSNO] = bAUT * etanend + * kho2 / (SO + kho2) + * khn03 / ((SNO + khn03) * (SNO + khn03)) * XA; + d[11][_idxXA] = bAUT * etanend + * SNO / (SNO + khn03) + * kho2 / (SO + kho2); + + //reaction13: Aeration: io2 / V; + // no jacobian terms + + // p14: Aerobic storage of SS_ad: k_sto * SO / (SO + kho2) * ( SS_ad ) / ( ( SS_ad + SS ) + khss ) * XH; + d[13][_idxSO] = k_sto + * (SS_ad) / ((SS_ad + SS) + khss) + * kho2 / ((SO + kho2) * (SO + kho2)) * XH; + d[13][_idxSS_ad] = k_sto + * SO / (SO + kho2) + * (khss * SS) / ((SS_ad + SS + khss) * (SS_ad + SS + khss)) * XH; + d[13][_idxSS] = k_sto + * SO / (SO + kho2) + * (-SS_ad) / ((SS_ad + SS + khss) * (SS_ad + SS + khss)) * XH; + d[13][_idxXH] = k_sto + * SO / (SO + kho2) + * (SS_ad) / ((SS_ad + SS) + khss); + + // p15: Anoxic storage of SS_ad: k_sto * etahno3 * kho2 / (SO + kho2) * ( SS_ad ) / ( ( SS_ad + SS ) + khss ) * SNO / (SNO + khn03) * XH; + d[14][_idxSO] = k_sto * etahno3 + * -kho2 / ((SO + kho2) * (SO + kho2)) + * (SS_ad) / (SS_ad + SS + khss) + * SNO / (SNO + khn03) * XH; + d[14][_idxSS_ad] = k_sto * etahno3 + * kho2 / (SO + kho2) + * (khss * SS) / ((SS_ad + SS + khss) * (SS_ad + SS + khss)) + * SNO / (SNO + khn03) * XH; + d[14][_idxSS] = k_sto * etahno3 + * kho2 / (SO + kho2) + * (-SS_ad) / ((SS_ad + SS + khss) * (SS_ad + SS + khss)) + * SNO / (SNO + khn03) * XH; + d[14][_idxSNO] = k_sto * etahno3 + * kho2 / (SO + kho2) + * (SS_ad) / ((SS_ad + SS) + khss) + * khn03 / ((SNO + khn03) * (SNO + khn03)) * XH; + d[14][_idxXH] = k_sto * etahno3 + * kho2 / (SO + kho2) + * (SS_ad) / ((SS_ad + SS) + khss) + * SNO / (SNO + khn03); + + + RowIterator curJac = jac; + for (size_t rIdx = 0; rIdx < _stoichiometry.columns(); rIdx++) + { + RowIterator curJac = jac; + for (int row = 0; row < _stoichiometry.rows(); ++row, ++curJac) + { + double colFactor = static_cast(_stoichiometry.native(row, rIdx)); + for (size_t compIdx = 0; compIdx < _stoichiometry.rows(); compIdx++) + { + curJac[compIdx - static_cast(row)] += colFactor * d[rIdx][compIdx]; + } + } + } + + } + + template + void jacobianCombinedImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, double factor, const RowIteratorLiquid& jacLiquid, const RowIteratorSolid& jacSolid, LinearBufferAllocator workSpace) const + { + } +}; + +typedef ActivatedSludgeModelThreeBase ActivatedSludgeModelThreeReaction; +typedef ActivatedSludgeModelThreeBase ExternalActivatedSludgeModelThreeReaction; + +namespace reaction +{ + void registerActivatedSludgeModelThreeReaction(std::unordered_map>& reactions) + { + reactions[ActivatedSludgeModelThreeReaction::identifier()] = []() { return new ActivatedSludgeModelThreeReaction(); }; + reactions[ExternalActivatedSludgeModelThreeReaction::identifier()] = []() { return new ExternalActivatedSludgeModelThreeReaction(); }; + } +} // namespace reaction + +} // namespace model + +} // namespace cadet diff --git a/test/MatrixHelper.hpp b/test/MatrixHelper.hpp index edb6c15b4..9dacdeea3 100644 --- a/test/MatrixHelper.hpp +++ b/test/MatrixHelper.hpp @@ -56,9 +56,9 @@ Matrix_t createBandMatrix(unsigned int rows, unsigned int lower, unsigned int up double val = 1.0; for (int row = 0; row < bm.rows(); ++row) { - const int lower = std::max(-static_cast(bm.lowerBandwidth()), -static_cast(row)); - const int upper = std::min(static_cast(bm.upperBandwidth()), static_cast(bm.rows() - row) - 1); - for (int col = lower; col <= upper; ++col) + const int low = std::max(-static_cast(bm.lowerBandwidth()), -static_cast(row)); + const int up = std::min(static_cast(bm.upperBandwidth()), static_cast(bm.rows() - row) - 1); + for (int col = low; col <= up; ++col) { bm.centered(row, col) = val; val += 1.0; diff --git a/test/ReactionModelTests.cpp b/test/ReactionModelTests.cpp index 812262c58..3f5c9f746 100644 --- a/test/ReactionModelTests.cpp +++ b/test/ReactionModelTests.cpp @@ -375,7 +375,7 @@ namespace reaction ad::prepareAdVectorSeedsForDenseMatrix(adY, 0, numDofs); ad::copyToAd(yState.data(), adY, numDofs); ad::resetAd(adRes, numDofs); - crm.model().residualCombinedAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, adY, adY + crm.nComp(), adRes, adRes + crm.nComp(), 1.0, crm.buffer()); + crm.model().residualCombinedAdd(1.0, 0u, ColumnPosition{ 0.0, 0.0, 0.0 }, adY, adY + crm.nComp(), adRes, adRes + crm.nComp(), 1.0, crm.buffer()); // Extract Jacobian cadet::linalg::DenseMatrix jacAD; @@ -385,30 +385,30 @@ namespace reaction // Calculate analytic Jacobian cadet::linalg::DenseMatrix jacAna; jacAna.resize(numDofs, numDofs); - crm.model().analyticJacobianCombinedAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yState.data(), yState.data() + crm.nComp(), 1.0, jacAna.row(0), jacAna.row(crm.nComp()), crm.buffer()); + crm.model().analyticJacobianCombinedAdd(1.0, 0u, ColumnPosition{ 0.0, 0.0, 0.0 }, yState.data(), yState.data() + crm.nComp(), 1.0, jacAna.row(0), jacAna.row(crm.nComp()), crm.buffer()); cadet::test::checkJacobianPatternFD( [&](double const* lDir, double* res) -> void - { - std::fill_n(res, numDofs, 0.0); - crm.model().residualCombinedAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, lDir, lDir + crm.nComp(), res, res + crm.nComp(), 1.0, crm.buffer()); - }, - [&](double const* lDir, double* res) -> void - { - jacAna.multiplyVector(lDir, res); - }, + { + std::fill_n(res, numDofs, 0.0); + crm.model().residualCombinedAdd(1.0, 0u, ColumnPosition{ 0.0, 0.0, 0.0 }, lDir, lDir + crm.nComp(), res, res + crm.nComp(), 1.0, crm.buffer()); + }, + [&](double const* lDir, double* res) -> void + { + jacAna.multiplyVector(lDir, res); + }, yState.data(), dir.data(), colA.data(), colB.data(), numDofs, numDofs); cadet::test::checkJacobianPatternFD( [&](double const* lDir, double* res) -> void - { - std::fill_n(res, numDofs, 0.0); - crm.model().residualCombinedAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, lDir, lDir + crm.nComp(), res, res + crm.nComp(), 1.0, crm.buffer()); - }, - [&](double const* lDir, double* res) -> void - { - jacAD.multiplyVector(lDir, res); - }, + { + std::fill_n(res, numDofs, 0.0); + crm.model().residualCombinedAdd(1.0, 0u, ColumnPosition{ 0.0, 0.0, 0.0 }, lDir, lDir + crm.nComp(), res, res + crm.nComp(), 1.0, crm.buffer()); + }, + [&](double const* lDir, double* res) -> void + { + jacAD.multiplyVector(lDir, res); + }, yState.data(), dir.data(), colA.data(), colB.data(), numDofs, numDofs); // Check Jacobians against each other @@ -499,6 +499,116 @@ namespace reaction testTimeDerivativeJacobianDynamicReactionsFD(jpp, bulk, particle, particleModifiers, h, absTol, relTol); } + void testLiquidReactionJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, double const* point, double absTol, double relTol) + { + ConfiguredDynamicReactionModel crm = ConfiguredDynamicReactionModel::create(modelName, nComp, nBound, config); + + const unsigned int numDofs = crm.nComp(); + std::vector yState(numDofs, 0.0); + std::copy_n(point, numDofs, yState.data()); + + std::vector dir(numDofs, 0.0); + std::vector colA(numDofs, 0.0); + std::vector colB(numDofs, 0.0); + + // Enable AD + cadet::ad::setDirections(cadet::ad::getMaxDirections()); + cadet::active* adRes = new cadet::active[numDofs]; + cadet::active* adY = new cadet::active[numDofs]; + + // Liquid phase only + + // Evaluate with AD + ad::prepareAdVectorSeedsForDenseMatrix(adY, 0, numDofs); + ad::copyToAd(yState.data(), adY, numDofs); + ad::resetAd(adRes, numDofs); + crm.model().residualFluxAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, crm.nComp(), adY, adRes, 1.0, crm.buffer()); + + // Extract Jacobian + cadet::linalg::DenseMatrix jacAD; + jacAD.resize(numDofs, numDofs); + ad::extractDenseJacobianFromAd(adRes, 0, jacAD); + + // Calculate analytic Jacobian + cadet::linalg::DenseMatrix jacAna; + jacAna.resize(numDofs, numDofs); + crm.model().analyticJacobianAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, crm.nComp(), yState.data(), 1.0, jacAna.row(0), crm.buffer()); + + delete[] adY; + delete[] adRes; + + cadet::test::checkJacobianPatternFD( + [&](double const* lDir, double* res) -> void + { + std::fill_n(res, numDofs, 0.0); + crm.model().residualFluxAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, crm.nComp(), lDir, res, 1.0, crm.buffer()); + }, + [&](double const* lDir, double* res) -> void + { + jacAna.multiplyVector(lDir, res); + }, + yState.data(), dir.data(), colA.data(), colB.data(), numDofs, numDofs); + + cadet::test::checkJacobianPatternFD( + [&](double const* lDir, double* res) -> void + { + std::fill_n(res, numDofs, 0.0); + crm.model().residualFluxAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, crm.nComp(), lDir, res, 1.0, crm.buffer()); + }, + [&](double const* lDir, double* res) -> void + { + jacAD.multiplyVector(lDir, res); + }, + yState.data(), dir.data(), colA.data(), colB.data(), numDofs, numDofs); + + // Check Jacobians against each other + for (unsigned int row = 0; row < numDofs; ++row) + { + for (unsigned int col = 0; col < numDofs; ++col) + { + CAPTURE(row); + CAPTURE(col); + CHECK(jacAna.native(row, col) == makeApprox(jacAD.native(row, col), absTol, relTol)); + } + } + } + + void testCompareTwoSimulationReaction(const std::string configFilePath1, const std::string configFilePath2, const double absTol, const double relTol, const int compIdx1, const int compIdx2) + { + // read json model setup file + const std::string setupFile1 = std::string(getTestDirectory()) + configFilePath1; + const std::string setupFile2 = std::string(getTestDirectory()) + configFilePath2; + JsonParameterProvider pp_setup_1(JsonParameterProvider::fromFile(setupFile1)); + JsonParameterProvider pp_setup_2(JsonParameterProvider::fromFile(setupFile2)); + + nlohmann::json* setupJson1 = pp_setup_1.data(); + nlohmann::json* setupJson2 = pp_setup_2.data(); + + // 1. simulation + cadet::Driver drv1; + drv1.configure(pp_setup_1); + drv1.run(); + + // 2. simulation + cadet::Driver drv2; + drv2.configure(pp_setup_2); + drv2.run(); + + cadet::InternalStorageUnitOpRecorder const* const Data1 = drv1.solution()->unitOperation(0); + cadet::InternalStorageUnitOpRecorder const* const Data2 = drv2.solution()->unitOperation(0); + + double const* outlet1 = Data1->outlet(); + double const* outlet2 = Data2->outlet(); + + const unsigned int nComp1 = Data1->numComponents(); + const unsigned int nComp2 = Data2->numComponents(); + for (unsigned int i = 0; i < Data2->numDataPoints(); ++i, outlet1 += nComp1, outlet2 += nComp2) + { + CAPTURE(i); + CHECK((outlet1[compIdx1]) == cadet::test::makeApprox(outlet2[compIdx2], relTol, absTol)); + } + } + } // namespace reaction } // namespace test } // namespace cadet diff --git a/test/ReactionModelTests.hpp b/test/ReactionModelTests.hpp index 0140370e1..70a3181e9 100644 --- a/test/ReactionModelTests.hpp +++ b/test/ReactionModelTests.hpp @@ -124,6 +124,18 @@ namespace reaction */ void testMichaelisMentenToSMAInhibitionMicroKinetic(const std::string configFilePathMM, const std::string configFilePathSMA, const double absTol, const double relTol); + /** + * @brief Checks the analytic Jacobians of the dynamic reaction model against AD + * @param [in] modelName Name of the reaction model + * @param [in] nComp Number of components + * @param [in] nBound Array with number of bound states for each component + * @param [in] config JSON string with reaction model parameters + * @param [in] point Liquid phase and solid phase values to check Jacobian at + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ + void testDynamicJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, double const* point, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0); + /** * @brief Checks the analytic Jacobians of the dynamic reaction model against AD * @param [in] modelName Name of the reaction model @@ -134,7 +146,7 @@ namespace reaction * @param [in] absTol Absolute error tolerance * @param [in] relTol Relative error tolerance */ - void testDynamicJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, double const* point, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0); + void testLiquidReactionJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, double const* point, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0); /** * @brief Extends a model with dynamic reactions in each phase and particle type @@ -193,6 +205,17 @@ namespace reaction */ void testTimeDerivativeJacobianDynamicReactionsFD(const std::string& uoType, const std::string& spatialMethod, bool bulk, bool particle, bool particleModifiers, double h, double absTol, double relTol); + /** + * @brief Compares two simulations (1 and 2) wrt specific components + * @param [in] configFilePathMM relative file path to 1. configuration file + * @param [in] configFilePathSMA relative file path to 2. configuration file + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + * @param [in] comIdx compare index component + */ + void testCompareTwoSimulationReaction(const std::string configFilePath1, const std::string configFilePath2, const double absTol, const double relTol, const int compIdx1, const int compIdx2); + + } // namespace reaction } // namespace test } // namespace cadet diff --git a/test/ReactionModels.cpp b/test/ReactionModels.cpp index 3f34af20c..6f57a18b6 100644 --- a/test/ReactionModels.cpp +++ b/test/ReactionModels.cpp @@ -178,6 +178,57 @@ TEST_CASE("MichaelisMenten kinetic analytic Jacobian vs AD with inhibition", "[M point, 1e-15, 1e-15 ); } +TEST_CASE("ASM3 analytic Jacobian vs AD", "[ASM3],[ReactionModel],[Jacobian],[AD],[CI]") +{ + const unsigned int nBound[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0, 0, 0 }; + unsigned int ncomp = 13; + const double point[] = { 1.0, 2.0, 1.4, 2.1, 0.2, 1.1, 1.8, 1.5, 1.0, 4.2, 1.4, 0.3, 1.4 }; + cadet::test::reaction::testLiquidReactionJacobianAD("ACTIVATED_SLUDGE_MODEL3", ncomp, nBound, + R"json({ + "ASM3_FISS_BM_PROD": 1.0, + "ASM3_FSI": 0.0, + "ASM3_YH_AER": 0.8, + "ASM3_YH_ANOX": 0.65, + "ASM3_YSTO_AER": 0.8375, + "ASM3_YSTO_ANOX": 0.7, + "ASM3_FXI": 0.2, + "ASM3_YA": 0.24, + "ASM3_KH20": 9.0, + "ASM3_KX": 1.0, + "ASM3_KSTO20": 12.0, + "ASM3_MU_H20": 3.0, + "ASM3_BH20": 0.33, + "ASM3_ETA_HNO3": 0.5, + "ASM3_KHO2": 0.2, + "ASM3_KHSS": 10.0, + "ASM3_KHNO3": 0.5, + "ASM3_KHNH4": 0.01, + "ASM3_KHALK": 0.1, + "ASM3_KHSTO": 0.1, + "ASM3_MU_AUT20": 1.12, + "ASM3_BAUT20": 0.18, + "ASM3_ETAH_END": 0.5, + "ASM3_ETAN_END": 0.5, + "ASM3_KNO2": 0.5, + "ASM3_KNNH4": 0.7, + "ASM3_KNALK": 0.5, + "ASM3_T": 12.0, + "ASM3_V": 1000.0, + "ASM3_IO2": 0.0, + "ASM3_INSI": 0.01, + "ASM3_INSS": 0.03, + "ASM3_INXI": 0.04, + "ASM3_INXS": 0.03, + "ASM3_INBM": 0.07, + "ASM3_IVSS_XI": 0.751879699, + "ASM3_IVSS_XS": 0.555555556, + "ASM3_IVSS_STO": 0.6, + "ASM3_IVSS_BM": 0.704225352, + "ASM3_ITSS_VSS_BM": 1.086956522 + })json", + point, 1e-12, 1e-12 + ); +} TEST_CASE("MassActionLaw old interface vs. two separate reactions", "[MassActionLaw],[ReactionModel],[Simulation],[CI]") { @@ -200,3 +251,14 @@ TEST_CASE("MassActionLaw one reaction vs. two separate reactions", "[MassActionL cadet::test::column::DummyParams disc; cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, true); } + +TEST_CASE("ASM3 simulation with adsorption component vs ASM3 without adsorption component", "[CSTR],[ASM3],[ReactionModel],[Simulation],[Reference],[CI]") +{ + const std::string& configFilePath1 = std::string("/data/configuration_CSTR_ASM3_without_ad.json"); + const std::string& configFilePath2 = std::string("/data/configuration_CSTR_ASM3_with_ad.json"); + + const double absTol = 1e-3; + const double relTol = 5e-4; + + cadet::test::reaction::testCompareTwoSimulationReaction(configFilePath1, configFilePath2, absTol, relTol,1, 14); +} diff --git a/test/data/configuration_CSTR_ASM3_with_ad.json b/test/data/configuration_CSTR_ASM3_with_ad.json new file mode 100644 index 000000000..c1d8a3f55 --- /dev/null +++ b/test/data/configuration_CSTR_ASM3_with_ad.json @@ -0,0 +1,1121 @@ +{ + "model": { + "NUNITS": 1, + "connections": { + "NSWITCHES": 1, + "switch_000": { + "CONNECTIONS": [], + "SECTION": 0 + } + }, + "solver": { + "GS_TYPE": 1, + "MAX_KRYLOV": 0, + "MAX_RESTARTS": 10, + "SCHUR_SAFETY": 1e-08 + }, + "unit_000": { + "INIT_C": [ + 1.0, + 0.0, + 3.0, + 4.0, + 5.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.0, + 11.0, + 12.0, + 13.0, + 0.0, + 2.0 + ], + "INIT_LIQUID_VOLUME": 10, + "NCOMP": 15, + "NREAC_LIQUID": 1, + "UNIT_TYPE": "CSTR", + "liquid_reaction_000": { + "TYPE": "ACTIVATED_SLUDGE_MODEL3", + "ASM3_FISS_BM_PROD": 1.0, + "ASM3_FSI": 0.0, + "ASM3_YH_AER": 0.8, + "ASM3_YH_ANOX": 0.65, + "ASM3_YSTO_AER": 0.8375, + "ASM3_YSTO_ANOX": 0.7, + "ASM3_FXI": 0.2, + "ASM3_YA": 0.24, + "ASM3_KH20": 9.0, + "ASM3_KX": 1.0, + "ASM3_KSTO20": 12.0, + "ASM3_MU_H20": 3.0, + "ASM3_BH20": 0.33, + "ASM3_ETA_HNO3": 0.5, + "ASM3_KHO2": 0.2, + "ASM3_KHSS": 10.0, + "ASM3_KHNO3": 0.5, + "ASM3_KHNH4": 0.01, + "ASM3_KHALK": 0.1, + "ASM3_KHSTO": 0.1, + "ASM3_MU_AUT20": 1.12, + "ASM3_BAUT20": 0.18, + "ASM3_ETAH_END": 0.5, + "ASM3_ETAN_END": 0.5, + "ASM3_KNO2": 0.5, + "ASM3_KNNH4": 0.7, + "ASM3_KNALK": 0.5, + "ASM3_T": 12.0, + "ASM3_V": 10.0, + "ASM3_IO2": 0.0, + "ASM3_INSI": 0.01, + "ASM3_INSS": 0.03, + "ASM3_INXI": 0.04, + "ASM3_INXS": 0.03, + "ASM3_INBM": 0.07, + "ASM3_IVSS_XI": 0.751879699, + "ASM3_IVSS_XS": 0.555555556, + "ASM3_IVSS_STO": 0.6, + "ASM3_IVSS_BM": 0.704225352, + "ASM3_ITSS_VSS_BM": 1.086956522, + "ASM3_FSS_AD": 1.0, + "ASM3_FSI_AD": 0.0 + } + } + }, + "return": { + "SPLIT_COMPONENTS_DATA": false, + "SPLIT_PORTS_DATA": 0, + "unit_000": { + "WRITE_COORDINATES": 1, + "WRITE_SENS_OUTLET": 1, + "WRITE_SOLUTION_BULK": 1, + "WRITE_SOLUTION_FLUX": 1, + "WRITE_SOLUTION_INLET": 1, + "WRITE_SOLUTION_OUTLET": 1, + "WRITE_SOLUTION_PARTICLE": 1, + "WRITE_SOLUTION_SOLID": 1, + "WRITE_SOLUTION_VOLUME": 1 + } + }, + "solver": { + "NTHREADS": 1, + "USER_SOLUTION_TIMES": [ + 0.0, + 0.05, + 0.1, + 0.15000000000000002, + 0.2, + 0.25, + 0.30000000000000004, + 0.35000000000000003, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6000000000000001, + 0.65, + 0.7000000000000001, + 0.75, + 0.8, + 0.8500000000000001, + 0.9, + 0.9500000000000001, + 1.0, + 1.05, + 1.1, + 1.1500000000000001, + 1.2000000000000002, + 1.25, + 1.3, + 1.35, + 1.4000000000000001, + 1.4500000000000002, + 1.5, + 1.55, + 1.6, + 1.6500000000000001, + 1.7000000000000002, + 1.75, + 1.8, + 1.85, + 1.9000000000000001, + 1.9500000000000002, + 2.0, + 2.0500000000000003, + 2.1, + 2.15, + 2.2, + 2.25, + 2.3000000000000003, + 2.35, + 2.4000000000000004, + 2.45, + 2.5, + 2.5500000000000003, + 2.6, + 2.6500000000000004, + 2.7, + 2.75, + 2.8000000000000003, + 2.85, + 2.9000000000000004, + 2.95, + 3.0, + 3.0500000000000003, + 3.1, + 3.1500000000000004, + 3.2, + 3.25, + 3.3000000000000003, + 3.35, + 3.4000000000000004, + 3.45, + 3.5, + 3.5500000000000003, + 3.6, + 3.6500000000000004, + 3.7, + 3.75, + 3.8000000000000003, + 3.85, + 3.9000000000000004, + 3.95, + 4.0, + 4.05, + 4.1000000000000005, + 4.15, + 4.2, + 4.25, + 4.3, + 4.3500000000000005, + 4.4, + 4.45, + 4.5, + 4.55, + 4.6000000000000005, + 4.65, + 4.7, + 4.75, + 4.800000000000001, + 4.8500000000000005, + 4.9, + 4.95, + 5.0, + 5.050000000000001, + 5.1000000000000005, + 5.15, + 5.2, + 5.25, + 5.300000000000001, + 5.3500000000000005, + 5.4, + 5.45, + 5.5, + 5.550000000000001, + 5.6000000000000005, + 5.65, + 5.7, + 5.75, + 5.800000000000001, + 5.8500000000000005, + 5.9, + 5.95, + 6.0, + 6.050000000000001, + 6.1000000000000005, + 6.15, + 6.2, + 6.25, + 6.300000000000001, + 6.3500000000000005, + 6.4, + 6.45, + 6.5, + 6.550000000000001, + 6.6000000000000005, + 6.65, + 6.7, + 6.75, + 6.800000000000001, + 6.8500000000000005, + 6.9, + 6.95, + 7.0, + 7.050000000000001, + 7.1000000000000005, + 7.15, + 7.2, + 7.25, + 7.300000000000001, + 7.3500000000000005, + 7.4, + 7.45, + 7.5, + 7.550000000000001, + 7.6000000000000005, + 7.65, + 7.7, + 7.75, + 7.800000000000001, + 7.8500000000000005, + 7.9, + 7.95, + 8.0, + 8.05, + 8.1, + 8.15, + 8.200000000000001, + 8.25, + 8.3, + 8.35, + 8.4, + 8.450000000000001, + 8.5, + 8.55, + 8.6, + 8.65, + 8.700000000000001, + 8.75, + 8.8, + 8.85, + 8.9, + 8.950000000000001, + 9.0, + 9.05, + 9.1, + 9.15, + 9.200000000000001, + 9.25, + 9.3, + 9.35, + 9.4, + 9.450000000000001, + 9.5, + 9.55, + 9.600000000000001, + 9.65, + 9.700000000000001, + 9.75, + 9.8, + 9.850000000000001, + 9.9, + 9.950000000000001, + 10.0, + 10.05, + 10.100000000000001, + 10.15, + 10.200000000000001, + 10.25, + 10.3, + 10.350000000000001, + 10.4, + 10.450000000000001, + 10.5, + 10.55, + 10.600000000000001, + 10.65, + 10.700000000000001, + 10.75, + 10.8, + 10.850000000000001, + 10.9, + 10.950000000000001, + 11.0, + 11.05, + 11.100000000000001, + 11.15, + 11.200000000000001, + 11.25, + 11.3, + 11.350000000000001, + 11.4, + 11.450000000000001, + 11.5, + 11.55, + 11.600000000000001, + 11.65, + 11.700000000000001, + 11.75, + 11.8, + 11.850000000000001, + 11.9, + 11.950000000000001, + 12.0, + 12.05, + 12.100000000000001, + 12.15, + 12.200000000000001, + 12.25, + 12.3, + 12.350000000000001, + 12.4, + 12.450000000000001, + 12.5, + 12.55, + 12.600000000000001, + 12.65, + 12.700000000000001, + 12.75, + 12.8, + 12.850000000000001, + 12.9, + 12.950000000000001, + 13.0, + 13.05, + 13.100000000000001, + 13.15, + 13.200000000000001, + 13.25, + 13.3, + 13.350000000000001, + 13.4, + 13.450000000000001, + 13.5, + 13.55, + 13.600000000000001, + 13.65, + 13.700000000000001, + 13.75, + 13.8, + 13.850000000000001, + 13.9, + 13.950000000000001, + 14.0, + 14.05, + 14.100000000000001, + 14.15, + 14.200000000000001, + 14.25, + 14.3, + 14.350000000000001, + 14.4, + 14.450000000000001, + 14.5, + 14.55, + 14.600000000000001, + 14.65, + 14.700000000000001, + 14.75, + 14.8, + 14.850000000000001, + 14.9, + 14.950000000000001, + 15.0, + 15.05, + 15.100000000000001, + 15.15, + 15.200000000000001, + 15.25, + 15.3, + 15.350000000000001, + 15.4, + 15.450000000000001, + 15.5, + 15.55, + 15.600000000000001, + 15.65, + 15.700000000000001, + 15.75, + 15.8, + 15.850000000000001, + 15.9, + 15.950000000000001, + 16.0, + 16.05, + 16.1, + 16.150000000000002, + 16.2, + 16.25, + 16.3, + 16.35, + 16.400000000000002, + 16.45, + 16.5, + 16.55, + 16.6, + 16.650000000000002, + 16.7, + 16.75, + 16.8, + 16.85, + 16.900000000000002, + 16.95, + 17.0, + 17.05, + 17.1, + 17.150000000000002, + 17.2, + 17.25, + 17.3, + 17.35, + 17.400000000000002, + 17.45, + 17.5, + 17.55, + 17.6, + 17.650000000000002, + 17.7, + 17.75, + 17.8, + 17.85, + 17.900000000000002, + 17.95, + 18.0, + 18.05, + 18.1, + 18.150000000000002, + 18.2, + 18.25, + 18.3, + 18.35, + 18.400000000000002, + 18.45, + 18.5, + 18.55, + 18.6, + 18.650000000000002, + 18.7, + 18.75, + 18.8, + 18.85, + 18.900000000000002, + 18.95, + 19.0, + 19.05, + 19.1, + 19.150000000000002, + 19.200000000000003, + 19.25, + 19.3, + 19.35, + 19.400000000000002, + 19.450000000000003, + 19.5, + 19.55, + 19.6, + 19.650000000000002, + 19.700000000000003, + 19.75, + 19.8, + 19.85, + 19.900000000000002, + 19.950000000000003, + 20.0, + 20.05, + 20.1, + 20.150000000000002, + 20.200000000000003, + 20.25, + 20.3, + 20.35, + 20.400000000000002, + 20.450000000000003, + 20.5, + 20.55, + 20.6, + 20.650000000000002, + 20.700000000000003, + 20.75, + 20.8, + 20.85, + 20.900000000000002, + 20.950000000000003, + 21.0, + 21.05, + 21.1, + 21.150000000000002, + 21.200000000000003, + 21.25, + 21.3, + 21.35, + 21.400000000000002, + 21.450000000000003, + 21.5, + 21.55, + 21.6, + 21.650000000000002, + 21.700000000000003, + 21.75, + 21.8, + 21.85, + 21.900000000000002, + 21.950000000000003, + 22.0, + 22.05, + 22.1, + 22.150000000000002, + 22.200000000000003, + 22.25, + 22.3, + 22.35, + 22.400000000000002, + 22.450000000000003, + 22.5, + 22.55, + 22.6, + 22.650000000000002, + 22.700000000000003, + 22.75, + 22.8, + 22.85, + 22.900000000000002, + 22.950000000000003, + 23.0, + 23.05, + 23.1, + 23.150000000000002, + 23.200000000000003, + 23.25, + 23.3, + 23.35, + 23.400000000000002, + 23.450000000000003, + 23.5, + 23.55, + 23.6, + 23.650000000000002, + 23.700000000000003, + 23.75, + 23.8, + 23.85, + 23.900000000000002, + 23.950000000000003, + 24.0, + 24.05, + 24.1, + 24.150000000000002, + 24.200000000000003, + 24.25, + 24.3, + 24.35, + 24.400000000000002, + 24.450000000000003, + 24.5, + 24.55, + 24.6, + 24.650000000000002, + 24.700000000000003, + 24.75, + 24.8, + 24.85, + 24.900000000000002, + 24.950000000000003, + 25.0, + 25.05, + 25.1, + 25.150000000000002, + 25.200000000000003, + 25.25, + 25.3, + 25.35, + 25.400000000000002, + 25.450000000000003, + 25.5, + 25.55, + 25.6, + 25.650000000000002, + 25.700000000000003, + 25.75, + 25.8, + 25.85, + 25.900000000000002, + 25.950000000000003, + 26.0, + 26.05, + 26.1, + 26.150000000000002, + 26.200000000000003, + 26.25, + 26.3, + 26.35, + 26.400000000000002, + 26.450000000000003, + 26.5, + 26.55, + 26.6, + 26.650000000000002, + 26.700000000000003, + 26.75, + 26.8, + 26.85, + 26.900000000000002, + 26.950000000000003, + 27.0, + 27.05, + 27.1, + 27.150000000000002, + 27.200000000000003, + 27.25, + 27.3, + 27.35, + 27.400000000000002, + 27.450000000000003, + 27.5, + 27.55, + 27.6, + 27.650000000000002, + 27.700000000000003, + 27.75, + 27.8, + 27.85, + 27.900000000000002, + 27.950000000000003, + 28.0, + 28.05, + 28.1, + 28.150000000000002, + 28.200000000000003, + 28.25, + 28.3, + 28.35, + 28.400000000000002, + 28.450000000000003, + 28.5, + 28.55, + 28.6, + 28.650000000000002, + 28.700000000000003, + 28.75, + 28.8, + 28.85, + 28.900000000000002, + 28.950000000000003, + 29.0, + 29.05, + 29.1, + 29.150000000000002, + 29.200000000000003, + 29.25, + 29.3, + 29.35, + 29.400000000000002, + 29.450000000000003, + 29.5, + 29.55, + 29.6, + 29.650000000000002, + 29.700000000000003, + 29.75, + 29.8, + 29.85, + 29.900000000000002, + 29.950000000000003, + 30.0, + 30.05, + 30.1, + 30.150000000000002, + 30.200000000000003, + 30.25, + 30.3, + 30.35, + 30.400000000000002, + 30.450000000000003, + 30.5, + 30.55, + 30.6, + 30.650000000000002, + 30.700000000000003, + 30.75, + 30.8, + 30.85, + 30.900000000000002, + 30.950000000000003, + 31.0, + 31.05, + 31.1, + 31.150000000000002, + 31.200000000000003, + 31.25, + 31.3, + 31.35, + 31.400000000000002, + 31.450000000000003, + 31.5, + 31.55, + 31.6, + 31.650000000000002, + 31.700000000000003, + 31.75, + 31.8, + 31.85, + 31.900000000000002, + 31.950000000000003, + 32.0, + 32.050000000000004, + 32.1, + 32.15, + 32.2, + 32.25, + 32.300000000000004, + 32.35, + 32.4, + 32.45, + 32.5, + 32.550000000000004, + 32.6, + 32.65, + 32.7, + 32.75, + 32.800000000000004, + 32.85, + 32.9, + 32.95, + 33.0, + 33.050000000000004, + 33.1, + 33.15, + 33.2, + 33.25, + 33.300000000000004, + 33.35, + 33.4, + 33.45, + 33.5, + 33.550000000000004, + 33.6, + 33.65, + 33.7, + 33.75, + 33.800000000000004, + 33.85, + 33.9, + 33.95, + 34.0, + 34.050000000000004, + 34.1, + 34.15, + 34.2, + 34.25, + 34.300000000000004, + 34.35, + 34.4, + 34.45, + 34.5, + 34.550000000000004, + 34.6, + 34.65, + 34.7, + 34.75, + 34.800000000000004, + 34.85, + 34.9, + 34.95, + 35.0, + 35.050000000000004, + 35.1, + 35.15, + 35.2, + 35.25, + 35.300000000000004, + 35.35, + 35.4, + 35.45, + 35.5, + 35.550000000000004, + 35.6, + 35.65, + 35.7, + 35.75, + 35.800000000000004, + 35.85, + 35.9, + 35.95, + 36.0, + 36.050000000000004, + 36.1, + 36.15, + 36.2, + 36.25, + 36.300000000000004, + 36.35, + 36.4, + 36.45, + 36.5, + 36.550000000000004, + 36.6, + 36.65, + 36.7, + 36.75, + 36.800000000000004, + 36.85, + 36.9, + 36.95, + 37.0, + 37.050000000000004, + 37.1, + 37.15, + 37.2, + 37.25, + 37.300000000000004, + 37.35, + 37.4, + 37.45, + 37.5, + 37.550000000000004, + 37.6, + 37.65, + 37.7, + 37.75, + 37.800000000000004, + 37.85, + 37.9, + 37.95, + 38.0, + 38.050000000000004, + 38.1, + 38.15, + 38.2, + 38.25, + 38.300000000000004, + 38.35, + 38.400000000000006, + 38.45, + 38.5, + 38.550000000000004, + 38.6, + 38.650000000000006, + 38.7, + 38.75, + 38.800000000000004, + 38.85, + 38.900000000000006, + 38.95, + 39.0, + 39.050000000000004, + 39.1, + 39.150000000000006, + 39.2, + 39.25, + 39.300000000000004, + 39.35, + 39.400000000000006, + 39.45, + 39.5, + 39.550000000000004, + 39.6, + 39.650000000000006, + 39.7, + 39.75, + 39.800000000000004, + 39.85, + 39.900000000000006, + 39.95, + 40.0, + 40.050000000000004, + 40.1, + 40.150000000000006, + 40.2, + 40.25, + 40.300000000000004, + 40.35, + 40.400000000000006, + 40.45, + 40.5, + 40.550000000000004, + 40.6, + 40.650000000000006, + 40.7, + 40.75, + 40.800000000000004, + 40.85, + 40.900000000000006, + 40.95, + 41.0, + 41.050000000000004, + 41.1, + 41.150000000000006, + 41.2, + 41.25, + 41.300000000000004, + 41.35, + 41.400000000000006, + 41.45, + 41.5, + 41.550000000000004, + 41.6, + 41.650000000000006, + 41.7, + 41.75, + 41.800000000000004, + 41.85, + 41.900000000000006, + 41.95, + 42.0, + 42.050000000000004, + 42.1, + 42.150000000000006, + 42.2, + 42.25, + 42.300000000000004, + 42.35, + 42.400000000000006, + 42.45, + 42.5, + 42.550000000000004, + 42.6, + 42.650000000000006, + 42.7, + 42.75, + 42.800000000000004, + 42.85, + 42.900000000000006, + 42.95, + 43.0, + 43.050000000000004, + 43.1, + 43.150000000000006, + 43.2, + 43.25, + 43.300000000000004, + 43.35, + 43.400000000000006, + 43.45, + 43.5, + 43.550000000000004, + 43.6, + 43.650000000000006, + 43.7, + 43.75, + 43.800000000000004, + 43.85, + 43.900000000000006, + 43.95, + 44.0, + 44.050000000000004, + 44.1, + 44.150000000000006, + 44.2, + 44.25, + 44.300000000000004, + 44.35, + 44.400000000000006, + 44.45, + 44.5, + 44.550000000000004, + 44.6, + 44.650000000000006, + 44.7, + 44.75, + 44.800000000000004, + 44.85, + 44.900000000000006, + 44.95, + 45.0, + 45.050000000000004, + 45.1, + 45.150000000000006, + 45.2, + 45.25, + 45.300000000000004, + 45.35, + 45.400000000000006, + 45.45, + 45.5, + 45.550000000000004, + 45.6, + 45.650000000000006, + 45.7, + 45.75, + 45.800000000000004, + 45.85, + 45.900000000000006, + 45.95, + 46.0, + 46.050000000000004, + 46.1, + 46.150000000000006, + 46.2, + 46.25, + 46.300000000000004, + 46.35, + 46.400000000000006, + 46.45, + 46.5, + 46.550000000000004, + 46.6, + 46.650000000000006, + 46.7, + 46.75, + 46.800000000000004, + 46.85, + 46.900000000000006, + 46.95, + 47.0, + 47.050000000000004, + 47.1, + 47.150000000000006, + 47.2, + 47.25, + 47.300000000000004, + 47.35, + 47.400000000000006, + 47.45, + 47.5, + 47.550000000000004, + 47.6, + 47.650000000000006, + 47.7, + 47.75, + 47.800000000000004, + 47.85, + 47.900000000000006, + 47.95, + 48.0, + 48.050000000000004, + 48.1, + 48.150000000000006, + 48.2, + 48.25, + 48.300000000000004, + 48.35, + 48.400000000000006, + 48.45, + 48.5, + 48.550000000000004, + 48.6, + 48.650000000000006, + 48.7, + 48.75, + 48.800000000000004, + 48.85, + 48.900000000000006, + 48.95, + 49.0, + 49.050000000000004, + 49.1, + 49.150000000000006, + 49.2, + 49.25, + 49.300000000000004, + 49.35, + 49.400000000000006, + 49.45, + 49.5, + 49.550000000000004, + 49.6, + 49.650000000000006, + 49.7, + 49.75, + 49.800000000000004, + 49.85, + 49.900000000000006, + 49.95, + 50.0 + ], + "sections": { + "NSEC": 1, + "SECTION_TIMES": [ + 0.0, + 50.0 + ] + }, + "time_integrator": { + "ABSTOL": 1e-06, + "ALGTOL": 1e-10, + "INIT_STEP_SIZE": 1e-06, + "MAX_STEPS": 1000000, + "RELTOL": 1e-06 + } + } +} \ No newline at end of file diff --git a/test/data/configuration_CSTR_ASM3_without_ad.json b/test/data/configuration_CSTR_ASM3_without_ad.json new file mode 100644 index 000000000..25e0ad9a4 --- /dev/null +++ b/test/data/configuration_CSTR_ASM3_without_ad.json @@ -0,0 +1,1117 @@ +{ + "model": { + "NUNITS": 1, + "connections": { + "NSWITCHES": 1, + "switch_000": { + "CONNECTIONS": [], + "SECTION": 0 + } + }, + "solver": { + "GS_TYPE": 1, + "MAX_KRYLOV": 0, + "MAX_RESTARTS": 10, + "SCHUR_SAFETY": 1e-08 + }, + "unit_000": { + "INIT_C": [ + 1.0, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.0, + 11.0, + 12.0, + 13.0 + ], + "INIT_LIQUID_VOLUME": 10, + "NCOMP": 13, + "NREAC_LIQUID": 1, + "UNIT_TYPE": "CSTR", + "liquid_reaction_000": { + "TYPE": "ACTIVATED_SLUDGE_MODEL3", + "ASM3_FISS_BM_PROD": 1.0, + "ASM3_FSI": 0.0, + "ASM3_YH_AER": 0.8, + "ASM3_YH_ANOX": 0.65, + "ASM3_YSTO_AER": 0.8375, + "ASM3_YSTO_ANOX": 0.7, + "ASM3_FXI": 0.2, + "ASM3_YA": 0.24, + "ASM3_KH20": 9.0, + "ASM3_KX": 1.0, + "ASM3_KSTO20": 12.0, + "ASM3_MU_H20": 3.0, + "ASM3_BH20": 0.33, + "ASM3_ETA_HNO3": 0.5, + "ASM3_KHO2": 0.2, + "ASM3_KHSS": 10.0, + "ASM3_KHNO3": 0.5, + "ASM3_KHNH4": 0.01, + "ASM3_KHALK": 0.1, + "ASM3_KHSTO": 0.1, + "ASM3_MU_AUT20": 1.12, + "ASM3_BAUT20": 0.18, + "ASM3_ETAH_END": 0.5, + "ASM3_ETAN_END": 0.5, + "ASM3_KNO2": 0.5, + "ASM3_KNNH4": 0.7, + "ASM3_KNALK": 0.5, + "ASM3_T": 12.0, + "ASM3_V": 10.0, + "ASM3_IO2": 0.0, + "ASM3_INSI": 0.01, + "ASM3_INSS": 0.03, + "ASM3_INXI": 0.04, + "ASM3_INXS": 0.03, + "ASM3_INBM": 0.07, + "ASM3_IVSS_XI": 0.751879699, + "ASM3_IVSS_XS": 0.555555556, + "ASM3_IVSS_STO": 0.6, + "ASM3_IVSS_BM": 0.704225352, + "ASM3_ITSS_VSS_BM": 1.086956522 + } + } + }, + "return": { + "SPLIT_COMPONENTS_DATA": false, + "SPLIT_PORTS_DATA": 0, + "unit_000": { + "WRITE_COORDINATES": 1, + "WRITE_SENS_OUTLET": 1, + "WRITE_SOLUTION_BULK": 1, + "WRITE_SOLUTION_FLUX": 1, + "WRITE_SOLUTION_INLET": 1, + "WRITE_SOLUTION_OUTLET": 1, + "WRITE_SOLUTION_PARTICLE": 1, + "WRITE_SOLUTION_SOLID": 1, + "WRITE_SOLUTION_VOLUME": 1 + } + }, + "solver": { + "NTHREADS": 1, + "USER_SOLUTION_TIMES": [ + 0.0, + 0.05, + 0.1, + 0.15000000000000002, + 0.2, + 0.25, + 0.30000000000000004, + 0.35000000000000003, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6000000000000001, + 0.65, + 0.7000000000000001, + 0.75, + 0.8, + 0.8500000000000001, + 0.9, + 0.9500000000000001, + 1.0, + 1.05, + 1.1, + 1.1500000000000001, + 1.2000000000000002, + 1.25, + 1.3, + 1.35, + 1.4000000000000001, + 1.4500000000000002, + 1.5, + 1.55, + 1.6, + 1.6500000000000001, + 1.7000000000000002, + 1.75, + 1.8, + 1.85, + 1.9000000000000001, + 1.9500000000000002, + 2.0, + 2.0500000000000003, + 2.1, + 2.15, + 2.2, + 2.25, + 2.3000000000000003, + 2.35, + 2.4000000000000004, + 2.45, + 2.5, + 2.5500000000000003, + 2.6, + 2.6500000000000004, + 2.7, + 2.75, + 2.8000000000000003, + 2.85, + 2.9000000000000004, + 2.95, + 3.0, + 3.0500000000000003, + 3.1, + 3.1500000000000004, + 3.2, + 3.25, + 3.3000000000000003, + 3.35, + 3.4000000000000004, + 3.45, + 3.5, + 3.5500000000000003, + 3.6, + 3.6500000000000004, + 3.7, + 3.75, + 3.8000000000000003, + 3.85, + 3.9000000000000004, + 3.95, + 4.0, + 4.05, + 4.1000000000000005, + 4.15, + 4.2, + 4.25, + 4.3, + 4.3500000000000005, + 4.4, + 4.45, + 4.5, + 4.55, + 4.6000000000000005, + 4.65, + 4.7, + 4.75, + 4.800000000000001, + 4.8500000000000005, + 4.9, + 4.95, + 5.0, + 5.050000000000001, + 5.1000000000000005, + 5.15, + 5.2, + 5.25, + 5.300000000000001, + 5.3500000000000005, + 5.4, + 5.45, + 5.5, + 5.550000000000001, + 5.6000000000000005, + 5.65, + 5.7, + 5.75, + 5.800000000000001, + 5.8500000000000005, + 5.9, + 5.95, + 6.0, + 6.050000000000001, + 6.1000000000000005, + 6.15, + 6.2, + 6.25, + 6.300000000000001, + 6.3500000000000005, + 6.4, + 6.45, + 6.5, + 6.550000000000001, + 6.6000000000000005, + 6.65, + 6.7, + 6.75, + 6.800000000000001, + 6.8500000000000005, + 6.9, + 6.95, + 7.0, + 7.050000000000001, + 7.1000000000000005, + 7.15, + 7.2, + 7.25, + 7.300000000000001, + 7.3500000000000005, + 7.4, + 7.45, + 7.5, + 7.550000000000001, + 7.6000000000000005, + 7.65, + 7.7, + 7.75, + 7.800000000000001, + 7.8500000000000005, + 7.9, + 7.95, + 8.0, + 8.05, + 8.1, + 8.15, + 8.200000000000001, + 8.25, + 8.3, + 8.35, + 8.4, + 8.450000000000001, + 8.5, + 8.55, + 8.6, + 8.65, + 8.700000000000001, + 8.75, + 8.8, + 8.85, + 8.9, + 8.950000000000001, + 9.0, + 9.05, + 9.1, + 9.15, + 9.200000000000001, + 9.25, + 9.3, + 9.35, + 9.4, + 9.450000000000001, + 9.5, + 9.55, + 9.600000000000001, + 9.65, + 9.700000000000001, + 9.75, + 9.8, + 9.850000000000001, + 9.9, + 9.950000000000001, + 10.0, + 10.05, + 10.100000000000001, + 10.15, + 10.200000000000001, + 10.25, + 10.3, + 10.350000000000001, + 10.4, + 10.450000000000001, + 10.5, + 10.55, + 10.600000000000001, + 10.65, + 10.700000000000001, + 10.75, + 10.8, + 10.850000000000001, + 10.9, + 10.950000000000001, + 11.0, + 11.05, + 11.100000000000001, + 11.15, + 11.200000000000001, + 11.25, + 11.3, + 11.350000000000001, + 11.4, + 11.450000000000001, + 11.5, + 11.55, + 11.600000000000001, + 11.65, + 11.700000000000001, + 11.75, + 11.8, + 11.850000000000001, + 11.9, + 11.950000000000001, + 12.0, + 12.05, + 12.100000000000001, + 12.15, + 12.200000000000001, + 12.25, + 12.3, + 12.350000000000001, + 12.4, + 12.450000000000001, + 12.5, + 12.55, + 12.600000000000001, + 12.65, + 12.700000000000001, + 12.75, + 12.8, + 12.850000000000001, + 12.9, + 12.950000000000001, + 13.0, + 13.05, + 13.100000000000001, + 13.15, + 13.200000000000001, + 13.25, + 13.3, + 13.350000000000001, + 13.4, + 13.450000000000001, + 13.5, + 13.55, + 13.600000000000001, + 13.65, + 13.700000000000001, + 13.75, + 13.8, + 13.850000000000001, + 13.9, + 13.950000000000001, + 14.0, + 14.05, + 14.100000000000001, + 14.15, + 14.200000000000001, + 14.25, + 14.3, + 14.350000000000001, + 14.4, + 14.450000000000001, + 14.5, + 14.55, + 14.600000000000001, + 14.65, + 14.700000000000001, + 14.75, + 14.8, + 14.850000000000001, + 14.9, + 14.950000000000001, + 15.0, + 15.05, + 15.100000000000001, + 15.15, + 15.200000000000001, + 15.25, + 15.3, + 15.350000000000001, + 15.4, + 15.450000000000001, + 15.5, + 15.55, + 15.600000000000001, + 15.65, + 15.700000000000001, + 15.75, + 15.8, + 15.850000000000001, + 15.9, + 15.950000000000001, + 16.0, + 16.05, + 16.1, + 16.150000000000002, + 16.2, + 16.25, + 16.3, + 16.35, + 16.400000000000002, + 16.45, + 16.5, + 16.55, + 16.6, + 16.650000000000002, + 16.7, + 16.75, + 16.8, + 16.85, + 16.900000000000002, + 16.95, + 17.0, + 17.05, + 17.1, + 17.150000000000002, + 17.2, + 17.25, + 17.3, + 17.35, + 17.400000000000002, + 17.45, + 17.5, + 17.55, + 17.6, + 17.650000000000002, + 17.7, + 17.75, + 17.8, + 17.85, + 17.900000000000002, + 17.95, + 18.0, + 18.05, + 18.1, + 18.150000000000002, + 18.2, + 18.25, + 18.3, + 18.35, + 18.400000000000002, + 18.45, + 18.5, + 18.55, + 18.6, + 18.650000000000002, + 18.7, + 18.75, + 18.8, + 18.85, + 18.900000000000002, + 18.95, + 19.0, + 19.05, + 19.1, + 19.150000000000002, + 19.200000000000003, + 19.25, + 19.3, + 19.35, + 19.400000000000002, + 19.450000000000003, + 19.5, + 19.55, + 19.6, + 19.650000000000002, + 19.700000000000003, + 19.75, + 19.8, + 19.85, + 19.900000000000002, + 19.950000000000003, + 20.0, + 20.05, + 20.1, + 20.150000000000002, + 20.200000000000003, + 20.25, + 20.3, + 20.35, + 20.400000000000002, + 20.450000000000003, + 20.5, + 20.55, + 20.6, + 20.650000000000002, + 20.700000000000003, + 20.75, + 20.8, + 20.85, + 20.900000000000002, + 20.950000000000003, + 21.0, + 21.05, + 21.1, + 21.150000000000002, + 21.200000000000003, + 21.25, + 21.3, + 21.35, + 21.400000000000002, + 21.450000000000003, + 21.5, + 21.55, + 21.6, + 21.650000000000002, + 21.700000000000003, + 21.75, + 21.8, + 21.85, + 21.900000000000002, + 21.950000000000003, + 22.0, + 22.05, + 22.1, + 22.150000000000002, + 22.200000000000003, + 22.25, + 22.3, + 22.35, + 22.400000000000002, + 22.450000000000003, + 22.5, + 22.55, + 22.6, + 22.650000000000002, + 22.700000000000003, + 22.75, + 22.8, + 22.85, + 22.900000000000002, + 22.950000000000003, + 23.0, + 23.05, + 23.1, + 23.150000000000002, + 23.200000000000003, + 23.25, + 23.3, + 23.35, + 23.400000000000002, + 23.450000000000003, + 23.5, + 23.55, + 23.6, + 23.650000000000002, + 23.700000000000003, + 23.75, + 23.8, + 23.85, + 23.900000000000002, + 23.950000000000003, + 24.0, + 24.05, + 24.1, + 24.150000000000002, + 24.200000000000003, + 24.25, + 24.3, + 24.35, + 24.400000000000002, + 24.450000000000003, + 24.5, + 24.55, + 24.6, + 24.650000000000002, + 24.700000000000003, + 24.75, + 24.8, + 24.85, + 24.900000000000002, + 24.950000000000003, + 25.0, + 25.05, + 25.1, + 25.150000000000002, + 25.200000000000003, + 25.25, + 25.3, + 25.35, + 25.400000000000002, + 25.450000000000003, + 25.5, + 25.55, + 25.6, + 25.650000000000002, + 25.700000000000003, + 25.75, + 25.8, + 25.85, + 25.900000000000002, + 25.950000000000003, + 26.0, + 26.05, + 26.1, + 26.150000000000002, + 26.200000000000003, + 26.25, + 26.3, + 26.35, + 26.400000000000002, + 26.450000000000003, + 26.5, + 26.55, + 26.6, + 26.650000000000002, + 26.700000000000003, + 26.75, + 26.8, + 26.85, + 26.900000000000002, + 26.950000000000003, + 27.0, + 27.05, + 27.1, + 27.150000000000002, + 27.200000000000003, + 27.25, + 27.3, + 27.35, + 27.400000000000002, + 27.450000000000003, + 27.5, + 27.55, + 27.6, + 27.650000000000002, + 27.700000000000003, + 27.75, + 27.8, + 27.85, + 27.900000000000002, + 27.950000000000003, + 28.0, + 28.05, + 28.1, + 28.150000000000002, + 28.200000000000003, + 28.25, + 28.3, + 28.35, + 28.400000000000002, + 28.450000000000003, + 28.5, + 28.55, + 28.6, + 28.650000000000002, + 28.700000000000003, + 28.75, + 28.8, + 28.85, + 28.900000000000002, + 28.950000000000003, + 29.0, + 29.05, + 29.1, + 29.150000000000002, + 29.200000000000003, + 29.25, + 29.3, + 29.35, + 29.400000000000002, + 29.450000000000003, + 29.5, + 29.55, + 29.6, + 29.650000000000002, + 29.700000000000003, + 29.75, + 29.8, + 29.85, + 29.900000000000002, + 29.950000000000003, + 30.0, + 30.05, + 30.1, + 30.150000000000002, + 30.200000000000003, + 30.25, + 30.3, + 30.35, + 30.400000000000002, + 30.450000000000003, + 30.5, + 30.55, + 30.6, + 30.650000000000002, + 30.700000000000003, + 30.75, + 30.8, + 30.85, + 30.900000000000002, + 30.950000000000003, + 31.0, + 31.05, + 31.1, + 31.150000000000002, + 31.200000000000003, + 31.25, + 31.3, + 31.35, + 31.400000000000002, + 31.450000000000003, + 31.5, + 31.55, + 31.6, + 31.650000000000002, + 31.700000000000003, + 31.75, + 31.8, + 31.85, + 31.900000000000002, + 31.950000000000003, + 32.0, + 32.050000000000004, + 32.1, + 32.15, + 32.2, + 32.25, + 32.300000000000004, + 32.35, + 32.4, + 32.45, + 32.5, + 32.550000000000004, + 32.6, + 32.65, + 32.7, + 32.75, + 32.800000000000004, + 32.85, + 32.9, + 32.95, + 33.0, + 33.050000000000004, + 33.1, + 33.15, + 33.2, + 33.25, + 33.300000000000004, + 33.35, + 33.4, + 33.45, + 33.5, + 33.550000000000004, + 33.6, + 33.65, + 33.7, + 33.75, + 33.800000000000004, + 33.85, + 33.9, + 33.95, + 34.0, + 34.050000000000004, + 34.1, + 34.15, + 34.2, + 34.25, + 34.300000000000004, + 34.35, + 34.4, + 34.45, + 34.5, + 34.550000000000004, + 34.6, + 34.65, + 34.7, + 34.75, + 34.800000000000004, + 34.85, + 34.9, + 34.95, + 35.0, + 35.050000000000004, + 35.1, + 35.15, + 35.2, + 35.25, + 35.300000000000004, + 35.35, + 35.4, + 35.45, + 35.5, + 35.550000000000004, + 35.6, + 35.65, + 35.7, + 35.75, + 35.800000000000004, + 35.85, + 35.9, + 35.95, + 36.0, + 36.050000000000004, + 36.1, + 36.15, + 36.2, + 36.25, + 36.300000000000004, + 36.35, + 36.4, + 36.45, + 36.5, + 36.550000000000004, + 36.6, + 36.65, + 36.7, + 36.75, + 36.800000000000004, + 36.85, + 36.9, + 36.95, + 37.0, + 37.050000000000004, + 37.1, + 37.15, + 37.2, + 37.25, + 37.300000000000004, + 37.35, + 37.4, + 37.45, + 37.5, + 37.550000000000004, + 37.6, + 37.65, + 37.7, + 37.75, + 37.800000000000004, + 37.85, + 37.9, + 37.95, + 38.0, + 38.050000000000004, + 38.1, + 38.15, + 38.2, + 38.25, + 38.300000000000004, + 38.35, + 38.400000000000006, + 38.45, + 38.5, + 38.550000000000004, + 38.6, + 38.650000000000006, + 38.7, + 38.75, + 38.800000000000004, + 38.85, + 38.900000000000006, + 38.95, + 39.0, + 39.050000000000004, + 39.1, + 39.150000000000006, + 39.2, + 39.25, + 39.300000000000004, + 39.35, + 39.400000000000006, + 39.45, + 39.5, + 39.550000000000004, + 39.6, + 39.650000000000006, + 39.7, + 39.75, + 39.800000000000004, + 39.85, + 39.900000000000006, + 39.95, + 40.0, + 40.050000000000004, + 40.1, + 40.150000000000006, + 40.2, + 40.25, + 40.300000000000004, + 40.35, + 40.400000000000006, + 40.45, + 40.5, + 40.550000000000004, + 40.6, + 40.650000000000006, + 40.7, + 40.75, + 40.800000000000004, + 40.85, + 40.900000000000006, + 40.95, + 41.0, + 41.050000000000004, + 41.1, + 41.150000000000006, + 41.2, + 41.25, + 41.300000000000004, + 41.35, + 41.400000000000006, + 41.45, + 41.5, + 41.550000000000004, + 41.6, + 41.650000000000006, + 41.7, + 41.75, + 41.800000000000004, + 41.85, + 41.900000000000006, + 41.95, + 42.0, + 42.050000000000004, + 42.1, + 42.150000000000006, + 42.2, + 42.25, + 42.300000000000004, + 42.35, + 42.400000000000006, + 42.45, + 42.5, + 42.550000000000004, + 42.6, + 42.650000000000006, + 42.7, + 42.75, + 42.800000000000004, + 42.85, + 42.900000000000006, + 42.95, + 43.0, + 43.050000000000004, + 43.1, + 43.150000000000006, + 43.2, + 43.25, + 43.300000000000004, + 43.35, + 43.400000000000006, + 43.45, + 43.5, + 43.550000000000004, + 43.6, + 43.650000000000006, + 43.7, + 43.75, + 43.800000000000004, + 43.85, + 43.900000000000006, + 43.95, + 44.0, + 44.050000000000004, + 44.1, + 44.150000000000006, + 44.2, + 44.25, + 44.300000000000004, + 44.35, + 44.400000000000006, + 44.45, + 44.5, + 44.550000000000004, + 44.6, + 44.650000000000006, + 44.7, + 44.75, + 44.800000000000004, + 44.85, + 44.900000000000006, + 44.95, + 45.0, + 45.050000000000004, + 45.1, + 45.150000000000006, + 45.2, + 45.25, + 45.300000000000004, + 45.35, + 45.400000000000006, + 45.45, + 45.5, + 45.550000000000004, + 45.6, + 45.650000000000006, + 45.7, + 45.75, + 45.800000000000004, + 45.85, + 45.900000000000006, + 45.95, + 46.0, + 46.050000000000004, + 46.1, + 46.150000000000006, + 46.2, + 46.25, + 46.300000000000004, + 46.35, + 46.400000000000006, + 46.45, + 46.5, + 46.550000000000004, + 46.6, + 46.650000000000006, + 46.7, + 46.75, + 46.800000000000004, + 46.85, + 46.900000000000006, + 46.95, + 47.0, + 47.050000000000004, + 47.1, + 47.150000000000006, + 47.2, + 47.25, + 47.300000000000004, + 47.35, + 47.400000000000006, + 47.45, + 47.5, + 47.550000000000004, + 47.6, + 47.650000000000006, + 47.7, + 47.75, + 47.800000000000004, + 47.85, + 47.900000000000006, + 47.95, + 48.0, + 48.050000000000004, + 48.1, + 48.150000000000006, + 48.2, + 48.25, + 48.300000000000004, + 48.35, + 48.400000000000006, + 48.45, + 48.5, + 48.550000000000004, + 48.6, + 48.650000000000006, + 48.7, + 48.75, + 48.800000000000004, + 48.85, + 48.900000000000006, + 48.95, + 49.0, + 49.050000000000004, + 49.1, + 49.150000000000006, + 49.2, + 49.25, + 49.300000000000004, + 49.35, + 49.400000000000006, + 49.45, + 49.5, + 49.550000000000004, + 49.6, + 49.650000000000006, + 49.7, + 49.75, + 49.800000000000004, + 49.85, + 49.900000000000006, + 49.95, + 50.0 + ], + "sections": { + "NSEC": 1, + "SECTION_TIMES": [ + 0.0, + 50.0 + ] + }, + "time_integrator": { + "ABSTOL": 1e-06, + "ALGTOL": 1e-10, + "INIT_STEP_SIZE": 1e-06, + "MAX_STEPS": 1000000, + "RELTOL": 1e-06 + } + } +} \ No newline at end of file